NSFW / app.js
xwwww's picture
Update app.js
12b037e
// 导入experss模块
const express=require("express");
let fs = require('fs');
// 创建服务器对象
let app = express();
// 导入body-parser插件
const bodyparser = require("body-parser");;
// 配置body-parser模块
app.use(bodyparser.urlencoded({extended:false}));
app.use(bodyparser.json());
// 导入系统模块path
const path = require("path");
const afs = require('fs-extra');
let multiparty = require('multiparty');
let imgJS = require("image-js");
const nsfw = require('nsfwjs');
const tf = require('@tensorflow/tfjs-node');
const safeContent = ['Drawing', 'Neutral']; // 设置图片内容安全的类型
// https://github.com/alex000kim/nsfw_data_scraper
let imgTypeoObj = {
Drawing: '艺术性的',
Neutral: '中性的',
Sexy: '性感的',
Porn: '色情的',
Hentai: '变态的',
};
//转换图片格式
const convert = async file => {
const image = await imgJS.Image.load(file.path);
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');
};
// 初始化模型
let model;
(async function() {
model = await nsfw.load('file://./web_model/', {
type: 'graph'
});
})();
const isSafeContent = predictions => {
let safeProbability = 0;
let imgTypeValArr = [];
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;
};
}
imgTypeValArr = predictions.sort((a, b) => b.probability - a.probability);
// console.log('imgTypeValArr:', imgTypeValArr);
let myimgType = '';
if (imgTypeValArr.length && imgTypeValArr[0]) {
myimgType = imgTypeoObj[imgTypeValArr[0].className];
}
return {
isSafe: safeProbability > 0.5,
imgType: myimgType
};
};
app.post('/checkImg',async (req, res) => {
try {
// 删除指定文件夹下面的所有文件或文件夹
try {
await afs.emptyDirSync('./tempImgs');
console.log('清空tempImgs成功');
} catch (error) {
console.log('清空tempImgs失败');
}
let form = new multiparty.Form();
// 设置文件存储路径,以当前编辑的文件为相对路径
form.uploadDir = './tempImgs';
form.parse(req, async (err, fields, files) => {
if (!files || !files.file[0]) {
return res.send({
code: -1,
msg: '请上传file图片资源(form-data格式)',
data: {}
})
}
// console.log('files.file[0]:', files.file[0]);
// 图片最大尺寸
if (files.file[0].size > 1024 * 1024 * 3) {
return res.send({
code: -2,
msg: '被检测图片最大3M',
data: {}
})
};
// 支持的图片类型
let imgReg = /\S+\.(png|jpeg|jpg)$/g;
let originImgName = files.file[0].originalFilename || files.file[0].path;
if (!imgReg.test(originImgName)) {
return res.send({
code: -3,
msg: '仅仅支持(png、jpeg、jpg)类型图片检测',
data: {}
})
}
let img = await convert(files.file[0]);
let predictions = await model.classify(img);
const {isSafe, imgType} = isSafeContent(predictions);
// console.log('是否安全:', predictions, isSafe);
res.send({
code: 0,
msg: isSafe ? '图片合规' : '图片可能存在不合规的风险,请核查',
data: {
isSafe,
imgType,
predictions,
}
})
});
} catch (error) {
res.send({
code: -9,
msg: '图片核查失败,请重试',
data: {}
})
}
});
// {
// "code": 0,
// "msg": "图片合规",
// "data": {
// "isSafe": true,
// "imgType": "艺术性的",
// "predictions": [
// {
// "className": "Drawing",
// "probability": 0.9533441662788391
// },
// {
// "className": "Neutral",
// "probability": 0.015517046675086021
// },
// {
// "className": "Sexy",
// "probability": 0.013940851204097271
// },
// {
// "className": "Porn",
// "probability": 0.012532410211861134
// },
// {
// "className": "Hentai",
// "probability": 0.004665587563067675
// }
// ]
// }
// }
// 监听端口
app.listen(7860,()=>{
console.log("图片鉴黄服务器启动成功!port:7860");
});