抱歉晚到了一点。不过如果我在谷歌上搜索“合并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]
顺时针排序点
我使用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://dev59.com/11gR5IYBdhLWcg3wBZW3
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 = [point[0]-self.origin[0], point[1]-self.origin[1]]
lenvector = np.linalg.norm(vector[0] - vector[1])
if lenvector == 0:
return -pi, 0
normalized = [vector[0]/lenvector, vector[1]/lenvector]
dotprod = normalized[0]*refvec[0] + normalized[1]*refvec[1]
diffprod = refvec[1]*normalized[0] - refvec[0]*normalized[1]
angle = atan2(diffprod, dotprod)
if angle < 0:
return 2*pi+angle, lenvector
return angle, lenvector
center_pt = np.array(list_of_pts).mean(axis = 0)
clock_ang_dist = clockwise_angle_and_distance(origin)
list_of_pts = sorted(list_of_pts, key=clock_ang_dist)
将一个点列表强制转换为cv2格式。
import numpy as np
ctr = np.array(list_of_pts).reshape((-1,1,2)).astype(np.int32)
使用
cv2.convexHull
来合并它们。
如果你使用这个方法,就不需要按顺时针顺序排列点了。然而,由于它无法保留轮廓的凹角,所以可能会丢失一些轮廓的特性。
# get a list of points
# force the list of points into cv2 format and then
ctr = cv2.convexHull(ctr) # done.
我认为合并两个轮廓的功能应该是opencv库的内容。这个方法非常简单明了,可惜很多使用opencv的程序员都不得不自己编写这段代码。