图像特征的匹配
通过对图像提取特征后,得到特征点和描述特征点信息的特征向量,在对图像的检索和匹配当中主要通过对描述符[特征向量]的计算来实现,下面主要通过ORB来进行图像特征的提取,使用不同的算法来实现图像的匹配.
1.暴力匹配(Brute-Force)
2.K-临近匹配
3.FLANN匹配(Fast Library for Approximate Nearest Neighbors)www.cs.ubc.ca/research/flann
ORB在对特征的提取过程中,返回的特征描述符是一个带方向的BRIEF特征.有点是在旋转不变性条件下学习一种不相关的BRIEF特征,这样在查询图像被旋转后不影响匹配的效果.
Brute-Force
最简单的匹配方法,比较两幅图像的描述符[描述特征点信息的特征向量],计算两个向量之间的距离,距离越近就表示两幅图像越匹配,OpenCV中提供的有用于暴力破解的对象BFMatcher实现暴力匹配.
import cv2
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline
person_1= cv2.imread('pic1.jpg')
grayScale_1 = cv2.cvtColor(person_1, cv2.IMREAD_GRAYSCALE)
person_2 = cv2.imread('pic2.jpg')
grayScale_2 = cv2.cvtColor(person_2, cv2.IMREAD_GRAYSCALE)
原图person_1,person_2
plt.figure(figsize=(11,7))
plt.imshow(person_1),plt.imshow(person_2)
特征提取
orb = cv2.ORB_create()
kp1, des1 = orb.detectAndCompute(grayScale_1, None)
kp2, des2 = orb.detectAndCompute(grayScale_2, None)
可视化特征点
keypoint_1 = cv2.drawKeypoints(image=person_1, outImage=person_1,keypoints=kp1,
flags=4, color=(0,255,0))
keypoint_2 = cv2.drawKeypoints(image=person_2, outImage=person_2,keypoints=kp2,
flags=4, color=(0,255,0))
图1
plt.figure(figsize=(11,7))
plt.imshow(keypoint_1)
图2
plt.imshow(keypoint_2)
特征匹配
匹配原理很简单,遍历两幅图像的特征描述符[包含特征点信息的向量],然后计算匹配的质量[向量之间的距离],根据距离对特征点进行排序,在一定的置信度之下显示前N个特征的匹配结果.
bf = cv2.BFMatcher(cv2.NORM_HAMMING, crossCheck=True)
matches = bf.match(des1,des2)
matches = sorted(matches, key = lambda x:x.distance)
特征匹配结果的可视化
matchImg = cv2.drawMatches(person_1, kp1, person_2, kp2, matches[:30], person_2,
flags=2)
plt.figure(figsize=(15,8))
plt.imshow(matchImg)
上面图片只显示了,特征匹配距离最近的前30个特征,没有匹配到嘴巴和鼻子上的信息,匹配特征主要集中在眼睛鼻子眉毛和耳朵,主要是因为对图1进行特征提取的时候,由于图像尺寸原因忽略了嘴巴和鼻子的信息.上面是对同一张图的局部信息进行匹配.
下面使用不同的两张图片[同一个人]来进行目标的匹配
import cv2
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline
p1 = cv2.imread('pic2.jpg')
p2 = cv2.imread('pic5.jpg')
grayp1 = cv2.cvtColor(p1, cv2.IMREAD_GRAYSCALE)
grayp2 = cv2.cvtColor(p2, cv2.IMREAD_GRAYSCALE)
orb = cv2.ORB_create()
keyp1 ,desp1= orb.detectAndCompute(grayp1, None)
keyp2 ,desp2= orb.detectAndCompute(grayp2, None)
bf = cv2.BFMatcher(cv2.NORM_HAMMING, crossCheck=True)
matches = bf.match(desp1, desp2)
matches = sorted(matches, key = lambda x:x.distance)
matchpic = cv2.drawMatches(p1, keyp1, p2, keyp2, matches[:25],
p2,flags=2)
Pic1andPic2
plt.imshow(p1)
plt.imshow(p2)
Pic1的特征点
p1_kp_img = cv2.drawKeypoints(image=p1, outImage=p1, keypoints=keyp1,flags=4,
color=(0,255,0))
plt.imshow(p1_kp_img)
Pic2的特征点
p2_kp_img = cv2.drawKeypoints(image=p2, outImage=p2, keypoints=keyp2,flags=4,
color=(0,255,0))
plt.imshow(p2_kp_img)
Pic1 and Pic2
特征匹配情况
下面分别使用不同的特征匹配算法来测试匹配
1.Brute-Force
2.KNN Macth
3.FLANN匹配
暴力匹配的结果
plt.figure(figsize=(15,8))
plt.imshow(matchpic)
KNN匹配
对于实时应用来说,如果计算资源受限制的话,应该选择计算成本较低的算法.KNN算法精度高,对异常值不敏感,但是计算复杂度较高,空间复杂度高,适用数据范围数值型和标称型.
KNN的工作原理:存在一个有标记的样本数据集合,输入新的没有标记的新的数据,然后计算新的样本的特征向量与已有的样本集合中所有的样本特征向量之间的[距离],然后根据计算结果进行排序,取出前k个与新样本最近的,这k个样本中出现最多的标记就是新的样本的标记.
仍使用上面ORB算法提取的特征,KNN匹配
bf = cv2.BFMatcher(cv2.NORM_HAMMING, crossCheck=True)
# KnnMatch()不直接返回匹配结果,而是返回最接近的前K个结果
# 前K个结果中出现次数最多的结果为最终的匹配结果或者距离最近
KnnMatches = bf.knnMatch(desp1, desp2, k=1)
KnnMatchImg = cv2.drawMatchesKnn(p1, keyp1, p2, keyp2, KnnMatches[:50],
p2, flags=2)
KNN匹配结果
plt.figure(figsize=(15,8))
plt.imshow(KnnMatchImg)
FLANN匹配
FLANN匹配有一种内部机制,他会根据数据的不同选择不同的算法来处理数据集
FLANN匹配算法中定义两个参数indexParams和searchParams.在Python中以字典的形式传递参数,(C++中以结构体的形式传递参数,FLANN在计算匹配时会自行决定索引参数和搜索对象.PLANN中可供选择的索引有LinearIndex,KTreeIndex,KMeansIndex,CompositeIndex,和AutotuneIndex.searchParams字典只包含一个字段checks.用来指定索引树要被遍历的次数,值越大,计算匹配时间越长,也越精准.不过匹配结果也受到提取特征质量的影响.
换一组新的匹配对象
import cv2
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline
image1 = cv2.imread('hwd2.jpg',0) #0:IMAGE_GRAYSCALE
image2 = cv2.imread('hwd3.jpg',0)
# create SIFT的实例
sift = cv2.xfeatures2d.SIFT_create()
keypoints_1 ,desvec_1= sift.detectAndCompute(image1, None)
keypoints_2 ,desvec_2= sift.detectAndCompute(image2, None)
# Flann
FLANN_INDEX_KDTREE = 0
# KTreeIndex设置简单,只用指定处理核密度树的数量(1-16)最好
# 若设置其他索引参数,参照官方文档
indexParams = dict(algorithm = FLANN_INDEX_KDTREE, trees= 5)
# searchParams指定遍历索引树的次数
searchParams = dict(checks=50)
flann = cv2.FlannBasedMatcher(indexParams, searchParams)
# falnn.KNN匹配
matches = flann.knnMatch(desvec_1, desvec_2, k=2)
'''
# 使用一个空列表对象来存储最佳的匹配结果index
# 如果前面KNN匹配K=3,matchesMask的列表元素为[0,0,0]
matchesMask = [[0,0] for i in xrange(len(matches))]
for i,(m,n) in enumerate(matches):
# 在返回的最近的特征点中,m如果比n的0.7还小,m留下
# 否则全丢弃,0.7并非固定,根据匹配情况调整
# 这样还可以过滤掉很多错误的匹配
if m.distance < 0.7*n,distance:
matchesMask[i] = [1,0]
'''
确定最佳匹配过滤错误匹配
# alpha比例系数
# 在返回的最近的特征点中,m如果比n的alpha倍还小,m留下
# 否则全丢弃,0.7并非固定,根据匹配情况调整
# 这样还可以过滤掉很多错误的匹配
def createMatchesMask(matches, alpha):
# 使用一个空列表对象来存储最佳的匹配结果index
# 如果前面KNN匹配K=3,matchesMask的列表元素为[0,0,0]
matchesMask = [[0,0] for i in xrange(len(matches))]
for i,(m,n) in enumerate(matches):
if m.distance < alpha*n.distance:
matchesMask[i]=[1,0]
return matchesMask
matchesMask = createMatchesMask(matches, 0.75)
# matchColor:匹配点的颜色
# singlePointColor:较差的匹配点
drawParams = dict(matchColor = (0,255,0),singlePointColor = (255,0,0),
matchesMask = matchesMask, flags=0)
# 匹配结果
resultImg = cv2.drawMatchesKnn(image2, keypoints_2, image1, keypoints_1,
matches,None, **drawParams)
Image1
plt.figure(figsize=(15,8))
plt.imshow(image1)
Image2
plt.figure(figsize=(15,8))
plt.imshow(image2)
Image1特征点
kpImage_1 = cv2.drawKeypoints(image=image1, outImage=image2, keypoints=keypoints_1,
flags=4,color=(0,255,0))
plt.figure(figsize=(15,8))
plt.imshow(kpImage_1)
Image2特征点域
kpImage_2 = cv2.drawKeypoints(image=image2, outImage=image2, keypoints=keypoints_2,
flags=4,color=(255,0,0))
plt.figure(figsize=(15,8))
plt.imshow(kpImage_2)
匹配结果
plt.figure(figsize=(20,10))
plt.imshow(resultImg)
有时间在补充.....