单目三维重建简介:

单目三维重建是根据单个摄像头的运动模拟双目视觉获得物体在空间中的三维视觉信息。已知单个摄像头在两个不同时间点上同时在空间中两个不同位置的图像等价于已知两个摄像头同一时间在空间两个不同位置的图像。所以问题分解为:
(1)如何用单目视觉替换双目视觉,即如何确定单个摄像头在两个不同时间点的空间转换关系;
(2)根据双目视觉确定图像中物体的三维视觉信息。

代码实现:

(1)标定摄像机获得摄像机矩阵K(内参数矩阵)

目的:确定空间物体表面某点的三维几何位置与其在图像中对应点之间的相互关系,建立摄像机成像的几何模型,这些几何模型参数就是摄像机参数。

内参数矩阵表示从归一化图像平面到物理成像平面的转换关系。 用OpenCV中的库函数标定摄像机会出现很多问题,主要包括:
1) 摄像头分辨率:尝试过使用手机摄像头,现在的手机拍摄的图片分辨率过高以至于找不到标定板的位置,因为有可能是检测角点的窗口范围设置有限,所以,分辨率过高的图像中的角点在窗口中水平方向和垂直方向的灰度梯度变化不明显,角点部分由过多的像素过渡,在过小的窗口看起来角点更像是变化的弧线。可以通过下采样的方式先降低图像分辨率,检测到模式后再上采样恢复角点实际在图像中的位置。然而,用笔记本的摄像头则没有这样的问题出现。
2) 设置模式大小:寻找标定板时,需要手动调整标定板对象点objpoint的容量,笔者这里的容量为9*6;如果不手动调整有可能找不到标定板。
摄像头标定代码如下:

#coding:utf-8
import cv2
import numpy as np
import glob
import PIL.ExifTags
import PIL.Image
# 找棋盘格角点
criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 30, 0.001)    
#棋盘格模板规格
w = 9
h = 6
# 世界坐标系中的棋盘格点,例如(0,0,0), (1,0,0), (2,0,0) ....,(8,5,0),去掉Z坐标,记为二维矩阵
objp = np.zeros((w*h,3), np.float32)   #返回来一个给定形状和类型的用0填充的数组
objp[:,:2] = np.mgrid[0:w,0:h].T.reshape(-1,2)
#存储的点需要是有序的,定义一个网格来存储棋盘格角点的世界坐标和图像坐标对
obj_points = [] # 在世界坐标系中的三维点
img_points = [] # 在图像平面的二维点
images = glob.glob('biaoding/*.JPG')  #加载图片
for fname in images:
    img = cv2.imread(fname)
    gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
    # 找到棋盘格角点
    ret, corners = cv2.findChessboardCorners(gray, (w,h),None)
    # 如果找到足够点对,将其存储起来
    if ret == True:
        cv2.cornerSubPix(gray,corners,(11,11),(-1,-1),criteria)   #亚像素角点检测
        obj_points.append(objp)
        img_points.append(corners)
        # 将角点在图像上显示
        cv2.drawChessboardCorners(img, (w,h), corners, ret)
        cv2.imshow('findCorners',img)
        cv2.waitKey(1)
cv2.destroyAllWindows()
ret, mtx, dist, rvecs, tvecs = cv2.calibrateCamera(obj_points, img_points, gray.shape[::-1], None, None)
#将这些值保存到不同的numpy文件中,方便后期加载
np.save("./camera_params/ret", ret)
np.save("./camera_params/K", mtx)
np.save("./camera_params/dist", dist)
np.save("./camera_params/rvecs", rvecs)
np.save("./camera_params/tvecs", tvecs)
print ("ret:",ret)
print ("mtx:\n",mtx)        # 内参数矩阵
print ("dist:\n",dist)      # 畸变系数   distortion cofficients = (k_1,k_2,p_1,p_2,k_3)
print ("rvecs:\n",rvecs)    # 旋转向量  # 外参数
print ("tvecs:\n",tvecs)   # 平移向量  # 外参数
# 去畸变
img2 = cv2.imread('biaoding/IMG_4656.JPG')
h,w = img2.shape[:2]
newcameramtx, roi=cv2.getOptimalNewCameraMatrix(mtx,dist,(w,h),0,(w,h)) # 自由比例参数
dst = cv2.undistort(img2, mtx, dist, None, newcameramtx)
# 根据前面ROI区域裁剪图片
x,y,w,h = roi
dst = dst[y:y+h, x:x+w]
cv2.imwrite('calibresult12.png',dst)
print ("dst的大小为:", dst.shape)
# 计算反投影误差
# 在Python 3中,range()与xrange()合并为range( )
tot_error = 0  
for i in range(len(obj_points)):    
    img_points2, _ = cv2.projectPoints(obj_points[i],rvecs[i],tvecs[i],mtx,dist)  
    error = cv2.norm(img_points[i],img_points2, cv2.NORM_L2)/len(img_points2)  
    tot_error += error  
mean_error = tot_error/len(obj_points)  
print ('total error: ', tot_error)  
print ('mean error: ', mean_error)

(2)SIFT特征点匹配
主要流程:首先获得两幅图像的特征描述子和关键点,然后在匹配时删除匹配不符合要求的点。最后根据匹配合格的点计算基础矩阵F。已知空间中的点在两个物理成像图像平面中的坐标(x, y)和(x’, y’),可以通过基础矩阵计算出在图像对中的另一个物理成像图像平面中的极线。
实现代码如下:

<pre name="code" class="python">################################################################################
print 'SIFT Keypoints and Descriptors'
sift = cv2.SIFT()
keypoint1, descriptor1 = sift.detectAndCompute(img1, None)
keypoint2, descriptor2 = sift.detectAndCompute(img2, None)
################################################################################
print 'SIFT Points Match'
FLANN_INDEX_KDTREE = 0
index_params = dict(algorithm = FLANN_INDEX_KDTREE, trees = 5)
search_params = dict




    
(checks = 50)
# flann = cv2.FlannBasedMatcher(index_params, search_params)
bf = cv2.BFMatcher()
matches = bf.knnMatch(descriptor1, descriptor2, k = 2)
################################################################################
good = []
points1 = []
points2 = []
################################################################################
for i, (m, n) in enumerate(matches):
    if m.distance < 0.7 * n.distance:
        good.append(m)
        points1.append(keypoint1[m.queryIdx].pt)
        points2.append(keypoint2[m.trainIdx].pt)
points1 = np.float32(points1)
points2 = np.float32(points2)
F, mask = cv2.findFundamentalMat(points1, points2, cv2.RANSAC)
# We select only inlier points
points1 = points1[mask.ravel() == 1]
points2 = points2[mask.ravel() == 1]

(3)根据求得的内参数矩阵,计算基础矩阵F和本征矩阵E:

基础矩阵适用于未标定的摄像头,假设空间点在两个物理成像平面中的坐标分别为p = (u, v)和p’ = (u’, v’),则满足transpose§ Fp = 0,*表示矩阵乘法。根据基础矩阵的定义F = inverse(transpose(K)) * E * inverse(K)计算出本征矩阵E。

# camera matrix from calibration
#加载保存的内参
K = np.load(r'F:\python\3D\camera_params\K.npy')
# essential matrix
E = K.T * F * K

(4)根据本征矩阵的旋转和平移分量构造投影矩阵对P和P’
根据本征矩阵E用SVD分解得到旋转矩阵R和平移向量t,检查旋转矩阵R是否有效,根据旋转矩阵的标准正交特性判断旋转矩阵的有效性。然后在旋转矩阵有效的情况下构造投影矩阵P0和P1。

注:旋转矩阵的有效性检查非常重要。因为实际上图像的噪声有可能导致特征点的位置存在误差,而且实际摄像机也并不是仿射摄像机,所以SVD分解有可能得不到有效的仿射结构。所以没有设置相关的参数调整来修正仿射结构。所以,如果得不到有效旋转矩阵,重建是没有意义的。

W = np.array([[0., -1., 0.], [1., 0., 0.], [0., 0., 1.]])
U, S, V = np.linalg.svd(E)
# rotation matrix
R = U * W * V
# translation vector
t = [U[0][2], U[1][2], U[2][2]]
checkValidRot(R)
P1 = [[R[0][0], R[0][1], R[0][2], t[0]], [R[1][0], R[1][1], R[1][2], t[1]], [R[2][0], R[2][1], R[2][2], t[2]]]
P = [[1., 0., 0., 0.], [0., 1., 0., 0.], [0., 0., 1., 0.]]

(5)有效特征点三角化实现重建
已知(u,v)为空间点在物理成像平面中的坐标,则p = (u,v,1)为空间点在物理成像平面中的齐次坐标,inverse(K) * p = u,u = (x/z, y/z, 1)为归一化图像平面中的齐次坐标。根据两个不同的归一化图像平面中的坐标t和u和u1,以及投影矩阵P和P‘,构造线性方程组:lamb * u = PX和lamb’ * u’ = P’X,解线性方程组得到X = (x, y, z)。保存三轴坐标至pointCloudX,pointCloudY,pointCloudZ,分开存储便于绘制三维图形。最后将三维坐标点投影到物理成像平面中去,计算重投影后的坐标点与原始图像坐标点之间的重投影误差。

print 'points triangulation'
u = []
u1 = []
Kinv = np.linalg.inv(K)
# convert points in gray image plane to homogeneous coordinates
for idx in range(len(points1)):
    t = np.dot(Kinv, np.array([points1[idx][0], points1[idx][1], 1.]))
    t1 = np.dot(Kinv, np.array([points2[idx][0], points2[idx][1], 1.]))
    u.append(t)   
    u1.append(t1)
################################################################################
# re-projection error
reprojError = 0
# point cloud (X,Y,Z)
pointCloudX = []
pointCloudY = []
pointCloudZ = []
for idx in range(len(points1)):
    X = linearLSTriangulation(u[idx], P, u1[idx], P1)
    pointCloudX.append(X[0])
    pointCloudY.append(X[1])
    pointCloudZ.append(X[2])
    temp = np.zeros(4, np.float32)
    temp[0] = X[0]
    temp[1] = X[1]
    temp[2] = X[2]
    temp[3] = 1.0    
    print temp
    # calculate re-projection error 
    reprojPoint = np.dot(np.dot(K, P1), temp)
    imgPoint = np.array([points1[idx][0], points1[idx][1], 1.])
    reprojError += math.sqrt((reprojPoint[0] / reprojPoint[2] - imgPoint[0]) * (reprojPoint[0] / reprojPoint[2] - imgPoint[0]) + (reprojPoint[1] / reprojPoint[2] - imgPoint[1]) * (reprojPoint[1] / reprojPoint[2] - imgPoint[1]))
print 'Re-project Error:', reprojError / len(points1)

总结
单目视觉三维重建其实还是利用的双视几何的原理来做的,唯一有变化的地方是单目视觉在不同时间段上的两个摄像头的关系。立体视觉固定位置的两个摄像头相互平行的结构极大地简化了摄像头之间的变换关系,而单目视觉的两个摄像头由于仿射空间变换的结果取决于特征点匹配的精度,而有时匹配合格的特征点如果太少会直接影响到仿射结构的结果,所以变换的结果不确定性很大。总的来说,相比单目视觉,双目视觉三维重建的结果会精确很多。
————————————————

原文链接:https://blog.csdn.net/shadow_guo/article/details/44193993

单目三维重建简介: 单目三维重建是根据单个摄像头的运动模拟双目视觉获得物体在空间中的三维视觉信息。已知单个摄像头在两个不同时间点上同时在空间中两个不同位置的图像等价于已知两个摄像头同一时间在空间两个不同位置的图像。所以问题分解为:(1)如何用单目视觉替换双目视觉,即如何确定单个摄像头在两个不同时间点的空间转换关系;(2)根据双目视觉确定图像中物体的三维视觉信息。代码实现:(1)标定摄像机获得摄像机矩阵K(内参数矩阵)目的:确定空间物体表面某点的三维几何位置与其在图像中对应点之间的相互关系, def draw(img, corners, imgpts): corner = tuple(corners[0].ravel()) img = cv2.line(img, corner, tuple(imgpts[0].ravel()), (255,0,0), 5) img = cv2.... 单目结构光三维扫描仪的标定,主要是将投影仪逆向成一个相机的过程,标定过程中投影仪投射横竖多频条纹,解相后得到在相机对应的投影感器的像素值,将相机纹理像素和投影仪传感像素对应后,识别两个相机的标定板(这里已经把投影仪看作一个相机)。 上图为相机识别标定板的输出结果,分辨率为12801024 上图为投影仪逆向相机后识别标定板的输出结果。分辨率为1280720 此时完全可以当成双目相机来标定。分别可以得出相应相机内参。 八参数标定 用单目八参数标定法的话,还需要根据竖条纹相位值做8个参数标定 八参数法三维
单目视觉三维重建 1. 单目视觉三维重建简介         单目视觉三维重建是根据单个摄像头的运动模拟双目视觉获得物体在空间中的三维视觉信息。已知单个摄像头在两个不同时间点上同时在空间中两个不同位置的图像等价于已知两个摄像头同一时间在空间两个不同位置的图像。所以问题分解为: (1)如何用单目视觉替换双目视觉,即如何确定单个摄像头在两个不同时间点的空间转换关系; (2)根据双目视觉确定图像
单目视觉成像模型以及双目立体视觉的三维点计算一、单目视觉成像模型1. 世界坐标系→相机坐标系:2. 相机坐标系→像平面坐标系3. 像平面坐标系→像素坐标系二、双目立体视觉的三维点计算 一、单目视觉成像模型 在整个单目视觉成像系统中,涉及到的坐标系有(均为左手系):世界坐标系(Xw,Yw,Zw),相机坐标系(Xc,Yc,Zc),像平面坐标系(X,Y),像素坐标(u,v)。像素坐标为离散的整数坐...
由双目立体视觉进行三位重建的第一步是寻找两幅图像中的对应点。目前人们已经发明了很多二维图像配准算法,比如SIFT, SURF等等。最新版本的OpenCV 2.2中的features2d库中包含了很多常用的算法,其中特征点定位的算法有FAST, SIFT, SURF ,MSER, HARRIS等,特征点描述算法有SURF, SIFT等,还有若干种特征点匹配算法。这三个步骤的算法可以任选其一,自由组合,非常方便。我经过实验,选择了一种速度、特征点数量和精度都比较好的组合方案:FAST角点检测算法+SURF特征描述子+FLANN(Fast Library for Approximate Nearest Neighbors) 匹配算法。 在匹配过程中需要有一些措施来过滤误匹配。一种比较常用的方法是比较第一匹配结果和第二匹配结果的得分差距是否足够大,这种方法可以过滤掉一些由于相似造成的误匹配。还有一种方法是利用已经找到的匹配点,使用RANSAC算法求得两幅视图之间的单应矩阵,然后将左视图中的坐标P用单应矩阵映射到右视图的Q点,观察与匹配结果Q’的欧氏距离是否足够小。当然由于图像是具有深度的,Q与
【资源介绍】 基于python实现的单目视觉三维重建源码(课程设计).zip 该项目是个人课设项目,答辩评审分达到95分,代码都经过调试测试,确保可以运行!欢迎下载使用,可用于小白学习、进阶。 该资源主要针对计算机、通信、人工智能、自动化等相关专业的学生、老师或从业者下载使用,亦可作为期末课程设计、课程大作业、毕业设计等。 项目整体具有较高的学习借鉴价值!基础能力强的可以在此基础上修改调整,以实现不同的功能。 基于python实现的单目视觉三维重建源码(课程设计).zip 基于python实现的单目视觉三维重建源码(课程设计).zip 基于python实现的单目视觉三维重建源码(课程设计).zip
该资源内项目源码是个人的毕设,代码都测试ok,都是运行成功后才上传资源,答辩评审平均分达到94.5分,放心下载使用! 该资源适合计算机相关专业(如人工智能、通信工程、自动化、软件工程等)的在校学生、老师或者企业员工下载,适合小白学习或者实际项目借鉴参考! 当然也可作为毕业设计、课程设计、课程作业、项目初期立项演示等。如果基础还行,可以在此代码基础之上做改动以实现更多功能。 双目测距理论及其python运用 一、双目测距基本流程 Stereo Vision, 也叫双目立体视觉,它的研究可以帮助我们更好的理解人类的双眼是如何进行深度感知的。双目视觉在许多领域得到了应用,例如城市三维重建、3D模型构建(如kinect fusion)、视角合成、3D跟踪、机器人导航(自动驾驶)、人类运动捕捉(Microsoft Kinect)等等。双目测距也属于双目立体视觉的一个应用领域,双目测距的基本原理主要是三角测量原理,即通过视差来判定物体的远近。 那么总结起来,双目测距的大致流程就是: **双目标定 --> 立体校正(含消除畸变) --> 立体匹配 --> 视差计算 --> 深度计算(3D坐标)计算** linux下安装opencv-python: ```python pip install opencv-python 二、相机畸变 光线经过相机的光学系统往往不能按照理想的情况投射到传感器上,也就是会产生所谓的畸变。畸变有两种情况:一种是由透镜形状引起的畸变称之为径向畸变。在针孔模型中,一条直线投影到像素平面上还是一条直线。可是,在实际拍摄的照片中,摄像机的透镜往往使得真实环境中的一条直线在图片中变成了曲线。越靠近图像的边缘,这种现象越明显。由于实际加工制作的透镜往往是中心对称的,这使得不规则的畸变通常径向对称。它们主要分为两大类,桶形畸变 和 枕形畸变(摘自《SLAM十四讲》)如图所示: <div align=center><img src="https://img-blog.csdnimg.cn/20190907184815326.PNG" width="324" height="100" /></div> 桶形畸变是由于图像放大率随着离光轴的距离增加而减小,而枕形畸变却恰好相反。 在这两种畸变中,穿过图像中心和光轴有交点的直线还能保持形状不变。
单目三维重建是指利用单张图像进行三维重建的技术。OpenCV C是一种基于C语言开发的计算机视觉库,可以用于实现单目三维重建。 在OpenCV中,可以通过以下步骤实现单目三维重建: 1. 相机标定:首先需要对相机进行标定,获取相机的内参和畸变参数。OpenCV提供了相机标定函数,可以通过拍摄特定的标定板来计算出这些参数。 2. 特征提取和匹配:从输入图像中提取特征点,并利用特征描述子进行特征匹配。OpenCV提供了ORB、SIFT、SURF等算法用于特征提取和匹配。 3. 三角化:根据匹配的特征点,通过三角化的方法计算出对应点的三维坐标。OpenCV提供了triangulatePoints函数用于三角化。 4. 姿态估计:利用单目相机的运动恢复相机的姿态信息,即相机在空间中的位置和朝向。OpenCV提供了solvePnP函数用于姿态估计。 5. 三维重建:根据相机的姿态信息和三角化得到的特征点,可以进行三维重建OpenCV提供了projectPoints函数用于将三维点投影到二维图像中,从而生成三维重建结果。 值得注意的是,单目三维重建是一种基于单张图像的估计方法,精度受到图像质量、特征点提取与匹配的准确性等因素的影响。为了提高重建的精度和稳定性,可以采用多张图像进行融合,或结合其他传感器(如IMU)进行联合估计。