[計算機視覺]基於內容的圖像搜索實現


圖像搜索引擎一般有三種實現方式:

(1)Search By Metadata,這種方式不會考慮圖片本身內容(圖片包含物體,以及圖像像素分布等),純粹根據圖像標簽來進行檢索。如果某個網頁中有一張賽馬的圖片,並且網頁文本內容中包含“賽馬”(或者相關詞匯)的文字,當用戶搜索“賽馬”、“馬”、“horse”等關鍵字時,搜索引擎就會把這張圖當作檢索結果返回給用戶。換句話說,此時的圖像搜索引擎干的事情跟普通搜索引擎差不多,匹配關鍵詞,並將對應圖片返回給用戶。這種工作方式的優點是速度快,在普通搜索引擎的技術基礎之上很容易改進去實現。缺點也很明顯,它完全依賴於描述圖片的文字(標簽),如果描述圖片的文字不對或者相關性不大時,搜索准確性可想而知,比如我這篇博客中如果插入一張“貓”的照片,但是整篇博客文章對“貓”只字不提,那么基於Search By Metadata的搜索引擎很難找到博客中貓的圖片。有一類圖片分享網站要求用戶在上傳圖片時,人工用幾個詞匯描述圖片中有什么(標簽),便於后面基於Metadata的搜索。當然也不排除一些基於深度學習的圖片分類自動打標簽的方式。

(2)Search By Example,這種方式考慮圖片本身內容(圖片包含物體,以及圖片像素分布等等),用戶輸入圖片,搜索引擎根據圖片內容,返回與該圖片相似的圖片結果。這種方式相比Search By Metadata要復雜一些,當然如果實現恰當,准確性也更能得到保障。如果用戶上傳一張包含“馬”的圖片,那么搜索引擎會返回包含馬的其他圖片,或者,當用戶上傳一張“沙灘”的風景圖片,搜索引擎會返回一堆海邊沙灘的圖片,這些圖片包含類似的內容:沙灘、天空、大海、游客。Search By Example的方式就是我們熟知的“以圖搜圖”,通過一張圖找出網上相似的其他圖片。實現方式有很多,有基於深度學習的方式,也有傳統的圖像分析算法,后者也是本篇文章后面會詳細介紹的部分。

(3)第三種就是前兩種的結合體,具體就不多說了,技術互補,到達最佳效果。

本篇文章主要介紹利用傳統圖像分析算法如何實現Search By Example的搜索引擎(以圖搜索)。

源碼地址:https://github.com/sherlockchou86/cbir-image-search

 

圖像指紋

人類有指紋,通過指紋對比可以判斷兩個人是否是同一個人,換句話說,指紋可以當作人類獨一無二的標識符。圖片也有類似的概念,成千上萬的圖片中,每張都有自己的特點,通過某種方式提取它的特征,可以作為圖像對比時的關鍵依據。

圖像指紋提取通常稱為“特征提取”,在程序代碼中,提取到的圖像特征通常用一個多維向量或者一串64/126位二進制數字表示。特征提取的方式有很多種(后面會介紹三類),不管通過哪種方式提取圖像特征,都應該保證:通過特征對比,我們可以反推原始圖片的相似度。

 

通過圖像特征評價圖片相似度

假設我們已經為兩張圖片提取到了合適的圖像特征,如何操作我們可以通過特征來反應原始圖片的相似度呢?一般我們計算兩個特征之前的距離,通過距離反映原始圖像的相似度,如果距離越近,兩張原始圖片相似度更高,反之亦然。

計算距離的方式有很多,需要根據特征提取的方式來選擇不同的距離計算方法,通常有以下幾個距離計算方法:

(1)歐氏距離。就是歐幾里德距離,這個距離應該是我們最熟悉的,在小學就接觸了。我們在計算直線上兩點的距離就是指歐式距離,上初中時計算平面坐標系中兩點的直線距離也是指歐式距離,上高中時計算三維坐標系中兩點的直線距離同樣指歐式距離,四維、五維甚至更高維都是這種計算方式。每個點的坐標用多維向量表示(a1, a2,..., an)和(b1,b2,...,bn),那么兩個向量之間的歐式距離為:(a1-b1)**2 + (a2-b2)**2 + ... + (an -bn)**2,然后開方。

(2)漢明距離。這個距離其實普通程序員應該也比較熟悉,它指兩個字符串(或者兩個位數相同的二進制數)對應位置不相同的字符個數,漢明距離越大,代表兩個串不相同的字符越多,兩個串相似度越低,反之亦然。如果是兩個二進制串計算漢明距離(1001010110)和(1001010100),非常簡單,直接使用XOR(異或運算符)即可,1001010110 xor 1001010100,然后計算結果中1的個數。

(3)余弦距離。余弦距離一般程序員可能用不到,它指兩個向量之間夾角的余弦值。二維平面坐標系中,向量(a1,a2)和向量(b1,b2)之間的夾角余弦值很好計算,同理,三維、四維也一樣。余弦距離代表兩個向量中各個分量的占比程度,比如(3,3)和(5,5)兩個向量的余弦距離為0,因為每個向量中各個分量占比都為50%(3/6,3/6 和 5/10,5/10)。同理三維向量(2,3,4)和(4,6,8)之間的余弦距離也是0。兩個向量的余弦距離為零,不代表它們之間的歐式距離為零。但是反過來卻成立。

(4)卡方距離。卡方距離主要用於衡量兩個概率分布的相似性。可以假設兩個多維向量中的每個分量都代表概率(分量的和為1),那么卡方距離就是計算這兩個概率分布的相似度。具體內容請網絡搜索。

 

以圖搜圖實現原理

以圖搜圖的過程其實很簡單,跟普通的檢索流程差不多。關鍵有兩個:

(1)一個是特征提取方法,這個是重點,如何合理提取圖像特征是保證准確性的第一要素;

(2)二個就是如何提高特征對比的速度,成千上萬張圖片源,如何快速從中找出相同/相似的圖片?

下面介紹三種傳統圖像特征提取的方式。

 

圖像特征點

任何一張數字圖片,微觀上看,像素之前總是能找到一些規律的,像素分布、像素值、像素密度等等。OpenCV內置很多特征點提取算法,比如ORB、SURF以及SIFT等等。以SIFT舉例,輸入圖片,輸出該圖片中具有某些特征的點(可以存在多個),從一定程度上講,這些點可以被當作圖片的標識,每個特征點用一個128維的向量表示。如果要比較兩張圖片的相似度,先分別提取兩張圖片的SIFT特征點,然后進行特征點匹配,看有多少對特征點能夠匹配上,如果能匹配的點超過一定數量,那么認為這兩張圖片相似。這個匹配的過程可能采用前面提到的各種距離比較。下面兩張圖通過特征點提取、特征點匹配后得到的結果,可以判斷兩張圖片相似:

 

可以看到,雖然圖片拍攝角度不一樣,但是包含的物體一致、場景類似,通過特征點匹配,可以找到很多匹配到的特征點。

這里的圖像特征用128維向量表示,可以包含多個,如果一張圖找到了100個特征點,那么每張圖有128*100個float數據需要存儲。

 

圖像感知哈希

哈希算法、哈希函數我們經常聽說,對於不同長度的輸入,哈希算法可以產生固定長度的輸出。那么我們能否直接通過計算圖片文件的哈希值來判斷圖片是否相似/相同呢?答案是不能。這種方式計算出來的哈希值對評價原始圖片相似度沒有任何參考價值,因為即使是相似的圖片通過這種方式計算出來的哈希值之間並不相似(可能相差十萬八千里),換句話說,我們不能通過哈希值之間的距離來反推原始圖片的相似度是多少。假如在一張圖片上修改了一點點,最后生成的哈希值跟原來的哈希值相差很遠。

因此我們需要用到圖像感知哈希算法,這種方法將圖片內容(更准確地講,應該是像素)考慮進來了。通過這種方式計算得到的哈希值可以反推出原始圖片之間的相似度。圖像感知哈希算法常見分三類:ahash,phash,dhash。計算方式基本差不多,下面以ahash為例說明如何計算:

(1)縮放圖片。直接將原始圖片縮小到8*8的尺寸,不用考慮原始圖片的長寬比;

(2)灰度化。將8*8的圖片灰度化,這樣處理后,這張圖片一共包含64個像素;

(3)計算平均值。計算這64個像素值的平均值;

(4)構建哈希。將64個像素值依次與(3)中的平均值進行比較,大於平均值為1,否則為0,這樣從左到右、從上到下就可以組合得到一個64位的二進制數(順序無所謂,只要保證都按這個順序即可)。這個二進制串就是原始圖片計算出來的ahash值,格式為110010001...001001,一共64位,將其轉換成16進制,得到的格式為8f373714acfcf4d0。

如何比較相似度?很簡單,計算兩張圖ahash值的漢明距離,如果距離(不相同的位數)超過10則認為兩張原始圖片不相似,小於10表示相似,具體閾值需要調整。這種方式非常簡單並且很好實現,而且很湊效,尤其是在一堆圖片中找相同圖片的時候(縮放無所謂),非常適合用縮略圖查找原始圖。

三種感知哈希計算方式:https://github.com/JohannesBuchner/imagehash

三種感知哈希原理說明:http://www.hackerfactor.com/blog/index.php?/archives/432-Looks-Like-It.html

http://www.hackerfactor.com/blog/index.php?/archives/529-Kind-of-Like-That.html

三種感知哈希區別和優劣勢請參照網絡。

這里的圖像特征用64位二進制表示,每張圖片的特征需要8個字節存儲。

 

基於區域的顏色直方圖

顏色直方圖統計整張圖片中各種像素的占比情況,嚴格來講它並不考慮顏色分布在圖片中的位置。而基於區域的顏色直方圖在計算像素占比時,先將整張圖片划分成不同的子區域,然后依次計算子區域的顏色占比情況,最后考慮子區域合並之后的結果。這種方式彌補了之前忽略顏色分布位置的缺陷,通過直方圖數據評價圖片的相似度更加可靠。

如上圖,將原始圖片划分成5個子區域,依次計算每個子區域的顏色直方圖數據。圖片是RGB三通道,每個通道依次分成(8,3,12)個區間,那么每個子區域顏色直方圖就可以使用8*3*12=288維向量表示。而我們又將原始圖片划分成了5個子區域,那么整張圖的特征就需要用 8*3*12*5 = 1440維向量來表示了。

這里如何計算直方圖特征距離呢?由於這里288維向量中每個分量代表對應區間像素出現的概率,那么距離計算就要用到前面提到過的卡方距離。通過卡方距離公式計算兩個288維特征向量之間的距離,從而反推原始圖片之間的相似度。
注意:使用顏色直方圖的方式提取圖像特征的前提是:接受“如果兩張圖片顏色分布差不多,則認為它們相似”這一假設,這一假設不考慮圖片中具體包含內容,比如包含馬、狗、人之類的目標,而只考慮顏色。事實上,經過實踐,大部分時候該假設確實成立。

 

如何提高查詢速度

給定一個圖像特征,如何從一堆圖像特征中快速找到與之相同、或者與之距離最近的N個、或者與之距離在M之內的所有特征呢?一個個的比較肯定不可接受,我們需要提前給這些特征創建索引,提高查找的速度。

VP-Tree是一種很好的數據結構,能夠解決緊鄰搜索的問題,這里是它的Python實現:https://github.com/RickardSjogren/vptree,它能夠先用圖像特征構建出一個二叉樹,提高查詢速度。具體原理請參照網絡。

 

感知哈希+基於區域的顏色直方圖demo

最后有一個demo,基於圖像感知哈希(ahash、phash以及dhash)和基於區域的顏色直方圖,完成了一個簡單的圖片搜索demo。服務端采用Python、Flask開發。

源碼地址:https://github.com/sherlockchou86/cbir-image-search

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM