feat:更新博客

This commit is contained in:
dengtongyu committed 2021-05-10 17:00:33 +08:00
1 parent 61c30d5121
commit c6c4841297
8 files changed
+176 -1

No files matched your search

@@ -0,0 +1,174 @@
---
title: node+nsfwjs搭建图片鉴黄服务
id: '363'
cover: '/images/nsfwjs_logo.jpeg'
categories:
- 分享
tags:
- node
- JavaScript
- nsfwjs
- egg
date: 2021-05-10 14:25:23
---
去年由于业务的需要,对图片的内容安全做一定的监控,简单的说就是图片鉴黄,由于早前就体验过nsfwjs的强大,所以第一时间就想到用它搭建一个图片鉴黄的服务。
翻看了github上[nsfwjs的文档](https://github.com/infinitered/nsfwjs),也有node相关的例子,原本会以为比较顺利的可以搭建完成,没想到后面还是经历了一波三折,所以也就有了这篇文章,记录一下自己的心路历程。
# 1. 服务框架搭建
node框架选择很多,我这里选了egg,因为egg集成了很多丰富的能力,简单省事,并且图片鉴黄,必定会有上传,egg也很好的集成了上传模块,简单配置一下即可放心使用。
egg的搭建以及上传的配置,就不展开了,参考官方文档即可。
[egg官方文档](https://eggjs.org/zh-cn/tutorials/index.html)
[egg上传](https://eggjs.org/zh-cn/basics/controller.html#%E8%8E%B7%E5%8F%96%E4%B8%8A%E4%BC%A0%E7%9A%84%E6%96%87%E4%BB%B6)
*node版本建议选用 14.x 及以上版本,反正我在centOS 7.x 环境下吃过亏了。*
# 2. 安装
``` sh
npm install nsfwjs
npm install @tensorflow/tfjs-node
```
# 3. 加载模型
官方文档给出的例子如下:
``` js
const axios = require('axios')
const tf = require('@tensorflow/tfjs-node')
const nsfw = require('nsfwjs')
async function fn() {
const pic = await axios.get(`link-to-picture`, {
responseType: 'arraybuffer',
})
const model = await nsfw.load() // 加载模型
const image = await tf.node.decodeImage(pic.data, 3)
const predictions = await model.classify(image)
image.dispose()
console.log(predictions)
}
fn()
```
nsfw.load() 不传参数的话,默认加载一个官方的模型地址,模型的大小大概几十上百兆,加载的过程会比较慢,如果你的服务部署到非互联网环境,那就直接启动不了了,所以把模型放到本地服务才是比较好的解决方案。
先去[nsfw_model](https://github.com/GantMan/nsfw_model/releases/tag/1.1.0)下载模型到本地(选择第一个nsfw_mobilenet_v2_140_224.zip
即可),然后解压zip包。web_model文件夹里面就是我们需要的模型了。
![web_model](/images/WX20210510-155205.png)
然后把web_model里面的文件,放到egg项目下的app/model目录下,然后在项目根目录下增加一个app.js文件,内容如下:
``` js
const tf = require('@tensorflow/tfjs-node')
const nsfw = require('nsfwjs')
class AppBootHook {
constructor(app) {
this.app = app;
}
async didLoad() {
console.log('nsfwModel 加载中···');
this.app.nsfwModel = await nsfw.load('file://./app/model/', {
type: 'graph'
});
console.log('nsfwModel 已经加州完毕!');
}
}
module.exports = AppBootHook;
```
*因为用的是graph model,所以type传'graph'*
# 4. 图片鉴定
在app.js里面已经加载了模型,并且挂载到app.nsfwModel上,所以只要在需要的时候直接使用即可。
router和controller部分直接跳过,这部分是定义一个上传的路由,然后上传成功后获取上传的图片文件。与nsfwjs的使用没有太大关系,直接跳到获取到图片文件后使用nsfwjs鉴定。
``` js
const Service = require('egg').Service;
const tf = require('@tensorflow/tfjs-node');
const {
Image
} = require('image-js'); // 官方到例子用'jpeg-js',自己测试png格式会报错,所以改用'image-js'
const contentBoundary = require('../util/contentBoundary');
const convert = async img => {
const image = await Image.load(img.filepath);
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) { // img 为上传的file
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;
```
``` js
// ../util/contentBoundary.js
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;
```
# 5. 测试
服务部署好后测试一下。
图一:
![web_model](/images/N8co23tdCfLVHk2m.png)
测试结果:
![web_model](/images/4666be0b-43c7-472b-9adc-8364d461bc4d.png)
check返回true,表示该图是安全的。
图二:
![web_model](/images/ksdfdksfdsddacsfsvsd.jpeg)
测试结果:
![web_model](/images/WX20210510-164355.png)
check返回false,表示该图是不安全,可能涉黄。
*因为我在代码里设置里Drawing和Neutral类型的才是安全的图片,Hentai、Porn、Sexy类型都认为是不安全,可能涉黄*
node+nsfwjs搭建图片鉴黄服务的大概思路就是这样,期间经历过不少曲折,就不一一赘述了。