infinitered / nsfwjs

NSFW detection on the client-side via TensorFlow.js

Home Page:https://nsfwjs.com/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Unable to use NSFWJS with File interface

MirzaLeka opened this issue · comments

I'm building a feature where users can upload files via input type file and then I want to validate images before they're uploaded to the backend.
To do that I created input and am listening to changes (selected files)

      <input
        type="file"
        onChange={handleChange}
      />

However, I noticed that the library works with the HTMLImageElement interface, while the input type file returns the File interface.

So I started playing around, looking for workarounds.
I tried to take it by creating an invisible image

      <img
        crossOrigin="anonymous"
        id="img"
        alt="img"
      />

And to put the source in it manually.

    const  selectedPicture = event.target.files[0]
    const reader = new FileReader()

    const imgTag = document.getElementById('img') as HTMLImageElement
    imgTag.src = URL.createObjectURL(selectedPicture)
    imgTag.title = selectedPicture.name

    const model = await nsfwjs.load()
    const predictions = await model.classify(imgTag)
    console.log('Predictions: ', predictions)

Unfortunately, it doesn't work as I expected. The predictions log always return a dummy response, saying everything is neutral.

Is it possible to make nsfwjs work with a file that comes from input?

You can use the FileReader API to locally load the selected image: https://stackoverflow.com/a/3814285

I have a solution, hope it can help you:
1. classify function accepts ImageData type, we can convert blob to ImageData.

let blobToImageData = (blob: File): Promise<ImageData> => {
  let blobUrl = URL.createObjectURL(blob)
  return new Promise((resolve, reject) => {
    let img = new Image(200, 200)
    img.onload = () => resolve(img)
    img.onerror = (err) => reject(err)
    img.src = blobUrl
  }).then((img: any) => {
    URL.revokeObjectURL(blobUrl)
    // Limit to 256x256px while preserving aspect ratio
    let [w, h] = [img.width, img.height]
    let aspectRatio = w / h
    // Say the file is 1920x1080
    // divide max(w,h) by 256 to get factor
    let factor = Math.max(w, h) / 256
    w = w / factor
    h = h / factor

    // REMINDER
    // 256x256 = 65536 pixels with 4 channels (RGBA) = 262144 data points for each image
    // Data is encoded as Uint8ClampedArray with BYTES_PER_ELEMENT = 1
    // So each images = 262144bytes
    // 1000 images = 260Mb
    let canvas = document.createElement('canvas')
    canvas.width = w
    canvas.height = h
    let ctx = canvas.getContext('2d')
    ctx!.drawImage(img, 0, 0)

    return ctx!.getImageData(0, 0, w, h) // some browsers synchronously decode image here
  })
}

2. load ImageData

const model = await nsfwjs.load()
const imgData = await blobToImageData(file)
const predictions = await model.classify(imgData)
console.log('Predictions: ', predictions)

@MirzaLeka I'm going to close this issue for now because it's been some time since some possible fixes were provided. If you're continuing to experience this error, we'll gladly reopen this issue! Thanks.