日日操夜夜添-日日操影院-日日草夜夜操-日日干干-精品一区二区三区波多野结衣-精品一区二区三区高清免费不卡

公告:魔扣目錄網為廣大站長提供免費收錄網站服務,提交前請做好本站友鏈:【 網站目錄:http://www.ylptlb.cn 】, 免友鏈快審服務(50元/站),

點擊這里在線咨詢客服
新站提交
  • 網站:51998
  • 待審:31
  • 小程序:12
  • 文章:1030137
  • 會員:747

無論你是在給你的個人圖片加標簽和分類,還是為你的公司網站搜索庫存照片,或者僅僅是為你的下一篇史詩般的博客文章尋找合適的圖片,試圖使用文本和關鍵詞來描述一些本質上是可視化的東西,都是一件非常痛苦的事情。

為了解決這個痛苦,OpenCV為我們提供了很好的工具,之前學習的很多關于圖像特征點提取的算法,現在就派上用場了。下面我們將創建一個屬于自己的圖像搜索引擎。

什么是圖像搜索引擎呢?

顧名思義,就是用一張圖像作為搜索關鍵詞,這和文本類搜索類似,區別就是一個用文本,一個用圖像文件。或許你以前使用過google和其它搜索引擎,基本上現在也支持圖像搜索,就是給定一張圖像文件,搜索引擎就會給出和給定圖像類似的圖像列表供你參考。聽起來很難,如何量化一個圖像的內容,使其可搜索? 我們稍后會討論這個問題的答案。但首先,讓我們學習更多關于圖像搜索引擎的知識。一般來說,有三種類型的圖像搜索引擎: 元數據搜索、實例搜索和兩者的混合方法。

元數據搜索

用Python和OpenCV構建屬于自己的圖像搜索引擎

 

Figure 1: 一個由元數據圖像搜索引擎搜索的例子。請注意如何將關鍵字和標記手動賦值到圖像。

使用元數據進行搜索與上面提到的基于關鍵字的標準搜索引擎略有不同。元數據系統的搜索很少檢查圖像本身的內容。相反,它們依賴于文本線索,如(1)人工注釋和標簽,以及(2)自動上下文提示,如網頁上出現在圖像附近的文本。

當用戶通過元數據系統對搜索執行搜索時,他們提供一個查詢,就像在傳統的文本搜索引擎中一樣,然后返回具有類似標記或注釋的圖像。

同樣,當使用元數據系統搜索時,很少檢查實際圖像本身。元數據圖像搜索引擎的一個很好的例子是Flickr。上傳圖片到Flickr后,會出現一個文本字段,用于輸入描述上傳圖片內容的標簽。然后Flickr獲取這些關鍵字,對它們進行索引,并利用它們來查找和推薦其他相關的圖片。

實例搜索

用Python和OpenCV構建屬于自己的圖像搜索引擎

 

Figure 2: TinEye是一個"逐例搜索"圖像搜索引擎的例子。圖像本身的內容用于執行搜索,而不是文本。

另一方面,實例系統的搜索完全依賴于圖像的內容——不提供關鍵字。對圖像進行分析、量化和存儲,以便系統在搜索期間返回類似的圖像。量化圖像內容的圖像搜索引擎稱為基于內容的圖像檢索(CBIR)系統。CBIR這個詞在學術文獻中很常用,但在現實中,它只是"圖像搜索引擎"的一種更奇特的說法,更令人辛酸的是,搜索引擎嚴格依賴于圖像的內容,而不是與圖像相關的任何文本注釋。

一個很好的例子是一個實例搜索系統如TinEye。TinEye實際上是一個反向圖像搜索引擎,您提供一個查詢圖像,然后TinEye返回幾乎相同的匹配圖像,以及原始圖像出現的網頁。看一下本節中的示例圖像。這里我上傳了一張谷歌logo的圖片。TinEye檢查了圖片的內容,在搜索了超過60億張圖片的索引后,返回給我谷歌標志出現的13000多個網頁。

所以考慮一下這個:你打算用TinEye手工標記這60億張圖片中的每一張嗎?當然不是。這將需要大量的員工,而且成本極高。相反,您可以使用某種算法來提取"特性"(即:用于量化和抽象地表示圖像的數字列表)。然后,當用戶提交查詢圖像時,您從查詢圖像中提取特性,并將它們與您的特性數據庫進行比較,然后嘗試找到類似的圖像。

再次強調,通過示例系統搜索嚴格依賴于圖像的內容,這一點很重要。這些類型的系統往往非常難以構建和擴展,但是允許一個完全自動化的算法來控制搜索——不需要人工干預。

混合模式

用Python和OpenCV構建屬于自己的圖像搜索引擎

 

Figure 3: 混合圖像搜索引擎可以同時考慮文本和圖像。

當然,兩者之間有一個中間地帶——以Twitter為例。在Twitter上,你可以上傳照片來配推文。一種混合的方法是將從圖片中提取的特征與推文相關聯。使用這種方法,您可以構建一個圖像搜索引擎,該引擎可以同時接受上下文提示和按示例搜索策略。

讓我們繼續定義一些重要的術語,這些術語將在描述和構建圖像搜索引擎時經常使用。

一些重要的術語

在深入討論之前,我們先花點時間來定義一些重要的術語。當建立一個圖像搜索引擎時,我們首先要索引我們的數據集。索引數據集是通過使用圖像描述符從每個圖像中提取特征來量化數據集的過程。圖像描述符定義了我們用來描述圖像的算法。

比如:

· 每個紅、綠、藍通道的均值和標準差

· 用圖像的統計矩來表征形狀。

· 用來描述形狀和紋理的梯度大小和方向。

這里的重要結論是,圖像描述符決定了如何對圖像進行量化。

另一方面,特性是圖像描述符的輸出。當您將圖像放入圖像描述符時,您將從另一端獲得特性。特征(或特征向量)是最基本的術語,只是用于抽象地表示和量化圖像的數字列表。

形象的用下圖表示:

用Python和OpenCV構建屬于自己的圖像搜索引擎

 

Figure 4: 圖像描述符的管道。將輸入圖像呈現給描述符,應用圖像描述符輸出特征向量(i。返回一個數字列表),用于量化圖像的內容。

這里向我們展示了一個輸入圖像,我們應用圖像描述符,然后我們的輸出是一個用于量化圖像的特性列表。然后利用距離度量或相似度函數對特征向量進行相似性比較。距離度量和相似度函數將兩個特征向量作為輸入,然后輸出一個數字,表示這兩個特征向量有多"相似"。

下圖顯示了比較兩幅圖像的過程:

用Python和OpenCV構建屬于自己的圖像搜索引擎

 

Figure 5:為了比較兩幅圖像,我們將各自的特征向量輸入距離度量/相似度函數。輸出是一個值,用于表示和量化這兩幅圖像彼此之間的"相似性"。

給定兩個特征向量,用距離函數來確定這兩個特征向量的相似程度。距離函數的輸出是一個單浮點值,用來表示兩幅圖像之間的相似性。

任意一個圖像檢索系統包含四步

無論你正在構建什么基于內容的圖像檢索系統,它們都可以歸結為4個不同的步驟:

1. 定義圖像描述符: 在這個階段,您需要決定要描述圖像的哪個方面。你對圖像的顏色感興趣嗎?圖像中物體的形狀?或者你想描述紋理?

2. 索引你的數據集: 現在,你有你的圖像描述符定義,你的工作是這個圖像描述符應用于每個圖像數據集,從這些圖像中提取的特征,編寫功能存儲(例CSV文件,RDBMS,redis,等等), 這樣便于后續比較他們的相似性。

3. 定義相似性度量: 現在你有了一堆特征向量。但是你要怎么比較它們呢? 流行的選擇包括歐氏距離、余弦距離和卡方距離,但實際的選擇高度依賴于(1)數據集和(2)提取的特征類型。

4. 搜索: 最后一步是執行實際的搜索。用戶提交查詢圖像系統(從一個上傳表單或通過移動應用程序,例如),你的工作將是(1)從這個查詢圖像, 然后提取特征(2)應用相似性函數比較查詢功能已索引的特性。然后,根據相似性函數返回最相關的結果。

同樣,這是任何CBIR系統中最基本的4個步驟。隨著它們變得更加復雜,并利用不同的特性表示,步驟的數量會增加,您將向上面提到的每個步驟添加大量的子步驟。但是現在,讓我們保持簡單,只使用這4個步驟。

讓我們看一些圖形,使這些高級步驟更具體一些。下圖詳細說明了步驟1和步驟2:

用Python和OpenCV構建屬于自己的圖像搜索引擎

 

Figure 6: 表示從數據集中每個圖像中提取特征的過程的流程圖。

我們首先獲取圖像數據集,從每個圖像中提取特征,然后將這些特征存儲在數據庫中。然后我們可以繼續執行搜索(步驟3和步驟4):

用Python和OpenCV構建屬于自己的圖像搜索引擎

 

Figure 7: 在CBIR系統上執行搜索。用戶提交查詢,描述查詢圖像,將查詢特性與數據庫中的現有特性進行比較,根據相關性對結果進行排序,然后將結果呈現給用戶。

首先,用戶必須向圖像搜索引擎提交查詢圖像。然后我們獲取查詢圖像并從中提取特性。然后將這些"查詢特性"與我們已經在數據集中索引的圖像的特性進行比較。最后,根據相關性對結果進行排序并呈現給用戶。

度假照片數據庫

我們將使用INRIA Holidays數據集作為我們的圖像數據集。

這個數據集包含了來自世界各地的各種度假旅行,包括埃及金字塔的照片、海底與海洋生物的潛水、山上的森林、晚餐時的酒瓶和餐盤、劃船遠足和大洋對岸的日落。

以下是數據集中的一些例子:

用Python和OpenCV構建屬于自己的圖像搜索引擎

 

Figure 8: 來自INRIA Holidays數據集的示例圖像。我們將使用這個數據集來構建我們的圖像搜索引擎。

一般來說,這個數據集在建模我們期望游客在風景旅游中拍攝什么方面做得非常好。

目標

我們的目標是建立一個個人圖像搜索引擎。給定我們的度假照片數據集,我們希望通過創建一個"更像這樣"的功能使這個數據集"可搜索"——這將是一個"示例搜索"圖像搜索引擎。例如,如果我提交一張帆船劃過河流的照片,我們的圖像搜索引擎應該能夠找到并檢索我們在游覽碼頭和碼頭時的度假照片。

請看下面的例子,我提交了一張水上船只的照片,并在我們的度假照片收藏中找到了相關的圖片:

用Python和OpenCV構建屬于自己的圖像搜索引擎

 

Figure 9: 我們的圖像搜索引擎的一個例子。我們提交了一個包含海上船只的查詢圖像。返回給我們的結果是相關的,因為它們也包括船只和大海。

為了構建這個系統,我們將使用一個簡單但有效的圖像描述符: 顏色直方圖。通過使用顏色直方圖作為圖像描述符,我們將依賴于圖像的顏色分布。因此,我們必須對我們的圖像搜索引擎做出一個重要的假設:

假設: 具有相似顏色分布的圖像將被認為彼此相關。即使圖像的內容有很大的不同,只要它們的顏色分布也相似,它們仍然會被認為是"相似的"。這是一個非常重要的假設,但是當使用顏色直方圖作為圖像描述符時,這通常是一個公平合理的假設。

步驟1: 定義圖像描述符

我們不使用標準的顏色直方圖,而是使用一些技巧,使它更加健壯和強大。

我們的圖像描述符將是HSV顏色空間(色調、飽和度、值)中的三維顏色直方圖。通常,圖像表示為紅、綠和藍(RGB)的三元組。我們經常把RGB顏色空間想象成"立方體",如下圖所示:

用Python和OpenCV構建屬于自己的圖像搜索引擎

 

Figure 10: RGB立方體的例子

然而,雖然RGB值很容易理解,但是RGB顏色空間不能模擬人類感知顏色的方式。相反,我們將使用HSV顏色空間,它將像素強度映射到一個圓柱體:

用Python和OpenCV構建屬于自己的圖像搜索引擎

 

Figure 11: HSV圓柱體的例子

還有其他一些顏色空間在模仿人類感知顏色方面做得更好,比如CIE L*a*b*和CIE XYZ空間,但是讓我們在第一個圖像搜索引擎實現中保持顏色模型相對簡單。

現在我們已經選擇了一個顏色空間,現在我們需要為直方圖定義方柱的數量。直方圖是用來給一個(粗略)的感覺像素強度的密度在一個圖像。本質上,我們的直方圖將估計底層函數的概率密度,在本例中,是圖像i中出現像素顏色C的概率P。如果你選擇了太少的方柱,那么你的直方圖將會有更少的成分,并且無法消除具有本質上不同顏色分布的圖像之間的歧義。同樣的,如果你使用了太多的方柱,你的直方圖將會有很多的成分和圖像非常相似的內容可能會被認為和不相似的時候,實際上他們是。下面是一個只有幾個方柱的直方圖的例子:

用Python和OpenCV構建屬于自己的圖像搜索引擎

 

Figure 12: 一個9箱直方圖的例子。

請注意,對于要表示的給定像素而言,算是非常少的了。

下面是一個有很多方柱的直方圖的例子:

用Python和OpenCV構建屬于自己的圖像搜索引擎

 

Figure 13: 一個128箱直方圖的例子。請注意,一個給定的像素可以放在很多方柱中。

在上面的例子中可以看到, 用了很多的方柱, 但隨著更多的方柱, 你失去了概括圖像之間相似的感知內容的能力, 因為所有的高峰和低谷的直方圖將不得不為了匹配兩個圖像是否是類似的。就我個人而言,我喜歡一種可迭代的、實驗性的方法來調整方柱的數量。這種迭代方法通常基于我的數據集的大小。我的數據集越小,我使用的方柱就越少。如果我的數據集很大,我使用更多的方柱,使我的直方圖更大,更有辨別力。

一般情況下,您需要對顏色直方圖描述符的方柱數進行實驗,因為它依賴于(1)數據集的大小和(2)數據集中的顏色分布彼此之間的相似性。

為我們的假期照片圖像搜索引擎, 我們將利用3D顏色直方圖,其中8個方柱用于HSV顏色空間中對的色彩通道,12個方柱為"飽和度"通道和3個方柱為"值"通道,總特征向量維度為 8 x 12 x 3 = 288。

這意味著,對于我們數據集中的每個圖像,無論是36 x 36像素的圖像還是2000 x 1800像素的圖像,都將只使用288個浮點數列表抽象地表示和量化所有圖像。我認為解釋3D直方圖最好的方法是使用連接詞AND。3D HSV顏色描述符將詢問給定的圖像中,有多少像素的色相值落在第1個方柱中,和有多少像素的飽和度值落在第1方柱中,以及有多少像素的強度值落在第1方柱中。然后列出滿足這些要求的像素數。對每個方柱的組合重復這個過程; 然而,我們能夠以一種計算效率極高的方式來做這件事。

新建文件,命名為colordescriptor.py,讓我們開始編寫需要的代碼:

Python

# import the necessary packages
import numpy as np
import cv2
import imutils
 
class ColorDescriptor:
	def __init__(self, bins):
		# store the number of bins for the 3D histogram
		self.bins = bins
 
	def describe(self, image):
		# convert the image to the HSV color space and initialize
		# the features used to quantify the image
		image = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)
		features = []
 
		# grab the dimensions and compute the center of the image
		(h, w) = image.shape[:2]
		(cX, cY) = (int(w * 0.5), int(h * 0.5))

我們將從導入我們需要的Python包開始。我們將使用NumPy進行數值處理,使用cv2進行OpenCV綁定,并使用imutils檢查OpenCV版本。然后在第6行定義ColorDescriptor類。這個類將封裝從圖像中提取3D HSV顏色直方圖所需的所有邏輯。ColorDescriptor的_init__方法只接受一個參數bin,它是顏色直方圖的bin數量。然后我們可以在第11行定義description方法。這個方法需要一個圖像,也就是我們要描述的圖像。在描述方法的內部,我們將從RGB顏色空間(或者更確切地說,BGR顏色空間;OpenCV將RGB圖像表示為NumPy數組,但順序相反)到HSV顏色空間,然后初始化我們的特性列表以量化和表示ourimage。第18行和第19行簡單地獲取圖像的維數并計算中心(x, y)坐標。所以現在艱苦的工作開始了。我們不計算整個圖像的三維HSV顏色直方圖,而是計算圖像不同區域的三維HSV顏色直方圖。使用基于區域的直方圖而不是全局直方圖可以讓我們模擬顏色分布中的局部。例如,請看下面這張圖片:

用Python和OpenCV構建屬于自己的圖像搜索引擎

 

Figure 14: 查詢圖像實例.

在這張照片中,我們可以清楚地看到圖片頂部的藍天和底部的沙灘。使用全局直方圖,我們將無法確定圖像中藍色出現的位置和棕色沙粒出現的位置。相反,我們只知道存在一定比例的藍色和一定比例的棕色。為了解決這個問題,我們可以計算圖像區域的顏色直方圖:

用Python和OpenCV構建屬于自己的圖像搜索引擎

 

Figure 15: 把我們的圖像分成5個不同的部分的例子。

對于圖像描述符,我們將圖像分為五個不同的區域:(1)左上角,(2)右上角,(3)右下角,(4)左下角,最后(5)圖像的中心。利用這些地區我們會能夠模仿一種原油的定位,能夠代表我們的海灘上方的形象在左上的深淺的藍色的天空和右上的角落,棕色砂在左下角和右下角的角落,然后結合藍天和布朗在中心地區的沙子。也就是說,下面是創建基于區域的顏色描述符的代碼:

Python

		# divide the image into four rectangles/segments (top-left,
		# top-right, bottom-right, bottom-left)
		segments = [(0, cX, 0, cY), (cX, w, 0, cY), (cX, w, cY, h),
			(0, cX, cY, h)]
 
		# construct an elliptical mask representing the center of the
		# image
		(axesX, axesY) = (int(w * 0.75) // 2, int(h * 0.75) // 2)
		ellipMask = np.zeros(image.shape[:2], dtype = "uint8")
		cv2.ellipse(ellipMask, (cX, cY), (axesX, axesY), 0, 0, 360, 255, -1)
 
		# loop over the segments
		for (startX, endX, startY, endY) in segments:
			# construct a mask for each corner of the image, subtracting
			# the elliptical center from it
			cornerMask = np.zeros(image.shape[:2], dtype = "uint8")
			cv2.rectangle(cornerMask, (startX, startY), (endX, endY), 255, -1)
			cornerMask = cv2.subtract(cornerMask, ellipMask)
 
			# extract a color histogram from the image, then update the
			# feature vector
			hist = self.histogram(image, cornerMask)
			features.extend(hist)
 
		# extract a color histogram from the elliptical region and
		# update the feature vector
		hist = self.histogram(image, ellipMask)
		features.extend(hist)
 
		# return the feature vector
		return features

第23行和第24行分別定義了左上角、右上角、右下角和左下角區域的索引。然后,我們需要構造一個橢圓來表示圖像的中心區域。我們將通過在第28行定義一個橢圓半徑,它是圖像寬度和高度的75%來實現這一點。然后初始化一個空白圖像(用0填充以表示黑色背景),其尺寸與我們想在第29行描述的圖像相同。最后,讓我們使用cv2.ellipse 函數在第30行上畫出實際的橢圓。這個函數需要八個不同的參數:

1. ellipMask: 我們想要繪制橢圓的圖像。我們將使用掩碼的概念,稍后我將對此進行討論。

2. (cX, cY): 表示圖像中心(x, y)坐標的2元組。

3. (axesX, axesY): 表示橢圓軸線長度的二元組。在本例中,橢圓將拉伸到我們所描述的圖像的寬度和高度的75%。

4. 0: 橢圓的旋轉。在這種情況下,不需要旋轉,所以我們提供一個0度的值。

5. 0: 橢圓的起始角。

6. 360: 橢圓的結束角。查看前面的參數,這表明我們將繪制一個從0到360度的橢圓(一個完整的圓)。

7. 255: 橢圓的顏色。255的值表示白色,這意味著我們的橢圓將在黑色背景上繪制為白色。

8. -1: 橢圓的邊框大小。提供一個正整數r將繪制一個大小為r像素的邊框。為r提供一個負值將填充橢圓。

然后為第36行上的每個角掩模分配內存,在第37行上繪制一個表示圖像角的白色矩形,然后從第38行上的矩形中減去中心橢圓。如果我們要使這個在角段上循環的過程動畫化它看起來會像這樣:

用Python和OpenCV構建屬于自己的圖像搜索引擎

 

Figure 16: 為要提取特征的圖像的每個區域構造掩碼。

正如這個動畫所示,我們分別檢查每個角段,在每次迭代時從矩形中刪除橢圓的中心。你可能會想,難道我們不應該從圖像中提取顏色直方圖嗎?為什么要做這些偽裝?好問題。原因是我們需要掩碼來指示OpenCV直方圖函數從哪里提取顏色直方圖。記住,我們的目標是分別描述這些部分。表示這些段的最有效方法是使用掩碼。直方圖計算中只包括(x, y)-在掩模中具有對應(x, y)位置的(x, y)坐標,其像素值為白色(255)。如果遮罩中(x, y)坐標的像素值為black(0),則會忽略它。要重申在直方圖中只包含具有相應掩碼值為白色的像素的概念,請看下面的動畫:

用Python和OpenCV構建屬于自己的圖像搜索引擎

 

Figure 17: 將掩碼區域應用于圖像。注意,如果左邊圖像中的像素在右邊圖像中具有相應的白色掩碼值,則只顯示左邊圖像中的像素。

如您所見,直方圖計算中只包含圖像掩碼區域的像素。現在明白了吧?現在,對于我們的每一個段,我們調用第42行上的直方圖方法,用我們想要提取特征的圖像作為第一個參數來提取顏色直方圖,用表示我們想要描述的區域的掩碼作為第二個參數來提取顏色直方圖。然后,直方圖方法返回表示當前區域的顏色直方圖,并將其添加到特性列表中。第47和48行提取中心(橢圓)區域的顏色直方圖,并更新特征列表a。最后,第51行將我們的特征向量返回給調用函數。現在,讓我們快速看看實際的直方圖方法:

Python

	def histogram(self, image, mask):
		# extract a 3D color histogram from the masked region of the
		# image, using the supplied number of bins per channel
		hist = cv2.calcHist([image], [0, 1, 2], mask, self.bins,
			[0, 180, 0, 256, 0, 256])
 
		# normalize the histogram if we are using OpenCV 2.4
		if imutils.is_cv2():
			hist = cv2.normalize(hist).flatten()
 
		# otherwise handle for OpenCV 3+
		else:
			hist = cv2.normalize(hist, hist).flatten()
 
		# return the histogram
		return hist

直方圖方法需要兩個參數:第一個參數是要描述的圖像,第二個參數是表示要描述的圖像區域的掩碼。通過調用cv2,在第56行和第57行處理圖像掩碼區域的直方圖。使用我們的構造函數提供的桶數calcHist。我們的顏色直方圖在第61行或第65行進行標準化(取決于OpenCV版本),以獲得尺度不變性。這意味著如果我們計算兩個相同圖像的顏色直方圖,除了其中一個比另一個大50%,我們的顏色直方圖將(幾乎)相同。將顏色直方圖標準化是非常重要的,這樣每個直方圖都由特定bin的相對百分比計數表示,而不是每個bin的整數計數。再次強調,執行這種標準化將確保具有相似內容但維度顯著不同的圖像在應用相似性函數后仍然是相似的。最后,將標準化的3D HSV顏色直方圖返回到第68行上的調用函數。

步驟2:從圖像數據中提取特征

現在我們已經定義了圖像描述符,我們可以繼續到步驟2,并從數據集中的每個圖像中提取特征(即顏色直方圖)。提取特征并將其存儲在持久存儲上的過程通常稱為"索引"。

讓我們繼續深入研究一些代碼來索引我們的度假照片數據集。打開一個新文件,命名為index.py,讓我們得到索引:

Python

# import the necessary packages
from pyimagesearch.colordescriptor import ColorDescriptor
import argparse
import glob
import cv2
 
# construct the argument parser and parse the arguments
ap = argparse.ArgumentParser()
ap.add_argument("-d", "--dataset", required = True,
	help = "Path to the directory that contains the images to be indexed")
ap.add_argument("-i", "--index", required = True,
	help = "Path to where the computed index will be stored")
args = vars(ap.parse_args())
 
# initialize the color descriptor
cd = ColorDescriptor((8, 12, 3))

我們將從導入我們需要的包開始。您將記得第1步中的ColorDescriptor類,出于組織目的,我決定將它放在pyimagesearch模塊中。我們還需要argparse來解析命令行參數,glob來獲取到圖像的文件路徑,以及cv2來綁定OpenCV。解析命令行參數是在第8-13行進行的。我們將需要兩個開關,—dataset,它是到我們的假期照片目錄的路徑,—index,它是包含圖像文件名和與每個圖像相關的特性的輸出CSV文件。最后,我們在第16行使用8個色調箱、12個飽和度箱和3個值箱初始化ColorDescriptor。現在一切都初始化了,我們可以從數據集中提取特性:

Python

# open the output index file for writing
output = open(args["index"], "w")
 
# use glob to grab the image paths and loop over them
for imagePath in glob.glob(args["dataset"] + "/*.png"):
	# extract the image ID (i.e. the unique filename) from the image
	# path and load the image itself
	imageID = imagePath[imagePath.rfind("/") + 1:]
	image = cv2.imread(imagePath)
 
	# describe the image
	features = cd.describe(image)
 
	# write the features to file
	features = [str(f) for f in features]
	output.write("%s,%sn" % (imageID, ",".join(features)))
 
# close the index file
output.close()

讓我們打開輸出文件,在第19行進行編寫,然后在第22行循環遍歷數據集中的所有圖像。對于每個圖像,我們將提取一個imageID,它只是圖像的文件名。對于這個示例搜索引擎,我們將假設所有文件名都是惟一的,但是我們同樣可以為每個圖像生成一個UUID。然后我們將在第26行從磁盤加載圖像。現在已經加載了圖像,讓我們繼續應用圖像描述符并從第29行圖像中提取特性。ColorDescriptor的describe方法返回一個浮點值列表,用于表示和量化圖像。這個數字列表或特征向量包含我們在步驟1中描述的5個圖像區域的每個表示。每個部分都用8 x 12 x 3 = 288個條目的直方圖表示。給定5個元素,我們的整體特征向量是5x288 = 1440維。因此,每幅圖像都用1440個數字進行量化和表示。第32行和第33行簡單地將圖像的文件名及其相關的特征向量寫入文件。要索引我們的度假照片數據集,打開一個shell并發出以下命令:

Shell

$ python index.py --dataset dataset --index index.csv

這個腳本的運行時間不會超過幾秒鐘。完成后,您將擁有一個新文件index.csv。使用您最喜歡的文本編輯器打開這個文件并查看其中的內容。您將看到,對于.csv文件中的每一行,第一個條目是文件名,后面跟著一個數字列表。這些數字是你的特征向量,用來表示和量化圖像。在索引上運行wc,可以看到我們已經成功地為805張圖像的數據集建立了索引:

Shell

$ wc -l index.csv
 805 index.csv

步驟3:搜索Step 3: The Searcher

現在我們已經從數據集中提取了特征,我們需要一個方法來比較這些特征的相似性。這就是第3步的作用—我們現在準備創建一個類來定義兩個圖像之間的實際相似性度量。

創建一個新文件,命名為 searcher.py 看看有什么神奇的事情發生:

Python

# import the necessary packages
import numpy as np
import csv
 
class Searcher:
	def __init__(self, indexPath):
		# store our index path
		self.indexPath = indexPath
 
	def search(self, queryFeatures, limit = 10):
		# initialize our dictionary of results
		results = {}

我們將繼續導入NumPy進行數值處理,并導入csv以方便解析index.csv文件。在此基礎上,讓我們在第5行定義Searcher類。搜索器的構造函數只需要一個參數indexPath,它是index.csv文件位于磁盤上的路徑。為了實際執行搜索,我們將調用第10行中的search方法。該方法將獲取兩個參數,即從查詢圖像中提取的queryFeatures(即我們將提交給CBIR系統并要求與之相似的圖像),并限制返回的結果的最大數量。最后,我們在第12行初始化結果字典。在這種情況下,dictionary是一種很好的數據類型,因為它允許我們使用給定圖像的(惟一的)imageID作為鍵,并使用查詢的相似性作為值。好了,注意這里。這就是奇跡發生的地方:

Python

		# open the index file for reading
		with open(self.indexPath) as f:
			# initialize the CSV reader
			reader = csv.reader(f)
 
			# loop over the rows in the index
			for row in reader:
				# parse out the image ID and features, then compute the
				# chi-squared distance between the features in our index
				# and our query features
				features = [float(x) for x in row[1:]]
				d = self.chi2_distance(features, queryFeatures)
 
				# now that we have the distance between the two feature
				# vectors, we can udpate the results dictionary -- the
				# key is the current image ID in the index and the
				# value is the distance we just computed, representing
				# how 'similar' the image in the index is to our query
				results[row[0]] = d
 
			# close the reader
			f.close()
 
		# sort our results, so that the smaller distances (i.e. the
		# more relevant images are at the front of the list)
		results = sorted([(v, k) for (k, v) in results.items()])
 
		# return our (limited) results
		return results[:limit]

我們在第15行打開index.csv文件,在第17行獲取CSV閱讀器的句柄,然后在第20行開始循環遍歷index.csv文件的每一行。對于每一行,我們提取與索引圖像關聯的顏色直方圖,然后使用chi2_distance(第25行)將其與查詢圖像特性進行比較,我將在稍后定義該特性。我們的結果字典在第32行更新,使用惟一的圖像文件名作為鍵,查詢圖像與索引圖像的相似性作為值。最后,我們所要做的就是按照相似度值的升序對結果字典進行排序。卡方相似度為0的圖像將被視為彼此相同。隨著卡方相似度值的增大,圖像之間的相似度降低。說到卡方相似度,我們來定義這個函數:

Python

	def chi2_distance(self, histA, histB, eps = 1e-10):
		# compute the chi-squared distance
		d = 0.5 * np.sum([((a - b) ** 2) / (a + b + eps)
			for (a, b) in zip(histA, histB)])
 
		# return the chi-squared distance
		return d

我們的chi2_distance函數需要兩個參數,這兩個參數就是我們要比較的相似性的兩個直方圖。一個可選的eps值用于防止零除錯誤。該函數的名稱來源于皮爾遜s卡方檢驗統計量,該統計量用于比較離散概率分布。由于我們在比較顏色直方圖,而顏色直方圖的定義是概率分布,所以卡方函數是一個很好的選擇。一般來說,大方柱和小方柱之間的區別不那么重要,應該這樣加權這就是卡方距離函數的作用。你還和我們在一起嗎?我們快到了,我保證。最后一步實際上是最簡單的,它只是一個將所有部件粘合在一起的驅動程序。

步驟4:執行一個搜索

實際上,它只是一個導入我們前面定義的所有包的驅動程序,并將它們相互結合使用來構建一個完整的基于內容的圖像檢索系統。

打開最后一個文件,命名為search.py:

Python

# import the necessary packages
from pyimagesearch.colordescriptor import ColorDescriptor
from pyimagesearch.searcher import Searcher
import argparse
import cv2
 
# construct the argument parser and parse the arguments
ap = argparse.ArgumentParser()
ap.add_argument("-i", "--index", required = True,
	help = "Path to where the computed index will be stored")
ap.add_argument("-q", "--query", required = True,
	help = "Path to the query image")
ap.add_argument("-r", "--result-path", required = True,
	help = "Path to the result path")
args = vars(ap.parse_args())
 
# initialize the image descriptor
cd = ColorDescriptor((8, 12, 3))

我們要做的第一件事是導入必要的包。我們將從步驟1導入colordescriptor,以便從查詢圖像中提取特性。我們還將導入我們在步驟3中定義的搜索器以便我們能夠執行實際的搜索。argparse和cv2包完成了導入。然后在第8-15行解析命令行參數。我們需要一個——index,它是index.csv文件所在的路徑。我們還需要一個——query,它是到查詢映像的路徑。此圖像將與索引中的每個圖像進行比較。我們的目標是在索引中找到與查詢圖像相似的圖像。當您進入谷歌并輸入術語Python OpenCV教程時,您會發現搜索結果中包含與學習Python和OpenCV相關的信息。同樣,如果我們正在為我們的度假照片構建一個圖像搜索引擎,并提交一張藍色海洋和白色浮云上的帆船圖片,我們希望從圖像搜索引擎中返回類似的海洋視圖圖像。然后,我們將請求一個——result-path,它是到我們的假期照片數據集的路徑。我們需要這個開關,因為我們需要向用戶顯示實際的結果圖像。最后,我們在第18行使用與索引步驟中相同的參數初始化圖像描述符。如果我們的目的是比較圖像的相似性(確實如此),那么將顏色直方圖中的箱子數量從索引改為搜索是沒有意義的。簡單地說:在步驟4中使用與步驟3中相同數量的箱子來處理顏色直方圖。這將確保您的圖像以一致的方式描述,從而具有可比性。好了,現在來執行實際的搜索:

Python

# load the query image and describe it
query = cv2.imread(args["query"])
features = cd.describe(query)
 
# perform the search
searcher = Searcher(args["index"])
results = searcher.search(features)
 
# display the query
cv2.imshow("Query", query)
 
# loop over the results
for (score, resultID) in results:
	# load the result image and display it
	result = cv2.imread(args["result_path"] + "/" + resultID)
	cv2.imshow("Result", result)
	cv2.waitKey(0)

我們在第21行從磁盤加載查詢圖像,并在第22行從中提取特性。然后使用從查詢圖像中提取的特性在第25行和第26行執行搜索,返回我們的排序結果列表。從這里開始,我們需要做的就是向用戶顯示結果。我們在第29行向用戶顯示查詢圖像。然后在第32-36行循環搜索結果,并將它們顯示在屏幕上。經過這么多工作,我相信你已經準備好看到這個系統的運行了,是嗎? 繼續讀下去,我們所有的努力都會有回報的。

看看我們構建的CBIR系統表現怎么樣

打開終端,導航到代碼所在的目錄,并發出以下命令:

Shell

$ python search.py --index index.csv --query queries/108100.png --result-path dataset
用Python和OpenCV構建屬于自己的圖像搜索引擎

 

Figure 18: 搜索我們的假期圖像數據集的圖片金字塔和埃及。

您將看到的第一個圖像是我們的查詢圖像的埃及金字塔。我們的目標是在數據集中找到相似的圖像。正如你所看到的,我們已經清楚地找到了我們訪問金字塔時數據集的其他照片。

我們還參觀了埃及的其他地區。讓我們嘗試另一個查詢圖像:

Shell

$ python search.py --index index.csv --query queries/115100.png --result-path dataset
用Python和OpenCV構建屬于自己的圖像搜索引擎

 

Figure 19: 我們的圖像搜索引擎的結果為埃及其他地區。請注意,藍色的天空始終出現在搜索結果中。

請務必密切關注我們的查詢圖像圖像。請注意,在圖像的上部區域,天空是一種明亮的藍色陰影。注意我們在圖片的底部和中心有棕色和棕色的沙漠和建筑。果然,在我們的結果中,返回給我們的圖像的上方區域是藍天,下方是棕黃色的沙漠和建筑物。這是因為我們在本文前面詳細介紹了基于區域的顏色直方圖描述符。通過使用這個圖像描述符,我們已經能夠執行一種粗略的本地化形式,為顏色直方圖提供關于圖像中像素強度發生在何處的提示。接下來的假期我們在海灘停了下來。執行以下命令搜索海灘照片

Shell

$ python search.py --index index.csv --query queries/103300.png --result-path dataset
用Python和OpenCV構建屬于自己的圖像搜索引擎

 

Figure 20: 使用OpenCV構建的基于內容的圖像檢索系統在數據集中查找海灘圖像。

請注意前三個結果是如何來自海灘之旅中完全相同的位置的。其余的結果圖像包含藍色陰影。

當然,沒有浮渣潛水的海灘之旅是不完整的:

Shell

$ python search.py --index index.csv --query queries/103100.png --result-path dataset
用Python和OpenCV構建屬于自己的圖像搜索引擎

 

Figure 21: 同樣,我們的圖像搜索引擎能夠返回相關的結果。因此,時間,水下探險

這次搜索的結果尤其令人印象深刻。排名前五的結果都來自于同一條魚,而排名前十的結果中,除了一條以外,其余都來自于水下游。

經過漫長的一天,終于到了看日落的時間了:

Shell

$ python search.py --index index.csv --query queries/127502.png --result-path dataset
用Python和OpenCV構建屬于自己的圖像搜索引擎

 

Figure 22: 我們的OpenCV圖像搜索引擎能夠在我們的度假照片數據集中找到日落的圖像。

這些搜索結果也相當不錯——返回的所有圖像都是黃昏時分的日落。好了! 你的第一個圖像搜索引擎搞定了。

總結

在這篇博客文章中,我們探討了如何構建一個圖像搜索引擎,使我們的度假照片可搜索。

我們使用顏色直方圖來描述照片的顏色分布。然后,我們使用顏色描述符為數據集建立索引,從數據集中的每個圖像中提取顏色直方圖。

為了比較圖像,我們使用了卡方距離,這是比較離散概率分布時的常用選擇。

在此基礎上,我們實現了接受查詢圖像并返回相關結果的必要邏輯。

下一步

那么接下來的步驟是什么呢?

正如您所看到的,與我們的圖像搜索引擎交互的唯一方法是通過命令行—這不是很有吸引力。

在下一篇文章中,我們將探索如何在Python web框架中包裝我們的圖像搜索引擎,使其使用起來更簡單、更性感。

原文地址:https://www.pyimagesearch.com/2014/12/01/complete-guide-building-image-search-engine-python-opencv/

分享到:
標簽:Python
用戶無頭像

網友整理

注冊時間:

網站:5 個   小程序:0 個  文章:12 篇

  • 51998

    網站

  • 12

    小程序

  • 1030137

    文章

  • 747

    會員

趕快注冊賬號,推廣您的網站吧!
最新入駐小程序

數獨大挑戰2018-06-03

數獨一種數學游戲,玩家需要根據9

答題星2018-06-03

您可以通過答題星輕松地創建試卷

全階人生考試2018-06-03

各種考試題,題庫,初中,高中,大學四六

運動步數有氧達人2018-06-03

記錄運動步數,積累氧氣值。還可偷

每日養生app2018-06-03

每日養生,天天健康

體育訓練成績評定2018-06-03

通用課目體育訓練成績評定