如何对轮廓进行分组并绘制单个边界矩形
How to group contours and draw a single bounding rectangle
我需要分组 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')
有什么直接的方法可以做到这一点,而不是以编程方式合并所有边界矩形
您可以通过迭代所有轮廓并从每个轮廓创建边界框然后计算 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)));
如果您不介意使用numpy
,您可以简单地使用concatenate
function from there, see the following code. Attention: I use OpenCV 4.0.0 where the order of the return values of 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++ 为赢)。非常欢迎提出意见、改进和强调 Python 不可行的问题!
@HansHirse 解决方案非常优雅。这是另一种解决方案,使用包围矩形。这可以直接使用 C++ 中 OpenCV 中的 Rect class 和 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 的作者
我需要分组 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')
有什么直接的方法可以做到这一点,而不是以编程方式合并所有边界矩形
您可以通过迭代所有轮廓并从每个轮廓创建边界框然后计算 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)));
如果您不介意使用numpy
,您可以简单地使用concatenate
function from there, see the following code. Attention: I use OpenCV 4.0.0 where the order of the return values of 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++ 为赢)。非常欢迎提出意见、改进和强调 Python 不可行的问题!
@HansHirse 解决方案非常优雅。这是另一种解决方案,使用包围矩形。这可以直接使用 C++ 中 OpenCV 中的 Rect class 和 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 的作者