1. 輪廓檢測的基本概念
輪廓是圖像中連續的、閉合的曲線段,代表物體的邊界(如圓形的輪廓是一條閉合曲線)。OpenCV 的輪廓檢測通過?cv2.findContours()
?實現,可用于形狀識別、物體計數、圖像分割等場景。
2. 核心函數與參數
(1)cv2.findContours()
:檢測輪廓
contours, hierarchy = cv2.findContours(image, # 輸入圖像(必須為二值圖,需提前灰度化+二值化)mode, # 輪廓檢索模式(如RETR_EXTERNAL、RETR_TREE)method # 輪廓逼近方法(如CHAIN_APPROX_SIMPLE、CHAIN_APPROX_NONE)
)
返回值:
contours
:檢測到的輪廓列表,每個輪廓是一個點的集合(ndarray
?類型)。
hierarchy
:輪廓的層次結構(如嵌套關系),若無需層次可忽略。
- 參數說明:
mode
(輪廓檢索模式):模式 含義 RETR_EXTERNAL
僅檢測最外層輪廓(忽略內部嵌套的輪廓,適合簡單物體計數)。 RETR_LIST
檢測所有輪廓,但不建立層次關系(最快,適合無需嵌套分析的場景)。 RETR_CCOMP
檢測所有輪廓,組織為兩級結構(外層為連通域,內層為孔洞)。 RETR_TREE
檢測所有輪廓,并建立完整的樹狀層次(適合嵌套輪廓,如 “矩形內的圓形”)。 method
(輪廓逼近方法):方法 含義 CHAIN_APPROX_NONE
存儲輪廓的所有像素點(最詳細,速度慢、占用內存大)。 CHAIN_APPROX_SIMPLE
壓縮輪廓,僅保留關鍵頂點(如矩形僅存 4 個角點,速度快、內存小)。 CHAIN_APPROX_TC89_L1
使用 Teh-Chin 鏈逼近算法(適合曲線輪廓)。
(2)cv2.drawContours()
:繪制輪廓
cv2.drawContours(image, # 要繪制輪廓的目標圖像contours, # 輪廓列表(來自findContours的輸出)contourIdx, # 要繪制的輪廓索引(-1表示繪制所有輪廓)color, # 輪廓顏色(如(0,255,0)表示綠色)thickness=2, # 輪廓線寬度(-1表示“填充輪廓”)
)
3. 完整流程:從預處理到輪廓檢測
輪廓檢測對圖像質量敏感,需先進行預處理(灰度化、二值化、降噪)。以下是完整代碼示例:
import cv2
import numpy as np# 1. 讀取并預處理圖像
img = cv2.imread("shape.jpg") # 讀取彩色圖像
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # 灰度化
blur = cv2.GaussianBlur(gray, (5,5), 0) # 高斯降噪(可選,視情況而定)
_, binary = cv2.threshold(blur, 127, 255, cv2.THRESH_BINARY) # 二值化(>127設為255,否則0)# 2. 檢測輪廓
contours, hierarchy = cv2.findContours(binary, cv2.RETR_TREE, # 檢測所有輪廓并建立樹狀層次cv2.CHAIN_APPROX_SIMPLE # 壓縮輪廓,保留關鍵頂點
)# 3. 繪制輪廓(綠色,線寬2)
cv2.drawContours(img, contours, -1, (0,255,0), 2) # 4. 顯示結果
cv2.imshow("Contours", img)
cv2.waitKey(0)
cv2.destroyAllWindows()
4. 關鍵注意事項
預處理必須到位:
輪廓檢測要求輸入為二值圖像(僅有黑 / 白)。若原圖是彩色,需先轉灰度(cv2.COLOR_BGR2GRAY
),再二值化(cv2.threshold
);若有噪聲,可先高斯模糊(cv2.GaussianBlur
)。
版本兼容性:
(contours, hierarchy)
(2 個值);
OpenCV 3/4 返回?(img, contours, hierarchy)
(3 個值)。
若代碼需兼容多版本,可按以下方式處理:
ret = cv2.findContours(binary, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
if len(ret) == 3:img, contours, hierarchy = ret # OpenCV 3/4
else:contours, hierarchy = ret # OpenCV 2
輪廓篩選與分析:
若需篩選特定輪廓(如面積最大的輪廓),可遍歷?contours
?并計算面積:
max_area = 0
max_contour = None
for cnt in contours:area = cv2.contourArea(cnt)if area > max_area:max_area = areamax_contour = cnt
5. 應用場景形狀識別
通過輪廓的周長、面積、近似多邊形(cv2.approxPolyDP
)判斷形狀(如矩形、圓形)。
物體計數:統計圖像中輪廓的數量(需配合RETR_EXTERNAL
排除內部孔洞)。
圖像分割:用輪廓包圍區域,提取目標物體。
6.實例:
import cv2
phone =cv2.imread('phone.png')#波收頗圖
phone_gray =cv2.cvtColor(phone,cv2.COLOR_BGR2GRAY)
cv2.imshow('phone_gray',phone_gray)
cv2.waitKey(0)
ret, phone_binary= cv2.threshold(phone_gray, 120, 255,cv2.THRESH_BINARY)
cv2.imshow( 'phone_binary',phone_binary)
cv2.waitKey(0)
_,contours, hierarchy = cv2.findContours(phone_binary, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)image_copy = phone.copy()
cv2.drawContours(image=image_copy, contours=contours, contourIdx=-1,color=(0,255,0),thickness=4)
cv2.imshow('Contours_show',image_copy)
cv2.waitKey(0)# cv2.contourArea(contour[, oriented]) -> retval 輪廓面積
# oriented: 定向區域標志,默認值為 False,返回面積的絕對值,True 時則根據輪廓方向返回帶符號的數值area_0 = cv2.contourArea(contours[0])
print(area_0)
area_1 = cv2.contourArea(contours[1])
print(area_1)# arcLength(InputArray curve, bool closed) 輪廓周長
# curve: 輸入的二維點集(輪廓頂點),可以是 vector 或 Mat 類型。
# closed: 用于指示曲線是否封閉。length = cv2.arcLength(contours[0], closed=True)
print(length)# 根據面積篩選顯示輪廓
a_list = []
for i in contours:if cv2.contourArea(i) > 10000:a_list.append(i)image_copy = phone.copy()
image_copy = cv2.drawContours(image=image_copy, contours=a_list, contourIdx=-1, color=(0, 255, 0), thickness=3)
cv2.imshow('Contours_show_10000', image_copy)
cv2.waitKey(0)# '''輪廓定位方法 根據輪廓面積進行排序'''
sortcnt = sorted(contours, key=cv2.contourArea, reverse=True)[0] # 選取最大面積的輪廓
image_contours = cv2.drawContours(phone.copy(), contours=[sortcnt], contourIdx=-1, color=(0, 0, 255), thickness=3) # 繪制輪廓
cv2.imshow('image_contours', image_contours)
cv2.waitKey(0)
這段代碼是基于 OpenCV 的圖像輪廓檢測與分析示例,主要實現了從圖像讀取、預處理到輪廓提取、篩選和排序的完整流程。以下是代碼解析:
1. 導入庫與讀取圖像
import cv2
phone = cv2.imread('phone.png') # 讀取原始圖像(假設為手機相關圖像)
導入 OpenCV 庫(cv2
),用于圖像處理。
使用cv2.imread()
讀取本地圖像phone.png
,返回的phone
是 BGR 格式的彩色圖像(OpenCV 默認讀取格式)。
2. 圖像預處理(灰度化與二值化)
# 灰度化:將彩色圖像轉為單通道灰度圖
phone_gray = cv2.cvtColor(phone, cv2.COLOR_BGR2GRAY)
cv2.imshow('phone_gray', phone_gray) # 顯示灰度圖
cv2.waitKey(0) # 等待用戶按鍵(0表示無限等待)# 二值化:將灰度圖轉為黑白二值圖(輪廓檢測的前提)
ret, phone_binary = cv2.threshold(phone_gray, 120, 255, cv2.THRESH_BINARY)
cv2.imshow('phone_binary', phone_binary) # 顯示二值圖
cv2.waitKey(0)
灰度化:通過cv2.cvtColor()
將 BGR 彩色圖轉為灰度圖(單通道),減少計算量,為后續處理簡化圖像。
二值化:使用cv2.threshold()
將灰度圖轉為黑白二值圖:
閾值設為 120,即灰度值 > 120 的像素設為 255(白色),≤120 的設為 0(黑色)。cv2.THRESH_BINARY
是二值化模式,返回ret
(閾值)和二值化結果phone_binary
。
cv2.imshow()
和cv2.waitKey(0)
用于顯示圖像并暫停,等待用戶確認后繼續。
3. 輪廓檢測與繪制
# 檢測輪廓(OpenCV 3/4版本返回3個值,用_忽略第一個)
_, contours, hierarchy = cv2.findContours(phone_binary, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)# 復制原始圖像,在副本上繪制所有輪廓
image_copy = phone.copy()
cv2.drawContours(image=image_copy, contours=contours, # 輪廓列表contourIdx=-1, # -1表示繪制所有輪廓color=(0,255,0), # 輪廓顏色(綠色,BGR格式)thickness=4 # 輪廓線寬
)
cv2.imshow('Contours_show', image_copy) # 顯示所有輪廓
cv2.waitKey(0)
輪廓檢測:cv2.findContours()
從二值圖中提取輪廓:
輸入:二值圖phone_binary
(背景為黑,目標為白)。
參數cv2.RETR_TREE
:檢測所有輪廓,并保留完整的層次關系(如嵌套輪廓的父子關系)。
參數cv2.CHAIN_APPROX_NONE
:存儲輪廓的所有像素點(不壓縮,最完整但內存占用大)。
返回:contours
(輪廓列表,每個輪廓是像素坐標的數組)、hierarchy
(輪廓層次信息)。
繪制輪廓:cv2.drawContours()
在圖像副本上繪制輪廓,避免修改原圖。
4. 輪廓特征計算(面積與周長)
# 計算輪廓面積(第一個和第二個輪廓)
area_0 = cv2.contourArea(contours[0])
print(area_0) # 打印第一個輪廓的面積
area_1 = cv2.contourArea(contours[1])
print(area_1) # 打印第二個輪廓的面積# 計算輪廓周長(第一個輪廓,閉合輪廓)
length = cv2.arcLength(contours[0], closed=True)
print(length) # 打印第一個輪廓的周長
輪廓面積:cv2.contourArea(contour)
計算單個輪廓的面積(單位:像素 2)。
輪廓周長:cv2.arcLength(curve, closed)
計算輪廓周長:closed=True
表示輪廓是閉合的(如圓形、矩形)。
5. 按面積篩選輪廓
# 篩選面積>10000的輪廓(去除小面積噪聲)
a_list = []
for i in contours:if cv2.contourArea(i) > 10000:a_list.append(i)# 繪制篩選后的輪廓
image_copy = phone.copy()
image_copy = cv2.drawContours(image=image_copy, contours=a_list, # 僅包含大面積輪廓的列表contourIdx=-1, color=(0, 255, 0), thickness=3
)
cv2.imshow('Contours_show_10000', image_copy) # 顯示篩選結果
cv2.waitKey(0)
實際場景中,檢測到的輪廓可能包含噪聲(如小斑點),通過面積閾值(10000)篩選出有意義的大輪廓。
遍歷所有輪廓,將面積大于 10000 的輪廓存入a_list
,再繪制這些輪廓。
6. 按面積排序并提取最大輪廓
# 按面積降序排序輪廓,取最大面積的輪廓
sortcnt = sorted(contours, key=cv2.contourArea, reverse=True)[0]# 繪制最大面積的輪廓(紅色)
image_contours = cv2.drawContours(phone.copy(), contours=[sortcnt], # 注意:contours參數需傳入列表,這里用[sortcnt]包裝contourIdx=-1, color=(0, 0, 255), # 紅色(BGR格式)thickness=3
)
cv2.imshow('image_contours', image_contours) # 顯示最大輪廓
cv2.waitKey(0)
sorted(contours, key=cv2.contourArea, reverse=True)
:按輪廓面積降序排序,reverse=True
表示從大到小。
[0]
取排序后的第一個元素(面積最大的輪廓)。
繪制時,contours
參數需傳入列表,因此用[sortcnt]
包裝單個輪廓。
總結
這段代碼完整演示了輪廓檢測的典型流程:
讀取圖像 → 灰度化 → 二值化 → 檢測輪廓 → 分析輪廓特征(面積、周長) → 篩選 / 排序輪廓 → 可視化結果。
核心目的是從圖像中提取目標的邊界(輪廓),并通過面積等特征篩選出感興趣的目標)。