更新API

This commit is contained in:
dengtongyu committed 2020-11-23 15:30:21 +08:00
1 parent 16769a8726
commit ece1e8065c
16 files changed
+161 -1

No files matched your search

+1
View File
@@ -12,3 +12,4 @@ run/
*.un~
typings/
.nyc_output/
api-service.zip
+31
View File
@@ -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;
+41
View File
@@ -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.
+1
View File
@@ -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
+1
View File
@@ -8,4 +8,5 @@ module.exports = app => {
// router.get('/', controller.home.index);
require('./router/music')(app);
require('./router/nsfw')(app);
};
+8
View File
@@ -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);
};
+39
View File
@@ -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;
+18
View File
@@ -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;
+16
View File
@@ -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
View File
@@ -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",