如何在opencv中合并轮廓?
How to merge contours in opencv?
好的,伙计们,我从事这个项目已经有一段时间了。
我正在构建这个玩 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)
这是一个老问题,似乎还没有得到正确的回答(向在评论中部分回答的其他 SOers 道歉)。在我看来,提问者的问题分为两部分:
- 有没有合并两个矩形的opencv函数?
这个问题的答案是肯定的和否定的。让我说清楚;是的,如果您使用的是 opencv C++ 绑定。简单 & 可用于联合和 |对于两个矩形的交集。但是 Python 绑定缺少这些功能。
如何在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.
抱歉来晚了一点。但是,如果我 google “合并 opencv 轮廓”,我会发现这个;我认为应该有一个答案。
您可以通过以下方法之一合并任意两个轮廓:
- 获取每个轮廓的点列表
- 附加它们
- 将它们强制转换为 cv2 轮廓格式
- 如果您不太在意细节,请获取 cv2.convexHull。
如果您不喜欢 convexHull 的结果,因为轮廓的凹面部分很重要,那么请改用以下方法:
- 获取每个轮廓的点列表
- 附加它们
- 得到一个共同的中心
- 围绕中心顺时针对所有点进行排序
- 将它们强制转换为 cv2 轮廓格式
如果这两个轮廓中有很多凹形,这可能会产生锯齿形图案,因为配方会通过两个轮廓而不考虑它们的原始结构。如果是这样,您需要遵循第三个秘诀:
- 获取每个轮廓的点列表
- 得到一个共同的中心
- 删除每个轮廓在另一个轮廓内的点
- 在每个轮廓中找到离公共中心最近的点。
- 遍历列出的第一个等高线,直到到达最近点。
- 然后切换到另一个列表,从最近的点开始顺时针穿过另一个轮廓,直到用完
- 切换回第一个轮廓并追加其余的点。
- 将它们强制转换为 cv2 轮廓格式
下一个更复杂的情况是,如果等高线之间有多个交点,并且您想保留两者之间的孔。那么最好做一个黑色的图像,然后用白色绘制轮廓 via cv2.fillPoly()
;然后通过 cv2.findContours()
恢复轮廓
我在这里勾画了前两个食谱的一些步骤
获取每个轮廓点的列表:
import cv2
list_of_pts = []
for ctr in ctrs_to_merge
list_of_pts += [pt[0] for pt in ctr]
顺时针点数
我用的是的函数,按顺时针顺序对点进行排序
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:
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 的程序员将不得不编写样板代码。
合并轮廓的最简单方法是堆叠它们
contours = np.vstack(contours)
在你的情况下(例如堆叠轮廓 5 和 6):
contours = np.vstack([contours[5], contours[6]])
好的,伙计们,我从事这个项目已经有一段时间了。
我正在构建这个玩 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)
这是一个老问题,似乎还没有得到正确的回答(向在评论中部分回答的其他 SOers 道歉)。在我看来,提问者的问题分为两部分:
- 有没有合并两个矩形的opencv函数?
这个问题的答案是肯定的和否定的。让我说清楚;是的,如果您使用的是 opencv C++ 绑定。简单 & 可用于联合和 |对于两个矩形的交集。但是 Python 绑定缺少这些功能。
如何在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.
抱歉来晚了一点。但是,如果我 google “合并 opencv 轮廓”,我会发现这个;我认为应该有一个答案。
您可以通过以下方法之一合并任意两个轮廓:
- 获取每个轮廓的点列表
- 附加它们
- 将它们强制转换为 cv2 轮廓格式
- 如果您不太在意细节,请获取 cv2.convexHull。
如果您不喜欢 convexHull 的结果,因为轮廓的凹面部分很重要,那么请改用以下方法:
- 获取每个轮廓的点列表
- 附加它们
- 得到一个共同的中心
- 围绕中心顺时针对所有点进行排序
- 将它们强制转换为 cv2 轮廓格式
如果这两个轮廓中有很多凹形,这可能会产生锯齿形图案,因为配方会通过两个轮廓而不考虑它们的原始结构。如果是这样,您需要遵循第三个秘诀:
- 获取每个轮廓的点列表
- 得到一个共同的中心
- 删除每个轮廓在另一个轮廓内的点
- 在每个轮廓中找到离公共中心最近的点。
- 遍历列出的第一个等高线,直到到达最近点。
- 然后切换到另一个列表,从最近的点开始顺时针穿过另一个轮廓,直到用完
- 切换回第一个轮廓并追加其余的点。
- 将它们强制转换为 cv2 轮廓格式
下一个更复杂的情况是,如果等高线之间有多个交点,并且您想保留两者之间的孔。那么最好做一个黑色的图像,然后用白色绘制轮廓 via cv2.fillPoly()
;然后通过 cv2.findContours()
我在这里勾画了前两个食谱的一些步骤
获取每个轮廓点的列表:
import cv2
list_of_pts = []
for ctr in ctrs_to_merge
list_of_pts += [pt[0] for pt in ctr]
顺时针点数
我用的是
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:
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 的程序员将不得不编写样板代码。
合并轮廓的最简单方法是堆叠它们
contours = np.vstack(contours)
在你的情况下(例如堆叠轮廓 5 和 6):
contours = np.vstack([contours[5], contours[6]])