Halcon 像机标定原理推导

一、 平移和旋转矩阵推导

平移:

 

二、标定流程

最详细、最完整的相机标定讲解-CSDN博客


相机标定步骤:

1、打印一张棋盘格,把它贴在一个平面上,作为标定物。
2、通过调整标定物或摄像机的方向,为标定物拍摄一些不同方向的照片。
3、从照片中提取棋盘格角点。
4、估算理想无畸变的情况下,五个内参和六个外参。
5、应用最小二乘法估算实际存在径向畸变下的畸变系数。
6、极大似然法,优化估计,提升估计精度。

坐标系介绍:

摄像机坐标系 、 图像坐标系、像素坐标系 和 世界坐标系(参考坐标系) 。

像素坐标系:

描述物体成像后的像点在数字图像中的坐标,单位是像素个数 

图像坐标系:

描述了投影变换的过程,是沟通像素坐标系的 单位M

相机坐标系:

相机坐标系是以相机的光轴作为Z轴,是一个很重要的桥梁,沟通了图像坐标系,单位是M

世界坐标系:

是一个参考坐标系,单位是M

坐标转换基础

示意图如下:

是相机坐标系 Z 轴与像平面夹角,一般情况下 Z 轴与像平面垂 直, 值为 90 度。

一、图像坐标系(x,y)----->像素坐标系(u,v)--平移

1.两坐标轴互相垂直

 

在这个过程中其实就是一个单位换算,距离单位、 像素单位的换算(像素与毫米间的线性关系

 二、相机坐标系(Xc,Yc,Zc)---->图像坐标系(x,y,1) 

小孔成像的原理---相似三角形

3D--->2D  

  

 当相机平面和像平面平行的时候cos(90)=0,那么此时就是红色字体的部分

 到次内参的部分已经全部完成:

 从上面我们可以看出有6个未知数(Sx, Sy,cx,cy,f,selta)

 三、世界坐标系(Xw,Yw,Zw)至相机坐标系(Xc,Yc,Zc)

 3维的旋转平移

最后一步:四个坐标之间矩阵相乘

 1.外参数矩阵。告诉你现实世界点(世界坐标)是怎样经过旋转和平移,然后落到另一个现实世界点(摄像机坐标)上。

2.内参数矩阵。告诉你上述那个点在1的基础上,是如何继续经过摄像机的镜头、并通过针孔成像和电子转化而成为像素点的。

halcon  标定 

gen_caltab (
7, //x方向的标记数;

7, //y方向的标记数;

0.0075, //标记点圆心之间的距离,单位:米;

0.5, //标记点直径与标记点圆心之间距离的比值;

‘C:/Users/Administrator/Desktop/caltab.descr’,//标定板的描述文件的保 存路径;

‘caltab.ps’,//描述标定板的一些信息,打印标定板时会用到)

 标定文件 :

标定中记录的是标定板的物理坐标

模式 

通用模式

 

多项式模式 

结果分析 

* Calibration 01: Code generated by Calibration 01
CameraParameters := ['area_scan_polynomial',
0.00839696, 焦距长
1978.45,  径向畸变系数K1
2.43869e+07, 径向畸变系数K2
-2.86552e+12,径向畸变系数K3
0.39432, 切向畸变系数p1
-0.126256, 切向畸变系数p2
8.34956e-06,  //单位像素的宽是8.34um
8.3e-06,//单位像素的高是8.3um
350.495,  图像坐标系原点在像素坐标系原点的位置
294.846,图像坐标系原点在像素坐标系原点的位置
768,    图库的大小
576] 图库的大小    
CameraPose := [-0.275525,0.104421,2.28401,6.7113,323.595,32.4834,0]
stop ()


对应的内矩阵为
            fx    0   u0   0
    K =     0    fy   v0    0
            0    0     1    0
就是 

    8.34956e-06    0           350.495   0

K=    0         8.3e-06        294.846    0 
      0          0              1


焦距f=0.00839696 m (注意单位是m)



模型不一样

* Calibration 01: Code generated by Calibration 01
CameraParameters := ['area_scan_division',0.00839081,-1987.45,8.35072e-06,8.3e-06,362.43,291.71,768,576]
CameraPose := [-0.302872,0.111578,2.27866,6.68474,322.99,32.6195,0]
stop ()

相机标定之后的外参数

R   T

0    1

是一个4*4 的矩阵

相机位姿,即外参(旋转矩阵+平移矢量)

CameraPose := [-0.0091626,-0.00625214,0.700967,2.46926,358.933,179.443,0]

代码标定

NumImages := 10
read_image (Images, 'calib/calib-3d-coord-' + [1:NumImages]$'02d')
for I := 1 to NumImages by 1
    select_obj (Images, Image, I)
    dev_display (Image)
    Message := 'Find calibration plate in\nall calibration images (' + I + '/' + NumImages + ')'
    disp_message (WindowHandle1, Message, 'window', 12, 12, 'black', 'true')
    * Find the calibration plate
    find_calib_object (Image, CalibDataID, 0, 0, I - 1, [], [])
    get_calib_data (CalibDataID, 'camera', 0, 'init_params', StartCamPar)
    get_calib_data_observ_points (CalibDataID, 0, 0, I - 1, Row, Column, Index, Pose)
    get_calib_data_observ_contours (Contours, CalibDataID, 'caltab', 0, 0, I - 1)
     *draw 
    gen_cross_contour_xld (Cross, Row, Column, 6, 0.785398)
    dev_set_color ('green')
    dev_display (Contours)
    dev_set_color ('yellow')
    dev_display (Cross)
endfor
disp_continue_message (WindowHandle1, 'black', 'true')
stop ()
calibrate_cameras (CalibDataID, Error)
get_calib_data (CalibDataID, 'camera', 0, 'params', CamParam)
gen_empty_obj (Maps)

添加厚度 

ImgPath := '3d_machine_vision/calib/'
dev_close_window ()
dev_open_window (0, 0, 652, 494, 'black', WindowHandle)
dev_update_off ()
dev_set_draw ('margin')
dev_set_line_width (3)
set_display_font (WindowHandle, 14, 'mono', 'true', 'false')
* 
* Calibrate the camera.
* 
*初始化标定板参数
*0.016 焦距
*0 Kappa初始值
*0.0000074, 0.0000074  X和Y像素当量
*326, 247 图像中心
*652, 494 图像大小
gen_cam_par_area_scan_division (0.016, 0, 0.0000074, 0.0000074, 326, 247, 652, 494, StartCamPar)
create_calib_data ('calibration_object', 1, 1, CalibDataID)
set_calib_data_cam_param (CalibDataID, 0, [], StartCamPar)
*设置描述信息
set_calib_data_calib_object (CalibDataID, 0, 'caltab_30mm.descr')
*描述一个X和Y方向黑点个数为7、间距为0.0125、标定比例0.5、caltab.descr:生成的标定板文件
* gen_caltab (7, 7, 0.0125, 0.5, 'caltab.descr', 'caltab.ps')
NumImages := 10
* Note, we do not use the image from which the pose of the measurement plane can be derived
for I := 1 to NumImages by 1
    read_image (Image, ImgPath + 'calib_' + I$'02d')
    dev_display (Image)
    *寻找标定板
    find_calib_object (Image, CalibDataID, 0, 0, I, [], [])
    get_calib_data_observ_contours (Caltab, CalibDataID, 'caltab', 0, 0, I)
    dev_set_color ('green')
    dev_display (Caltab)
endfor
*执行标定
*Error:成功校准后,以像素为单位返回优化的反投影的均方根误差(RMSE),如果与0.1相差较大,则校准效果不佳
calibrate_cameras (CalibDataID, Error)
curIndex:=10
*获取内参
get_calib_data (CalibDataID, 'camera', 0, 'params', CamParam)
*获取外参:位姿
*先读取一个标定板,读取基于此标定板下的相机的位姿
*所以只需要一张标定板图像就可以获取相机的外参(基于此标定板的外参),Index是索引为10的标定板图像
get_calib_data (CalibDataID, 'calib_obj_pose', [0,curIndex], 'pose', CameraPose)
disp_3d_coord_system (WindowHandle, CamParam, CameraPose, 0.01)
CalTabThickness:=0.5
*重新设置原点,Z方向偏移0.5
set_origin_pose (CameraPose, 0, 0, CalTabThickness, PoseNewOrigin)
disp_3d_coord_system (WindowHandle, CamParam, PoseNewOrigin, 0.01)
* Write the internal camera parameters to a file
write_cam_par (CamParam, 'camera_parameters.dat')
Message := 'Interior camera parameters have'
Message[1] := 'been written to file'
disp_message (WindowHandle, Message, 'window', 12, 12, 'red', 'false')
* clear_calib_data (CalibDataID)

内参矫正 

* Calibration 01: Code generated by Calibration 01
* 相机内参
CameraParameters := ['area_scan_division',0.00839081,-1987.45,8.35072e-06,8.3e-06,    362.43,  291.71,  768, 576]
* 矫正后的内参       ['area_scan_division', 0.00839081, 0.0,   8.52659e-06,8.3954e-06, 361.554, 291.807, 768, 576]

* 位姿
CameraPose := [-0.302872,0.111578,2.27866,6.68474,322.99,32.6195,0]
stop ()

* 进行相机的径向畸变矫正  得到新的相机内参   一般来说切向畸变没有那么大。这里就想不管
change_radial_distortion_cam_par ('adaptive', CameraParameters, 0, NewCamParamOut)
  
NumImages := 10
*
read_image (Images, 'calib/calib-3d-coord-' + [1:NumImages]$'02d')
for I := 1 to NumImages by 1
    select_obj (Images, Image, I)
    *对发生径向畸变的图像形成映射,图像的映射到第一个参数中
    gen_radial_distortion_map (Map, CameraParameters, NewCamParamOut, 'bilinear')
    * 对图像进行畸变矫正
    map_image (Image, Map, ImageMapped)
    * ImageMapped 就是畸变矫正后的图像
    
    
    * 将像素坐标---》实际坐标
    image_points_to_world_plane (NewCamParamOut, CameraPose, 100, 100, 'm', X, Y)
    
    
endfor
 

code 

* **********************************************************
* 使用标定助手标定图像
* **********************************************************
dev_update_window ('off')
dev_close_window()
dev_open_window(0, 0, 640, 480, 'black', WindowHandle)
* 绘制轮廓线,不填充
dev_set_draw('margin')
dev_set_line_width(3)
set_display_font(WindowHandle, 14, 'mono', 'true', 'false')
* 相机参数
CameraParameters := ['area_scan_division',0.0186694,-515.222,8.35294e-006,8.3e-006,241.908,254.532,640,480]
* 相机位姿
CameraPose := [0.010563,-0.00289847,0.290847,358.416,32.4994,91.0794,0]
* 加载图像
read_image (Image, 'scratch/scratch_calib_10.png')
TmpCtrl_PlateDescription := 'D:/Program Files/MVTec/HALCON-18.11-Progress/calib/caltab_30mm.descr'
TmpCtrl_FindCalObjParNames := ['gap_tolerance','alpha','skip_find_caltab']
TmpCtrl_FindCalObjParValues := [1,1,'false']
* 创建数据模型
create_calib_data ('calibration_object', 1, 1, CalibHandle)
* 参数
set_calib_data_cam_param (CalibHandle, 0, [], CameraParameters)
* 标定板初始化
set_calib_data_calib_object (CalibHandle, 0, TmpCtrl_PlateDescription)
* 提取标记点
find_calib_object (Image, CalibHandle, 0, 0, 0, TmpCtrl_FindCalObjParNames, TmpCtrl_FindCalObjParValues)
* 得到标记点的坐标
get_calib_data_observ_points (CalibHandle, 0, 0, 0, TmpCtrl_MarkRows, TmpCtrl_MarkColumns, TmpCtrl_Ind, CameraPose)
* 标定板厚度 0.001
set_origin_pose (CameraPose, 0.0, 0.0, 0.001, CameraPose)

* **********************************************************
* 测量结果 → 世界坐标
* **********************************************************
TmpCtrl_ImageRows := [TmpCtrl_MarkRows[0], TmpCtrl_MarkRows[1]]
TmpCtrl_ImageColumns := [TmpCtrl_MarkColumns[0], TmpCtrl_MarkColumns[1]]
* 得到一条线
gen_contour_polygon_xld (TmpObj_ImageContour, TmpCtrl_ImageRows, TmpCtrl_ImageColumns)
* 图像坐标 → 世界坐标
image_points_to_world_plane (CameraParameters, CameraPose, TmpCtrl_ImageRows, TmpCtrl_ImageColumns, 'mm', TmpCtrl_WorldX, TmpCtrl_WorldY)
* 计算世界坐标距离
distance_pp (TmpCtrl_WorldY[0], TmpCtrl_WorldX[0], TmpCtrl_WorldY[1], TmpCtrl_WorldX[1], TmpCtrl_Distance)
* 显示结果
dev_display (Image)
dev_set_color ('red')
dev_display (TmpObj_ImageContour)
* 按 F5 继续
disp_continue_message (WindowHandle, 'black', 'true')
stop ()

* **********************************************************
* 轮廓 → 世界坐标
* **********************************************************
* 得到轮廓
gen_contour_polygon_xld (TmpObj_ImageContour, TmpCtrl_MarkRows, TmpCtrl_MarkColumns)
* 轮廓 → 世界坐标
contour_to_world_plane_xld (TmpObj_ImageContour, TmpObj_WorldContour, CameraParameters, CameraPose, 'mm')
* 提取中心点
get_contour_xld (TmpObj_WorldContour, TmpCtrl_WorldX, TmpCtrl_WorldY)
* 显示结果
dev_display (Image)
dev_set_color ('yellow')
dev_display (TmpObj_ImageContour)
* 按 F5 继续
disp_continue_message (WindowHandle, 'black', 'true')
stop ()

* **********************************************************
* 图像矫正
* **********************************************************
* 图像宽度,mm → m
TmpCtrl_RectificationWidth := 73
TmpCtrl_RectificationWidth := TmpCtrl_RectificationWidth / 1000.0
* 调整原点使得标定板大致居中
set_origin_pose (CameraPose, -0.5*TmpCtrl_RectificationWidth, -0.4*TmpCtrl_RectificationWidth, 0, TmpCtrl_RectificationPose)
* 创建投射图
gen_image_to_world_plane_map (TmpObj_RectificationMap, CameraParameters, TmpCtrl_RectificationPose, 640, 480, 640, 480, TmpCtrl_RectificationWidth / 640, 'bilinear')
* 释放内存
clear_calib_data (CalibHandle)
* 使用投射图校准
map_image (Image, TmpObj_RectificationMap, TmpObj_RectifiedImage)
* 显示结果
dev_display (TmpObj_RectifiedImage)
dev_update_window ('on')














最新标定
dev_close_window ()
dev_open_window (0, 0, 768 / 2, 576 / 2, 'black', WindowHandle1)
set_display_font (WindowHandle1, 14, 'mono', 'true', 'false')
dev_update_off ()
dev_set_draw ('margin')
dev_set_line_width (2)
* 
* Calibrate the camera.
* 
CalTabDescrFile := 'caltab_big.descr'
* Make sure that the file 'CaltabName' is in the current directory,
* the HALCONROOT/calib directory, or use an absolute path
gen_cam_par_area_scan_division (0.008, 0, 0.0000086, 0.0000086, 384, 288, 768, 576, StartCamPar)
create_calib_data ('calibration_object', 1, 1, CalibDataID)
set_calib_data_cam_param (CalibDataID, 0, [], StartCamPar)
set_calib_data_calib_object (CalibDataID, 0, CalTabDescrFile)
NumImages := 10
read_image (Images, 'calib/calib-3d-coord-' + [1:NumImages]$'02d')
for I := 1 to NumImages by 1
    select_obj (Images, Image, I)
    dev_display (Image)
    Message := 'Find calibration plate in\nall calibration images (' + I + '/' + NumImages + ')'
    disp_message (WindowHandle1, Message, 'window', 12, 12, 'black', 'true')
    * Find the calibration plate
    find_calib_object (Image, CalibDataID, 0, 0, I - 1, [], [])
    get_calib_data (CalibDataID, 'camera', 0, 'init_params', StartCamPar)
    get_calib_data_observ_points (CalibDataID, 0, 0, I - 1, Row, Column, Index, Pose)
    get_calib_data_observ_contours (Contours, CalibDataID, 'caltab', 0, 0, I - 1)
     *draw 
    gen_cross_contour_xld (Cross, Row, Column, 6, 0.785398)
    dev_set_color ('green')
    dev_display (Contours)
    dev_set_color ('yellow')
    dev_display (Cross)
endfor
disp_continue_message (WindowHandle1, 'black', 'true')
stop ()
calibrate_cameras (CalibDataID, Error)
get_calib_data (CalibDataID, 'camera', 0, 'params', CamParam)
gen_empty_obj (Maps)
for I := 1 to NumImages by 1
    * Obtain the pose of the calibration table
    get_calib_data (CalibDataID, 'calib_obj_pose', [0,I - 1], 'pose', Pose)
    set_origin_pose (Pose, -1.125, -1.0, 0, PoseNewOrigin)
    * Generate map
    gen_image_to_world_plane_map (MapSingle, CamParam, PoseNewOrigin, 768, 576, 900, 800, 0.0025, 'bilinear')
    concat_obj (Maps, MapSingle, Maps)
endfor
* 
* Map the images
* 
dev_open_window (0, 391, 900 / 2, 800 / 2, 'black', WindowHandle2)
set_display_font (WindowHandle2, 14, 'mono', 'true', 'false')
Button := 0
NumImage := 1
for I := 1 to NumImages by 1
    dev_set_window (WindowHandle1)
    dev_set_part (0, 0, 575, 767)
    dev_clear_window ()
    select_obj (Images, Image, I)
    dev_display (Image)
    select_obj (Maps, MapSingle, I)
    map_image (Image, MapSingle, ImageMapped)
    dev_set_window (WindowHandle2)
    dev_set_part (0, 0, 799, 899)
    dev_clear_window ()
    dev_display (ImageMapped)
    Message := 'Calibration image (' + I + '/' + NumImages + ')'
    disp_message (WindowHandle1, Message, 'window', 12, 12, 'black', 'true')
    Message := 'Mapped image'
    disp_message (WindowHandle2, Message, 'window', 12, 12, 'black', 'true')
    if (I < NumImages)
        disp_continue_message (WindowHandle1, 'black', 'true')
        stop ()
    endif
endfor

 三、畸变

最详细、最完整的相机标定讲解_a083614的专栏-CSDN博客_相机标定方法

畸变一般可以分为:径向畸变、切向畸变
ps:畸变是相机本身的固有特性,和相机内参相同,标定一次之后即可。

畸变计算

 其中r^{2}=x^{2}+y^{2}

径向畸变来自于透镜形状。
切向畸变来自于整径向畸变(桶形畸变和枕形畸变):
实际摄像机的透镜总是在成像仪的边缘产生显著的畸变,这种现象来源于“筒形”或“鱼眼”的影响。
如下图,光线在原理透镜中心的地方比靠近中心的地方更加弯曲。对于常用的普通透镜来说,这种现象更加严重。筒形畸变在便宜的网络摄像机中非常厉害,但在高端摄像机中不明显,因为这些透镜系统做了很多消除径向畸变的工作。

*1、校正径向畸变,得到新的相机内参

change_radial_distortion_cam_par (‘adaptive’, CameraParameters, 0, CamParamOut)

stop ()



Image Acquisition 02: Code generated by Image Acquisition 02

open_framegrabber (‘GigEVision’, 0, 0, 0, 0, 0, 0, ‘default’, -1, ‘default’, -1, ‘false’, ‘default’, ‘CAMERA_QBY_DM’, 0, -1, AcqHandle)

grab_image_start (AcqHandle, -1)

while (true)

grab_image_async (Image, AcqHandle, -1)

*2、对发生径向畸变的图像生成投影映射,图像的映射数据存在第一个参数中

gen_radial_distortion_map (Map, CameraParameters, CamParamOut, ‘bilinear’)

*3、对图像进行畸变校正

map_image (Image, Map, ImageMapped)

endwhile

close_framegrabber (AcqHandle)

注意:相机标定之后,相机焦距、上下位置不能再动,否则需要重新标定。

步骤:

1)通过标定求出相机内参。

2)通过有畸变的内参求出无畸变的内参。用chage_radial_distortion_cam_par()函数完成。

3)求出有畸变的内参和无畸变的内参之间的映射关系。用

gen_radial_distortion_map()函数

4)将上边的映射关系作用到产生畸变的物体当中,完成畸变校正

四、matalb 标定

生成标定板

J = (checkerboard(300,4,5)>0.5);
figure, imshow(J);

 

基本步骤

https://gene2022.blog.csdn.net/article/details/124125912?spm=1001.2101.3001.6661.1&utm_medium=distribute.pc_relevant_t0.none-task-blog-2%7Edefault%7EBlogCommendFromBaidu%7ERate-1-124125912-blog-103223103.235%5Ev40%5Epc_relevant_3m_sort_dl_base1&depth_1-utm_source=distribute.pc_relevant_t0.none-task-blog-2%7Edefault%7EBlogCommendFromBaidu%7ERate-1-124125912-blog-103223103.235%5Ev40%5Epc_relevant_3m_sort_dl_base1&utm_relevant_index=1icon-default.png?t=N7T8https://gene2022.blog.csdn.net/article/details/124125912?spm=1001.2101.3001.6661.1&utm_medium=distribute.pc_relevant_t0.none-task-blog-2~default~BlogCommendFromBaidu~Rate-1-124125912-blog-103223103.235%5Ev40%5Epc_relevant_3m_sort_dl_base1&depth_1-utm_source=distribute.pc_relevant_t0.none-task-blog-2~default~BlogCommendFromBaidu~Rate-1-124125912-blog-103223103.235%5Ev40%5Epc_relevant_3m_sort_dl_base1&utm_relevant_index=1

1、加载文件

2、标定

 3、标定结果

 

Matlab的存储格式为(一般对于非球面相机,k3为0)

那么我可以看做是其畸变为

对应matlab 中的结果为:

4、径向畸变参数选择

 

5、结果分析

  • 5
    点赞
  • 74
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值