大家都知道,,通過文本或標簽來搜索圖片的體驗非常糟糕。 無論你是將個人照片貼標簽并分類,,或是在公司的網(wǎng)站上搜索一堆照片,,還是在為下一篇博客尋找合適的圖片。在用文本和關(guān)鍵字來描述圖片是非常痛苦的事,。 我就遇到了這樣的痛苦的事情,,上周二我打開了一個很老的家庭相冊,其中的照片是9年前掃描成電子檔的,。 我想找到我家在夏威夷海灘拍的照片,。我用iPhoto打開相冊,慢慢的瀏覽,。這個過程非常辛苦,,每個JPEG圖像的元信息中的日期都是錯的。我已經(jīng)不記得文件夾中的圖片是如何排列的,,我絕望的搜索海灘的照片,,但還是找不到。 也許是運氣,,我跌跌撞撞的找到了其中一幅海灘上的照片,。多美的一幅照片啊。藍天中飄著棉花糖般的白云,。晶瑩透徹的海水,,像絲綢一樣掠過在金色的沙灘上。我?guī)缀蹩梢愿杏X微風(fēng)輕撫著面龐,,呼吸著海邊濕潤的空氣,。 找到這幅照片后,,我停止了手動搜索,打開一個代碼編輯器,。 雖然iPhoto這樣的應(yīng)用能讓你將相片分組,,甚至可以檢測人臉,但我們可以做的更多,。 注意,,我并不是介紹如何手動給圖片添加標簽。我是在介紹更強大的東西,。比如通過一幅圖片來搜索一組相似的圖片。 這是不是很酷,?只需鼠標點擊一次就可以可視化搜索圖片,。 這就是我的工作內(nèi)容。我用半個小時寫好代碼,,完成了一個針對家庭假期相冊的圖片搜索引擎,。 然后用上面找到的那張海灘圖片作為搜索源。幾秒后我就找到了相冊中其他的海灘圖片,,其中沒有任何為某張圖片添加標簽的動作,。 感興趣嗎,我們繼續(xù),。 在本文的其他部分,,我將介紹如何自己創(chuàng)建一個圖像搜索引擎。 想要文本中的代碼,? 直接跳到文本中的最后的“下載”一節(jié),。 什么是圖像搜索引擎?讀者也許會問,,什么才是一個真正的圖像搜素引擎,? 我的意思是,我們都熟悉基于文本的搜索引擎,,如Google,、Bing、Baidu等,。用戶只需輸入幾個與內(nèi)容相關(guān)的關(guān)鍵字,,接著就會獲得搜索結(jié)果。但對于圖像搜索引擎,,其工作方式就有點區(qū)別,。搜索時使用的不是文字,而是圖片,。 聽起來很困難,,我的意思是,,如何量化圖像的內(nèi)容,讓其可搜索呢,? 本文將逐步回答這個問題,。首先,先了解一下圖像搜索引擎的內(nèi)容,。 一般來說,,有三種類型的圖像搜索引擎:基于元數(shù)據(jù)、基于例子,、混合模式,。 基于元數(shù)據(jù)圖1:基于元數(shù)據(jù)的圖像搜索引擎的例子。注意其中關(guān)鍵字和標簽是手動關(guān)聯(lián)到圖像上的,。 通過元數(shù)據(jù)搜索與標準的關(guān)鍵字搜索引擎沒有本質(zhì)的不同,。這種方式很少檢測圖像本身的內(nèi)容。而是用相關(guān)的文本信息:如手動注釋或添加標簽,;以及自動上下文提示(如網(wǎng)頁中該圖片附近的文字信息),。 當用戶在基于元數(shù)據(jù)的系統(tǒng)上進行搜索時,與傳統(tǒng)的文本搜索引擎其實差點不多的,。得到的是含有類似標簽或注釋的圖片,。 再次說明,使用基于元數(shù)據(jù)系統(tǒng)的工具進行搜索,,基本上是不查找圖像本身的,。 用基于元數(shù)據(jù)進行搜索的應(yīng)用中,一個比較好的例子就是Flickr,。將圖像上傳到Flickr后,,輸入一些文本作為標簽描述這幅圖像。接著Flickr會使用這些關(guān)鍵字進行搜索,,查找并推薦其他相關(guān)的圖像,。 基于例子搜索圖2:TinEye就是一個基于例子的圖像搜索引擎。該搜索引擎會使用圖像本身的內(nèi)容進行搜索,,而不是使用文本搜索,。 另一方面,基于例子搜索僅僅依賴于圖像的內(nèi)容,,不需要提供關(guān)鍵字,。引擎會分析、量化并存儲圖像,,然后返回其他相關(guān)的圖像,。 圖像搜索引擎量化圖像內(nèi)容的過程稱為基于內(nèi)容的圖像信息獲取(Content-Based Image Retrieval,,CBIR)系統(tǒng),。術(shù)語CBIR通常用在學(xué)術(shù)文獻中,,但在實際上,這是“圖像搜索引擎”的另一種表述,,并特意指明該搜索引擎是嚴格基于圖像的內(nèi)容的,,沒有任何關(guān)于圖像的文本的信息。 基于例子系統(tǒng)的一個比較好的例子就是TinEye,。向TinEyet提交待查找圖像時,,TinEye實際上是一個逆向圖像搜索引擎,TinEye返回該圖像最相近的匹配,,以及該圖像位于的原始網(wǎng)頁地址,。 看下本節(jié)剛開始的示例圖像。我上傳了一個Google logo圖像,。TinEye檢測了圖像的內(nèi)容,,在搜索了超過60億幅圖片后,返回了1.3萬個含有Google logo圖片的網(wǎng)頁,。 所以仔細想一下:你需要為TinEye中的60億幅圖像收到添加標簽嗎?當然不需要,,為這么多圖片手動添加標簽需要龐大的人力物力,。 取而代之,使用某些算法從圖像本身中提取“特征”(如用一組數(shù)字來量化并抽象表示圖像),。接著,,當用戶提交了需要查找的圖像,從這幅圖像中提取特征,,將其與數(shù)據(jù)庫中的特征進行比較,,嘗試返回相似的圖像。 同樣,,依然需要強調(diào),,基于例子的搜索系統(tǒng)非常依賴圖像的內(nèi)容。這種類型的系統(tǒng)很難構(gòu)建并擴展,,但可以用算法全自動的搜索,,無需人工干預(yù)。 混合方式圖3:混合式圖像搜索引擎可以同時基于圖像和文本描述搜索,。 當然,,除了前面介紹的兩種方式,還有一種介于兩者之間的方式,,如Twitter使用的,。 在Twitter上,可以與推文一起上傳圖像,。這樣就可以即使用提取圖像本身的特征,,也可以使用推文中的文本,,從而誕生一種混合方式?;谶@種方式可以可以構(gòu)建一個即使用上下文關(guān)系,,又使用基于例子搜索的策略的圖像搜索引擎。 提示:有興趣閱讀更多關(guān)于不同類型的圖像搜索引擎的資料,?我有一篇完整的博客介紹比較這些搜索引擎的,,鏈接在此。 在進一步描述和構(gòu)建圖像搜索引擎之前,,讓我們先來了解一些重要的術(shù)語,。 一些重要的術(shù)語在深入了解之前,先花點時間了解一些重要的術(shù)語,。 在構(gòu)建圖像搜索引擎時,,首先要對數(shù)據(jù)集編列索引。索引化數(shù)據(jù)集是量化數(shù)據(jù)集的過程,,即通過圖像描述符(image descriptor,,也稱描述子)提取每幅圖像的特征。 圖像描述符就是用來描述圖像的算法,。 例如:
這里最重要的是圖像描述符確定了圖像是如何量化的。 另一方面,,特征是圖像描述符的輸出,。當將一幅圖像放入圖像描述符中時,就會獲得這幅圖像的特征,。 以基本的術(shù)語來說,。特征(或特征向量)僅僅是一個用來抽象表示或量化的圖像的數(shù)字列表。 來看下面這幅示例圖像: 圖4:圖像描述符的管道,。描述符中有一幅輸入圖像,,使用圖像描述符會返回一個特征向量(一個數(shù)字列表), 這里對一幅輸入圖像使用圖像描述符,,輸出是一組用來量化圖像的數(shù)字,。 通過距離量測或其他相似度比較函數(shù),特征向量可以用來表示比較的相似度,。距離量測和相似度函數(shù)采用兩個特征向量作為輸入,,返回一個數(shù)值來描述著兩個特征向量的相似度。 下圖以可視化的方式比較了兩幅圖的比較過程: 圖5:為了比較兩幅圖,必須將對應(yīng)的特征向量輸入進距離量測/相似度比較函數(shù),。輸出結(jié)果是一個數(shù)值,,量化地描述兩幅圖下的相似度。 給定兩個特征向量,,使用距離函數(shù)來確定這兩個特征向量的相似度,。距離函數(shù)的輸出是一個浮點數(shù),用來描述兩幅圖像的相似度,。 CBIR系統(tǒng)的4個步驟無論構(gòu)建的是什么樣的CBIR系統(tǒng),,最終都可以分解成4個不同的步驟。
再次強調(diào),這是所有CBIR系統(tǒng)中最基本的4步,。如果使用的特征表示不同,,則步驟數(shù)會增加,也會為每個步驟增加一定數(shù)量的子步驟,。就目前而言,,讓我們關(guān)注并使用這4步。 下面通過圖像來具體了解這4個大步驟,。下圖表述的是步驟1和2: 圖6:處理并提取數(shù)據(jù)集中的每幅圖像的流程圖,。 首先提取數(shù)據(jù)集中每幅圖像的特征,將這些特征存入一個數(shù)據(jù)庫,。 接著可以執(zhí)行搜索(步驟3和4): 圖7:在CBIR系統(tǒng)中執(zhí)行搜索,。用戶提交一個搜索請求,系統(tǒng)對搜索圖像進行描述,,其特征會與數(shù)據(jù)集中已有的特征進行比較,,并對結(jié)果根據(jù)相關(guān)度進行排序,返回給用戶,。 首先,,用戶必須像搜索引擎提交一幅需要查找的圖像。接著對這幅圖像提取特征信息,。將這些特征信息與數(shù)據(jù)集中已有的圖像的特征信息進行比較,。最后,對結(jié)果根據(jù)相關(guān)度進行排序并返回給用戶,。 數(shù)據(jù)集——假期相冊這里將INRIA假期數(shù)據(jù)集作為圖像搜索的數(shù)據(jù)集,。 這個數(shù)據(jù)集含有全世界許多地方的假期旅行,包括埃及金字塔,、潛水,、山區(qū)的森林、餐桌上的瓶子和盤子,、游艇,、海面上的日落。 下面是數(shù)據(jù)集中的一些圖片: 圖8:數(shù)據(jù)集中的示例圖像,。我們將使用這些圖像構(gòu)建自己的圖像搜索引擎,。 在本例中,對于我們希望從旅行相冊中找到某種景色的相片來說,,用這幅數(shù)據(jù)集作為示例來說非常好,。 目標我們的目標是構(gòu)建一個個人圖像搜索引擎。將假期照片作為數(shù)據(jù)集,我們希望將這個數(shù)據(jù)集變成可搜索的,,即一個“基于例子”的圖像搜索引擎,。例如,如果我提交了一幅在河中航行的帆船的照片,,圖像搜索引擎應(yīng)該能找到并返回相冊中碼頭和船塢拍攝的照片,。 看下面的圖,其中有我提交的照片,,即一幅在水里的船,。得到了假期照片集合中相關(guān)的圖像。 圖9:圖像搜索引擎的例子,。提交了一幅含有海中船只的圖像,。返回相關(guān)的圖像,這些圖像都是在海中的船,。 為了構(gòu)建這個系統(tǒng),,將使用一個簡單且有效的圖像描述符:顏色直方圖。 通過將顏色直方圖作為我們的圖像描述符,,可以根據(jù)圖像的色彩分布提取特征,。由于這一點,我們可以對我們的圖像搜索引擎做個重要的假設(shè): 假設(shè):如果圖像含有相似的色彩分布,,那么這兩幅圖像就認為是相似的,。即使圖像的內(nèi)容差別非常大,依然會根據(jù)色彩分布而被認為是相近的,。 這個假設(shè)非常重要,,在使用顏色直方圖作為圖像描述符時,這是個公平且合理的假設(shè),。 第一步1:定義圖像描述符這里不使用標準的顏色直方圖,,而是對其進行一些修改,使其更加健壯和強大,。 這個圖像描述符是HSV顏色空間的3D顏色直方圖(色相、飽和度,、明度),。一般來說,圖像由RGB構(gòu)成的元組表示,。通常將RGB色彩空間想象成一個立方體,,如下圖所示。 圖10:RGB立方體的例子,。 然而,,雖然RGB值很容易理解,但RGB色彩空間無法模擬人眼接受到的色彩。取而代之,,我們使用HSV色彩空間將像素點的映射到圓bin體上,。 圖11:HSV圓bin體的例子。 還有其他顏色空間能夠更好的模擬人眼接收的顏色,,如CIE L*a*b*和CIE XYZ顏色空間,,但作為第一個圖像搜索引擎的實現(xiàn),先簡化使用的色彩模型,。 現(xiàn)在選定了顏色空間,,接著需要定義直方圖中bin的數(shù)量。直方圖用來粗略的表示圖像中各強度像素的密度,。本質(zhì)上,,直方圖會估計底層函數(shù)的概率密度。在本例中,,P是圖像I中像素色彩C出現(xiàn)的概率,。 主要注意的是,為直方圖選取bin的數(shù)目需要不斷的權(quán)衡,。如果選擇的bin數(shù)目過少,,那么直方圖含有的數(shù)據(jù)量就不夠,無法區(qū)分某些不同顏色分布的圖像,。反之,,如果直方圖選取的bin的數(shù)目過多,那么其中的組件就過多,,導(dǎo)致內(nèi)容很相近的圖片也會判斷成不相似,。 下面是直方圖bin過少的例子。 圖12:9個bin直方圖的例子,。注意其中bin的數(shù)量很少,,只有少數(shù)給定的像素值位列其中。 注意其中只有少數(shù)幾個bin及相應(yīng)的像素值,。 下面是直方圖bin過多的例子,。 圖13:128 bin直方圖的例子。注意其中含有許多的柱和相應(yīng)的像素值,。 在上面的的例子中使用了許多bin,,bin的數(shù)目過多,由于需要直方圖中每個“山峰”和“山谷”都需要匹配才能認為圖像是“相似的”,,所以就失去了“概括”圖像的能力,。 就我個人而言,我喜歡用迭代,、實驗性的方式來調(diào)整bin的數(shù)目,。迭代方法一般基于數(shù)據(jù)集的大小調(diào)整,。數(shù)據(jù)集越小,使用的bin的數(shù)目就越少,。如果數(shù)據(jù)集非常大,,則會使用更多的bin,這樣可以讓直方圖更大,,更能區(qū)分圖像,。 一般來說,讀者需要為顏色直方圖描述符實驗bin的個數(shù),,具體取決于數(shù)據(jù)集的大小和數(shù)據(jù)集中圖像之間色彩分布的差異,。 對于我們的假期照片圖像搜索引擎,將在HSV色彩空間中使用3D顏色直方圖,,8個bin用于色相通道,、12個bin用于飽和度通道、3個bin用于明度通道,,總共的特征向量有8 × 12 × 3=288,。 這意味著數(shù)據(jù)集中的每幅圖像,無論其像素數(shù)目是36 × 36,,還是2000 × 1800,。最終都會用288個浮點數(shù)構(gòu)成的列表抽象并量化表示。 我認為解釋3D直方圖最好的方式是用連接詞AND,。一個3D HSV顏色描述符將查找指定圖像中1號bin有多少像素含有色相值,,AND有多少像素有飽和度值,AND有多少像素有明度值,。計算出符合條件的像素值,。雖然需要對每個bin重復(fù)這個操作,但可以非常高效的完成這個任務(wù),。 很酷,,是吧! 理論講解的夠多了,,下面來開始編碼,。 用你最喜歡的編輯器打開一個新文件,,命名為colordescriptor.py,。加入下面代碼:
首先導(dǎo)入所需的Python模塊,。用NumPy進行數(shù)值處理,,用cv2使用OpenCV的Python綁定,。 在第五行定義了ColorDescriptor類,。該類用來封裝所有用于提取圖像中3D HSV顏色直方圖的邏輯,。 ColorDescriptor的__init__方法只有一個參數(shù)——bins,,即顏色直方圖中bin的數(shù)目,。 在第10行定義describe方法,,用于描述指定的圖像。 在describe方法中,,將圖像從RGB顏色空間(或是BGR顏色空間,,OpenCV以NumPy數(shù)組的形式反序表示RGB圖像)轉(zhuǎn)成HSV顏色空間。接著初始化用于量化圖像的特征列表features,。 17和18行獲取圖像的維度,,并計算圖像中心(x, y)的位置。 現(xiàn)在遇到難點,。 這里不計算整個圖像的3D HSV顏色直方圖,,而是計算圖像中不同區(qū)域的3D HSV顏色直方圖。 使用基于區(qū)域的直方圖,,而不是全局直方圖的好處是:這樣我們可以模擬各個區(qū)域的顏色分布,。例如看下面的這幅圖像: 圖14:待搜索的圖像。 在這幅圖像中,,很明顯,,藍天在圖像的上部,而沙灘在底部,。使用全局搜索的話,,就無法確定圖像中“藍色”區(qū)域和“棕色”沙子區(qū)域的位置。而是僅僅知道圖像中有多少比例是藍色,,有多少比例是棕色,。 為了消除這個問題,可以對圖像中的不同區(qū)域計算顏色直方圖: 圖15:將圖像分為5個不同區(qū)域的例子,。 對于我們的圖像描述符,,將圖像分為5個不停的區(qū)域:1、左上角,;2,、右上角;3,、右下角,;4、左下角,;以及圖像的中央,。 使用這些區(qū)域,可以粗略模擬出不同的區(qū)域,。能夠表示出藍天在左上角和右上角,,沙灘在左下角和右下角。圖像的中央是沙灘和藍天的結(jié)合處,。 下面的代碼是創(chuàng)建基于區(qū)域的顏色描述符:
22和23行用于分別定義左上,、右上,、右下、和左下區(qū)域,。 這里,,我們需要構(gòu)建一個橢圓用來表示圖像的中央?yún)^(qū)域。在代碼的27行,,定義一個長短軸分別為圖像長寬75%的橢圓,。 接著初始化一個空白圖像(將圖像填充0,表示黑色的背景),,該圖像與需要描述的圖像大小相同,,見28行。 最后,,在29行使用cv2.ellipse函數(shù)繪制實際的橢圓,。該函數(shù)需要8個不同的參數(shù):
在35行為每個角的掩模分配內(nèi)存,,在36行為圖像的每個角繪制白色矩形,接著在37行將矩形減去中間的橢圓,。 如果將這個過程用圖像動態(tài)表示,,看上去應(yīng)該是這樣的: 圖16:為圖像中每個需要提取特征的區(qū)域構(gòu)建掩模,。 如這個動畫所示,我們獨立檢測每塊區(qū)域,,在迭代中移除每個矩形與圖像中間的橢圓重疊的部分。 讀者也許會奇怪,,“我們不是要提取圖像的顏色直方圖嗎,?為什么要做這些掩模的事情?” 問得好,! 原因是因為我們需要告訴OpenCV直方圖函數(shù)我們要提取的顏色直方圖的區(qū)域,。 記住,我們的目標是分開描述圖像的每個區(qū)域,。表述不同區(qū)域最高效的方法是使用掩模,。對于圖像中某個點(x, y),只有掩模中該點位白色(255)時,,該像素點才會用于計算直方圖,。如果該像素點對應(yīng)的位置在掩模中是黑色(0),將會被忽略,。 通過下圖可以更加深刻的了解這個概念,。 圖17:對圖像使用掩模。注意左圖中只有右圖掩模中對應(yīng)的區(qū)域為白色才會顯示,。 可以看到,,只有掩模的區(qū)域才會用于直方圖的計算中。 很合理,,是吧,。 所以現(xiàn)在在41行針對每個區(qū)域都調(diào)用直方圖方法。第一個參數(shù)是需要提取特征的圖像,,第二個參數(shù)是掩模區(qū)域,,這樣來提取顏色直方圖。 histogram方法會返回當前區(qū)域的顏色直方圖表示,,我們將其添加到特征列表中,。 46和47行提取圖像中間(橢圓)區(qū)域的顏色直方圖并更新features列表。 最后,,在50行像調(diào)用函數(shù)返回特征向量,。 現(xiàn)在來快速瀏覽下實際的histogram方法:
這里的histogram方法需要兩個參數(shù),第一個是需要描述的圖像,,第二個是mask,,描述需要描述的圖像區(qū)域。 在5和6行,,通過調(diào)用cv2.calcHist計算圖像掩模區(qū)域的直方圖,,使用構(gòu)造器中的bin數(shù)目作為參數(shù),。 在7行對直方圖歸一化。這意味著如果我們計算兩幅相同的圖像,,其中一幅比另一幅大50%,,直方圖會是相同的。對直方圖進行歸一化非常重要,,這樣每個直方圖表示的就是圖像中每個bin的所占的比例,,而不是每個bin的個數(shù)。同樣,,歸一化能保證不同尺寸但內(nèi)容近似的圖像也會在比較函數(shù)中認為是相似的,。 最后,在10行向調(diào)用函數(shù)返回歸一化后的3D HSV顏色直方圖,。 步驟2:從數(shù)據(jù)集提取特征現(xiàn)在有了定義好的圖像描述符,,進入第二步,對數(shù)據(jù)集中的每幅圖像提取特征(如顏色直方圖),。提取特征并將其持久保存起來的過程一般稱為“索引化”,。 繼續(xù)來看對假期照片數(shù)據(jù)集進行索引化的代碼。創(chuàng)建一個新文件,,命名為index.py,,添加索引化所需的代碼:
首先導(dǎo)入所需的模塊。注意第一步中的ColorDescriptor類,,這里將其放入pyimagesearch模塊,,以便更好的組織代碼。 還需要argparse模塊來處理命令行參數(shù),、glob來獲取圖像的文件路徑,,以及cv2來使用OpenCV的接口。 7-12行用來處理命令行指令,。這里需要兩個指令,,–dataset,表示假期相冊的路徑,。–index,,表示輸出的CSV文件含有圖像文件名和對應(yīng)的特征。 最后,,在16行初始化ColorDescriptor,,8 bin擁有色相、12 bin用于飽和度,、3 bin用于明度,。 現(xiàn)在所有內(nèi)容都初始化了,可以從數(shù)據(jù)集提取特征了:
在2行代開輸出文件,在5行遍歷數(shù)據(jù)集中的所有圖像,。 對于每幅圖像,,可以提取一個imageID,即圖像的文件名,。對于這個作為示例的搜索引擎,,我們假定每個文件名都是唯一的,也可以針對每幅圖像生產(chǎn)一個UUID,。在9行將從磁盤上讀取圖像,。 現(xiàn)在圖像載入內(nèi)存了,在12行對圖像使用圖像描述符并提取特征,。ColorDescriptor的describe方法返回由浮點數(shù)構(gòu)成的列表,用來量化并表示圖像,。 這個數(shù)字列表,,或者說特征向量,含有第一步中圖像的5個區(qū)域的描述,。每個區(qū)域由一個直方圖表示,,含有8 × 12 × 3 = 288項。5個區(qū)域總共有5 × 288 = 1440維度,。,。。因此每個圖像使用1440個數(shù)字量化并表示,。 15和16行簡單的將圖像的文件名和管理的特征向量寫入文件,。 為了索引化我們的相冊數(shù)據(jù)集,打開一個命令行輸入下面的命令:
這個腳本運行的很快,,完成后將會獲得一個名為index.csv的新文件,。 使用你最喜歡的文本編輯器打開并查看該文件。 可以看到在.csv文件的每一行,,第一項是文件名,,第二項是一個數(shù)字列表。這個數(shù)字列表就是用來表示并量化圖像的特征向量,。 對index文件運行wc命令,,可以看到已經(jīng)成功對數(shù)據(jù)集中805幅圖像索引化了:
第3步:搜索現(xiàn)在已經(jīng)從數(shù)據(jù)集提取了特征了,接下來需要一個方法來比較這些特征,,獲取相似度,。這就是第三步的內(nèi)容,創(chuàng)建一個類來定義兩幅圖像的相似矩陣,。 創(chuàng)建一個新文件,,命名為searcher.py,讓我們在這里做點神奇的事情:
首先先導(dǎo)入NumPy用于數(shù)值計算,csv用于方便的處理index.csv文件,。 在第5行定義Searcher類,。Searcher類的構(gòu)造器只需一個參數(shù),indexPath,,用于表示index.csv文件在磁盤上的路徑,。 要實際執(zhí)行搜索,需要在第10行調(diào)用search方法,。該方法需要兩個參數(shù),,queryFeatures是提取自待搜索圖像(如向CBIR系統(tǒng)提交并請求返回相似圖像的圖像),和返回圖像的數(shù)目的最大值,。 最后,,在12行初始化results字典。在這里,,字典有很用的用途,,每個圖像有唯一的imageID,可以作為字典的鍵,,而相似度作為字典的值,。 好了,現(xiàn)在將注意力放在這里,。這里是發(fā)生神奇的地方:
在1行打開index.csv文件,,在3行獲取CSV讀取器的句柄,接著在6行循環(huán)讀取index.csv文件的每一行,。 對于每一行,,提取出索引化后的圖像的顏色直方圖,用11行的chi2_distance函數(shù)將其與待搜索的圖像特征進行比較,,該函數(shù)在下面介紹,。 在32行使用唯一的圖像文件名作為鍵,用與待查找圖像的與索引后的圖像的相似讀作為值來更新results字典,。 最后,,將results字典根據(jù)相似讀升序排序。 卡方相似度為零的圖片表示完全相同,。相似度數(shù)值越高,,表示兩幅圖像差別越大。 說到卡方相似讀,,看下面的源碼:
chi2_distance函數(shù)需要兩個參數(shù),,即用來進行比較的兩個直方圖??蛇x的eps值用來預(yù)防除零錯誤,。 這個函數(shù)的名稱來自皮爾森的卡方測試統(tǒng)計,用來比較離散概率分布。 由于比較的是顏色直方圖,,根據(jù)概率分布的定義,,卡方函數(shù)是個完美的選擇。 一般來說,,直方圖兩端的值的差別并不重要,,可以使用權(quán)重對其進行處理,卡方距離函數(shù)就是這么做的,。 還能跟的上嗎,?我保證,最后一步是最簡單的,,僅僅需要將前面的各部分組合在一起,。 第四步:執(zhí)行搜索如果我告訴你,執(zhí)行搜索是最簡單的一步,,你信嗎,?實際上,只需一個驅(qū)動程序?qū)肭懊娑x的所有的模塊,,將其依次組合成具有完整功能的CBIR系統(tǒng),。 所以新建最后一個文件,,命名為search.py,,這樣我們的例子就能完成了:
首先導(dǎo)入所需的包,導(dǎo)入第一步的ColorDescriptor來提取待查找圖像的特征,;導(dǎo)入第三步定義的Searcher類,,用于執(zhí)行執(zhí)行實際的搜索。 argparse和cv2模塊一直會導(dǎo)入,。 在8-15行處理命令行參數(shù),。我們需要用一個–index來表示index.csv文件的位置。 還需要–query來表示帶搜索圖像的存儲路徑,。該圖像將與數(shù)據(jù)集中的每幅圖像進行比較,。目標是找到數(shù)據(jù)集中歐給你與待搜索圖像相似的圖像。 想象一下,,使用Google搜索并輸入“Python OpenCV tutorials”,,會希望獲得與Python和OpenCV相關(guān)的信息。 與之相同,,如果針對相冊構(gòu)建一個圖像搜索引擎,,提交了一副關(guān)于云、大海上的帆船的圖像,,希望通過圖像搜索引擎獲得相似的圖像,。 接著需要一個–result-path,用來表示相冊數(shù)據(jù)集的路徑。通過這個命令可以選擇不同的數(shù)據(jù)集,,向用戶顯示他們所需要的最終結(jié)果,。 最后,在18行使用圖像描述符提取相同的參數(shù),,就如同在索引化那一步做的一樣,。如果我們是為了比較圖像的相似度(事實也正是如此),就無需改變數(shù)據(jù)集中顏色直方圖的bin的數(shù)目,。 直接將第三步中使用的直方圖bin的數(shù)目作為參數(shù)在第四步使用,。 這樣會保證圖像的描述是連續(xù)且可比較的。 現(xiàn)在到了進行真正比較的時候:
在2行從磁盤讀取待搜索圖像,,在3行提取該圖像的特征,。 在6和7行使用提取到的特征進行搜索,返回經(jīng)過排序后的結(jié)果列表,。 到此,,所需做的就是將結(jié)果顯示給用戶。 在9行顯示出待搜索的圖像,。接著在13-17行遍歷搜索結(jié)果,,將相應(yīng)的圖像顯示在屏幕上。 所有這些工作完成后,,就可以實際操作了,。 繼續(xù)閱讀,看最終效果如何,。 CBIR系統(tǒng)實戰(zhàn)打開終端,,切換到代碼所在的目錄,執(zhí)行下面的命令:
圖18:在相冊中搜索含有埃及金字塔的圖像,。 第一幅圖像是待搜索的埃及金字塔,。我們的目標是在相冊中找到相似的圖像??梢钥吹?,在相冊中找到了去金字塔游玩拍攝的照片。 我們還游覽的埃及其他地方,,所以用其他照片搜索試試:
圖19:使用搜索引起搜索埃及其他地方的圖片,,注意圖中藍天的位置。 注意我們的搜索圖像中,,上半部分是藍天,。中間和下半部分是褐色的建筑和土地。 可以肯定,,圖像搜索引擎會返回上半部分是藍天,,下半部分是棕褐色建筑和沙子的圖像,。 這是因為我們使用了本文開頭介紹的基于區(qū)域的顏色直方圖描述符。使用這種圖像描述符可以粗略的針對每個區(qū)域執(zhí)行,,最后的結(jié)果中會含有圖像每個區(qū)域的像素的密度,。 旅途的最后一站是海灘,用下面的命令搜索海灘上的圖像:
圖20:使用OpenCV構(gòu)建CBIR系統(tǒng)來搜索相冊,。 注意,,前3個搜索結(jié)果是在相同地點拍攝到的圖像。其他圖像都含有藍色的區(qū)域,。 當然,,沒有潛水的海灘之旅是不完整的。
圖21:圖像搜索引起再次返回相關(guān)的結(jié)果,。這次是水下冒險,。 結(jié)果非常棒。前5個結(jié)果是同一條魚,,前10幅有9幅是水下探險,。 最后,一天的旅途結(jié)束了,,到了觀看夕陽的時候:
圖22:這個OpenCV圖像搜索引起可以查找到相冊集中含有夕陽的相片,。 搜索結(jié)果非常棒,所有的結(jié)果都含有夕陽,。 這樣,,你就有了第一個圖像搜索引擎: 總結(jié)本文介紹了如何構(gòu)建一個圖像搜索引擎,來查找相冊中的圖像,。 使用顏色直方圖對相冊中的圖像的顏色部分進行分類,。接著,,使用顏色描述符索引化相冊,,提取相冊中每一副圖像的顏色直方圖。 使用卡方距離比較圖像,,這是比較離散概率分布最常見的選擇,。 接著,實現(xiàn)了提交待搜索圖像和返回查找結(jié)果的邏輯,。 下一步接下來該干什么,? 可以看到,使用命令行是與這個圖像搜索引擎交互的唯一方式,。這樣還不是太吸引人 下一篇文章將探索如何將這個圖像搜索引擎封裝進一個Python網(wǎng)絡(luò)框架中,,讓其更易于使用。 |
|