寫一隻具有識別能力的圖片爬蟲
在網上看到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)
本文總結了如何利用已經訓練好的資料進行人臉識別,希望能幫到有需要的朋友。