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()
输出:
首先,我们把输入图像转换为灰度图像,再做阈值处理得到二值图像。这一步对后续的轮廓检测至关重要。上面的阈值处理让形状保持白色、背景为黑色;如果你的矩形是深色、背景是浅色,应改用 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 条线段,包括较小矩形的边。
需要注意的是,这种方法绘制的是检测到的线段本身,并不会把它们组合成矩形。四条在角点相交的线段是矩形存在的有力证据,而按角度和端点将它们分组,是完整的检测器在此示例之上还要补做的一步。
输出:
结论
我们深入探讨了两种主要方法:使用 findContours() 和 contourArea() 进行基于轮廓的矩形检测,以及应用霍夫变换识别几何形状。在本指南中,我们强调了微调参数并根据具体图像特征调整技术的重要性。
我们的实用示例和详细讲解旨在帮助你全面理解这些方法,展示 OpenCV 的多功能性和鲁棒性。