更新API
This commit is contained in:
1 parent
16769a8726
commit
ece1e8065c
16 files changed
+161
-1
No files matched your search
@@ -12,3 +12,4 @@ run/
|
||||
*.un~
|
||||
typings/
|
||||
.nyc_output/
|
||||
api-service.zip
|
||||
@@ -0,0 +1,31 @@
|
||||
'use strict';
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
const nsfw = require('nsfwjs');
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
const tf = require('@tensorflow/tfjs-node');
|
||||
|
||||
class AppBootHook {
|
||||
constructor(app) {
|
||||
this.app = app;
|
||||
}
|
||||
|
||||
configWillLoad() {
|
||||
// console.log('config: ', this.app.config.uploadHost);
|
||||
}
|
||||
|
||||
async didLoad() {
|
||||
console.log('nsfwModel 加载中···');
|
||||
this.app.nsfwModel = await nsfw.load('file://./app/model/', { type: 'graph' });
|
||||
// this.app.nsfwModel = await tf.loadGraphModel('file://./app/model/model.json');
|
||||
console.log('nsfwModel 已经加州完毕!');
|
||||
}
|
||||
|
||||
// async willReady() {
|
||||
|
||||
// }
|
||||
// async didReady() {
|
||||
|
||||
// }
|
||||
}
|
||||
|
||||
module.exports = AppBootHook;
|
||||
@@ -0,0 +1,41 @@
|
||||
'use strict';
|
||||
|
||||
const Controller = require('egg').Controller;
|
||||
// const fs = require('fs');
|
||||
const respFormat = require('../util/respFormat');
|
||||
|
||||
class CheckImageController extends Controller {
|
||||
async check() {
|
||||
const { ctx } = this;
|
||||
console.log(ctx.request.body);
|
||||
console.log('got %d files', ctx.request.files.length);
|
||||
|
||||
try {
|
||||
|
||||
const filesResult = [];
|
||||
// 遍历处理多个文件
|
||||
for (const file of ctx.request.files) {
|
||||
console.log('field: ' + file.fieldname);
|
||||
console.log('filename: ' + file.filename);
|
||||
console.log('encoding: ' + file.encoding);
|
||||
console.log('mime: ' + file.mime);
|
||||
console.log('tmp filepath: ' + file.filepath);
|
||||
const contentCheck = await ctx.service.indecentContentCheck.contentCheck(file);
|
||||
console.log('contentCheck检测结果: ', contentCheck);
|
||||
const item = {
|
||||
check: contentCheck,
|
||||
filename: file.filename,
|
||||
};
|
||||
filesResult.push(item);
|
||||
}
|
||||
|
||||
ctx.body = respFormat({ data: filesResult });
|
||||
await ctx.cleanupRequestFiles();
|
||||
} finally {
|
||||
// 需要删除临时文件
|
||||
await ctx.cleanupRequestFiles();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = CheckImageController;
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1 @@
|
||||
{"format": "graph-model", "generatedBy": "2.1.0", "convertedBy": "TensorFlow.js Converter v1.5.2", "userDefinedMetadata": {"signature": {"inputs": {"input:0": {"name": "input:0", "dtype": "DT_FLOAT", "tensorShape": {"dim": [{"size": "-1"}, {"size": "224"}, {"size": "224"}, {"size": "3"}]}}}, "outputs": {"Identity:0": {"name": "Identity:0", "dtype": "DT_FLOAT", "tensorShape": {"dim": [{"size": "-1"}, {"size": "5"}]}}}}}, "modelTopology": {"node": [{"name": "input", "op": "Placeholder", "attr": {"shape": {"shape": {"dim": [{"size": "-1"}, {"size": "224"}, {"size": "224"}, {"size": "3"}]}}, "dtype": {"type": "DT_FLOAT"}}}, {"name": "StatefulPartitionedCall/sequential/keras_layer/StatefulPartitionedCall/StatefulPartitionedCall/StatefulPartitionedCall/predict/MobilenetV2/Conv/Conv2D_weights", "op": "Const", "attr": {"value": {"tensor": {"dtype": "DT_FLOAT", "tensorShape": {"dim": [{"size": "3"}, {"size": "3"}, {"size": "3"}, {"size": "48"}]}}}, "dtype": {"type": "DT_FLOAT"}}}, {"name": "StatefulPartitionedCall/sequential/keras_layer/StatefulPartitionedCall/StatefulPartitionedCall/StatefulPartitionedCall/predict/MobilenetV2/expanded_conv_14/expand/Conv2D_bn_offset", "op": "Const", "attr": {"value": {"tensor": {"dtype": "DT_FLOAT", "tensorShape": {"dim": [{"size": "1344"}]}}}, "dtype": {"type": "DT_FLOAT"}}}, {"name": "StatefulPartitionedCall/sequential/keras_layer/StatefulPartitionedCall/StatefulPartitionedCall/StatefulPartitionedCall/predict/MobilenetV2/Conv/Conv2D_bn_offset", "op": "Const", "attr": {"value": {"tensor": {"dtype": "DT_FLOAT", "tensorShape": {"dim": [{"size": "48"}]}}}, "dtype": {"type": "DT_FLOAT"}}}, {"name": "StatefulPartitionedCall/sequential/keras_layer/StatefulPartitionedCall/StatefulPartitionedCall/StatefulPartitionedCall/predict/MobilenetV2/expanded_conv/depthwise/depthwise_weights", "op": "Const", "attr": {"value": {"tensor": {"dtype": "DT_FLOAT", "tensorShape": {"dim": [{"size": "3"}, {"size": "3"}, {"size": "48"}, {"size": "1"}]}}}, "dtype": {"type": "DT_FLOAT"}}}, {"name": "StatefulPartitionedCall/sequential/keras_layer/StatefulPartitionedCall/StatefulPartitionedCall/StatefulPartitionedCall/predict/MobilenetV2/expanded_conv/depthwise/depthwise_bn_offset", "op": "Const", "attr": {"value": {"tensor": {"dtype": "DT_FLOAT", "tensorShape": {"dim": [{"size": "48"}]}}}, "dtype": {"type": "DT_FLOAT"}}}, {"name": "StatefulPartitionedCall/sequential/keras_layer/StatefulPartitionedCall/StatefulPartitionedCall/StatefulPartitionedCall/predict/MobilenetV2/expanded_conv/project/Conv2D_weights", "op": "Const", "attr": {"value": {"tensor": {"dtype": "DT_FLOAT", "tensorShape": {"dim": [{"size": "1"}, {"size": "1"}, {"size": "48"}, {"size": "24"}]}}}, "dtype": {"type": "DT_FLOAT"}}}, {"name": "StatefulPartitionedCall/sequential/keras_layer/StatefulPartitionedCall/StatefulPartitionedCall/StatefulPartitionedCall/predict/MobilenetV2/expanded_conv/project/Conv2D_bn_offset", "op": "Const", "attr": {"value": {"tensor": {"dtype": "DT_FLOAT", "tensorShape": {"dim": [{"size": "24"}]}}}, "dtype": {"type": "DT_FLOAT"}}}, {"name": "StatefulPartitionedCall/sequential/keras_layer/StatefulPartitionedCall/StatefulPartitionedCall/StatefulPartitionedCall/predict/MobilenetV2/expanded_conv_1/expand/Conv2D_weights", "op": "Const", "attr": {"value": {"tensor": {"dtype": "DT_FLOAT", "tensorShape": {"dim": [{"size": "1"}, {"size": "1"}, {"size": "24"}, {"size": "144"}]}}}, "dtype": {"type": "DT_FLOAT"}}}, {"name": "StatefulPartitionedCall/sequential/keras_layer/StatefulPartitionedCall/StatefulPartitionedCall/StatefulPartitionedCall/predict/MobilenetV2/expanded_conv_14/depthwise/depthwise_weights", "op": "Const", "attr": {"value": {"tensor": {"dtype": "DT_FLOAT", "tensorShape": {"dim": [{"size": "3"}, {"size": "3"}, {"size": "1344"}, {"size": "1"}]}}}, "dtype": {"type": "DT_FLOAT"}}}, {"name": "StatefulPartitionedCall/sequential/keras_layer/StatefulPartitionedCall/StatefulPartitionedCall/StatefulPartitionedCall/predict/MobilenetV2/expanded_conv_1/expand/Conv2D_bn_offset", "op": "Const", "attr": {"value": {"tensor": {"dtype": "DT_FLOAT", "tensorShape": {"dim": [{"size": "144"}]}}}, "dtype": {"type": "DT_FLOAT"}}}, {"name": "StatefulPartitionedCall/sequential/keras_layer/StatefulPartitionedCall/StatefulPartitionedCall/StatefulPartitionedCall/predict/MobilenetV2/expanded_conv_1/depthwise/depthwise_weights", "op": "Const", "attr": {"value": {"tensor": {"dtype": "DT_FLOAT", "tensorShape": {"dim": [{"size": "3"}, {"size": "3"}, {"size": "144"}, {"size": "1"}]}}}, "dtype": {"type": "DT_FLOAT"}}}, {"name": "StatefulPartitionedCall/sequential/keras_layer/StatefulPartitionedCall/StatefulPartitionedCall/StatefulPartitionedCall/predict/MobilenetV2/expanded_conv_1/depthwise/depthwise_bn_offset", "op": "Const", "attr": {"value": {"tensor": {"dtype": "DT_FLOAT", "tensorShape": {"dim": [{"size": "144"}]}}}, "dtype": {"type": "DT_FLOAT"}}}, {"name": "StatefulPartitionedCall/sequential/keras_layer/StatefulPartitionedCall/StatefulPartitionedCall/Line truncated
|
||||
@@ -8,4 +8,5 @@ module.exports = app => {
|
||||
// router.get('/', controller.home.index);
|
||||
|
||||
require('./router/music')(app);
|
||||
require('./router/nsfw')(app);
|
||||
};
|
||||
@@ -0,0 +1,8 @@
|
||||
'use strict';
|
||||
|
||||
module.exports = app => {
|
||||
const { router, controller } = app;
|
||||
// router.get(neteasecloud, controller.music.neteasecloud);
|
||||
|
||||
router.post('/nsfw/images/check', controller.checkImage.check);
|
||||
};
|
||||
@@ -0,0 +1,39 @@
|
||||
'use strict';
|
||||
|
||||
const Service = require('egg').Service;
|
||||
const tf = require('@tensorflow/tfjs-node');
|
||||
// const jpeg = require('jpeg-js');
|
||||
const { Image } = require('image-js');
|
||||
const contentBoundary = require('../util/contentBoundary');
|
||||
|
||||
const convert = async img => {
|
||||
// const image = await jpeg.decode(img, true);
|
||||
const image = await Image.load(img.filepath);
|
||||
// console.log('jpeg.decode:', image);
|
||||
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');
|
||||
};
|
||||
|
||||
|
||||
class ContentCheckService extends Service {
|
||||
async contentCheck(img) {
|
||||
try {
|
||||
const image = await convert(img);
|
||||
const predictions = await this.app.nsfwModel.classify(image);
|
||||
console.log('Predictions: ', predictions);
|
||||
const isSafeContent = contentBoundary(predictions);
|
||||
return isSafeContent;
|
||||
} catch (err) {
|
||||
throw new Error(err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = ContentCheckService;
|
||||
@@ -0,0 +1,18 @@
|
||||
'use strict';
|
||||
|
||||
const safeContent = [ 'Drawing', 'Neutral' ];
|
||||
|
||||
const contentBoundary = predictions => {
|
||||
let safeProbability = 0;
|
||||
for (let index = 0; index < predictions.length; index++) {
|
||||
const item = predictions[index];
|
||||
const className = item.className;
|
||||
const probability = item.probability;
|
||||
if (safeContent.includes(className)) {
|
||||
safeProbability += probability;
|
||||
}
|
||||
}
|
||||
return safeProbability > 0.5;
|
||||
};
|
||||
|
||||
module.exports = contentBoundary;
|
||||
@@ -23,6 +23,22 @@ module.exports = appInfo => {
|
||||
// myAppName: 'egg',
|
||||
};
|
||||
|
||||
config.multipart = {
|
||||
mode: 'file',
|
||||
fieldNameSize: 100,
|
||||
fieldSize: '100kb',
|
||||
fields: 9,
|
||||
fileSize: '10mb',
|
||||
files: 9,
|
||||
whitelist: [ '.jpg', '.jpeg', '.png', '.gif', '.webp', '.bmp' ],
|
||||
};
|
||||
|
||||
config.security = {
|
||||
csrf: {
|
||||
enable: false,
|
||||
},
|
||||
};
|
||||
|
||||
config.onerror = {
|
||||
all(error, ctx) {
|
||||
ctx.body = { errcode: 500, desc: error, result: false, data: null };
|
||||
|
||||
+5
-1
@@ -8,7 +8,11 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"egg": "^2.15.1",
|
||||
"egg-scripts": "^2.11.0"
|
||||
"egg-scripts": "^2.11.0",
|
||||
"@tensorflow/tfjs": "^2.7.0",
|
||||
"@tensorflow/tfjs-node": "^2.7.0",
|
||||
"image-js": "^0.30.3",
|
||||
"nsfwjs": "^2.3.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"autod": "^3.0.1",
|
||||
|
||||
Reference in new issue
Block a user