如何使用cv2.minAreaRect(cnt)获得多轮廓图像上唯一的最小面积矩形?

时间:2019-04-09 08:00:28

标签: python opencv image-processing contour opencv-contour

我只想使用一个矩形来覆盖此图像中的圆:

my image

并通过cv2.minAreaRect(cnt)获得此结果:

After processing

此图像似乎分为多个部分。可能是因为此图像的边缘有一些断点。您能告诉我如何仅使用一个矩形覆盖图像的这个圆吗?非常感谢你!

这是我的代码:

def draw_min_rect_circle(img, cnts):  # conts = contours
    img = np.copy(img)

    for cnt in cnts:
        x, y, w, h = cv2.boundingRect(cnt)
        cv2.rectangle(img, (x, y), (x + w, y + h), (255, 0, 0), 2)  # blue

        min_rect = cv2.minAreaRect(cnt)  # min_area_rectangle
        min_rect = np.int0(cv2.boxPoints(min_rect))
        cv2.drawContours(img, [min_rect], 0, (0, 255, 0), 2)  # green

        (x, y), radius = cv2.minEnclosingCircle(cnt)
        center, radius = (int(x), int(y)), int(radius)  # center and radius of minimum enclosing circle
        img = cv2.circle(img, center, radius, (0, 0, 255), 2)  # red
return img

3 个答案:

答案 0 :(得分:2)

您可能使用cv2.findContours()搜索轮廓,并对其进行遍历以在图像上绘制矩形。问题在于您的图像没有由一条连接的线组成的圆,而是由许多虚线组成的圆。

轮廓是连接所有连续点(沿边界)的曲线,具有相同的颜色或强度(OpenCV文档)。

因此,要获得更好的效果,应先准备图像,然后再搜索轮廓。您可以使用各种工具对图像进行预处理(可以搜索OpenCV文档)。在这种情况下,我将尝试使用一个小的内核执行称为“关闭”的过程。关闭是扩张,然后是像素侵蚀。它可以帮助将小轮廓连接到一个大轮廓(圆形)。然后,您可以选择最大的一个并绘制一个边界矩形。

示例:

输入图像:

enter image description here

import cv2
import numpy as np

img = cv2.imread('test.png')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
_, thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY+cv2.THRESH_OTSU)
kernel = np.ones((3,3), dtype=np.uint8)
closing = cv2.morphologyEx(thresh, cv2.MORPH_CLOSE, kernel)
_, contours, hierarchy = cv2.findContours(closing, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)
cnt = max(contours, key=cv2.contourArea)
x,y,w,h = cv2.boundingRect(cnt)
cv2.rectangle(img, (x,y), (x+w, y+h), (255,255,0), 1)
cv2.imshow('img', img)
cv2.waitKey(0)
cv2.destroyAllWindows()

结果:

enter image description here

执行关闭操作后的图片:

enter image description here

希望有帮助。干杯!

答案 1 :(得分:2)

可以将所有轮廓连接到单个轮廓中,因为它们只是描述轮廓的点坐标的 numpy 数组。您可以使用 np.concatenate(contours) 并且似乎 cv2.minAreaRect 函数并不关心新数组中的点不连续。就我而言,这比使用关闭函数效果更好,因为我有更复杂的对象。如果您愿意,可以尝试一下,这很简单。你的函数应该是这样的:

def draw_min_rect_circle(img, cnts):  # conts = contours
    img = np.copy(img)

    join_cnts = np.concatenate(cnts)

    x, y, w, h = cv2.boundingRect(join_cnts)
    cv2.rectangle(img, (x, y), (x + w, y + h), (255, 0, 0), 2)  # blue

    min_rect = cv2.minAreaRect(join_cnts)  # min_area_rectangle
    min_rect = np.int0(cv2.boxPoints(min_rect))
    cv2.drawContours(img, [min_rect], 0, (0, 255, 0), 2)  # green

    (x, y), radius = cv2.minEnclosingCircle(join_cnts)
    center, radius = (int(x), int(y)), int(radius)  # center and radius of minimum enclosing circle
    img = cv2.circle(img, center, radius, (0, 0, 255), 2)  # red
    
return img

答案 2 :(得分:0)

您需要做的是,通过合并等高线以某种方式仅从图像中获得1个等高线,这有点困难,因此,如果您只想在所有等高线周围使用封闭矩形,则可以做一些事情这样

def draw_min_rect_circle(img, cnts):  # conts = contours
    img = np.copy(img)
    x1,y1 = np.inf
    x2,y2 = 0
    for cnt in cnts:
        x, y, w, h = cv2.boundingRect(cnt)
        if x > x1:
           x1=x
        if y > y1:
           y1=y
        if x2 < x+w
           x2 = x+w
        if y2 < y+h
           y2 = y+h
     w = x2 - x1
     h = y2 - y1
     r = math.sqrt((w*w) + (h*h)) / 2

     cv2.rectangle(img, (x, y), (x + w, y + h), (255, 0, 0), 2)
     cv2.circle(img, (x1+w/2,y1+h/2), r, (0, 0, 255), 2)