HALCON 到 OpenCV 失真系数的转换
Posted
技术标签:
【中文标题】HALCON 到 OpenCV 失真系数的转换【英文标题】:HALCON to OpenCV Distortion Coefficients Convertion 【发布时间】:2020-02-24 15:10:17 【问题描述】:我有一个经过校准的多摄像头系统。内部(焦距、失真等)和外部(姿势)相机参数均已使用基于 HALCON 的程序进行了估计。现在,目标是编写一个 C++ 程序,读取相机参数,特别是 HALCON 估计的失真系数 (k1, k2, k3, p1, p2),并使用它们使用 OpenCV 对图像进行不失真处理。不幸的是,到目前为止,我无法成功:在 HALCON 和 OpenCV 中使用相同的失真系数会生成非常不同的未失真图像!我想,问题在于 HALCON 和 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”。 OpenCV 中的失真系数“distCoeffs”取自 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 中类似的未失真结果(但不完全相同),我必须按比例缩小(~100 倍!)OpenCV 中的失真系数。事实上,我注意到 HALCON 估计了巨大的失真系数。例如,为了在未失真的图像中产生可见的变化,我必须在 HALCON k1=1000 中进行设置,而在 OpenCV k1=1 中已经明显地改变了图像。对于某些失真系数,我什至不得不反转(用负号)值以获得朝着相似方向前进的未失真结果......
我又挖掘了一点 HALCON 不失真代码,并尝试按照文档手动估计未失真坐标(u,v),它应该对应于“地图”中找到的坐标。我这样做是为了确保“地图”确实是按照文档中指定的方式/我理解的方式估计的。然而,即使在这里,与“地图”中估计的结果相比,我也得到了非常不同的结果......为了进行测试,我使用了以下代码:
* 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 进行校准和不失真是有争议的,但不幸的是对我来说不是一个可接受的解决方案。
【问题讨论】:
好问题!要是有人知道答案就好了。 【参考方案1】:我能够找到自己问题的答案。简而言之,答案是肯定的。是的,可以将 HALCON 转换为 OpenCV 失真参数,反之亦然。原因在于,HALCON 和 OpenCV显然估计了相同的底层模型。我做了几次成功的测试来证实这一点,我想分享我的见解。下面是我计算的将每个失真参数从 HALCON 转换为 OpenCV 的公式:
k1_opencv = k1_halcon * fmm * fmm;
k2_opencv = k2_halcon * fmm * fmm * fmm * fmm;
k3_opencv = k3_halcon * fmm * fmm * fmm * fmm * fmm * fmm;
p1_opencv = p2_halcon * fmm; // Notice: swap
p2_opencv = p1_halcon * fmm;
请注意,fmm 是以毫米为单位的焦距,如在 e.g.海康。 估计地图中估计的相同值的正确 HALCON 代码是:
get_cam_par_data (CamParam, 'sx', Sx)
get_cam_par_data (CamParam, 'sy', Sy)
* Convert into image plane coordinates
u_1 := (x - cx) * sx
v_1 := (y - cy) * sy
* 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 * p2 * u_1 * v_1 + p1 * (r2 + 2 * u_1 * u_1)
v_2 := v_1 * (1 + k1 * r2 + k2 * r2 * r2 + k3 * r2 * r2 * r2) + 2 * p1 * u_1 * v_1 + p2 * (r2 + 2 * v_1 * v_1)
* Back to pixel coordinate
x_1 := u_2 / sx + cx
y_1 := v_2 / sy + cy
* Compare coordinates. NOTICE: we get the values from the distortion map
* going from UNdistorted to DIstorted coordinates!!!
G_found := [y_1, x_1]
gen_radial_distortion_map(MapUD, CamParamOut, CamParam, 'coord_map_sub_pix')
get_grayval(MapUD, y, x, G_est)
关于我在问题中发布的初始代码,坐标使用以毫米为单位的像素大小sx 和sy 进行转换,而不是焦距。另一个区别是我们将估计的坐标与 MapUD 中的值进行比较,其中 MapUD(UNdistorted coordinate) := DIstorted coordinate。估计的坐标和地图中的坐标对应于图像边界处的舍入误差和条件。
OpenCV 改为执行以下操作(完全符合文档!):
float u = (x - cx) / fpx;
float v = (y - cy) / fpx;
float x_1 = u;
float y_1 = v;
float r2 = x_1 * x_1 + y_1 * y_1;
float x_2 = x_1 * (1 + k1 * r2 + k2 * r2 * r2 + k3 * r2 * r2 * r2) + 2 * p1 * x_1 * y_1 + p2 * (r2 + 2 * x_1 * x_1);
float y_2 = y_1 * (1 + k1 * r2 + k2 * r2 * r2 + k3 * r2 * r2 * r2) + p1 * (r2 + 2 * y_1 * y_1) + 2 * p2 * x_1 * y_1;
float map_x_est = x_2 * fpx + cx;
float map_y_est = y_2 * fpx + cy;
// Compare coordinates
initUndistortRectifyMap(cameraMatrix, distCoeffs, Mat(),
cameraMatrix, viewSize, CV_32FC1, mapx, mapy);
float map_x = mapx.at<float>(y, x);
float map_y = mapy.at<float>(y, x);
在上面的代码中,map_x的值对应map_x_est,map_y对应map_y_est最多舍入误差。 如果我们使用与 HALCON 中相同的 cameraMatrix,并使用上述公式转换失真系数 distCoeffs,我们可以清楚地看到 OpenCV 变量 map_x 和 map_y中的映射值> 对应于在 HALCON 的 MapUD 中找到的那些。我通过在 HALCON 和 OpenCV 中为不同的失真参数一一输出映射值(在整个域中)来测试这一点,获得相同的结果,直到小误差
其他信息:MVTec 向我发送了一些 HALCON 代码,用于将手动估计的坐标与地图中的坐标进行比较。请注意,就我的解决方案而言,它们是相反的,即从失真到未失真。在许多情况下,该代码不对我有用。随意尝试并告诉我:
dev_close_window ()
dev_update_off ()
Width:=1600
Height:=1200
dev_open_window_fit_size(0, 0, Width, Height, -1, -1, WindowHandle)
gen_cam_par_area_scan_polynomial (0.008, 0, 0, 0, 0, 10, 5.2e-006, 5.2e-006, Width/2, Height/2, Width, Height, CamParam)
change_radial_distortion_cam_par ('fixed', CamParam, [0, 0, 0, 0, 0], CamParamOut)
gen_radial_distortion_map(Map, CamParam, CamParamOut, 'coord_map_sub_pix')
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, 'sx', Sx)
get_cam_par_data (CamParam, 'sy', Sy)
* Select a valid point
Row := 86
Col := 89
get_grayval(Map, Row, Col, G_map)
get_domain (Map, Domain)
test_region_point (Domain, Row, Col, IsInside)
if (IsInside)
* Check calculation
GRow:=G_map[0]
GCol:=G_map[1]
U_1 := (GCol - cx) * Sx
V_1 := (GRow - cy) * Sy
R_2 := U_1 * U_1 + V_1 * V_1
U_2 := U_1 * (1 + k1 * R_2 + k2 * R_2 * R_2 + k3 * R_2 * R_2 * R_2) + p1 * (R_2 + 2 * U_1 * U_1) + 2 * p2 * U_1 * V_1
V_2 := V_1 * (1 + k1 * R_2 + k2 * R_2 * R_2 + k3 * R_2 * R_2 * R_2) + p2 * (R_2 + 2 * V_1 * V_1) + 2 * p1 * U_1 * V_1
Col_calc := U_2 / Sx + cx
Row_calc := V_2 / Sy + cy
G_input:=[Row, Col]
G_calc:=[Row_calc, Col_calc]
G_diff:=G_calc-G_input
dev_inspect_ctrl ([G_input, G_calc, G_diff])
stop()
else
* Point is outside domain of distortion map
stop()
endif
dev_close_inspect_ctrl ([G_input, G_calc, G_diff])
dev_clear_window ()
dev_update_on ()
disp_message (WindowHandle, 'No more lines to execute...', 'window', 12, 12, 'black', 'true')
【讨论】:
【参考方案2】:我们向 MVTec 提出支持请求并收到以下答复(收到于 2019 年 11 月 21 日星期四;09:27):
OpenCV 和 HALCON 校准参数之间的转换不是 可能。
原因是HALCON多项式模型使用方程系统 其中扭曲的图像坐标在右侧给出。就这样 失真系数描述了这个计算方向。在 相反,OpenCV 实现使用一个方程系统,其中 un右侧给出了扭曲的图像坐标。因此,失真系数描述了反转的方向。因为 高多项式等级,不能转换。
要比较方程式,请参考运算符参考 calibrate_cameras 和OpenCV camera calibration tutorial。
在解析中不存在可以反演的划分模型 OpenCV。
我们还获得了一个 hdev
脚本,用于从 HALCON 到 OpenCV 参数的近似映射(收到 2019 年 11 月 21 日星期四;16:27):
<?xml version="1.0" encoding="UTF-8"?>
<hdevelop file_version="1.2" halcon_version="19.05.0.0">
<procedure name="main">
<interface/>
<body>
<c>************************************************************************************************</c>
<c>* Parameter</c>
<c>************************************************************************************************</c>
<c></c>
<l>PathImg := './'</l>
<l>PathIdRect := '.rect.'</l>
<l>ZoomDisplay := 0.3</l>
<c></c>
<c>* Distortions from OpenCV</c>
<l>Distortions := [-0.161881, 0.092025, 0.000072, -0.000105, 0.000000]</l>
<c>*</c>
<c>* Camera matrices from OpenCV</c>
<l>* CamMatrixOpenCV := [1402.101918, 0.000000, 967.367190,\
0.000000, 1399.751916, 580.546496,\
0.000000, 0.000000, 1.000000]</l>
<c></c>
<c>* CamMatrixOpenCV</c>
<l>Cx := 967.3672</l>
<l>Cy := 580.546496</l>
<l>fxPix := 1402.101918</l>
<l>fyPix := 1399.751916</l>
<l>f := 0.00824144</l>
<c>*</c>
<l>* ProjectionOpenCV := [ fxPix, 0.000000, Cx, 0.000000,\
0.000000, fyPix, Cy, 0.000000,\
0.000000, 0.000000, 1.000000, 0.000000,\
0, 0, 0, 1 ]</l>
<c></c>
<c>************************************************************************************************</c>
<c>* Initialization</c>
<c>************************************************************************************************</c>
<c></c>
<l>dev_update_off ()</l>
<c></c>
<c>* Prepare the image data.</c>
<l>list_image_files (PathImg, 'png', [], ImageFilesAll)</l>
<l>ImageFilesRectified := regexp_select(ImageFilesAll, PathIdRect)</l>
<l>ImageFilesNonRectified := difference(ImageFilesAll, ImageFilesRectified)</l>
<l>if (|ImageFilesNonRectified| != |ImageFilesRectified|)</l>
<l> throw (['Uneven amounts of images found, please check the image pairs'])</l>
<l>endif</l>
<c></c>
<c>* Prepare the display</c>
<l>read_image (Image, ImageFilesNonRectified[0])</l>
<l>get_image_size (Image, Width, Height)</l>
<l>for I := 0 to 2 by 1</l>
<l> dev_open_window (0, I*(Width*ZoomDisplay+12), Width*ZoomDisplay, Height*ZoomDisplay, 'black', WindowHandles.at(I))</l>
<l> set_display_font (WindowHandles.at(I), 16, 'mono', 'true', 'false')</l>
<l>endfor</l>
<c></c>
<c>* Perform the calibration using an arbitrary grid (full image also possible but slow)</c>
<l>gen_grid_region (RegionGrid, 5, 5, 'points', Width, Height)</l>
<l>get_region_points (RegionGrid, Row, Col)</l>
<l>campar_opencv2halcon (Distortions, f, Cx, Cy, fxPix, fyPix, Row, Col, Width, Height, Error, CamParamsOpt)</l>
<l>change_radial_distortion_cam_par ('adaptive', CamParamsOpt, [0,0,0,0,0], CamParamOptRect) </l>
<l>dev_set_window (WindowHandles.at(0))</l>
<l>dev_disp_text ('Error (pxl): ' + Error, 'window', 'top', 'left', 'black', [], [])</l>
<l>dev_disp_text (['HALCON camera params:', CamParamsOpt], 'window', 'bottom', 'left', 'black', [], [])</l>
<l>disp_continue_message (WindowHandles.at(0), 'black', 'true')</l>
<l>stop ()</l>
<c></c>
<c>* Ignore errors at the close image border</c>
<l>Padding := min([Width, Height])/100</l>
<l>gen_rectangle1 (RoiDiff, Padding, Padding, Height-Padding, Width-Padding)</l>
<c></c>
<c></c>
<c>************************************************************************************************</c>
<c>* Initialization</c>
<c>************************************************************************************************</c>
<c></c>
<c>* Apply the calibration</c>
<l>for I := 0 to |ImageFilesNonRectified|-1 by 1</l>
<c> * Select the image pair</c>
<l> FileNameNonRectCurrent := ImageFilesNonRectified[I]</l>
<l> FileNameRectCurrent := regexp_select(ImageFilesRectified, split(FileNameNonRectCurrent, PathImg)[0])</l>
<c> </c>
<l> read_image (ImageNonRect, FileNameNonRectCurrent)</l>
<l> read_image (ImageRectOpenCV, FileNameRectCurrent)</l>
<c> </c>
<c> * Rectify the image using the HALCON calibration and compare it to the </c>
<c> * OpenCV ground truth</c>
<l> change_radial_distortion_image (ImageNonRect, ImageNonRect, ImageRectHALCON, CamParamsOpt, CamParamOptRect)</l>
<c></c>
<l> reduce_domain (ImageRectOpenCV, RoiDiff, ImageRectOpenCVReduced)</l>
<l> abs_diff_image (ImageRectOpenCVReduced, ImageRectHALCON, ImageAbsDiff, 1)</l>
<l> threshold (ImageAbsDiff, RegionDiff, 50, 255)</l>
<l> region_features (RegionDiff, 'area', AreaDiff)</l>
<c> </c>
<l> ImagesDisp := ImageNonRect, ImageRectOpenCV, ImageRectHALCON</l>
<l> DispText := ['Original image', 'Rect image (OpenCV)', 'Rect image (HALCON)']</l>
<l> for J := 0 to ImagesDisp.length()-1 by 1</l>
<l> dev_set_window (WindowHandles.at(J))</l>
<l> dev_display (ImagesDisp.at(J))</l>
<l> dev_disp_text (DispText[J], 'window', 'top', 'left', 'black', [], [])</l>
<l> endfor</l>
<l> if (AreaDiff>100)</l>
<l> dev_set_color ('#ff0000c0')</l>
<l> dev_display (RegionDiff)</l>
<l> smallest_rectangle1 (RegionDiff, Row1, Column1, Row2, Column2)</l>
<l> dev_set_color ('#ff000040')</l>
<l> gen_rectangle1 (Rectangle, Row1, Column1, Row2, Column2)</l>
<l> dev_disp_text ('Deviation detected', 'window', 'top', 'right', 'red', 'box', 'false')</l>
<l> stop ()</l>
<l> else</l>
<l> dev_disp_text ('No deviation', 'window', 'top', 'right', 'green', 'box', 'false')</l>
<l> endif</l>
<l>endfor</l>
<l>disp_end_of_program_message (WindowHandles.at(WindowHandles.length()-1), 'black', 'true')</l>
<l>stop ()</l>
<c></c>
<c>************************************************************************************************</c>
<c>* Clean up</c>
<c>************************************************************************************************</c>
<c></c>
<l>for I := 0 to WindowHandles.length()-1 by 1</l>
<l> dev_set_window (WindowHandles.at(I))</l>
<l> dev_close_window ()</l>
<l>endfor</l>
<l>dev_update_on ()</l>
</body>
<docu id="main">
<parameters/>
</docu>
</procedure>
<procedure name="campar_opencv2halcon">
<interface>
<ic>
<par name="OpenCv_Distortions" base_type="ctrl" dimension="0"/>
<par name="OpenCvF" base_type="ctrl" dimension="0"/>
<par name="OpenCv_Cx" base_type="ctrl" dimension="0"/>
<par name="OpenCv_Cy" base_type="ctrl" dimension="0"/>
<par name="OpenCv_fxPix" base_type="ctrl" dimension="0"/>
<par name="OpenCv_fyPix" base_type="ctrl" dimension="0"/>
<par name="RowObservations" base_type="ctrl" dimension="0"/>
<par name="ColObservations" base_type="ctrl" dimension="0"/>
<par name="WidthImage" base_type="ctrl" dimension="0"/>
<par name="HeightImage" base_type="ctrl" dimension="0"/>
</ic>
<oc>
<par name="Error" base_type="ctrl" dimension="0"/>
<par name="CamParamsOpt" base_type="ctrl" dimension="0"/>
</oc>
</interface>
<body>
<c>************************************************************************************************</c>
<c>* Prepare the OpenCV values as input for HALCON calibration</c>
<c>************************************************************************************************</c>
<c></c>
<c>* Extract the distortions</c>
<l>k_1 := OpenCv_Distortions[0]</l>
<l>k_2 := OpenCv_Distortions[1]</l>
<l>p_1 := OpenCv_Distortions[2]</l>
<l>p_2 := OpenCv_Distortions[3]</l>
<l>k_3 := OpenCv_Distortions[4]</l>
<c></c>
<c>* Image plane coord. system -> HALCONs "description plate"</c>
<l>x_ := (ColObservations - OpenCv_Cx)/OpenCv_fxPix</l>
<l>y_ := (RowObservations - OpenCv_Cy)/OpenCv_fyPix</l>
<c></c>
<c>* Calculate the distorted points using the OpenCV Model</c>
<l>r2 := x_*x_+y_*y_</l>
<c></c>
<l>x2_tmp := x_ * (1 + k_1 * r2 + k_2 * r2 * r2 + k_3 * r2 * r2 * r2)</l>
<l>x2_ := x2_tmp + (2.0 * p_1 *x_ * y_ + p_2*(r2+2.0*x_*x_))</l>
<c></c>
<l>y2_tmp := y_ * (1 + k_1*r2+k_2*r2*r2+k_3*r2*r2*r2)</l>
<l>y2_ := y2_tmp + (p_1 * (r2 + 2 * y_ * y_) + 2 * p_2 * x_ * y_)</l>
<c></c>
<c>* Image plane coord. system -> Image coord. system (= Pixel coord)</c>
<l>u := OpenCv_fxPix * x2_ + OpenCv_Cx</l>
<l>v := OpenCv_fyPix * y2_ + OpenCv_Cy</l>
<c></c>
<c>* Compute the sensor size</c>
<l>sx := OpenCvF/OpenCv_fxPix</l>
<l>sy := OpenCvF/OpenCv_fyPix</l>
<c></c>
<c></c>
<c>************************************************************************************************</c>
<c>* Perform a HALCON calibration</c>
<c>************************************************************************************************</c>
<c></c>
<l>create_calib_data ('calibration_object', 1, 1, CalibDataID)</l>
<c>* Define a calibration plate</c>
<l>tuple_gen_const (|x_|, OpenCvF, Zeroes)</l>
<l>set_calib_data_calib_object (CalibDataID, 0, [x_, y_, Zeroes])</l>
<c>* Define the start params</c>
<l>gen_cam_par_area_scan_polynomial (OpenCvF, 0, 0, 0, 0, 0, sx, sy, OpenCv_Cx, OpenCv_Cy, WidthImage, HeightImage, CameraParamStart)</l>
<l>set_calib_data_cam_param (CalibDataID, 0, [], CameraParamStart)</l>
<c>* Exclude all params we can set directly from OpenCV</c>
<l>set_calib_data (CalibDataID, 'camera', 0, 'excluded_settings', ['pose'])</l>
<l>set_calib_data (CalibDataID, 'camera', 0, 'excluded_settings', ['focus','cx','cy'])</l>
<c></c>
<c>* Set the observation points</c>
<l>hom_mat3d_identity (HomMat3DIdentity)</l>
<l>hom_mat3d_to_pose (HomMat3DIdentity, Pose)</l>
<l>set_calib_data_observ_points (CalibDataID, 0, 0, 0, v, u, 'all', Pose)</l>
<c>* Calibrate the camera and deliver the results</c>
<l>calibrate_cameras (CalibDataID, Error)</l>
<l>get_calib_data (CalibDataID, 'camera', 0, 'params', CamParamsOpt)</l>
<l>clear_calib_data (CalibDataID)</l>
<l>return ()</l>
</body>
<docu id="campar_opencv2halcon">
<parameters>
<parameter id="CamParamsOpt">
<sem_type>campar</sem_type>
</parameter>
<parameter id="ColObservations">
<default_type>real</default_type>
<sem_type>number</sem_type>
<type_list>
<item>real</item>
</type_list>
</parameter>
<parameter id="Error">
<default_type>real</default_type>
<multivalue>false</multivalue>
<sem_type>number</sem_type>
<type_list>
<item>real</item>
</type_list>
</parameter>
<parameter id="HeightImage">
<default_type>integer</default_type>
<sem_type>number</sem_type>
<type_list>
<item>integer</item>
</type_list>
<value_max>1</value_max>
<value_min>1</value_min>
</parameter>
<parameter id="OpenCvF">
<default_type>real</default_type>
<multivalue>false</multivalue>
<sem_type>number</sem_type>
<type_list>
<item>real</item>
</type_list>
</parameter>
<parameter id="OpenCv_Cx">
<default_type>real</default_type>
<sem_type>number</sem_type>
<type_list>
<item>real</item>
</type_list>
</parameter>
<parameter id="OpenCv_Cy">
<default_type>real</default_type>
<sem_type>number</sem_type>
<type_list>
<item>real</item>
</type_list>
</parameter>
<parameter id="OpenCv_Distortions">
<default_type>real</default_type>
<mixed_type>optional</mixed_type>
<multivalue>true</multivalue>
<sem_type>number</sem_type>
<type_list>
<item>integer</item>
<item>real</item>
</type_list>
</parameter>
<parameter id="OpenCv_fxPix">
<default_type>real</default_type>
<sem_type>number</sem_type>
<type_list>
<item>real</item>
</type_list>
</parameter>
<parameter id="OpenCv_fyPix">
<default_type>real</default_type>
<sem_type>number</sem_type>
<type_list>
<item>real</item>
</type_list>
</parameter>
<parameter id="RowObservations">
<default_type>real</default_type>
<sem_type>number</sem_type>
<type_list>
<item>real</item>
</type_list>
</parameter>
<parameter id="WidthImage">
<default_type>integer</default_type>
<sem_type>number</sem_type>
<type_list>
<item>integer</item>
</type_list>
<value_max>1</value_max>
<value_min>1</value_min>
</parameter>
</parameters>
</docu>
</procedure>
</hdevelop>
MVTec 对脚本的评论如下:
基本上,这个想法是执行经典的 HALCON 校准。去做 因此,我们使用以下公式为其准备输入值 OpenCV 文档(即“校准板”的定义, 使用 OpenCV 扭曲的扭曲点)。 HALCON 校准 然后用于确定 HALCON 特定的失真值 - rest 可以直接从 OpenCV 参数集派生/获取。
对于验证,图像集具有未失真且经过 OpenCV 校正 可以使用图片:
获取原始(失真)图像并使用 HALCON 校准对其进行校正。 将其与通过 abs_diff_image 校正的 OpenCV 图像进行比较。
请注意,这仅在单个数据集/校准上进行了测试。 对不同的方法测试这种方法是有意义的 校准/相机。
【讨论】:
感谢您发布此信息。你有机会测试代码吗? 还没有。我们不打算在这里依赖近似值。【参考方案3】:不幸的是,HALCON 和 OpenCV 校准参数之间没有直接转换,因为基础模型和估计参数的方式不同。 HALCON 参数描述了从扭曲坐标到非扭曲坐标的转换,而 OpenCV 参数描述了相反的转换。由于多项式等级较高,因此无法进行转换。 OpenCV中不存在可解析倒置的划分模型。
【讨论】:
感谢您的回答!根据我的发现,HALCON 文档中似乎存在错误。实际上看起来 OpenCV 和 HALCON 中的底层模型实际上是相同的。详情请看我的回答 感谢您的回答。我们将来肯定会使用它。我也在与 MVTec 团队讨论这个问题,并期待他们的回应和更正。一旦我们得到他们的确认,我很乐意将您的回答标记为正确答案。 我咨询了 MVTec 开发团队。在研究了这个问题并评估了代码后,MVTec 确认 HALCON 文档是正确的,并且无法解析转换。 感谢您的评论。我也得到了 MVTec “确认”,但他们发送给我的代码不起作用。你能补充更多细节吗?以上是关于HALCON 到 OpenCV 失真系数的转换的主要内容,如果未能解决你的问题,请参考以下文章
Halcon实例转OpenCvSharp(C# OpenCV)实现--瓶口缺陷检测(附源码)