专栏首页大龄程序员的人工智能之路利用人工智能检测色情图片

利用人工智能检测色情图片

色情内容在中国一直处于严格的监管,即使这样,互联网上还是很容易就能访问到色情内容。还记得曾经的“绿坝-花季护航”软件么?由于其识别效果差、软件不稳定,最后不了了之,浪费了大量的人力和金钱。

随着计算机视觉和深度学习的发展,算法已经成熟,利用人工智能,我们能够更加精确的识别色情内容。现在有很多云服务商提供鉴黄服务,通过集成鉴黄API到产品中,就可以给产品增加色情过滤功能。这种模式对于大多数互联网产品非常有效,可以极大的降低运营风险(对于博客、图床、直播等服务提供商而言,过滤色情、暴力等内容是重中之重)。但对于一部分产品而言,这种模式存在一些不足:

  1. 鉴别图片、视频内容等必须通过网络服务进行,响应速度难以保证;
  2. 通常鉴黄服务按次或者按照流量计费,对于个人开发者而言,有成本负担;

open_nsfw

现在有个好消息,雅虎开源了其深度学习色情图片检测模型open_nsfw,项目地址: http://github.com/yahoo/open_nsfw,这里的NSFW代表Not Suitable for Work。项目提供了基于caffe的深度神经网络模型和一个python脚本,可以供测试:

python ./classify_nsfw.py \
--model_def nsfw_model/deploy.prototxt \
--pretrained_model nsfw_model/resnet_50_1by2_nsfw.caffemodel \
test_image.jpg

定义NSFW内容是非常主观的,在某个国家或地区会引起反感的内容可能在另一个地方完全没问题。所以,open_nsfw模型只关注一种类型的NSFW内容:色情图片。但需要注意的是,该模型不能解决草图、漫画、文本等内容的识别。

色情图片的判别也是非常主观的,所以该模型并不会直接给出某个图片是否色情的结果,而是给出一个概率(0-1之间的分数)。一般而言,得分小于0.2表示图像很可能是安全的,评分大于0.8则基本可判定图片属于色情图片。如果得分介于这两个值之间,则需要程序员根据需求来设定一个阀值。如果需要比较严格的过滤,设一个比较低的阀值,反之设一个比较高的阀值。

集成open_nsfw到C++程序

open_nsfw提供了一个python脚本,google了一圈,也没有找到有人将open_nsfw集成到C++代码中。好在classify_nsfw.py这个脚本比较简单,而caffe提供了C++的例子cpp_classification,结合这两部分的代码,我编写了一个C++的鉴黄程序,源码参考:https://gitee.com/mogoweb/dpexamples。

当然看似简单的代码翻译工作,遇到的坑也不少,下面就总结一下C++代码中需要注意的地方。

图片预处理

在classify_nsfw.py中,编写了一个resize_image函数来处理图片缩放,没有采用caffe内置的图片缩放程序。代码注释中解释是因为训练时使用了这个缩放算法,所以为了达到最好的效果,测试中也需要采用该缩放算法。随后又对图片进行了一次裁剪,因为调用resize_image缩放为256x256大小,而模型接受的size为224x224。

img_data_rs = resize_image(pimg, sz=(256, 256))
image = caffe.io.load_image(StringIO(img_data_rs))H, W, _ = image.shape
_, _, h, w = caffe_net.blobs['data'].data.shape
h_off = max((H - h) / 2, 0)
w_off = max((W - w) / 2, 0)
crop = image[h_off:h_off + h, w_off:w_off + w, :]

在C++代码中,我使用了caffe中提供的opencv方法处理这个步骤:

cv::Mat img = ReadImageToCVMat(file, 256, 256);
...
// crop image
cv::Size size = sample.size();
int H = size.height;
int W = size.width;
int h = input_geometry_.height;
int w = input_geometry_.width;int h_off = std::max((H - h) / 2, 0);
int w_off = std::max((W - w) / 2, 0);
sample(cv::Rect(w_off, h_off, w, h)).copyTo(sample_resized);

数据预处理

在classify_nsfw.py中,我们可以看到这样一段代码:

caffe_transformer = caffe.io.Transformer({'data': nsfw_net.blobs['data'].data.shape})
# move image channels to outermost
caffe_transformer.set_transpose('data', (2, 0, 1))
# subtract the dataset-mean value in each channel
caffe_transformer.set_mean('data', np.array([104, 117, 123]))
# rescale from [0, 1] to [0, 255]
caffe_transformer.set_raw_scale('data', 255)
# swap channels from RGB to BGR
caffe_transformer.set_channel_swap('data', (2, 1, 0))

这段代码其实是对数据做某种变换,将读入的数据转换为caffe模型能够接受的格式。

caffe_transformer.set_transpose('data', (2, 0, 1))

该语句困扰了我好长时间,不知道在C++程序中该如何处理。后查网络资料,才了解到因为caffe.io读取的图像为HWC(H:高,W:宽,C:颜色)矩阵,而caffe模型需要(CHW)格式,所以需要对矩阵做一个变换,将颜色值维度提前到最前面。(2, 0, 1)的含义就是将原来数据的第2, 0, 1列按照新的顺序重新排列。

caffe_transformer.set_raw_scale('data', 255)

这个处理是因为使用caffe.io读取的颜色值归一化为0~1之间的数,而caffe模型接受的是一个字节的整数,范围0~255,所以需要进行一个放大。

在C++代码中,由于采用了opencv进行图像处理,不需要上面两步的变换处理。

caffe_transformer.set_mean('data', np.array([104, 117, 123]))

在很多示例中,均值通常从均值文件中加载,这里直接给了一个固定值。所谓均值,可以理解为数据的算术平均值。通常输入数据减去均值,是为了减少奇异值(异常的值,比平均值大很多或小很多的值)的影响。这里是一个三元组,分别代表RGB通道上的均值。

对应的C++代码如下:

cv::Mat sample_normalized;
cv::subtract(sample_float, mean_, sample_normalized);

接下来的代码是RGB转BGR,这个比较容易理解。

caffe_transformer.set_channel_swap('data', (2, 1, 0))

查看了一些caffe的C++例子,均没有这个步骤,可能cv::Mat中已经能够正确判断出RGB和BGR,所以代码中没有增加这一步骤。

对比测试

这个程序是否能够如愿工作呢?让我们找一些图片测试一下。考虑到内容审查,这里进行测试的图片均不是严格意义上的色情图片,只是裸露程度不同。下面使用C++程序和open_nsfw python脚本测试的结果进行对比。

C++ : 0.6122

python: 0.875480949879

C++ : 0.2536

python: 0.0773676931858

C++ : 0.6319

python: 0.782215833664

C++ : 0.0914

python: 0.0774072110653

C++ : 0.0073

python: 3.04092318402e-05

从结果可以看出,使用C++程序进行测试,结果基本符合预期,但是和python版本还是有一些差距,猜测问题可能在于对图片进行缩放采用的算法不同,如果要获得好的结果,训练和测试阶段对数据的预处理需要一致。另外一个可能是没有RGB到BGR的转换,这个还需要再验证。

如果你有兴趣,可以找一些尺度更大的图片测试,看看是不是能够正确的识别出来。

本文分享自微信公众号 - 云水木石(ourpoeticlife),作者:云水木石

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2018-04-09

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 从人工智能鉴黄模型,尝试TensorRT优化

    随着互联网的快速发展,越来越多的图片和视频出现在网络,特别是UCG产品,激发人们上传图片和视频的热情,比如微信每天上传的图片就高达10亿多张。每个人都可以上传,...

    云水木石
  • 一场疫情,炸出了退休的COBOL程序员

    COBOL编程语言,估计大多数程序员从没听说过,我这样的编程老司机,也是只闻其名,从未一睹芳容。出门问了问度娘,答案如下:

    云水木石
  • AI.未来

    轰轰烈烈的双十一购物狂欢终于过去了,你的荷包是不是轻了好几吨?不知道你有没有被各种优惠、满减整的头晕脑胀,反正我是晕了。想想现在都已经是人工智能时代了,一二十年...

    云水木石
  • 【java并发编程实战4】偏向锁-轻量锁-重量锁的那点秘密(synchronize实现原理)synchronized自旋锁偏向锁轻量锁重量锁小结

    在多线程并发编程中,synchronized一直都是元老级别的角色,人们都通常称呼它为重量锁,但是在jdk1.6版本之后,jdk就对synchronized做了...

    yukong
  • 【PAT乙级】德才论

    版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。 ...

    喜欢ctrl的cxk
  • forEach遍历数组对象且去重

    这里要注意的是:function()里面的参数第一个是value ,第二个是下标(index),第三个是要便利的数组;

    半指温柔乐
  • 不要再问我 in,exists 走不走索引了...

    最近,有一个业务需求,给我一份数据 A ,把它在数据库 B 中存在,而又比 A 多出的部分算出来。由于数据比较杂乱,我这里简化模型。

    烟雨星空
  • 【刘文彬】EOS技术研究:合约与数据库交互

    原文链接:醒者呆的博客园,https://www.cnblogs.com/Evsward/p/multi_index.html

    圆方圆学院
  • 【C语言简单说】二十一:双重指针基础 (完结)

    其实后面这两节我是用我几年前写的教程复制过来的=。=。。 ’ – ’ ) ( 3 )╱~~ 如有错误,请留言提醒哈~~~尴尬的笑=。=

    公众号 碧油鸡
  • 还在用if else?策略模式了解一下!

    小编在公司负责的就是订单取消业务,老系统中各种类型订单取消都是通过if else 判断不同的订单类型进行不同的逻辑。在经历老系统的折磨和产品需求的不断变更,...

    cxuan

扫码关注云+社区

领取腾讯云代金券