Halcon 双相机标定与拼图(二)

一、概述

这种标定有两种模式,有一个标定板和多个标定板两种

一个标定板

两个相机的重叠区域比较大,那么我们可以把标定板放到那个重叠区域来统一坐标系,如下

这种是只需要一个标定板,这种是推荐的方式 。这种是比较简单的,今天主要看的第二种

多个标定板

由于要求的视野没有重叠区域,或者重叠的区域太小了,那么我们可以采用这种方式

 这个方法就是对上面绿色的部分,两个标定板的关系很重要,会直接影响到最后的图像结果

二、算法分析

halcon例程:双相机标定进行高精度对象镶嵌-工业视觉/halcon-少有人走的路

1、相机标定

单独标定连个相机Cam1 Cam2

*两个相机的内参  之前已经标定过了
gen_cam_par_area_scan_division (0.01619, -734.789, 7.402e-006, 7.4e-006, 324.911,\256.894, 640, 480, CamParam1)
gen_cam_par_area_scan_division (0.0162584, -763.35, 7.39842e-006, 7.4e-006, 324.176,\245.371, 640, 480, CamParam2)
* 

2、获得姿态Pose

获得各自标定板在各自相机的字体

* 通过相机的内参来而且拍在两个相机中拍同一个标定板
CaltabName := 'caltab_30mm.descr'
*标定参数
*2 表示的是两个相机 
* 表示的是只有一个物体object ,这里是指只有一个标定板
create_calib_data ('calibration_object', 2, 1, CalibDataID)*设置标定参数
set_calib_data_calib_object (CalibDataID, 0, CaltabName)set_calib_data_cam_param (CalibDataID, 0, [], CamParam1)
set_calib_data_cam_param (CalibDataID, 1, [], CamParam2)
* 
* Find and display the calibration plate in the images.
dev_set_window (WindowHandle1)
find_calib_object (Image1, CalibDataID, 0, 0, 0, [], [])
* 这一步很重要,重要就在计算的那个Pose 表示在相机1 下的姿态*      get_calib_data_observ_points( : : CalibDataID, CameraIdx, CalibObjIdx, CalibObjPoseIdx : Row, Column, Index, Pose)功能:从标定数据模型中提取标记点坐标。
*      参数1:CalibDataID:标定数据模型句柄;
*      参数2:CameraIdx:摄像机索引,默认值为0;
*      参数3:CalibObjIdx:标定板索引,默认值0;
*      参数4:CalibObjPoseIdx:观察到的标定板位姿的索引;
*      参数1:( Row, Column):检测到的标记点坐标;
*      参数2: Index:检测到的点与观测到的标定板上的点的对应关系。
*      参数3: Pose:粗略估计观测到的标定板相对于观测相机的姿态。
get_calib_data_observ_points (CalibDataID, 0, 0, 0, RowCoord1, ColumnCoord1, Index1, Pose1)*    get_calib_data( : : CalibDataID, ItemType, ItemIdx, DataName : DataValue):查询存储在标定模型中的数据(比如相机的内参和外参)。
*    控制输入参数1:CalibDataID:标定数据模型句柄;
*    控制输入参数2:ItemType:数据类型,'camera':表示要获取数据类型是与摄像机相关数据; 'calib_obj_pose':表示要获取数据类型与标定板位姿相关数据
*    控制输入参数3:ItemIdx:ItemIdx:输入参数,ItemType='camera'时,ItemIdx表示摄像机索引;ItemType='calib_obj_pose'时,ItemIdx是一个数组[CalibObjIdx, CalibObjPoseIdx],其中CalibObjIdx表示标定板索引,CalibObjPoseIdx表示参考位姿的图像索引;
*    控制输入参数4:DataName:输入要查询的属性名,'params'表示摄像机内参数;  'pose'表示摄像机外参数;
gen_cross_contour_xld (Cross, RowCoord1, ColumnCoord1, 6, 0.785398)
get_calib_data_observ_contours (Caltab1, CalibDataID, 'caltab', 0, 0, 0)
dev_display (Caltab1)
* 
dev_set_window (WindowHandle2)
find_calib_object (Image2, CalibDataID, 1, 0, 0, [], [])
* 这一步很重要,重要就在计算的那个Pose 表示在相机2 下的姿态
get_calib_data_observ_points (CalibDataID, 1, 0, 0, RowCoord2, ColumnCoord2, Index2, Pose2)
gen_cross_contour_xld (Cross2, RowCoord2, ColumnCoord2, 6, 0.785398)
get_calib_data_observ_contours (Caltab2, CalibDataID, 'caltab', 1, 0, 0)
dev_display (Caltab2)

 此时标定板的情况如下:

3、设置标定板等的厚度

*标定板的厚度     为什么要除以1000
ThicknessCaliper := 2.9 / 1000.0 
*平台的厚度
ThicknessPlate := 5.65 / 1000.0
*DiffHeight=先还原到基准面(+ThicknessCaliper)然后再加上要测量的产品的高度(-ThicknessPlate)=ThicknessPlate-ThicknessCaliper
DiffHeight := ThicknessPlate - ThicknessCaliper
*

4、设置裁剪图像以及在不同方向上的差

这个是在宏观意义的,这里只是画一个示意图

第一个标定和第二个标定板之间的间距如下

*=================================================================================================================================
*DistancePlates 表示连个标定板之间的距离
DistancePlates := 0.06488
*定义马赛克图像的像素大小(以米为单位)。
PixelSize := 0.0001
* 
*第一(左)图像,确定必要的转变的姿势很简单。
*您可以根据预先选择的边框宽度在图像坐标中定义校正图像的左上角
BorderInPercent := 3
get_image_size (Image1, WidthImage1, HeightImage1)
* gen_cross_contour_xld (Cross1, HeightImage1,  WidthImage1,600, 0.785398)* 图片中左上角的点
UpperRow := HeightImage1 * BorderInPercent / 100.0
LeftColumn := WidthImage1 * BorderInPercent / 100.0
*然后,这个点必须转换成世界坐标。
*     image_points_to_world_plane( : : CameraParam, WorldPose, Rows, Cols, Scale : X, Y):将图像点变换到世界坐标系的z=0平面中,*                        并返回它们在3D坐标中的X和Y值。*     Map1 输出隐射*     控制输入参数1: CameraParam:相机内参;*     控制输入参数2:WorldPose:相机坐标系中世界坐标系的三维姿态(相机外参);*     控制输入参数3: (Rows, Cols):待转换点的坐标;*     控制输入参数4:Scale:比例或尺寸,Default value: 'm';*     控制输出参数:X:世界坐标系中点的X坐标;*     控制输出参数:Y:世界坐标系中点的Y坐标。
image_points_to_world_plane (CamParam1, Pose1, UpperRow, LeftColumn, 'm', Left_Up_X, Left_Up_Y)
* 为了确定校正图像的高度,我们需要定义一个位于第一个图像的下边界附近的点。*左下角的点 
LowerRow := HeightImage1 * (100 - BorderInPercent) / 100.0
LeftColumn := WidthImage1 * BorderInPercent / 100.0*同样,这一点必须转换成世界坐标系。
image_points_to_world_plane (CamParam1, Pose1, LowerRow, LeftColumn, 'm', Left_Down_X, Left_Down_Y)
*高度可以确定为左上点和靠近下图像边界的点之间的垂直距离,以校正图像的像素表示。
HeightRect := int((Left_Down_Y - Left_Up_Y) / PixelSize)*类似地,宽度可以由位于两个图像的重叠区域中的一个点来确定,在第一张图片的右边界附近。
OverlapInPercent := 10
RightColumn := WidthImage1 * (100 - OverlapInPercent / 2.0) / 100.0
image_points_to_world_plane (CamParam1, Pose1, UpperRow, RightColumn, 'm', Left_Right_X, Left_Right_Y)
WidthRect := int((Left_Right_X - Left_Up_X) / PixelSize)* 具体示意图如下*    X --------------WidthRect-------------- X*   |*   |*   |*   |*HeightRect*   |*   |*   |*   |*   |*  X*============================================================================================================================

 5、计算第二张图像的拼接位置

                              WidthRect, HeightRect, PixelSize, 'bilinear')
* 
* Generate a new homogeneous transformation matrix.
*生成一个新的齐次变换矩阵。
hom_mat3d_identity (HomMat3DIdentity)
* 
* The second image must be rectified such that it fits exactly
* to the right of the first rectified image. This means that the
* upper left corner of the second rectified image must be identical
* with the upper right corner of the first rectified image.
* Therefore, we need to know the coordinates of the upper right corner
* of the first rectified image in the coordinate system that is defined
* by the calibration plate in the second image.
* First, we express the upper right corner of the first rectified image
* in the world coordinate system that is defined by the calibration plate
* in the first image. It can be determined by a transformation from
* the origin into the upper left corner of the
* first rectified image (a translation) followed by a translation along
* the upper border of the first rectified image. Together with the shift
* that compensates the thickness of the calibration plate, this
* transformation is represented by the homogeneous transformation matrix:
*第二幅图像必须校正,使其正好适合第一幅校正图像的右侧。
*这意味着第二校正图像的左上角必须与第一校正图像的右上角相同。
*因此,我们需要知道第一个校正图像的右上角在由第二个图像中的校准板定义的坐标系中的坐标。
*首先,我们在世界坐标系中表示第一幅校正图像的右上角,该坐标系由第一幅图像中的校准板定义。
*它可以通过从原点到第一校正图像的左上角的变换(平移)以及沿着第一校正图像的上边界的平移来确定。
*与补偿校准板厚度的位移一起,该变换由齐次变换矩阵表示:
second:=Left_Up_X + PixelSize * WidthRect* 第二张图的拼接位置  具体示意图如下
*              first Image                        second Image*  Left_Up_X ----------------------PixelSize * WidthRect---------X----------------------*   |                                          |*   |                                          |*   |                                          |*   |                                          |*   |                                          |*HeightRect                                    |*   |                                          |*   |                                          |*   |                                          |*   |                                          |*   |                                          |*   X                                          |hom_mat3d_translate_local (HomMat3DIdentity, Left_Up_X + PixelSize * WidthRect, Left_Up_Y, DiffHeight, cp1Hur1)

6、第二相机和第一个相机的关系

7、姿态转变矩阵计算 

主要是为了得到第二个相机到世界坐标系之间的矩阵变换

DistancePlates是基于第一个相机(0,0,0)到第二个相机的关系

这个就是很简单了,最重要的一步就算完了

get_image_size (Image1, Width1, Height1)
DistancePlates:=0.06488
*DistancePlates:= WidthRect*PixelSize
*DistancePlates 是第一个相机-》第二个相机的矩阵 只是个X方向的移动  1H2
hom_mat3d_translate_local (HomMat3DIdentity, DistancePlates, 0, 0, cp1Hcp2)
* 
* Then, we need the transformation between the two calibration plates of
* the calibration object. The homogeneous transformation matrix cp1Hcp2
* describes how the world coordinate system defined by the calibration plate
* in the first image is transformed into the world coordinate system
* defined by the calibration plate in the second image. This transformation
* must be known beforehand from a precise measurement of the calibration object.
* From these two transformations, it is easy to derive
* the transformation that transforms the world coordinate system
* of the second image such that its origin lies in the upper left corner
* of the second rectified image. For this, the two transformations
* have to be combined appropriately.
*然后,我们需要校准对象的两个校准板之间的转换。
*齐次变换矩阵cp1Hcp2描述如何将第一图像中校准板定义的世界坐标系转换为第二图像中校准板定义的世界坐标系。
*这种转换必须事先从校准对象的精确测量中得知。
*从这两个变换,很容易导出变换第二图像的世界坐标系的变换,使得其原点位于第二校正图像的左上角。
*为此,必须将这两个转换适当地结合起来。
*从第二个相机-》第一个相机的比那换
*1H2-》2H1
hom_mat3d_invert (cp1Hcp2, cp2Hcp1)
*相机2-》世界坐标   =2H1*1He  计算从相机二坐标到世界坐标的Pose 转换
hom_mat3d_compose (cp2Hcp1, cp1Hur1, cp2Hul2)
* 
* With this, the pose of the calibration plate in the second image
* can be modified such that the origin of the world coordinate system
* lies in the upper left corner of the second rectified image.
*这样,可以修改第二图像中校准板的姿态,使得世界坐标系的原点位于第二校正图像的左上角。
*和上面的同一个道理
pose_to_hom_mat3d (Pose2, cam2Hcp2)
hom_mat3d_compose (cam2Hcp2, cp2Hul2, cam2Hul2)
hom_mat3d_to_pose (cam2Hul2, PoseNewOrigin2)
* 
* With the resulting new pose and the size of the rectified image,
* which can be the same as for the first rectified image,
* the rectification map for the second image can be derived.
*根据得到的新姿势和校正图像的大小(其可以与第一校正图像相同),可以导出第二图像的校正地图。

8、生成映射

我们前面算这么多都是为了拿到各个相机的映射,如果是三个相机就是三个映射,四个相机就是4个映射

gen_image_to_world_plane_map (MapSingle2, CamParam2,\PoseNewOrigin2, Width, Height,\WidthRect, HeightRect, PixelSize, 'bilinear')

9、贴图

三、代码与效果

*示例程序展示了如何用校准的相机实现高精度的拼接。
*进一步的信息可以在解决方案指南III-C 3D视觉,第9章和第10章中找到。
*本示例程序在第9章中用于说明校准镶嵌。
* 前提,那个标定板放在两个产品的中间,而且两个相机的视野中都能看到完整的标定板
dev_update_off ()
ImgPath := '3d_machine_vision/multiple_cameras/'
* 
* Open two windows for the left and the right image.
dev_close_window ()
read_image (Image1, ImgPath + 'camera1_ref')
get_image_size (Image1, Width, Height)
WindowScale := 0.66
dev_open_window (0, 0, Width * WindowScale, Height * WindowScale, 'black', WindowHandle1)
dev_open_window (0, Width * WindowScale + 6, Width * WindowScale, Height * WindowScale,'black', WindowHandle2)
* 
* Set some parameters for both windows.
dev_set_window (WindowHandle1)
dev_set_color ('green')
dev_set_draw ('margin')
dev_set_line_width (2)
dev_set_part (0, 0, Height - 1, Width - 1)
set_display_font (WindowHandle1, 16, 'mono', 'true', 'false')
* 
dev_set_window (WindowHandle2)
dev_set_color ('green')
dev_set_draw ('margin')
dev_set_line_width (2)
dev_set_part (0, 0, Height - 1, Width - 1)
set_display_font (WindowHandle2, 16, 'mono', 'true', 'false')
* 
*两个相机的内参  之前已经标定过了
gen_cam_par_area_scan_division (0.01619, -734.789, 7.402e-006, 7.4e-006, 324.911,\256.894, 640, 480, CamParam1)
gen_cam_par_area_scan_division (0.0162584, -763.35, 7.39842e-006, 7.4e-006, 324.176,\245.371, 640, 480, CamParam2)
* 
* Read the images and display them.
read_image (Image1, ImgPath + 'camera1_ref')
read_image (Image2, ImgPath + 'camera2_ref')
dev_set_window (WindowHandle1)
dev_display (Image1)
dev_set_window (WindowHandle2)
dev_display (Image2)
* 
* 通过相机的内参来而且拍在两个相机中拍同一个标定板
CaltabName := 'caltab_30mm.descr'
*标定参数
*2 表示的是两个相机 
* 表示的是只有一个物体object ,这里是指只有一个标定板
create_calib_data ('calibration_object', 2, 1, CalibDataID)*设置标定参数
set_calib_data_calib_object (CalibDataID, 0, CaltabName)set_calib_data_cam_param (CalibDataID, 0, [], CamParam1)
set_calib_data_cam_param (CalibDataID, 1, [], CamParam2)
* 
* Find and display the calibration plate in the images.
dev_set_window (WindowHandle1)
find_calib_object (Image1, CalibDataID, 0, 0, 0, [], [])
* 这一步很重要,重要就在计算的那个Pose 表示在相机1 下的姿态*      get_calib_data_observ_points( : : CalibDataID, CameraIdx, CalibObjIdx, CalibObjPoseIdx : Row, Column, Index, Pose)功能:从标定数据模型中提取标记点坐标。
*      参数1:CalibDataID:标定数据模型句柄;
*      参数2:CameraIdx:摄像机索引,默认值为0;
*      参数3:CalibObjIdx:标定板索引,默认值0;
*      参数4:CalibObjPoseIdx:观察到的标定板位姿的索引;
*      参数1:( Row, Column):检测到的标记点坐标;
*      参数2: Index:检测到的点与观测到的标定板上的点的对应关系。
*      参数3: Pose:粗略估计观测到的标定板相对于观测相机的姿态。
get_calib_data_observ_points (CalibDataID, 0, 0, 0, RowCoord1, ColumnCoord1, Index1, Pose1)*    get_calib_data( : : CalibDataID, ItemType, ItemIdx, DataName : DataValue):查询存储在标定模型中的数据(比如相机的内参和外参)。
*    控制输入参数1:CalibDataID:标定数据模型句柄;
*    控制输入参数2:ItemType:数据类型,'camera':表示要获取数据类型是与摄像机相关数据; 'calib_obj_pose':表示要获取数据类型与标定板位姿相关数据
*    控制输入参数3:ItemIdx:ItemIdx:输入参数,ItemType='camera'时,ItemIdx表示摄像机索引;ItemType='calib_obj_pose'时,ItemIdx是一个数组[CalibObjIdx, CalibObjPoseIdx],其中CalibObjIdx表示标定板索引,CalibObjPoseIdx表示参考位姿的图像索引;
*    控制输入参数4:DataName:输入要查询的属性名,'params'表示摄像机内参数;  'pose'表示摄像机外参数;
gen_cross_contour_xld (Cross, RowCoord1, ColumnCoord1, 6, 0.785398)
get_calib_data_observ_contours (Caltab1, CalibDataID, 'caltab', 0, 0, 0)
dev_display (Caltab1)
* 
dev_set_window (WindowHandle2)
find_calib_object (Image2, CalibDataID, 1, 0, 0, [], [])
* 这一步很重要,重要就在计算的那个Pose 表示在相机2 下的姿态
get_calib_data_observ_points (CalibDataID, 1, 0, 0, RowCoord2, ColumnCoord2, Index2, Pose2)
gen_cross_contour_xld (Cross2, RowCoord2, ColumnCoord2, 6, 0.785398)
get_calib_data_observ_contours (Caltab2, CalibDataID, 'caltab', 1, 0, 0)
dev_display (Caltab2)
* * 同一个标定板在不同的相机坐标系下有两个不同的姿态,但是在世界坐标系下两个姿态表示的是同一个东西,同一个姿态。那么这个就是多相机标定的桥梁disp_message (WindowHandle1, 'Calibration successful', 'window', 12, 12, 'black', 'true')
disp_continue_message (WindowHandle1, 'black', 'true')
stop ()
clear_calib_data (CalibDataID)* 
* Determine the offset between the calibration plate surface
* and the object surface
* 确定标定板表面与目标表面之间的偏移量*标定板的厚度     为什么要除以1000
ThicknessCaliper := 2.9 / 1000.0 
*平台的厚度
ThicknessPlate := 5.65 / 1000.0
*DiffHeight=先还原到基准面(+ThicknessCaliper)然后再加上要测量的产品的高度(-ThicknessPlate)=ThicknessPlate-ThicknessCaliper
DiffHeight := ThicknessPlate - ThicknessCaliper
*
*=================================================================================================================================
*DistancePlates 表示连个标定板之间的距离
DistancePlates := 0.06488
*定义马赛克图像的像素大小(以米为单位) 像素当量
PixelSize := 0.0001
* 
*第一(左)图像,确定必要的转变的姿势很简单。
*您可以根据预先选择的边框宽度在图像坐标中定义校正图像的左上角
BorderInPercent := 3
get_image_size (Image1, WidthImage1, HeightImage1)
* gen_cross_contour_xld (Cross1, HeightImage1,  WidthImage1,600, 0.785398)* 图片中左上角的点
UpperRow := HeightImage1 * BorderInPercent / 100.0
LeftColumn := WidthImage1 * BorderInPercent / 100.0
*然后,这个点必须转换成世界坐标。
*     image_points_to_world_plane( : : CameraParam, WorldPose, Rows, Cols, Scale : X, Y):将图像点变换到世界坐标系的z=0平面中,*                        并返回它们在3D坐标中的X和Y值。*     Map1 输出隐射*     控制输入参数1: CameraParam:相机内参;*     控制输入参数2:WorldPose:相机坐标系中世界坐标系的三维姿态(相机外参);*     控制输入参数3: (Rows, Cols):待转换点的坐标;*     控制输入参数4:Scale:比例或尺寸,Default value: 'm';*     控制输出参数:X:世界坐标系中点的X坐标;*     控制输出参数:Y:世界坐标系中点的Y坐标。
image_points_to_world_plane (CamParam1, Pose1, UpperRow, LeftColumn, 'm', Left_Up_X, Left_Up_Y)
* 为了确定校正图像的高度,我们需要定义一个位于第一个图像的下边界附近的点。*左下角的点 
LowerRow := HeightImage1 * (100 - BorderInPercent) / 100.0
LeftColumn := WidthImage1 * BorderInPercent / 100.0*同样,这一点必须转换成世界坐标系。
image_points_to_world_plane (CamParam1, Pose1, LowerRow, LeftColumn, 'm', Left_Down_X, Left_Down_Y)
*高度可以确定为左上点和靠近下图像边界的点之间的垂直距离,以校正图像的像素表示。
HeightRect := int((Left_Down_Y - Left_Up_Y) / PixelSize)*类似地,宽度可以由位于两个图像的重叠区域中的一个点来确定,在第一张图片的右边界附近。
OverlapInPercent := 10
RightColumn := WidthImage1 * (100 - OverlapInPercent / 2.0) / 100.0
image_points_to_world_plane (CamParam1, Pose1, UpperRow, RightColumn, 'm', Left_Right_X, Left_Right_Y)
WidthRect := int((Left_Right_X - Left_Up_X) / PixelSize)* 具体示意图如下*    X --------------WidthRect-------------- X*   |*   |*   |*   |*HeightRect*   |*   |*   |*   |*   |*  X*============================================================================================================================
*利用变换后的姿态和校正后图像的大小,可以得到第一幅图像的校正图。
set_origin_pose (Pose1, Left_Up_X, Left_Up_Y, DiffHeight, PoseNewOrigin1)*
gen_image_to_world_plane_map (MapSingle1, CamParam1, PoseNewOrigin1, Width, Height, \WidthRect, HeightRect, PixelSize, 'bilinear')
* 
* Generate a new homogeneous transformation matrix.
*生成一个新的齐次变换矩阵。
hom_mat3d_identity (HomMat3DIdentity)
* 
* The second image must be rectified such that it fits exactly
* to the right of the first rectified image. This means that the
* upper left corner of the second rectified image must be identical
* with the upper right corner of the first rectified image.
* Therefore, we need to know the coordinates of the upper right corner
* of the first rectified image in the coordinate system that is defined
* by the calibration plate in the second image.
* First, we express the upper right corner of the first rectified image
* in the world coordinate system that is defined by the calibration plate
* in the first image. It can be determined by a transformation from
* the origin into the upper left corner of the
* first rectified image (a translation) followed by a translation along
* the upper border of the first rectified image. Together with the shift
* that compensates the thickness of the calibration plate, this
* transformation is represented by the homogeneous transformation matrix:
*第二幅图像必须校正,使其正好适合第一幅校正图像的右侧。
*这意味着第二校正图像的左上角必须与第一校正图像的右上角相同。
*因此,我们需要知道第一个校正图像的右上角在由第二个图像中的校准板定义的坐标系中的坐标。
*首先,我们在世界坐标系中表示第一幅校正图像的右上角,该坐标系由第一幅图像中的校准板定义。
*它可以通过从原点到第一校正图像的左上角的变换(平移)以及沿着第一校正图像的上边界的平移来确定。
*与补偿校准板厚度的位移一起,该变换由齐次变换矩阵表示:
second:=Left_Up_X + PixelSize * WidthRect* 第二张图的拼接位置  具体示意图如下
*              first Image                        second Image*  Left_Up_X ----------------------PixelSize * WidthRect---------X----------------------*   |                                          |*   |                                          |*   |                                          |*   |                                          |*   |                                          |*HeightRect                                    |*   |                                          |*   |                                          |*   |                                          |*   |                                          |*   |                                          |*   X                                          |hom_mat3d_translate_local (HomMat3DIdentity, Left_Up_X + PixelSize * WidthRect, Left_Up_Y, DiffHeight, cp1Hur1)get_image_size (Image1, Width1, Height1)
DistancePlates:=0.06488
*DistancePlates:= WidthRect*PixelSize
*DistancePlates 是第一个相机-》第二个相机的矩阵 只是个X方向的移动  1H2
hom_mat3d_translate_local (HomMat3DIdentity, DistancePlates, 0, 0, cp1Hcp2)
* 
* Then, we need the transformation between the two calibration plates of
* the calibration object. The homogeneous transformation matrix cp1Hcp2
* describes how the world coordinate system defined by the calibration plate
* in the first image is transformed into the world coordinate system
* defined by the calibration plate in the second image. This transformation
* must be known beforehand from a precise measurement of the calibration object.
* From these two transformations, it is easy to derive
* the transformation that transforms the world coordinate system
* of the second image such that its origin lies in the upper left corner
* of the second rectified image. For this, the two transformations
* have to be combined appropriately.
*然后,我们需要校准对象的两个校准板之间的转换。
*齐次变换矩阵cp1Hcp2描述如何将第一图像中校准板定义的世界坐标系转换为第二图像中校准板定义的世界坐标系。
*这种转换必须事先从校准对象的精确测量中得知。
*从这两个变换,很容易导出变换第二图像的世界坐标系的变换,使得其原点位于第二校正图像的左上角。
*为此,必须将这两个转换适当地结合起来。
*从第二个相机-》第一个相机的比那换
*1H2-》2H1
hom_mat3d_invert (cp1Hcp2, cp2Hcp1)
*相机2-》世界坐标   =2H1*1He  计算从相机二坐标到世界坐标的Pose 转换
hom_mat3d_compose (cp2Hcp1, cp1Hur1, cp2Hul2)
* 
* With this, the pose of the calibration plate in the second image
* can be modified such that the origin of the world coordinate system
* lies in the upper left corner of the second rectified image.
*这样,可以修改第二图像中校准板的姿态,使得世界坐标系的原点位于第二校正图像的左上角。
*和上面的同一个道理
pose_to_hom_mat3d (Pose2, cam2Hcp2)
hom_mat3d_compose (cam2Hcp2, cp2Hul2, cam2Hul2)
hom_mat3d_to_pose (cam2Hul2, PoseNewOrigin2)
* 
* With the resulting new pose and the size of the rectified image,
* which can be the same as for the first rectified image,
* the rectification map for the second image can be derived.
*根据得到的新姿势和校正图像的大小(其可以与第一校正图像相同),可以导出第二图像的校正地图。
gen_image_to_world_plane_map (MapSingle2, CamParam2,\PoseNewOrigin2, Width, Height,\WidthRect, HeightRect, PixelSize, 'bilinear')
* 
* Open a new Graphics Window for the merged image.
*打开合并图像的新图形窗口。
dev_open_window (Height * WindowScale, 0, Width * 2 * WindowScale, Height * WindowScale, \'black', WindowHandleCombined)
set_display_font (WindowHandleCombined, 16, 'mono', 'true', 'false')
dev_set_color ('green')
dev_set_draw ('margin')
ScalePlot := 200
RowPlot := 400
Coord := [0:2000]
*  
*  Process all image pairs in a loop.
*循环处理所有图像对
for I := 1 to 3 by 1* * Display both images.dev_set_window (WindowHandle1)read_image (Image1, ImgPath + 'camera1_' + I$'02d')get_image_size (Image1, WidthImage1, HeightImage1)dev_set_part (0, 0, HeightImage1 - 1, WidthImage1 - 1)dev_display (Image1)dev_set_window (WindowHandle2)read_image (Image2, ImgPath + 'camera2_' + I$'02d')get_image_size (Image2, WidthImage2, HeightImage2)dev_set_part (0, 0, HeightImage2 - 1, WidthImage2 - 1)dev_display (Image2)*     tile_images(Image1, Image2, 2, 'vertical')* * Start the time measurement.count_seconds (TimeStart1)* * Rectify the image pair from the two-camera setup with map_image.*使用map_image校正两个摄像机设置中的图像对map_image (Image1, MapSingle1, RectifiedImage1)
*    get_image_size (RectifiedImage1, Width2, Height2)map_image (Image2, MapSingle2, RectifiedImage2)
*    get_image_size (RectifiedImage2, Width44, Height44)concat_obj (RectifiedImage1, RectifiedImage2, Concat)* End the time measurement and calculate the difference.*结束时间测量并计算差异count_seconds (TimeEnd1)Time1 := TimeEnd1 - TimeStart1* dev_set_window (WindowHandleCombined)* Start the time measurement again.count_seconds (TimeStart2)* * Tile both images into one large image.* 将两个图像平铺成一个大图像tile_images (Concat, Combined, 2, 'vertical')get_image_size (Combined, Width3, Height3)* * End the time measurement again and calculate the difference.count_seconds (TimeEnd2)Time2 := TimeEnd2 - TimeStart2* * Display the combined image and the time measurement.get_image_size (Combined, WidthComb, HeightComb)dev_set_part (0, 0, HeightComb - 1, WidthComb - 1)dev_display (Combined)disp_message (WindowHandle1, 'Merge cameras: ' + (1000 * (Time1 + Time2))$'.3' + ' ms', \'window', 12, 12, 'black', 'true')* * In addition, we plot the accuracy of the mosaicking with a procedure.*此外,我们用一个程序来绘制马赛克的准确性。plot_mosaicking_accuracy (Combined, WidthRect, HeightRect, WindowHandleCombined, Coord, ScalePlot, RowPlot)if (I < 3)disp_continue_message (WindowHandleCombined, 'black', 'true')stop ()endif
endfor

四、疑问

虽然上面的原理我懂了,但是对于这种方法有几个细节东西还有疑惑,望各位帮忙

1、加入两个标定板直接不仅有平移关系,还有旋转关系,只是在两个标定板之间再添加一个旋转关系吗,对于高精度的项目来说,两个标定板的厚度是不是要做一个矫正?

2、在实际的项目中,标定板如何定做,对精度有要求的,不然可以直接打印一张纸上画两个标定板

以上两个问题我已解决,后面我会做一个文章专门回答这个问题

参考博文:双相机融合标定_双相机标定拼图-CSDN博客

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/24956.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

第二十节:带你梳理Vue2:Vue子组件向父组件传参(事件传参)

1. 自定义事件 除了可以处理原生的DOM事件, v-on指令也可以处理组件内部触发的自定义的事件,调用this.$emit()函数就可以触发一个自定义事件 $emit() 触发事件函数接受一个自定义事件的事件名以及其他任何给事件函数传递的参数. 然后就可以在组件上使用v-on来绑定这个自定义事…

启动游戏出现concrt140.dll错误的解决方法

concrt140.dll是一个动态链接库文件&#xff0c;属于Microsoft Visual C 2015 Redistributable组件集的一部分。这个文件是并发运行时库&#xff08;Concurrency Runtime&#xff09;的一部分&#xff0c;对于支持和增强应用程序的多线程与并发执行能力至关重要。它包含了实现并…

003-两台交换机堆叠(IRF)

两台交换机堆叠&#xff08;IRF&#xff09; 概念 IRF (Intelligent Resilient Framework) 是华为公司提出的一种交换机堆叠技术&#xff0c;它允许用户将多台物理交换机设备连接在一起&#xff0c;从而逻辑上形成一个单一的、统一的、大容量的虚拟交换机。IRF堆叠技术通过堆…

【自动部署】4.阿里云ECS服务器 IDEA自动部署项目

如何在IDEA中,自动部署项目到阿里云ECS服务器?今天我们就来实现一键部署功能。 执行maven命令打包免密登录,在IEDA中连接服务器执行stop服务上传jar包到服务器执行start脚本查看运行日志1.安装Alibaba Cloud Toolkit 2.配置host 3.自动化部署后端 右击项目,选择Alibaba CL…

第一篇红队笔记-百靶精讲之W1R3S-john

https://download.vulnhub.com/w1r3s/w1r3s.v1.0.1.zip 主机发现 nmap端口扫描及思路 扫描某个网段 扫描单个ip所有端口 重复扫描单个ip具体端口 udp协议再来一次 漏洞扫描 FTP渗透 尝试匿名登陆 防止文件损坏 识别加密方式-hash-identifier base64 Web目录爆破…

C#操作MySQL从入门到精通(19)——插入数据

前言: 谈到数据库,大家最容易脱口而出的就是增删改查,在本篇文章之前一直都是说的各种查询,本文就是说的增删改查中的增,所谓增也就是增加数据的意思,插入数据就是增加数据。 本文测试使用的数据库如下: 1、插入完整行 所谓插入完整行就是一行所有列的数据都是自己插…

《精通ChatGPT:从入门到大师的Prompt指南》第4章:避免常见错误

第4章&#xff1a;避免常见错误 在使用ChatGPT进行Prompt编写时&#xff0c;常见的错误可能会大大影响生成内容的质量和准确性。本章将详细讨论这些错误&#xff0c;并提供如何避免它们的建议。 4.1 不明确的指令 在使用ChatGPT时&#xff0c;一个常见的问题是指令不够明确。…

理解数仓建模

​​​在数仓建设的过程中&#xff0c;由于未能完全按照规范操作&#xff0c; 从而导致数据仓库建设比较混乱&#xff0c;常见有以下问题&#xff1a; 数仓常见问题 ● 数仓分层不清晰&#xff1a;数仓的分层没有明确的逻辑&#xff0c;难以管理和维护。 ● 数据域划分不明确…

【网络编程开发】10.UNIX套接字域

10.UNIX套接字域 UNIX域套接字是用于在同一台计算机上运行的进程之间进行通信的一种机制。它与传统基于TCP/IP协议栈的套接字不同&#xff0c;UNIX域套接字操作更为高效&#xff0c;因为它避免了网络层的开销&#xff0c;不涉及网络报头、检验和、顺序号等复杂的网络协议处理过…

C#操作MySQL从入门到精通(16)——使用子查询

前言: 我们在查询数据的过程中有时候查询的数据不是从数据库中来的,而是从另一个查询的结果来的,这时候就需要使用子查询,本文使用的测试数据如下: 1、子查询 下面的代码就是先查询地址是安徽和广西的学生年龄,然后获取年龄对应的姓名 private void button__SubQuery…

Spring boot+vue前后端分离

目录 1、前端vue的搭建 2、后端项目的构建 pom文件中引入的jar包 yml文件用来配置连接数据库和端口的设置 application.property进行一些整合 service层 imp层 mapper 实体类 额外写一个类、解决跨域问题 3、测试 1、前端vue的搭建 建立项目的过程略 开启一个建立好…

用自然语言连接信息孤岛

信息孤岛互联互通的困难 尽管已经进入了互联网时代&#xff0c;信息系统中的信息孤岛现象仍然十分地严重&#xff0c;不同部门&#xff0c;不同机器之间难以实现信息的互联互通。存在大量的信息孤岛。 不同信息系统的相互通信依赖通信协议和数据模型的定义&#xff0c;前者决定…

上海安全员C证继续教育题库(附答案)

1.从业人员经过安全教育培训&#xff0c;了解岗位操作规程&#xff0c;但未遵守而造成事故的&#xff0c;行为人应负( )责任&#xff0c;有关负责人应负( )责任。 A.直接 间接 B.直接 领导 C.间接 管理D.直接 管理 2.对生产附着式升降脚手架产品的单位&#xff0c;必须…

一定要了解的 WordPress 数据库中默认 12 个表

WordPressan 安装的时候会有 12 张默认的数据表,每张表的数据都包含了 WordPress 不同的功能。看看这些表的结构,你能很容易的了解网站不同的部分都是存在哪里的。目前,默认的 WordPress 安装会创建如下的数据表。 注意:每张表名前面的 wp_ 是你在安装过程中选择的数据库前…

【IoT NTN】3GPP R18中关于各类IoT设备在NTN中的增强和扩展

博主未授权任何人或组织机构转载博主任何原创文章&#xff0c;感谢各位对原创的支持&#xff01; 博主链接 本人就职于国际知名终端厂商&#xff0c;负责modem芯片研发。 在5G早期负责终端数据业务层、核心网相关的开发工作&#xff0c;目前牵头6G技术研究。 博客内容主要围绕…

SAP ABAP 往数据库表里加数据

目录 方法一&#xff1a;SE16N SE11 方法二&#xff1a;创建维护VIEW&#xff1a;SE11 SM30 Error补充说明&#xff1a; 方法一&#xff1a;SE16N SE11 首先SE16N 进来。 进来之后在テーブル的位置输入表名&#xff0c;然后点击执行&#xff08;F8&#xff09; 如果第一次…

spring 解决循环依赖

在 spring 框架中&#xff0c;我们知道它是通过三级缓存来解决循环依赖的&#xff0c;那么它具体是怎么实现的&#xff0c;以及是否必须需要三级缓存才能解决循环依赖&#xff0c;本文来作相关介绍。 具体实现 先来看看它的三级缓存到底是什么&#xff0c;先看如下代码&#…

Unity动画录制工具在运行时录制和保存模型骨骼运动的方法录制动画给其他角色模型使用支持JSON、FBX等格式

如果您正在寻找一种在运行时录制和保存模型骨骼运动的方法&#xff0c;那么此插件是满足您需求的完美解决方案。 实时录制角色运动 将录制到的角色动作转为动画文件 将录制好的动作给新的角色模型使用&#xff0c;完美复制 支持导出FBX格式 操作简单&#xff0c;有按钮界面…

【机器学习】我们该如何评价GPT-4o?GPT-4o的技术能力分析以及前言探索

目录 &#x1f926;‍♀️GPT-4o是什么&#xff1f; &#x1f68d;GPT-4o的技术能力 1. 自然语言理解 2. 自然语言生成 3. 对话系统 4. 语言翻译 5. 文本纠错 6. 知识问答 7. 定制和微调 8. 透明性和可解释性 9. 扩展性 &#x1f690;版本对比分析 1. GPT-4标准版 …

像素蛋糕Photoshop颜色导出不一致问题分析与解决

问题点&#xff1a;发现用像素蛋糕修完图明天应该为最右边图片显示 模特应该是白皙的&#xff0c;但是导出图片无论是否勾选SRGB都表现的为种间图片颜色一样 饱和度巨高。 问题分析&#xff1a;那这一定是颜色配置文件出现问题&#xff0c;找到客服表示可以去PS打开看是否与预…