root
commited on
Commit
·
dfe9a5e
1
Parent(s):
934878b
update
Browse files- .dockerignore +8 -0
- .eslintrc.js +48 -0
- .gitignore +5 -0
- Dockerfile +19 -3
- NSFW-API/.gitattributes +35 -0
- NSFW-API/Dockerfile +5 -0
- NSFW-API/README.md +11 -0
- README.md +122 -11
- models/default/group1-shard10of22 +3 -0
- models/default/group1-shard11of22 +3 -0
- models/default/group1-shard12of22 +3 -0
- models/default/group1-shard13of22 +3 -0
- models/default/group1-shard14of22 +3 -0
- models/default/group1-shard15of22 +3 -0
- models/default/group1-shard16of22 +3 -0
- models/default/group1-shard17of22 +3 -0
- models/default/group1-shard18of22 +3 -0
- models/default/group1-shard19of22 +3 -0
- models/default/group1-shard1of22 +3 -0
- models/default/group1-shard20of22 +3 -0
- models/default/group1-shard21of22 +3 -0
- models/default/group1-shard22of22 +3 -0
- models/default/group1-shard2of22 +3 -0
- models/default/group1-shard3of22 +3 -0
- models/default/group1-shard4of22 +3 -0
- models/default/group1-shard5of22 +3 -0
- models/default/group1-shard6of22 +3 -0
- models/default/group1-shard7of22 +3 -0
- models/default/group1-shard8of22 +3 -0
- models/default/group1-shard9of22 +3 -0
- models/default/model.json +3 -0
- models/min/group1-shard1of6 +3 -0
- models/min/group1-shard2of6 +3 -0
- models/min/group1-shard3of6 +3 -0
- models/min/group1-shard4of6 +3 -0
- models/min/group1-shard5of6 +3 -0
- models/min/group1-shard6of6 +3 -0
- models/min/model.json +3 -0
- nsfw-api +1 -0
- package.json +37 -0
- src/NsfwImageClassifier.ts +46 -0
- src/config/model.ts +5 -0
- src/controllers/NsfwController.ts +42 -0
- src/index.ts +17 -0
- tsconfig.json +33 -0
- yarn.lock +0 -0
.dockerignore
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
Dockerfile
|
| 2 |
+
node_modules
|
| 3 |
+
build
|
| 4 |
+
.eslintrc.js
|
| 5 |
+
.git
|
| 6 |
+
.gitattributes
|
| 7 |
+
.gitignore
|
| 8 |
+
.github
|
.eslintrc.js
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
module.exports = {
|
| 2 |
+
env: {node: true},
|
| 3 |
+
root: true,
|
| 4 |
+
parser: '@typescript-eslint/parser',
|
| 5 |
+
plugins: [
|
| 6 |
+
'@typescript-eslint',
|
| 7 |
+
],
|
| 8 |
+
extends: [
|
| 9 |
+
'eslint:recommended',
|
| 10 |
+
'plugin:@typescript-eslint/recommended',
|
| 11 |
+
],
|
| 12 |
+
rules: {
|
| 13 |
+
'no-async-promise-executor': 0,
|
| 14 |
+
'no-case-declarations': ['warn'],
|
| 15 |
+
'no-irregular-whitespace': 0,
|
| 16 |
+
'require-await': 1,
|
| 17 |
+
'no-console': 0,
|
| 18 |
+
'indent': 'off',
|
| 19 |
+
'@typescript-eslint/indent': [
|
| 20 |
+
'error',
|
| 21 |
+
2,
|
| 22 |
+
{
|
| 23 |
+
'SwitchCase': 1,
|
| 24 |
+
'ignoredNodes': [
|
| 25 |
+
'FunctionExpression > .params[decorators.length > 0]',
|
| 26 |
+
'FunctionExpression > .params > :matches(Decorator, :not(:first-child))',
|
| 27 |
+
'ClassBody.body > PropertyDefinition[decorators.length > 0] > .key'
|
| 28 |
+
]
|
| 29 |
+
}
|
| 30 |
+
],
|
| 31 |
+
'linebreak-style': ['error', 'unix'],
|
| 32 |
+
'quotes': ['error', 'single'],
|
| 33 |
+
'semi': ['error', 'always'],
|
| 34 |
+
'prefer-const': ['warn'],
|
| 35 |
+
'@typescript-eslint/no-unused-vars': [
|
| 36 |
+
'warn',
|
| 37 |
+
{'vars': 'all', 'args': 'after-used', 'ignoreRestSiblings': false}
|
| 38 |
+
],
|
| 39 |
+
'switch-colon-spacing': [
|
| 40 |
+
'error',
|
| 41 |
+
{'after': false, 'before': false}
|
| 42 |
+
],
|
| 43 |
+
'@typescript-eslint/no-explicit-any': 0,
|
| 44 |
+
'@typescript-eslint/explicit-module-boundary-types': 0,
|
| 45 |
+
'@typescript-eslint/no-var-requires': 0,
|
| 46 |
+
'@typescript-eslint/ban-ts-comment': 0,
|
| 47 |
+
},
|
| 48 |
+
};
|
.gitignore
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
.idea
|
| 2 |
+
node_modules
|
| 3 |
+
build
|
| 4 |
+
.env
|
| 5 |
+
yarn-error.log
|
Dockerfile
CHANGED
|
@@ -1,5 +1,21 @@
|
|
| 1 |
-
FROM
|
| 2 |
|
|
|
|
|
|
|
| 3 |
|
| 4 |
-
|
| 5 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
FROM node:lts-slim
|
| 2 |
|
| 3 |
+
RUN apt-get update && apt-get install -y python3 build-essential \
|
| 4 |
+
&& rm -rf /var/lib/apt/lists/*
|
| 5 |
|
| 6 |
+
COPY yarn.lock package.json ./
|
| 7 |
+
|
| 8 |
+
RUN npm_config_build_from_source=true yarn install --prod
|
| 9 |
+
|
| 10 |
+
COPY src ./src
|
| 11 |
+
COPY tsconfig.json ./
|
| 12 |
+
|
| 13 |
+
ARG modelType=default
|
| 14 |
+
|
| 15 |
+
COPY ./models/$modelType ./model
|
| 16 |
+
|
| 17 |
+
RUN yarn build
|
| 18 |
+
|
| 19 |
+
EXPOSE 3000
|
| 20 |
+
|
| 21 |
+
ENTRYPOINT ["yarn", "start"]
|
NSFW-API/.gitattributes
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
*.7z filter=lfs diff=lfs merge=lfs -text
|
| 2 |
+
*.arrow filter=lfs diff=lfs merge=lfs -text
|
| 3 |
+
*.bin filter=lfs diff=lfs merge=lfs -text
|
| 4 |
+
*.bz2 filter=lfs diff=lfs merge=lfs -text
|
| 5 |
+
*.ckpt filter=lfs diff=lfs merge=lfs -text
|
| 6 |
+
*.ftz filter=lfs diff=lfs merge=lfs -text
|
| 7 |
+
*.gz filter=lfs diff=lfs merge=lfs -text
|
| 8 |
+
*.h5 filter=lfs diff=lfs merge=lfs -text
|
| 9 |
+
*.joblib filter=lfs diff=lfs merge=lfs -text
|
| 10 |
+
*.lfs.* filter=lfs diff=lfs merge=lfs -text
|
| 11 |
+
*.mlmodel filter=lfs diff=lfs merge=lfs -text
|
| 12 |
+
*.model filter=lfs diff=lfs merge=lfs -text
|
| 13 |
+
*.msgpack filter=lfs diff=lfs merge=lfs -text
|
| 14 |
+
*.npy filter=lfs diff=lfs merge=lfs -text
|
| 15 |
+
*.npz filter=lfs diff=lfs merge=lfs -text
|
| 16 |
+
*.onnx filter=lfs diff=lfs merge=lfs -text
|
| 17 |
+
*.ot filter=lfs diff=lfs merge=lfs -text
|
| 18 |
+
*.parquet filter=lfs diff=lfs merge=lfs -text
|
| 19 |
+
*.pb filter=lfs diff=lfs merge=lfs -text
|
| 20 |
+
*.pickle filter=lfs diff=lfs merge=lfs -text
|
| 21 |
+
*.pkl filter=lfs diff=lfs merge=lfs -text
|
| 22 |
+
*.pt filter=lfs diff=lfs merge=lfs -text
|
| 23 |
+
*.pth filter=lfs diff=lfs merge=lfs -text
|
| 24 |
+
*.rar filter=lfs diff=lfs merge=lfs -text
|
| 25 |
+
*.safetensors filter=lfs diff=lfs merge=lfs -text
|
| 26 |
+
saved_model/**/* filter=lfs diff=lfs merge=lfs -text
|
| 27 |
+
*.tar.* filter=lfs diff=lfs merge=lfs -text
|
| 28 |
+
*.tar filter=lfs diff=lfs merge=lfs -text
|
| 29 |
+
*.tflite filter=lfs diff=lfs merge=lfs -text
|
| 30 |
+
*.tgz filter=lfs diff=lfs merge=lfs -text
|
| 31 |
+
*.wasm filter=lfs diff=lfs merge=lfs -text
|
| 32 |
+
*.xz filter=lfs diff=lfs merge=lfs -text
|
| 33 |
+
*.zip filter=lfs diff=lfs merge=lfs -text
|
| 34 |
+
*.zst filter=lfs diff=lfs merge=lfs -text
|
| 35 |
+
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
NSFW-API/Dockerfile
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
FROM ghcr.io/arnidan/nsfw-api:latest
|
| 2 |
+
|
| 3 |
+
|
| 4 |
+
# 暴露7860端口
|
| 5 |
+
EXPOSE 7860
|
NSFW-API/README.md
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
---
|
| 2 |
+
title: NSFW API
|
| 3 |
+
emoji: 😻
|
| 4 |
+
colorFrom: red
|
| 5 |
+
colorTo: purple
|
| 6 |
+
sdk: docker
|
| 7 |
+
pinned: false
|
| 8 |
+
license: mit
|
| 9 |
+
---
|
| 10 |
+
|
| 11 |
+
Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
|
README.md
CHANGED
|
@@ -1,11 +1,122 @@
|
|
| 1 |
-
|
| 2 |
-
|
| 3 |
-
|
| 4 |
-
|
| 5 |
-
|
| 6 |
-
|
| 7 |
-
|
| 8 |
-
|
| 9 |
-
|
| 10 |
-
|
| 11 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# NSFW API
|
| 2 |
+
|
| 3 |
+
[](https://github.com/arnidan/nsfw-api/actions/workflows/build.yml)
|
| 4 |
+
[](https://www.typescriptlang.org/)
|
| 5 |
+
|
| 6 |
+
Wrapper around [NSFWJS](https://github.com/infinitered/nsfwjs) to provide API.
|
| 7 |
+
|
| 8 |
+
## Usage
|
| 9 |
+
|
| 10 |
+
### Docker
|
| 11 |
+
|
| 12 |
+
There are docker images bundled with models v3 from https://github.com/gantman/nsfw_model:
|
| 13 |
+
|
| 14 |
+
* `ghcr.io/arnidan/nsfw-api:latest` - bundled with TensorflowJS 299x299 Image Model (better detection, as for me)
|
| 15 |
+
* `ghcr.io/arnidan/nsfw-api:latest-min` - bundled with TensorflowJS Quantized 299x299 Image Model (see [#39](https://github.com/arnidan/nsfw-api/issues/49))
|
| 16 |
+
|
| 17 |
+
Each image is available for `linux/amd64` and `linux/arm64` platforms.
|
| 18 |
+
|
| 19 |
+
```
|
| 20 |
+
docker run -p 3000:3000 ghcr.io/arnidan/nsfw-api:latest
|
| 21 |
+
```
|
| 22 |
+
|
| 23 |
+
<details>
|
| 24 |
+
<summary>docker-compose.yml example</summary>
|
| 25 |
+
|
| 26 |
+
```yaml
|
| 27 |
+
version: "3.9"
|
| 28 |
+
|
| 29 |
+
services:
|
| 30 |
+
nsfw-api:
|
| 31 |
+
image: "ghcr.io/arnidan/nsfw-api:latest"
|
| 32 |
+
ports:
|
| 33 |
+
- "3000:3000"
|
| 34 |
+
restart: always
|
| 35 |
+
```
|
| 36 |
+
|
| 37 |
+
</details>
|
| 38 |
+
|
| 39 |
+
### Manual deploy
|
| 40 |
+
|
| 41 |
+
1. Clone the repo
|
| 42 |
+
2. Download and unpack model from [models repo](https://github.com/gantman/nsfw_model) to `model` folder
|
| 43 |
+
3. `yarn`
|
| 44 |
+
4. `yarn build`
|
| 45 |
+
5. `yarn start`
|
| 46 |
+
|
| 47 |
+
Now app started on port 3000.
|
| 48 |
+
|
| 49 |
+
## API Methods
|
| 50 |
+
|
| 51 |
+
- POST /classify
|
| 52 |
+
- POST /classify-many
|
| 53 |
+
|
| 54 |
+
### POST /classify
|
| 55 |
+
|
| 56 |
+
#### Example of the request
|
| 57 |
+
|
| 58 |
+
```http request
|
| 59 |
+
POST /classify HTTP/1.1
|
| 60 |
+
Content-Type: multipart/form-data
|
| 61 |
+
```
|
| 62 |
+
|
| 63 |
+
Image should be provided in `image` field.
|
| 64 |
+
|
| 65 |
+
#### Example of the response
|
| 66 |
+
|
| 67 |
+
```
|
| 68 |
+
HTTP/1.1 200 OK
|
| 69 |
+
Content-Type: application/json
|
| 70 |
+
```
|
| 71 |
+
```json
|
| 72 |
+
{
|
| 73 |
+
"porn": 0.59248286485672,
|
| 74 |
+
"sexy": 0.39802199602127075,
|
| 75 |
+
"hentai": 0.006243097595870495,
|
| 76 |
+
"neutral": 0.0031403270550072193,
|
| 77 |
+
"drawing": 0.00011181648733327165
|
| 78 |
+
}
|
| 79 |
+
```
|
| 80 |
+
|
| 81 |
+
### POST /classify-many
|
| 82 |
+
|
| 83 |
+
#### Example of the request
|
| 84 |
+
|
| 85 |
+
```http request
|
| 86 |
+
POST /classify-many HTTP/1.1
|
| 87 |
+
Content-Type: multipart/form-data
|
| 88 |
+
```
|
| 89 |
+
|
| 90 |
+
Images should be provided in `images` field.
|
| 91 |
+
|
| 92 |
+
#### Example of the response
|
| 93 |
+
|
| 94 |
+
```
|
| 95 |
+
HTTP/1.1 200 OK
|
| 96 |
+
Content-Type: application/json
|
| 97 |
+
```
|
| 98 |
+
```json
|
| 99 |
+
[
|
| 100 |
+
{
|
| 101 |
+
"porn": 0.3996206820011139,
|
| 102 |
+
"neutral": 0.388679563999176,
|
| 103 |
+
"sexy": 0.19470958411693573,
|
| 104 |
+
"hentai": 0.015063910745084286,
|
| 105 |
+
"drawing": 0.001926235854625702
|
| 106 |
+
},
|
| 107 |
+
{
|
| 108 |
+
"sexy": 0.8366416692733765,
|
| 109 |
+
"porn": 0.13645528256893158,
|
| 110 |
+
"neutral": 0.0222245492041111,
|
| 111 |
+
"hentai": 0.004213324282318354,
|
| 112 |
+
"drawing": 0.0004651622730307281
|
| 113 |
+
},
|
| 114 |
+
{
|
| 115 |
+
"sexy": 0.8017168045043945,
|
| 116 |
+
"porn": 0.1770564466714859,
|
| 117 |
+
"neutral": 0.015829339623451233,
|
| 118 |
+
"hentai": 0.005097625777125359,
|
| 119 |
+
"drawing": 0.00029983260901644826
|
| 120 |
+
}
|
| 121 |
+
]
|
| 122 |
+
```
|
models/default/group1-shard10of22
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:2304b6697ffb4e6b812461722dd44d808ec3dbd2b6d584656d802021d089637f
|
| 3 |
+
size 4194304
|
models/default/group1-shard11of22
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:c3921daff4e77b822d7d0538013dee405b06e721bd24ca32d7d842eaa33df554
|
| 3 |
+
size 4194304
|
models/default/group1-shard12of22
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:081ce576fde1d060a49f2908bbd9b2e9df1e266eb014ee79b75be8b654631bc2
|
| 3 |
+
size 4194304
|
models/default/group1-shard13of22
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:63a9eb50544d1b606b33b0b13bf8b671fd6871900a8d1e98183f9155c82d2151
|
| 3 |
+
size 4194304
|
models/default/group1-shard14of22
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:67b803099de34d091e7dc65adc8963f2a3d1e287590dcccb979becf3929b4d7f
|
| 3 |
+
size 4194304
|
models/default/group1-shard15of22
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:b3dd86d30dcb4ca5ae80df0a84045ab1c24a2143abeab1c00e4ae6afeb78edc9
|
| 3 |
+
size 4194304
|
models/default/group1-shard16of22
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:987f5eaa433a83bbbef9eb239c8a364c071c0f5d2a56e337b3eadb5b9eef8bef
|
| 3 |
+
size 4194304
|
models/default/group1-shard17of22
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:0b32181ac7995f9f6d25e48f92527b50cbbdb08727205fa577c4576f39a451a6
|
| 3 |
+
size 4194304
|
models/default/group1-shard18of22
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:38b08fd98aa97ce5b348978d9a7fd5e94354908faaad5f37ba08a44a1fb9bd03
|
| 3 |
+
size 4194304
|
models/default/group1-shard19of22
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:aad15be21400a538b2d21c66a900d73a67afd96f3979cc69d8496fe9e4dc41ce
|
| 3 |
+
size 4194304
|
models/default/group1-shard1of22
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:6f631596a54b464c305eaa32e43a009fe3cf44332001e37735ff73591b0410d7
|
| 3 |
+
size 4194304
|
models/default/group1-shard20of22
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:16ec8baa36f0050b108fd3785d40cedd493adc398eb04b726370931543f1f41a
|
| 3 |
+
size 4194304
|
models/default/group1-shard21of22
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:b146f6e84f07b7fc5379b639b7dfbe71f955b5a678e7962c079824d4013490d9
|
| 3 |
+
size 4194304
|
models/default/group1-shard22of22
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:27c141257999d4b20c4b3e3b0b8bc5b4388db4eb65e202499b27a4e0e16a103c
|
| 3 |
+
size 1363092
|
models/default/group1-shard2of22
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:c3546b791ad3167e2557d145d319c813a81ce9c9261b9a1b880e249e19880eac
|
| 3 |
+
size 4194304
|
models/default/group1-shard3of22
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:3736ceb6db2accb3e8f6e44ad0a5b0ab5f68fa4b2a8a1ee7859afd2155f069bf
|
| 3 |
+
size 4194304
|
models/default/group1-shard4of22
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:8f2b24050c3289cf68c6cae7e9a285be4d441cf69b9488cd64000100dc9b702b
|
| 3 |
+
size 4194304
|
models/default/group1-shard5of22
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:45cbfc744817cde12b4c5e105799115611b2155c6f7ed39a1e808d19456da7d6
|
| 3 |
+
size 4194304
|
models/default/group1-shard6of22
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:ba39e02d47d536b791e522bac126d3573d3fab4c58ffc2e5f5af3351c848d9fe
|
| 3 |
+
size 4194304
|
models/default/group1-shard7of22
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:529755427a9b89e68b7c171ad5208d426ebfc43b880c1c1da27d3b69c3ffc26c
|
| 3 |
+
size 4194304
|
models/default/group1-shard8of22
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:40887e59f348659ff684f18fba7345887ca6d55cadb32a2e767b483a7f7d4b03
|
| 3 |
+
size 4194304
|
models/default/group1-shard9of22
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:456c4d41b34bfeadfe9b3781cb752e44f024baa32810b0a50840b0fd2390f994
|
| 3 |
+
size 4194304
|
models/default/model.json
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:fc4dbb6255feaed52a79d80728b6fa947b014655fe44012c07b16590d7198ef7
|
| 3 |
+
size 179999
|
models/min/group1-shard1of6
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:7a3a4c075cfcaa7f0b095f55b2ba54a288c2f770e504c8537394efda9545994b
|
| 3 |
+
size 4194304
|
models/min/group1-shard2of6
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:8826da12bda0db9415aef0e6a8d1f178a0d950deab28c4e79b9da994748702a4
|
| 3 |
+
size 4194304
|
models/min/group1-shard3of6
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:4557a96cf87ceb8df6c9647b647ac9843251a4455457cb40f35c02250a63e51c
|
| 3 |
+
size 4194304
|
models/min/group1-shard4of6
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:399b99397ccd64351b59f04cdabe30000a15494c82f75c1e2ccb41c9b6338b7d
|
| 3 |
+
size 4194304
|
models/min/group1-shard5of6
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:994515b0f73407d71740301e9877511b086609dbdc7ead1b31bcb82b3397ae74
|
| 3 |
+
size 4194304
|
models/min/group1-shard6of6
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:cfb856c9d8fcefacca87b6063ed4949adf69f0372407d6edca8cec0c1694a28b
|
| 3 |
+
size 1389349
|
models/min/model.json
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:1220fc23413405858655f9c7342d5e201d9b8e0356904267d59659da71202cc3
|
| 3 |
+
size 215920
|
nsfw-api
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
Subproject commit 0155c893c2d688ae4e17aca7dce2de55e4f5602d
|
package.json
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"name": "nsfwjs-api",
|
| 3 |
+
"version": "1.0.0",
|
| 4 |
+
"main": "build/index.js",
|
| 5 |
+
"author": "Andrew",
|
| 6 |
+
"private": true,
|
| 7 |
+
"scripts": {
|
| 8 |
+
"build": "tsc -p tsconfig.json",
|
| 9 |
+
"start": "node build/index.js",
|
| 10 |
+
"start-dev": "ts-node --project ./tsconfig.json -T --emit --files src/index.ts",
|
| 11 |
+
"watch": "ts-node-dev -T src/index.ts",
|
| 12 |
+
"lint": "eslint src/ --ext .js,.ts"
|
| 13 |
+
},
|
| 14 |
+
"dependencies": {
|
| 15 |
+
"@tensorflow/tfjs-node": "^3.20.0",
|
| 16 |
+
"@types/body-parser": "^1.19.2",
|
| 17 |
+
"@types/express": "^4.17.13",
|
| 18 |
+
"@types/multer": "^1.4.7",
|
| 19 |
+
"@types/node": "^18.15.11",
|
| 20 |
+
"body-parser": "^1.20.2",
|
| 21 |
+
"express": "^4.17.1",
|
| 22 |
+
"module-alias": "^2.2.2",
|
| 23 |
+
"multer": "^1.4.4",
|
| 24 |
+
"nsfwjs": "^2.4.2",
|
| 25 |
+
"reflect-metadata": "^0.1.13",
|
| 26 |
+
"simple-ts-express-decorators": "1.3.0",
|
| 27 |
+
"typescript": "^5.0.3"
|
| 28 |
+
},
|
| 29 |
+
"devDependencies": {
|
| 30 |
+
"@typescript-eslint/eslint-plugin": "^5.57.0",
|
| 31 |
+
"@typescript-eslint/parser": "^5.57.0",
|
| 32 |
+
"eslint": "^8.37.0"
|
| 33 |
+
},
|
| 34 |
+
"_moduleAliases": {
|
| 35 |
+
"app": "./build"
|
| 36 |
+
}
|
| 37 |
+
}
|
src/NsfwImageClassifier.ts
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import * as tf from '@tensorflow/tfjs-node';
|
| 2 |
+
import * as nsfwjs from 'nsfwjs';
|
| 3 |
+
import {NSFWJS} from 'nsfwjs';
|
| 4 |
+
import {Tensor3D} from '@tensorflow/tfjs';
|
| 5 |
+
import {model as config} from 'app/config/model';
|
| 6 |
+
|
| 7 |
+
tf.enableProdMode();
|
| 8 |
+
|
| 9 |
+
export class NsfwImageClassifier {
|
| 10 |
+
#model?: NSFWJS;
|
| 11 |
+
|
| 12 |
+
async classify(imageBuffer: Buffer) {
|
| 13 |
+
const [model, image] = await Promise.all([
|
| 14 |
+
this.#getModel(),
|
| 15 |
+
tf.node.decodeImage(imageBuffer, 3),
|
| 16 |
+
]);
|
| 17 |
+
|
| 18 |
+
const predictions = await model.classify(image as Tensor3D);
|
| 19 |
+
|
| 20 |
+
image.dispose();
|
| 21 |
+
|
| 22 |
+
return this.#transformData(predictions);
|
| 23 |
+
}
|
| 24 |
+
|
| 25 |
+
async classifyMany(imagesBuffers: Buffer[]) {
|
| 26 |
+
return await Promise.all(imagesBuffers.map(buffer => this.classify(buffer)));
|
| 27 |
+
}
|
| 28 |
+
|
| 29 |
+
async #getModel(): Promise<NSFWJS> {
|
| 30 |
+
if (!this.#model) {
|
| 31 |
+
this.#model = await nsfwjs.load('file://model/', {size: config.size});
|
| 32 |
+
}
|
| 33 |
+
|
| 34 |
+
return this.#model;
|
| 35 |
+
}
|
| 36 |
+
|
| 37 |
+
#transformData(data: { className: string; probability: number }[]): Record<string, number> {
|
| 38 |
+
const result: Record<string, number> = {};
|
| 39 |
+
|
| 40 |
+
for (const item of data) {
|
| 41 |
+
result[item.className.toLowerCase()] = item.probability;
|
| 42 |
+
}
|
| 43 |
+
|
| 44 |
+
return result;
|
| 45 |
+
}
|
| 46 |
+
}
|
src/config/model.ts
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
const DEFAULT_MODEL_SIZE = 299;
|
| 2 |
+
|
| 3 |
+
export const model = {
|
| 4 |
+
size: Number(process.env.MODEL_SIZE) || DEFAULT_MODEL_SIZE,
|
| 5 |
+
};
|
src/controllers/NsfwController.ts
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import {Controller, Post} from 'simple-ts-express-decorators';
|
| 2 |
+
import multer, {memoryStorage} from 'multer';
|
| 3 |
+
import {Request, Response} from 'express';
|
| 4 |
+
import {NsfwImageClassifier} from 'app/NsfwImageClassifier';
|
| 5 |
+
|
| 6 |
+
const upload = multer({storage: memoryStorage()});
|
| 7 |
+
|
| 8 |
+
@Controller()
|
| 9 |
+
export class NsfwController {
|
| 10 |
+
classifier: NsfwImageClassifier;
|
| 11 |
+
|
| 12 |
+
constructor() {
|
| 13 |
+
this.classifier = new NsfwImageClassifier();
|
| 14 |
+
}
|
| 15 |
+
|
| 16 |
+
@Post('/classify', upload.single('image'))
|
| 17 |
+
async classify(request: Request, response: Response) {
|
| 18 |
+
if (!request.file) {
|
| 19 |
+
return response
|
| 20 |
+
.status(410)
|
| 21 |
+
.json({error: 'Specify image'});
|
| 22 |
+
}
|
| 23 |
+
|
| 24 |
+
const data = await this.classifier.classify(request.file.buffer);
|
| 25 |
+
|
| 26 |
+
return response.json(data);
|
| 27 |
+
}
|
| 28 |
+
|
| 29 |
+
@Post('/classify-many', upload.array('images', 10))
|
| 30 |
+
async classifyMany(request: Request, response: Response) {
|
| 31 |
+
if (!request.files || !request.files.length) {
|
| 32 |
+
return response
|
| 33 |
+
.status(410)
|
| 34 |
+
.json({error: 'Specify images'});
|
| 35 |
+
}
|
| 36 |
+
|
| 37 |
+
const buffers = (request.files as Express.Multer.File[]).map(file => file.buffer);
|
| 38 |
+
const data = await this.classifier.classifyMany(buffers);
|
| 39 |
+
|
| 40 |
+
return response.json(data);
|
| 41 |
+
}
|
| 42 |
+
}
|
src/index.ts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import 'reflect-metadata';
|
| 2 |
+
import 'module-alias/register';
|
| 3 |
+
|
| 4 |
+
import express from 'express';
|
| 5 |
+
import bodyParser from 'body-parser';
|
| 6 |
+
import {ControllersLoader} from 'simple-ts-express-decorators';
|
| 7 |
+
import {NsfwController} from 'app/controllers/NsfwController';
|
| 8 |
+
|
| 9 |
+
const app = express();
|
| 10 |
+
|
| 11 |
+
app.use(bodyParser.json());
|
| 12 |
+
|
| 13 |
+
new ControllersLoader({
|
| 14 |
+
controllers: [NsfwController]
|
| 15 |
+
}).load(app);
|
| 16 |
+
|
| 17 |
+
app.listen(3000);
|
tsconfig.json
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"compilerOptions": {
|
| 3 |
+
"target": "es2022",
|
| 4 |
+
"module": "commonjs",
|
| 5 |
+
"experimentalDecorators": true,
|
| 6 |
+
"emitDecoratorMetadata": true,
|
| 7 |
+
"outDir": "./build",
|
| 8 |
+
"removeComments": true,
|
| 9 |
+
"moduleResolution": "node",
|
| 10 |
+
"noFallthroughCasesInSwitch": true,
|
| 11 |
+
"noEmitOnError": true,
|
| 12 |
+
"baseUrl": ".",
|
| 13 |
+
"strict": true,
|
| 14 |
+
"strictFunctionTypes": true,
|
| 15 |
+
"strictNullChecks": true,
|
| 16 |
+
"strictPropertyInitialization": true,
|
| 17 |
+
"strictBindCallApply": true,
|
| 18 |
+
"paths": {
|
| 19 |
+
"app/*": ["./src/*"]
|
| 20 |
+
},
|
| 21 |
+
"incremental": true,
|
| 22 |
+
"types": ["reflect-metadata", "node"],
|
| 23 |
+
"allowSyntheticDefaultImports": true,
|
| 24 |
+
"esModuleInterop": true,
|
| 25 |
+
"skipLibCheck": true
|
| 26 |
+
},
|
| 27 |
+
"include": [
|
| 28 |
+
"./src/**/*.ts",
|
| 29 |
+
],
|
| 30 |
+
"exclude": [
|
| 31 |
+
"./node_modules"
|
| 32 |
+
]
|
| 33 |
+
}
|
yarn.lock
ADDED
|
Binary file (116 kB). View file
|
|
|