英文:
How can I use cv2.minAreaRect to obtain the largest contour, even if the image has broken regions?
问题
这是原始图像。
我想使用cv2.minAreaRect来获取最大轮廓,如下图所示。
尝试1 - 失败
cnt, hierarchy = cv2.findContours(im_bw, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
min_rect = cv2.minAreaRect(cnt[0])
box = np.int0(cv2.boxPoints(min_rect))
cv2.drawContours(temp_result, [box], 0, (255, 0, 0), 2)
尝试2 - 失败
我参考了这篇帖子来获取用于绘制的有序坐标。但是,我得到了以下结果,其中线条不匹配,四个点无法与cv2.minAreaRect一起使用。
def order_points(pts):
# 初始化一个将被排序的坐标列表,使得列表中的第一个条目是左上角,
# 第二个条目是右上角,第三个是右下角,第四个是左下角
rect = np.zeros((4, 2), dtype="float32")
# 左上角的点将具有最小的和,而
# 右下角的点将具有最大的和
s = np.sum(pts, axis=1)
rect[0] = pts[np.argmin(s)] # 左上角
rect[2] = pts[np.argmax(s)] # 右下角
# 现在,计算点之间的差异,右上角的点将具有最小的差异,
# 而左下角将具有最大的差异
diff = np.diff(pts, axis=1)
rect[1] = pts[np.argmin(diff)] # 右上角
rect[3] = pts[np.argmax(diff)] # 左下角
# 返回有序的坐标
return rect
# pts = [(93, 50), (109, 82), (76, 47), (93, 77), (58, 38), (76, 72), (36, 32), (54, 67), (20, 27), (35, 62), (3, 22), (18, 56), (111, 54), (128, 87)]
t = order_points(pts)
cv2.line(temp_result, pt1=(int(t[0][0]), int(t[0][1])), pt2=(int(t[1][0]), int(t[1][1])), color=(0, 0, 255), thickness=2)
cv2.line(temp_result, pt1=(int(t[3][0]), int(t[3][1])), pt2=(int(t[2][0]), int(t[2][1])), color=(0, 0, 255), thickness=2)
任何帮助将不胜感激。
英文:
I want to use cv2.minAreaRect to obtain the maximum contour, as shown in the following image.
Attempt 1 - Fail
cnt, hierarchy = cv2.findContours(im_bw, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
min_rect = cv2.minAreaRect(cnt[0])
box = np.int0(cv2.boxPoints(min_rect))
cv2.drawContours(temp_result, [box], 0, (255, 0, 0), 2)
Attempt 2 - Fail
I referred to this post to obtain the ordered coordinates for drawing. However, I obtained the following result, where the lines don't match and the four points cannot be used with cv2.minAreaRect.
def order_points(pts):
# initialzie a list of coordinates that will be ordered
# such that the first entry in the list is the top-left,
# the second entry is the top-right, the third is the
# bottom-right, and the fourth is the bottom-left
rect = np.zeros((4, 2), dtype = "float32")
# the top-left point will have the smallest sum, whereas
# the bottom-right point will have the largest sum
s = np.sum(pts, axis = 1)
rect[0] = pts[np.argmin(s)] # top-left
rect[2] = pts[np.argmax(s)] # bottom-right
# now, compute the difference between the points, the
# top-right point will have the smallest difference,
# whereas the bottom-left will have the largest difference
diff = np.diff(pts, axis = 1)
rect[1] = pts[np.argmin(diff)] # top-right
rect[3] = pts[np.argmax(diff)] # bottom-left
# return the ordered coordinates
return rect
#########################################################################
# pts = [(93, 50), (109, 82), (76, 47), (93, 77), (58, 38), (76, 72), (36, 32), (54, 67), (20, 27), (35, 62), (3, 22), (18, 56), (111, 54), (128, 87)]
t = order_points(pts)
cv2.line(temp_result, pt1=(int(t[0][0]), int(t[0][1])), pt2=(int(t[1][0]), int(t[1][1])), color=(0, 0, 255), thickness=2)
cv2.line(temp_result, pt1=(int(t[3][0]), int(t[3][1])), pt2=(int(t[2][0]), int(t[2][1])), color=(0, 0, 255), thickness=2)
Any help will be appreciated.
答案1
得分: 5
我们可以找到合并轮廓的凸包:
<!-- language: lang-python -->
merged_cnt = cv2.convexHull(np.vstack(cnt))
代码示例:
<!-- language: lang-python -->
import cv2
import numpy as np
im_bw = cv2.imread('im_bw.png', cv2.IMREAD_GRAYSCALE) # 将im_bw以灰度读取
temp_result = cv2.cvtColor(im_bw, cv2.COLOR_GRAY2BGR)
cnt, hierarchy = cv2.findContours(im_bw, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
# 将所有轮廓合并为一个大轮廓 - 结果是所有轮廓的凸包。
merged_cnt = cv2.convexHull(np.vstack(cnt))
cv2.drawContours(temp_result, [merged_cnt], 0, (255, 0, 0), 2)
# 用于测试的显示结果
cv2.imshow('temp_result', temp_result)
cv2.waitKey()
cv2.destroyAllWindows()
如果目标是将轮廓近似为矩形多边形,我们可以使用以下答案中的simplify_contour
方法
<!-- language: lang-python -->
approx = simplify_contour(merged_cnt)
cv2.drawContours(temp_result, [approx], 0, (0, 0, 255), 2)
如果目标是找到如标题所述的minAreaRect
:
<!-- language: lang-python -->
min_rect = cv2.minAreaRect(merged_cnt) # 找到合并轮廓的minAreaRect
box = np.int0(cv2.boxPoints(min_rect))
英文:
We may find the Convex hull of the merged contours:
<!-- language: lang-python -->
merged_cnt = cv2.convexHull(np.vstack(cnt))
Code sample:
<!-- language: lang-python -->
import cv2
import numpy as np
im_bw = cv2.imread('im_bw.png', cv2.IMREAD_GRAYSCALE) # Read im_bw as grayscale
temp_result = cv2.cvtColor(im_bw, cv2.COLOR_GRAY2BGR)
cnt, hierarchy = cv2.findContours(im_bw, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
# Merge all the contours into one large contour - the result is the convex hull of all contour.
merged_cnt = cv2.convexHull(np.vstack(cnt))
cv2.drawContours(temp_result, [merged_cnt], 0, (255, 0, 0), 2)
# Show result for testing
cv2.imshow('temp_result', temp_result)
cv2.waitKey()
cv2.destroyAllWindows()
In case the objective is to approximate the contour to rectangular polygon, we may use simplify_contour
method from the following answer
<!-- language: lang-python -->
approx = simplify_contour(merged_cnt)
cv2.drawContours(temp_result, [approx], 0, (0, 0, 255), 2)
In case the objective is to find minAreaRect
as stated in the title:
<!-- language: lang-python -->
min_rect = cv2.minAreaRect(merged_cnt) # Fine minAreaRect of the merged contours
box = np.int0(cv2.boxPoints(min_rect))
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论