写一只具有识别能力的图片爬虫

在网上看到python做图像识别的相关文章后,真心感觉python的功能实在太强大,因此将这些文章总结一下,建立一下自己的知识体系。

当然了,图像识别这个话题作为计算机科学的一个分支,不可能就在本文简单几句就说清,所以本文只作基本算法的科普向。如有错误,请多包涵和多多指教。

本文参考文章和图片来源

wbj0110的文章 http://soledede.iteye.com/blog/1940910 赖勇浩的文章 http://blog.csdn.net/gzlaiyonghao/article/details/2325027

以及本篇文章所用的代码都会在底下给出github地址:

https://github.com/MashiMaroLjc/Learn-to-identify-similar-images

本文参考文章:

http://blog.csdn.net/u012162613/article/details/43523507

安装相关库

python用作图像处理的相关库主要有openCV(C++编写,提供了python语言的接口),PIL,但由于PIL很早就停了,所以不支持python3.x,所以建议使用基于PIL的pillow,本文也是在python3.4和pillow的环境下进行实验。

pillow下载地址 https://pypi.python.org/pypi/Pillow PIL的下载地址 https://pypi.python.org/pypi/Pillow openCV的官网 http://opencv.org/

至于opencv,在做人脸识别的时候会用到,但本文不会涉及到,在本专栏的后续中会谈及openCV的人脸识别和基于此的python图片爬虫,有兴趣的朋友可以关注本专栏。

相关背景

要识别两张相似图像,我们从感性上来谈是怎么样的一个过程?首先我们会区分这两张相片的类型,例如是风景照,还是人物照。风景照中,是沙漠还是海洋,人物照中,两个人是不是都是国字脸,还是瓜子脸(还是倒瓜子脸……哈哈……)。

那么从机器的角度来说也是这样的,先识别图像的特征,然后再相比。

很显然,在没有经过训练的计算机(即建立模型),那么计算机很难区分什么是海洋,什么是沙漠。但是计算机很容易识别到图像的像素值。

因此,在图像识别中,颜色特征是最为常用的。(其余常用的特征还有纹理特征形状特征空间关系特征等)

其中又分为

  • 直方图
  • 颜色集
  • 颜色矩
  • 聚合向量
  • 相关图

直方图计算法

这里先用直方图进行简单讲述。

先借用一下恋花蝶的图片,

从肉眼来看,这两张图片大概也有八成是相似的了。

在python中可以依靠Image对象的histogram()方法获取其直方图数据,但这个方法返回的结果是一个列表,如果想得到下图可视化数据,需要另外使用 matplotlib,这里因为主要介绍算法思路,matplotlib的使用这里不做介绍。

是的,我们可以明显的发现,两张图片的直方图是近似重合的。所以利用直方图判断两张图片的是否相似的方法就是,计算其直方图的重合程度即可。

计算方法如下:

其中gi和si是分别指两条曲线的第i个点。

最后计算得出的结果就是就是其相似程度。

不过,这种方法有一个明显的弱点,就是他是按照颜色的全局分布来看的,无法描述颜色的局部分布和色彩所处的位置。

也就是假如一张图片以蓝色为主,内容是一片蓝天,而另外一张图片也是蓝色为主,但是内容却是妹子穿了蓝色裙子,那么这个算法也很可能认为这两张图片的相似的。

缓解这个弱点有一个方法就是利用Imagecrop方法把图片等分,然后再分别计算其相似度,最后综合考虑。

图像指纹与汉明距离

在介绍下面其他判别相似度的方法前,先补充一些概念。第一个就是图像指纹

图像指纹和人的指纹一样,是身份的象征,而图像指纹简单点来讲,就是将图像按照一定的哈希算法,经过运算后得出的一组二进制数字。

说到这里,就可以顺带引出汉明距离的概念了。

假如一组二进制数据为101,另外一组为111,那么显然把第一组的第二位数据0改成1就可以变成第二组数据111,所以两组数据的汉明距离就为1

简单点说,汉明距离就是一组二进制数据变成另一组数据所需的步骤数,显然,这个数值可以衡量两张图片的差异,汉明距离越小,则代表相似度越高。汉明距离为0,即代表两张图片完全一样。

如何计算得到汉明距离,请看下面三种哈希算法

平均哈希法(aHash)

此算法是基于比较灰度图每个像素与平均值来实现的

一般步骤

  • 1.缩放图片,可利用Image对象的resize(size)改变,一般大小为8*8,64个像素值。
  • 2.转化为灰度图 转灰度图的算法。
    • 1.浮点算法:Gray=Rx0.3+Gx0.59+Bx0.11
    • 2.整数方法:Gray=(Rx30+Gx59+Bx11)/100
    • 3.移位方法:Gray =(Rx76+Gx151+Bx28)>>8;
    • 4.平均值法:Gray=(R+G+B)/3;
    • 5.仅取绿色:Gray=G;

python中,可用Image的对象的方法convert('L')直接转换为灰度图

  • 3.计算平均值:计算进行灰度处理后图片的所有像素点的平均值。
  • 4.比较像素灰度值:遍历灰度图片每一个像素,如果大于平均值记录为1,否则为0.
  • 5.得到信息指纹:组合64个bit位,顺序随意保持一致性。

最后比对两张图片的指纹,获得汉明距离即可。

感知哈希算法(pHash)

平均哈希算法过于严格,不够精确,更适合搜索缩略图,为了获得更精确的结果可以选择感知哈希算法,它采用的是DCT(离散余弦变换)来降低频率的方法

一般步骤:

  • 缩小图片:32 * 32是一个较好的大小,这样方便DCT计算
  • 转化为灰度图:把缩放后的图片转化为256阶的灰度图。(具体算法见平均哈希算法步骤)
  • 计算DCT:DCT把图片分离成分率的集合
  • 缩小DCT:DCT计算后的矩阵是32 * 32,保留左上角的8 * 8,这些代表的图片的最低频率
  • 计算平均值:计算缩小DCT后的所有像素点的平均值。
  • 进一步减小DCT:大于平均值记录为1,反之记录为0.
  • 得到信息指纹:组合64个信息位,顺序随意保持一致性。

最后比对两张图片的指纹,获得汉明距离即可。

这里给出别人的DCT的介绍和计算方法(离散余弦变换的方法)

DCT的维基百科 https://zh.wikipedia.org/wiki/%E7%A6%BB%E6%95%A3%E4%BD%99%E5%BC%A6%E5%8F%98%E6%8D%A2 luoweifu的博客 http://blog.csdn.net/luoweifu/article/details/8214959

dHash算法

相比pHash,dHash的速度要快的多,相比aHash,dHash在效率几乎相同的情况下的效果要更好,它是基于渐变实现的。

步骤:

  • 缩小图片:收缩到9*8的大小,以便它有72的像素点
  • 转化为灰度图:把缩放后的图片转化为256阶的灰度图。(具体算法见平均哈希算法步骤)
  • 计算差异值:dHash算法工作在相邻像素之间,这样每行9个像素之间产生了8个不同的差异,一共8行,则产生了64个差异值
  • 获得指纹:如果左边的像素比右边的更亮,则记录为1,否则为0. 最后比对两张图片的指纹,获得汉明距离即可。

这几种算法是识别相似图像的基础,显然,有时两图中的人相似比整体的颜色相似更重要,所以我们有时需要进行人脸识别, 然后在脸部区进行局部哈希,或者进行其他的预处理再进行哈希,这里涉及其他知识本文不作介绍。

下一次将讲述利用opencv和以训练好的模型来进行人脸识别。

网上各种首先你要有一个女朋友的系列一样,想进行人脸判断,首先要有脸, 只要能靠确定人脸的位置,那么进行两张人脸是否相似的操作便迎刃而解了。

所以本篇文章着重讲述如何利用openCV定位人脸。

安装openCV

opencv官网 http://opencv.org/

在进行下一步操作时,我们需要安装openCV,本来安装openCV的步骤跟平常安装其他模块一样,而然 由于python的历史原因(用过都懂……),弄得一点都不友好。

先说一下,python2.7的用户,可以直接在openCV的官网上直接下载,然后在openCV的build\python 的目录下,根据自己的情况,选择x86,x64下的cv2.pyd放到你python的安装目录的 \Lib\site-packages\下。

至于python3.4的用户,即有点特别。你可以在StackOverFlow找到这样( h tp://stackoverflow.com/questions/20953273/install-opencv-for-python-3-3 ) 和这样( http://stackoverflow.com/questions/7664803/setup-opencv-2-3-w-python-bindings-in-ubuntu )的答案,但我们不要这么麻烦。

进入这个网站( http://www.lfd.uci.edu/~gohlke/pythonlibs/#opencv ),下载openCV相关whl文件,例如 opencv_python-3.1.0-cp35-none-win_amd64.whl

然后再对应目录下使用pip install opencv_python-3.1.0-cp35-none-win_amd64.whl命令即可

安装完成后,可以在python的命令行下测试。

import cv2

如果没有报错的话,恭喜你安装成功。

不过无论是哪个版本的用户,在python上使用openCV都需要先安装numpy这个模块。

numpy http://www.numpy.org/

人脸识别的原理

opencv的人脸识别是基于了haar特征,关于什么叫haar特征,足以开另外一篇文章说明了,碍于篇幅,这里不做介绍。 opencv提供已经训练好的数据写成了xml文件,放在了opencv\sources\data\haarcascades的目录下。

如果只是安装了opencv_python-3.1.0-cp35-none-win_amd64.whl的,可以在我的github上,下载cvdata里面的内容 ,地址会在文章底部给出。

除了人脸识别的数据外,还有人眼,上半身,下半身……等人体特征的数据,观察xml文件的命名,不难见名知义。

接下来会介绍如何利用这个已经训练好的数据,如果仍对haar模型感兴趣,可以参考以下地址。

zouxy09的专栏 http://blog.csdn.net/zouxy09/article/details/7929570

如何使用训练好的数据

先讲关于openCV基本的一些操作。全部具体代码,请查看我的github。

读入一张图片

cv2.imread(path)

如果你用type()把其返回值的类型是numpy.ndarray

而同样,numpy.asarray(Image)返回的亦是numpy.ndarray对象,为什么强调这两点?

  1. cv2.imread(path)不能读取中文路径,若路径中含有中文字符,其会返回None
  2. 在后面的操作中,包括是切割图片(人脸部分),再进行局部哈希,比较相似度, 等等都是用Image对象进行操作,如果再用Image.open()读入图片未免显得麻烦。

所以干脆统一用Image.open()打开图片,再用numpy.asarray(Image)转化即可。

需要注意有一个不同的地方是虽然其返回的也是三维数组,但在第三维,即某个坐标下的RGB值,两个矩阵的顺序是反的,但只要另外编写一个小函数将其反转即可。

载入xml数据

face_cascade = cv2.CascadeClassifier(xml_path)

将图片灰度化

 if img.ndim == 3:
    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) else:
    gray = img 

# 如果img维度为3,说明不是灰度图,先转化为灰度图gray,如果不为3,也就是2,原图就是灰度图

img是之前读入的三维数组,虽然灰度图可以用Image对象的convert('L')完成,但由于不确定 opencv的处理方法是否和该方法一样,所以还是用opencv自己的方法进行处理比较好。

获取人脸坐标

faces = face_cascade.detectMultiScale(gray, scaleFactor=1.1, minNeighbors=3, minSize=(10,10),flags=cv2.CASCADE_SCALE_IMAGE)
  • scale_factor:被检测对象的尺度变化。尺度越大,越容易漏掉检测的对象,但检测速度加快;尺度越小,检测越细致准确,但检测速度变慢。
  • min_neighbors:数值越大,检测到对象的条件越苛刻;反之检测到对象的条件越宽松;
  • minSize:检测对象的大小

该方法返回的是一个列表,每个列表元素是长度为四的元组,分别脸部的左上角的x,y值,脸部区域的宽度和高度。

下一步操作

通过上述的方法,我们就已经获取到人脸的位置,下一步你可以通过ImageDraw`对象进行绘图,框出人脸的位置。

同样,你也可以使用Imagecrop方法把人脸部分提取出来,然后进行局部哈希, 通过上一篇文章提及的算法,比较两者的相似度。

两种操作分别在我的github中实现了,请参考我的github中face1.py,和face2.py两个python文件。

写一只具有识别能力的图片爬虫

我说了会应用这些算法做成以只具有识别能力的图片爬虫,然现在我也确实是在做 但考虑到作为核心的图片识别和人脸识别的部分我已经写成文章分享出来,其余部分就是想写其他爬虫一样而已。(原文:https://segmentfault.com/a/1190000004500523?_ea=630748)

本文总结了如何利用已经训练好的数据进行人脸识别,希望能帮到有需要的朋友。

原文发布于微信公众号 - 大数据挖掘DT数据分析(datadw)

原文发表时间:2016-05-05

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

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏开发与安全

算法:最短路径之弗洛伊德(Floyd)算法

为了能讲明白弗洛伊德(Floyd)算法的主要思想,我们先来看最简单的案例。图7-7-12的左图是一个简单的3个顶点的连通网图。 ? 我们先定义两个二维数组D[3...

3486
来自专栏alexqdjay

HashMap 多线程下死循环分析及JDK8修复

1K4
来自专栏聊聊技术

原 初学图论-Kahn拓扑排序算法(Kah

2888
来自专栏java闲聊

JDK1.8 ArrayList 源码解析

当运行 ArrayList<Integer> list = new ArrayList<>() ; ,因为它没有指定初始容量,所以它调用的是它的无参构造

1192
来自专栏后端之路

LinkedList源码解读

List中除了ArrayList我们最常用的就是LinkedList了。 LInkedList与ArrayList的最大区别在于元素的插入效率和随机访问效率 ...

19710
来自专栏Phoenix的Android之旅

Java 集合 Vector

List有三种实现,ArrayList, LinkedList, Vector, 它们的区别在于, ArrayList是非线程安全的, Vector则是线程安全...

672
来自专栏聊聊技术

原 数据结构-二叉搜索树(Binary S

2887
来自专栏刘君君

JDK8的HashMap源码学习笔记

3068
来自专栏xingoo, 一个梦想做发明家的程序员

20120918-向量实现《数据结构与算法分析》

#include <iostream> #include <list> #include <string> #include <vector> #include...

1736
来自专栏MelonTeam专栏

ArrayList源码完全分析

导语: 这里分析的ArrayList是使用的JDK1.8里面的类,AndroidSDK里面的ArrayList基本和这个一样。 分析的方式是逐个API进行解析 ...

4519

扫码关注云+社区