diff --git a/.all-contributorsrc b/.all-contributorsrc index f6ea4f17..888341cb 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -241,6 +241,20 @@ "maintenance", "code" ] + }, + { + "login": "haZya", + "name": "Hasitha Wickramasinghe", + "avatar_url": "https://avatars.githubusercontent.com/u/63403456?v=4", + "profile": "https://hazya.dev", + "contributions": [ + "code", + "doc", + "example", + "ideas", + "infra", + "test" + ] } ], "contributorsPerLine": 7, diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 00000000..2b360231 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,19 @@ +# top-most EditorConfig file +root = true + +# all files +[*] +end_of_line = lf +insert_final_newline = true +indent_style = space +indent_size = 2 +charset = utf-8 +trim_trailing_whitespace = true +max_line_length = 80 +quote_type = double + +[*.{js,ts}] +quote_type = double +curly_bracket_next_line = false +spaces_around_brackets = inside +indent_brace_style = BSD KNF \ No newline at end of file diff --git a/.gitignore b/.gitignore index 6574ff89..97078704 100644 --- a/.gitignore +++ b/.gitignore @@ -10,5 +10,8 @@ examples/manual-testing/data/* !examples/manual-testing/data/images/ !examples/manual-testing/data/videos/ +/models/**/*.js +/models/**/*.min.js + # Local Netlify folder .netlify diff --git a/.npmignore b/.npmignore index c95da7bb..edb692a3 100644 --- a/.npmignore +++ b/.npmignore @@ -17,11 +17,27 @@ _art/ yarn-error.log bundle.js .DS_Store +.all-contributorsrc +.editorconfig +.gitignore +bundle-model.mjs +jest.config.js +tsconfig.cjs.json +tsconfig.esm.json +tsconfig.json +.circleci/ +.github/ +__tests__/ + +# Models +models/ # Ignore intermediate browserify files # Mountpoint -dist/nsfwjs.d.ts -dist/nsfwjs.js +dist/cjs/nsfwjs.d.ts +dist/cjs/nsfwjs.js +dist/esm/nsfwjs.d.ts +dist/esm/nsfwjs.js # Unminified bundle -dist/bundle.js +dist/browser/bundle.js diff --git a/README.md b/README.md index a08531d9..71ec6b1b 100644 --- a/README.md +++ b/README.md @@ -16,16 +16,18 @@ Why would this be useful? [Check out the announcement blog post](https://shift.i

## **Table of Contents** + - [QUICK: How to use the module](#quick-how-to-use-the-module) - [Library API](#library-api) - - [`load` the model](#load-the-model) - - [`classify` an image](#classify-an-image) + - [`load` the model](#load-the-model) + - [Caching](#caching) + - [`classify` an image](#classify-an-image) - [Production](#production) - [Install](#install) - - [Host your own model](#host-your-own-model) + - [Host your own model](#host-your-own-model) - [Run the Examples](#run-the-examples) - [Tensorflow.js in the browser](#tensorflowjs-in-the-browser) - [Browserify](#browserify) @@ -34,8 +36,8 @@ Why would this be useful? [Check out the announcement blog post](https://shift.i - [NSFW Filter](#nsfw-filter) - [Learn TensorFlow.js](#learn-tensorflowjs) - [More!](#more) - - [Open Source](#open-source) - - [Need the experts? Hire Infinite Red for your next project](#need-the-experts-hire-infinite-red-for-your-next-project) + - [Open Source](#open-source) + - [Need the experts? Hire Infinite Red for your next project](#need-the-experts-hire-infinite-red-for-your-next-project) - [Contributors](#contributors) @@ -48,79 +50,94 @@ The library categorizes image probabilities in the following 5 classes: - `Porn` - pornographic images, sexual acts - `Sexy` - sexually explicit images, not pornography -> _The demo is a continuous deployment source - Give it a go: http://nsfwjs.com/_ +> _The demo is a continuous deployment source - Give it a go: http://nsfwjs.com_ ## QUICK: How to use the module With `async/await` support: ```js -import * as nsfwjs from 'nsfwjs' +import * as nsfwjs from "nsfwjs"; -const img = document.getElementById('img') +const img = document.getElementById("img"); -// Load model from my S3. -// See the section hosting the model files on your site. -const model = await nsfwjs.load() +// If you want to host models on your own or use different model from the ones available, see the section "Host your own model". +const model = await nsfwjs.load(); // Classify the image -const predictions = await model.classify(img) -console.log('Predictions: ', predictions) +const predictions = await model.classify(img); +console.log("Predictions: ", predictions); ``` Without `async/await` support: ```js -import * as nsfwjs from 'nsfwjs' +import * as nsfwjs from "nsfwjs"; -const img = document.getElementById('img') +const img = document.getElementById("img"); -// Load model from my S3. -// See the section hosting the model files on your site. -nsfwjs.load() +// If you want to host models on your own or use different model from the ones available, see the section "Host your own model". +nsfwjs + .load() .then(function (model) { // Classify the image - return model.classify(img) + return model.classify(img); }) .then(function (predictions) { - console.log('Predictions: ', predictions) - }) + console.log("Predictions: ", predictions); + }); ``` ## Library API -#### `load` the model +### `load` the model + +Before you can classify any image, you'll need to load the model. + +```js +const model = nsfwjs.load(); // Default: "MobileNetV2" +``` + +You can use the optional first parameter to specify which model you want to use from the three that are bundled together. Defaults to: `"MobileNetV2"`. This supports tree-shaking on supported bundlers like Webpack, so you will only be loading the model you are using. + +```js +const model = nsfwjs.load("MobileNetV2Mid"); // "MobileNetV2" | "MobileNetV2Mid" | "InceptionV3" +``` -Before you can classify any image, you'll need to load the model. You should use the optional first parameter and load the model from your website, as explained in the install directions. +You can also use same parameter and load the model from your website/server, as explained in the [Host your own model](#host-your-own-model) section. Doing so could reduce the bundle size for loading the model by approximately 1.33 times (33%) since you can directly use the binary files instead of the base64 that are bundled with the package. i.e. The `"MobileNetV2"` model bundled into the package is 3.5MB instead of 2.6MB for hosted binary files. This would only make a difference if you are loading the model every time (without [Caching](#caching)) on the client-side browser since on the server-side, you'd only be loading the model once at the server start. -Model example - [224x224](https://github.com/infinitered/nsfwjs/blob/master/examples/nsfw_demo/public/quant_nsfw_mobilenet/) +Model MobileNetV2 - [224x224](https://github.com/infinitered/nsfwjs/blob/master/models/mobilenet_v2/) ```js -const model = nsfwjs.load('/path/to/model/directory/') +const model = nsfwjs.load("/path/to/model/directory/"); ``` If you're using a model that needs an image of dimension other than 224x224, you can pass the size in the options parameter. -Model example - [299x299](https://github.com/infinitered/nsfwjs/tree/master/examples/nsfw_demo/public/model) +Model MobileNetV2Mid - [299x299](https://github.com/infinitered/nsfwjs/tree/master/models/inception_v3) ```js -const model = nsfwjs.load('/path/to/different/model/', { size: 299 }) +const model = nsfwjs.load("/path/to/different/model/", { size: 299 }); ``` If you're using a graph model, you cannot use the infer method, and you'll need to tell model load that you're dealing with a graph model in options. -Model example - [Graph](https://github.com/infinitered/nsfwjs/tree/master/examples/nsfw_demo/public/quant_mid) +Model InceptionV3 - [Graph](https://github.com/infinitered/nsfwjs/tree/master/models/mobilenet_v2_mid) ```js -const model = nsfwjs.load('/path/to/different/model/', { type: 'graph' }) +const model = nsfwjs.load("/path/to/different/model/", { type: "graph" }); ``` +### Caching + If you're using in the browser and you'd like to subsequently load from indexed db or local storage you can save the underlying model using the appropriate scheme and load from there. ```js -const initialLoad = await nsfwjs.load('/path/to/different/model') -await initialLoad.model.save('indexeddb://model') -const model = await nsfwjs.load('indexeddb://model') +const initialLoad = await nsfwjs.load( + "/path/to/different/model" /*{ ...options }*/ +); +await initialLoad.model.save("indexeddb://model"); +const model = await nsfwjs.load("indexeddb://model" /*{ ...options }*/); ``` **Parameters** @@ -132,13 +149,13 @@ const model = await nsfwjs.load('indexeddb://model') - Ready to use NSFWJS model object -#### `classify` an image +### `classify` an image This function can take any browser-based image elements (``, `