360全景图像拼接

刚到实验室,做了一个全景图像拼接的training project,自己前前后后搞了1个多月(汗啊,只能自我安慰是接触到的第一个图形学的东西),尝试了几种方法,写了很多无用代码,下面把我一路曲曲折折的过程抛出来,希望能对大家有些许帮助吧。

    一. 做拼接的第一步一定是warp,就是把每张图像投影到柱面或者球面,我将每张图片做了柱面投影,而这么做的理由呢,就是将图像投影到统一的面上以方便拼接,根据我做的过程中的理解就是使拼接的图像很平滑,当然如果只是拼接两张图片的话,完全可以省略这一步。

    二. 图像拼接的第二步就是提取特征点(也可以采用边特征的提取),特征点的提取方法有很多,我使用了lowe的SIFT,SIFT是一个非常鲁棒的算法,但是速度很慢,如果想程序运行速度很快,可以使用其他特征点提取方法,lowe提供了SIFT的源码,可以直接使用。

    三. 提取出两张图片的特征点以后,就要找到特征点间的对应关系,这一步是一个简单的距离比值删选的过程,我自己实现了一个暴力筛选的方法,还是比较简单的,但是这样肯定会很影响速度,所以要使用一个k-d tree的approximate nearest neighbour算法,可以用ann库实现,当然也可以直接用rob hess的特征点匹配算法实现。

    四. 上一步找到对应特征点以后,接下就是用RANSAC对已有匹配进行筛选并求出对应的homography,实际上就是一个简单的迭代过程。

    五. 求出homography以后就可以进行alignment了,这一步可以用opencv提供的WarpPerspective函数实现,但是用这个函数一张图像经过几次align之后,拼接后图像越靠右边越有发散效果,所以用这个函数只有两张图片好用(如果有人知道多张图片怎么用这个函数进行拼接,请您不吝赐教),这一步还有一个很重要的边界问题,及拼接图像转换后的坐标可能出现负值,这一点一定要考虑进去,否则会出现拼接后的图像包含不全的情况。

    六. alignment之后就是blend的问题,以使拼接边界不那么明显,我采用了一个简单的线性的距离权值算法,效果还可以看,即pixel=pixel_a*alpha+pixel_b*(1-alpha),alpha为当前像素点距拼接左边界的距离除以整个重叠区域的宽度。下面两张图分别是4张,3张图像用WarpPerspective函数拼接的。



    七. 如果你只需要拼接两张图片,那到上一步就可以了,以下是360度拼接的额外过程,即图片有n张的情况,我只考虑了所有图片都属于一个全景图没有噪音图片的乱序图片的情况。要先将图片排好序,以下为排序过程:①在所有图像对间找到inliers个数最多两张图像②在两张图片的基础上向前向后依次找出与当前图像邻接的图像直到结束。

    八. 经过上一步就可以进行alignment了,因为之前用提过的WarpPerspective函数使用中的问题,这一步只使用了homography中的水平和垂直平移分量tx,ty,依旧采用线性权值的方法进行blend,多长图像拼接还有一个很重要的问题就是拼接后首尾不能相接的情况,这就需要惊醒调整,比较好的方法是bundle adjustment,我也分别用opencv和sba实现了,但是估计还是实现的方法不对,调了好长时间没有出效果,无奈只能先放弃了,以后比较闲的时候再实现吧(如果有人在这一步用bundle adjustment实现,请赐教)。转去实现了一个比较简单的线性调整的方法,最后结果看起来还可以,有些模糊的地方,我估计可能是blend方法的问题,但是没有继续实现multi-blend,所以结果只能是现在这样了。

    总之呢,整个拼接程序还比较粗糙,有很多地方有待完善,欢迎大家有问题可以和我交流。下面为两个17张图片的全景图


压缩包中包含的具体内容: 对给定数据中的6个不同场景图像,进行全景拼接操作,具体要求如下: (1) 寻找关键点,获取关键点的位置和尺度信息(DoG检测子已由KeypointDetect文件夹中的detect_features_DoG.m文件实现;请参照该算子,自行编写程序实现Harris-Laplacian检测子)。 (2) 在每一幅图像中,对每个关键点提取待拼接图像的SIFT描述子(编辑SIFTDescriptor.m文件实现该操作,运行EvaluateSIFTDescriptor.m文件检查实现结果)。 (3) 比较来自两幅不同图像的SIFT描述子,寻找匹配关键点(编辑SIFTSimpleMatcher.m文件计算两幅图像SIFT描述子间的Euclidean距离,实现该操作,运行EvaluateSIFTMatcher.m文件检查实现结果)。 (4) 基于图像中的匹配关键点,对两幅图像进行配准。请分别采用最小二乘方法(编辑ComputeAffineMatrix.m文件实现该操作,运行EvaluateAffineMatrix.m文件检查实现结果)和RANSAC方法估计两幅图像间的变换矩阵(编辑RANSACFit.m 文件中的ComputeError()函数实现该操作,运行TransformationTester.m文件检查实现结果)。 (5) 基于变换矩阵,对其中一幅图像进行变换处理,将其与另一幅图像进行拼接。 (6) 对同一场景的多幅图像进行上述操作,实现场景的全景拼接(编辑MultipleStitch.m文件中的makeTransformToReferenceFrame函数实现该操作)。可以运行StitchTester.m查看拼接结果。 (7) 请比较DoG检测子和Harris-Laplacian检测子的实验结果。图像拼接的效果对实验数据中的几个场景效果不同,请分析原因。 已经实现这些功能,并且编译运行均不报错!
根据提供的引用内容,360全景拼接的基本原理是透视变换。下面是一个使用OpenCV库实现360全景拼接的Python代码示例: ```python import cv2 import numpy as np # 读取图像 img1 = cv2.imread('image1.jpg') img2 = cv2.imread('image2.jpg') img3 = cv2.imread('image3.jpg') img4 = cv2.imread('image4.jpg') # 设置拼接图像的大小和输出图像的大小 width, height = 960, 480 size = (width, height) # 设置透视变换的参数 fov = 180 theta = fov / 2 * np.pi / 180 f = width / 2 / np.tan(theta / 2) # 计算透视变换矩阵 K = np.array([[f, 0, width / 2], [0, f, height / 2], [0, 0, 1]]) R1 = np.array([[1, 0, 0], [0, np.cos(np.pi / 2), -np.sin(np.pi / 2)], [0, np.sin(np.pi / 2), np.cos(np.pi / 2)]]) R2 = np.array([[np.cos(np.pi), 0, -np.sin(np.pi)], [0, 1, 0], [np.sin(np.pi), 0, np.cos(np.pi)]]) R3 = np.array([[1, 0, 0], [0, np.cos(-np.pi / 2), -np.sin(-np.pi / 2)], [0, np.sin(-np.pi / 2), np.cos(-np.pi / 2)]]) R4 = np.array([[np.cos(np.pi / 2), 0, -np.sin(np.pi / 2)], [0, 1, 0], [np.sin(np.pi / 2), 0, np.cos(np.pi / 2)]]) P1 = K.dot(R1).dot(np.array([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0]])).dot(K) P2 = K.dot(R2).dot(np.array([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0]])).dot(K) P3 = K.dot(R3).dot(np.array([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0]])).dot(K) P4 = K.dot(R4).dot(np.array([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0]])).dot(K) # 进行透视变换 dst1 = cv2.warpPerspective(img1, P1, size) dst2 = cv2.warpPerspective(img2, P2, size) dst3 = cv2.warpPerspective(img3, P3, size) dst4 = cv2.warpPerspective(img4, P4, size) # 拼接图像 result = np.zeros((height * 2, width * 2, 3), dtype=np.uint8) result[0:height, 0:width] = dst1 result[0:height, width:width * 2] = dst2 result[height:height * 2, 0:width] = dst3 result[height:height * 2, width:width * 2] = dst4 # 显示结果 cv2.imshow('result', result) cv2.waitKey(0) cv2.destroyAllWindows() ``` 注意:上述代码仅为示例,实际使用时需要根据具体情况进行调整。
评论 39
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值