图像拼接与图像融合
图像拼接在实际的应用场景很广,比如无人机航拍,遥感图像等等,图像拼接是进一步做图像理解基础步骤,拼接效果的好坏直接影响接下来的工作,所以一个好的图像拼接算法非常重要。
再举一个身边的例子吧,你用你的手机对某一场景拍照,但是你没有办法一次将所有你要拍的景物全部拍下来,所以你对该场景从左往右依次拍了好几张图,来把你要拍的所有景物记录下来。那么我们能不能把这些图像拼接成一个大图呢?我们利用opencv就可以做到图像拼接的效果!
那么要实现图像拼接需要那几步呢?简单来说有以下几步:
1.对每幅图进行特征点提取
2.对对特征点进行匹配
3.进行图像配准
4.把图像拷贝到另一幅图像的特定位置
5.对重叠边界进行特殊处理
from pylab import *
from numpy import *
from PIL import Image
from PCV.geometry import homography, warp
from PCV.localdescriptors import sift
“”"
这是3.3节中的全景示例。
“”"
设置数据文件夹的路径
featname = [‘B:/第二章/pic3/’ + str(i + 1) + ‘.sift’ for i in range(5)]
imname = [‘B:/第二章/pic3/’ + str(i + 1) + ‘.jpg’ for i in range(5)]
提取特征和匹配
l = {}
d = {}
for i in range(5):
sift.process_image(imname[i], featname[i]) # 处理图像生成sift并保存在文件中
l[i], d[i] = sift.read_features_from_file(featname[i]) # 读取特征符并以矩阵形式返回
matches = {}
for i in range(4):
matches[i] = sift.match(d[i + 1], d[i]) # 匹配特征符
可视化匹配
for i in range(4):
im1 = array(Image.open(imname[i]))
im2 = array(Image.open(imname[i + 1]))
figure()
sift.plot_matches(im2, im1, l[i + 1], l[i], matches[i], show_below=True)
#将匹配转化成齐次坐标点的函数
def convert_points(j):
ndx = matches[j].nonzero()[0]
fp = homography.make_homog(l[j + 1][ndx, :2].T)
ndx2 = [int(matches[j][i]) for i in ndx]
tp = homography.make_homog(l[j][ndx2, :2].T) # 将点集转化为齐次坐标表示
# 转化x和y - TODO这应该移动到其他地方
fp = vstack([fp[1], fp[0], fp[2]])
tp = vstack([tp[1], tp[0], tp[2]])
return fp, tp
#估计单应性矩阵
model = homography.RansacModel()
fp, tp = convert_points(1)
H_12 = homography.H_from_ransac(fp, tp, model)[0] # m1到m2的单应性矩阵
fp, tp = convert_points(0)
H_01 = homography.H_from_ransac(fp, tp, model)[0] # m0到m1的单应性矩阵
tp, fp = convert_points(2) # 注意:点是反序的
H_32 = homography.H_from_ransac(fp, tp, model)[0] # m3到m2的单应性矩阵
tp, fp = convert_points(3) # 注意:点是反序的
H_43 = homography.H_from_ransac(fp, tp, model)[0] # m4到m2的单应性矩阵
#扭曲图像
delta = 2000 # 用于填充和平移
im1 = array(Image.open(imname[1]), “uint8”)
im2 = array(Image.open(imname[2]), “uint8”)
im_12 = warp.panorama(H_12, im1, im2, delta, delta)
im1 = array(Image.open(imname[0]), “f”)
im_02 = warp.panorama(dot(H_12, H_01), im1, im_12, delta, delta)
im1 = array(Image.open(imname[3]), “f”)
im_32 = warp.panorama(H_32, im1, im_02, delta, delta)
im1 = array(Image.open(imname[4]), “f”)
im_42 = warp.panorama(dot(H_32, H_43), im1, im_32, delta, 2 * delta)
figure()
imshow(array(im_42, “uint8”))
axis(‘off’)
show()
SIFT算法进行图像特征提取:
SIFT算法在这里就不详细说了,上面的链接已经讲的很详细了(使用上面的代码要配置opencv环境,挺简单的,网上很多教程)。我是将上面链接的代码改写成C++,封装了一些方法,使得能够提取中间结果。
SIFT算法的输入是图片,我们需要的输出是各个关键点的位置、128维描述子(用于关键点匹配)。而代码把一个关键点的这些信息都封装在一个结构体Keypoint里面。同时,代码将所有的关键点Keypoint保存为一个链表List形式,即可以根据第一个节点访问到所有的Keypoint节点。
因此我在改写后的MySift.h文件里,添加了几个方法,一个是SIFT的方法入口SiftMainProcess(),一个是获取处理后得到的关键点的头结点方法getFirstKeyDescriptors()。
特征点提取和匹配的方法我在上一篇文章《OpenCV探索之路(二十三):特征检测和特征匹配方法汇总》中做了详细的介绍,在这里直接使用上文所总结的SURF特征提取和特征匹配的方法。
这样我们也能通过RANSAC的方法来确定两张图像的单应性矩阵。因为单应性矩阵的确定需要最少四个点对。通过RANSAC我们可以选择到合理的点对,来计算单应性矩阵。
单应性矩阵是要实现图像拼接重要方法。它表示了两张图象之间的对应特征点的变换关系。有了这个关系,我们就可以实现图像正确的拼接到另一张图像上。
其实这份代码普适性不高_(:зゝ∠),比如图片是需要先人工排序再扔进去跑的,这个问题想了下应该可以根据转移向量V来进行一定的判别。另外上面也提到了,如果图片之间存在物体放缩,那就不能用上面的方法了(放缩的暂时还想不到解决方案……)。还有就是如果图片的横着的,比如数据集2,就也不能解决了。(想想就很难(:зゝ∠)_)