OpenCV+相机校准和3D重建

相机校准至少需要10个测试图案,所需的重要输入数据是3D现实世界点集以及图像中这些点的相应2D坐标。3D点称为对象点,而2D图像点称为图像点。

准备工作

除了棋盘,我们还可以使用圆形网格。 在这种情况下,我们必须使用函数cv.findCirclesGrid()来找到模式。 较少的图像足以使用圆形网格执行相机校准。
一旦找到拐角,就可以使用cv.cornerSubPix()来提高其精度。我们还可以使用cv.drawChessboardCorners()绘制图案。

import numpy as np
import cv2 as cv
import glob
# 终止条件
criteria = (cv.TERM_CRITERIA_EPS + cv.TERM_CRITERIA_MAX_ITER, 30, 0.001)
# 准备对象点, 如 (0,0,0), (1,0,0), (2,0,0) ....,(6,5,0)
objp = np.zeros((6*7,3), np.float32)
objp[:,:2] = np.mgrid[0:7,0:6].T.reshape(-1,2)
# 用于存储所有图像的对象点和图像点的数组。
objpoints = [] # 真实世界中的3d点
imgpoints = [] # 图像中的2d点
images = glob.glob('*.jpg')
for fname in images:
    img = cv.imread(fname)
gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
# 找到棋盘角落
ret, corners = cv.findChessboardCorners(gray, (7,6), None)
# 如果找到,添加对象点,图像点(细化之后)
if ret == True:
    objpoints.append(objp)
corners2 = cv.cornerSubPix(gray,corners, (11,11), (-1,-1), criteria)
imgpoints.append(corners)
# 绘制并显示拐角
cv.drawChessboardCorners(img, (7,6), corners2, ret)
cv.imshow('img', img)
cv.waitKey(500)
cv.destroyAllWindows()

校准

现在我们有了目标点和图像点,现在可以进行校准了。我们可以使用函数cv.calibrateCamera()返回相机矩阵,失真系数,旋转和平移矢量等。

ret, mtx, dist, rvecs, tvecs = cv.calibrateCamera(objpoints, imgpoints,
gray.shape[::-1], None, None)

不失真

现在,我们可以拍摄图像并对其进行扭曲。OpenCV提供了两种方法来执行此操作。但是,首先,我们可以使用cv.getOptimalNewCameraMatrix()基于自由缩放参数来优化相机矩阵。如果缩放参数alpha = 0,则返回具有最少不需要像素的未失真图像。因此,它甚至可能会删除图像角落的一些像素。如果alpha = 1,则所有像素都保留有一些额外的黑色图像。此函数还返回可用于裁剪结果的图像ROI。

使用cv.undistort()

这是最简单的方法。只需调用该函数并使用上面获得的ROI裁剪结果即可。

使用remapping

该方式有点困难。首先,找到从扭曲图像到未扭曲图像的映射函数。然后使用重映射功能。

img = cv.imread('left12.jpg')
h, w = img.shape[:2]
newcameramtx, roi = cv.getOptimalNewCameraMatrix(mtx, dist, (w,h), 1, (w,h))
# 1
dst = cv.undistort(img, mtx, dist, None, newcameramtx)
# 剪裁图像
x, y, w, h = roi
dst = dst[y:y+h, x:x+w]
cv.imwrite('calibresult.png', dst)
# 2
mapx, mapy = cv.initUndistortRectifyMap(mtx, dist, None, newcameramtx, (w,h), 5)
dst = cv.remap(img, mapx, mapy, cv.INTER_LINEAR)
# 裁剪图像
x, y, w, h = roi
dst = dst[y:y+h, x:x+w]
cv.imwrite('calibresult.png', dst)

参数:重投影误差

重投影误差可以很好地估计找到的参数的精确程度。重投影误差越接近零,我们发现的参数越准确。给定固有,失真,旋转和平移矩阵,我们必须首先使用cv.projectPoints()将对象点转换为图像点。然后,我们可以计算出通过变换得到的绝对值和拐角发现算法之间的绝对值范数。为了找到平均误差,我们计算为所有校准图像计算的误差的算术平均值。

mean_error = 0
for i in xrange(len(objpoints)):
imgpoints2, _ = cv.projectPoints(objpoints[i], rvecs[i], tvecs[i], mtx, dist)
error = cv.norm(imgpoints[i], imgpoints2, cv.NORM_L2)/len(imgpoints2)
mean_error += error
print( "total error: {}".format(mean_error/len(objpoints)) )

姿态估计

先优化。然后使用函数cv.solvePnPRansac()计算旋转和平移。一旦有了这些变换矩阵,就可以使用它们将轴点投影到图像平面上。

for fname in glob.glob('left*.jpg'):
img = cv.imread(fname)
gray = cv.cvtColor(img,cv.COLOR_BGR2GRAY)
ret, corners = cv.findChessboardCorners(gray, (7,6),None)
if ret == True:
corners2 = cv.cornerSubPix(gray,corners,(11,11),(-1,-1),criteria)
# 找到旋转和平移矢量。
ret,rvecs, tvecs = cv.solvePnP(objp, corners2, mtx, dist)
# 将3D点投影到图像平面
imgpts, jac = cv.projectPoints(axis, rvecs, tvecs, mtx, dist)
img = draw(img,corners2,imgpts)
cv.imshow('img',img)
k = cv.waitKey(0) & 0xFF
if k == ord('s'):
cv.imwrite(fname[:6]+'.png', img)
cv.destroyAllWindows()

对极几何

首先我们需要在两个图像之间找到尽可能多的匹配项,以找到基本矩阵。为此,我们将SIFT描述符与基于FLANN的匹配器和比率测试结合使用。

import numpy as np
import cv2 as cv
from matplotlib import pyplot as plt
img1 = cv.imread('myleft.jpg',0) #索引图像 # left image
img2 = cv.imread('myright.jpg',0) #训练图像 # right image
sift = cv.SIFT()
# 使用SIFT查找关键点和描述符
kp1, des1 = sift.detectAndCompute(img1,None)
kp2, des2 = sift.detectAndCompute(img2,None)
# FLANN 参数
FLANN_INDEX_KDTREE = 1
index_params = dict(algorithm = FLANN_INDEX_KDTREE, trees = 5)
search_params = dict(checks=50)
flann = cv.FlannBasedMatcher(index_params,search_params)
matches = flann.knnMatch(des1,des2,k=2)
good = []
pts1 = []
pts2 = []
# 根据Lowe的论文进行比率测试
for i,(m,n) in enumerate(matches):
if m.distance < 0.8*n.distance:
good.append(m)
pts2.append(kp2[m.trainIdx].pt)
pts1.append(kp1[m.queryIdx].pt)

现在,我们获得了两张图片的最佳匹配列表。 让我们找到基本面矩阵。

pts1 = np.int32(pts1)
pts2 = np.int32(pts2)
F, mask = cv.findFundamentalMat(pts1,pts2,cv.FM_LMEDS)
# 我们只选择内点
pts1 = pts1[mask.ravel()==1]
pts2 = pts2[mask.ravel()==1]

接下来,我们找到Epilines。在第二张图像上绘制与第一张图像中的点相对应的Epilines。因此,在这里提到正确的图像很重要。我们得到了一行线。因此,我们定义了一个新功能来在图像上绘制这些线条。

def drawlines(img1,img2,lines,pts1,pts2):
''' img1 - 我们在img2相应位置绘制极点生成的图像
lines - 对应的极点 '''
r,c = img1.shape
img1 = cv.cvtColor(img1,cv.COLOR_GRAY2BGR)
img2 = cv.cvtColor(img2,cv.COLOR_GRAY2BGR)
for r,pt1,pt2 in zip(lines,pts1,pts2):
color = tuple(np.random.randint(0,255,3).tolist())
x0,y0 = map(int, [0, -r[2]/r[1] ])
x1,y1 = map(int, [c, -(r[2]+r[0]*c)/r[1] ])
img1 = cv.line(img1, (x0,y0), (x1,y1), color,1)
img1 = cv.circle(img1,tuple(pt1),5,color,-1)
img2 = cv.circle(img2,tuple(pt2),5,color,-1)
return img1,img2

现在,我们在两个图像中都找到了Epiline并将其绘制。

# 在右图(第二张图)中找到与点相对应的极点,然后在左图绘制极线
lines1 = cv.computeCorrespondEpilines(pts2.reshape(-1,1,2), 2,F)
lines1 = lines1.reshape(-1,3)
img5,img6 = drawlines(img1,img2,lines1,pts1,pts2)
# 在左图(第一张图)中找到与点相对应的Epilines,然后在正确的图像上绘制极线
lines2 = cv.computeCorrespondEpilines(pts1.reshape(-1,1,2), 1,F)
lines2 = lines2.reshape(-1,3)
img3,img4 = drawlines(img2,img1,lines2,pts2,pts1)
plt.subplot(121),plt.imshow(img5)
plt.subplot(122),plt.imshow(img3)
plt.show()

立体图像的深度图

import numpy as np
import cv2 as cv
from matplotlib import pyplot as plt
imgL = cv.imread('tsukuba_l.png',0)
imgR = cv.imread('tsukuba_r.png',0)
stereo = cv.StereoBM_create(numDisparities=16, blockSize=15)
disparity = stereo.compute(imgL,imgR)
plt.imshow(disparity,'gray')
plt.show()

结果受到高度噪声的污染。通过调整numDisparities和blockSize的值,可以获得更好的结果。
参数:

  • texture_threshold:过滤出纹理不足以进行可靠匹配
  • 区域斑点范围和大小:基于块的匹配器通常会在对象边界附近产生“斑点”,其中匹配窗口捕获一侧的前景和背景
  • 在另一场景中,匹配器似乎还在桌子上投影的纹理中找到小的虚假匹配项。为了消除这些伪像,我们使用由speckle_size和speckle_range参数控制的散斑滤镜对视差图像进行后处理。speckle_size是将视差斑点排除为“斑点”的像素数。speckle_range控制必须将值差异视为同一对象的一部分的程度
  • 视差数量:滑动窗口的像素数。它越大,可见深度的范围就越大,但是需要更多的计算
  • min_disparity:从开始搜索的左像素的x位置开始的偏移量
  • uniqueness_ratio:另一个后过滤步骤。如果最佳匹配视差不足够好于搜索范围中的所有其他视差,则将像素滤出。如果texture_threshold和斑点过滤仍在
  • 通过虚假匹配,则可以尝试进行调整
  • prefilter_size和prefilter_cap:预过滤阶段,可标准化图像亮度并增强纹理,以准备块匹配
  • 通常,你不需要调整这些
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
### 回答1: 双目相机是利用两个摄像头分别拍摄同一场景,从而获取不同视角下的图像信息,以实现深度信息的获取。三维重建是指通过图像处理技术将拍摄得到的二维图像转化为三维模型的过程。 在实现双目相机三维重建的过程中,可以使用OpenCV和PCL这两个开源库。OpenCV是一款计算机视觉库,提供了许多图像处理和计算机视觉相关的函数和工具,比如图像读取和处理、特征提取和匹配等。PCL是一款点云处理库,能够处理三维点云数据,提供了点云滤波、分割、配准和特征提取等功能。 具体步骤如下: 1. 获取双目相机的图像,进行标定。标定可以校准摄像头对应的内参矩阵和外参矩阵,以保证匹配时的准确度。 2. 通过绝对/相对模板匹配获取左右匹配的特征点。之后可以使用立体匹配算法(例如SGBM算法)计算出匹配点的视差(即左右视图在深度方向上的偏差),根据视差反向计算出点的深度。 3. 将获取的深度点云数据使用PCL进行处理,如点云滤波、重采样、分割等。之后可以使用PCL提供的立体配准算法对左右图像进行配准,基于此获取的点云数据中的关键点,进行特征点匹配,从而实现三维重建。 总之,双目相机三维重建opencv-pcl结合使用能够高效地完成三维重建任务,这是一个较为复杂的过程,需要仔细设计,注意参数设置和优化算法。 ### 回答2: 双目相机三维重建是利用双目相机获取的两幅图像,通过计算机视觉算法对相机观察到的场景进行三维重建的技术。OpenCV是一种开源的计算机视觉库,提供了许多图像处理和计算机视觉算法,PCL(PointCloud Library)则是一种开源的点云处理库,提供了各种点云相关的处理算法。 通过结合OpenCV和PCL,我们可以实现双目相机的三维重建。首先,需要利用OpenCV对双目相机获取的两幅图像进行立体匹配,得到两幅图像中对应像素点的视差。然后,通过视差计算相机与场景物体之间的距离信息,并将其转化为点云数据。最后,利用PCL对点云数据进行处理,实现三维重建。 具体的步骤包括: 1.读取左右相机的图像并进行预处理,包括图像去畸变和校正,以及调整图像的大小和尺度等。 2.使用OpenCV的立体匹配算法对左右相机图像进行匹配,得到像素点的视差图像。 3.通过三角化算法将视差信息转化为深度信息,并将深度信息转换为点云数据。 4.利用PCL对点云数据进行后续处理,包括点云滤波、点云重建和点云配准等。 5.最终得到的结果是场景的三维模型,可以对其进行渲染和可视化等操作。 总之,双目相机的三维重建是一项复杂的技术,在实践过程中需要综合运用计算机视觉、图像处理和点云处理等多个领域的知识和算法,但是对于建模、制造等领域来说,这是一项非常重要的技术。 ### 回答3: 双目相机三维重建是一种利用双目相机获取的视差数据来进行三维物体建模的技术。这种技术可以被应用到多个领域,如机器人导航、自动驾驶、医疗影像等。OpenCV是一个强大的计算机视觉库,支持多种计算机视觉算法和工具,能够方便地进行图像处理、特征提取和目标跟踪。而PCL是点云库,提供了处理点云数据的算法、工具和可视化功能。 实现双目相机三维重建,需要使用OpenCV和PCL。首先,利用双目相机捕获的两幅图像计算出视差图。然后,使用OpenCV提供的函数或者自定义算法,将视差图转换为深度图。接着,利用PCL提供的点云算法,将深度图转换为点云数据。最后,利用PCL的可视化工具,对点云数据进行可视化展示或二次处理。 在使用OpenCV和PCL进行双目相机三维重建时,需要注意几个关键点。首先,在捕获图像前需要进行相机标定,获取相机内参和外参等参数。这是很重要的,因为只有相机准确校准后,才能保证三维建模的精度和稳定性。其次,在计算视差图和深度图时,需要选择适合的计算方法和参数。这些参数可能会受到图像分辨率、灰度分布和光照等因素的影响。最后,在进行点云处理和可视化时,需要选择合适的算法和工具,以免因数据量过大或算法不精确而影响计算效率和准确性。 总之,双目相机三维重建结合OpenCV和PCL,是一种强大的三维重建技术。它可以应用到多个领域,包括机器人导航、自动驾驶、医疗影像等。在实现过程中,需要根据具体实际情况进行合理选择和优化,以保证算法的精度、效率和稳定性。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

神仙别闹

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值