diff --git a/pack.sh b/pack.sh index 00a58ef..e6af79e 100755 --- a/pack.sh +++ b/pack.sh @@ -1,5 +1,6 @@ #!/bin/sh echo "正在打包..." -zip -ry -q ./hexo-blog.zip ./ -x "./node_modules/*" -x "./package-lock.json" -x "./wordpress.2020-11-13.xml" -x "./hexo-blog.zip" -x "./db.json" +zip -ry -q ./hexo-blog.zip ./ -x "./node_modules/*" -x "./package-lock.json" -x "./wordpress.2020-11-13.xml" -x "./hexo-blog.zip" -x "./db.json" -x "./.git/*" -x "./yarn.lock" echo "打包完成!" +# unzip -o ./hexo-blog.zip \ No newline at end of file diff --git a/source/_posts/node+nsfwjs搭建图片鉴黄服务.md b/source/_posts/node+nsfwjs搭建图片鉴黄服务.md new file mode 100644 index 0000000..839290d --- /dev/null +++ b/source/_posts/node+nsfwjs搭建图片鉴黄服务.md @@ -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搭建图片鉴黄服务的大概思路就是这样,期间经历过不少曲折,就不一一赘述了。 diff --git a/source/images/4666be0b-43c7-472b-9adc-8364d461bc4d.png b/source/images/4666be0b-43c7-472b-9adc-8364d461bc4d.png new file mode 100644 index 0000000..b8efb72 Binary files /dev/null and b/source/images/4666be0b-43c7-472b-9adc-8364d461bc4d.png differ diff --git a/source/images/N8co23tdCfLVHk2m.png b/source/images/N8co23tdCfLVHk2m.png new file mode 100644 index 0000000..9d41c25 Binary files /dev/null and b/source/images/N8co23tdCfLVHk2m.png differ diff --git a/source/images/WX20210510-155205.png b/source/images/WX20210510-155205.png new file mode 100644 index 0000000..0507a74 Binary files /dev/null and b/source/images/WX20210510-155205.png differ diff --git a/source/images/WX20210510-164355.png b/source/images/WX20210510-164355.png new file mode 100644 index 0000000..813e5c8 Binary files /dev/null and b/source/images/WX20210510-164355.png differ diff --git a/source/images/ksdfdksfdsddacsfsvsd.jpeg b/source/images/ksdfdksfdsddacsfsvsd.jpeg new file mode 100644 index 0000000..ba2656e Binary files /dev/null and b/source/images/ksdfdksfdsddacsfsvsd.jpeg differ diff --git a/source/images/nsfwjs_logo.jpeg b/source/images/nsfwjs_logo.jpeg new file mode 100644 index 0000000..37f1f9f Binary files /dev/null and b/source/images/nsfwjs_logo.jpeg differ