Posted on

OpenCV裡面形狀擬合的幾種方法

取得輪廓的矩形邊界框

cv2.boundingRect() 函數可以用來計算一個輪廓的矩形邊界框(bounding box),即最小矩形框,這個矩形框可以完全包圍輪廓的所有點。這個函數的返回值是一個元組 (x,y,w,h),其中 (x,y) 是矩形框左上角的座標,wh 是矩形框的寬度和高度。

下面是一個使用 cv2.boundingRect() 函數找到最小矩形框的範例程式碼:

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)

# 畫出輪廓
cv2.drawContours(img, contours, -1, (0, 255, 0), 2)

# 尋找最小矩形框
x, y, w, h = cv2.boundingRect(contours[0])

# 畫出矩形框
cv2.rectangle(img, (x, y), (x+w, y+h), (0, 0, 255), 2)

# 顯示圖像
cv2.imshow('image', img)
cv2.waitKey(0)
cv2.destroyAllWindows()

最小擬合矩形

cv2.minAreaRect() 可計算最小擬合矩形,這個函數會將給定的輪廓點集擬合成一個矩形,這個矩形具有最小面積,可以包圍住所有的輪廓點。

下面是一個使用 cv2.minAreaRect() 函數找到最小擬合矩形的範例程式碼:

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)

# 畫出輪廓
cv2.drawContours(img, contours, -1, (0, 255, 0), 2)

# 尋找最小擬合矩形
rect = cv2.minAreaRect(contours[0])
box = cv2.boxPoints(rect)
box = np.int0(box)

# 畫出矩形
cv2.drawContours(img, [box], 0, (0, 0, 255), 2)

# 顯示圖像
cv2.imshow('image', img)
cv2.waitKey(0)
cv2.destroyAllWindows()

最小擬合矩形所得到的結果rect其實是會有三個值,包括中心點座標、寬和高的數組、矩形的角度,我們可以用下面的程式產生自己定義的rect(因為rect本身無法修改,要修改就要自己建一個)

# 定義旋轉矩形的中心點、寬度、高度和角度
center = (250, 250)
width = 200
height = 100
angle = 45

# 計算旋轉矩形的四個角點
rect = ((center[0], center[1]), (width, height), angle)
box = cv2.boxPoints(rect)
box = np.int0(box)

取得最小包圍橢圓

若需要找到一個能夠包圍所有點的橢圓,可以使用 cv2.minEnclosingEllipse() 函數。這個函數會將給定的點集包圍在一個最小面積橢圓內。

下面是使用 cv2.minEnclosingEllipse() 函數找到最小包圍橢圓的範例程式碼:

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)

# 畫出輪廓
cv2.drawContours(img, contours, -1, (0, 255, 0), 2)

# 尋找最小包圍橢圓
ellipse = cv2.fitEllipse(contours[0])
cv2.ellipse(img, ellipse, (0, 0, 255), 2)

# 尋找最小面積包圍橢圓
ellipse = cv2.minEnclosingEllipse(contours[0])
cv2.ellipse(img, (int(ellipse[0][0]), int(ellipse[0][1])),
            (int(ellipse[1][0] / 2), int(ellipse[1][1] / 2)),
            ellipse[2], 0, 360, (255, 0, 0), 2)

# 顯示圖像
cv2.imshow('image', img)
cv2.waitKey(0)
cv2.destroyAllWindows()

最佳擬合橢圓

cv2.fitEllipse() 函數找到的是能夠最好擬合給定點集的橢圓,並不一定能夠包圍住所有點。

這個函數會將輸入的輪廓點集擬合成一個橢圓,返回橢圓的中心座標、軸長、旋轉角度等相關信息。

下面是一個簡單的範例程式碼,展示如何使用 cv2.fitEllipse() 找到最小包圍橢圓:

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)

# 畫出輪廓
cv2.drawContours(img, contours, -1, (0, 255, 0), 2)

# 尋找最小包圍橢圓
ellipse = cv2.fitEllipse(contours[0])
cv2.ellipse(img, ellipse, (0, 0, 255), 2)

# 顯示圖像
cv2.imshow('image', img)
cv2.waitKey(0)
cv2.destroyAllWindows()

最小包圍圓

要找到一個能夠最小外接圓包圍給定的點集,可以使用 cv2.minEnclosingCircle() 函數。這個函數會將給定的點集包圍在一個最小面積圓內。

下面是一個使用 cv2.minEnclosingCircle() 函數找到最小外接圓的範例程式碼:

import cv2
import numpy as np

# 讀入圖像,轉為灰度
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)

# 畫出輪廓
cv2.drawContours(img, contours, -1, (0, 255, 0), 2)

# 尋找最小外接圓
(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.imshow('image', img)
cv2.waitKey(0)
cv2.destroyAllWindows()

最適擬合直線

要找到一個能夠最好擬合給定點集的直線,可以使用 cv2.fitLine() 函數。這個函數會將給定的點集擬合成一條直線,返回的是一個向量 (vx,vy,x0,y0),其中 (vx,vy) 是直線的方向向量,(x0,y0) 是直線上的一點。

下面是一個使用 cv2.fitLine() 函數找到最適擬和直線的範例程式碼:

import cv2
import numpy as np

# 讀入圖像,轉為灰度
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)

# 畫出輪廓
cv2.drawContours(img, contours, -1, (0, 255, 0), 2)

# 尋找最適擬和直線
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)

# 顯示圖像
cv2.imshow('image', img)
cv2.waitKey(0)
cv2.destroyAllWindows()