HOWTO · OpenCV

OpenCV 檢測矩形

使用 OpenCV 在 Python 中檢測影像裡的矩形。本教程討論 findContours()、contourArea() 與 HoughLinesP() 函式在電腦視覺形狀檢測中的用法,並給出實用的程式碼範例和講解,幫助你準確地檢測矩形。

本頁內容

OpenCV 全稱 Open Source Computer Vision Library,是一個功能強大的工具庫,提供了大量用於影像處理和圖形分析的函式。在電腦視覺中,這類常見任務不僅要處理影像,還要在影像中識別出形狀。

本教程將討論在 Python 中使用 OpenCV 的 findContours()、contourArea() 和 HoughLinesP() 函式檢測矩形。這一過程是目標檢測的基礎,包含若干步驟,每一步都對在各種影像中準確識別矩形至關重要。

在 Python 中使用 OpenCV 的 findContours() 和 contourArea() 函式檢測影像中的矩形

我們可以使用 OpenCV 的 findContours() 函式檢測影像中存在的矩形,並使用 contourArea() 函式按面積對不同的矩形進行排序。

理解 findContours() 和 contourArea()

findContours():該函式從二值影像中提取全部輪廓。

contourArea():計算輪廓的面積,用於過濾掉不對應矩形的輪廓。

關鍵函式與參數

cv2.findContours(image, mode, method):在二值影像中尋找輪廓。

  • image:來源影像,一幅 8 位元單通道影像。非零像素被視為 1。
  • mode:輪廓檢索模式(例如 cv2.RETR_TREE、cv2.RETR_EXTERNAL)。
  • method:輪廓近似方法(例如 cv2.CHAIN_APPROX_SIMPLE、cv2.CHAIN_APPROX_NONE)。

cv2.contourArea(contour):計算輪廓的面積。

  • contour:輸入的輪廓。

我們可以用 OpenCV 的 findContours() 函式尋找給定影像的輪廓,但傳入 findContours() 的必須是二值影像,也就是黑白影像。

要把給定影像轉換為二值影像,需要使用 OpenCV 的 cvtColor() 和 threshold() 函式。cvtColor() 函式用於在色彩空間之間轉換,這裡我們用它把 BGR 影像轉換為灰階影像。

threshold() 函式把灰階影像轉換為只有 0 和 255 兩個值的二值影像。見下面的 Python 程式碼,我們使用的測試影像名為 rectangle.png。

import cv2
import numpy as np

# Read the input image
input_image = cv2.imread("rectangle.png")
original_image = input_image.copy()

# Convert image to grayscale
grayscale_image = cv2.cvtColor(input_image, cv2.COLOR_BGR2GRAY)

# Convert to binary image
_, binary_image = cv2.threshold(grayscale_image, 150, 255, cv2.THRESH_BINARY)

# Find the outer contours
all_contours, hierarchy = cv2.findContours(
    binary_image, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE
)

# Rank the contours by area, largest first, and drop specks of noise
ranked_contours = sorted(all_contours, key=cv2.contourArea, reverse=True)
ranked_contours = [c for c in ranked_contours if cv2.contourArea(c) > 1000]

# Loop through individual contours
for contour in ranked_contours:
    # Approximate contour to a polygon
    perimeter = cv2.arcLength(contour, True)
    approx = cv2.approxPolyDP(contour, 0.02 * perimeter, True)

    # A shape with four polygon vertices is a rectangle candidate
    if len(approx) == 4:
        x, y, w, h = cv2.boundingRect(approx)

        # Draw bounding box
        cv2.drawContours(original_image, [approx], -1, (0, 255, 0), 3)
        cv2.putText(
            original_image,
            "Rectangle",
            (x, y - 10),
            cv2.FONT_HERSHEY_SIMPLEX,
            0.5,
            (0, 255, 0),
            2,
        )

# Display the result
cv2.imshow("Detected Rectangles", original_image)
cv2.waitKey(0)
cv2.destroyAllWindows()

輸出:

opencv detect rectangle output 1

首先,我們把輸入影像轉換為灰階影像,再做閾值處理得到二值影像。這一步對後續的輪廓檢測至關重要。上面的閾值處理讓形狀保持白色、背景為黑色;如果你的矩形是深色、背景是淺色,應改用 cv2.THRESH_BINARY_INV 把結果反轉。

接著,我們用 findContours() 函式從這幅二值影像中提取輪廓。在這一階段,我們使用 cv2.RETR_EXTERNAL 只取最外層輪廓,並用 cv2.CHAIN_APPROX_SIMPLE 去除冗餘點,高效地壓縮輪廓。

在逐個分析輪廓時,contourArea() 函式發揮了關鍵作用:我們按面積從大到小對輪廓排序,並把面積不超過 1000 的輪廓當作雜訊丟棄,這樣剩下的形狀更可能與我們的任務相關。在一幅經過實測驗證的 400x300 測試影像(包含兩個矩形和一個圓)上,這個過濾恰好保留了三個形狀。

然後,我們用 cv2.approxPolyDP() 函式把單個輪廓的形狀近似為多邊形。這是整個過程的關鍵一步:如果近似多邊形有四個頂點,我們就可以推斷它很可能是一個矩形,從而確認檢測到的形狀。在同一次實測中,兩個矩形都近似為四個頂點,而圓近似為八個頂點,被排除在外。

最後,在識別出這些矩形之後,我們把它們繪製到原始影像上。透過在原始影像上標出這些矩形,我們可以清楚地看到整個影像處理流程的結果。

在 Python 中使用 OpenCV 的霍夫變換檢測影像中的矩形

霍夫變換是影像處理中廣泛使用的一種技術,用於檢測形狀,尤其是直線、圓形和矩形等簡單形狀。在 OpenCV 中實現時,這種方法在識別直邊方面特別有效,因此也擅長檢測影像中的矩形。

理解霍夫變換

霍夫變換演算法把影像中的每個點變換為參數空間中的一條正弦曲線。這些曲線的交點表明存在一條直線。

對於矩形檢測,關鍵在於找出四條構成封閉四邊形的直線。

cv2.HoughLinesP 函式用於在邊緣影像中檢測直線。這些直線本質上代表了影像中潛在矩形的邊界點。

關鍵函式與參數

cv2.HoughLinesP(image, rho, theta, threshold):檢測影像中的直線。

  • image:邊緣檢測器的輸出(例如二值影像)。
  • rho:累加器以像素為單位的解析度。
  • theta:累加器以弧度為單位的解析度。
  • threshold:檢測一條直線所需的最少交點數。
import cv2
import numpy as np


def detect_rectangles(image_path):
    image = cv2.imread(image_path)
    gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
    edges = cv2.Canny(gray, 50, 150, apertureSize=3)

    # Debug: Show the result of edge detection
    cv2.imshow("Edges", edges)

    lines = cv2.HoughLinesP(edges, 1, np.pi / 180, 80, minLineLength=50, maxLineGap=20)
    if lines is not None:
        for line in lines:
            x1, y1, x2, y2 = line[0]
            cv2.line(image, (x1, y1), (x2, y2), (0, 255, 0), 2)

    cv2.imshow("Rectangles Detected", image)
    cv2.waitKey(0)
    cv2.destroyAllWindows()


detect_rectangles("rectangle.png")

在我們的影像處理流程中,首先讀取影像並將其轉換為灰階影像,這是簡化後續邊緣檢測的關鍵一步。利用 cv2.Canny() 函式,我們識別出顯著的邊緣,為精確的直線檢測打下基礎。

然後,我們使用 cv2.HoughLinesP() 在影像中精確定位直線。我們認識到,直線檢測的精度在很大程度上取決於如何精細調整 rho、theta、threshold、minLineLength 和 maxLineGap 等參數。

透過仔細調整這些參數,我們希望提高直線檢測的精度。threshold 是一個投票計數,因此比該閾值更短的邊緣根本無法收集到足夠的票數被報告出來:在同一幅經過實測驗證的 400x300 測試影像上,上面的參數只返回了 2 條線段(最長的兩條邊),而把閾值降為 50、minLineLength 降為 40 時返回了 6 條線段,包括較小矩形的邊。

需要注意的是,這種方法繪製的是檢測到的線段本身,並不會把它們組合成矩形。四條在角點相交的線段是矩形存在的有力證據,而按角度和端點將它們分組,是完整的檢測器在此範例之上還要補做的一步。

輸出:

opencv detect rectangle output 2

結論

我們深入探討了兩種主要方法:使用 findContours() 和 contourArea() 進行基於輪廓的矩形檢測,以及應用霍夫變換識別幾何形狀。在本指南中,我們強調了微調參數並根據具體影像特徵調整技術的重要性。

我們的實用範例和詳細講解旨在幫助你全面理解這些方法,展示 OpenCV 的多功能性和穩健性。