nsfwjs

repository·master·Indexed 25 days ago

https://github.com/infinitered/nsfwjs

A client-side JavaScript library for detecting indecent content in images using TensorFlow.js. It categorizes images into five classes: Drawing, Hentai, Neutral, Porn, and Sexy. The library supports multiple models including MobileNetV2, MobileNetV2Mid, and InceptionV3, and can be used in both browser and Node.js environments. It offers features such as IndexedDB caching, tree-shaking via nsfwjs/core, and support for various TensorFlow.js backends including WebGPU, WebGL, and WASM.

Tokens
4.1K
Snippets
13
Records
23
Agent score
93%

What's inside nsfwjs

  1. Cache the model using IndexedDB

    master

    In browser environments, you can save the model to indexeddb to avoid re-downloading it on subsequent visits.

    Note: Model size may be too large for localStorage.

    // Initial load and save to IndexedDB
    const initialLoad = await nsfwjs.load(
      "/path/to/different/model/" /*, { ...options }*/
    );
    await initialLoad.model.save("indexeddb://exampleModel");
    
    // Subsequent load from IndexedDB
    const model = await nsfwjs.load("indexeddb://exampleModel" /*, { ...options }*/);
  2. Host your own NSFWJS model files

    master

    To reduce bundle size or use a custom model, you can host the model files on your own server (e.g., S3 or a static directory).

    1. Download the models folder from the NSFWJS repository.
    2. Serve these files as static assets from your web application's public directory or a CDN.
    3. Pass the URL of the model.json file to the nsfwjs.load() function.

    Note: If you are loading a hosted model URL, it is recommended to use nsfwjs/core to avoid bundling the built-in model definitions into your application.

  3. Use selective model bundles (tree-shaking)

    master

    To reduce your bundle size, you can use nsfwjs/core to import only the specific models you need. This prevents the default built-in model definitions from being included in your bundle.

    When using nsfwjs/core, you must pass the desired models in the modelDefinitions option.

    import { load } from "nsfwjs/core";
    import { MobileNetV2Model } from "nsfwjs/models/mobilenet_v2";
    import { MobileNetV2MidModel } from "nsfwjs/models/mobilenet_v2_mid";
    
    const model = await load("MobileNetV2", {
      modelDefinitions: [MobileNetV2Model, MobileNetV2MidModel],
    });
  4. Select a TensorFlow.js backend

    master

    NSFWJS uses the active TensorFlow.js backend. You can let TensorFlow.js pick the best available backend automatically by calling await tf.ready(), or you can pin a specific backend using tf.setBackend().

    Backend Options:

    • webgpu: Often fastest on supported hardware.
    • webgl: Strong default for modern browsers.
    • wasm: Useful fallback when WebGL is unavailable.
    • cpu: Broad compatibility, but slower.
    import * as tf from "@tensorflow/tfjs";
    import "@tensorflow/tfjs-backend-webgpu";
    import "@tensorflow/tfjs-backend-wasm";
    import * as nsfwjs from "nsfwjs";
    
    // Automatic selection
    await tf.ready();
    const model = await nsfwjs.load();
    
    // Pinned selection
    await tf.setBackend("webgpu");
    await tf.ready();
  5. Install NSFWJS and TensorFlow.js

    master

    NSFWJS requires TensorFlow.js as a peer dependency. Install both using your preferred package manager.

    # Install peer dependency
    yarn add @tensorflow/tfjs
    
    # Install NSFWJS
    yarn add nsfwjs
    # peer dependency
    $ yarn add @tensorflow/tfjs
    # install NSFWJS
    $ yarn add nsfwjs
  6. Use NSFWJS via Browserify script tags

    master

    For a minimal browser implementation using only promises and script tags, include the model shards and the NSFWJS bundle in your HTML. You must host the nsfwjs.min.js file and all required model bundles alongside your project.

    <script src="/path/to/model/directory/model.min.js"></script>
    <script src="/path/to/model/directory/group1-shard1of2.min.js"></script>
    <script src="/path/to/model/directory/group1-shard2of2.min.js"></script>
    <script src="/path/to/bundle/nsfwjs.min.js"></script>
    <script src="/path/to/model/directory/model.min.js"></script>
    <script src="/path/to/model/directory/group1-shard1of2.min.js"></script>
    <script src="/path/to/model/directory/group1-shard2of2.min.js"></script>
    <script src="/path/to/bundle/nsfwjs.min.js"></script>
  7. Setup WebGPU backend

    master

    To use the WebGPU backend, install the package and set the backend explicitly.

    yarn add @tensorflow/tfjs-backend-webgpu
    import * as tf from "@tensorflow/tfjs";
    import "@tensorflow/tfjs-backend-webgpu";
    
    await tf.setBackend("webgpu");
    await tf.ready();
  8. Setup WASM backend

    master

    To use the WASM backend, install the package and optionally set the path to the .wasm binaries if you are not using a standard bundler.

    yarn add @tensorflow/tfjs-backend-wasm
    import * as tf from "@tensorflow/tfjs";
    import { setWasmPaths } from "@tensorflow/tfjs-backend-wasm";
    import "@tensorflow/tfjs-backend-wasm";
    
    // Set path to .wasm binaries if not using a standard bundler
    setWasmPaths("https://cdn.jsdelivr.net/npm/@tensorflow/tfjs-backend-wasm/dist/");
    
    await tf.setBackend("wasm");
    await tf.ready();
  9. Quick start with NSFWJS

    master

    To quickly identify indecent content in images using the default model, import nsfwjs, load the model, and pass an image element to classify.

    NSFWJS categorizes images into 5 classes:

    • Drawing: safe for work drawings (including anime)
    • Hentai: hentai and pornographic drawings
    • Neutral: safe for work neutral images
    • Porn: pornographic images, sexual acts
    • Sexy: sexually explicit images, not pornography
    import * as nsfwjs from "nsfwjs";
    
    const img = document.getElementById("img");
    
    // Load the default model (MobileNetV2)
    const model = await nsfwjs.load();
    
    // Classify the image
    const predictions = await model.classify(img);
    console.log("Predictions: ", predictions);
  10. Configure TensorFlow.js for Production

    master

    When using NSFWJS in production, enable TensorFlow.js production mode to optimize performance. It is also recommended to host the model yourself and use caching.

    import * as tf from "@tensorflow/tfjs";
    import * as nsfwjs from "nsfwjs";
    
    tf.enableProdMode();
    let model = await nsfwjs.load(`${urlToNSFWJSModel}`);
  11. Use NSFWJS in a Node.js application

    master

    To use NSFWJS on the server side, install nsfwjs and @tensorflow/tfjs-node. You must provide the image as a tf.tensor3d. You can use tf.node.decodeImage to convert raw image data into the required tensor format.

    Important: You must explicitly call .dispose() on tensors to prevent memory leaks, as TensorFlow.js does not automatically release tensor memory when they go out of scope.

    const axios = require("axios"); //you can use any http client
    const tf = require("@tensorflow/tfjs-node");
    const nsfw = require("nsfwjs");
    async function fn() {
      const pic = await axios.get(`link-to-picture`, {
        responseType: "arraybuffer",
      });
      const model = await nsfw.load(); // To load a local model, nsfw.load('file://./path/to/model/')
      // Image must be in tf.tensor3d format
      // you can convert image to tf.tensor3d with tf.node.decodeImage(Uint8Array,channels)
      const image = await tf.node.decodeImage(pic.data, 3);
      const predictions = await model.classify(image);
      image.dispose(); // Tensor memory must be managed explicitly (it is not sufficient to let a tf.Tensor go out of scope for its memory to be released).
      console.log(predictions);
    }
    fn();
  12. Implement NSFW filtering in an Express app

    master

    This example demonstrates how to handle multipart/form-data uploads using multer and jpeg-js to classify images in an Express server. The model should be loaded once and kept in memory to avoid the overhead of reloading it on every request.

    const express = require("express");
    const multer = require("multer");
    const jpeg = require("jpeg-js");
    
    const tf = require("@tensorflow/tfjs-node");
    const nsfw = require("nsfwjs");
    
    const app = express();
    const upload = multer();
    
    let _model;
    
    const convert = async (img) => {
      // Decoded image in UInt8 Byte array
      const image = await jpeg.decode(img, { useTArray: true });
    
      const numChannels = 3;
      const numPixels = image.width * image.height;
      const values = new Int32Array(numPixels * numChannels);
    
      for (let i = 0; i < numPixels; i++)
        for (let c = 0; c < numChannels; ++c)
          values[i * numChannels + c] = image.data[i * 4 + c];
    
      return tf.tensor3d(values, [image.height, image.width, numChannels], "int32");
    };
    
    app.post("/nsfw", upload.single("image"), async (req, res) => {
      if (!req.file) res.status(400).send("Missing image multipart/form-data");
      else {
        const image = await convert(req.file.buffer);
        const predictions = await _model.classify(image);
        image.dispose();
        res.json(predictions);
      }
    });
    
    const load_model = async () => {
      _model = await nsfw.load();
    };
    
    // Keep the model in memory, make sure it's loaded only once
    load_model().then(() => app.listen(8080));
    
    // curl --request POST localhost:8080/nsfw --header 'Content-Type: multipart/form-data' --data-binary 'image=@/full/path/to/picture.jpg'