模板匹配场景:
实现多目标匹配:
为了实现多目标匹配,我选用opencv和sklearn两个库中的模板匹配和聚类算法来实现。
问题描述:
在进行多目标匹配是,同一匹配区域内,会出现多个冗余的框:
import cv2
import matplotlib.pyplot as plt
import numpy as np
from sklearn import cluster
# 读取图片和模板
img_rgb = cv2.imread('d:/opencvData/lena2.png')
img_gray = cv2.cvtColor(img_rgb,cv2.COLOR_BGR2GRAY)#转换成灰度图片
template = cv2.imread('d:/opencvData/face.png',0)
h,w = template.shape[:2]
# 进行模板匹配,方法采用的归一化相关系数
res = cv2.matchTemplate(img_gray,template,cv2.TM_CCOEFF_NORMED)
# 设置置信度阈值
threshold = 0.9
# 取匹配程度大于90%的坐标
loc = np.argwhere(res>=threshold)
# 显示匹配框,并用圆圈标记左上角点
for pt in loc[,::-1]: # 图片和数组的x对应列,y对应行,所以需要前后交换坐标
bottom_right = (pt[0]+w,pt[1]+h)
cv2.rectangle(img_rgb,pt,bottom_right,(0,0,255),2)
cv2.circle(img_rgb,pt,5,(255,255,255),2)
#显示图像
cv2.imshow('img_rgb',img_rgb)
cv2.waitKey(0)
原因分析:
因为筛选的方法是通过设置阈值,而满足设置的阈值会有多个备选框。而单目标匹配的时候,是选择最大值,它只产生一个备选框。
解决方案:
借助单目标检测的思路,也只选置信度最大值的那个框。
不同点在于如果全图范围只选一个最大置信度,那么会只保留一个匹配框,这与我们的多目标匹配的目标不一致。
为了解决这个问题,我们引进来了聚类算法,先进行聚类运算完成分区,然后在每个区中,选择置信度最大的。
选择聚类算法时,我们不知道究竟会有多少个可以匹配的对象,需要聚类算法自己计算有多少个,为了达到这个目的,我使用了近邻传播算法。
代码如下:
def filterRec(res,loc,draw=False):
#对同一对象的多个框按位置聚类后,按置信度选最大的一个进行保留。
#res是cv2.matchTemplate返回值
#loc是cv2.np.argwhere(res>threshold)返回值
#draw 是否进行画图显示
#返回保留的点的列表pts
#进行聚类分析-近邻传播算法
model=cluster.AffinityPropagation(damping=0.5,max_iter=500,convergence_iter=30,
preference=-50).fit(loc)
y_pred=model.labels_
if draw:
# 画图显示样本数据分类情况
plt.title('AffinityPropagation',fontsize=16)
plt.scatter(loc[:,0],loc[:,1],s=20,c=y_pred,cmap='brg',label='Samples')
plt.legend()
plt.show()
pts = []
# 使用循环函数提取每个区域
for i in set(y_pred):
argj = loc[y_pred==i]
argi = argj.T
# 下面需要注意数组切片操作时,索引需要时tuple格式,
# 如果是numpy.array格式会报错。
# 选择置信度最大的点的坐标,注意这时的格式是[行,列]
# 输出图片中的坐标时需要转换成[x,y],用的方法是pt[::-1]
pt = argj[np.argmax(res[tuple(argi)])]
# 每一区域保留一个选框的左上角坐标
pts.append(pt[::-1])
return pts
使用这个方法之后的效果
完整代码
import cv2
import matplotlib.pyplot as plt
import numpy as np
from sklearn import cluster
# 读取图片和模板
img_rgb = cv2.imread('d:/opencvData/lena2.png')
img_gray = cv2.cvtColor(img_rgb,cv2.COLOR_BGR2GRAY)#转换成灰度图片
template = cv2.imread('d:/opencvData/face.png',0)
h,w = template.shape[:2]
# 进行模板匹配,方法采用的归一化相关系数
res = cv2.matchTemplate(img_gray,template,cv2.TM_CCOEFF_NORMED)
# 设置置信度阈值
threshold = 0.9
# 取匹配程度大于90%的坐标
loc = np.argwhere(res>=threshold)
def filterRec(res,loc,draw=False):
#对同一对象的多个框按位置聚类后,按置信度选最大的一个进行保留。
#res是cv2.matchTemplate返回值
#loc是cv2.np.argwhere(res>threshold)返回值
#draw 是否进行画图显示
#返回保留的点的列表pts
#进行聚类分析-近邻传播算法
model=cluster.AffinityPropagation(damping=0.5,max_iter=500,convergence_iter=30,
preference=-50).fit(loc)
y_pred=model.labels_
if draw:
# 画图显示样本数据分类情况
plt.title('AffinityPropagation',fontsize=16)
plt.scatter(loc[:,0],loc[:,1],s=20,c=y_pred,cmap='brg',label='Samples')
plt.legend()
plt.show()
pts = []
# 使用循环函数提取每个区域
for i in set(y_pred):
argj = loc[y_pred==i]
argi = argj.T
# 下面需要注意数组切片操作时,索引需要时tuple格式,
# 如果是numpy.array格式会报错。
# 选择置信度最大的点的坐标,注意这时的格式是[行,列]
# 输出图片中的坐标时需要转换成[x,y],用的方法是pt[::-1]
pt = argj[np.argmax(res[tuple(argi)])]
# 每一区域保留一个选框的左上角坐标
pts.append(pt[::-1])
return pts
# 对匹配框进行过滤
pts = filterRec(res,loc)
# 显示匹配框,并用圆圈标记左上角点
for pt in pts: # 图片和数组的x对应列,y对应行,所以需要前后交换坐标
bottom_right = (pt[0]+w,pt[1]+h)
cv2.rectangle(img_rgb,pt,bottom_right,(0,0,255),2)
cv2.circle(img_rgb,pt,5,(255,255,255),2)
#显示图像
cv2.imshow('img_rgb',img_rgb)
cv2.waitKey(0)
原始图片