OpenCV单目相机校准后3D物体测量精度异常排查求助
问题背景
已通过OpenCV棋盘格法完成固定单目网络摄像头的标定,获取内参mtx、畸变系数dist、旋转向量rvecs、平移向量tvecs,且相机深度为已知恒定值。校准阶段已将世界坐标通过乘以棋盘格方格边长转为毫米单位,但用标定参数测量盒子长度时结果不准确,尽管重投影误差仅0.02%。
关键代码片段
- 摄像头标定
ret, mtx, dist, rvecs, tvecs = cv.calibrateCamera(objpoints, imgpoints, gray.shape[::-1], None, None)
- 世界坐标单位转换(转为毫米)
objp[:, :2] = np.mgrid[0:6, 0:9].T.reshape(-1, 2)*square_size
- 图像点转相机坐标系点
img_end = internal_inverse.dot(np.array(point_end)) img_end *= depth_value
- 相机坐标系点转世界坐标系点
world_coords_end = R_inv.dot(img_end) + R_inv.dot(t_neg)
- 重投影误差计算
errorx = np.mean([(abs(jx - ix)/jx)*100 for (ix, _), (jx, _) in zip(reprojected_points, imgpoints_checkproject)])
问题根源排查方向
深度值的单位与精度问题
确认depth_value的单位是否和世界坐标(毫米)完全一致,若深度以厘米/米为单位直接相乘,会导致缩放倍数错误;同时检查深度测量是否真的完全恒定,微小的深度波动都会直接影响坐标计算结果。齐次坐标处理错误
图像点转齐次坐标时,必须确保point_end是[u, v, 1]的三维形式,若输入仅为[u, v]的二维坐标,直接与内参逆矩阵相乘会导致维度不匹配或计算偏差。坐标系转换公式错误
世界坐标系到相机坐标系的转换公式为P_cam = R * P_world + t,逆变换应为P_world = R_inv.dot(P_cam) - R_inv.dot(t)。需确认代码中t_neg是否为-tvecs,若t_neg定义错误(未取负),会导致平移量计算完全偏离;此外,固定摄像头场景下,必须使用对应测量帧的旋转/平移参数,而非任意标定帧的rvecs/tvecs。重投影误差计算逻辑缺陷
当前仅统计x方向的相对误差,且以原始图像点x坐标为分母,若jx数值过小会导致误差被异常放大,无法真实反映标定精度。正确的重投影误差应计算重投影点与原始点的欧氏距离均值:total_error = 0 for i in range(len(objpoints)): imgpoints2, _ = cv.projectPoints(objpoints[i], rvecs[i], tvecs[i], mtx, dist) error = cv.norm(imgpoints[i], imgpoints2, cv.NORM_L2)/len(imgpoints2) total_error += error mean_error = total_error/len(objpoints)畸变校正遗漏
处理测量的图像点point_end前,必须先通过cv.undistortPoints消除畸变,直接使用原始像素坐标计算会引入系统误差:undistorted_point = cv.undistortPoints(np.array([point_end]), mtx, dist, None, mtx) # 再将校正后的点转齐次坐标进行后续计算
内容的提问来源于stack exchange,提问作者Nikhil

