A computer generated image of a red object on a tiled floor

← INSIGHTS & PERSPECTIVES | 機器學習

OpenCV 輪廓形狀擬合有哪些常用方法

整理 OpenCV 輪廓擬合常用函數,包含 boundingRect、minAreaRect、fitEllipse、minEnclosingCircle 與 fitLine。

OpenCV 輪廓形狀擬合常用方法包括 cv2.boundingRect()cv2.minAreaRect()cv2.fitEllipse()cv2.minEnclosingCircle()cv2.fitLine()。選擇函數時,要先判斷需要的是外接範圍、旋轉角度、橢圓軸向、圓形範圍,還是點集趨勢線。

cv2.boundingRect() 適合做什麼?

cv2.boundingRect() 適合取得不旋轉的矩形邊界框。函數會回傳 (x, y, w, h),其中 (x, y) 是左上角座標,wh 是矩形寬高。

這個方法速度快,也很適合做裁切 ROI。限制是矩形邊界框永遠與影像座標軸平行,所以物件旋轉時,框出的區域會包含較多背景。

import cv2

img = cv2.imread("image.jpg")
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

_, thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)
contours, _ = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)

x, y, w, h = cv2.boundingRect(contours[0])
cv2.rectangle(img, (x, y), (x + w, y + h), (0, 0, 255), 2)

cv2.minAreaRect() 適合做什麼?

cv2.minAreaRect() 適合取得能包住輪廓的最小旋轉矩形。函數會回傳中心點、寬高與角度,常用在需要知道物件方向的情境。

原文特別提醒,rect 本身不是可以任意修改的物件。若要自己產生旋轉矩形,可以建立 ((center_x, center_y), (width, height), angle) 這種結構,再用 cv2.boxPoints() 取四個角點。

import cv2
import numpy as np

rect = cv2.minAreaRect(contours[0])
box = cv2.boxPoints(rect)
box = np.int0(box)
cv2.drawContours(img, [box], 0, (0, 0, 255), 2)
center = (250, 250)
width = 200
height = 100
angle = 45

rect = (center, (width, height), angle)
box = cv2.boxPoints(rect)
box = np.int0(box)

cv2.fitEllipse() 和最小包圍橢圓差在哪?

cv2.fitEllipse() 找的是最能擬合點集的橢圓,不保證包住所有點。最小包圍橢圓的概念是包住所有點,但 OpenCV Python 常見實作多用 fitEllipse() 取得中心、軸長與角度。

使用 cv2.fitEllipse() 時,輪廓點數至少要足夠形成橢圓擬合。若點數太少或輪廓品質不穩,橢圓角度也會跟著不穩。

import cv2

ellipse = cv2.fitEllipse(contours[0])
cv2.ellipse(img, ellipse, (0, 0, 255), 2)

cv2.minEnclosingCircle() 適合做什麼?

cv2.minEnclosingCircle() 適合取得能包住點集的最小外接圓。函數會回傳圓心與半徑,適合近似圓形物件或需要快速估算物件尺寸的場景。

最小外接圓不會描述物件方向,因此不適合拿來轉正影像。若任務需要方向角,應改用 cv2.minAreaRect()cv2.fitEllipse()cv2.fitLine()

(x, y), radius = cv2.minEnclosingCircle(contours[0])
center = (int(x), int(y))
radius = int(radius)

cv2.circle(img, center, radius, (0, 0, 255), 2)

cv2.fitLine() 適合做什麼?

cv2.fitLine() 適合從一組輪廓點擬合出趨勢直線。函數回傳 (vx, vy, x0, y0),其中 (vx, vy) 是方向向量,(x0, y0) 是線上的一點。

這個方法常用於判斷長條形物件、邊緣方向或點集主要方向。若要把直線畫滿整張影像,可以用方向向量推算左右邊界交點。

rows, cols = img.shape[:2]
[vx, vy, x, y] = cv2.fitLine(contours[0], cv2.DIST_L2, 0, 0.01, 0.01)
lefty = int((-x * vy / vx) + y)
righty = int(((cols - x) * vy / vx) + y)

cv2.line(img, (cols - 1, righty), (0, lefty), (0, 0, 255), 2)

OpenCV 形狀擬合該怎麼選?

OpenCV 形狀擬合要依輸出需求選函數。只要裁切用 boundingRect(),要旋轉矩形用 minAreaRect(),要橢圓軸向用 fitEllipse(),要外接圓用 minEnclosingCircle()

需求建議函數主要輸出
快速裁切輪廓cv2.boundingRect()x、y、寬、高
找旋轉外框cv2.minAreaRect()中心、寬高、角度
找橢圓方向cv2.fitEllipse()中心、軸長、角度
找最小外接圓cv2.minEnclosingCircle()圓心、半徑
找點集趨勢線cv2.fitLine()方向向量、線上一點

我自己的習慣是先用 boundingRect() 做最穩的裁切,再看任務是否需要角度。如果物件邊界容易因為小缺口改變,橢圓擬合有時比最小矩形更穩。

常見問題

Qcv2.boundingRect()cv2.minAreaRect() 差在哪?

cv2.boundingRect() 回傳不旋轉矩形,速度快且適合裁切。cv2.minAreaRect() 回傳旋轉矩形,適合取得物件角度與更貼合的外框。

Qcv2.fitEllipse() 一定能包住所有輪廓點嗎?

cv2.fitEllipse() 不保證包住所有輪廓點。cv2.fitEllipse() 是擬合橢圓,重點是符合點集趨勢,不是最小外接邊界。

Q哪個 OpenCV 函數適合做物件轉正?

若物件是近似長條或橢圓形,我會優先測 cv2.fitEllipse() 的角度。若物件是明確矩形,cv2.minAreaRect() 也可以取得旋轉角度。

Q輪廓點太少會影響擬合嗎?

輪廓點太少會影響擬合結果,尤其是橢圓擬合。實作前應先確認 contours[0] 的點數與輪廓品質。

參考資料

延伸閱讀

最後更新

Mon May 08 2023 08:00:00 GMT+0800 (Taiwan Standard Time)

關於作者

Claire Chang | 企業 AI 導入與流程轉型顧問。專注於 AI Agent 架構設計、ERP 系統整合與企業 AI 治理。

首次發布:2023-05-08