【opencv】(7) 图像匹配、直方图、图像均衡化
各位同學好,今天和大家分享一下opencv中圖像匹配方法,和圖像均衡化方法
(1)模板匹配:?cv2.matchTemplate();(2)圖像直方圖:?cv2.calcHist();(3)圖像均衡化:?cv2.equalizeHist();(4)自適應均衡化:?cv2.createCLAHE()
1. 模板匹配
模板匹配和卷積的原理很像,模板在原圖像上從原點開始滑動(從左到右, 從上到下),計算模板與(圖像被模板覆蓋的地方)的差別程度,在opencv中計算差別程度有6種計算方法。然后將每次計算結果放入一個矩陣里,作為輸出結果。假如原圖形是A*B大小,而模板是a*b大小,則輸出結果的矩陣大小是(A-a+1)*(B-b+1)
匹配方法:
cv2.matchTemplate(img, templ, method)
參數:
img: 原始圖像
temple: 模板圖像
method: 匹配度計算方法,如下:
cv2.TM_SQDIFF: 計算平方差,計算結果越小,越相關
cv2.TM_CCORR: 計算相關性,計算出來的值越大,越相關
cv2.TM_CCOEFF: 計算相關系數,計算出的值越大,越相關
cv2.TM_SQDIFF_NORMED: 計算歸一化平方差,計算結果越接近0,越相關
cv2.TM_CCORR_NORMED: 計算歸一化相關性,計算結果越接近1,越相關
cv2.TM_CCOEFF_NORMED: 計算歸一化相關系數,計算結果越接近1,越相關
在開始前我們先導入需要用的庫函和圖像,定義一個圖像顯示函數,方便后續操作
import cv2
import numpy as np
import matplotlib.pyplot as plt
# 獲取圖片所在文件夾
filepath = 'C:\\...\\opencv\\img'
# 獲取文件夾中的某一張圖片,0代表轉化灰度圖
img = cv2.imread(filepath+'\\team.jpg',0)
# 原圖上的一塊模板
template = cv2.imread(filepath+'\\face.jpg',0)
# 定義繪圖函數
def cv_show(name,img):cv2.imshow(name,img) # 傳入自定義圖像名,和圖像變量cv2.waitKey(0) # 圖片不會自動消失cv2.destroyWindow() # 手動關閉窗口
# 顯示圖像
cv_show('img',img)
cv_show('face',template)
我們需要在第一張圖img中找到模板face的位置,并把它框出來
?
1.1 匹配單個對象?
模板在原圖像上移動時,返回匹配度最高的一塊區域。這里使用v2.TM_SQDIFF平方差計算方法,值越小代表匹配度越高。res中保存所有的計算結果,使用cv2.minMaxLoc()函數,獲取res中的最值及最值最在的坐標位置,最值坐標是指左上角坐標(x, y),根據坐標位置和模板的寬和高,可以在原圖像中畫出目標所在位置。x軸向右為正,y軸向下為正,獲得目標右下角坐標(x+w, y+h)。注意在template.shape中提取模板尺寸的時候,shape[0]保存的是高,shape[1]保存的是寬。
#(1)匹配單個對象
# img代表原始圖像,template代表模板窗口,1默認為cv2.TM_SQDIFF方法
res = cv2.matchTemplate(img, template, 1)
# 獲取結果的最值和最值位置
min_val,max_val,min_loc,max_loc = cv2.minMaxLoc(res)
# 最值位置是左上角的坐標位置,通過模板的寬和高可以在原圖上把模板位置畫出來
h,w = template.shape # shape值是(高,寬)
# 找出右下位置
bottom_loc = (min_loc[0]+w,min_loc[1]+h)
# 復制一份圖像,不然畫框的時候原圖像會變
draw = img.copy()
# 輸入圖像畫板draw,左上坐標,右下坐標
cv2.rectangle(draw,min_loc,bottom_loc,(0,0,255),2)
# 繪圖
plt.subplot(121)
plt.imshow(res,cmap='gray') # 計算出的每一個窗口的結果值
plt.subplot(122)
plt.imshow(draw,cmap='gray') # 在畫板上把目標值框出來
plt.tight_layout() #自動排版
左側是res的圖像是模板和整個圖像運算后的結果,右側框出來的是匹配度最高的圖像,匹配正確
1.2 多目標匹配
設定一個閾值,只要模板和圖像運算后的結果大于這個閾值,就將這個區域框出來,不單單識別匹配度最高的,只要滿足給定的條件就行。
在這里使用cv2.TM_SQDIFF_NORMED歸一化平方差來表明匹配度,只要匹配度小于0.2,就將這個區域選出來。使用np.where()函數來獲取所有滿足閾值條件的區域的左上角坐標點,需要注意的是,獲取的坐標點loc中依次保存的是高和寬,即(y, x),而我們在畫矩形框的時候需要的坐標pt是(x, y),因此需要把loc的坐標順序翻轉一下。得到左上角坐標(x, y),右下角坐標(x+w, y+h)
# 導入灰度圖
img = cv2.imread(filepath+'\\team.jpg',0) # 原圖
template = cv2.imread(filepath+'\\face.jpg',0) # 從原圖上取下的一塊
h,w = template.shape # 獲取模板的高和寬
# 圖像匹配,使用歸一化相關系數
res = cv2.matchTemplate(img,template,cv2.TM_SQDIFF_NORMED)
# 設置閾值,只要計算出的歸一化平方差小于0.2就把那一塊位置找出來
threshold = 0.2
loc = np.where(res<threshold) #輸出滿足條件的坐標
# 繪制所有的滿足匹配度的窗口
# zip(*) 可理解為解壓,返回二維矩陣式。loc中loc[0]是高,loc[1]是寬,[::-1]表示倒序。p[0]代表寬,p[1]代表高
for pt in zip(*loc[::-1]): bottom_loc = (pt[0]+w,pt[1]+h)cv2.rectangle(img,pt,bottom_loc,(0,0,255),1) # 參數:圖像,左上坐標,右下坐標,線條顏色,線條粗細
# 繪圖
cv2.imshow('img',img)
cv2.waitKey(0)
線條粗細代表匹配度,中間的可能性最大,(都是同一個人...)
2. 圖像直方圖
同樣,我們先導入需要的庫,和圖像文件,再定義一個圖像顯示函數
import cv2
import numpy as np
import matplotlib.pyplot as plt
# 獲取圖片所在文件夾
filepath = 'C:\\...\\opencv\\img'
# 定義繪圖函數
def cv_show(name,img):# 傳入自定義圖像名,即圖像變量cv2.imshow(name,img) # 圖片不會自動消失cv2.waitKey(0)# 手動關閉窗口cv2.destroyWindow()
2.1 直方圖繪制
用于統計圖像上每個像素值出現的次數。
(1)方法1:使用matplot繪制直方圖
# 導入圖像
img = cv2.imread(filepath+'\\mh.jpg',0) #0代表灰度圖
# 直方圖展示
plt.hist(img.ravel(),256) #ravel將二維拉長成一維,統計0-255每個像素值出現的個數
plt.show()
讀入的img是灰度圖,只有兩個維度,將它變成一維統計像素值出現個數,設置range=[0,256]顧頭不顧尾,對0-255每一個像素值計數。
(2)方法2:使用cv2.calcHist()函數
cv2.calcHist(img, channels, mask, histsize, ranges)
img:輸入圖像的圖像格式為uint8或float32,當傳入函數時,應用中括號,[img]
channels:是用中括號來告知函數繪制圖像直方圖。如果輸入的是灰度圖,則值為[0];如果是彩圖,傳入的參數可以是[0]或[1]或[2],分別對應BGR
mask:掩模圖像,統計整幅圖的直方圖就是None。如果畫某一部分直方圖,需要制作一個掩模圖像并使用。掩模大小和img一樣的np數組,需要的部分為255,不需要的部分為0.
histSize:BIN的數目。應使用中括號,控制x軸取值區間,一般為256
ranges:使用中括號表示像素值范圍[0, 256]顧頭不顧尾
下面對彩圖img進行直方圖統計,不使用掩模,分別統計圖像BGR三通道上的各個像素值出現了多少次,使用折線圖繪制曲線。hist中保存的是每個通道每個像素值出現的次數。
img = cv2.imread(filepath+'\\mh.jpg') # 以彩色圖為例
color = ['b','g','r'] #分別研究三顏色通道的直方圖,用顏色區分
for i,col in enumerate(color): #enumerate遍歷數據對象,返回數據和對應下標# i存放color下標,col存放具體值 hist = cv2.calcHist([img], [i], None, [256], [0,256])plt.plot(hist, color=col, label=f'{col}--channel') # 繪制三通道上每個通道像素點出現的個數的折線圖plt.xlim([0,256])
plt.legend()
2.2 掩模mask操作
掩模mask的大小和原圖像大小一致。掩模中只有兩部分,0和255,一部分為白色一部分為黑色。掩模中白色部分覆蓋到的區域保留原圖,黑色部分覆蓋到的區域置為0。
如果我們讀入的圖像時一張彩圖,在構建np數組時,需要舍棄第三個維度,即通道。保留前兩個維度img.shape[:2],掩模的size和原圖像相同。由于mask是一個數組,可以使用切片的方法將需要保留的位置變成白色255。
使用圖像按位操作方法:cv2.bitwise_and(src1, src2, mask)
src: 輸入圖像
mask:掩膜,用一副二值化圖片對另外一幅圖片進行局部的遮
分別統計加了掩模和沒加掩模的圖像的像素點個數來對比。
img = cv2.imread(filepath+'\\shandi.jpg',0)
# 創建mask,由于如果img是彩圖就是三維,舍棄通道取長和寬,創建一個和img相同大小的mask
mask = np.zeros(img.shape[:2],np.uint8) # 8位無符號整型,0-255
# 使用切片方法,將maks中的一部分變成255白色
mask[200:700,200:700] = 255
# 在圖像上覆蓋一層掩模
masked_img = cv2.bitwise_and(img,img,mask=mask)
# 直方圖統計
hist_full = cv2.calcHist([img],[0],None,[256],[0,256]) # 無掩模
hist_mask = cv2.calcHist([img],[0],mask,[256],[0,256]) # 有掩模
# 繪圖
plt.subplot(221),plt.imshow(img,'gray') # 原圖,繪制灰度圖,不然顏色太雜
plt.subplot(222),plt.imshow(mask,'gray') # 掩模圖
plt.subplot(223),plt.imshow(masked_img,'gray') # 掩模覆蓋原圖
plt.subplot(224),plt.plot(hist_full,label='non-mask'),plt.plot(hist_mask,label='masked') #像素值統計
plt.legend()
plt.tight_layout()
plt.show()
3. 圖像均衡化
圖像的直方圖是對圖像對比度效果上的一種處理,旨在使得圖像整體效果均勻。原始圖像由于其灰度分布可能集中在較窄的區間,造成圖像不夠清晰。通過改變圖像的直方圖,來改變圖像中各像素的灰度,用于增強局部的對比度而不影響整體的對比度。這種方法對于背景和前景都太亮或者太暗的圖像非常有用。
直方圖均衡化的基本原理:對在圖像中像素個數多的灰度值(即對畫面起主要作用的灰度值)進行展寬,而對像素個數少的灰度值(即對畫面不起主要作用的灰度值)進行歸并,從而增大對比度,使圖像清晰,達到增強的目的。
均衡化函數:?cv2.equalizeHist(img)
img:指需要均衡化的原圖像,灰度圖像。返回值為均衡化后的圖像。
3.1 對圖像整體進行均衡化
# 原圖像有些位置的像素值特別多,有的位置很少
img = cv2.imread(filepath+'\\mh1.jpg',0) #0灰度圖
plt.hist(img.ravel(),256) #0-255每一個值出現了多少次
plt.show()
# ==2== 均衡化
equ = cv2.equalizeHist(img)
plt.hist(equ.ravel(),256) # 將均衡化后的圖像使用ravel()拉長成一維,計算0-255每個值出現的次數
plt.show
# ==3== 圖像展示,整體做均衡化會丟失一些細節
res = np.hstack((img,equ)) # 將兩張圖組合在一起
cv_show('res',res)
?左圖時原始圖像的像素直方圖,右圖是均衡化后的像素直方圖
???
左圖為原始圖像,右圖為均衡化之后的圖像。我們看出,對整體均衡化之后會大致一下細節的丟失
3.2 自適應均衡化
整幅圖像會被分成很多小塊,然后再對每一個小塊分別進行直方圖均衡化。缺點是:如果有噪聲的話,噪聲會被放大。為了避免這種情況的出現要使用對比度限制。對于每個小塊來說,如果直方圖中的bin超過對比度的上限的話,就把 其中的像素點均勻分散到其他 bins中,然后在進行直方圖均衡化。
cv.createCLAHE(clipLimit, tileGridSize)
clipLimit:?顏色對比度限制,默認是40
tileGridSize: 進行像素均衡化的網格大小,默認為8*8
img = cv2.imread(filepath+'\\mh1.jpg',0)
# 設置均衡化參數,對比度閾值為2,網格為3*3
clahe = cv2.createCLAHE(clipLimit=2,tileGridSize=(3,3))
# 生成自適應均衡化后的圖像
res_clahe = clahe.apply(img)
# 將三張圖象組合在一起看一下區別
res = np.hstack((img,equ,res_clahe))
cv_show('res',res)
第一張是原圖,第二張是整體均衡化之后的圖像,第三張是自適應均衡化之后的圖像
總結
以上是生活随笔為你收集整理的【opencv】(7) 图像匹配、直方图、图像均衡化的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【opencv】(6) 图像轮廓处理
- 下一篇: 【opencv】(8) 傅里叶变换,高通