简体   繁体   English

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

[英]How to get the only min area rectangle on a multiple contours image with cv2.minAreaRect(cnt)?

I want to use only one rectangle to cover the circle in this image:我只想使用一个矩形来覆盖此图像中的圆圈:

我的形象

And get this result with cv2.minAreaRect(cnt) :并使用cv2.minAreaRect(cnt)得到这个结果:

加工后

This image seems to be divided into multiple parts.此图像似乎分为多个部分。 Maybe it's because there are some breakpoint on the edge of this image.也许是因为这张图片的边缘有一些断点。 can you tell me how to use only one rectangle to cover this circle of my image?你能告诉我如何只使用一个矩形来覆盖我图像的这一圈吗? Thank you very much!非常感谢你!

This is my code:这是我的代码:

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

You probably searched for contours with the cv2.findContours() and iterated through them to draw the rectangle on the image.您可能使用cv2.findContours()搜索轮廓并遍历它们以在图像上绘制矩形。 The problem is that your image doesn't have the circle made out of one connected line but many broken lines.问题是你的图像没有由一条连接线组成的圆圈,而是许多虚线。

Contours are curves joining all the continuous points (along the boundary), having same color or intensity (OpenCV documentation).轮廓是连接所有连续点(沿着边界)的曲线,具有相同的颜色或强度(OpenCV 文档)。

So to get a better result you should first prepare your image before you search for contours.因此,为了获得更好的结果,您应该在搜索轮廓之前先准备图像。 You can use various tools for preprocessing the image (you can search the OpenCV documentation).您可以使用各种工具对图像进行预处理(您可以搜索 OpenCV 文档)。 In this case I would try to perform the procedure called "closing" with a small kernel.在这种情况下,我会尝试用一个小内核执行称为“关闭”的过程。 Closing is dilation followed by erosion of pixels.关闭是先膨胀后像素腐蚀。 It can help connect your small contours to a one big contour (circle).它可以帮助将您的小轮廓连接到一个大轮廓(圆)。 Then you can select the biggest one and draw a bounding rectangle.然后你可以选择最大的一个并绘制一个边界矩形。

Example:例子:

Input image:输入图像:

在此处输入图像描述

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()

Result:结果:

在此处输入图像描述

Image after performing the closing operation:执行关闭操作后的图像:

在此处输入图像描述

Hope it helps.希望能帮助到你。 Cheers!干杯!

It is possible to join all the contours into a single contour, as they are just numpy arrays of point coordinates describing the contour.可以将所有轮廓连接成一个轮廓,因为它们只是描述轮廓的点坐标的 numpy 数组。 You can use np.concatenate(contours) and it seems the cv2.minAreaRect function doesn't care that the points in the new array are not continuous.您可以使用np.concatenate(contours)并且cv2.minAreaRect函数似乎并不关心新数组中的点是否连续。 In my case this worked better than using the closing function, as I have more complex objects.在我的例子中,这比使用关闭函数效果更好,因为我有更复杂的对象。 If you want you can give it a try, it's simple.如果您愿意,可以尝试一下,这很简单。 This is how your function should look like:这是您的函数的样子:

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

What you need to do is that you need to somehow obtain only 1 contour from the image by merging the contours, that is a bit difficult to do, so if you only want an enclosing rect around all contours, you can do something as such你需要做的是,你需要通过合并轮廓以某种方式从图像中只获得 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)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM