【问题标题】:How to group contours and draw a single bounding rectangle如何对轮廓进行分组并绘制单个边界矩形
【发布时间】:2019-08-27 02:03:42
【问题描述】:

我需要将contours 分组并绘制一个包含所有轮廓的bounding rectangle,就像这样

from matplotlib import pyplot as plt
import cv2 as cv

img = cv.imread('shapes1.png', 0)
imgRGB = cv.cvtColor(img.copy(), cv.COLOR_GRAY2RGB)

_, ctrs, _ = cv.findContours(img, cv.RETR_EXTERNAL, cv.CHAIN_APPROX_SIMPLE)

boxes = []
for ctr in ctrs:
    x, y, w, h = cv.boundingRect(ctr)
    boxes.append([x, y, w, h])

for box in boxes:
    top_left     = (box[0], box[1])
    bottom_right = (box[0] + box[2], box[1] + box[3])
    cv.rectangle(imgRGB, top_left, bottom_right, (0,255,0), 2)

fig = plt.figure(figsize = (10, 10))
ax  = fig.add_subplot(111)
ax.imshow(imgRGB, cmap='gray')

是否有任何直接的方法可以做到这一点,而不是以编程方式合并所有边界矩形

【问题讨论】:

    标签: python python-3.x opencv opencv-contour


    【解决方案1】:

    您可以通过迭代所有轮廓并从每个轮廓创建一个边界框,然后计算 minX、minY、maxX、maxY 来做到这一点。

    int minX=MAX_INTEGER, minY=MAX_INTEGER, maxX=0, maxY=0;
    
    for each contour in contours:
        Rect rect = boundRecFromContour(contour)
        if rect.x < minX:
            minx = rect.x
        if rect.y < minY:
            minY = rect.y
        if rect.x+rect.width > maxX:
            maxX = rect.x+rect.width
        if rect.y+rect.height > maxY:
            maxY = rect.y+rect.height
    Rect groupRect = (minX, minY, minX+maxX, minY+maxY)
    

    现在您可以绘制groupRect

    此外,您也可以使用该技术来解决问题:

    cv::Mat idx;
    cv::findNonZero(binaryImg, idx);
    // find min max points
    double maxX = 0, minX = MAX_INTEGER;
    double maxY = 0, minY = MAX_INTEGER;
    for (int i=0; i<idx.rows; ++i) {
        cv::Point pnt = idx.at<cv::Point>(i);
        if (pnt.x > maxX) {
            maxX = pnt.x;
        }
        if (pnt.x < minX) {
            minX = pnt.x;
        }
        if (pnt.y > maxY) {
            maxY = pnt.y;
        }
        if (pnt.y < minY) {
            minY = pnt.y;
        }
    }
    Rect groupRect = cv::Rect(cv::Point(int(minX), int(minY)), cv::Point(int(maxX), int(maxY)));
    

    【讨论】:

    • 我想,这正是提问者想要避免的:有什么直接的方法可以做到这一点,而不是以编程方式合并所有边界矩形。。跨度>
    【解决方案2】:

    如果您不介意使用numpy,您可以简单地从那里使用concatenate 函数,请参阅以下代码。注意:我使用的是OpenCV 4.0.0,其中findContours的返回值顺序不同。

    import cv2
    import numpy as np
    
    # Input image
    input = cv2.imread('images/kchZb.png', cv2.IMREAD_GRAYSCALE)
    
    # Modify input image to extract "original" image
    _, input = cv2.threshold(input[10:400, 40:580], 224, 255, cv2.THRESH_BINARY)
    
    # Find contours
    cnts, _ = cv2.findContours(input, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
    
    # Concatenate all contours
    cnts = np.concatenate(cnts)
    
    # Determine and draw bounding rectangle
    x, y, w, h = cv2.boundingRect(cnts)
    cv2.rectangle(input, (x, y), (x + w - 1, y + h - 1), 255, 2)
    
    # Output image
    cv2.imwrite('images/output.png', input)
    cv2.imshow('Input', input)
    cv2.waitKey(0)
    

    免责声明:总的来说,我是 Python 的新手,尤其是 OpenCV 的 Python API(C++ for the win)。非常欢迎评论、改进、强调 Python 的禁忌!

    【讨论】:

      【解决方案3】:

      @HansHirse 解决方案非常优雅。这是另一种解决方案,使用包围矩形。这可以直接使用 C++ 中 OpenCV 中的 Rect 类和OpenCV Wrapper library

      或 (|) 运算符用于给出最小封闭矩形。

      import cv2 as cv
      import opencv_wrapper as cvw
      
      img = cv.imread("shapes1.png", 0)
      imgRGB = cvw.gray2bgr(img)
      
      contours = cvw.find_external_contours(img)
      
      enclosing_rect = contours[0].bounding_rect | contours[1].bounding_rect
      enclosing_rect = enclosing_rect | contours[2].bounding_rect
      
      cvw.rectangle(imgRGB, enclosing_rect, cvw.Color.GREEN)
      
      cv.imshow("Image", imgRGB)
      cvw.wait_key(0)
      

      披露:我是 OpenCV Wrapper 的作者

      【讨论】:

        猜你喜欢
        • 2021-09-06
        • 1970-01-01
        • 1970-01-01
        • 2012-09-05
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-12-03
        • 2016-11-20
        相关资源
        最近更新 更多