目錄
簡介
一、輪廓檢測
1.查找輪廓的API
2.代碼分析
2.1.圖像二值化處理
2.2輪廓檢測
2.3輪廓繪制
2.4輪廓面積計算
2.5輪廓周長計算
2.6篩選特定面積的輪廓
2.7查找最大面積的輪廓
2.8繪制輪廓的外接圓
2.9繪制輪廓的外接矩形
二、輪廓的近似
三、模板匹配?
簡介
????????今天繼續學習opencv,今天學習輪廓檢測、輪廓的近似
計算機視覺第一課opencv(三)保姆級教學
計算機視覺第一課opencv(一)保姆級教學
計算機視覺第一課opencv(二)保姆級教學
一、輪廓檢測
1.查找輪廓的API
image, contours, hierarchy = cv2.findContours(img, mode, method)
參數:img: 需要實現輪廓檢測的原圖 mode: 輪廓的檢索模式,主要有四種方式:cv2.RETR_EXTERNAL:只檢測外輪廓,所有子輪廓被忽略cv2.RETR_LIST:檢測的輪廓不建立等級關系,所有輪廓屬于同一等級cv2.RETR_CCOMP:返回所有的輪廓,只建立兩個等級的輪廓,一個對象的外輪廓為第1級組織結構,而對象內中空的輪廓為第2級組織結構,空間中的任何對象的輪廓又是第 1 級組織結構。 -> cv2.RETR_TREE:返回所有的輪廓,建立一個完整的組織結構的輪廓。
method: 輪廓的近似方法,主要有以下兩種:
-> cv2.CHAIN_APPROX_NONE:存儲所有的輪廓點。 -> cv2.CHAIN_APPROX_SIMPLE:壓縮模式,只保留該方向的終點坐標,例如一個矩形輪廓只畫4個點來保存輪廓信息。
返回:image: 返回類型的原圖contours:包含圖像中所有輪廓的list對象,其中每一個獨立的輪廓信息以邊界點坐標(x,y)的形式儲存在numpy數組中。hierarchy:輪廓的層次結構,一個包含4個值的數組:[Next, Previous, First Child, Parent]Next:與當前輪廓處于同一層級的下一條輪廓Previous:與當前輪廓處于同一層級的上一條輪廓First Child:當前輪廓的第一條子輪廓Parent:當前輪廓的父輪廓注意:做輪廓檢測前需要將圖片讀取為二值數據,即像素值只為0和255。
2.代碼分析
圖像讀取與預處理
import cv2
phone = cv2.imread('phone.png') # 讀取原圖,默認以BGR格式加載
phone_gray = cv2.cvtColor(phone, cv2.COLOR_BGR2GRAY) # 轉換為灰度圖
cv2.imshow('phone_0', phone_gray) # 顯示灰度圖
cv2.waitKey(0) # 等待用戶按鍵,0表示無限等待
- 這部分代碼首先讀取圖像,然后將彩色圖像轉為灰度圖(減少計算量,便于后續處理)
cv2.imshow()
用于顯示圖像,cv2.waitKey(0)
確保圖像窗口不會立即關閉
2.1.圖像二值化處理
ret, phone_binary = cv2.threshold(phone_gray, 120, 255, cv2.THRESH_BINARY) # 二值化處理
cv2.imshow('phone_binary', phone_binary)
cv2.waitKey(0)
- 二值化是輪廓檢測的前提,將灰度圖轉換為只有黑白兩種顏色的圖像
- 參數解釋:
- 120:閾值,像素值高于此值會被處理
- 255:最大值,滿足條件的像素會被設置為此值
cv2.THRESH_BINARY
:二值化類型,大于閾值的設為 255,否則設為 0
2.2輪廓檢測
contours = cv2.findContours(phone_binary, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)[-2] # 檢測輪廓
print(len(contours)) # 打印檢測到的輪廓數量
cv2.findContours()
是輪廓檢測的核心函數- 參數解釋:
cv2.RETR_TREE
:輪廓檢索模式,檢測所有輪廓并建立完整的層次結構cv2.CHAIN_APPROX_NONE
:輪廓近似方法,存儲所有輪廓點[-2]
:兼容不同 OpenCV 版本的寫法,確保獲取到輪廓列表
2.3輪廓繪制
image_copy = phone.copy() # 復制原圖,避免在原圖上直接繪制
cv2.drawContours(image=image_copy, contours=contours, contourIdx=-1, # -1表示繪制所有輪廓color=(0,0,255), # 紅色(BGR格式)thickness=2 # 線條粗細
)
cv2.imshow('Contours_show', image_copy)
cv2.waitKey(0)
cv2.drawContours()
用于在圖像上繪制輪廓- 這里創建原圖副本是為了保留原圖,方便后續其他處
2.4輪廓面積計算
area_0 = cv2.contourArea(contours[0]) # 計算第一個輪廓的面積
print(area_0)
area_1 = cv2.contourArea(contours[1]) # 計算第二個輪廓的面積
print(area_1)
cv2.contourArea()
用于計算輪廓所包圍的面積- 面積單位是像素,值越大表示輪廓包圍的區域越大
2.5輪廓周長計算
length = cv2.arcLength(contours[0], closed=True) # 計算第一個輪廓的周長
print(length)
cv2.arcLength()
用于計算輪廓的周長closed=True
表示輪廓是封閉的
2.6篩選特定面積的輪廓
a_list = []
for i in contours:if cv2.contourArea(i) > 10000: # 篩選面積大于10000的輪廓a_list.append(i)
image_copy = phone.copy()
image_copy = cv2.drawContours(image_copy, a_list, -1, (0, 255, 0), 3) # 用綠色繪制篩選后的輪廓
cv2.imshow('Contours_show_10000', image_copy)
cv2.waitKey(0)
- 這部分實現了根據面積篩選輪廓的功能
- 只保留面積大于 10000 的輪廓,并用綠色繪制,便于觀察較大的物體輪廓
2.7查找最大面積的輪廓
# 按面積降序排序,取第一個(面積最大的輪廓)
sortcnt = sorted(contours, key=cv2.contourArea, reverse=True)[0]
image_contours = cv2.drawContours(phone.copy(), [sortcnt], -1, (0, 0, 255), 3)
cv2.imshow('image_contours', image_contours)
cv2.waitKey(0)
- 使用
sorted()
函數和cv2.contourArea
作為排序鍵,找到面積最大的輪廓 - 這種方法常用于定位圖像中最主要的物體
2.8繪制輪廓的外接圓
cnt = contours[6] # 選擇第7個輪廓(索引從0開始)
(x, y), r = cv2.minEnclosingCircle(cnt) # 計算最小外接圓
phone_circle = cv2.circle(phone, (int(x), int(y)), int(r), (0, 255, 0), 2) # 繪制外接圓
cv2.imshow('phone_circle', phone_circle)
cv2.waitKey(0)
cv2.minEnclosingCircle()
計算能包圍輪廓的最小圓- 返回值包括圓心坐標 (x,y) 和半徑 r
cv2.circle()
用于繪制圓形,這里用綠色線條
2.9繪制輪廓的外接矩形
x, y, w, h = cv2.boundingRect(cnt) # 計算最小外接矩形
phone_rectangle = cv2.rectangle(phone, (x, y), (x + w, y + h), (255, 0, 0), 2) # 繪制矩形
cv2.imshow('phone_rectangle', phone_rectangle)
cv2.waitKey(0)
cv2.boundingRect()
計算能包圍輪廓的最小矩形(軸對齊矩形)- 返回值包括矩形左上角坐標 (x,y)、寬度 w 和高度 h
cv2.rectangle()
用于繪制矩形,這里用藍色線條
二、輪廓的近似
approx = cv2.approxPolyDP(curve, epsilon, closed)
參數說明:curve:輸入輪廓。epsilon:近似精度,即兩個輪廓之間最大的歐式距離。該參數越小,得到的近似結果越接近實際輪廓;反之,得到的近似結果會更加粗略。closed:布爾類型的參數,表示是否封閉輪廓。如果是 True,表示輸入輪廓是封閉的,近似結果也會是封閉的;否則表示輸入輪廓不是封閉的,近似結果也返回值:approx:近似結果,是一個ndarray數組,為1個近似后的輪廓,包含了被近似出來的輪廓上的點的坐標
圖像讀取與預處理
phone = cv2.imread('phone.png') # 讀取原圖
phone_gray = cv2.cvtColor(phone,cv2.COLOR_BGR2GRAY) # 轉換為灰度圖
ret,phone_thresh = cv2.threshold(phone_gray, 120, 255,cv2.THRESH_BINARY) # 二值化處理
- 將彩色圖像轉為灰度圖,簡化圖像處理
- 二值化處理:將灰度圖轉換為只有黑白兩種顏色的圖像,閾值設為 120,大于 120 的像素變為 255(白色),小于等于 120 的變為 0(黑色)
輪廓檢測
contours=cv2.findContours(phone_thresh,cv2.RETR_TREE,cv2.CHAIN_APPROX_NONE)[-2]
- 使用
cv2.findContours
函數檢測輪廓 - 參數
cv2.RETR_TREE
表示檢測所有輪廓并建立完整的層次結構 - 參數
cv2.CHAIN_APPROX_NONE
表示存儲所有輪廓點 [-2]
是為了兼容不同 OpenCV 版本,確保獲取到輪廓列表
輪廓近似處理
# 設置近似精度,epsilon值越小,近似結果越接近原始輪廓
epsilon = 0.002 * cv2.arcLength(contours[0], True) # 對輪廓進行近似
approx = cv2.approxPolyDP(contours[0], epsilon, True)
cv2.arcLength
計算輪廓周長,True
表示輪廓是封閉的epsilon
是近似精度,這里設置為輪廓周長的 0.002 倍cv2.approxPolyDP
函數實現輪廓近似,將輪廓用更少的點來表示,保留主要形狀
結果對比與顯示
# 打印原始輪廓和近似輪廓的點數量
print(contours[0].shape)
print(approx.shape)# 繪制近似后的輪廓
phone_new = phone.copy()
image_contours = cv2.drawContours(phone_new, [approx], contourIdx=-1, color=(0,255,0), thickness=3)# 顯示原圖和處理后的圖像
cv2.imshow('phone', phone)
cv2.waitKey(0)
cv2.imshow('image_contours', image_contours)
cv2.waitKey(0)
- 通過打印形狀可以看到,近似后的輪廓點數量明顯減少
- 用綠色線條繪制近似后的輪廓,并與原圖進行對比顯示
cv2.waitKey(0)
表示等待用戶按鍵后再繼續執行
????????通過調整epsilon
的值,可以控制近似程度,值越大,輪廓簡化越明顯,保留的點越少;值越小,越接近原始輪廓。
?
三、模板匹配?
模板匹配是一種用于查找與模板圖像(補丁)匹配(相似)的圖像區域的技術
為了識別匹配區域,我們必須通過滑動來將模板圖像與源圖像進行比較
一次移動一個像素(從左到右,從上到下)。在每個位置,都會計算一個度量(度量計算公式),以便它表示該位置的匹配“好”或“壞”程度
cv2.matchTemplate(image, templ, method, result=None, mask=None)
image :待搜索圖像
templ:模板圖像
method:計算匹配程度的方法,可以有:
????????TM_SQDIFF 平方差匹配法,該方法采用平方差進行匹配;匹配越好,值越小;匹配越差,值越大。
????????TM_CCORR 相關匹配法,該方法采用乘積操作;數值越大表明匹配程度越好。
????????TM_CCOEFF 相關系數匹配法,數值越大表明匹配程度越好。
????????TM_SQDIFF_NORMED 歸一化平方差匹配法,匹配越好,值越小;匹配越差,值越大。 ????????TM_CCORR_NORMED 歸一化相關匹配法,數值越大表明匹配程度越好。 ????????TM_CCOEFF_NORMED 歸一化相關系數匹配法,數值越大表明匹配程度越好。
圖像讀取與顯示
kele = cv2.imread('kele.png')
template = cv2.imread('template.png')
cv2.imshow(winname='kele', mat=kele)
cv2.imshow(winname='template', mat=template)
cv2.waitKey(0)
cv2.imread
:從本地加載圖像,kele
?存大圖像、template
?存模板小圖像,默認按?BGR 色彩空間?讀入(和日常 RGB 有區別,OpenCV 歷史原因導致 )。cv2.imshow
:創建窗口顯示圖像,第一個參數是窗口名稱(winname
?),第二個是要顯示的圖像數據(mat
?)。cv2.waitKey(0)
:暫停程序,等待用戶按下任意鍵再繼續執行,0
?表示 “無限等待”,保證圖像窗口能停留讓我們看到內容。
獲取模板尺寸與執行模板匹配
h, w = template.shape[:2]
res = cv2.matchTemplate(kele, template, cv2.TM_CCOEFF_NORMED)
template.shape[:2]
:獲取模板圖像的?高度(h)、寬度(w)?,shape
?返回 (高,寬,通道數) ,[:2]
?取前兩個值,后續標記匹配區域會用到尺寸。cv2.matchTemplate(...)
:執行模板匹配核心操作!把?template
?當 “模板”,在?kele
?里逐像素 / 逐區域滑動比較 。cv2.TM_CCOEFF_NORMED
?是選的?歸一化相關系數匹配法?,結果存在?res
?里:res
?是個二維矩陣,每個元素值表示對應位置與模板的匹配程度(越接近 1 ,匹配越好 )。
解析匹配結果,標記目標區域
min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(res)
top_left = max_loc
bottom_right = (top_left[0] + w, top_left[1] + h)
kele_template = cv2.rectangle(kele, top_left, bottom_right, color=(0, 255, 0), thickness=2)
cv2.minMaxLoc(res)
:從?res
(匹配結果矩陣 )里找?最小值(min_val
?)、最大值(max_val
?),以及它們對應的坐標(min_loc
、max_loc
?)?。因為用了?TM_CCOEFF_NORMED
?,值越大匹配越好,所以關注?max_loc
(最佳匹配區域的左上角坐標 )。bottom_right = (top_left[0] + w, top_left[1] + h)
:根據模板寬高,算出匹配區域?右下角坐標?,這樣就能確定一個矩形范圍。cv2.rectangle(...)
:在?kele
?圖像上畫矩形!參數分別是:要畫的圖像(kele
?)、左上角坐標(top_left
?)、右下角坐標(bottom_right
?)、矩形顏色((0, 255, 0)
?,即綠色,BGR 格式 )、線條粗細(thickness=2
?),結果存在?kele_template
?里。
顯示最終結果
cv2.imshow(winname='kele_template', mat=kele_template)
cv2.waitKey(0)
- 用?
cv2.imshow
?顯示標記了匹配區域的圖像?kele_template
?,再用?cv2.waitKey(0)
?等待按鍵,方便我們查看匹配效果,程序最后才結束。