在電腦視覺應用中,輪廓(Contour)常用來描述物體的邊界。
當圖像中有雜訊或物體邊緣過於複雜時,我們可以利用輪廓逼近技術,將輪廓簡化成較少點數的多邊形,這不僅有助於後續的形狀分析,也能提高處理速度。本文將介紹如何使用 OpenCV 中的cv2.arcLength
與 cv2.approxPolyDP
來進行輪廓逼近,並展示一些實際應用。1. 理解輪廓與輪廓逼近
輪廓 (Contour):
- 定義:輪廓是一系列連續點,通常用來表示二值圖像中物體的邊界。
- 取得方式:透過 OpenCV 的
cv2.findContours
可以提取圖像中的輪廓。
輪廓逼近 (Contour Approximation):
- 目的:簡化輪廓,去除不必要的細節,只保留代表形狀的關鍵點。
- 優點:
- 降低計算量。
- 有助於識別基本幾何形狀(如三角形、矩形、多邊形等)。
- 減少雜訊影響,提高辨識精準度。
2. 計算輪廓周長:cv2.arcLength
在進行輪廓逼近前,我們先需要計算原始輪廓的周長,這可以透過 cv2.arcLength
完成。
peri = cv2.arcLength(cnt, True)
- 參數說明:
- cnt:輸入的輪廓(通常是 cv2.findContours 返回的其中一個輪廓)。
- True:表示輪廓是封閉的(即輪廓的起點與終點相連)。
3. 進行輪廓逼近:cv2.approxPolyDP
獲得周長後,便可以使用 cv2.approxPolyDP
進行輪廓逼近。
epsilon = epsilon_factor * peri
approx = cv2.approxPolyDP(cnt, epsilon, True)
- 參數說明:
- cnt:原始輪廓。
- epsilon:逼近精度,通常設為周長的某個比例。這個值越小,逼近結果越接近原始輪廓;值越大,簡化效果越明顯。
- True:表示逼近後的輪廓依然是封閉的多邊形。
- 工作原理:
- 該函數基於 Douglas-Peucker 演算法,透過遞歸方式去除那些對輪廓形狀影響較小的點,保留最能描述形狀的頂點。
4. 應用範例:形狀辨識
實際案例
假設我們要對圖像中的幾何形狀進行識別,透過輪廓逼近可以幫助我們判斷該形狀的頂點數量:
- 3 個頂點:可能為三角形。
- 4 個頂點:可能為矩形或正方形。
- 多於 4 個頂點:可能為圓形或其他多邊形。
範例圖片
利用小畫家隨便畫幾個圖形來測試

結果圖
奇怪為什麼星星怎判斷成圓形呢?,原因是因為程式碼當中,我們設定超過四個頂點就判成圓形,如果需要判斷成星星,需要先知道星星圖形頂點有幾個,在重新修改判斷邏輯

範例程式碼
以下是一個完整範例,展示如何從圖像中提取輪廓、進行逼近,並根據逼近後的頂點數來辨識形狀:
import cv2
import numpy as np
# 讀取圖像並轉為灰階
image = cv2.imread('shapes.jpg')
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
# 二值化處理:可以使用閾值或Canny邊緣檢測
_, thresh = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY)
# 或者使用 Canny: thresh = cv2.Canny(gray, 50, 150)
# 找到輪廓
contours, _ = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
for cnt in contours:
# 計算周長
peri = cv2.arcLength(cnt, True)
# 設定逼近精度,epsilon_factor 可根據實際需求調整
epsilon_factor = 0.02
epsilon = epsilon_factor * peri
# 輪廓逼近
approx = cv2.approxPolyDP(cnt, epsilon, True)
# 根據頂點數量判斷形狀
vertices = len(approx)
shape = "unidentified"
if vertices == 3:
shape = "Triangle"
elif vertices == 4:
# 進一步可以計算寬高比來區分正方形與矩形
shape = "Rectangle"
elif vertices > 4:
shape = "Circle" # 也可以進一步判斷圓形的程度
# 畫出逼近後的輪廓與形狀名稱
cv2.drawContours(image, [approx], -1, (0, 255, 0), 2)
# 計算中心點來顯示文字
M = cv2.moments(cnt)
if M["m00"] != 0:
cX = int(M["m10"] / M["m00"])
cY = int(M["m01"] / M["m00"])
else:
cX, cY = 0, 0
cv2.putText(image, shape, (cX - 20, cY), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 0, 0), 2)
# 顯示結果
cv2.imshow("Shapes", image)
cv2.waitKey(0)
cv2.destroyAllWindows()
程式說明:
- 讀取與預處理:將圖像讀入並轉為灰階,再進行二值化處理,以便提取明顯輪廓。
- 輪廓提取:利用
cv2.findContours
找出所有外部輪廓。 - 輪廓逼近:對每個輪廓計算周長,並根據周長與設定的比例因子計算逼近精度,再透過
cv2.approxPolyDP
簡化輪廓。 - 形狀辨識:根據逼近後的頂點數量來判斷形狀,並在圖像上標示出來。
修正可判斷星星
修改部分程式碼 先只印出頂點數量,由結果圖得知星星頂點為10
# 根據頂點數量判斷形狀
vertices = len(approx)
# 畫出逼近後的輪廓與形狀名稱
cv2.drawContours(image, [approx], -1, (0, 255, 0), 2)
# 計算中心點來顯示文字
M = cv2.moments(cnt)
if M["m00"] != 0:
cX = int(M["m10"] / M["m00"])
cY = int(M["m01"] / M["m00"])
else:
cX, cY = 0, 0
cv2.putText(image, str(vertices), (cX + 200, cY-200), cv2.FONT_HERSHEY_SIMPLEX,4, (255, 0, 0), 2)
顯示頂點結果圖

修正邏輯
得知星星數量後,修正判斷邏輯
# 根據頂點數量判斷形狀
vertices = len(approx)
shape = "unidentified"
if vertices == 3:
shape = "Triangle"
elif vertices == 4:
# 進一步可以計算寬高比來區分正方形與矩形
shape = "Rectangle"
elif vertices == 10:
shape = "Star" # 也可以進一步判斷圓形的程度
elif vertices > 4:
shape = "Circle" # 也可以進一步判斷圓形的程度
# 畫出逼近後的輪廓與形狀名稱
cv2.drawContours(image, [approx], -1, (0, 255, 0), 2)
# 計算中心點來顯示文字
M = cv2.moments(cnt)
if M["m00"] != 0:
cX = int(M["m10"] / M["m00"])
cY = int(M["m01"] / M["m00"])
else:
cX, cY = 0, 0
cv2.putText(image, shape, (cX + 200, cY-200), cv2.FONT_HERSHEY_SIMPLEX, 2, (255, 0, 0), 2)

5. 應用場景
輪廓逼近技術不僅用於基本的形狀辨識,還有許多實際應用:
- 物體偵測:在工業自動化中辨識和計數物體。
- 文件分析:檢測文件邊界、校正傾斜的文件圖像。
- 標誌辨識:例如交通標誌的識別,通過辨識輪廓形狀來進行分類。
- 手勢識別:在手勢控制中,辨識手的輪廓和關鍵點。
6. 小結
本文介紹了如何使用 OpenCV 的 cv2.arcLength
與 cv2.approxPolyDP
進行輪廓逼近,並以實際程式範例說明了如何從圖像中提取輪廓、簡化輪廓及識別形狀。透過這項技術,不僅能夠減少雜訊影響,還可以提高形狀辨識的效率和精度。無論是在工業檢測、文件處理還是手勢識別等領域,輪廓逼近都是一個非常實用的工具。