要求
输入两幅相同机位、不同视角拍摄的图片,输出两者拼接结果。
考察重点为拼接后的场景连续性、拼接后的光照一致性、关键点映射可视化。
本实验的整体思路为先寻找两幅图像中的关键点,然后将关键点进行匹配,根据匹配结果对图像进行转换,然后将转换后的图像进行拼接。转换后的图像进行拼接时需要处理光照,使图像的拼接痕迹不过于明显。
PS:这个富文本编辑器太难用我受不了了剩下的部分写在下一篇了。
流程
先将images文件夹放入当前目录下,运行main.py。
输入:待拼接图像的序号n,合法输入为:1,2,3,4。
输出:关键点匹配结果(./middle_res/beforen.jpg)与图像拼接结果(./result/outputn.jpg)。
步骤1. 使用SIFT算法寻找图像的特征点;
步骤2. 使用knnMatch对特征点进行匹配;
步骤3. 对left与right图像的光照进行处理;
步骤4. 根据特征点匹配结果求解变换矩阵H,并将right图像进行变换;
步骤5. 对结果图像的融合边界进行处理;
步骤6. 将两侧图像分别与掩码相乘,并融合两侧图像,得到最终结果。
算法介绍
寻找关键点
使用了SIFT算法寻找关键点。SIFT算法,即Scale Invariant Feature Transform,尺度不变特征转换。David Lowe在1998年的论文Object Recognition from Local Scale-Invariant Features中首次提出了SIFT算法,并在2004年的论文Distinctive Image Features from Scale-Invariant Keypoints中提出了对SIFT算法的改进,此后,该算法受到的关注越来越高。
SIFT算法找到的特征点具有尺度不变性,旋转不变性,光照不变性。首先构建高斯金字塔,然后检测尺度空间的极值点,再对极值点进行精确定位,选取特征点方向,最后生成关键点描述子。该算法通过连续变化尺度参数获得多尺度下的尺度空间表示序列,并对这些序列进行尺度空间主轮廓的提取,使用高斯函数实现平滑,尺度参数为标准差。通过尺度空间中各尺度图像的模糊程度逐渐变大来模拟人在距离目标由近到远时目标在视网膜上的形成过程。该算法的具体实现细节见代码设计。
SIFT算法的缺点为实时性不高,对边缘光滑的目标难以准确提取特征点。本次实验我自己实现了SIFT算法,但该算法计算出一副输入图像(约9M左右)的特征点需要若干小时,速度非常慢。直接使用opencv中的SIFT算法可以在一分钟内算出所有特征点。
匹配关键点
使用了knnMatch算法进行关键点匹配。该算法遍历描述符,将每个对应的描述符的特征进行比较,每次比较都会给出一个距离值并进行排序,最好的结果被认为是一个匹配。本次实验使用的距离测量为
distance= 2ixi2
光照处理
一共尝试了两种光照处理方法。
第一种方法为将两幅图片从RGB转为HSV,然后分别计算两幅图像V分量的和,并求出V分量的比例k,对右侧图像的V进行补偿,从而使两幅图的V分量接近,达到光照处理的效果。
第二种方法为直方图匹配。即将左侧图像作为参考图像,并求出其累计直方图。将右侧图像作为待变换图像,计算出其累计直方图。然后找出两个直方图之间的映射关系,将右侧的直方图匹配到左侧的直方图,使两幅图像的整体色调看起来一致,从而实现光照补偿。
这两种方法的效果见实验结果部分。
图像融合
第一种方法,直接将右侧图像拼接到左侧图像上,使用掩码使右侧图像只保留需要拼接的部分,去掉所有融合部分。
第二种方法,使用一个固定的偏移值offset并进行feathering处理,使用加权平均颜色值融合重叠的像素。将中心像素处的mask设为1,与边界像素之间线性递减,边界处的值为0。使用这种方法,当offset设置较大时,可能会出现重影。当offset设置过小时,过渡区域过小,接缝处仍然比较明显。
第三种方法,根据图像重合区域大小确定offset并进行feathering处理。计算出图像重合区域大小,并取其五分之一作为offset值,进行feathering处理。
图像融合部分需要考虑两方面的问题,对于左侧和右侧图像整体光照看起来差别不大时,offset应尽可能小,这样可以保留更多左侧图片的信息,并且可以使图像不出现鬼影。当左侧和右侧图像整体光照差别较大,应当在合理范围内使offset尽可能大,增加过渡区域可以使图像看起来更加自然。
因此,最终使用的方法为计算两幅图像的V分量的和,当两幅图像V分量和差别不大时,采用第二种方法进行融合,并将offset设置为40;当两幅图像V分量和差别较大时,使用第三种方法进行融合,offset为图像重合部分大小的1/5。
代码设计
整体设计
首先读入图像,然后创建SIFT对象并获取关键点和描述子,使用detectAndCompute函数获取关键点和特征描述符。然后进行关键点匹配,创建一个暴力匹配器,返回DMatch对象列表,每个DMatch对象表示关键点的一个匹配结果,尝试所有匹配从而找到最佳匹配。其中,distance属性表示距离,距离越小匹配值越高。
PS:富文本编辑器太难用,剩下的写在下一篇
print("Reading images ...")
left = cv2.imread(img1_path)
right = cv2.imread(img2_path)
print("Computing keypoints and descriptors ...")
sift = mysift.MySift()
left_kp, left_feature = sift.detectAndCompute(left, None)
right_kp, right_feature = sift.detectAndCompute(right, None)
print("Matching keypoints ...")
matcher = cv2.BFMatcher()
feature_match = matcher.knnMatch(left_feature, right_feature, k=2)