最近在看关于图像融合的内容:
主要的步骤就是:
1.特征检测
2.特征匹配
3.求取单应性矩阵
4.图像投影变换
5.图像合并
本文章采用的是python3.7 + opencv3.4.5
由于特征检测和匹配需要用到sift算法或者surf算法,而由于专利问题,在 opencv3中将该算法放在了opencv_contrib中 所以需要下载opencv_contrib(3.4.5)以及cmake进行重编译
(最新的opencv4.4中 由于专利到期 sift和surf已经回到了opencv中 哈哈 好消息呀 )
具体的配置请参考上一篇文章
https://blog.csdn.net/dg1011/article/details/114833424
本文是基于其他参考进行的改写
https://blog.csdn.net/lla520/article/details/77488332
关于几种匹配算法的比较:
https://blog.csdn.net/zilanpotou182/article/details/66478915
SURF与SIFT的比较
1 方法比较
(1)在生成尺度空间方面,SIFT算法利用的是差分高斯金字塔与不同层级的空间图像相互卷积生成。
SURF算法采用的是不同尺度的box filters与原图像卷积
(2)在特征点检验时,SIFT算子是先对图像进行非极大值抑制,再去除对比度较低的点。然后通过Hessian矩阵去除边缘的点。
而SURF算法是先通过Hessian矩阵来检测候选特征点,然后再对非极大值的点进行抑制
(3)在特征向量的方向确定上,SIFT算法是在正方形区域内统计梯度的幅值的直方图,找到最大梯度幅值所对应的方向。SIFT算子确定的特征点可以有一个或一个以上方向,其中包括一个主方向与多个辅方向。
SURF算法则是在圆形邻域内,检测各个扇形范围内水平、垂直方向上的Haar小波响应,找到模值最大的扇形指向,且该算法的方向只有一个。
(4)SIFT算法生成描述子时,是将的采样点划分为的区域,从而计算每个分区种子点的幅值并确定其方向,共计128维。
SURF算法在生成特征描述子时将的正方形分割成的小方格,每个子区域25个采样点,计算小波haar响应,一共64维。
综上,SURF算法在各个步骤上都简化了一些繁琐的工作,仅仅计算了特征点的一个主方向,生成的特征描述子也与前者相比降低了维数。
2.效果比较:
加速3倍
亮度变化下效果好
模糊方面优于SIFT
尺度不变性不及SIFT
旋转不变上差很多
以上比较部分源自:
https://blog.csdn.net/lucifer_24/article/details/88823448?spm=1001.2014.3001.5501
附上代码
import numpy as np
import cv2
#图像路径
imgname1 = 'rr.jpg' #右图
imgname2 = 'll.jpg' #左图
surf = cv2.xfeatures2d.SURF_create()
# FLANN 参数设计
#这里使用的是KTreeIndex配置索引,指定待处理核密度树的数量(理想的数量在1-16)
#值越高结果越准确,但是消耗的时间也越多。实际上,匹配效果很大程度上取决于输入。
# 5kd-trees和50checks总能取得合理精度,而且短时间完成。
FLANN_INDEX_KDTREE = 0
index_params = dict(algorithm = FLANN_INDEX_KDTREE, trees = 5)
search_params = dict(checks=50)
flann = cv2.FlannBasedMatcher(index_params,search_params)
#图像读取
img1 = cv2.imread(imgname1)
img2 = cv2.imread(imgname2)
cv2.imshow("img1",img1)
cv2.imshow("img2",img2)
cv2.waitKey(0)
# gray1 = cv2.cvtColor(img1, cv2.COLOR_BGR2GRAY) #灰度处理图像
# gray2 = cv2.cvtColor(img2, cv2.COLOR_BGR2GRAY)
# cv2.imshow("gray1", gray1)
# cv2.imshow("gray2", gray2)
# cv2.waitKey(0)
kp1, des1 = surf.detectAndCompute(img1,None)#des是描述子
kp2, des2 = surf.detectAndCompute(img2,None)
img3 = cv2.drawKeypoints(img1,kp1,np.array([]))#绘制特征点
img4 = cv2.drawKeypoints(img2,kp2,np.array([]))
cv2.imshow("kp1", img3)
cv2.imshow("kp2", img4)
cv2.waitKey(0)
matches = flann.knnMatch(des1,des2,k=2)#匹配特征点
goodMatches = []
for m,n in matches:#更佳匹配
# ratio = 0.4:对于准确度要求高的匹配;
# ratio = 0.6:对于匹配点数目要求比较多的匹配;
# ratio = 0.5:一般情况下。
if m.distance < 0.4 * n.distance:
goodMatches.append(m)
img5 = cv2.drawMatches(img3,kp1,img4,kp2,goodMatches,None,flags=2)#绘制匹配结果
cv2.imshow("SURF", img5)
cv2.waitKey(0)
#注意trainIdx和queryIdx的对应关系
image2Points = np.float32([ kp2[m.trainIdx].pt for m in goodMatches ])
image1Points = np.float32([ kp1[m.queryIdx].pt for m in goodMatches ])
#求取单应性矩阵homo
homo, mask = cv2.findHomography(image1Points, image2Points, cv2.RANSAC, 5.0)
print(homo)
#计算右图投影变换后四个顶点
#右侧的两个顶点就是最终图像的最右侧的两个顶点
#四个匹配点对就可以确定唯一的一个投影变换矩阵
h = img1.shape[0]
w = img1.shape[1]
trainBorder = np.float32([[[0, 0], [w, 0], [w, h], [0, h]]])
queryBorder = cv2.perspectiveTransform(trainBorder, homo)
print(queryBorder)
#将右图投影变换成和左图同一坐标系中的对应点
size = (max(queryBorder[0][1][0], queryBorder[0][2][0]), max(img2.shape[0], queryBorder[0][2][1]))
imageTransform = cv2.warpPerspective(img1, homo, size)
cv2.imshow("imageTransform", imageTransform)
cv2.waitKey(0)
#将左图直接复制到转换后的图像上
#注意:python中问对应的copyto函数
dst = np.zeros(imageTransform.shape)
#不可以使用 dst = imageTransform 这样创建的是软连接
# 这样做在接下来的操作会使imageTransform的值改变
dst = imageTransform.copy()
dst[0:img2.shape[0], 0:img2.shape[1]] = img2
cv2.imshow("dst", dst)
cv2.waitKey(0)
#加权融合图像
start = min(queryBorder[0][0][0], queryBorder[0][3][0])
DoubleWidth = img2.shape[1] - start
alpha = 1
for i in range(dst.shape[0]):
theLeft = dst[i]
theRight = imageTransform[i]
for j in range(int(start), img2.shape[1]):
if sum(theRight[j]) == 0:
alpha = 0
else:
alpha = (j - start) / DoubleWidth
for k in range(3):
dst[i][j][k] = theLeft[j][k] * (1 - alpha) + theRight[j][k] * alpha
cv2.imshow("result", dst)
cv2.waitKey(0)
cv2.destroyAllWindows()
由于ORB算法的检测结果为二进制
因此需要在
matches = flann.knnMatch(des1,des2,k=2)#匹配特征点
前加上数据类型转换
des1 = np.float32(des1)
des2 = np.float32(des2)
不过在本文的实例中 ORB算法检测出来的特征点个数偏少 无法使用 哈哈