在電腦視覺應用中,輪廓(Contour)常用來描述物體的邊界。
當圖像中有雜訊或物體邊緣過於複雜時,我們可以利用輪廓逼近技術,將輪廓簡化成較少點數的多邊形,這不僅有助於後續的形狀分析,也能提高處理速度。
本文將介紹如何使用 OpenCV 中的 cv2.arcLength
與 cv2.approxPolyDP
來進行輪廓逼近,並展示一些實際應用。
cv2.findContours
可以提取圖像中的輪廓。在進行輪廓逼近前,我們先需要計算原始輪廓的周長,這可以透過 cv2.arcLength
完成。
peri = cv2.arcLength(cnt, True)
獲得周長後,便可以使用 cv2.approxPolyDP
進行輪廓逼近。
epsilon = epsilon_factor * peri
approx = cv2.approxPolyDP(cnt, epsilon, True)
假設我們要對圖像中的幾何形狀進行識別,透過輪廓逼近可以幫助我們判斷該形狀的頂點數量:
利用小畫家隨便畫幾個圖形來測試
奇怪為什麼星星怎判斷成圓形呢?,原因是因為程式碼當中,我們設定超過四個頂點就判成圓形,如果需要判斷成星星,需要先知道星星圖形頂點有幾個,在重新修改判斷邏輯
以下是一個完整範例,展示如何從圖像中提取輪廓、進行逼近,並根據逼近後的頂點數來辨識形狀:
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)
輪廓逼近技術不僅用於基本的形狀辨識,還有許多實際應用:
本文介紹了如何使用 OpenCV 的 cv2.arcLength
與 cv2.approxPolyDP
進行輪廓逼近,並以實際程式範例說明了如何從圖像中提取輪廓、簡化輪廓及識別形狀。透過這項技術,不僅能夠減少雜訊影響,還可以提高形狀辨識的效率和精度。無論是在工業檢測、文件處理還是手勢識別等領域,輪廓逼近都是一個非常實用的工具。