从轮廓中找到旋转矩形

6
我正在尝试使用OpenCV从图像中识别和提取一个相当明显的区域。到目前为止,通过使用阈值和一系列膨胀和腐蚀操作,我成功地找到所需区域的轮廓。
然而,我的尝试使用minAreaRect作为旋转和裁剪的先决条件未能生成包含输入轮廓的矩形。
contours, hierarchy = cv2.findContours(morph.copy() ,cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
contour = contours[0]

draw = cv2.cvtColor(morph, cv2.COLOR_GRAY2BGR)
cv2.drawContours(draw, [contour], 0, (0,255,0), 2)

rotrect = cv2.minAreaRect(contour)
box = cv2.cv.BoxPoints(rotrect)
box = numpy.int0(box)
cv2.drawContours(draw, [box], 0, (0,0,255), 2)

cv2.imshow('image', draw); cv2.waitKey(0)

以下是翻译的结果:

这里是输出的示例:

Output

红色描边是rect,绿色描边是contour。我本来希望红色描边能够包围绿色描边。

不幸的是,我无法提供输入图像。


最终,我自己编写了旋转卡壳算法的实现来寻找最小边界矩形。不过,我仍然想知道如何使用 minAreaRect() 来找到结果。 - thomasfedb
我对Python不是很了解,但在C++中它的表现符合预期。只需进行一些检查,如果您还没有这样做。1)我认为问题不在于minAreaRect。您可以通过在rotrect的左上角绘制一个圆并检查其是否合理来验证这一点。2)还可以尝试使用box = cv2.boxPoints(rect)而不是cv2.cv.BoxPoints(...)。抱歉,这就是我所知道的全部。 - Miki
我可以访问的OpenCV版本中没有cv2.boxPoints(rect)。通过从rotrect绘制中心点,我已经确定minAreaRect()实际上给出了错误的结果。 - thomasfedb
1个回答

1

我最终通过实现自己的旋转卡尺程序来找到最小的矩形。它使用凸包来确定候选的旋转。

def p2abs(point):
    return math.sqrt(point[0] ** 2 + point[1] ** 2)

def rotatePoint(point, angle):
    s, c = math.sin(angle), math.cos(angle)
    return (p[0] * c - p[1] * s, p[0] * s + p[1] * c)

def rotatePoints(points, angle):
    return [rotatePoint(point, angle) for point in points]

points = map(lambda x: tuple(x[0]), contour)
convexHull = map(lambda x: points[x], scipy.spatial.ConvexHull(numpy.array(points)).vertices)

minArea = float("inf")
minRect = None

for i in range(len(hull)):
    a, b = convexHull[i], convexHull[i - 1]
    ang = math.atan2(b[0] - a[0], b[1] - a[1])

    rotatedHull = rotatePoints(convexHull, ang)

    minX = min(map(lambda p: p[0], rotatedHull))
    maxX = max(map(lambda p: p[0], rotatedHull))
    minY = min(map(lambda p: p[1], rotatedHull))
    maxY = max(map(lambda p: p[1], rotatedHull))

    area = (maxX - minX) * (maxY - minY)

    if area < minArea:
        minArea = area

        rotatedRect = [(minX, minY), (minX, maxY), (maxX, maxY), (maxX, minY)]
        minRect = rotatePoints(rotatedRect, -ang)

_, topLeft = min([(p2abs(p), i) for p, i in zip(range(4), minRect)])
rect = minrect[topLeft:] + minrect[:topLeft]

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