OpenCV - 如何找到带有圆角的矩形的轮廓?

19

我正在尝试在图像中找到一个带有圆角的矩形对象的轮廓。我尝试了HoughLinesPfindContours,但没有达到期望的结果。

results

我想要找到这样的矩形: desired result

代码:

import cv2
import matplotlib.pyplot as plt
import util

image = cv2.imread("./img/findrect0.png", 1)
gray = util.grayImage(image)

edges = cv2.Canny(image, 50, 200)
lines = cv2.HoughLinesP(edges, 1, cv2.cv.CV_PI/180, 50, minLineLength=50, maxLineGap=10)[0]
linesImage = image.copy()
util.drawLines(linesImage, lines, thickness=10)

contoursImage = image.copy()
(contours, hierarchy) = cv2.findContours(gray.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

util.drawContours(contoursImage, contours, thickness=10)

util.showOpenCVImagesGrid([image, edges, linesImage, contoursImage], 2, 2, titles=["original image", "canny image", "lines image", "contours image"])

工具:

import cv2
import math
import matplotlib.pyplot as plt

def showOpenCVImagesGrid(images, x, y, titles=None, axis="on"):
    fig = plt.figure()
    i = 1
    for image in images:
        copy = image.copy()
        channel = len(copy.shape)
        cmap = None
        if channel == 2:
            cmap = "gray"
        elif channel == 3:
            copy = cv2.cvtColor(copy, cv2.COLOR_BGR2RGB)
        elif channel == 4:
            copy = cv2.cvtColor(copy, cv2.COLOR_BGRA2RGBA)

        fig.add_subplot(x, y, i)
        if titles is not None:
            plt.title(titles[i-1])
        plt.axis(axis)
        plt.imshow(copy, cmap=cmap)
        i += 1
    plt.show()


def grayImage(image):
    gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
    return gray


def drawLines(image, lines, thickness=1):
    for line in lines:
        # print("line="+str(line))
        cv2.line(image, (line[0], line[1]), (line[2], line[3]),
                (0, 0, 255), thickness)


def drawContours(image, contours, thickness=1):
    i = 0
    for contour in contours:
        cv2.drawContours(image, [contours[i]], i, (0, 255, 0), thickness)
        area = cv2.contourArea(contour)
        i += 1

我正在使用Python 2.7.13OpenCV 2.4.13.3

我一直在考虑如何扩展这些代码以获取线的交点。最终,我将获得矩形的四个坐标。 但如果图像更加复杂,我不知道该怎么处理。


轮廓没有起作用的原因是因为轮廓寻找白色像素区域,而您的矩形是黑色像素区域。反转图像将产生正确的结果,就像Zindarod的答案一样。 - alkasm
3个回答

26

你需要找到找到的轮廓的边界矩形。

img = cv2.imread("image.png", -1)

gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

binary = cv2.bitwise_not(gray)

(_,contours,_) = cv2.findContours(binary, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)

for contour in contours:
    (x,y,w,h) = cv2.boundingRect(contour)
    cv2.rectangle(img, (x,y), (x+w,y+h), (0,255,0), 2)

result


好的解决方案。对于简单的图像,您可以查找非零而不是轮廓。 - Kamil Szelag
@KamilSzelag 谢谢。对于OpenCV的初学者,我尽量提供通用解决方案而不是具体的解决方案。但在这种情况下,您的解决方案当然是最优的。 - zindarod
这就是为什么我点赞你的答案。我发表了我的意见,因为拥有多种解决方案很好。 - Kamil Szelag

3
你可以找到非零点的边界矩形。
image = cv2.imread("./img/findrect0.png", 1)
gray = util.grayImage(image)
gray_inv = cv2.bitwise_not(gray)
points = cv2.findNonZero(gray)
rect = cv2.boundingRect(points)

1
没问题,我在修复后删除了评论。使用 util.grayImage() 而不是直接使用 cv2.imread('file.png', 0) 读取灰度图像的原因是什么?另外,这个库是哪个?编辑:我没有意识到这就是 OP 在使用的。继续进行。 - alkasm

2
我一直在思考如何延伸这些线并获取交点,最终,我将获得矩形的四个坐标。如果轮廓不适用于您的情况,或多或少,这是一个不错的方法。确实,正如您所说,
但是,如果图像更加复杂,我就不知道该怎么处理了。
有一些问题需要解决。主要问题是典型的线检测并不总是能够完美地给出线段。您可能会在同一条线上有多个线段,这些线段可以沿着长度方向堆叠或重叠。此外,您需要以某种方式自动分割线,以便您不会试图找到平行线的交点。
然而,这些问题都不难解决。我曾经在这个网站上回答过一个关于从HoughLinesP中找到交点的问题,使用了下面的很多建议,虽然不太健壮(例如将线段分成两组),但它应该为您提供了一个良好的起点。
在检测到线之后,您需要将线分成组或平行线段。如果您的矩形具有定义的方向,则可以根据该方向过滤线,这将是简单的情况。但是,如果矩形可以处于任何方向,则需要其他方法对其进行分段。您可以使用k-means聚类以k = 2找到两个主要角度,并将与一个角度对应的线放入一个箱中,将与另一个角度对应的线放入另一个箱中,并找到一个箱中的线与另一个箱中的线的交点。这种方法的好处是它适用于任何平行四边形。如果您想坚持矩形,则可以拒绝每个箱中不在与另一个箱的平均角度相差某个阈值(如10度或其他)的直角线。
一旦您将所有线分组,就可以计算它们的交点。实际上,有一个很好的公式使用行列式来计算两条线之间的交点,给定线上的两个点,您已经从端点中获得了这些点。所以,这很方便!箱0中的每条线都将与箱1中的线有一个交点,这就是你需要做的。
因此,在这里你将有4个交点簇。一种选择是使用k=4k-means简单地将它们分组在一起,然后你将得到这四个点簇的质心,代表你的矩形的角落。当然,由于你一路上使用了许多近似步骤,你的点不会完全定义一个矩形,所以你需要将最接近的可能矩形适配到这些点上。或者,除了使用k-means之外,另一种方法是尝试找到你的许多交点中最准确地表示矩形的子集,然后再适配最接近的矩形。可能有一些方法可以使用线性回归、最小二乘、RANSAC等来解决这个问题。或者,如果你想要的话,你可以使用boundingRect()来找到这四个点的边界矩形。

谢谢,我会尝试的。实际上,我的目标是处理比我问题中的图像更复杂的图像。现在我必须先处理简单的图像,一步一步来。 - tomfriwel

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接