“一滴水,用显微镜看,也是一个大世界。”
在项目中,偶尔会遇到由于产品形状、拍摄位置等原因导致标定板很难放置的情况,此时可以考虑使用halcon自标定算法来标定相机。由于自标定可以在不使用用标定板情况下,进行畸变矫正。所以,极大地方便在设备现场进行调试。
1. 自标定处理流程
自标定原理来源于此论文:“Automatic line-based estimation of radial lens distortion”。
论文下载地址:
链接:https://pan.baidu.com/s/118YDti24nvbnsz2CbOJa1Q 提取码:5t67
从论文中可知自标定的一般流程:轮廓检测、共线线段连接、剔除干扰线、求取畸变参数。如下图所示。
在halcon我们可以对自标定流程梳理为两大步骤:1.检测直线轮廓XLD 2.图像畸变矫正,如下图所示。
2. 自标定相关算子
radial_distortion_self_calibration(Contours : SelectedContours : Width, Height, InlierThreshold, RandSeed, DistortionModel, DistortionCenter, PrincipalPointVar : CameraParam)
名字:径向畸变自标定
描述:此算子通过XLD轮廓数据来估算镜头的畸变参数和畸变中心。其获得的即便参数通过相机内参数CameraParam返回。此算子不能矫正焦距和比例因子,因此不能用于3D测量中。
参数:
Contours :输入用来矫正的轮廓数据
SelectedContours :矫正后的轮廓数据
Width:获取轮廓数据的图像宽度
Height:获取轮廓数据的图像高度
InlierThreshold:分类阈值
RandSeed:随机种子
DistortionModel:畸变模式
DistortionCenter:畸变中心的估算模式
PrincipalPointVar :偏差控制
CameraParam:输出相机内参数
change_radial_distortion_cam_par( : : Mode, CamParamIn, DistortionCoeffs : CamParamOut)
名字:矫正畸变参数
描述:根据指定的径向畸变系数,求取理想无畸变的相机内参。
参数:
Mode:畸变模式
CamParamIn:畸变的相机内部参数
DistortionCoeffs :畸变系数值
CamParamOut:已校正的相机内参
change_radial_distortion_image(Image, Region : ImageRectified : CamParamIn, CamParamOut : )
名字:矫正畸变图像
描述:根据指定图像和指定相加参数来矫正输入图像的畸变。
参数:
Image:输入图像
Region :矫正图像的区域
ImageRectified :矫正图像
CamParamIn:输入相机参数
CamParamOut :输出相机参数
3. 实例分析
首先进行边缘检测, 选择包含一定长度,一定数量直线段的XLD轮廓
使用radial_distortion_self_calibration算子进行自标定,
得到相机内参以及用于标定的直线段
利用change_radial_distortion_cam_par 、以及change_radial_distortion_image
进行图像畸变矫正
完整源码如下:
*公众号:机器视觉那些事儿*
*1. 算法功能:径向畸变自校正
*2. 算法思路:
*(1)检测含直线轮廓XLD
*(2)矫正畸变图像
*---------初始化,读图------------
dev_update_off ()
read_image (Image, 'board/board-01')
dev_close_window ()
dev_open_window_fit_image (Image, 0, 0, -1, -1, WindowHandle)
set_display_font (WindowHandle, 16, 'mono', 'true', 'false')
dev_display (Image)
disp_message (WindowHandle, 'Image with radial distortions', 'window', 0, 0, 'black', 'true')
disp_continue_message (WindowHandle, 'black', 'true')
stop ()
*----------------(1)检测直线轮廓XLD=----------------
* i 利用canny滤波器提取图像边缘
edges_sub_pix (Image, Edges, 'canny', 1, 10, 40)
* ii 分割直线段以及圆弧线段
segment_contours_xld (Edges, SplitEdges, 'lines_circles', 5, 4, 2)
* iii 选择足够长的线段用于自标定
select_shape_xld (SplitEdges, SelectedEdges, 'contlength', 'and', 30, 100000)
dev_display (Image)
dev_set_colored (12)
dev_display (SelectedEdges)
disp_message (WindowHandle, 'Extracted edges', 'window', 0, 0, 'black', 'true')
disp_continue_message (WindowHandle, 'black', 'true')
stop ()dev_clear_window ()
disp_message (WindowHandle, 'Performing self-calibration...', 'window', 0, 0, 'black', 'true')
*----------------(2)矫正畸变图像=----------------
* i 相机自标定,根据边缘求取内参
radial_distortion_self_calibration (SelectedEdges, CalibrationEdges, 646, 492, 0.08, 42, 'division', 'variable', 0, CamParSingleImage)
* ii 获取无畸变相机内参
get_domain (Image, Domain)
change_radial_distortion_cam_par ('fixed', CamParSingleImage, 0, CamParSingleImageRect)
* iii 根据内参,进行图像畸变矫正
change_radial_distortion_image (Image, Domain, ImageRectified, CamParSingleImage, CamParSingleImageRect)
*-----------结果显示------------
*显示原始图像XLD以及用于矫正图像所需的直线段
dev_display (Image)
dev_display (CalibrationEdges)
disp_message (WindowHandle, 'Edges used for calibration', 'window', 0, 0, 'black', 'true')
disp_continue_message (WindowHandle, 'black', 'true')
stop ()
*显示矫正图像
dev_display (ImageRectified)
disp_message (WindowHandle, 'Image without radial distortions', 'window', 0, 0, 'black', 'true')
disp_continue_message (WindowHandle, 'black', 'true')stop ()
4. 注意事项
halcon的自标定是根据物体边缘的直线,来进行图像矫正,所以需保证图像畸变较大的边缘有足够的直线段;
实际项目中被检测物若没有理想的直线段,可以使用棋盘格、标准物、菲林片等代替;
运行内存大于等于4G。
5. 后续步骤
自标定只需要一张图片,即可标定相机内参,后续若需要将像素单位换算至公有制单位,需要使用棋盘格、标准物、菲林片等进行转换,便可应用于测量项目;也可以利用一定网格构建XY坐标系,用于定位项目。所以,相机标定后的应用,会在后续的测量篇以及手眼标定篇涉及,请持续关注。
另提供一份源码如下:
* This program shows how radial_distortion_self_calibration can be used to
* calibrate the radial distortion coefficient and the center of distortions.
* 能够用于标定径向畸变参数和畸变中心
* In the first part of the program, the edges extracted from
* a single image are used for the calibration. The results show that the
* radial distortions are extracted fairly accurately from the single image.
* 第一部分,单幅图像提取的边缘能够用于标定,结果显示径向畸变能够相当准确的从单幅图像中求解出。
* To increase the accuracy, the second part of the program shows
* how the edges extracted from 20 images can be used to perform the
* calibration. Since the edges occur in many different orientations in
* the 20 images, the principal point can be determined significantly more accurately.
* 为了提高径向畸变的准确性,第二部分从20张图像提取边缘来进行标定。
* 因为在这20幅图像中,边缘出现在许多不同的方向,主点能够能准确的确定
dev_update_off ()
read_image (Image, 'board/board-01')
dev_close_window ()
dev_open_window_fit_image (Image, 0, 0, -1, -1, WindowHandle)
set_display_font (WindowHandle, 16, 'mono', 'true', 'false')
dev_display (Image)
disp_message (WindowHandle, 'Image with radial distortions', 'window', 0, 0, 'black', 'true')
disp_continue_message (WindowHandle, 'black', 'true')
stop ()
* 用canny算子提取亚像素精度的边缘
* Extract subpixel-precise edges using the Canny filter.
edges_sub_pix (Image, Edges, 'canny', 1, 10, 40)
* 把边缘分割成线和圆
* Segment the edges into lines and circular arcs.
segment_contours_xld (Edges, SplitEdges, 'lines_circles', 5, 4, 2)
* 提取轮廓较长的边
* Select edges that are long enough to be useful for the calibration.
select_shape_xld (SplitEdges, SelectedEdges, 'contlength', 'and', 30, 100000)
dev_display (Image)
dev_set_colored (12)
dev_display (SelectedEdges)
disp_message (WindowHandle, 'Extracted edges', 'window', 0, 0, 'black', 'true')
disp_continue_message (WindowHandle, 'black', 'true')
stop ()
dev_clear_window ()
disp_message (WindowHandle, 'Performing self-calibration...', 'window', 0, 0, 'black', 'true')
* Perform the self-calibration of the radial distortions.
* 执行径向畸变的自标定 输出:需要用于标定的边、相机内参
radial_distortion_self_calibration (SelectedEdges, CalibrationEdges, 646, 492, 0.08, 42, 'division', 'variable', 0, CamParSingleImage)
* Rectify the image, i.e., remove the radial distortions.
* 矫正图像,去除径向畸变
get_domain (Image, Domain)
change_radial_distortion_cam_par ('fixed', CamParSingleImage, 0, CamParSingleImageRect)
change_radial_distortion_image (Image, Domain, ImageRectified, CamParSingleImage, CamParSingleImageRect)
* Display the distorted and undistorted image five times to visualize the
* differences between the images.
dev_display (Image)
dev_display (CalibrationEdges)
disp_message (WindowHandle, 'Edges used for calibration', 'window', 0, 0, 'black', 'true')
disp_continue_message (WindowHandle, 'black', 'true')
stop ()
dev_display (ImageRectified)
disp_message (WindowHandle, 'Image without radial distortions', 'window', 0, 0, 'black', 'true')
disp_continue_message (WindowHandle, 'black', 'true')
stop ()
* 20张图像中提取边缘进行自标定,这些边缘会进行累加
* Now perform the self-calibration using edges extracted from 20 images.
* The variable Edges will accumulate the edges extracted from the images.
gen_empty_obj (Edges)
for J := 1 to 20 by 1
read_image (Image, 'board/board-' + J$'02d')
* Extract subpixel-precise edges using the Canny filter.
edges_sub_pix (Image, ImageEdges, 'canny', 1, 10, 40)
* Segment the edges into lines and circular arcs.
segment_contours_xld (ImageEdges, SplitEdges, 'lines_circles', 5, 4, 2)
* Select edges that are long enough to be useful for the calibration.
select_shape_xld (SplitEdges, SelectedEdges, 'contlength', 'and', 30, 100000)
* Accumulate the edges.
concat_obj (Edges, SelectedEdges, Edges)
dev_display (Image)
dev_set_colored (12)
dev_display (SelectedEdges)
disp_message (WindowHandle, 'Edges extracted from image ' + J$'d', 'window', 0, 0, 'black', 'true')
endfor
dev_clear_window ()
dev_set_colored (12)
dev_display (Edges)
disp_message (WindowHandle, 'Collected edges from multiple images', 'window', 0, 0, 'black', 'true')
disp_continue_message (WindowHandle, 'black', 'true')
stop ()
dev_clear_window ()
disp_message (WindowHandle, 'Performing self-calibration...', 'window', 0, 0, 'black', 'true')
* Perform the self-calibration of the radial distortions.
radial_distortion_self_calibration (Edges, CalibrationEdges, 646, 492, 0.08, 42, 'division', 'variable', 0, CamParMultiImage)
dev_clear_window ()
dev_set_colored (12)
dev_display (CalibrationEdges)
disp_message (WindowHandle, 'Edges used for calibration', 'window', 0, 0, 'black', 'true')
disp_continue_message (WindowHandle, 'black', 'true')
stop ()
* Compute the camera parameters of the undistorted image.
change_radial_distortion_cam_par ('fixed', CamParMultiImage, 0, CamParMultiImageRect)
for J := 1 to 20 by 1
read_image (Image, 'board/board-' + J$'02d')
get_domain (Image, Domain)
* Rectify the image, i.e., remove the radial distortions.
change_radial_distortion_image (Image, Domain, ImageRectified, CamParMultiImage, CamParMultiImageRect)
* Display the distorted and undistorted image to visualize the
* differences between the images.
dev_display (Image)
disp_message (WindowHandle, 'Image with radial distortions', 'window', 0, 0, 'black', 'true')
wait_seconds (0.5)
dev_display (ImageRectified)
disp_message (WindowHandle, 'Image without radial distortions', 'window', 0, 0, 'black', 'true')
wait_seconds (0.5)
endfor
原图
校正畸变后的图像

