Как сгруппировать контуры и нарисовать один ограничивающий прямоугольник - PullRequest
0 голосов
/ 05 апреля 2019

Мне нужно сгруппировать contours и нарисовать один bounding rectangle, который охватывает все контуры, что-то вроде этого

enter image description here

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

enter image description here

Есть ли прямой способ сделать это, вместо программного объединения всех ограничивающих прямоугольников

Ответы [ 3 ]

2 голосов
/ 05 апреля 2019

Если вы не возражаете против использования 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)

Output

Отказ от ответственности: я новичок в Python в целом, и особенно в Python API OpenCV (C ++ для победы). Комментарии, улучшения, выделение Python no-gos приветствуются!

0 голосов
/ 05 апреля 2019

@ Решение HansHirse было очень элегантным.Вот еще одно решение, использующее ограничивающие прямоугольники.Это возможно непосредственно с помощью класса Rect в OpenCV в C ++ и с помощью библиотеки OpenCV Wrapper .

Оператор or (|) используется для получения минимального заключающего прямоугольника.

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

0 голосов
/ 05 апреля 2019

Вы можете сделать это путем итерации всех контуров и создать ограничивающий прямоугольник из каждого контура, а затем вычислить 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)));
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...