C#Halcon从零开发_Day17_代码标定相机(不使用标定助手!)_halcon标定 c#
目录
一、引言
二、标定过程
三、九点标定
四、博主的标定板
一、引言
硬件配置:海康800W像素
镜头:25mm
为什么使用代码标定相机?
原本博主也是按照网上的教程运用标定助手,但是拍了20多张照片,没有一张是检测通过的!无限报错找不到特征点。故而转向用代码来标定,可以实时监控异常便于修改。
二、标定过程
* 1. 生成面阵相机的初始参数(Division 畸变模型)
* 参数说明:
* - 焦距(Focus):0.025 米(25 毫米)
* - 畸变因子(Kappa):0(无畸变)
* - 像素尺寸(Sx, Sy):0.0000024 米(2.4 微米)
* - 主点坐标(Cx, Cy):(326, 247) 像素
* - 图像尺寸(Width, Height):(3840, 2748) 像素
gen_cam_par_area_scan_division(0.025, 0, 0.0000024, 0.0000024, 326, 247, 3840, 2748, StartCamPar)
* 2. 创建标定数据模型(用于存储标定图像和参数)
create_calib_data(\'calibration_object\', 1, 1, CalibDataID)
* 3. 设置相机初始参数(关联到标定数据模型)
set_calib_data_cam_param(CalibDataID, 0, \'area_scan_division\', StartCamPar)
* 4. 设置标定板描述文件(需提前生成或使用 HALCON 自带标定板)
* 例如:使用 4x4 圆点标定板,圆点间距 8 mm,圆直径4mm
CaltabDescr := \'D:/Desktop/decsrice.descr\'
set_calib_data_calib_object(CalibDataID, 0, CaltabDescr)
* 5. 加载标定图像(需从不同角度拍摄 8-15 张标定板图像,本实例只用了四张)
* 示例:假设已拍摄 15张图像,存储在路径 \'D:/Desktop/pic/\' 下
NumImages := 15
for I := 1 to NumImages by 1
* 读取图像
read_image(Image, \'D:/Desktop/pic/\' + I$\'02d\' + \'.png\')
*此处只是为了得到一次照片的大小,便于修改gen_cam_par_area_scan_division的图像尺寸
*get_image_size (Image, Width, Height)
*均值滤波,改善图像对比度
mean_image (Image, ImageMean, 6, 6)
*通过增强图像的高频区域(如边缘和拐角)来提升局部对比度,使图像细节更清晰
emphasize(ImageMean, ImageEmphasized, 21, 21, 2)
* 查找标定板区域(高斯滤波 + 阈值分割)
* 3:高斯滤波核大小 100:用于标记提取的阈值 30:校准板上标记的预期最小直径
find_caltab(ImageEmphasized, Caltab, CaltabDescr, 3, 100, 30)
*reduce_domain (ImageEmphasized, Caltab, ImageReduced)
* 提取标定板上的圆心坐标和位姿(最重要的一步)
*128: 搜索轮廓的开始阈值(一般不用更改)
*10:用于逐步降低 StartThresh
的循环步长值。默认值:10(一般不用更改)
*18: 轮廓检测的最小阈值(对比度正常的话 18已经完全够用了)
*0.2:轮廓检测的滤波参数 (这个参数最坑,大多数拍摄情况不好导致检测不出来的都是因为这个参数) 该参数推荐值是0.9 范围是0-2,
*15: 标记轮廓的最小长度
*300 \'max_diam_marks\': 要比标定板圆点直径所占的像素值大一点 (一定要改对,否则也会失败,报错:mininum thrshold while search for ellipses)
find_marks_and_pose (ImageEmphasized, Caltab, CaltabDescr, StartCamPar, 128, 10, 18, 0.2, 15, 300, RCoord, CCoord, StartPose1)
*新增补偿标定板厚度2mm
set_origin_pose(StartPose1, 0, 0, -0.002, PoseCorrected)
* 将观测数据存储到标定模型中
set_calib_data_observ_points(CalibDataID, 0, 0, I, RCoord, CCoord, \'all\',PoseCorrected)
endfor
* 6. 执行相机标定(优化内部和外部参数)
calibrate_cameras(CalibDataID, Errors)
* 7. 获取标定结果(相机内参和外参)
get_calib_data(CalibDataID, \'camera\', 0, \'params\', CameraParameters)
get_calib_data(CalibDataID, \'calib_obj_pose\', [0, 1], \'pose\', CameraPose)
* 8. 输出标定误差(评估标定精度)
* Errors 为平均投影误差(单位:像素),值越小越好(通常 < 0.5 像素)
dev_inspect_ctrl([[\'Average Projection Error: \' + Errors$\'.2f\']])
* 9. 保存标定参数(供后续使用)
write_cam_par(CameraParameters, \'camera_parameters.dat\')
write_pose(CameraPose, \'camera_pose.dat\')
*测试标定距离是否准确
CameraParameters:=[ 0.0332139, 32.4519, 2.36469e-06, 2.4e-06, -1937.35, 1983.77, 3840, 2748]
CameraPose:=[0.0796164, -0.0167615, 0.334127, 0.70856, 356.229, 302.011, 0]
NumImages := 15
for I := 1 to NumImages by 1
* 读取图像
read_image(Image, \'C:/Users/10314/Desktop/C#编程心得/运动控制卡/正运动框架_Winform/ZmotionFramework_Winf/RinseDevice/bin/Debug/Picture/\' + I$\'02d\' + \'.png\')
mean_image (Image, ImageMean, 6, 6)
emphasize(ImageMean, ImageEmphasized, 21, 21, 2)
threshold (ImageEmphasized, Region, 0, 60)
connection (Region, ConnectedRegions)
select_shape (ConnectedRegions, SelectedRegions, \'circularity\', \'and\', 0.6, 1)
select_obj (SelectedRegions, ObjectSelected1, 1)
select_obj (SelectedRegions, ObjectSelected2, 2)
area_center (ObjectSelected1, Area, Row, Column)
area_center (ObjectSelected2, Area1, Row1, Column1)
gen_cross_contour_xld (Cross, Row, Column, 366, 0.785398)
gen_cross_contour_xld (Cross1, Row1, Column1, 366, 0.785398)
image_points_to_world_plane (CameraParameters, CameraPose, Row, Column, \'mm\', X, Y)
image_points_to_world_plane (CameraParameters, CameraPose, Row1, Column1, \'mm\', X1, Y1)
distance_pp (X, Y, X1, Y1, Distance)
dev_get_window (WindowHandle)
msg:=\'距离为:\'+Distance +\'mm\'
dev_disp_text (msg, \'window\', 0, 0, \'lime green\', [], [])
stop ()
endfor
测试示意图如下:
有下图可知标定误差在 (-0.034mm , 0.004mm)之间,满足精度需求
三、九点标定
承接上文,得出相机的内参和外参后,我们还需要进行九点标定以便于把像素坐标转为实际的机械坐标,有利于机械定位。
明星算子:vector_to_hom_mat2d()
CameraParameters:=[ 0.0332139, 32.4519, 2.36469e-06, 2.4e-06, -1937.35, 1983.77, 3840, 2748]
CameraPose:=[0.0796164, -0.0167615, 0.334127, 0.70856, 356.229, 302.011, 0]
*拍摄照片,照片的方向与实际的机械方向平行
read_image(Image, \'C:/Users/10314/Desktop/C#编程心得/运动控制卡/正运动框架_Winform/ZmotionFramework_Winf/RinseDevice/bin/Debug/Picture/16.png\')
mean_image (Image, ImageMean, 20, 20)
emphasize (ImageMean, ImageEmphasize, 30, 30, 1)
threshold (ImageEmphasize, Region, 0, 60)
connection (Region, ConnectedRegions)
select_shape (ConnectedRegions, SelectedRegions, [\'area\',\'circularity\'], \'and\', [10000,0.6], [99999,1])
*我们挑选左上角开始的3X3的圆形,注意序号可能会打乱,这里最好不要用循环
select_obj (SelectedRegions, ObjectSelected1, 1)
select_obj (SelectedRegions, ObjectSelected2, 2)
select_obj (SelectedRegions, ObjectSelected3, 3)
select_obj (SelectedRegions, ObjectSelected4, 4)
select_obj (SelectedRegions, ObjectSelected5, 6)
select_obj (SelectedRegions, ObjectSelected6, 7)
select_obj (SelectedRegions, ObjectSelected7, 8)
select_obj (SelectedRegions, ObjectSelected8, 10)
select_obj (SelectedRegions, ObjectSelected9, 11)
area_center (ObjectSelected1, Area1, Row1, Column1)
area_center (ObjectSelected2, Area2, Row2, Column2)
area_center (ObjectSelected3, Area3, Row3, Column3)
area_center (ObjectSelected4, Area4, Row4, Column4)
area_center (ObjectSelected5, Area5, Row5, Column5)
area_center (ObjectSelected6, Area6, Row6, Column6)
area_center (ObjectSelected7, Area7, Row7, Column7)
area_center (ObjectSelected8, Area8, Row8, Column8)
area_center (ObjectSelected9, Area9, Row9, Column9)
Row:=[Row1,Row1,Row3,Row4,Row5,Row6,Row7,Row8,Row9]
Column:=[Column1,Column2,Column3,Column4,Column5,Column6,Column7,Column8,Column9]
*1.把像素点转为世界坐标
image_points_to_world_plane (CameraParameters, CameraPose, Row, Column, \'mm\', Y1, X1)
*2.确定机械坐标系下这九个点的坐标,设左上角第一点中心为(0,0) X,Y轴方向,推导出其他的点为:
X:=[0,0.8,1.6,0,0.8,1.6,0,0.8,1.6]
Y:=[0,0,0,0.8,0.8,0.8,1.6,1.6,1.6]
***需要注意的是X对应像素坐标的Column,Y对应像素坐标的Row
vector_to_hom_mat2d (Y1, X1, Y, X, HomMat2D)
*HomMat2D即是我们要求的仿射变换矩阵,测试第二行第四列,索引为9的点看正确与否
select_obj (SelectedRegions, ObjectSelected16, 9)
area_center (ObjectSelected16, Area16, Row16, Column16)
*顺序为像素坐标--世界坐标--机械坐标
image_points_to_world_plane (CameraParameters, CameraPose, Row16, Column16, \'mm\', Y16, X16)
*运用仿射变换点算子获取Y X
affine_trans_point_2d (HomMat2D, Y16, X16, Qy, Qx)
dev_get_window (WindowHandle)
msg:= \'X:\' +Qx+\' Y:\'+Qy
dev_disp_text (msg, \'window\', 1, 1, \'lime green\', [], [])
最终结果为:
由图可知,在X轴方向误差为0.08mm,Y轴方向误差为0.0229mm,基本满足需求。
四、博主的标定板
下图可以看到,无论拍的亮还是暗,都可以正常通过。
ps:这些图片如果使用标定助手,全部会失败。