??????????OpenCV實(shí)戰(zhàn)系列總目錄
打印一個(gè)圖片可以做出一個(gè)函數(shù):
def cv_show(img,name):
cv2.imshow(name,img)
cv2.waitKey()
cv2.destroyAllWindows()
1、輪廓特征與近似
1.1 輪廓特征
前面我們計(jì)算了這個(gè)圖片的輪廓:
?它的輪廓信息保存在了contours中,取出第一個(gè)輪廓,計(jì)算相關(guān)參數(shù):
cnt = contours[0]
cv2.contourArea(cnt)
cv2.arcLength(cnt,True)
打印結(jié)果:
8500.5?
437.9482651948929
這是分別求出了周長和面積,這里的True表示的是否是閉合的。?
1.2 輪廓近似
?
如圖,第一個(gè)圖是原圖,如果將它的輪廓計(jì)算出來應(yīng)該是第三個(gè)圖的結(jié)果,但是我不想要這樣一些帶坑坑洼洼的結(jié)果,我只想要圖2這樣的結(jié)果呢?
原圖中含有一些曲線,比如有一條曲線,這條曲線有A、B兩個(gè)點(diǎn),先將這兩個(gè)點(diǎn)連上,在曲線中選到一個(gè)C點(diǎn),使得這個(gè)C點(diǎn)到AB這條直線上距離最大,如果這個(gè)距離d小于指定的閾值t,那么這個(gè)AB直線就可以當(dāng)做曲線的近似了。
那如果大于設(shè)定的閾值呢?那么曲線就會被分解成兩個(gè)部分變成兩個(gè)曲線,AC和BC,然后AC和BC繼續(xù)去做前面的判斷操作一直到找到近似直線。
但是在代碼的實(shí)現(xiàn)卻非常簡單:
img = cv2.imread('contours2.png')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
ret, thresh = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY)
contours, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)
cnt = contours[0]
draw_img = img.copy()
res = cv2.drawContours(draw_img, [cnt], -1, (0, 0, 255), 2)
cv_show(res,'res')
每行代碼的意思:
- 讀進(jìn)來圖像,還是前面的圖像
- 做二值處理
- 找輪廓信息?
- 找出第一個(gè)輪廓
- 深度復(fù)制圖像
- 提取輪廓信息
- 將輪廓圖像打印
打印結(jié)果:?
?接下來做輪廓近似的處理:
epsilon = 0.1*cv2.arcLength(cnt,True)
approx = cv2.approxPolyDP(cnt,epsilon,True)
draw_img = img.copy()
res = cv2.drawContours(draw_img, [approx], -1, (0, 0, 255), 2)
cv_show(res,'res')
關(guān)鍵代碼:approx = cv2.approxPolyDP(cnt,epsilon,True)
cv2.approxPolyDP這是計(jì)算輪廓的函數(shù),第一個(gè)參數(shù)表示計(jì)算的輪廓,第二個(gè)是指定的閾值,這個(gè)閾值是自己指定的,一般通過周長來計(jì)算,所以approx是計(jì)算的輪廓信息,再用cv2.drawContours將輪廓擬合出來,打印圖像。
打印結(jié)果:
?這就是近似完的結(jié)果,這里可以調(diào)整前面計(jì)算周長的權(quán)重0.1多執(zhí)行幾次,這個(gè)值指定的越小結(jié)果越接近原始輪廓。
1.3 邊界矩陣
?繼續(xù)用上面的圖片,如何將一個(gè)輪廓的外接矩形標(biāo)出來呢?不廢話直接上代碼:
img = cv2.imread('contours.png')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
ret, thresh = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY)
contours, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)
cnt = contours[5]
x,y,w,h = cv2.boundingRect(cnt)
img = cv2.rectangle(img,(x,y),(x+w,y+h),(0,255,0),2)
cv_show(img,'img')
前面幾行都已經(jīng)學(xué)習(xí)過了,直接看到這里
x,y,w,h = cv2.boundingRect(cnt)
cnt是輪廓信息,通過cv2.boundingRect可以計(jì)算出四個(gè)值x,y,w,h,一個(gè)坐標(biāo)加上長寬,有這個(gè)信息就可以得到一個(gè)確定的矩形。
通過這個(gè)函數(shù)cv2.rectangle,依次傳進(jìn)去圖像,坐標(biāo)1,坐標(biāo)2,顏色,線條寬度,擬合出這個(gè)輪廓
打印結(jié)果:
?計(jì)算外接矩形和原始圖形的面積比值:
area = cv2.contourArea(cnt)
x, y, w, h = cv2.boundingRect(cnt)
rect_area = w * h
extent = float(area) / rect_area
print ('輪廓面積與邊界矩形比',extent)
第一行是計(jì)算原始面積,第二行+第三行計(jì)算外接矩形的面積,然后計(jì)算比值打印出來:
輪廓面積與邊界矩形比 0.5154317244724715
外接圓:
(x,y),radius = cv2.minEnclosingCircle(cnt)
center = (int(x),int(y))
radius = int(radius)
img = cv2.circle(img,center,radius,(0,255,0),2)
cv_show(img,'img')
?
2、模板匹配方法
模板匹配在openCV中是非常重要的內(nèi)容,和卷積原理很像,模板在原圖像上從原點(diǎn)開始滑動(dòng),計(jì)算模板與(圖像被模板覆蓋的地方)的差別程度,這個(gè)差別程度的計(jì)算方法在opencv里有6種,然后將每次計(jì)算的結(jié)果放入一個(gè)矩陣?yán)?,作為結(jié)果輸出。假如原圖形是AxB大小,而模板是axb大小,則輸出結(jié)果的矩陣是(A-a+1)x(B-b+1)?
如圖這是兩個(gè)圖片,我需要做的是將lena臉的部分框出來,然后右圖相當(dāng)于是標(biāo)簽,假如左圖是一個(gè)9*9的圖像,右圖是一個(gè)3*3的圖像,那么左圖可以分解成9個(gè)3*3的圖像,將右圖與這9個(gè)區(qū)域的圖像進(jìn)行比對,通過計(jì)算兩個(gè)圖像的像素匹配程度來判斷是這9個(gè)區(qū)域的那一個(gè)區(qū)域,9個(gè)區(qū)域就是從左至右從上至下一個(gè)一個(gè)進(jìn)行匹配。
那這個(gè)匹配程度怎么計(jì)算呢,openCV提供了多種方法來計(jì)算,比如計(jì)算對應(yīng)位置之間的像素值差異,差異值就是量化匹配程度,當(dāng)然差異值越小說明匹配程度越接近。具體的匹配方法:
- TM_SQDIFF:計(jì)算平方不同,計(jì)算出來的值越小,越相關(guān)
- TM_CCORR:計(jì)算相關(guān)性,計(jì)算出來的值越大,越相關(guān)
- TM_CCOEFF:計(jì)算相關(guān)系數(shù),計(jì)算出來的值越大,越相關(guān)
- TM_SQDIFF_NORMED:計(jì)算歸一化平方不同,計(jì)算出來的值越接近0,越相關(guān)
- TM_CCORR_NORMED:計(jì)算歸一化相關(guān)性,計(jì)算出來的值越接近1,越相關(guān)
- TM_CCOEFF_NORMED:計(jì)算歸一化相關(guān)系數(shù),計(jì)算出來的值越接近1,越相關(guān)
這里給出一個(gè)openCV官網(wǎng)鏈接,是上面這些匹配方法的計(jì)算公式:
OpenCV: Object Detection
分別將lena和模板(lena的臉)讀進(jìn)來,轉(zhuǎn)化為灰度圖后打印出大小:
# 模板匹配
img = cv2.imread('lena.jpg', 0)
template = cv2.imread('face.jpg', 0)
h, w = template.shape[:2]
print(img.shape)
print(template.shape)
h和w是模板的長和寬,打印的shape值為:
(263, 263)
(110, 85)
?調(diào)用模板匹配操作:
methods = ['cv2.TM_CCOEFF', 'cv2.TM_CCOEFF_NORMED', 'cv2.TM_CCORR',
'cv2.TM_CCORR_NORMED', 'cv2.TM_SQDIFF', 'cv2.TM_SQDIFF_NORMED']
res = cv2.matchTemplate(img, template, cv2.TM_SQDIFF)
print(res.shape)
methods是所有方法
?cv2.matchTemplate的參數(shù)分別為原始圖像、模板、匹配方法
然后打印shape值
打印結(jié)果:
(154, 179)
這里的154=263-110+1,179=263-85+1
用這個(gè)結(jié)果去定位一下最小損失的那個(gè)像素點(diǎn)的位置:
min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(res)
print(min_val, max_val, min_loc, max_loc)
?打印結(jié)果:
39168.0
74403584.0
(107, 89)
(159, 62)
在這個(gè)匹配方法中,我們需要的是min_loc,這個(gè)點(diǎn)的坐標(biāo)再加上模板的長寬,就可以得到我們想要框住的區(qū)域了。
3、模板匹配效果
用6種不同的匹配方法進(jìn)行模板匹配,看下結(jié)果的差異:
for meth in methods:
img2 = img.copy()
# 匹配方法的真值
method = eval(meth)
print (method)
res = cv2.matchTemplate(img, template, method)
min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(res)
# 如果是平方差匹配TM_SQDIFF或歸一化平方差匹配TM_SQDIFF_NORMED,取最小值
if method in [cv2.TM_SQDIFF, cv2.TM_SQDIFF_NORMED]:
top_left = min_loc
else:
top_left = max_loc
bottom_right = (top_left[0] + w, top_left[1] + h)
# 畫矩形
cv2.rectangle(img2, top_left, bottom_right, 255, 2)
plt.subplot(121), plt.imshow(res, cmap='gray')
plt.xticks([]), plt.yticks([]) # 隱藏坐標(biāo)軸
plt.subplot(122), plt.imshow(img2, cmap='gray')
plt.xticks([]), plt.yticks([])
plt.suptitle(meth)
plt.show()
對這個(gè)代碼塊逐行解釋:
- for循環(huán)
- 深度復(fù)制圖像
- 取出當(dāng)前匹配方法名稱(前面有一個(gè)數(shù)組存了全部的6個(gè)方法)(加上eval的原因是不能傳進(jìn)來一個(gè)字符串)
- 計(jì)算一個(gè)結(jié)果
- 找出最好結(jié)果和最壞結(jié)果的差異程度值和坐標(biāo)
- 判斷當(dāng)前方法是算最小值為最佳結(jié)果還是最大值為最佳結(jié)果
- 6已解釋
- 6已解釋
- 6已解釋
- 計(jì)算出右下角的坐標(biāo)
- 通過對焦的兩個(gè)點(diǎn)的坐標(biāo)畫出一個(gè)矩形將目標(biāo)區(qū)域框出來
- 后面全是將結(jié)果打印出來
打印結(jié)果幾乎都是一樣的,就只列出一個(gè)了:
?左邊的圖好理解,就是將lena的臉框出來了,我們完成了任務(wù),右邊就是計(jì)算出了一個(gè)最亮的位置也就是前面res變量的輸出結(jié)果。
沒有加上歸一化操作的結(jié)果會稍微差點(diǎn)。
同樣的道理我們做一下多個(gè)模板的匹配,比如一張圖上有多個(gè)模板需要全部框出來:
img_rgb = cv2.imread('mario.jpg')
img_gray = cv2.cvtColor(img_rgb, cv2.COLOR_BGR2GRAY)
template = cv2.imread('mario_coin.jpg', 0)
h, w = template.shape[:2]
res = cv2.matchTemplate(img_gray, template, cv2.TM_CCOEFF_NORMED)
threshold = 0.8
# 取匹配程度大于%80的坐標(biāo)
loc = np.where(res >= threshold)
for pt in zip(*loc[::-1]): # *號表示可選參數(shù)
bottom_right = (pt[0] + w, pt[1] + h)
cv2.rectangle(img_rgb, pt, bottom_right, (0, 0, 255), 2)
cv2.imshow('img_rgb', img_rgb)
cv2.waitKey(0)
打印結(jié)果:文章來源:http://www.zghlxwxcb.cn/news/detail-686483.html
文章來源地址http://www.zghlxwxcb.cn/news/detail-686483.html
到了這里,關(guān)于openCV實(shí)戰(zhàn)-系列教程7:輪廓檢測2與模板匹配(輪廓檢測/輪廓特征/輪廓近似/輪廓邊界矩陣/輪廓邊界圓/模版匹配)、原理解析、源碼解讀的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!