在計算機視覺(Computer Vision, CV)中,圖像輪廓(Image Contour)是圖像中物體邊界的重要表現形式。它不僅能描述物體的形狀特征,還能為目標識別、目標檢測、圖像分割、場景理解、三維重建等任務提供重要依據。與像素級的邊緣檢測相比,輪廓更強調封閉的邊界、整體性和幾何結構。
基本概念
定義
在圖像處理中,輪廓通常指圖像中前景目標與背景之間的邊界線。它是一個閉合曲線,可以用像素點序列來表示。
- 對于二值圖像,輪廓是前景與背景的交界線。
- 對于灰度圖像,輪廓常通過邊緣檢測或閾值分割得到。
輪廓與邊緣的區別
- 邊緣(Edge):灰度強度發生顯著變化的位置,可能是不連續的。
- 輪廓(Contour):物體的完整邊界,更強調閉合性和結構性。
數學表示
若一個二值目標區域記為 R,則其輪廓 C 可表示為:
即邊界點是區域中與背景相鄰的像素點集合。
提取方法
輪廓提取的目標是將圖像中物體的邊界點序列化,形成閉合曲線。
1. 閾值分割 + 輪廓提取
- 先通過全局閾值(如 Otsu 算法)或自適應閾值將圖像轉為二值圖。
- 然后使用輪廓追蹤算法(如 OpenCV
findContours
)提取邊界。 - 優點:簡單高效,適合對比度清晰的目標。
- 缺點:對噪聲和光照敏感。
2. 邊緣檢測 + 輪廓連接
- 常用邊緣算子:Sobel、Canny、Laplacian。
- 檢測到邊緣后,通過連通域分析、霍夫變換或形態學操作獲得閉合輪廓。
- 適合復雜紋理和低對比度圖像。
3. 形態學方法
- 利用腐蝕(Erode)、膨脹(Dilate)、開運算、閉運算等形態學操作去噪或填洞。
- 提取骨架(Skeletonization)后再追蹤輪廓。
- 常用于噪聲較多的工業檢測任務。
4. 主動輪廓模型(Snake)
- 通過能量函數(包含圖像梯度約束和曲線光滑性約束)使輪廓曲線自動收縮到物體邊界。
- 優點:輪廓連續性好,可處理復雜目標。
- 缺點:依賴初始輪廓位置,計算復雜。
5. 基于深度學習的方法
- 使用語義分割(如 U-Net、Mask R-CNN)獲得物體區域,再提取輪廓。
- 優點:魯棒性強,適應復雜場景。
- 缺點:需要大量訓練數據。
輪廓的表示與特征
提取到輪廓后,需要對其進行表示和特征化,便于后續處理。
1. 邊界鏈碼(Chain Code)
- 以起點為基準,用 4 鄰域或 8 鄰域的方向序列描述輪廓。
- 優點:存儲緊湊。
- 缺點:不平移不變。
2. 多邊形逼近
- 用折線逼近復雜輪廓,例如 Douglas-Peucker 算法。
- 常用于簡化輪廓,提高計算效率。
3. 矩(Moments)
- 幾何矩:描述面積、質心。
- Hu 不變矩:在旋轉、縮放、平移下保持不變,適合形狀識別。
4. 傅里葉描述子
- 將輪廓點序列看作復數序列,做傅里葉變換。
- 低頻分量表示整體形狀,高頻分量表示細節。
- 可用于形狀匹配。
5. 曲率與角點
- 曲率較大的點通常對應物體的特征角點。
- 在目標識別和姿態估計中很重要。
OpenCV中的輪廓查找
核心函數
cv2.findContours()函數
查找輪廓。
contours, hierarchy = cv2.findContours(image, mode, method)
參數解析:
image
: 輸入的二值圖像。注意: 此函數會修改輸入圖像,通常建議傳入一個副本。mode
: 輪廓檢索模式,決定了如何組織輪廓的層級關系。cv2.RETR_EXTERNAL
: 只檢索最外層的輪廓。cv2.RETR_LIST
: 檢索所有輪廓,但不建立任何層級關系。cv2.RETR_CCOMP
: 檢索所有輪廓,并將它們組織成兩級層次結構(外部輪廓和孔洞)。cv2.RETR_TREE
: 檢索所有輪廓,并建立完整的輪廓層次結構樹。
method
: 輪廓近似方法,決定如何存儲輪廓點。cv2.CHAIN_APPROX_NONE
: 存儲輪廓上所有的點。cv2.CHAIN_APPROX_SIMPLE
: 只存儲輪廓的端點,例如,一個矩形只存儲四個角點。
返回值:
contours
: 找到的所有輪廓的列表。每個輪廓都是一個 NumPy 數組,包含了該輪廓的所有點。hierarchy
: 一個可選的層級信息數組。它告訴我們每個輪廓的父輪廓、子輪廓、上一個輪廓和下一個輪廓。
cv2.drawContours()函數
在圖像上繪制找到的輪廓。
cv2.drawContours(image, contours, contourIdx, color, thickness)
參數解析:
image
: 要在上面繪制輪廓的圖像。contours
: 輪廓列表,通常是cv2.findContours()
的返回值。contourIdx
: 輪廓的索引,指定繪制哪個輪廓。如果要繪制所有輪廓,傳入-1
。color
: 輪廓的顏色,以(B, G, R)
元組形式表示。thickness
: 輪廓線的粗細。如果傳入-1
或cv2.FILLED
,則輪廓內部會被填充。
示例
查找并繪制圖像中的輪廓
import cv2
import numpy as np# 1. 讀取圖像并轉換為灰度圖
image = cv2.imread('shape.png')
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)# 2. 圖像閾值化
# 將圖像轉換為二值圖像,大于127的像素變為255,否則變為0
ret, thresh = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY)# 3. 查找輪廓
# 檢索所有輪廓,并使用簡單的近似方法
contours, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)# 4. 在原圖上繪制輪廓
# 繪制所有輪廓,顏色為綠色,粗細為2
output_image = image.copy()
cv2.drawContours(output_image, contours, -1, (0, 255, 0), 2)# 5. 顯示結果
cv2.imshow('Original Image', image)
cv2.imshow('Binary Image', thresh)
cv2.imshow('Contours', output_image)
cv2.waitKey(0)
cv2.destroyAllWindows()
執行效果:
形狀分析與測量
通過計算輪廓的面積、周長等屬性來了解物體的基本幾何信息。
import cv2
import numpy as np# 1. 創建一個二值圖像
img = np.zeros((300, 300), dtype=np.uint8)
# 畫一個圓,填充它
cv2.circle(img, (150, 150), 80, 255, -1)# 2. 查找輪廓
contours, _ = cv2.findContours(img, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)# 獲取第一個也是唯一的輪廓
cnt = contours[0]# 3. 計算輪廓屬性
area = cv2.contourArea(cnt)
perimeter = cv2.arcLength(cnt, True) # True 表示輪廓是閉合的#print(f"輪廓面積: {area}")
#print(f"輪廓周長: {perimeter}")# 4. 繪制輪廓的邊界框和最小外接圓
x, y, w, h = cv2.boundingRect(cnt)
cv2.rectangle(img, (x, y), (x + w, y + h), (100), 2)(center_x, center_y), radius = cv2.minEnclosingCircle(cnt)
center = (int(center_x), int(center_y))
radius = int(radius)
cv2.circle(img, center, radius, (100), 2)cv2.imshow("Shape Analysis", img)
cv2.waitKey(0)
cv2.destroyAllWindows()
執行效果:
形狀匹配
形狀匹配用于比較兩個輪廓的相似度。這在物體識別、缺陷檢測等任務中非常有用。cv2.matchShapes
函數是實現這一功能的關鍵。
import cv2
import numpy as np# 1. 定義兩個形狀(輪廓)
# 第一個形狀:矩形
rect = np.zeros((200, 200), dtype=np.uint8)
cv2.rectangle(rect, (50, 50), (150, 150), 255, -1)
contours1, _ = cv2.findContours(rect, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)# 第二個形狀:旋轉的矩形
rotated_rect = np.zeros((200, 200), dtype=np.uint8)
M = cv2.getRotationMatrix2D((100, 100), 45, 1) # 旋轉45度
rotated_rect = cv2.warpAffine(rect, M, (200, 200))
contours2, _ = cv2.findContours(rotated_rect, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)# 2. 匹配兩個形狀
# cv2.CONTOURS_MATCH_I1 是一個常用的方法
match_score = cv2.matchShapes(contours1[0], contours2[0], cv2.CONTOURS_MATCH_I1, 0)print(f"result: {match_score}")
# 分數越低,兩個形狀越相似。理想情況下,完全相同的形狀得分為0。
# 即使形狀經過旋轉、縮放或平移,matchShapes函數也能很好地工作。cv2.imshow("Original", rect)
cv2.imshow("Rotated", rotated_rect)
cv2.waitKey(0)
cv2.destroyAllWindows()
執行結果:
基于輪廓的圖像分割與對象提取
找到輪廓后,可以用它來創建一個掩模(Mask),以精確地從原始圖像中提取出物體。
import cv2
import numpy as np# 1. 讀取彩色圖像
image = cv2.imread('object.jpg')# 2. 轉到 HSV 顏色空間
hsv = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)# 3. 定義顏色范圍(這里以綠色圓為例)
lower_green = np.array([40, 50, 50])
upper_green = np.array([80, 255, 255])# 4. 生成掩模
mask = cv2.inRange(hsv, lower_green, upper_green)# 5. 提取物體
extracted_object = cv2.bitwise_and(image, image, mask=mask)# 6. 顯示
cv2.imshow("Original", image)
cv2.imshow("Mask", mask)
cv2.imshow("Extracted Object", extracted_object)
cv2.waitKey(0)
cv2.destroyAllWindows()
執行效果:
輪廓近似與多邊形擬合
輪廓可能包含成千上萬個點,為了簡化形狀并減少計算量,可以使用多邊形擬合。
import cv2
import numpy as np# 1. 創建一個形狀(這里是一個圓,但可以用于任何復雜的形狀)
img = np.zeros((300, 300), dtype=np.uint8)
cv2.circle(img, (150, 150), 100, 255, -1)# 2. 查找輪廓
contours, _ = cv2.findContours(img, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE) # 使用CHAIN_APPROX_NONE來獲取所有點
original_contour = contours[0]# 3. 輪廓近似
# 參數 epsilon 決定了近似的精度,值越小,近似越接近原始輪廓
epsilon = 0.04 * cv2.arcLength(original_contour, True)
approx_contour = cv2.approxPolyDP(original_contour, epsilon, True)# 4. 繪制原始輪廓和近似后的輪廓
# 原始輪廓(紅色)
original_img = cv2.cvtColor(img, cv2.COLOR_GRAY2BGR)
cv2.drawContours(original_img, [original_contour], -1, (0, 0, 255), 2)# 近似輪廓(綠色)
approx_img = np.zeros_like(original_img)
cv2.drawContours(approx_img, [approx_contour], -1, (0, 255, 0), 2)
cv2.imshow("Original vs. Approximated", np.hstack([original_img, approx_img]))
cv2.waitKey(0)
cv2.destroyAllWindows()
執行效果: