OpenCV-Python教程:直方圖比對、直方圖反投影(compareHist,calcBackProject)


目錄

1、直方圖比對compareHist

2、直方圖反投影calcBackProject

擴展閱讀:



返回Opencv-Python教程

1、直方圖比對compareHist

通過compareHist()可以從直方圖的角度對比2幅圖像的相關性,比較的對象可以是1D或2D直方圖。

接口形式:

cv2.compareHist(H1, H2, method) ->retval
  • 參數含義:
  • H1:輸入圖像直方圖;
  • H2:輸入圖像直方圖,和H1相同的尺寸;
  • method:比較方法;

method包含6種方法,整數數值從0~5:

enum  	cv::HistCompMethods {
  cv::HISTCMP_CORREL = 0,
  cv::HISTCMP_CHISQR = 1,
  cv::HISTCMP_INTERSECT = 2,
  cv::HISTCMP_BHATTACHARYYA = 3,
  cv::HISTCMP_HELLINGER = HISTCMP_BHATTACHARYYA,
  cv::HISTCMP_CHISQR_ALT = 4,
  cv::HISTCMP_KL_DIV = 5
}

下面這個例子對讀出的圖像分別經過平滑、像素值減去一個數、加上一個數的操作,分別計算着4幅圖像的直方圖,並和源圖像做直方圖比對,就得到源圖像和源圖像、平滑圖像、加數圖像、減數圖像的4個比對結果:

import numpy as np
import matplotlib.pyplot as plt
import cv2
print('VX公眾號: 桔子code / juzicode.com')
print('cv2.__version__:',cv2.__version__)
plt.rc('font',family='Youyuan',size='9')
plt.rc('axes',unicode_minus='False')

img_src = cv2.imread('..\\samples\\data\\lena.jpg',0) 
img_comp1 = cv2.blur(img_src,(13,13))
img_comp2 = cv2.subtract(img_src,35)
img_comp3 = cv2.add(img_src,35)
#計算直方圖
histSize = 256
histRange = (0, 256) 
hist_src = cv2.calcHist([img_src], [0], None, [histSize], histRange) 
hist_comp1 = cv2.calcHist([img_comp1], [0], None, [histSize], histRange)  
hist_comp2 = cv2.calcHist([img_comp2], [0], None, [histSize], histRange)  
hist_comp3 = cv2.calcHist([img_comp3], [0], None, [histSize], histRange)  
#直方圖比較
for method in range(6):
    src_src = cv2.compareHist(hist_src, hist_src, method)
    src_comp1 = cv2.compareHist(hist_src, hist_comp1, method)
    src_comp2 = cv2.compareHist(hist_src, hist_comp2, method)
    src_comp3 = cv2.compareHist(hist_src, hist_comp3, method)
    print('method=%d src blur sub add'%method,src_src,src_comp1,src_comp2,src_comp3)

#顯示圖像
fig,ax = plt.subplots(2,2)
ax[0,0].set_title('hist_src')
ax[0,0].plot(hist_src) 
ax[0,1].set_title('blur')
ax[0,1].plot(hist_comp1)
ax[1,0].set_title('sub')
ax[1,0].plot(hist_comp2)
ax[1,1].set_title('add')
ax[1,1].plot(hist_comp3)
#ax[0,0].axis('off');ax[0,1].axis('off');ax[1,0].axis('off');ax[1,1].axis('off')#關閉坐標軸顯示
plt.show()  

運行結果:

從顯示的直方圖可以看到,平滑處理后圖像的直方圖和源圖像差不多一致,加數或減數會導致直方圖左右或右移,可以推測平滑處理的直方圖比對結果和源圖像會比較接近,而加數或減數圖像的直方圖比對結果會相差更大,下面打印的比較結果數值可以驗證該結論:

VX公眾號: 桔子code / juzicode.com
cv2.__version__: 4.5.3
method=0 src blur sub add 1.0 0.9285279683865829 0.35037265936214546 0.3512659314459189
method=1 src blur sub add 0.0 23850.06026419833 7146491.858903675 5844706.253864458
method=2 src blur sub add 262144.0 236732.0 167915.0 167915.0
method=3 src blur sub add 0.0 0.15727062402652872 0.4038869008245676 0.4038869008245676
method=4 src blur sub add 0.0 37400.61455341503 217214.77007193133 217214.77007193133
method=5 src blur sub add 0.0 79978.49978387816 462272.6122239813 1060797.6089261884

method=0和method=2表示的HISTCMP_CORREL和HISTCMP_INTERSECT比對,得到的比對值越大表示2幅圖像越接近;method的其他值表示的比對方法得到的值越小表示2幅圖像越接近。

下圖是前面例子中得到的4張圖像,平滑處理的圖像(blur)模糊不清,人眼看起來和源圖像(src)要顯得“更不像”一些,而加減數得到的圖像(sub,add)和源圖像除了亮度幾乎沒有差異。但是通過compareHist()計算得到的對比結果卻認為平滑處理的圖像和源圖像要“更像”一些,加減數得到的圖像卻“更不像”一些,這點和常識是有些相悖的。

得出這個“悖論”的原因是compareHist()比對的是直方圖,也就是像素值分布的比對,而人眼觀察到的清晰度、梯度變化等內容在直方圖里是體現不出來的。下面我們再通過一個更極端的例子來看看直方圖比對的局限性,我們將前面代碼中的img_comp3圖像改成源圖像的左右半邊對調后得到的新圖像:

img_src = cv2.imread('..\\samples\\data\\lena.jpg',0) 
img_comp1 = cv2.blur(img_src,(13,13))
img_comp2 = cv2.subtract(img_src,35)
img_comp3 = img_src.copy() 
img_comp3[:,:256]=img_src[:,256:] #左右半邊對調
img_comp3[:,256:]=img_src[:,:256] #左右半邊對調

這個時候圖像img_comp3(下圖中的swap)和源圖像(下圖中的src)從直觀上看起來差別就非常大了,但是他們的直方圖卻是一樣的:

通過compareHist()計算的比對值顯示,交換左右半邊得到的新圖像(swap)和源圖像(src)是一樣的圖像:

method=0 src blur sub swap 1.0 0.9285279683865829 0.35037265936214546 1.0
method=1 src blur sub swap 0.0 23850.06026419833 7146491.858903675 0.0
method=2 src blur sub swap 262144.0 236732.0 167915.0 262144.0
method=3 src blur sub swap 0.0 0.15727062402652872 0.4038869008245676 0.0
method=4 src blur sub swap 0.0 37400.61455341503 217214.77007193133 0.0
method=5 src blur sub swap 0.0 79978.49978387816 462272.6122239813 0.0u

compareHist()除了可以比對一維直方圖,還可以比對2D直方圖,這時compareHist()入參的H1和H2要求都是2D直方圖:

#計算直方圖
histSize = (256,256)
histRange = (0, 256,0, 256) 
hist_src = cv2.calcHist([img_src], [0,1], None, histSize, histRange) 
hist_comp1 = cv2.calcHist([img_comp1], [0,1], None, histSize, histRange)  
hist_comp2 = cv2.calcHist([img_comp2], [0,1], None, histSize, histRange)  
hist_comp3 = cv2.calcHist([img_comp3], [0,1], None, histSize, histRange)  
#直方圖比較
for method in range(6):
    src_src = cv2.compareHist(hist_src, hist_src, method)
    src_comp1 = cv2.compareHist(hist_src, hist_comp1, method)
    src_comp2 = cv2.compareHist(hist_src, hist_comp2, method)
    src_comp3 = cv2.compareHist(hist_src, hist_comp3, method)
    print('method=%d src blur sub add'%method,src_src,src_comp1,src_comp2,src_comp3)

2、直方圖反投影calcBackProject

反投影用來計算源圖像的像素和特征圖像直方圖中像素分布的匹配程度。它返回一個與輸入圖像尺寸相同的圖像,其中每個像素對應於該像素屬於特征圖像的概率,概率越高越接近要查找的對象,可以用於圖像分割或查找感興趣的區域。

接口形式:

cv2.calcBackProject(images,channels,hist,ranges,scale[,dst])->dst
  • 參數含義:
  • images:輸入圖像,是一個圖像集合,可以是包含多通道彩色圖像的list或tuple,也可以是多個灰度圖組成的list或者tuple;list或tuple形式的輸入
  • channels:根據images確定,指明要用images里的哪個通道號,根據images的形式確定;list或tuple形式的輸入;
  • hist:輸入直方圖;
  • ranges:圖像元素取值的范圍;包含2個元素的list或tuple;
  • scale:縮放比例;
  • dst:目標圖像,單通道,和images[0]同樣的尺寸和depth ;

其中入參images、channels、ranges參數用法同calcHist()

入參hist是特征圖像的的直方圖,使用它在源圖像中查找該特征。

下面這里例子從源圖像img_src中找出綠色的草地:

import matplotlib.pyplot as plt
import cv2
print('VX公眾號: 桔子code / juzicode.com')
print('cv2.__version__:',cv2.__version__)
plt.rc('font',family='Youyuan',size='9')
plt.rc('axes',unicode_minus='False')
 
img_src = cv2.imread('..\\samples\\picture\\cow.jpeg' ) 
img_roi = cv2.imread('..\\samples\\picture\\cow-roi.jpeg' ) 
#計算特征圖像直方圖
hsv_src = cv2.cvtColor(img_src,cv2.COLOR_BGR2HSV)
hsv_roi = cv2.cvtColor(img_roi,cv2.COLOR_BGR2HSV)
hist_roi = cv2.calcHist([hsv_roi],[0, 1], None, [180, 256], [0, 180, 0, 256] )
cv2.normalize(hist_roi,hist_roi,0,255,cv2.NORM_MINMAX)
#計算反投影
img_back = cv2.calcBackProject([hsv_src],[0,1],hist_roi,[0,180,0,256],2)
#二值化和疊加
disc = cv2.getStructuringElement(cv2.MORPH_ELLIPSE,(5,5))
img_dest=cv2.filter2D(img_back,-1,disc)
ret,thresh = cv2.threshold(img_dest,30,255,0)
thresh = cv2.merge((thresh,thresh,thresh))
img_merge = cv2.bitwise_and(img_src,thresh)
#繪圖
fig,ax = plt.subplots(1,4)
ax[0].set_title('img_src')
ax[0].imshow(cv2.cvtColor(img_src,cv2.COLOR_BGR2RGB)) 
ax[1].set_title('img_roi')
ax[1].imshow(cv2.cvtColor(img_roi,cv2.COLOR_BGR2RGB)) 
ax[2].set_title('img_back')
ax[2].imshow(img_back,'gray') 
ax[3].set_title('img_merge')
ax[3].imshow(cv2.cvtColor(img_merge,cv2.COLOR_BGR2RGB)) 
plt.show()  

在這個例子里首先手動從源圖像img_src中截取部分草地作為特征圖像img_roi,然后讀入源圖像和特征圖像轉換為HSV色彩空間,計算特征圖像的H-S通道的2D直方圖,然后利用該直方圖作為輸入傳入calcBackProject()計算匹配程度,得到第3張圖img_back,該圖像為單通道灰度圖像,其中的草地部分表現出更高的灰度級,經過閾值處理后和源圖像相與就是綠色的草地部分img_merge。

擴展閱讀:

  1. OpenCV-Python教程
  2. OpenCV-Python教程:直方圖(calcHist)與繪制


免責聲明!

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



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