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

news2024/12/24 21:08:08

一、概述

最近有一个多相机标定的项目,大概是4个相机来标定,同一坐标系,然后拼接图,之前双相机标定的时候也大概看看,所以今天就找了那个halcon 案例多学一下,后面我打算做一个对位贴合的东西,类似于VM的那种,最后我想把整个流程封装起来。

思路:

1、做联合标定,每个相机的内参标定,但是外参第二个相机是基于第一相机的

2、获得标定板(只有一个标定板)相机的第一个姿态,作为第一个相机的决定姿态 FirstPose

这个FirstPose 是对世界坐标的

3、对第一个相机的绝对姿态还原高度,由于那个标定板是有高度的

从此开始,第二个参数的pose 是基于第一个相机的

4、获得第二个相机的相对第一个相机的姿态

5、我们第二个相机是姿态的,那么我们想要从第二个相机-》世界坐标===>pose2->FirstPose-》世界坐标

6、把那个尺子的高度也加进去,生成映射  

7、还原图像 拼接图

二、算子解释

create_calib_data

* 创建一个Halcon标定数据模型(即建立标定对象),用于存储相机标定的过程、标定数据以及相机标定或手眼标定的结果
* 'calibration_object'类型用于基于从对象标定的观测中提取的度量信息来校准一个或多个相机的相机内参和相机姿态(相机外参);
*  2 表示两个连个相机
*  1 表示的是一个标定板
*  CalibDataID 标定的数据
create_calib_data ('calibration_object', 2, 1, CalibDataID)

set_calib_data_cam_param 

*   set_calib_data_cam_param( : : CalibDataID, CameraIdx, CameraType, CameraParam : ) 功能:在标定数据模型中设置相机的类型和初始参数。
*   参数1:CalibDataID:标定数据模型句柄;
*   参数2:CameraIdx:摄像机索引,默认值为0;
*   参数3:CameraType:摄像机类型,默认值: [];
*   参数4:CameraParam:摄像机初始内参。
set_calib_data_cam_param (CalibDataID, 0, [], StartCamParam1)

set_calib_data_calib_object 

*标定文件 这个是相机标定的重要部分

*      set_calib_data_calib_object( : : CalibDataID, CalibObjIdx, CalibObjDescr : )功能:在标定模型中定义标定对象(设置标定板描述文件)。
*      参数1:CalibDataID:标定数据模型句柄;
*      参数2:CalibObjIdx:标定板索引,默认值0;
*      参数3:CalibObjDescr:标定板三维点坐标或标定板描述文件名
set_calib_data_calib_object (CalibDataID, 0, 'calplate_160mm.cpd')

 find_calib_object

   
    *     find_calib_object(Image : : CalibDataID, CameraIdx, CalibObjIdx, CalibObjPoseIdx, GenParamName, GenParamValue : )功能:找到Halcon标定板,并在标定数据模型中设置提取的点和轮廓。
    *     参数1:Image:输入图像;
    *     参数1:CalibDataID: 标定数据模型句柄;
    *     参数2:CameraIdx: 摄像机索引,默认值为0;
    *     参数3:CalibObjIdx:标定板索引,默认值0;
    *     参数4:CalibObjPoseIdx:观察到的标定版的索引;
    *     参数5:GenParamName:待设置的通用参数的名称,默认值[];
    *     参数6:GenParamValue:待设置的通用参数的值,默认值[]。
    
    *第一个相机(坐标的)
    find_calib_object (ImageCam1, CalibDataID, 0, 0, I, [], [])

get_calib_data

*    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'表示摄像机外参数;
get_calib_data_observ_contours
    *get_calib_data_observ_contours( : Contours : CalibDataID, ContourName, CameraIdx, CalibObjIdx, CalibObjPoseIdx : ) 功能:从标定数据模型中提取轮廓。
    *图像输出参数:Contours:输出轮廓;
    *控制输入参数1:CalibDataID:标定数据模型句柄;
    *控制输入参数2:ContourName:待返回的轮廓对象的名称,Default value: 'marks':提取标定板所有标记点的轮廓,'caltab':提取标定板中心6个标记点的轮廓;
    *控制输入参数3:CameraIdx:摄像机索引,默认值为0;
    *控制输入参数4:CalibObjIdx:标定板索引,默认值0;
    *控制输入参数5:CalibObjPoseIdx:观察到的标定板位姿的索引。
    get_calib_data_observ_contours (ContoursCam1, CalibDataID, 'marks', 0, 0, I)
 camera-pose

获得基于第一个相机的第二个相机的Pose

get_calib_data (CalibDataID, 'camera', 1, 'pose', RelPose2)
*relative  相对  
* To get the absolute pose of the second camera, its relative pose needs
* to be inverted and combined with the absolute pose of the first camera.
*为了获得第二个相机的绝对姿态,需要将其相对姿态反转并与第一个相机的绝对姿态合并

set_origin_pose


*[0,0] --》 获得第一个标定板第一张图形的姿态(这里只有一个标定板)
*我这里是10组图像 只有一个标定板,所以
get_calib_data (CalibDataID, 'calib_obj_pose', [0,0], 'pose', Pose1)
PP:=Pose1
* Since the calibration plate has a certain thickness, the pose of the first camera
* needs to be corrected by the thickness, which is 4mm here.

*    set_origin_pose( : : PoseIn, DX, DY, DZ : PoseNewOrigin):计算原始的3D位姿经过向量平移之后新的位姿。
*    控制输入参数1:PoseIn:原始的3D位姿;
*    控制输入参数2:DX:沿着世界坐标的X轴的平移量;
*    控制输入参数3:DY:沿着世界坐标Y轴的平移量;
*    控制输入参数4:DZ:沿着世界坐标Z轴的平移量; 高度 0.004 4mm 表示的标定板的厚度是4mm 
*    控制输出参数:PoseNewOrigin:输出新的位姿。
set_origin_pose (Pose1, 0, 0, 0.004, Pose1)

结果: 

 
 CalibDataID

 image_points_to_world_plane

    *     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坐标。

 gen_image_to_world_plane_map

 
    *gen_image_to_world_plane_map( : Map : CameraParam, WorldPose, WidthIn, HeightIn, WidthMapped, HeightMapped, Scale, MapType : )
    *—生成一个投影图,该投影图描述图像平面与世界坐标系的平面z = 0之间的映射。
	
    *  Map                  [OUT] 输出的映射图
    *  CameraParam [IN] 相机内参
    *  WorldPose       [IN] 世界坐标系在摄像机坐标系中的3D位姿
    *  WidthIn            [IN] 要转换的图像的宽
    *  HeightIn           [IN] 要转换的图像的高
    *  WidthMapped  [IN] 映射图的宽
    *  HeightMapped [IN] 映射图的高
    *  Scale        [IN] 坐标系的单位尺寸
                      * 1um的像素大小意味着变换后的图像中的像素对应于测量平面中的1um x 1um区域,默认单位是m
                      * Scale=sqrt(像素当量(m))
    *  MapType[IN] 映射的算法类型

     Scale2:=sqrt(4.40189e-06/100)//4.40189e-06mm 是上面标定相机内参得到的像素当量
    gen_image_to_world_plane_map (Map1, CamParam1, WorldPose1, WidthCam1, HeightCam1, TargetWidth, TargetHeight, Scale2, 'bilinear')
    gen_image_to_world_plane_map (Map2, CamParam2, WorldPose2, WidthCam1, HeightCam1, TargetWidth, TargetHeight, Scale2, 'bilinear')

三、halcon 代码


dev_update_off ()
* 
* Path to the calibration and object images.
ImagePath := '3d_machine_vision/calibrated_mosaic/'
* 
* Display workflow explanation text.
dev_close_window ()
dev_open_window (0, 0, 600, 300, 'black', WindowHandle1)
set_display_font (WindowHandle1, 16, 'mono', 'true', 'false')
dev_disp_workflow_text ()
stop ()
* 
* Display calibration explanation text.
dev_clear_window ()
dev_disp_calibration_text ()
stop ()
* 
dev_close_window ()
dev_open_window (0, 0, 600, 480, 'black', WindowHandle1)
dev_open_window (0, 605, 600, 480, 'black', WindowHandle2)
set_display_font (WindowHandle1, 16, 'mono', 'true', 'false')
set_display_font (WindowHandle2, 16, 'mono', 'true', 'false')
* 
* **********************************************************
* *********** Step 1: Calibration of the cameras ***********
* **********************************************************
* 
* Number of calibration images.
NumCalibImages := 10
* 
*
read_image (ImageCam1, ImagePath + '/calib_cam_1_01')
read_image (ImageCam2, ImagePath + '/calib_cam_2_01')
get_image_size (ImageCam1, WidthCam1, HeightCam1)
get_image_size (ImageCam2, WidthCam2, HeightCam2)
* 
* 初始化两个相机的内参数
gen_cam_par_area_scan_division (0.012, 0, 4.4e-6, 4.4e-6, WidthCam1 / 2, HeightCam1 / 2, WidthCam1, HeightCam1, StartCamParam1)
gen_cam_par_area_scan_division (0.012, 0, 4.4e-6, 4.4e-6, WidthCam2 / 2, HeightCam2 / 2, WidthCam2, HeightCam2, StartCamParam2)



* 创建一个Halcon标定数据模型(即建立标定对象),用于存储相机标定的过程、标定数据以及相机标定或手眼标定的结果
* 'calibration_object'类型用于基于从对象标定的观测中提取的度量信息来校准一个或多个相机的相机内参和相机姿态(相机外参);
*  2 表示两个连个相机
*  1 表示的是一个标定板
*  CalibDataID 标定的数据
create_calib_data ('calibration_object', 2, 1, CalibDataID)


*   set_calib_data_cam_param( : : CalibDataID, CameraIdx, CameraType, CameraParam : ) 功能:在标定数据模型中设置相机的类型和初始参数。
*   参数1:CalibDataID:标定数据模型句柄;
*   参数2:CameraIdx:摄像机索引,默认值为0;
*   参数3:CameraType:摄像机类型,默认值: [];
*   参数4:CameraParam:摄像机初始内参。
set_calib_data_cam_param (CalibDataID, 0, [], StartCamParam1)
set_calib_data_cam_param (CalibDataID, 1, [], StartCamParam2)

*标定文件 这个是相机标定的重要部分

*      set_calib_data_calib_object( : : CalibDataID, CalibObjIdx, CalibObjDescr : )功能:在标定模型中定义标定对象(设置标定板描述文件)。
*      参数1:CalibDataID:标定数据模型句柄;
*      参数2:CalibObjIdx:标定板索引,默认值0;
*      参数3:CalibObjDescr:标定板三维点坐标或标定板描述文件名
set_calib_data_calib_object (CalibDataID, 0, 'calplate_160mm.cpd')
* 
* 
for I := 0 to NumCalibImages - 1 by 1
    * 分别读取左右两个相机的图片
    read_image (ImageCam1, ImagePath + '/calib_cam_1_' + (I + 1)$'02d')
    read_image (ImageCam2, ImagePath + '/calib_cam_2_' + (I + 1)$'02d')
    * 
    * 找到标定板 分别开始标定
    
    
    
    
    *     find_calib_object(Image : : CalibDataID, CameraIdx, CalibObjIdx, CalibObjPoseIdx, GenParamName, GenParamValue : )功能:找到Halcon标定板,并在标定数据模型中设置提取的点和轮廓。
    *     参数1:Image:输入图像;
    *     参数1:CalibDataID: 标定数据模型句柄;
    *     参数2:CameraIdx: 摄像机索引,默认值为0;
    *     参数3:CalibObjIdx:标定板索引,默认值0;
    *     参数4:CalibObjPoseIdx:观察到的标定版的索引;
    *     参数5:GenParamName:待设置的通用参数的名称,默认值[];
    *     参数6:GenParamValue:待设置的通用参数的值,默认值[]。
    
    *第一个相机(坐标的)
    find_calib_object (ImageCam1, CalibDataID, 0, 0, I, [], [])
    
    
    
    *get_calib_data_observ_contours( : Contours : CalibDataID, ContourName, CameraIdx, CalibObjIdx, CalibObjPoseIdx : ) 功能:从标定数据模型中提取轮廓。
    *图像输出参数:Contours:输出轮廓;
    *控制输入参数1:CalibDataID:标定数据模型句柄;
    *控制输入参数2:ContourName:待返回的轮廓对象的名称,Default value: 'marks':提取标定板所有标记点的轮廓,'caltab':提取标定板中心6个标记点的轮廓;
    *控制输入参数3:CameraIdx:摄像机索引,默认值为0;
    *控制输入参数4:CalibObjIdx:标定板索引,默认值0;
    *控制输入参数5:CalibObjPoseIdx:观察到的标定板位姿的索引。
    get_calib_data_observ_contours (ContoursCam1, CalibDataID, 'marks', 0, 0, I)
    dev_set_window (WindowHandle1)
    dev_display (ImageCam1)
    dev_display (ContoursCam1)
    
*     get_calib_data_observ_points (CalibDataID, 0, 0, 0, Row, Column, Index, Pose)
    dev_disp_text ('Image ' + (I + 1) + '/' + NumCalibImages + ' (Camera 1)', 'window', 'top', 'left', 'black', [], [])
    
    * 第二个相机
    find_calib_object (ImageCam2, CalibDataID, 1, 0, I, [], [])
    get_calib_data_observ_contours (ContoursCam2, CalibDataID, 'marks', 1, 0, I)
    dev_set_window (WindowHandle2)
    dev_display (ImageCam2)
    dev_display (ContoursCam2)
    dev_disp_text ('Image ' + (I + 1) + '/' + NumCalibImages + ' (Camera 2)', 'window', 'top', 'left', 'black', [], [])
    disp_continue_message (WindowHandle2, 'black', 'true')
    stop ()
endfor
stop ()
* 
*   calibrate_cameras( : : CalibDataID : Error)功能:标定相机的内参和外参。
*   控制输入参数:CalibDataID:标定数据模型句柄;
*   控制输出参数:Error:均方根误差 (RMSE)
calibrate_cameras (CalibDataID, Errors)


dev_set_window (WindowHandle1)
dev_disp_text ('Calibration successful', 'window', 'top', 'left', 'green', [], [])
dev_set_window (WindowHandle2)
dev_disp_text ('Calibration successful', 'window', 'top', 'left', 'green', [], [])
disp_continue_message (WindowHandle2, 'black', 'true')
stop ()
* 
* **********************************************************
* ******************* Step 2: Mosaicking  拼接 马赛克 *******************
* **********************************************************
* 
* Display mosaicking explanation text.
dev_close_window ()
dev_close_window ()
dev_open_window (0, 0, 600, 300, 'black', WindowHandle1)
set_display_font (WindowHandle1, 16, 'mono', 'true', 'false')
dev_disp_mosaicking_text ()
stop ()
* 
* ========================================获取标定的参数=====================================================
NumObjects := 2
NumObjImages := 2
* 
*    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'表示摄像机外参数;
get_calib_data (CalibDataID, 'camera', 0, 'params', CamParam1)
get_calib_data (CalibDataID, 'camera', 1, 'params', CamParam2)
* 
* Get pose with respect to the first camera (reference camera). The
* calibration image which lies in the same plane as the object is
* used as the rectification plane (the world plane the object
* images will be mapped onto). So e.g. if the object lies flat
* on the measurement plane, a calibration plate pose which lies in the
* same flat plane should also be used. Here, we take the first calibration
* image [CalibObjIdx,CalibObjPoseIdx] = [0,0] for the reference pose


*[0,0] --》 获得第一个标定板第一张图形的姿态(这里只有一个标定板)
*我这里是10组图像 只有一个标定板,所以 [0,0] 表示第一个标定板的第一张姿态
get_calib_data (CalibDataID, 'calib_obj_pose', [0,0], 'pose', FirstPose)
PP:=FirstPose
* Since the calibration plate has a certain thickness, the pose of the first camera
* needs to be corrected by the thickness, which is 4mm here.

*    set_origin_pose( : : PoseIn, DX, DY, DZ : PoseNewOrigin):计算原始的3D位姿经过向量平移之后新的位姿。
*    控制输入参数1:PoseIn:原始的3D位姿;
*    控制输入参数2:DX:沿着世界坐标的X轴的平移量;
*    控制输入参数3:DY:沿着世界坐标Y轴的平移量;
*    控制输入参数4:DZ:沿着世界坐标Z轴的平移量; 高度 0.004 4mm 表示的标定板的厚度是4mm 
*    控制输出参数:PoseNewOrigin:输出新的位姿。
set_origin_pose (FirstPose, 0, 0, 0.004, FirstPose)
* 
* Get the pose of the second camera which is given relative to the first camera.
* 获得第二个相机相对于第一个相机姿态RelPose2 ,因为此时第一个相机的姿态是[0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0]  
get_calib_data (CalibDataID, 'camera', 1, 'pose', RelativePose2BaseFirstCamera)
*relative  相对  
* To get the absolute pose of the second camera, its relative pose needs
* to be inverted and combined with the absolute pose of the first camera.
*为了获得第二个相机的绝对姿态,需要将其相对姿态反转并与第一个相机的绝对姿态合并
pose_invert (RelativePose2BaseFirstCamera, RelativePose2BaseFirstCameraInverted)

*第二个相机的相对逆姿态转为绝对姿态  世界坐标
pose_compose (RelativePose2BaseFirstCameraInverted, FirstPose, AbsolutePose2)
* 
* Set width, height and scale for the target image so that the full object
* image fits well after the mapping.
get_image_size (ImageCam1, Width, Height)
TargetWidth := 1340
TargetHeight := 800
Scale := 0.0002
*

* The mapped images have some black borders which are cut off for display.
Borders := [125,110,665,1222]
* 
* For the mapping of the object images, the camera poses need to be corrected
* again by the thickness of the objects. Here we use two different objects:
* A ruler with a thickness of about 2.5mm and a thin brochure which can be
* approximated with 0mm thickness.
*上面我们设置了标定板的高度是0.004,我们在原来的标定图像的基础上减去了0.004,但是这里由于我们测试的是一把尺子,其高度是
*0.0025,那么我们就要加上这个0.0025,也就说上面0.004是还原到平台,这里在平台上方尺子,所以要加上0.0025
HeightCorrections := [-0.0025,0]
* 
dev_close_window ()
dev_open_window (0, 0, 600, 480, 'black', WindowHandle1)
dev_open_window (0, 605, 600, 480, 'black', WindowHandle2)
dev_open_window (535, 0, 740, 360, 'black', WindowHandle3)
set_display_font (WindowHandle1, 16, 'mono', 'true', 'false')
set_display_font (WindowHandle2, 16, 'mono', 'true', 'false')
set_display_font (WindowHandle3, 16, 'mono', 'true', 'false')
* 
* Perform mosaicking on the example images.
* Therefore, we create a map which projects the camera images onto
* the rectification plane. Since this map depends on the thickness of
* the object, we generate it once for each object.
for OIdx := 0 to NumObjects - 1 by 1
    *获得世界坐标系的姿态,由于Z的方向上我们上升了HeightCorrections,可以设XY,也可以不设XY,这里是设了的
    * As mentioned above, the z component of the world poses needs to be corrected by the
    * thickness of the object. Also correct the x and y values to move the origin of the
    * calibration plate to the bottom right of the images. (Usually, the origin should be
    * at (0,0). But since the x- and y-axis of the poses point to the reverse direction here, we
    * also move the origin accordingly.)
    
    
    set_origin_pose (FirstPose, -0.14, -0.07, HeightCorrections[OIdx], WorldPose1)
    set_origin_pose (AbsolutePose2, -0.14, -0.07, HeightCorrections[OIdx], WorldPose2)
    * 
    * Generate mappings to map the images to worldplane.
    
    *gen_image_to_world_plane_map( : Map : CameraParam, WorldPose, WidthIn, HeightIn, WidthMapped, HeightMapped, Scale, MapType : )
    *—生成一个投影图,该投影图描述图像平面与世界坐标系的平面z = 0之间的映射。
	
    *  Map                  [OUT] 输出的映射图
    *  CameraParam [IN] 相机内参
    *  WorldPose       [IN] 世界坐标系在摄像机坐标系中的3D位姿
    *  WidthIn            [IN] 要转换的图像的宽
    *  HeightIn           [IN] 要转换的图像的高
    *  WidthMapped  [IN] 映射图的宽
    *  HeightMapped [IN] 映射图的高
    *  Scale        [IN] 坐标系的单位尺寸
                      * 1um的像素大小意味着变换后的图像中的像素对应于测量平面中的1um x 1um区域,默认单位是m
                      * Scale=sqrt(像素当量(m))
    *  MapType[IN] 映射的算法类型
  * 计算出连个映射关系
     Scale2:=sqrt(4.40189e-06/100)//4.40189e-06mm 是上面标定相机内参得到的像素当量
    gen_image_to_world_plane_map (Map1, CamParam1, WorldPose1, WidthCam1, HeightCam1, TargetWidth, TargetHeight, Scale2, 'bilinear')
    gen_image_to_world_plane_map (Map2, CamParam2, WorldPose2, WidthCam1, HeightCam1, TargetWidth, TargetHeight, Scale2, 'bilinear')
    * 
    * Map the object images.
    for IIdx := 1 to NumObjImages by 1
        ObjImageIdx := 2 * OIdx + IIdx
        read_image (ImageCam1, ImagePath + '/obj_cam_1_' + ObjImageIdx$'02d')
        read_image (ImageCam2, ImagePath + '/obj_cam_2_' + ObjImageIdx$'02d')
        * 
        * Display input images and camera poses.
        dev_set_window (WindowHandle1)
        dev_display (ImageCam1)
        dev_disp_text ('Camera 1 image', 'window', 'top', 'left', 'black', [], [])
        disp_3d_coord_system (WindowHandle1, CamParam1, FirstPose, 0.05)
        dev_set_window (WindowHandle2)
        dev_display (ImageCam2)
        dev_disp_text ('Camera 2 image', 'window', 'top', 'left', 'black', [], [])
        disp_3d_coord_system (WindowHandle2, CamParam2, AbsolutePose2, 0.05)
        * 
        * Rectify images by mapping them into world coordinates.
        *矫正图像通过上面得到的映射关系
        *    映射图像
        *    Image                 [IN] 原始图
        *    Map                    [IN] 映射图
        *    ImageMapped    [OUT] 映射后的图像
        map_image (ImageCam1, Map1, ImageWorld1)
        map_image (ImageCam2, Map2, ImageWorld2)
        * 
        * Stitching the mapped images together.
        get_domain (ImageWorld1, Domain1)
        get_domain (ImageWorld2, Domain2)
        *求交集
        intersection (Domain1, Domain2, RegionIntersection)
        *将重叠的部分重新染色为黑色
        paint_region (RegionIntersection, ImageWorld1, ImageWorld1Blackended, 0, 'fill')
        full_domain (ImageWorld1Blackended, ImagePart1)
        full_domain (ImageWorld2, ImagePart2)
        *将两张图像叠加到一起
        add_image (ImagePart1, ImagePart2, ImageFull, 1, 10)
        * 
        * Rotate image and remove the black borders for display.
        rotate_image (ImageFull, ImageRotated, 12, 'constant')
        gen_rectangle1 (RectangleDomain, Borders[0], Borders[1], Borders[2], Borders[3])
        reduce_domain (ImageRotated, RectangleDomain, ImageReduced)
        crop_domain (ImageReduced, ImageReduced)
        mirror_image (ImageReduced, ImageReduced, 'row')
        mirror_image (ImageReduced, ImageResult, 'column')
        * 
        * Display the result image.
        dev_set_window (WindowHandle3)
        dev_display (ImageResult)
        dev_disp_text ('Result image', 'window', 'top', 'left', 'black', [], [])
        if (ObjImageIdx < NumObjects * NumObjImages)
            disp_continue_message (WindowHandle3, 'black', 'true')
            stop ()
        else
            dev_disp_text ('End of program', 'window', 'bottom', 'right', 'black', [], [])
        endif
    endfor
endfor

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

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

相关文章

FreeModBusRtu移植 --stm32L431RCT6(小熊派)

文章目录 前言一、移植前需要的工作二、修改点讲解1.串口中断2.定时器3.保持寄存器4.测试 总结 前言 最近需要做一个modbus485的传感器&#xff0c;主要是用来做从机。之前做过主机的是stm标准库&#xff0c;那这次做一个HAL的从机协议栈&#xff0c;方便大家直接获取数据。 移…

迈入智能新纪元:智慧机房运维系统引领行业变革

在数字化飞速发展的今天&#xff0c;机房作为信息时代的“心脏”&#xff0c;其稳定运行对于企业的业务连续性至关重要。然而&#xff0c;传统的机房运维模式面临着诸多挑战&#xff0c;如响应速度慢、故障定位难、资源浪费大等问题。智慧机房运维系统&#xff0c;它将以智能化…

裁判文书:司法公正的“名片”

在法治社会的建设中&#xff0c;裁判文书不仅是法院裁判活动的产物&#xff0c;更是司法公正的重要体现。今天&#xff0c;我们就来聊聊裁判文书上的那些知识&#xff0c;带大家走进这个充满法律智慧的领域。 一、什么是裁判文书&#xff1f; 裁判文书&#xff0c;是人民法院…

【乐吾乐2D可视化组态编辑器】在线使用,快速入门

一、在线使用 乐吾乐2D可视化组态编辑器地址&#xff1a;https://2d.le5le.com/ 二、步骤 本教程将带领你快速体验2D可视化编辑器的全流程开发。 1.创建图纸 进入2d编辑器主界面后&#xff0c;主界面最中心为图纸面板&#xff0c;默认为空图纸&#xff0c;在界面左侧为组…

Paperless-Ngx文档管理系统结合内网穿透实现随时远程搜索查阅文本

文章目录 前言1. 部署Paperless-ngx2. 本地访问Paperless-ngx3. Linux安装Cpolar4. 配置公网地址5. 远程访问6. 固定Cpolar公网地址7. 固定地址访问 前言 Paperless-ngx是一个开源的文档管理系统&#xff0c;可以将物理文档转换成可搜索的在线档案&#xff0c;从而减少纸张的使…

工业级物联网边缘网关解决方案-天拓四方

随着工业4.0时代的到来&#xff0c;越来越多的企业开始寻求智能化升级&#xff0c;以提高生产效率、降低运营成本并增强市场竞争力。然而&#xff0c;在实际的转型升级过程中&#xff0c;许多企业面临着数据孤岛、设备兼容性差、网络安全风险高等问题&#xff0c;这些问题严重制…

Google Guice超轻量级依赖注入容器

Google Guice是Goolge开源的一款超轻量级依赖注入容器&#xff0c;超轻量的特点主要体现在&#xff1a;可与其它依赖注入容器混合使用&#xff0c;例如和Spring/Spring Boot的IOC容器混合使用&#xff1b;不需要任何配置信息&#xff0c;只需要引入几个有限的依赖包即可以使用&…

容器化实践:DevOps环境下的容器交付流程

DevOps的兴起是为了应对市场和消费者对技术应用的不断增长的需求。它的目标是构建一个更快的开发环境&#xff0c;同时保持软件的高质量标准。DevOps还致力于在敏捷开发周期中提升软件的整体品质。这一目标的实现依赖于多种技术、平台和工具的综合运用。 结合容器化技术与DevO…

echarts学习: 在图表中添加多条y轴会怎么样?

前言 在撰写如何绘制双y轴图表文章时&#xff0c;我突然萌生出了一个想法&#xff0c;如果给图表添加两个以上的y轴会怎么样呢? 带着这个问题我开始了自己的探索之旅。 我找到了一篇优秀的文章作为参考&#xff0c;虽然它需要付费&#xff0c;但是不要紧&#xff0c;文中免费…

达摩院重大“遗产”!fluxonium量子比特初始化300纳秒且保真度超过99%

通用量子计算机开发的主要挑战之一是制备量子比特。十多年来&#xff0c;研究人员在构建量子计算机的过程中主要使用了transmon量子比特&#xff0c;这也是迄今为止商业上最成功的超导量子比特。 但与业界多数选择transmon量子比特不同&#xff0c;&#xff08;前&#xff09;…

关于浔川python社在全网博主原力月度排名泸州地区排名第三名的调查——浔川总社部

6月6日&#xff0c;浔川python社在全网博主原力月度排名泸州地区排名第三名。 引起了广大python爱好者等的不满&#xff0c;为什么浔川python社这么一个大的python社排名第三&#xff1f; 2024-06-04 21:59:52 浔川python社在全网博主原力月度排名泸州地区排名第一名。 2024-…

基于Vue3的Uniapp实训项目|一家鲜花店

基于Vue的Uniapp实训指导项目 项目预览&#xff1a; 在这里插入图片描述 pages.json {"pages": [ //pages数组中第一项表示应用启动页&#xff0c;参考&#xff1a;https://uniapp.dcloud.io/collocation/pages{"path": "pages/index/index",&…

static的用法

static一般用于修饰局部变量&#xff0c;全局变量&#xff0c;函数 1 static修饰局部变量 是因为改为static int a1;后&#xff0c;出了作用域&#xff0c;不会销毁a的值&#xff0c;想要理解其本质&#xff0c;首先先看一下这个图&#xff1a; static修饰局部变量时&#xf…

每天一道大厂SQL题【Day32】按消息量给广东省qq打标记

文章目录 每天一道大厂SQL题【Day32】按消息量给广东省qq打标记每日语录第32题 需求三&#xff1a;按消息量给广东省qq打标记思路分析附表 答案获取加技术群讨论文末SQL小技巧 后记 每天一道大厂SQL题【Day32】按消息量给广东省qq打标记 大家好&#xff0c;我是Maynor。相信大…

RabbitMQ系列-rabbitmq无法重新加入集群,启动失败的问题

当前存在3个节点&#xff1a;rabbitmq5672、rabbitmq5673、rabbitmq5674 当rabbitmq5673节点掉线之后&#xff0c;重启失败 重启的时候5672节点报错如下&#xff1a; 解决方案 在集群中取消失败节点 rabbitmqctl forget_cluster_node rabbitrabbitmq5673删除失败节点5673的…

工欲善其事必先利其器——IntelliJ IDEA神器使用技巧

1.IntelliJ IDEA神器使用技巧【时长2小时20分】 程序员每日都会花费数小时使用ide编写和调试代码&#xff0c;其中很多操作都是机械重复且频率非常高&#xff0c;本着"工欲善其事必先利其器"的精神&#xff0c;闷头写代码之外花点时间研究一下自己用的ide&#xff0…

zookeeper启动(一)

1.zookeeper启动入口 在zkServer.sh的启动命令中,我们可以找到zookeeper启动的关键类org.apache.zookeeper.server.quorum.QuorumPeerMain QuorumPeerMain#main 我们可以直接看org.apache.zookeeper.server.quorum.QuorumPeerMain中的main方法,从下面的main方法中,我们可以…

JavaSE基础语法合集

随着不断学习&#xff0c;我们已经走完了JavaSE基础语法的所有内容&#xff0c;博主的单独语法篇共十二篇&#xff0c;感兴趣的也可以去看看&#xff0c;内容基本一致&#xff0c;目录是重新排布的&#xff0c;数组和方法都在初识Java章节。 适合&#xff1a;老手复习和新手从零…

Ubuntu系统升级k8s节点的node节点遇到的问题

从1.23版本升级到1.28版本 node节点的是Ubuntu系统20.04的版本 Q1 node节点版本1.23升级1.28失败 解决办法&#xff1a; # 改为阿里云镜像 vim /etc/apt/sources.list.d/kubernetes.list# 新增 deb https://mirrors.aliyun.com/kubernetes/apt/ kubernetes-xenial main# 执…

pdf处理命令合集

安装weasyprint用于生成pdf 单个文件合成多个pdf linux - Merge / convert multiple PDF files into one PDF - Stack Overflow