三维重建实际列子讲解.docx
《三维重建实际列子讲解.docx》由会员分享,可在线阅读,更多相关《三维重建实际列子讲解.docx(14页珍藏版)》请在冰豆网上搜索。
三维重建实际列子讲解
三维重建实验报告
1.基于特征点的重建
1.1.特征提取
由双目立体视觉进行三位重建的第一步是寻找两幅图像中的对应点。
目前人们已经发明了很多二维图像配准算法,比如SIFT,SURF等等。
最新版本的OpenCV2.2中的features2d库中包含了很多常用的算法,其中特征点定位的算法有FAST,SIFT,SURF,MSER,HARRIS等,特征点描述算法有SURF,SIFT等,还有若干种特征点匹配算法。
这三个步骤的算法可以任选其一,自由组合,非常方便。
我经过实验,选择了一种速度、特征点数量和精度都比较好的组合方案:
FAST角点检测算法+SURF特征描述子+FLANN(FastLibraryforApproximateNearestNeighbors)匹配算法。
在匹配过程中需要有一些措施来过滤误匹配。
一种比较常用的方法是比较第一匹配结果和第二匹配结果的得分差距是否足够大,这种方法可以过滤掉一些由于相似造成的误匹配。
还有一种方法是利用已经找到的匹配点,使用RANSAC算法求得两幅视图之间的单应矩阵,然后将左视图中的坐标P用单应矩阵映射到右视图的Q点,观察与匹配结果Q’的欧氏距离是否足够小。
当然由于图像是具有深度的,Q与Q’必定会有差距,因此距离阈值可以设置的稍微宽松一些。
我使用了这两种过滤方法。
另外,由于图像有些部分的纹理较多,有些地方则没有什么纹理,造成特征点疏密分布不均匀,影响最终重建的效果,因此我还采取了一个措施:
限制特征点不能取的太密。
如果新加入的特征点与已有的某一特征点距离太小,就舍弃之。
最终匹配结果如下图所示,精度和均匀程度都较好。
上述工作实现在cvFuncs.cpp中的GetPair函数。
1.2.计算3D坐标
这次老师给出的试验图片左右极线都是对齐的,省去了校正的步骤,计算3D坐标也比较方便。
计算方法和老师课件上的相似,见下图:
如果(x1,y1),(x2,y2)用各自图像上的像素坐标表示,L和(X,Y,Z)用毫米表示,f用像素表示的话,用相似三角形的知识就可以推出:
其中W和H是图像的宽高(像素数),y是y1和y2的均值,Z加负号是为了保持右手坐标系,而Y加负号是由于图像成像过程中上下发生了倒转。
三维世界原点取为左摄像机的焦点。
计算的代码见cvFunc.cpp中的StereoTo3D函数。
1.3.三角剖分
三角剖分是为了之后的纹理贴图,我用了OpenCV中的Delaunay三角剖分函数,这种剖分算法的可以使所形成的三角形的最小角最大。
剖分的示例如下:
三角剖分的代码见cvFuncs.cpp中的TriSubDiv函数,我将特征点存储到一个vector变量中,剖分结果存储到一个vector变量中,Vec3i中存储的是3个表示顶点编号的整数。
1.4.三维重构
三维重构的思路很简单,用OpenGL中纹理贴图功能,将平面图像中的三角形逐个贴到计算出的三维坐标上去就可以了。
为了便于观察3D效果,我还设计了交互功能:
用方向键可以上下左右旋转重构的模型,用鼠标滚轮可以放大或缩小。
用gluLookAt函数可以实现视点旋转的功能。
三维重构的代码实现在glFuncs.cpp中。
1.5.效果展示及不足
Cloth图像是重构效果比较好的一组:
可以比较明显的看出3D效果,也比较符合直觉。
然而其他图像效果就差强人意了:
仔细分析造成这种效果的原因,一方面,特征点的匹配可能有些误差,造成3D坐标的计算不太精确,但大部分坐标还是准确的。
另一方面,左右视图可能会有不同的遮挡、偏移等情况,因此匹配得到的特征点可能实际上并不是3维世界中的同一点,这种误差是无法消除的。
但造成效果变差的最重要的原因,还是图像中深度变化较大,而特征点选取的比较稀疏,因此正面看还比较正常,一旦旋转纹理就显得扭曲变形了。
为了解决这个问题,应当试图把特征点取到深度变化较剧烈的地方,一般是图像中的边界处。
然而特征点检测一般都检测出的是角点和纹理密集的区域,因此可以考虑更换对应点匹配的方法。
2.基于块匹配的三维重建
2.1.基于块匹配的视差计算
上面提取特征点的过程中实际上忽略了一个辅助信息:
对应点应当是取在对应极线上的一个区间内的。
利用这个信息可以大幅简化对应点的匹配,事实上只要用L1距离对一个像素周围的block计算匹配距离就可以了,也就是OpenCV中实现的块匹配算法的基本思路。
比起特征点匹配,这是一种“稠密”的匹配算法,精度也可以接受。
下图中浅色表示视差较大,对应深度较浅。
左侧有一块区域是左右视图不相交的部分,因此无法计算视差。
可以发现视差计算结果中有很多噪声。
事实上在纹理平滑的区域,还有左右视图中不同遮挡的区域,是很难计算视差的。
因此我利用最近邻插值和数学形态学平滑的方法对视差图进行了修复(见cvFuncs2.cpp中的FixDisparity函数):
2.2.对应点的选取
上面提到,为了获得较好的重构效果,特征点最好取在深度变化较大的区域。
基于这种猜想,我首先对上面的视差图求梯度,然后找到梯度最大的点,观察梯度的方向,如果是偏x方向,就在该点左右若干像素各取一个点;否则就在上下若干像素各取一个点。
然后根据这两个点的视差值就可以计算出另外一个视图中的对应点的坐标。
特征点还不能分布过密,因此我取完一对特征点后,将其周围一圈像素的梯度置零,然后在寻找下一个梯度最大值,这样一直下去,直到取够特征点数。
特征点也不能全取在深度变化剧烈的区域,在平坦的区域也可以取一些。
最终我取的特征点如下图:
其中紫色的点是在较平坦的区域取到的,其他颜色是在边界区域取到的。
这些算法实现在ChooseKeyPointsBM函数中。
2.3.效果及不足
取完特征点后其余的步骤不变。
下面是一些最终的重建效果:
能更加明显的看出深度效果,不过在边界处仍然会造成纹理的扭曲。
如果要进一步改进效果,我认为可以先对视差图像进行分割,将图像分成视差比较连续的几块区域分别贴图,视差变化剧烈的区域就不必把扭曲的纹理贴上去了。
我尝试了以下分割的效果,如下图所示,应该可以达到更好的效果,不过由于时间所限,就没有进一步实现下去了。
关于上面实现的两种算法,我在main函数的前面设置了一个变量g_algo,可以用来切换不同的算法。