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 的多功能性和鲁棒性。