使用OpenCV undistortPoints与triangulatePoints出现异常结果的排查请求
双摄像头三角化3D坐标异常问题排查
操作流程
我搭建双摄像头系统,尝试用OpenCV三角化功能获取物体3D坐标,操作步骤如下:
1. 单相机标定
通过cv2.calibrateCamera分别标定左右相机,获取内参矩阵和畸变系数:
_, left_camera_intrinsic_matrix, left_camera_distortion_coefficients, _, _ = cv2.calibrateCamera( obj_points_left, img_points_left, left_shape, None, None) _, right_camera_intrinsic_matrix, right_camera_distortion_coefficients, _, _ = cv2.calibrateCamera( obj_points_right, img_points_right, right_shape, None, None)
2. 立体标定求解外参
用cv2.stereoCalibrate计算左相机相对右相机的旋转矩阵和平移向量:
_, _, _, _, _, rot, trans, _, _ = cv2.stereoCalibrate(obj_points, img_points_left, img_points_right, left_camera_intrinsic_matrix, left_camera_distortion_coefficients, right_camera_intrinsic_matrix, right_camera_distortion_coefficients, left_shape) left_camera_rotation, _ = cv2.Rodrigues(rot) left_camera_position = trans
3. 获取校正投影矩阵
通过cv2.stereoRectify生成校正后的投影矩阵:
right_camera_position = (0, 0, 0) right_camera_rotation = (0, 0, 0) right_projection_matrix, _ = cv2.Rodrigues(right_camera_rotation) right_projection_matrix = np.hstack( (right_projection_matrix, np.array(right_camera_position).reshape(-1, 1))) left_projection_matrix = np.hstack((rot, left_camera_position.reshape(-1, 1))) _, _, rectified_right_prj_mtx, rectified_left_prj_mtx, _, _, _ = \ cv2.stereoRectify(right_camera_intrinsic_matrix, right_camera_distortion_coefficients, left_camera_intrinsic_matrix, left_camera_distortion_coefficients, left_shape, rot, left_camera_position)
4. 逐帧三角化计算3D坐标
对视频每一帧执行去畸变和三角化:
right = cv2.undistortPoints(right_points, right_camera_intrinsic_matrix, right_camera_distortion_coefficients, rectified_right_prj_mtx) left = cv2.undistortPoints(left_points, left_camera_intrinsic_matrix, left_camera_distortion_coefficients, rectified_left_prj_mtx) # 三角化求解3D点 homogeneous_points = \ cv2.triangulatePoints(right_projection_matrix, left_projection_matrix, right, left) points_3d = cv2.convertPointsFromHomogeneous(homogeneous_points.T)
异常现象与疑问
测试时将6×4棋盘格移近右相机再移远,出现棋盘格弯曲、表观尺寸大幅变化的异常。
疑问:
- 棋盘格为何弯曲?相机畸变不是应该由
cv2.undistortPoints处理吗? - 是什么导致棋盘格表观尺寸大幅变化?透视畸变不会如此明显吧?
- 操作流程中是否存在根本性错误?
补充说明
- 单相机标定各用约60张不同角度、距离的图像;立体标定也用约60张图像,少量图像测试结果类似
- 相机夹角约50度,尝试过多种布局,异常现象始终存在(仅棋盘格平行于某一相机移动时尺寸变化较小,但仍有弯曲)
- 两台相机型号、分辨率一致
已尝试无效的方法
- 用
cv2.stereoRectifyUncalibrated替代cv2.stereoRectify - 更换标定图像
- 先对图像去畸变,而非使用
cv2.undistortPoints
问题分析与解决
1. 棋盘格弯曲的原因
cv2.undistortPoints参数误用:你将立体校正后的投影矩阵作为第四个参数传入,该参数的作用是将去畸变后的点投影到新图像平面,而非单纯去畸变。这样会同时完成去畸变和立体校正投影,若立体校正参数或点匹配存在误差,就会导致点的位置扭曲,最终三角化出的3D点呈现弯曲。
正确做法是:先仅做去畸变(不传入P矩阵,或传入内参矩阵),再应用立体校正映射;或直接用cv2.initUndistortRectifyMap生成映射表,对整幅图像做去畸变+立体校正后再提取点,保证点的几何关系正确。
2. 尺寸大幅变化的根源
投影矩阵构建错误:
- 手动构建的
right_projection_matrix和left_projection_matrix未结合内参矩阵。投影矩阵的正确形式是P = K * [R | t](K为内参,[R|t]为外参组合矩阵),你现在的投影矩阵仅包含外参,缺少内参,导致三角化尺度完全错误,3D点尺寸出现异常波动。 cv2.stereoRectify输出的rectified_right_prj_mtx和rectified_left_prj_mtx是包含内参和校正后外参的完整投影矩阵,应直接用于cv2.triangulatePoints,无需手动构建。
3. 流程中的根本性错误
核心错误有两处:
- 投影矩阵缺失内参:手动构建的投影矩阵未结合内参,导致三角化尺度错误。
- undistortPoints参数误用:传入立体校正投影矩阵,引发点的几何扭曲。
- 立体校正参数逻辑混乱:
cv2.stereoRectify输出的投影矩阵可直接使用,无需手动重新构建。
修正后的流程建议
- 单相机标定:保持现有步骤,用重投影误差验证标定质量(平均误差应低于1像素)。
- 立体标定:保持现有步骤,同样验证立体标定的重投影误差。
- 生成立体校正映射:使用
cv2.initUndistortRectifyMap生成去畸变+立体校正的映射表:
# 执行立体校正计算 R1, R2, P1, P2, Q, _, _ = cv2.stereoRectify( left_camera_intrinsic_matrix, left_camera_distortion_coefficients, right_camera_intrinsic_matrix, right_camera_distortion_coefficients, left_shape, rot, trans, flags=cv2.CALIB_ZERO_DISPARITY, alpha=1.0 ) # 生成左右相机的校正映射 left_map_x, left_map_y = cv2.initUndistortRectifyMap( left_camera_intrinsic_matrix, left_camera_distortion_coefficients, R1, P1, left_shape, cv2.CV_32FC1 ) right_map_x, right_map_y = cv2.initUndistortRectifyMap( right_camera_intrinsic_matrix, right_camera_distortion_coefficients, R2, P2, left_shape, cv2.CV_32FC1 )
- 逐帧处理:先校正图像,再提取点,最后三角化:
# 对左右帧做去畸变+立体校正 rectified_left = cv2.remap(left_frame, left_map_x, left_map_y, cv2.INTER_LINEAR) rectified_right = cv2.remap(right_frame, right_map_x, right_map_y, cv2.INTER_LINEAR) # 在校正后的图像上提取对应点(left_points_rectified, right_points_rectified) # 使用stereoRectify输出的P1、P2作为投影矩阵三角化 homogeneous_points = cv2.triangulatePoints(P1, P2, left_points_rectified, right_points_rectified) points_3d = cv2.convertPointsFromHomogeneous(homogeneous_points.T)
额外注意事项
- 确保左右相机的点严格一一对应,匹配错误会直接导致三角化结果异常。
- 50度相机夹角较大,立体匹配难度上升,需保证棋盘格在双相机视野中清晰可见,角点提取准确。
- 立体标定时,棋盘格摆放需覆盖相机大部分视野,尤其是边缘区域,提升外参标定精度。
内容的提问来源于stack exchange,提问作者land O
相关产品推荐
相关产品推荐

