我有一个校准的多摄像头系统。使用基于HALCON的程序估计了相机内部(焦距、畸变等)和外部(姿态)相机参数。现在的目标是编写一个C++程序,读取相机参数,特别是由HALCON估计的畸变系数(k1、k2、k3、p1、p2),并使用它们使用OpenCV对图像进行去畸变。不幸的是,到目前为止,我无法成功:在HALCON和OpenCV中使用相同的畸变系数生成非常不同的去畸变图像!我猜想问题可能是HALCON和OpenCV解释畸变系数的方式,甚至是它们执行去畸变的方式。
下面是我的HALCON代码,用于读取畸变系数并使用它们对测试图像进行去畸变:
在OpenCV中,变量"cameraMatrix"等于HALCON中的变量"CamMatrix"。失真系数"distCoeffs"在OpenCV中取自HALCON(k1,k2,k3,p1,p2),并按照文档重新排列,如下所示:
请注意,k3作为第五个参数提供,并且p2和p1被交换了。根据HALCON文档(https://www.mvtec.com/doc/halcon/12/en/calibrate_cameras.html,请参见函数calibrate_cameras),在图像平面中无畸变的坐标(u,v)从有畸变的坐标(u',v')计算如下: u = u' + u' (k1 r^2 + k2 r^4 + k3 r^6) + p1 (r^2 + 2 u'^2) + 2 p2 u' v' v = v' + v' (k1 r^2 + k2 r^4 + k3 r^6) + p2 (r^2 + 2 v'^2) + 2 p1 u' v'
其中r = sqrt(u'^2 + v'^2)
而在OpenCV中(https://docs.opencv.org/2.4/modules/imgproc/doc/geometric_transformations.html, 请参见函数initUndistortRectifyMap),同样估计出相同的无畸变坐标,只是p1和p2被交换了。
显然,OpenCV和HALCON都以类似的方式将像素投影到图像平面上。也就是说,对于像素(x, y),相应的图像平面坐标被计算为: u' = x - cx / fx v' = y - cy / fy
这些当然可以反投影以重新获得相应的像素坐标:
x = u' * fx + cx y = v' * fy + cy
根据文档,似乎所有事情都应该按预期工作。然而,我不明白为什么基于HALCON和基于OpenCV的代码仍然输出非常不同的结果。我注意到,为了产生类似于HALCON中的无畸变结果(但不完全相同),我必须在OpenCV中缩小(约100倍!)畸变系数。实际上,我注意到HALCON估计出巨大的畸变系数。例如,为了在无畸变图像中产生可见变化,我必须在HALCON中设置k1=1000,而在OpenCV中k1=1就已经能够显著改变图像。对于某些畸变系数,我甚至不得不取反(加上负号)才能获得类似方向的无畸变结果...
我尝试一次只集中在少量的畸变系数上,即仅当k1>0时,其他系数设置为0。然而,在大多数情况下(除了x=cx,y=cy的少数例外),未畸变的坐标超出了图像大小,甚至变成了负数。
HALCON难道不是以这种方式估计畸变校正映射坐标吗?我错过了什么吗?应该如何转换这些畸变系数才能使OpenCV产生完全相同的未畸变结果图像?非常感谢您的帮助!
由于某些软件限制,仅使用OpenCV进行校准和去畸变是有争议的,但对我来说,这是不可接受的解决方案。
下面是我的HALCON代码,用于读取畸变系数并使用它们对测试图像进行去畸变:
* Read the internal camera calibratino parameters ('area_scan_polynomial' model)
read_cam_par('Calibration.dat', CamParam)
* Estimate camera parameters without distortion: set all distortion coefficients to [k1, k2, k3, p1, p2] = [0, 0, 0, 0, 0]
change_radial_distortion_cam_par ('fixed', CamParam, [0, 0, 0, 0, 0], CamParamOut)
* Estimate camera matrix of distortion-free parameters (used for OpenCV)
cam_par_to_cam_mat(CamParamOut, CamMatrix, ImageWidth, ImageHeight)
* Generate map to use for undistortion.
* Note the use of type 'coord_map_sub_pix' which is of type 'vector_field_absolute',
* i.e. the values are 2D absolute coordinates of the corresponding undistorted pixel location
gen_radial_distortion_map(Map, CamParam, CamParamOut, 'coord_map_sub_pix')
* Read a test image and undistort it using the estimate map
read_image (Image, 'test.jpg')
map_image(Image, Map, ImageRectified)
我正在尝试在OpenCV中实现完全相同的功能,使用以下代码:
Mat view , rview, mapx, mapy;
// Read the same test image as in HALCON
view = imread("test.jpg");
// Get the image size
const Size viewSize = view.size();
// Generate map to use for undistortion
initUndistortRectifyMap(cameraMatrix, distCoeffs, Mat(),
cameraMatrix, viewSize, CV_16SC2, mapx, mapy);
// Undistort the image using the estimated map
remap(view, rview, mapx, mapy, INTER_LINEAR);
在OpenCV中,变量"cameraMatrix"等于HALCON中的变量"CamMatrix"。失真系数"distCoeffs"在OpenCV中取自HALCON(k1,k2,k3,p1,p2),并按照文档重新排列,如下所示:
distCoeffs = (Mat_<double>(5, 1) << k1, k2, p2, p1, k3)
请注意,k3作为第五个参数提供,并且p2和p1被交换了。根据HALCON文档(https://www.mvtec.com/doc/halcon/12/en/calibrate_cameras.html,请参见函数calibrate_cameras),在图像平面中无畸变的坐标(u,v)从有畸变的坐标(u',v')计算如下: u = u' + u' (k1 r^2 + k2 r^4 + k3 r^6) + p1 (r^2 + 2 u'^2) + 2 p2 u' v' v = v' + v' (k1 r^2 + k2 r^4 + k3 r^6) + p2 (r^2 + 2 v'^2) + 2 p1 u' v'
其中r = sqrt(u'^2 + v'^2)
而在OpenCV中(https://docs.opencv.org/2.4/modules/imgproc/doc/geometric_transformations.html, 请参见函数initUndistortRectifyMap),同样估计出相同的无畸变坐标,只是p1和p2被交换了。
显然,OpenCV和HALCON都以类似的方式将像素投影到图像平面上。也就是说,对于像素(x, y),相应的图像平面坐标被计算为: u' = x - cx / fx v' = y - cy / fy
这些当然可以反投影以重新获得相应的像素坐标:
x = u' * fx + cx y = v' * fy + cy
根据文档,似乎所有事情都应该按预期工作。然而,我不明白为什么基于HALCON和基于OpenCV的代码仍然输出非常不同的结果。我注意到,为了产生类似于HALCON中的无畸变结果(但不完全相同),我必须在OpenCV中缩小(约100倍!)畸变系数。实际上,我注意到HALCON估计出巨大的畸变系数。例如,为了在无畸变图像中产生可见变化,我必须在HALCON中设置k1=1000,而在OpenCV中k1=1就已经能够显著改变图像。对于某些畸变系数,我甚至不得不取反(加上负号)才能获得类似方向的无畸变结果...
我进一步研究了HALCON的去畸变代码,并尝试根据文档手动估计无畸变坐标(u,v),这应该与“Map”中找到的坐标相对应。我这样做是为了确保“Map”是按照文档/我理解的方式进行估算的。但即使在这里,与“Map”中估计的结果相比,我也得到了非常不同的结果...为了进行测试,我使用了以下代码:
* Get the camera parameters from the calibration
get_cam_par_data (CamParam, 'k1', k1)
get_cam_par_data (CamParam, 'k2', k2)
get_cam_par_data (CamParam, 'k3', k3)
get_cam_par_data (CamParam, 'p1', p1)
get_cam_par_data (CamParam, 'p2', p2)
get_cam_par_data (CamParam, 'cx', cx)
get_cam_par_data (CamParam, 'cy', cy)
get_cam_par_data (CamParam, 'image_width', width)
get_cam_par_data (CamParam, 'image_height', height)
* Estimate the camera matrix, to read the focal length in pixel
cam_par_to_cam_mat(CamParamOut, CamMatrix, width, height)
* Extract the focal lenths in pixel from the estimated camera matrix (see above)
fx_px := CamMatrix[0]
fy_px := CamMatrix[4]
* Pick a pixel coordinate (I tried different values) in the image domain
x := 350
y := 450
* Convert into image plane coordinates
u_1 := (x - cx) / fx_px
v_1 := (y - cy) / fy_px
* Estimate the undistorted location u_2 and v_2
r2 := u_1 * u_1 + v_1 * v_1
u_2 := u_1 * (1 + k1 * r2 + k2 * r2 * r2 + k3 * r2 * r2 * r2) + 2 * p1 * u_1 * v_1 + p2 * (r2 + 2 * u_1 * u_1)
v_2 := v_1 * (1 + k1 * r2 + k2 * r2 * r2 + k3 * r2 * r2 * r2) + 2 * p2 * u_1 * v_1 + p1 * (r2 + 2 * v_1 * v_1)
* Back to pixel coordinate
x_1 := u_2 * fx_px + cx
y_1 := v_2 * fy_px + cy
* Compare the values with the value in Map (estimated as before). G_found and G_est should match!
G_found := [y_1, x_1]
get_grayval(Map, y, x, G_est)
我尝试一次只集中在少量的畸变系数上,即仅当k1>0时,其他系数设置为0。然而,在大多数情况下(除了x=cx,y=cy的少数例外),未畸变的坐标超出了图像大小,甚至变成了负数。
HALCON难道不是以这种方式估计畸变校正映射坐标吗?我错过了什么吗?应该如何转换这些畸变系数才能使OpenCV产生完全相同的未畸变结果图像?非常感谢您的帮助!
由于某些软件限制,仅使用OpenCV进行校准和去畸变是有争议的,但对我来说,这是不可接受的解决方案。