为何OpenCV StereoCalibrate函数会修改作为输入传入的局部变量?
cv2.stereoCalibrate 运行时修改传入的numpy数组问题
问题复现
调用cv2.stereoCalibrate函数时,传入的numpy数组会被意外修改,以相机内参矩阵K01为例,复现代码如下:
self.K01, self.D01 = load_coefficients(path) self.K02, self.D02 = load_coefficients(path2) print('K01:', K01)
函数调用前打印的K01数值:
array([[2.31510096e+03, 0.00000000e+00, 7.21346331e+02], [0.00000000e+00, 2.31473055e+03, 5.00531281e+02], [0.00000000e+00, 0.00000000e+00, 1.00000000e+00]])
调用cv2.stereoCalibrate的代码段:
stereoParameters = cv2.stereoCalibrate(self.objpoints, self.left_imgpoints, self.right_imgpoints, self.K01, self.D01, self.K02, self.D02, self.image_format, flags = calib_flags, criteria=calib_criteria) print("Stereo calibration rms:", self.ret) print('K01 after stereoCalibrate',self.K01)
函数执行完成后打印的K01数值:
array([[3.34185693e+03, 0.00000000e+00, 5.61957072e+02], [0.00000000e+00, 4.81426914e+04, 7.05288783e+02], [0.00000000e+00, 0.00000000e+00, 1.00000000e+00]])
原因说明
这个现象不是函数bug,是OpenCV接口的正常行为:
cv2.stereoCalibrate的C++底层实现中,相机内参矩阵K、畸变系数D属于输入输出双向参数。只要传入的标定flags没有包含cv2.CALIB_FIX_INTRINSIC,函数就会在迭代求解双目标定参数的过程中优化这几个内参和畸变系数,最终直接把优化结果写入传入数组对应的内存地址,不会额外创建副本,因此外部持有的原数组会被直接改写。- 优化后的K01出现fy值从2314跳到48142这种明显不合理的量级跳变,说明当前标定优化已经发散,常见诱因包括:左右图像的角点匹配存在大量错点、标定板拍摄角度覆盖不足、单目标定得到的初始内参误差过大、迭代终止条件设置不合理。
解决方法
- 如果需要固定单目标定得到的内参和畸变系数,仅求解双目标定的外参(旋转矩阵R、平移矩阵T、本质矩阵E、基础矩阵F),在
calib_flags中追加cv2.CALIB_FIX_INTRINSIC标记即可,开启该标记后函数不会修改传入的内参和畸变参数。 - 如果需要在双目标定阶段同步优化内参,又不想覆盖原本加载的单目标定结果,传入参数时手动传入numpy数组的副本即可,参考写法:
stereoParameters = cv2.stereoCalibrate( self.objpoints, self.left_imgpoints, self.right_imgpoints, self.K01.copy(), self.D01.copy(), self.K02.copy(), self.D02.copy(), self.image_format, flags=calib_flags, criteria=calib_criteria )
- 如果已经添加
CALIB_FIX_INTRINSIC标记仍然出现参数被改写的情况,优先检查OpenCV版本,4.5之前的部分旧版本存在Python绑定层的参数内存拷贝bug,升级到4.5以上的稳定版本即可解决。
内容的提问来源于stack exchange,提问作者Janik
相关产品推荐
相关产品推荐

