實戰項目實現以下功能:
對圖片 hua.png
進行輪廓提取,并在同一窗口中完成以下兩個繪制操作:
用紅色畫出花的外部輪廓(即最外層輪廓)
用綠色畫出該輪廓的近似多邊形,其中近似精度參數
ε
設置為輪廓周長的 0.005
項目完整代碼:
import cv2
hua = cv2.imread('picture/hua.png')#讀取原圖
hua_gray = cv2.cvtColor(hua,cv2.COLOR_BGR2GRAY)#灰度圖的處理
cv2.imshow('hua_b',hua_gray)
cv2.waitKey(0)
# hua_gray=cv2.imread('hua.png',0) #讀取灰度圖
ret, hua_binary = cv2.threshold(hua_gray, 240, 255, cv2.THRESH_BINARY_INV)#閾值處理為二值
cv2.imshow('hua_binary',hua_binary)
cv2.waitKey(0)
_,contours, hierarchy = cv2.findContours(hua_binary, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE) #cv2.RETR_EXTERNAL:只檢測外輪廓(黑底),所有子輪廓被忽略
# _,contours, hierarchy = cv2.findContours(hua_binary, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
# sortcnt=contours[0]
print(len(contours))# image_copy = hua.copy()
# cv2.drawContours(image=image_copy, contours=contours, contourIdx=-1,color=(0,255,0),thickness=2)
# cv2.imshow('Contours_show', image_copy)
# cv2.waitKey(0)image_copy = hua.copy()
sortcnt = sorted(contours, key=cv2.contourArea, reverse=True)[0] # 選取最大面積的輪廓
image_contours = cv2.drawContours(image_copy,[sortcnt],contourIdx=-1,color=(0,0,255),thickness=3)#繪制輪廓
cv2.imshow('image_contours',image_contours)
cv2.waitKey(0)epsilon = 0.005 * cv2.arcLength(sortcnt,True) #設置近似精度 【h要<ε;ε越小,點越多,越精確】
approx = cv2.approxPolyDP(sortcnt, epsilon, True) #對輪廓進行近似
print(sortcnt.shape)
print(approx.shape)image_contours = cv2.drawContours(image_copy,[approx],contourIdx=-1,color=(0,255,0),thickness=3)#繪制輪廓
cv2.imshow('image_contours',image_contours)
cv2.waitKey(0)
整體功能概述
此代碼實現了以下核心功能:
- 圖像預處理 → 灰度轉換 + 反向二值化
- 輪廓檢測 → 提取所有輪廓并篩選最大面積輪廓
- 輪廓可視化 → 繪制原始輪廓與多邊形逼近結果
- 幾何簡化 → 使用 Douglas-Peucker 算法對輪廓進行多點精簡
?模塊 1: 圖像加載與預處理
import cv2
hua = cv2.imread('picture/hua.png') # ① 讀取原圖
hua_gray = cv2.cvtColor(hua, cv2.COLOR_BGR2GRAY) # ② 轉為灰度圖
cv2.imshow('hua_b', hua_gray) # ③ 顯示灰度圖
cv2.waitKey(0) # ④ 暫停等待按鍵
關鍵點解析:
- 灰度轉換必要性:減少顏色干擾,聚焦亮度信息,提升后續二值化效果
cv2.COLOR_BGR2GRAY
:OpenCV 默認 BGR 色彩空間,此處正確轉換- 可視化驗證:通過
imshow
確保灰度轉換無誤
模塊 2: 二值化處理
ret, hua_binary = cv2.threshold(hua_gray, 240, 255, cv2.THRESH_BINARY_INV) # ⑤ 反向二值化
cv2.imshow('hua_binary', hua_binary) # ⑥ 顯示二值圖
cv2.waitKey(0)
關鍵參數詳解:
參數 | 值 | 說明 |
---|---|---|
thresh=240 | 閾值 | 像素值 >240 → 設為255(白);≤240 → 設為0(黑) |
maxval=255 | 飽和值 | 超過閾值時的賦值上限 |
THRESH_BINARY_INV | 反向模式 | 關鍵! 將高亮區域變為黑色背景,低亮區域變為白色前景 |
為何選擇反向模式?
如果花朵主體比背景更暗(常見情況),正向二值化會使背景變白,導致輪廓斷裂。反向模式可保留暗色前景的完整性。
模塊 3: 輪廓檢測
_, contours, hierarchy = cv2.findContours(hua_binary, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE) # ⑦ 檢測所有輪廓
print(len(contours)) # ⑧ 輸出輪廓數量
核心概念解析:
三個返回值:
_
:忽略的retval
(通常無用)contours
:存儲所有輪廓點的列表(每個輪廓是一個 numpy 數組)hierarchy
:輪廓層級關系矩陣(父子兄弟關系)
關鍵參數:
cv2.RETR_TREE
:檢索所有輪廓并建立完整層級樹(包括嵌套輪廓)cv2.CHAIN_APPROX_SIMPLE
:壓縮水平/垂直方向連續點,僅保留端點
典型輪廓數量:若輸出
n
,表示檢測到n
個獨立輪廓(含嵌套結構)
模塊 4: 篩選最大面積輪廓
image_copy = hua.copy() # ⑨ 創建原圖副本用于繪圖
sortcnt = sorted(contours, key=cv2.contourArea, reverse=True)[0] # ⑩ 按面積降序排序,取最大輪廓
關鍵操作:
cv2.contourArea()
:計算單個輪廓的面積(像素數)sorted(..., reverse=True)
:按面積從大到小排序[0]
:取排序后的第一個元素(最大面積輪廓)
注意事項:
如果圖像存在多個相似大小的物體,此方法可能無法穩定選取目標。建議結合位置/形狀特征進一步過濾。
模塊 5: 繪制原始輪廓
image_contours = cv2.drawContours(image_copy, [sortcnt], contourIdx=-1, color=(0,0,255), thickness=3) # ? 繪制紅色輪廓
cv2.imshow('image_contours', image_contours) # ? 顯示結果
cv2.waitKey(0)
繪圖參數詳解:
參數 | 值 | 說明 |
---|---|---|
image | image_copy | 在原圖副本上繪制 |
contours=[sortcnt] | 僅繪制最大輪廓 | 注意傳入的是單元素列表 |
contourIdx=-1 | 繪制全部層級 | -1 表示繪制輪廓的所有層級 |
color=(0,0,255) | 純紅色 | BGR 格式 |
thickness=3 | 線寬 | 較大的線寬使輪廓更醒目 |
模塊 6: 多邊形逼近(幾何簡化)
epsilon = 0.005 * cv2.arcLength(sortcnt, True) # ? 計算近似精度(基于輪廓周長)
approx = cv2.approxPolyDP(sortcnt, epsilon, True) # ? 執行多邊形逼近
print(sortcnt.shape) # ? 輸出原始輪廓點數
print(approx.shape) # ? 輸出逼近后點數
?核心算法解析:
近似精度計算:
cv2.arcLength(sortcnt, True)
:計算輪廓總長度(閉合路徑)epsilon = 0.005 * 周長
:控制逼近誤差容忍度(經驗值)- 原理:ε 越小,保留的細節越多;越大,簡化程度越高
Douglas-Peucker 算法:
- 遞歸刪除偏離直線距離小于 ε 的中間點
closed=True
:保證首尾相連形成閉合多邊形
點數對比示例:
假設原始輪廓有 1000 個點 → 逼近后可能只剩 10-20 個頂點,大幅減少數據量
模塊 7: 繪制逼近輪廓
image_contours = cv2.drawContours(image_copy, [approx], contourIdx=-1, color=(0,255,0), thickness=3) # ? 繪制綠色逼近輪廓
cv2.imshow('image_contours', image_contours) # ? 顯示結果
cv2.waitKey(0)
可視化對比:
顏色 | 含義 | 特點 |
---|---|---|
紅色 | 原始輪廓 | 顯示實際邊緣細節 |
綠色 | 多邊形逼近結果 | 展示幾何簡化后的頂點分布 |
應用場景:
可用于形狀識別(如判斷是否為多邊形)、尺寸測量、碰撞檢測等需要簡化幾何表示的場景
?常見問題與優化建議
二值化失效怎么辦?
- 調整閾值(嘗試
127
或自適應閾值adaptiveThreshold
) - 添加高斯模糊去噪:
blurred = cv2.GaussianBlur(hua_gray, (5,5), 0)
- 調整閾值(嘗試
輪廓斷裂如何處理?
- 提高閾值使前景更連貫
- 使用形態學操作連接斷開部分:
kernel = np.ones((3,3), np.uint8); closed = cv2.morphologyEx(hua_binary, cv2.MORPH_CLOSE, kernel)
多目標場景改進方案:
# 過濾小面積噪聲 min_area = 100 # 根據實際調整 valid_contours = [cnt for cnt in contours if cv2.contourArea(cnt) > min_area]
完整流程總結
讀取圖像 → 灰度轉換 → 反向二值化 → 檢測所有輪廓 → 篩選最大輪廓 → 繪制原始輪廓 → 多邊形逼近 → 繪制簡化輪廓