前言
3D重建是计算机视觉领域的一项关键技术,它能够从二维图像中恢复出场景的三维结构。这项技术在机器人视觉、增强现实和3D建模等多个领域都有广泛的应用。在本篇技术博客中,我们将详细介绍如何使用OpenCV这一流行的计算机视觉库来实现3D重建。我们将逐步探讨整个过程,包括相机标定、特征点提取与匹配、立体校正、深度图计算以及最终的三维重建。
准备工作
在开始之前,请确保你已经安装了OpenCV。可以通过以下命令通过pip安装:
pip install opencv-python opencv-contrib-python
第一步:相机标定
相机标定是3D重建的第一步,也是最关键的一步。它涉及确定相机的内部参数(例如焦距和主点)以及外部参数(旋转和平移)。
示例代码
import cv2
import numpy as np
# 准备对象点(真实世界空间中的3D点)
object_points = np.zeros((6*7, 3), np.float32)
object_points[:, :2] = np.mgrid[0:7, 0:6].T.reshape(-1, 2)
# 存储所有图像的对象点和图像点的数组
object_points_list = []
image_points_list = []
# 加载图像并找到棋盘格角点
for i in range(1, 13): # 假设我们有12张图像
img = cv2.imread(f'calibration_image_{i}.jpg')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
ret, corners = cv2.findChessboardCorners(gray, (7, 6), None)
if ret:
object_points_list.append(object_points)
image_points_list.append(corners)
img = cv2.drawChessboardCorners(img, (7, 6), corners, ret)
cv2.imshow('img', img)
cv2.waitKey(500)
cv2.destroyAllWindows()
# 相机标定
ret, camera_matrix, dist_coeffs, rvecs, tvecs = cv2.calibrateCamera(
object_points_list, image_points_list, gray.shape[::-1], None, None
)
print("相机矩阵:\n", camera_matrix)
print("畸变系数:\n", dist_coeffs)
第二步:特征点提取与匹配
要从两张图像中重建3D场景,我们需要找到图像中的对应点。OpenCV提供了多种特征检测器和匹配器来完成这个任务。
示例代码
# 使用ORB进行特征检测和匹配
orb = cv2.ORB_create()
keypoints1, descriptors1 = orb.detectAndCompute(image1, None)
keypoints2, descriptors2 = orb.detectAndCompute(image2, None)
matcher = cv2.BFMatcher(cv2.NORM_HAMMING, crossCheck=True)
matches = matcher.match(descriptors1, descriptors2)
matches = sorted(matches, key=lambda x: x.distance)
# 绘制匹配结果
matched_image = cv2.drawMatches(image1, keypoints1, image2, keypoints2, matches[:10], None, flags=cv2.DrawMatchesFlags_NOT_DRAW_SINGLE_POINTS)
cv2.imshow('Matches', matched_image)
cv2.waitKey(0)
cv2.destroyAllWindows()
第三步:立体校正
立体校正将图像对齐,使 epipolar 线水平,从而简化立体匹配过程。
示例代码
# 立体校正
R1, R2, P1, P2, Q, _, _ = cv2.stereoRectify(
camera_matrix, dist_coeffs, camera_matrix, dist_coeffs, gray.shape[::-1], R, t
)
# 计算校正映射
map1x, map1y = cv2.initUndistortRectifyMap(camera_matrix, dist_coeffs, R1, P1, gray.shape[::-1], cv2.CV_32FC1)
map2x, map2y = cv2.initUndistortRectifyMap(camera_matrix, dist_coeffs, R2, P2, gray.shape[::-1], cv2.CV_32FC1)
# 应用校正
image1_rectified = cv2.remap(image1, map1x, map1y, cv2.INTER_LINEAR)
image2_rectified = cv2.remap(image2, map2x, map2y, cv2.INTER_LINEAR)
第四步:深度图计算
深度图表示每个像素与相机的距离。OpenCV提供了几种立体匹配算法来计算深度图。
示例代码
# 深度图计算
stereo = cv2.StereoBM_create(numDisparities=16, blockSize=15)
disparity = stereo.compute(image1_rectified, image2_rectified)
# 归一化深度图以便显示
disparity = cv2.normalize(disparity, None, alpha=0, beta=255, norm_type=cv2.NORM_MINMAX, dtype=cv2.CV_8U)
cv2.imshow('Disparity Map', disparity)
cv2.waitKey(0)
cv2.destroyAllWindows()
第五步:三维重建
最后,我们可以使用深度图和相机参数来重建场景的三维结构。OpenCV提供了reprojectImageTo3D
函数,可以将深度图转换为三维点云。
示例代码
# 三维重建
points_3D = cv2.reprojectImageTo3D(disparity, Q)
colors = cv2.cvtColor(image1_rectified, cv2.COLOR_BGR2RGB)
mask = disparity > disparity.min()
out_points = points_3D[mask]
out_colors = colors[mask]
# 保存点云
import pptk
v = pptk.viewer(out_points)
v.attributes(out_colors / 255.0)
v.set(point_size=0.001)
总结
3D重建是一个复杂的过程,涉及多个步骤和多种算法。OpenCV提供了强大的工具来支持这一过程,从相机标定到特征点提取与匹配,再到立体校正和深度图计算,最后实现三维重建。通过合理使用这些工具,可以实现高质量的3D重建效果。希望这篇博客能帮助你更好地理解和实现3D重建项目。