目標
在本章中,我們將學習直方圖反投影。
理論
這是由Michael J. Swain和Dana H. Ballard在他們的論文《通過顏色直方圖索引》中提出的。
用簡單的話說是什麼意思?它用於圖像分割或在圖像中查找感興趣的對象。簡而言之,它創建的圖像大小與輸入圖像相同(但只有一個通道),其中每個像素對應於該像素屬於我們物體的概率。用更簡單的話來說,與其餘部分相比,輸出圖像將在可能有對象的區域具有更多的白色值。好吧,這是一個直觀的解釋。(我無法使其更簡單)。直方圖反投影與camshift算法等配合使用。
我們該怎麼做呢?我們創建一個圖像的直方圖,其中包含我們感興趣的對象(在我們的示例中是背景,離開播放器等)。對象應儘可能填充圖像以獲得更好的效果。而且顏色直方圖比灰度直方圖更可取,因為對象的顏色對比灰度強度是定義對象的好方法。然後,我們將該直方圖「反投影」到需要找到對象的測試圖像上,換句話說,我們計算出屬於背景的每個像素的概率並將其顯示出來。在適當的閾值下產生的輸出使我們僅獲得背景。
Numpy中的算法
1.首先,我們需要計算我們要查找的對象(使其為「 M」)和要搜索的圖像(使其為「 I」)的顏色直方圖。
import numpy as npimport cv2 as cvfrom matplotlib import pyplot as plt#roi是我們需要找到的對象或對象區域roi = cv.imread('rose_red.png')hsv = cv.cvtColor(roi,cv.COLOR_BGR2HSV)#目標是我們搜索的圖像target = cv.imread('rose.png')hsvt = cv.cvtColor(target,cv.COLOR_BGR2HSV)# 使用calcHist查找直方圖。也可以使用np.histogram2d完成M = cv.calcHist([hsv],[0, 1], None, [180, 256], [0, 180, 0, 256] )I = cv.calcHist([hsvt],[0, 1], None, [180, 256], [0, 180, 0, 256] )2.求出比值R=M/I。然後反向投影R,即使用R作為調色板,並以每個像素作為其對應的目標概率創建一個新圖像。即B(x,y) = R[h(x,y),s(x,y)] 其中h是色調,s是像素在(x,y)的飽和度。之後,應用條件B(x,y) = min[B(x,y), 1]。
h,s,v = cv.split(hsvt)B = R[h.ravel(),s.ravel()]B = np.minimum(B,1)B = B.reshape(hsvt.shape[:2])3.現在對圓盤應用卷積,B = D * B,其中D是圓盤內核。
disc = cv.getStructuringElement(cv.MORPH_ELLIPSE,(5,5))cv.filter2D(B,-1,disc,B)B = np.uint8(B)cv.normalize(B,B,0,255,cv.NORM_MINMAX)4.現在最大強度的位置給了我們物體的位置。如果我們期望圖像中有一個區域,則對合適的值進行閾值處理將獲得不錯的結果。
ret,thresh = cv.threshold(B,50,255,0) 就是這樣!!
OpenCV的反投影
OpenCV提供了一個內建的函數cv.calcBackProject()。它的參數幾乎與cv.calchist()函數相同。它的一個參數是直方圖,也就是物體的直方圖,我們必須找到它。另外,在傳遞給backproject函數之前,應該對對象直方圖進行歸一化。它返回概率圖像。然後我們用圓盤內核對圖像進行卷積並應用閾值。下面是我的代碼和結果:
import numpy as npimport cv2 as cvroi = cv.imread('rose_red.png')hsv = cv.cvtColor(roi,cv.COLOR_BGR2HSV)target = cv.imread('rose.png')hsvt = cv.cvtColor(target,cv.COLOR_BGR2HSV)# 計算對象的直方圖roihist = cv.calcHist([hsv],[0, 1], None, [180, 256], [0, 180, 0, 256] )# 直方圖歸一化並利用反傳算法cv.normalize(roihist,roihist,0,255,cv.NORM_MINMAX)dst = cv.calcBackProject([hsvt],[0,1],roihist,[0,180,0,256],1)# 用圓盤進行卷積disc = cv.getStructuringElement(cv.MORPH_ELLIPSE,(5,5))cv.filter2D(dst,-1,disc,dst)# 應用閾值作與操作ret,thresh = cv.threshold(dst,50,255,0)thresh = cv.merge((thresh,thresh,thresh))res = cv.bitwise_and(target,thresh)res = np.vstack((target,thresh,res))cv.imwrite('res.jpg',res)以下是我處理過的一個示例。我將藍色矩形內的區域用作示例對象,我想提取整個地面。
附加資源
"Indexing via color histograms", Swain, Michael J. , Third international conference on computer vision,1990.