基于单应性矩阵进行图像拼接的技术文档
[toc]
基于单应性矩阵进行图像拼接的技术文档
我使用的图像拼接方法分为一下5个部分
-
特征提取和匹配
-
单应性矩阵的计算和优化
-
用户人为增加和删除特征点
-
拼接缝的寻找
-
拼接缝融合
之后想要完善图像拼接方法可以参照OpenCV的图像拼接框架:
OpenCV总结3——图像拼接Stitching_opencv stitching-CSDN博客
这是我理解OpenCV源码时的参考资料:opencv stitching算法分析_opencv光束法平差-CSDN博客
1. 特征提取和匹配
使用的是OpenCV自带的SIFT、ORB、以及开源的LoFTR。
1.1 遇到的问题
- SIFT直接处理原始分辨率图像会占用很大的内存
- 解决:将图像分patch放入SIFT,需要根据patch左上角的坐标还原特征点的坐标
- LoFTR直接处理原始分辨率图像会占用很大的显存
- 未解决。理论上,LoFTR更好的处理特征稀疏的图像。也可以考虑使用其他的开源模型。
2. 单应性矩阵的计算和优化
使用RANSAC计算单应性矩阵,使用Ceres库进行BA优化
2.1 RANSAC
使用RANSAC需要注意阈值的控制,一般是在(0,10]之间选择,阈值越小,约束的更严格。但是并不是越小越好,阈值过小可能会导致RANSAC以错误匹配点作为基准计算单应性矩阵。
2.1.1 实验
- 多次RANSAC:使用小阈值的RANSAC筛选由大阈值的RANSAC筛选出的内点。结果表明没有明显的改善。可以考虑进一步实验。
2.1.2 遇到的问题
- 单应性矩阵的叠加:对变换后的图像进行平移,不能直接在单应性矩阵的分量上进行加减,而是使用矩阵相乘的方法进行叠加。例如,
2.2 基于Ceres库的BA优化
该优化参考《视觉SLAM十四讲》的第九讲P254进行编写。优化器实现的主函数是在smv_optimer
文件中的CeresSolver::performBADoubleGlobal_pairs
2.2.1 注意事项
-
ceres::AutoDiffCostFunction
的参数: 在smv_optimer.cpp
文件中,有new ceres::AutoDiffCostFunction<SnavelyReprojectionError, 2, 9, 9>
,这其中的三个数值分别指的是残差(residuals
)的维度、输入值x的维度、输入值y的维度。这里的输入值x和y是指输入ceres库用于优化的值,也就是两个单应性矩阵。 -
每对矩阵只能出现一次:矩阵1和矩阵2输入到求解器后,不能再将矩阵2和矩阵1输入到求解器,即每种矩阵组合只能输入一次。我使用单向邻接链表
adjacent_indexes_direct
来构建矩阵对和特征点匹配对point_pairs
。- 参考资料遗失,没找到
-
固定一个矩阵作为基准:需要固定一个矩阵来保证优化后的图像在当前的平面上。固定的方式是使用
SetParameterBlockConstant
-
if (index_src == anchor) { problem.SetParameterBlockConstant(pp_src); }
-
3. 用户人为增加和删除特征点
获得用户添加的特征点 -> 以为中心划分出一个patch(patch_size在400左右) -> 通过单应性矩阵计算在目标图像上的映射 -> 以为中心划分搜索窗口(search_size在20左右,匹配效果差的情况下,可以设的大一些) -> 基于SIFT描述符和余弦相似度搜索匹配度最高的点
3.1 注意事项
-
坐标系的选择:在修改代码时,注意当前特征点所在的坐标系。我使用了三个坐标系,分别是全局坐标系(以整个拼接图像的左上角为原点)、overlap坐标系(以overlap的左上角为原点)、patch坐标系(以patch的左上角为原点)
-
代码冗余:在后续编写接口函数时,因为要输出不同的数据,所以编写了几个不同的接口函数。但是实际上,这几个接口函数的内容都相差不大。之后可以考虑删除冗余代码
-
上采样和亚像素:可以考虑对图像进行上采样之后再搜索,这样可以在亚像素级别搜索。效果不明显,可以进一步测试一下。
4. 拼接缝的寻找
使用基于最小能量值的方法寻找拼接缝,步骤为:
最后寻找最佳缝合线思想,步骤:
-
考虑强度值E的每一列都为一条最佳缝合线,首先从第一行开始,累计添加下一行的强度值;
-
每一列强度值向下一行的左、中、右进行扫描,获取最小的强度累计值,并记录最小强度值的列号;
-
统计出强度值最小的一条做为最佳缝合线。
-
最后根据最佳缝合线进行图像的拼接融合,融合也可以采用改进加权平均融合。
参考资料:
[python最佳缝合线(Image Stitching 2)_optimal seam的代码-CSDN博客](https://blog.csdn.net/YMilton/article/details/103634234#:~:text=最佳缝合线能够有效的去除拼接中运动物体移动出现的鬼影,如何寻找最佳缝合线对于图像拼接去除鬼影比较的重要。 寻找最佳缝合线涉及到比较重要的一个思想是动态规划,寻找强度值最优的路径。 关于强度值的计算,具体情况如下: 以上公式 是重叠像素点的颜色值之差,,是结构值之差。 这里的结构值计算采用Sobel算子,也可以考虑其他的边缘检测算子,其中x%2Cy方向的算子如下: 最后寻找最佳缝合线思想,步骤: 1、考虑强度值E的每一列都为一条最佳缝合线,首先从第一行开始,累计添加下一行的强度值; 2、每一列强度值向下一行的左、中、右进行扫描,获取最小的强度累计值,并记录最小强度值的列号;)
基于最佳缝合线的多图像拼接【附python代码】_缝合线拼接-CSDN博客
当前使用的拼接方法中没有使用这种方法。可以考虑在后续的研发中实现。
当前使用的拼接缝寻找方法是基于overlap的长宽比,以较长的边的方向作为拼接缝的方向。但是基于长宽比的方法在重叠区域过大时会出现拼接缝方向错误的问题。之后使用了基于图像编号的方法来确定拼接缝的方向。
5. 拼接缝融合
使用拉普拉斯融合来进行拼接缝融合
[数字图像处理Python实战–高斯&拉普拉斯金字塔&图像重建_python使用高斯和拉普拉斯完成图像拼接-CSDN博客](https://blog.csdn.net/qq_43836026/article/details/105565359?ops_request_misc=&request_id=&biz_id=102&utm_term=拉普拉斯金字塔拼接 线性过度&utm_medium=distribute.pc_search_result.none-task-blog-2allsobaiduweb~default-1-105565359.142v96pc_search_result_base2&spm=1018.2226.3001.4187)
5.1 注意事项
- 融合的重叠区域:在之前的测试中,单纯根据下采样和上采样来融合拼接缝,这种方式的融合效果并不显著,仍然有比较明显的边缘存在。我在每次下采样的时候都在拼接缝设置了3个像素的重叠区域。具体来说,拉普拉斯融合是根据一个黑白图像mask来区分两个图像,白色区域属于图1,黑色区域属于图2 。对mask的白色区域进行3个像素的膨胀处理得到mask1,然后将mask1减去mask就可以得到一个3像素宽的重叠区域。在重叠区域进行的简单融合。这样可以有效的改善融合效果。后续可以考虑使用非线性加权平均进行测试。
- 重叠区域的宽度不宜设置过高,因为 重叠区域的宽度X下采样倍数 才是实际重叠的宽度
- 有看到过一个示意图,但是现在找不到了
- 只对拼接缝附近的图像进行融合:因为图像过大,所以将拼接缝附近的图像单独划分成patch进行融合。需要注意融合的重叠区域宽度不宜设置过高,否则融合后的patch会与原始图像有明显的分界线。根据划分patch的方式进行融合,最后融合的图像实际上是一个井字形。井字形的四个交点出会有4个patch重叠。我目前的拼接策略是,首先进行硬拼接,然后融合时从硬拼接图像中选取patch,将融合后的patch放回硬拼接图像,在下一次融合时再从硬拼接图像选取patch。这样可以保证井字形的四个交点,即4个patch的重叠区域没有明显分界线,但是会使得有一些patch会被多次融合。
6. 其他注意事项
- 头文件交叉引用:
smv_overlap.h
和smv_interfaceImpl.h
存在交叉引用的风险。我目前是在smv_interfaceImpl.h
中新定义了一个结构体overlapAndFeatures
,避免需要在smv_interfaceImpl.h
中调用smv_overlap.h
的结构体overlapAndFeatures
。这两个结构体的内容是一样的。 - 内存泄漏:程序可能存在多处内存泄漏的问题,后续可以考虑进行内存优化。