写一只具有识别能力的图片爬虫
在网上看到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个点。
最后计算得出的结果就是就是其相似程度。
不过,这种方法有一个明显的弱点,就是他是按照颜色的全局分布来看的,无法描述颜色的局部分布和色彩所处的位置。
也就是假如一张图片以蓝色为主,内容是一片蓝天,而另外一张图片也是蓝色为主,但是内容却是妹子穿了蓝色裙子,那么这个算法也很可能认为这两张图片的相似的。
缓解这个弱点有一个方法就是利用Image
的crop
方法把图片等分,然后再分别计算其相似度,最后综合考虑。
图像指纹与汉明距离
在介绍下面其他判别相似度的方法前,先补充一些概念。第一个就是图像指纹
图像指纹和人的指纹一样,是身份的象征,而图像指纹简单点来讲,就是将图像按照一定的哈希算法,经过运算后得出的一组二进制数字。
说到这里,就可以顺带引出汉明距离的概念了。
假如一组二进制数据为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的buildpython
的目录下,根据自己的情况,选择x86
,x64
下的cv2.pyd
放到你python的安装目录的
Libsite-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文件,放在了opencvsourcesdatahaarcascades
的目录下。
如果只是安装了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
对象,为什么强调这两点?
-
cv2.imread(path)
不能读取中文路径,若路径中含有中文字符,其会返回None
- 在后面的操作中,包括是切割图片(人脸部分),再进行局部哈希,比较相似度,
等等都是用
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
`对象进行绘图,框出人脸的位置。
同样,你也可以使用Image
的crop
方法把人脸部分提取出来,然后进行局部哈希,
通过上一篇文章提及的算法,比较两者的相似度。
两种操作分别在我的github中实现了,请参考我的github中face1.py
,和face2.py
两个python文件。
写一只具有识别能力的图片爬虫
我说了会应用这些算法做成以只具有识别能力的图片爬虫,然现在我也确实是在做 但考虑到作为核心的图片识别和人脸识别的部分我已经写成文章分享出来,其余部分就是想写其他爬虫一样而已。(原文:https://segmentfault.com/a/1190000004500523?_ea=630748)
本文总结了如何利用已经训练好的数据进行人脸识别,希望能帮到有需要的朋友。
- Retrofit OKHttp 教你怎么持久化管理Cookie
- 关于在数据库中分页的问题,此处以sqlServer为例
- 使用PBE方式进行加密,解密
- Java 反射基础(上)
- Oracle存储过程基本语法介绍
- web项目定时执行任务
- GnuRadio Hacking ①:使用GnuRadio+SDR破解固定码无线遥控
- Java 反射基础(下)
- jQuery.data() 的实现方式
- MyBatis学习总结——调用存储过程
- 无线宝宝wifi热点共享软件刷流量行为分析
- 解决servlet doGet() 中文乱码问题
- 代码实验室--带你一步步理解使用 ConstraintLayout
- php绘制图片验证码
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- 提高数据分析工作效率-Sublime如何设置默认打开文件格式
- 使用 freemarker 制作代码生成器
- 假期闲的慌,不如做一点SQL基础练习吧
- Vue 中全局过滤器的使用
- 游戏-CSP201712-2-Java
- Mybatis generator 生成 Mapper 方法不全
- Vue 中使用 JQuery 插件不起作用
- 公共钥匙盒-CSP数组排序练习
- 疫情这么严重,还不待家里学Numpy和Pandas?
- CSP-201812-2-小明放学-Java
- Mac怎么设置docker国内镜像源来加速下载?
- 适合数据分析面试笔试入门的编程题
- 女同事问狗哥什么是线程池的阻塞队列?
- BAT某厂数据分析终面面经
- 通过常见的业务掌握SQL高级功能