识别 Python 中的相似项目以减少/澄清 CV 结果的数量
Identifying similar items in Python to reduce / clarify number of CV results
在 运行 对图像使用两种不同的 CV 算法(以查找细节的多次出现)之后,它们都提供如下列表的结果列表。
第一个算法交付(左、上、宽、高的元组):
[(816, 409, 35, 39), (817, 409, 35, 39), (818, 409, 35, 39), (815, 410, 35, 39), (816, 410, 35, 39), (817, 410, 35, 39), (818, 410, 35, 39), (819, 410, 35, 39), (816, 411, 35, 39), (817, 411, 35, 39), (818, 411, 35, 39), (816, 447, 35, 39), (817, 447, 35, 39), (818, 447, 35, 39), (815, 448, 35, 39), (816, 448, 35, 39), (817, 448, 35, 39), (818, 448, 35, 39), (816, 449, 35, 39), (817, 449, 35, 39), (818, 449, 35, 39), (856, 639, 35, 39), (857, 639, 35, 39), (858, 639, 35, 39), (855, 640, 35, 39), (856, 640, 35, 39), (857, 640, 35, 39), (858, 640, 35, 39), (859, 640, 35, 39), (856, 641, 35, 39), (857, 641, 35, 39), (858, 641, 35, 39)]
第二个(CV2)算法的输出为(左上角坐标):
[(816, 409), (817, 409), (818, 409), (815, 410), (816, 410), (817, 410), (818, 410), (819, 410), (816, 411), (817, 411), (818, 411), (816, 447), (817, 447), (818, 447), (815, 448), (816, 448), (817, 448), (818, 448), (819, 448), (816, 449), (817, 449), (818, 449), (856, 639), (857, 639), (858, 639), (855, 640), (856, 640), (857, 640), (858, 640), (859, 640), (856, 641), (857, 641), (858, 641)]
但是在屏幕上 只出现了三次 搜索项。仔细观察,您会发现 - 例如 - 前两个条目非常相似(左侧位置为 816 而不是 817)。
CV2 代码如下所示:
# detect image in image
img_rgb = open_cv_image # original image
template = cv2.imread('C:/temp/detail.png') # searching this!
w, h = template.shape[:-1]
res = cv2.matchTemplate(img_rgb, template, cv2.TM_CCOEFF_NORMED)
threshold = .8
loc = np.where(res >= threshold)
a = []
for pt in zip(*loc[::-1]):
cv2.rectangle(img_rgb, pt, (pt[0] + w, pt[1] + h), (0, 0, 255), 2)
a.append(pt)
#cv2.imwrite('result.png', img_rgb)
print(a)
因此,这两种方法都不会提供准确的结果,而是类似结果的分散列表。我要解决的问题是:
1.如何找出真正找到了多少项(对结果进行分组)?
2。如何将列表减少到每组中的一个项目(哪一个并不重要,因为它们都是相似的)?
有没有一种简单的方法可以将 Python 中的相似元组/列表分组,并将其简化为基本项目?或者 Python 是否有任何简单的 CV 机制可以给出精确匹配?感谢任何帮助...
提前致谢!乌尔里希
你可以得到一个列表,其中每个元组的 euclidean_distance
大于 1(或其他),而前一个元组具有简单的 list comprehension
。但是,您需要在开头插入一个零值 tuple
。如果 s
是您的输入列表,那么
s.insert(0, (0,0,0,0))
t = [s[x] for x in range(1,len(s)) if euclidean_distance(s[x],s[x-1]) > 1]
>>> t
[(816, 409, 35, 39), (815, 410, 35, 39), (816, 411, 35, 39), (816, 447, 35, 39), (815, 448, 35, 39), (816, 449, 35, 39), (856, 639, 35, 39), (855, 640, 35, 39), (856, 641, 35, 39)]
Non local Maxima Supression(参见: and especially for computer vision: https://www.pyimagesearch.com/2015/02/16/faster-non-maximum-suppression-python/)似乎是解决此问题的既定方法。缺点:占用资源多,速度慢。
我采用了 重叠矩形 的想法,假设是:
- 重叠的矩形表示相同的结果
- 接触矩形或仅轻微重叠的矩形不代表相同的结果(需要阈值)
- 数据仅包含真实表示(不包含错误/数据中的第一项是结果之一)
有了这个,您只需要一些几何公式就可以得到一组正确的结果。这是代码的初稿(有点乱,但是可以用):
data = [(816, 409, 35, 39), (817, 409, 35, 39), (818, 409, 35, 39),
(815, 410, 35, 39), (816, 410, 35, 39), (817, 410, 35, 39),
(818, 410, 35, 39), (819, 410, 35, 39), (816, 411, 35, 39),
(817, 411, 35, 39), (818, 411, 35, 39), (816, 447, 35, 39),
(817, 447, 35, 39), (818, 447, 35, 39), (815, 448, 35, 39),
(816, 448, 35, 39), (817, 448, 35, 39), (818, 448, 35, 39),
(816, 449, 35, 39), (817, 449, 35, 39), (818, 449, 35, 39),
(856, 639, 35, 39), (857, 639, 35, 39), (858, 639, 35, 39),
(855, 640, 35, 39), (856, 640, 35, 39), (857, 640, 35, 39),
(858, 640, 35, 39), (859, 640, 35, 39), (856, 641, 35, 39),
(857, 641, 35, 39), (858, 641, 35, 39) ]
def Points(i):
# converts (X, Y, W, H) to (X1, Y1, X2, Y2)
return (i[0],i[1],i[0]+i[2],i[1]+i[3])
def XYWH(i):
# converts (X1, Y1, X2, Y2) to (X, Y, W, H)
return (i[0],i[1],i[2]-i[0],i[3]-i[1])
def Overlap(R1, R2):
# check if two rectangles overlap
R1 = Points(R1)
R2 = Points(R2)
if (R1[0]>=R2[2]) or (R1[2]<=R2[0]) or \
(R1[3]<=R2[1]) or (R1[1]>=R2[3]):
return False
return True
def Intersection(a,b):
# return the intersection area
a = Points(a)
b = Points(b)
x1 = max(min(a[0], a[2]), min(b[0], b[2]))
y1 = max(min(a[1], a[3]), min(b[1], b[3]))
x2 = min(max(a[0], a[2]), max(b[0], b[2]))
y2 = min(max(a[1], a[3]), max(b[1], b[3]))
if x1 < x2 and y1 < y2:
return XYWH((x1, y1, x2, y2))
def Area(i):
# calculate the size of a rectangle
return i[2]*i[3]
def Covers(a, b):
# calculates the share (0 to 1) of coverage
if not Overlap(a, b): return None
inters = Area(Intersection(a, b))
original = Area(a)
return inters/original
def Uniques(Data, Threshold = 0.8):
ret = [Data[0]]
for i in range(len(Data)-1):
c = Covers(data[i],data[i+1])
if not c or c < Threshold:
ret.append(data[i+1])
return ret
print(Uniques(data))
您需要对阈值进行一些试验才能获得好的结果。在 Uniques 函数内的 for 循环中包含一个 print(c)
以查看它的传播。上面代码的结果显示了三个正确的发现:
[(816, 409, 35, 39), (816, 447, 35, 39), (856, 639, 35, 39)]
唯一的缺点是,这是每个结果的一种可能解决方案,而不是最佳解决方案,但对于大多数用例来说,这应该足够了。
在 运行 对图像使用两种不同的 CV 算法(以查找细节的多次出现)之后,它们都提供如下列表的结果列表。
第一个算法交付(左、上、宽、高的元组):
[(816, 409, 35, 39), (817, 409, 35, 39), (818, 409, 35, 39), (815, 410, 35, 39), (816, 410, 35, 39), (817, 410, 35, 39), (818, 410, 35, 39), (819, 410, 35, 39), (816, 411, 35, 39), (817, 411, 35, 39), (818, 411, 35, 39), (816, 447, 35, 39), (817, 447, 35, 39), (818, 447, 35, 39), (815, 448, 35, 39), (816, 448, 35, 39), (817, 448, 35, 39), (818, 448, 35, 39), (816, 449, 35, 39), (817, 449, 35, 39), (818, 449, 35, 39), (856, 639, 35, 39), (857, 639, 35, 39), (858, 639, 35, 39), (855, 640, 35, 39), (856, 640, 35, 39), (857, 640, 35, 39), (858, 640, 35, 39), (859, 640, 35, 39), (856, 641, 35, 39), (857, 641, 35, 39), (858, 641, 35, 39)]
第二个(CV2)算法的输出为(左上角坐标):
[(816, 409), (817, 409), (818, 409), (815, 410), (816, 410), (817, 410), (818, 410), (819, 410), (816, 411), (817, 411), (818, 411), (816, 447), (817, 447), (818, 447), (815, 448), (816, 448), (817, 448), (818, 448), (819, 448), (816, 449), (817, 449), (818, 449), (856, 639), (857, 639), (858, 639), (855, 640), (856, 640), (857, 640), (858, 640), (859, 640), (856, 641), (857, 641), (858, 641)]
但是在屏幕上 只出现了三次 搜索项。仔细观察,您会发现 - 例如 - 前两个条目非常相似(左侧位置为 816 而不是 817)。
CV2 代码如下所示:
# detect image in image
img_rgb = open_cv_image # original image
template = cv2.imread('C:/temp/detail.png') # searching this!
w, h = template.shape[:-1]
res = cv2.matchTemplate(img_rgb, template, cv2.TM_CCOEFF_NORMED)
threshold = .8
loc = np.where(res >= threshold)
a = []
for pt in zip(*loc[::-1]):
cv2.rectangle(img_rgb, pt, (pt[0] + w, pt[1] + h), (0, 0, 255), 2)
a.append(pt)
#cv2.imwrite('result.png', img_rgb)
print(a)
因此,这两种方法都不会提供准确的结果,而是类似结果的分散列表。我要解决的问题是:
1.如何找出真正找到了多少项(对结果进行分组)?
2。如何将列表减少到每组中的一个项目(哪一个并不重要,因为它们都是相似的)?
有没有一种简单的方法可以将 Python 中的相似元组/列表分组,并将其简化为基本项目?或者 Python 是否有任何简单的 CV 机制可以给出精确匹配?感谢任何帮助...
提前致谢!乌尔里希
你可以得到一个列表,其中每个元组的 euclidean_distance
大于 1(或其他),而前一个元组具有简单的 list comprehension
。但是,您需要在开头插入一个零值 tuple
。如果 s
是您的输入列表,那么
s.insert(0, (0,0,0,0))
t = [s[x] for x in range(1,len(s)) if euclidean_distance(s[x],s[x-1]) > 1]
>>> t
[(816, 409, 35, 39), (815, 410, 35, 39), (816, 411, 35, 39), (816, 447, 35, 39), (815, 448, 35, 39), (816, 449, 35, 39), (856, 639, 35, 39), (855, 640, 35, 39), (856, 641, 35, 39)]
Non local Maxima Supression(参见:
我采用了 重叠矩形 的想法,假设是:
- 重叠的矩形表示相同的结果
- 接触矩形或仅轻微重叠的矩形不代表相同的结果(需要阈值)
- 数据仅包含真实表示(不包含错误/数据中的第一项是结果之一)
有了这个,您只需要一些几何公式就可以得到一组正确的结果。这是代码的初稿(有点乱,但是可以用):
data = [(816, 409, 35, 39), (817, 409, 35, 39), (818, 409, 35, 39),
(815, 410, 35, 39), (816, 410, 35, 39), (817, 410, 35, 39),
(818, 410, 35, 39), (819, 410, 35, 39), (816, 411, 35, 39),
(817, 411, 35, 39), (818, 411, 35, 39), (816, 447, 35, 39),
(817, 447, 35, 39), (818, 447, 35, 39), (815, 448, 35, 39),
(816, 448, 35, 39), (817, 448, 35, 39), (818, 448, 35, 39),
(816, 449, 35, 39), (817, 449, 35, 39), (818, 449, 35, 39),
(856, 639, 35, 39), (857, 639, 35, 39), (858, 639, 35, 39),
(855, 640, 35, 39), (856, 640, 35, 39), (857, 640, 35, 39),
(858, 640, 35, 39), (859, 640, 35, 39), (856, 641, 35, 39),
(857, 641, 35, 39), (858, 641, 35, 39) ]
def Points(i):
# converts (X, Y, W, H) to (X1, Y1, X2, Y2)
return (i[0],i[1],i[0]+i[2],i[1]+i[3])
def XYWH(i):
# converts (X1, Y1, X2, Y2) to (X, Y, W, H)
return (i[0],i[1],i[2]-i[0],i[3]-i[1])
def Overlap(R1, R2):
# check if two rectangles overlap
R1 = Points(R1)
R2 = Points(R2)
if (R1[0]>=R2[2]) or (R1[2]<=R2[0]) or \
(R1[3]<=R2[1]) or (R1[1]>=R2[3]):
return False
return True
def Intersection(a,b):
# return the intersection area
a = Points(a)
b = Points(b)
x1 = max(min(a[0], a[2]), min(b[0], b[2]))
y1 = max(min(a[1], a[3]), min(b[1], b[3]))
x2 = min(max(a[0], a[2]), max(b[0], b[2]))
y2 = min(max(a[1], a[3]), max(b[1], b[3]))
if x1 < x2 and y1 < y2:
return XYWH((x1, y1, x2, y2))
def Area(i):
# calculate the size of a rectangle
return i[2]*i[3]
def Covers(a, b):
# calculates the share (0 to 1) of coverage
if not Overlap(a, b): return None
inters = Area(Intersection(a, b))
original = Area(a)
return inters/original
def Uniques(Data, Threshold = 0.8):
ret = [Data[0]]
for i in range(len(Data)-1):
c = Covers(data[i],data[i+1])
if not c or c < Threshold:
ret.append(data[i+1])
return ret
print(Uniques(data))
您需要对阈值进行一些试验才能获得好的结果。在 Uniques 函数内的 for 循环中包含一个 print(c)
以查看它的传播。上面代码的结果显示了三个正确的发现:
[(816, 409, 35, 39), (816, 447, 35, 39), (856, 639, 35, 39)]
唯一的缺点是,这是每个结果的一种可能解决方案,而不是最佳解决方案,但对于大多数用例来说,这应该足够了。