当前位置: 首页 > news >正文

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

一、概述

最近有一个多相机标定的项目,大概是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 1ObjImageIdx := 2 * OIdx + IIdxread_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 ()elsedev_disp_text ('End of program', 'window', 'bottom', 'right', 'black', [], [])endifendfor
endfor

相关文章:

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

一、概述 最近有一个多相机标定的项目&#xff0c;大概是4个相机来标定&#xff0c;同一坐标系&#xff0c;然后拼接图&#xff0c;之前双相机标定的时候也大概看看&#xff0c;所以今天就找了那个halcon 案例多学一下&#xff0c;后面我打算做一个对位贴合的东西&#xff0c;…...

计算机网络学习记录 应用层 Day6

你好,我是Qiuner. 为记录自己编程学习过程和帮助别人少走弯路而写博客 这是我的 github https://github.com/Qiuner ⭐️ ​ gitee https://gitee.com/Qiuner &#x1f339; 如果本篇文章帮到了你 不妨点个赞吧~ 我会很高兴的 &#x1f604; (^ ~ ^) 想看更多 那就点个关注吧 我…...

如何编辑pdf文件内容?3种PDF编辑方法分享

如何编辑pdf文件内容&#xff1f;在当今数字化时代&#xff0c;PDF文件因其跨平台兼容性和保持原样不变的特点&#xff0c;在办公、学习、生活等多个领域得到了广泛应用。然而&#xff0c;PDF文件的不可编辑性也让许多用户感到困扰。你是否曾经遇到过需要修改PDF文件内容&#…...

汇总!7种大模型的部署方法!

我们如何在本地部署运行私有的开源大型语言模型&#xff08;LLMs&#xff09;呢&#xff1f;本文将向您梳理七种实用的方法及如何选择。 Hugging Face的Transformers 这是一个强大的Python库&#xff0c;专为简化本地运行LLM而设计。其优势在于自动模型下载、提供丰富的代码片段…...

什么是函数?在C语言中如何定义一个函数

函数是编程中用于执行特定任务的一组指令的集合。它有一个名称&#xff08;即函数名&#xff09;&#xff0c;可以通过该名称在程序中多次调用该函数以执行相同的任务。这有助于提高代码的可重用性和可维护性。 在C语言中&#xff0c;函数的定义通常包括以下几个部分&#xff…...

Stable Diffusion——四种模型 LoRA(包括LyCORIS)、Embeddings、Dreambooth、Hypernetwork

目前 Stable diffusion 中用到主要有四种模型&#xff0c;分别是 Textual Inversion &#xff08;TI&#xff09;以 Embeddings 为训练结果的模型、Hypernetwork 超网络模型、LoRA&#xff08;包括 LoRA 的变体 LyCORIS&#xff09;模型、Dreambooth 模型。 视频博主 koiboi 用…...

MySQL深分页,limit 100000,10 优化

文章目录 一、limit深分页为什么会变慢二、优化方案2.1 通过子查询优化&#xff08;覆盖索引&#xff09;回顾B树结构覆盖索引把条件转移到主键索引树 2.2 INNER JOIN 延迟关联2.3 标签记录法&#xff08;要求id是有序的&#xff09;2.4 使用between...and... 我们日常做分页需…...

Windows defender 开启时无法访问共享文件夹,禁用时却可以的解决方法

...

Linux[高级管理]——使用源码包编译安装Apache网站

&#x1f3e1;作者主页&#xff1a;点击&#xff01; &#x1f468;‍&#x1f4bb;Linux高级管理专栏&#xff1a;点击&#xff01; ⏰️创作时间&#xff1a;2024年5月31日14点20分 &#x1f004;️文章质量&#xff1a;96分 在Linux系统上编译和安装Apache HTTP Server是…...

Docker+JMeter+InfluxDB+Grafana 搭建性 能监控平台

JMeter原生报告的缺点&#xff1a; 无法实时共享 报告信息的展示不美观 需求方案 为了解决上述问题&#xff0c;可以通过 InfluxDB Grafana解决 &#xff1a; InfluxDB &#xff1a;是一个开源分布式指标数据库&#xff0c;使用 Go 语言编写&#xff0c;无需外部依赖 应用&am…...

NoSQL实战(MongoDB搭建主从复制)

什么是复制集&#xff1f; MongoDB复制是将数据同步到多个服务器的过程&#xff1b; 复制集提供了数据的冗余备份并提高了数据的可用性&#xff0c;通常可以保证数据的安全性&#xff1b; 复制集还允许您从硬件故障和服务中断中恢复数据。 保障数据的安全性 数据高可用性 (2…...

【讯为Linux驱动开发】3.内核空间和用户空间

【问】内存空间的组成部分&#xff1f;&#xff1f; 内存空间分为内核空间和用户空间 1.内核空间控制硬件资源&#xff0c;提供系统调用接口&#xff0c;保护系统自身安全稳定 2.用户空间实现业务逻辑 【问】如何进入内核空间使用硬件资源&#xff1f; 1.系统调用 2.软中断 3.…...

AI论文:一键生成论文的高效工具

说到这个问题&#xff0c;那真的得看你对“靠谱”的定义是怎样的啦&#xff1f; 众所周知&#xff0c;写论文是一项极其耗时间的事情&#xff0c;从开始的选题到文献资料搜索查阅&#xff0c;大纲整理等等一大堆的繁杂工作是极艰辛的。用AI写论文就不一样了&#xff0c;自动化…...

申请医疗设备注册变更时,需要补充考虑网络安全的情况有哪些?

在申请医疗器械设备注册变更时&#xff0c;需要补充网络安全的情况主要包括以下几点&#xff1a; 网络安全功能更新&#xff1a;如果医疗器械的自研软件发生网络安全功能更新&#xff0c;或者合并网络安全补丁更新的情形&#xff0c;需要单独提交一份自研软件网络安全功能更新…...

打对钩的方式做人机验证(vue+javascript)

要实现一个通过打对钩方式的人机验证&#xff0c;并且让它不容易被破解&#xff0c;可以考虑以下几点&#xff1a; 动态生成选项和题目&#xff1a;每次生成的验证选项和题目都不一样&#xff0c;防止简单的脚本通过固定的答案绕过验证。使用图像和文字混合验证&#xff1a;增…...

可视化脚本用于使用MMDetection库进行图像的目标检测

# Copyright (c) OpenMMLab. All rights reserved. import asyncio from argparse import ArgumentParserfrom mmdet.apis import (async_inference_detector, inference_detector,init_detector, show_result_pyplot) import denseclip# 解析命令行参数 def parse_args():pars…...

React-组件通信

组件通信 概念&#xff1a;组件通信就是组件之间的数据传递&#xff0c;根据组件嵌套关系的不同&#xff0c;有不同的通信方法 父传子 基础实现 实现步骤&#xff1a; 1.父组件传递数据-在子组件标签上绑定属性 2.子组件接收数据-子组件通过props参数接收数据 props说明 1.…...

低代码选型要注意什么问题?

低代码选型时&#xff0c;确实需要从多个角度综合考虑&#xff0c;以下是根据您给出的角度进行的分析和建议&#xff1a; 公司的人才资源&#xff1a; 评估团队中是否有具备编程能力的开发人员&#xff0c;以确保能够充分利用低代码平台的高级功能和进行必要的定制开发。考察实…...

hive切换spark引擎倒入数据乱码

...

fpga入门 串口定时1秒发送1字节

一、 程序说明 FPGA通过串口定时发送数据&#xff0c;每秒发送1字节&#xff0c;数据不断自增 参考小梅哥教程 二、 uart_tx.v timescale 1ns / 1psmodule uart_tx(input wire sclk,input wire rst_n,output reg uart_tx);parameter …...

华为云AI开发平台ModelArts

华为云ModelArts&#xff1a;重塑AI开发流程的“智能引擎”与“创新加速器”&#xff01; 在人工智能浪潮席卷全球的2025年&#xff0c;企业拥抱AI的意愿空前高涨&#xff0c;但技术门槛高、流程复杂、资源投入巨大的现实&#xff0c;却让许多创新构想止步于实验室。数据科学家…...

反向工程与模型迁移:打造未来商品详情API的可持续创新体系

在电商行业蓬勃发展的当下&#xff0c;商品详情API作为连接电商平台与开发者、商家及用户的关键纽带&#xff0c;其重要性日益凸显。传统商品详情API主要聚焦于商品基本信息&#xff08;如名称、价格、库存等&#xff09;的获取与展示&#xff0c;已难以满足市场对个性化、智能…...

MFC内存泄露

1、泄露代码示例 void X::SetApplicationBtn() {CMFCRibbonApplicationButton* pBtn GetApplicationButton();// 获取 Ribbon Bar 指针// 创建自定义按钮CCustomRibbonAppButton* pCustomButton new CCustomRibbonAppButton();pCustomButton->SetImage(IDB_BITMAP_Jdp26)…...

为什么需要建设工程项目管理?工程项目管理有哪些亮点功能?

在建筑行业&#xff0c;项目管理的重要性不言而喻。随着工程规模的扩大、技术复杂度的提升&#xff0c;传统的管理模式已经难以满足现代工程的需求。过去&#xff0c;许多企业依赖手工记录、口头沟通和分散的信息管理&#xff0c;导致效率低下、成本失控、风险频发。例如&#…...

【机器视觉】单目测距——运动结构恢复

ps&#xff1a;图是随便找的&#xff0c;为了凑个封面 前言 在前面对光流法进行进一步改进&#xff0c;希望将2D光流推广至3D场景流时&#xff0c;发现2D转3D过程中存在尺度歧义问题&#xff0c;需要补全摄像头拍摄图像中缺失的深度信息&#xff0c;否则解空间不收敛&#xf…...

【CSS position 属性】static、relative、fixed、absolute 、sticky详细介绍,多层嵌套定位示例

文章目录 ★ position 的五种类型及基本用法 ★ 一、position 属性概述 二、position 的五种类型详解(初学者版) 1. static(默认值) 2. relative(相对定位) 3. absolute(绝对定位) 4. fixed(固定定位) 5. sticky(粘性定位) 三、定位元素的层级关系(z-i…...

sipsak:SIP瑞士军刀!全参数详细教程!Kali Linux教程!

简介 sipsak 是一个面向会话初始协议 (SIP) 应用程序开发人员和管理员的小型命令行工具。它可以用于对 SIP 应用程序和设备进行一些简单的测试。 sipsak 是一款 SIP 压力和诊断实用程序。它通过 sip-uri 向服务器发送 SIP 请求&#xff0c;并检查收到的响应。它以以下模式之一…...

管理学院权限管理系统开发总结

文章目录 &#x1f393; 管理学院权限管理系统开发总结 - 现代化Web应用实践之路&#x1f4dd; 项目概述&#x1f3d7;️ 技术架构设计后端技术栈前端技术栈 &#x1f4a1; 核心功能特性1. 用户管理模块2. 权限管理系统3. 统计报表功能4. 用户体验优化 &#x1f5c4;️ 数据库设…...

深入浅出深度学习基础:从感知机到全连接神经网络的核心原理与应用

文章目录 前言一、感知机 (Perceptron)1.1 基础介绍1.1.1 感知机是什么&#xff1f;1.1.2 感知机的工作原理 1.2 感知机的简单应用&#xff1a;基本逻辑门1.2.1 逻辑与 (Logic AND)1.2.2 逻辑或 (Logic OR)1.2.3 逻辑与非 (Logic NAND) 1.3 感知机的实现1.3.1 简单实现 (基于阈…...

SQL慢可能是触发了ring buffer

简介 最近在进行 postgresql 性能排查的时候,发现 PG 在某一个时间并行执行的 SQL 变得特别慢。最后通过监控监观察到并行发起得时间 buffers_alloc 就急速上升,且低水位伴随在整个慢 SQL,一直是 buferIO 的等待事件,此时也没有其他会话的争抢。SQL 虽然不是高效 SQL ,但…...