|
|
|
const express=require("express"); |
|
let fs = require('fs'); |
|
|
|
let app = express(); |
|
|
|
const bodyparser = require("body-parser");; |
|
|
|
app.use(bodyparser.urlencoded({extended:false})); |
|
app.use(bodyparser.json()); |
|
|
|
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']; |
|
|
|
|
|
|
|
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); |
|
|
|
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: {} |
|
}) |
|
} |
|
|
|
|
|
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); |
|
|
|
res.send({ |
|
code: 0, |
|
msg: isSafe ? '图片合规' : '图片可能存在不合规的风险,请核查', |
|
data: { |
|
isSafe, |
|
imgType, |
|
predictions, |
|
} |
|
}) |
|
}); |
|
} catch (error) { |
|
res.send({ |
|
code: -9, |
|
msg: '图片核查失败,请重试', |
|
data: {} |
|
}) |
|
} |
|
}); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
app.listen(7860,()=>{ |
|
console.log("图片鉴黄服务器启动成功!port:7860"); |
|
}); |