【问题标题】:How to merge contours in opencv?如何在opencv中合并轮廓?
【发布时间】:2017-06-12 14:19:06
【问题描述】:

好的,伙计们,我从事这个项目已经有一段时间了。

我正在构建这个玩 chrome 恐龙游戏的机器人。所以我尝试了其他方法来检测 matchTemplate 之类的字符,甚至制作了自己的算法来定位对象,但我最喜欢这个(findcontours)。

这是我所拥有的:

谁能帮我看看我应该如何合并仙人掌的两个矩形?

img = screen_cap()
roi = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
ret, thresh = cv2.threshold(roi,127, 255, 0)
im2, contours, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
first = True
for cnt in contours:
    area = cv2.contourArea(cnt)
    if area > 200: #filtering contours
        x,y,w,h = cv2.boundingRect(cnt)
        if w/h < 4: # filtering even more
            cv2.rectangle(img,(x,y),(x+w,y+h),(255,0,0),2)

【问题讨论】:

  • 是什么阻止您检查边界框的像素位置是否靠近其他边界框的位置,如果是,则扩展最左侧像素的宽度和高度?
  • @AlexanderReynolds 他说合并轮廓,所以如果矩形偏移,或者有多个轮廓,我不确定他们是否只是希望边界框或轮廓之间的实际线消失(这样它们将是任意轴对齐的多边形)
  • @snb 实际上它很模糊,在标题中它要求合并轮廓,但在它要求合并边界矩形的问题中。可以使用聚类来找到附近的轮廓。
  • @AlexanderReynolds 很抱歉这个问题含糊不清。我想要做的是让计算机认为仙人掌作为一个单一的实体并且在一个边界框中,必须是一个矩形。我够清楚了吗?我也在尝试自己实现它。只是在这里询问是否有原生的opencv方法。
  • @snb 查找...

标签: python opencv artificial-intelligence


【解决方案1】:

很抱歉来晚了一点。但是,如果我用谷歌搜索“合并 opencv 轮廓”,我会发现这个;我认为应该有一个答案。

您可以通过其中一种方法合并任意两个轮廓:

  • 获取每个轮廓的点列表
  • 附加它们
  • 强制它们变成 cv2 轮廓格式
  • 如果您不太关心细节,请获取 cv2.convexHull。

如果您不喜欢 convexHull 的结果,因为轮廓的凹面部分很重要,那么请改用以下方法:

  • 获取每个轮廓的点列表
  • 附加它们
  • 获得一个共同的中心
  • 围绕中心按顺时针顺序对所有点进行排序
  • 强制它们变成 cv2 轮廓格式

如果两个轮廓中有很多凹形,这可能会产生锯齿形图案,因为配方会通过两个轮廓而不考虑它们的原始结构。如果是这种情况,您需要遵循第三个秘诀:

  • 获取每个轮廓的点列表
  • 获得一个共同的中心
  • 删除每个轮廓中位于另一个轮廓内的点
  • 在每个轮廓中找到最接近公共中心的点。
  • 遍历列出的第一个轮廓,直到找到最近的点。
  • 然后切换到另一个列表,从最近的点开始顺时针穿过另一个轮廓,直到它用完
  • 切换回第一个轮廓并附加其余的点。
  • 强制它们变成 cv2 轮廓格式

下一个更复杂的情况是,如果轮廓之间有多个交叉点,并且您希望保留两者之间的孔洞。然后最好通过cv2.fillPoly()制作黑色图像并用白色绘制轮廓;然后通过cv2.findContours() 将轮廓恢复出来

我在这里为前两个食谱画了一些步骤

获取每个轮廓的点列表:

import cv2
list_of_pts = [] 
for ctr in ctrs_to_merge
    list_of_pts += [pt[0] for pt in ctr]

顺时针排序点

我使用this really great posting of MSeifert的功能按顺时针顺序排列点

class clockwise_angle_and_distance():
    '''
    A class to tell if point is clockwise from origin or not.
    This helps if one wants to use sorted() on a list of points.

    Parameters
    ----------
    point : ndarray or list, like [x, y]. The point "to where" we g0
    self.origin : ndarray or list, like [x, y]. The center around which we go
    refvec : ndarray or list, like [x, y]. The direction of reference

    use: 
        instantiate with an origin, then call the instance during sort
    reference: 
    https://stackoverflow.com/questions/41855695/sorting-list-of-two-dimensional-coordinates-by-clockwise-angle-using-python

    Returns
    -------
    angle
    
    distance
    

    '''
    def __init__(self, origin):
        self.origin = origin

    def __call__(self, point, refvec = [0, 1]):
        if self.origin is None:
            raise NameError("clockwise sorting needs an origin. Please set origin.")
        # Vector between point and the origin: v = p - o
        vector = [point[0]-self.origin[0], point[1]-self.origin[1]]
        # Length of vector: ||v||
        lenvector = np.linalg.norm(vector[0] - vector[1])
        # If length is zero there is no angle
        if lenvector == 0:
            return -pi, 0
        # Normalize vector: v/||v||
        normalized = [vector[0]/lenvector, vector[1]/lenvector]
        dotprod  = normalized[0]*refvec[0] + normalized[1]*refvec[1] # x1*x2 + y1*y2
        diffprod = refvec[1]*normalized[0] - refvec[0]*normalized[1] # x1*y2 - y1*x2
        angle = atan2(diffprod, dotprod)
        # Negative angles represent counter-clockwise angles so we need to 
        # subtract them from 2*pi (360 degrees)
        if angle < 0:
            return 2*pi+angle, lenvector
        # I return first the angle because that's the primary sorting criterium
        # but if two vectors have the same angle then the shorter distance 
        # should come first.
        return angle, lenvector

center_pt = np.array(list_of_pts).mean(axis = 0) # get origin
clock_ang_dist = clockwise_angle_and_distance(origin) # set origin
list_of_pts = sorted(list_of_pts, key=clock_ang_dist) # use to sort

强制将点列表转换为 cv2 格式

import numpy as np
ctr = np.array(list_of_pts).reshape((-1,1,2)).astype(np.int32)

将它们与cv2.convexHull 合并

如果您使用它,则无需顺时针排列点。但是,convexHull 可能会丢失一些轮廓属性,因为它不会保留轮廓的凹角。

# get a list of points
# force the list of points into cv2 format and then
ctr = cv2.convexHull(ctr) # done.

我认为合并两个轮廓的功能应该是opencv库的内容。方法很简单,很遗憾许多使用 opencv 的程序员不得不编写样板代码。

【讨论】:

  • 谢谢安德拉斯,这很有帮助!
【解决方案2】:

这是一个古老的问题,似乎还没有得到正确的回答(尽管对部分在 cmets 中完成的 SOers 同胞表示歉意)。对我来说,提问者似乎有一个由两部分组成的问题:

  1. 有没有合并两个矩形的opencv函数?

这个问题的答案是肯定的和否定的。让我说清楚;是的,如果您使用的是 opencv C++ 绑定。简单的 & 可以用来取并集和 |用于两个矩形的交集。但是 Python 绑定缺少这些功能。

  1. 如何在 Python 中做到这一点?

    def union(a,b):
        x = min(a[0], b[0])
        y = min(a[1], b[1])
        w = max(a[0]+a[2], b[0]+b[2]) - x
        h = max(a[1]+a[3], b[1]+b[3]) - y
        return (x, y, w, h)
    
    def intersection(a,b):
        x = max(a[0], b[0])
        y = max(a[1], b[1])
        w = min(a[0]+a[2], b[0]+b[2]) - x
        h = min(a[1]+a[3], b[1]+b[3]) - y
        if w<0 or h<0: return () # or (0,0,0,0) ?
        return (x, y, w, h)
        # Please remember a and b are rects.
    

源代码来源:OpenCV union and intersaction on rects

【讨论】:

  • 如果是多个轮廓并且它们彼此非常接近,有没有办法将它们合并成一个轮廓?
  • @abss,您只需要遍历它们即可合并它们。或者,您可以在查找轮廓之前对二进制图像本身使用形态膨胀,具体取决于最适合您的用例。
  • 谢谢你的回答,问题是,我要合并的轮廓,它们非常接近,看起来像是一体的,但是它们之间有一个很小的间隙,并且有一个它们周围有一点噪音,所以使用膨胀会将噪音包含在轮廓中,我已经尽可能多地过滤了图像,但我无法完全去除它们
  • @abss,很难告诉您如何处理您拥有的图像,直到您将它们发布给其他人查看。如果与噪声相比,您的前景轮廓大于一定大小,那么您可以使用 cv2.ContourArea() 函数过滤掉较小的轮廓,重新绘制前景轮廓,然后运行形态膨胀。
  • 是的,我明白,我目前仍在尝试进行一些过滤,如果我没有想法,我会用图像发布我的问题。感谢您的回复
【解决方案3】:

合并轮廓最简单的方法是堆叠它们

contours = np.vstack(contours)

在您的情况下(例如堆叠轮廓 5 和 6):

contours = np.vstack([contours[5], contours[6]])

【讨论】:

    猜你喜欢
    • 2014-05-13
    • 1970-01-01
    • 2021-09-07
    • 2022-01-10
    • 2023-03-18
    • 2016-09-25
    • 2014-07-12
    • 2011-10-03
    • 2021-07-26
    相关资源
    最近更新 更多