OpenCV 三维重建实战:从工业检测到自动驾驶,3 大场景代码全解析

  1. 工业零部件三维建模与检测
    • 案例背景:在汽车制造工厂,对于复杂形状的发动机零部件质量检测与逆向工程需求,需要高精度的三维模型。传统检测方法效率低且精度有限,而三维重建技术可快速获取零部件三维信息,实现高效检测与设计优化。
    • 技术实现:使用多个相机从不同角度拍摄零部件,利用calib3d模块进行相机标定,获取准确的相机内参和外参。通过特征点检测与匹配算法(如SIFT、ORB等)找到不同图像间的对应点,再用findEssentialMat计算本质矩阵,recoverPose恢复相机姿态,最后用triangulatePoints重建零部件的三维点云。
    • 应用价值:通过对比重建模型与标准模型,能快速检测出零部件的尺寸偏差、表面缺陷等问题,精度可达亚毫米级。还可基于重建模型进行逆向工程,优化产品设计,缩短研发周期,提高生产效率与产品质量。
  2. 文化遗产数字化保护与修复
    • 案例背景:面对历史悠久的古建筑和文物,自然侵蚀与人为破坏威胁其存在。为长期保存和研究,需要数字化手段记录其详细信息,为修复提供精准数据支持。
    • 技术实现:在古建筑或文物现场,布置多个相机进行全方位拍摄。借助calib3d模块标定相机,通过特征匹配算法确定不同图像间对应关系,利用stereoCalibratestereoRectify对双目相机或多目相机进行校准和校正,获取视差图,再用reprojectImageTo3D将视差图转换为三维点云,构建高精度三维模型。
    • 应用价值:完整记录文化遗产的外观和结构信息,为修复提供精确尺寸和形状数据,帮助文物保护工作者制定科学修复方案。还能基于三维模型开发虚拟展示项目,让公众更直观了解文化遗产,促进文化传承与保护。
  3. 自动驾驶场景感知与地图构建
    • 案例背景:自动驾驶汽车需要实时感知周围环境,构建高精度地图以实现路径规划和安全行驶。准确的三维重建能帮助汽车识别障碍物、判断距离和预测物体运动轨迹。
    • 技术实现:汽车上安装多个摄像头,结合激光雷达等传感器。利用calib3d模块标定相机,通过视觉里程计算法(如基于特征点匹配和相机位姿估计)确定汽车的运动轨迹,用solvePnP计算物体的三维位置,结合激光雷达数据进行融合,构建精确的三维地图。
    • 应用价值:提升自动驾驶系统的环境感知能力,增强对复杂路况和障碍物的识别与应对能力,提高行驶安全性。精确的三维地图为路径规划提供更准确信息,优化行驶路线,提高出行效率,推动自动驾驶技术发展。 ### OpenCV calib3d模块三维重建实际案例
案例1:基于双目相机的室内场景三维重建

这是一个使用双目相机进行室内场景三维重建的完整流程,包括相机标定、立体校正、视差计算和点云生成:

import cv2
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D

# 1. 相机标定(假设已经完成,直接加载标定结果)
def load_calibration_results():
    # 加载左右相机内参和畸变系数
    left_camera_matrix = np.array([
        [832.73, 0, 318.22],
        [0, 831.99, 242.15],
        [0, 0, 1]
    ])
    left_dist_coeffs = np.array([-0.365, 0.132, 0.001, 0.002, -0.027])
    
    right_camera_matrix = np.array([
        [830.47, 0, 321.54],
        [0, 829.87, 245.11],
        [0, 0, 1]
    ])
    right_dist_coeffs = np.array([-0.371, 0.139, 0.003, 0.001, -0.029])
    
    # 双目相机关系参数
    R = np.array([
        [0.9998, -0.0153, 0.0104],
        [0.0153, 0.9999, -0.0006],
        [-0.0104, 0.0007, 0.9999]
    ])
    T = np.array([-119.97, 0.35, 1.22])  # 基线距离(毫米)
    
    return left_camera_matrix, left_dist_coeffs, right_camera_matrix, right_dist_coeffs, R, T

# 2. 双目相机立体校正
def stereo_rectify(left_img, right_img):
    # 加载标定结果
    K1, D1, K2, D2, R, T = load_calibration_results()
    
    # 图像尺寸
    h, w = left_img.shape[:2]
    
    # 计算校正变换
    R1, R2, P1, P2, Q, validPixROI1, validPixROI2 = cv2.stereoRectify(
        K1, D1, K2, D2, (w, h), R, T, alpha=0)
    
    # 生成校正映射
    map1x, map1y = cv2.initUndistortRectifyMap(K1, D1, R1, P1, (w, h), cv2.CV_32FC1)
    map2x, map2y = cv2.initUndistortRectifyMap(K2, D2, R2, P2, (w, h), cv2.CV_32FC1)
    
    # 应用校正
    rectified_left = cv2.remap(left_img, map1x, map1y, cv2.INTER_LINEAR)
    rectified_right = cv2.remap(right_img, map2x, map2y, cv2.INTER_LINEAR)
    
    return rectified_left, rectified_right, Q

# 3. 计算视差图
def compute_disparity(left_img, right_img):
    # 使用StereoSGBM算法(更精确但速度较慢)
    window_size = 3
    min_disp = 0
    num_disp = 112 - min_disp
    stereo = cv2.StereoSGBM_create(
        minDisparity=min_disp,
        numDisparities=num_disp,
        blockSize=5,
        P1=8 * 3 * window_size ** 2,
        P2=32 * 3 * window_size ** 2,
        disp12MaxDiff=1,
        uniquenessRatio=15,
        speckleWindowSize=0,
        speckleRange=2,
        preFilterCap=63,
        mode=cv2.STEREO_SGBM_MODE_SGBM_3WAY
    )
    
    # 计算视差
    disparity = stereo.compute(left_img, right_img).astype(np.float32) / 16.0
    
    # 归一化视差图以便显示
    disparity_normalized = cv2.normalize(disparity, None, 0, 255, cv2.NORM_MINMAX, dtype=cv2.CV_8U)
    
    return disparity, disparity_normalized

# 4. 从视差图生成点云
def generate_point_cloud(disparity, Q, left_img=None):
    # 从视差图和校正矩阵计算3D点
    points_3d = cv2.reprojectImageTo3D(disparity, Q)
    
    # 如果提供了左图像,则使用颜色信息
    if left_img is not None:
        colors = cv2.cvtColor(left_img, cv2.COLOR_BGR2RGB)
        mask = disparity > disparity.min()
        out_points = points_3d[mask]
        out_colors = colors[mask]
    else:
        mask = disparity > disparity.min()
        out_points = points_3d[mask]
        out_colors = None
    
    return out_points, out_colors

# 5. 可视化点云(使用matplotlib)
def visualize_point_cloud(points, colors=None):
    fig = plt.figure(figsize=(10, 8))
    ax = fig.add_subplot(111, projection='3d')
    
    # 设置坐标轴范围
    max_range = np.array([
        points[:, 0].max() - points[:, 0].min(),
        points[:, 1].max() - points[:, 1].min(),
        points[:, 2].max() - points[:, 2].min()
    ]).max() / 2.0
    
    mid_x = (points[:, 0].max() + points[:, 0].min()) * 0.5
    mid_y = (points[:, 1].max() + points[:, 1].min()) * 0.5
    mid_z = (points[:, 2].max() + points[:, 2].min()) * 0.5
    
    ax.set_xlim(mid_x - max_range, mid_x + max_range)
    ax.set_ylim(mid_y - max_range, mid_y + max_range)
    ax.set_zlim(mid_z - max_range, mid_z + max_range)
    
    # 绘制点云
    if colors is not None:
        ax.scatter(points[:, 0], points[:, 1], points[:, 2], c=colors/255.0, s=2)
    else:
        ax.scatter(points[:, 0], points[:, 1], points[:, 2], c='b', s=2)
    
    ax.set_xlabel('X')
    ax.set_ylabel('Y')
    ax.set_zlabel('Z')
    plt.title('3D Point Cloud')
    plt.show()

# 主函数
def main():
    # 读取双目图像
    left_img = cv2.imread('left_image.jpg')
    right_img = cv2.imread('right_image.jpg')
    
    # 确保图像为灰度图
    if len(left_img.shape) == 3:
        left_gray = cv2.cvtColor(left_img, cv2.COLOR_BGR2GRAY)
    else:
        left_gray = left_img
    
    if len(right_img.shape) == 3:
        right_gray = cv2.cvtColor(right_img, cv2.COLOR_BGR2GRAY)
    else:
        right_gray = right_img
    
    # 立体校正
    rectified_left, rectified_right, Q = stereo_rectify(left_gray, right_gray)
    
    # 计算视差
    disparity, disparity_normalized = compute_disparity(rectified_left, rectified_right)
    
    # 生成点云
    points, colors = generate_point_cloud(disparity, Q, left_img)
    
    # 可视化结果
    cv2.imshow('Left Image', left_img)
    cv2.imshow('Right Image', right_img)
    cv2.imshow('Rectified Left', rectified_left)
    cv2.imshow('Rectified Right', rectified_right)
    cv2.imshow('Disparity Map', disparity_normalized)
    cv2.waitKey(0)
    cv2.destroyAllWindows()
    
    # 显示点云
    visualize_point_cloud(points, colors)

if __name__ == "__main__":
    main()
案例2:基于单目相机的运动恢复结构(SfM)

下面是一个简化的单目SfM流程,通过多幅图像重建3D场景:

import cv2
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
from scipy.spatial.transform import Rotation

# 1. 特征提取与匹配
def feature_detection_and_matching(img1, img2):
    # 使用ORB特征检测器
    orb = cv2.ORB_create()
    
    # 检测关键点和描述符
    kp1, des1 = orb.detectAndCompute(img1, None)
    kp2, des2 = orb.detectAndCompute(img2, None)
    
    # 使用BFMatcher匹配描述符
    bf = cv2.BFMatcher(cv2.NORM_HAMMING, crossCheck=True)
    matches = bf.match(des1, des2)
    
    # 按距离排序
    matches = sorted(matches, by=lambda x: x.distance)
    
    # 获取匹配点
    points1 = np.float32([kp1[m.queryIdx].pt for m in matches]).reshape(-1, 1, 2)
    points2 = np.float32([kp2[m.trainIdx].pt for m in matches]).reshape(-1, 1, 2)
    
    return points1, points2, matches, kp1, kp2

# 2. 计算本质矩阵和相机位姿
def estimate_pose(points1, points2, camera_matrix):
    # 计算本质矩阵
    E, mask = cv2.findEssentialMat(points1, points2, camera_matrix, method=cv2.RANSAC, prob=0.999, threshold=1.0)
    
    # 从本质矩阵恢复旋转和平移
    points, R, t, mask = cv2.recoverPose(E, points1, points2, camera_matrix)
    
    return E, R, t, mask

# 3. 三角测量恢复3D点
def triangulate_points(points1, points2, R1, t1, R2, t2, camera_matrix):
    # 构建投影矩阵
    P1 = np.dot(camera_matrix, np.hstack((R1, t1)))
    P2 = np.dot(camera_matrix, np.hstack((R2, t2)))
    
    # 三角测量
    points_4d = cv2.triangulatePoints(P1, P2, points1.T, points2.T)
    points_3d = points_4d[:3, :] / points_4d[3, :]
    
    return points_3d.T

# 4. 主重建流程
def main():
    # 相机内参矩阵(示例值,需根据实际相机标定)
    camera_matrix = np.array([
        [800, 0, 320],
        [0, 800, 240],
        [0, 0, 1]
    ])
    
    # 读取图像序列
    img1 = cv2.imread('image1.jpg')
    img2 = cv2.imread('image2.jpg')
    img3 = cv2.imread('image3.jpg')
    
    # 转为灰度图
    gray1 = cv2.cvtColor(img1, cv2.COLOR_BGR2GRAY)
    gray2 = cv2.cvtColor(img2, cv2.COLOR_BGR2GRAY)
    gray3 = cv2.cvtColor(img3, cv2.COLOR_BGR2GRAY)
    
    # 第一对图像的特征匹配和位姿估计
    points1_1, points1_2, matches1, kp1_1, kp1_2 = feature_detection_and_matching(gray1, gray2)
    E1, R1, t1, mask1 = estimate_pose(points1_1, points1_2, camera_matrix)
    
    # 第二对图像的特征匹配和位姿估计
    points2_1, points2_2, matches2, kp2_1, kp2_2 = feature_detection_and_matching(gray2, gray3)
    E2, R2, t2, mask2 = estimate_pose(points2_1, points2_2, camera_matrix)
    
    # 初始相机位姿(第一幅图像为世界坐标系原点)
    R_cam1 = np.eye(3)
    t_cam1 = np.zeros((3, 1))
    
    # 第二幅图像的相机位姿
    R_cam2 = R1
    t_cam2 = t1
    
    # 第三幅图像的相机位姿(相对于第二幅图像)
    R_cam3 = np.dot(R2, R_cam2)
    t_cam3 = np.dot(R2, t_cam2) + t2
    
    # 三角测量第一对图像的3D点
    points_3d_1 = triangulate_points(
        points1_1[mask1.ravel() > 0].reshape(-1, 2),
        points1_2[mask1.ravel() > 0].reshape(-1, 2),
        R_cam1, t_cam1, R_cam2, t_cam2, camera_matrix
    )
    
    # 三角测量第二对图像的3D点(只考虑与第一对图像的共同特征)
    # 实际应用中需要进行特征跟踪和 Bundle Adjustment
    
    # 可视化点云和相机位姿
    fig = plt.figure(figsize=(10, 8))
    ax = fig.add_subplot(111, projection='3d')
    
    # 绘制3D点
    ax.scatter(points_3d_1[:, 0], points_3d_1[:, 1], points_3d_1[:, 2], c='b', s=2)
    
    # 绘制相机位置
    ax.scatter(0, 0, 0, c='r', s=100, marker='^')  # 相机1
    
    # 绘制相机2的位置和朝向
    cam2_pos = -np.dot(R_cam2.T, t_cam2).flatten()
    ax.scatter(cam2_pos[0], cam2_pos[1], cam2_pos[2], c='g', s=100, marker='^')
    
    # 绘制相机3的位置和朝向
    cam3_pos = -np.dot(R_cam3.T, t_cam3).flatten()
    ax.scatter(cam3_pos[0], cam3_pos[1], cam3_pos[2], c='m', s=100, marker='^')
    
    # 设置坐标轴范围
    max_range = np.array([
        points_3d_1[:, 0].max() - points_3d_1[:, 0].min(),
        points_3d_1[:, 1].max() - points_3d_1[:, 1].min(),
        points_3d_1[:, 2].max() - points_3d_1[:, 2].min()
    ]).max() / 2.0
    
    mid_x = (points_3d_1[:, 0].max() + points_3d_1[:, 0].min()) * 0.5
    mid_y = (points_3d_1[:, 1].max() + points_3d_1[:, 1].min()) * 0.5
    mid_z = (points_3d_1[:, 2].max() + points_3d_1[:, 2].min()) * 0.5
    
    ax.set_xlim(mid_x - max_range, mid_x + max_range)
    ax.set_ylim(mid_y - max_range, mid_y + max_range)
    ax.set_zlim(mid_z - max_range, mid_z + max_range)
    
    ax.set_xlabel('X')
    ax.set_ylabel('Y')
    ax.set_zlabel('Z')
    plt.title('3D Reconstruction with SfM')
    plt.show()

if __name__ == "__main__":
    main()
案例3:基于ArUco标记的物体位姿估计

下面是一个使用ArUco标记进行物体位姿估计的示例,可用于AR增强现实应用:

import cv2
import numpy as np

# 1. 加载相机标定参数(假设已经完成标定)
def load_camera_params():
    # 相机内参矩阵
    camera_matrix = np.array([
        [832.73, 0, 318.22],
        [0, 831.99, 242.15],
        [0, 0, 1]
    ])
    
    # 畸变系数
    dist_coeffs = np.array([-0.365, 0.132, 0.001, 0.002, -0.027])
    
    return camera_matrix, dist_coeffs

# 2. 检测ArUco标记并估计位姿
def detect_aruco_and_estimate_pose(image):
    # 加载相机参数
    camera_matrix, dist_coeffs = load_camera_params()
    
    # 创建ArUco字典和参数
    aruco_dict = cv2.aruco.Dictionary_get(cv2.aruco.DICT_6X6_250)
    aruco_params = cv2.aruco.DetectorParameters_create()
    
    # 检测ArUco标记
    corners, ids, rejected = cv2.aruco.detectMarkers(image, aruco_dict, parameters=aruco_params)
    
    # 如果检测到标记
    if ids is not None and len(ids) > 0:
        # 绘制检测到的标记
        image = cv2.aruco.drawDetectedMarkers(image.copy(), corners, ids)
        
        # 标记大小(米)
        marker_size = 0.05  # 5厘米
        
        # 估计每个标记的位姿
        rvecs, tvecs, _ = cv2.aruco.estimatePoseSingleMarkers(corners, marker_size, camera_matrix, dist_coeffs)
        
        # 绘制每个标记的坐标轴
        for i in range(len(ids)):
            cv2.aruco.drawAxis(image, camera_matrix, dist_coeffs, rvecs[i], tvecs[i], 0.1)  # 绘制0.1米长的坐标轴
            
            # 打印每个标记的位置和姿态
            print(f"Marker ID: {ids[i][0]}")
            print(f"Position (x, y, z): {tvecs[i][0]}")
            
            # 旋转向量转旋转矩阵
            R, _ = cv2.Rodrigues(rvecs[i])
            # 旋转矩阵转欧拉角
            r = Rotation.from_matrix(R)
            euler_angles = r.as_euler('xyz', degrees=True)
            print(f"Orientation (roll, pitch, yaw): {euler_angles}")
            print("-------------------")
    
    return image

# 3. 主函数(从摄像头捕获视频)
def main():
    # 打开摄像头
    cap = cv2.VideoCapture(0)
    
    while True:
        # 读取一帧
        ret, frame = cap.read()
        if not ret:
            break
        
        # 检测ArUco标记并估计位姿
        result = detect_aruco_and_estimate_pose(frame)
        
        # 显示结果
        cv2.imshow('ArUco Marker Detection', result)
        
        # 按ESC键退出
        if cv2.waitKey(1) & 0xFF == 27:
            break
    
    # 释放资源
    cap.release()
    cv2.destroyAllWindows()

if __name__ == "__main__":
    main()

应用领域案例

1. 工业自动化中的机器人视觉引导
  • 任务:机器人抓取不规则物体
  • 技术:双目立体视觉系统计算物体3D位置
  • 实现
    • 使用两个相机构建立体视觉系统
    • 标定相机并计算相对位置关系
    • 计算物体表面点云
    • 提取物体轮廓和特征点
    • 规划机器人抓取路径
2. 文化遗产数字化保护
  • 任务:对古建筑进行高精度3D建模
  • 技术:多视图立体重建(MVS)
  • 实现
    • 从不同角度拍摄古建筑照片
    • 使用SfM技术估计相机位姿
    • 密集匹配生成点云
    • 点云处理与三角网格化
    • 纹理映射生成真实感模型
3. 增强现实游戏开发
  • 任务:在真实场景中叠加虚拟物体
  • 技术:基于标记或无标记的位姿估计
  • 实现
    • 使用ArUco或AprilTag等标记检测
    • 计算相机相对于标记的位姿
    • 根据位姿在正确位置渲染虚拟物体
    • 处理阴影和光照效果增强真实感
4. 自动驾驶环境感知
  • 任务:检测道路、车辆和障碍物
  • 技术:双目立体视觉+深度学习
  • 实现
    • 双目相机计算视差图
    • 从视差图生成点云
    • 点云分割识别地面、车辆和行人
    • 结合深度学习语义分割增强识别精度
    • 构建环境3D模型用于路径规划

通过这些案例可以看出,OpenCV的calib3d模块为计算机视觉中的3D重建提供了基础工具,结合其他技术(如深度学习)可以构建更强大的应用系统。

你可能感兴趣的:(opencv,自动驾驶,数码相机)