“一滴水,用显微镜看,也是一个大世界。”
在项目中,偶尔会遇到由于产品形状、拍摄位置等原因导致标定板很难放置的情况,此时可以考虑使用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
原图
校正畸变后的图像

