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 双相机标定与拼图(一)
一、概述 最近有一个多相机标定的项目,大概是4个相机来标定,同一坐标系,然后拼接图,之前双相机标定的时候也大概看看,所以今天就找了那个halcon 案例多学一下,后面我打算做一个对位贴合的东西,…...
计算机网络学习记录 应用层 Day6
你好,我是Qiuner. 为记录自己编程学习过程和帮助别人少走弯路而写博客 这是我的 github https://github.com/Qiuner ⭐️ gitee https://gitee.com/Qiuner 🌹 如果本篇文章帮到了你 不妨点个赞吧~ 我会很高兴的 😄 (^ ~ ^) 想看更多 那就点个关注吧 我…...
如何编辑pdf文件内容?3种PDF编辑方法分享
如何编辑pdf文件内容?在当今数字化时代,PDF文件因其跨平台兼容性和保持原样不变的特点,在办公、学习、生活等多个领域得到了广泛应用。然而,PDF文件的不可编辑性也让许多用户感到困扰。你是否曾经遇到过需要修改PDF文件内容&#…...
汇总!7种大模型的部署方法!
我们如何在本地部署运行私有的开源大型语言模型(LLMs)呢?本文将向您梳理七种实用的方法及如何选择。 Hugging Face的Transformers 这是一个强大的Python库,专为简化本地运行LLM而设计。其优势在于自动模型下载、提供丰富的代码片段…...
什么是函数?在C语言中如何定义一个函数
函数是编程中用于执行特定任务的一组指令的集合。它有一个名称(即函数名),可以通过该名称在程序中多次调用该函数以执行相同的任务。这有助于提高代码的可重用性和可维护性。 在C语言中,函数的定义通常包括以下几个部分ÿ…...
Stable Diffusion——四种模型 LoRA(包括LyCORIS)、Embeddings、Dreambooth、Hypernetwork
目前 Stable diffusion 中用到主要有四种模型,分别是 Textual Inversion (TI)以 Embeddings 为训练结果的模型、Hypernetwork 超网络模型、LoRA(包括 LoRA 的变体 LyCORIS)模型、Dreambooth 模型。 视频博主 koiboi 用…...
MySQL深分页,limit 100000,10 优化
文章目录 一、limit深分页为什么会变慢二、优化方案2.1 通过子查询优化(覆盖索引)回顾B树结构覆盖索引把条件转移到主键索引树 2.2 INNER JOIN 延迟关联2.3 标签记录法(要求id是有序的)2.4 使用between...and... 我们日常做分页需…...
Linux[高级管理]——使用源码包编译安装Apache网站
🏡作者主页:点击! 👨💻Linux高级管理专栏:点击! ⏰️创作时间:2024年5月31日14点20分 🀄️文章质量:96分 在Linux系统上编译和安装Apache HTTP Server是…...
Docker+JMeter+InfluxDB+Grafana 搭建性 能监控平台
JMeter原生报告的缺点: 无法实时共享 报告信息的展示不美观 需求方案 为了解决上述问题,可以通过 InfluxDB Grafana解决 : InfluxDB :是一个开源分布式指标数据库,使用 Go 语言编写,无需外部依赖 应用&am…...
NoSQL实战(MongoDB搭建主从复制)
什么是复制集? MongoDB复制是将数据同步到多个服务器的过程; 复制集提供了数据的冗余备份并提高了数据的可用性,通常可以保证数据的安全性; 复制集还允许您从硬件故障和服务中断中恢复数据。 保障数据的安全性 数据高可用性 (2…...
【讯为Linux驱动开发】3.内核空间和用户空间
【问】内存空间的组成部分?? 内存空间分为内核空间和用户空间 1.内核空间控制硬件资源,提供系统调用接口,保护系统自身安全稳定 2.用户空间实现业务逻辑 【问】如何进入内核空间使用硬件资源? 1.系统调用 2.软中断 3.…...
AI论文:一键生成论文的高效工具
说到这个问题,那真的得看你对“靠谱”的定义是怎样的啦? 众所周知,写论文是一项极其耗时间的事情,从开始的选题到文献资料搜索查阅,大纲整理等等一大堆的繁杂工作是极艰辛的。用AI写论文就不一样了,自动化…...
申请医疗设备注册变更时,需要补充考虑网络安全的情况有哪些?
在申请医疗器械设备注册变更时,需要补充网络安全的情况主要包括以下几点: 网络安全功能更新:如果医疗器械的自研软件发生网络安全功能更新,或者合并网络安全补丁更新的情形,需要单独提交一份自研软件网络安全功能更新…...
打对钩的方式做人机验证(vue+javascript)
要实现一个通过打对钩方式的人机验证,并且让它不容易被破解,可以考虑以下几点: 动态生成选项和题目:每次生成的验证选项和题目都不一样,防止简单的脚本通过固定的答案绕过验证。使用图像和文字混合验证:增…...
可视化脚本用于使用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-组件通信
组件通信 概念:组件通信就是组件之间的数据传递,根据组件嵌套关系的不同,有不同的通信方法 父传子 基础实现 实现步骤: 1.父组件传递数据-在子组件标签上绑定属性 2.子组件接收数据-子组件通过props参数接收数据 props说明 1.…...
低代码选型要注意什么问题?
低代码选型时,确实需要从多个角度综合考虑,以下是根据您给出的角度进行的分析和建议: 公司的人才资源: 评估团队中是否有具备编程能力的开发人员,以确保能够充分利用低代码平台的高级功能和进行必要的定制开发。考察实…...
fpga入门 串口定时1秒发送1字节
一、 程序说明 FPGA通过串口定时发送数据,每秒发送1字节,数据不断自增 参考小梅哥教程 二、 uart_tx.v timescale 1ns / 1psmodule uart_tx(input wire sclk,input wire rst_n,output reg uart_tx);parameter …...
论文解读:交大港大上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化学习框架(二)
HoST框架核心实现方法详解 - 论文深度解读(第二部分) 《Learning Humanoid Standing-up Control across Diverse Postures》 系列文章: 论文深度解读 + 算法与代码分析(二) 作者机构: 上海AI Lab, 上海交通大学, 香港大学, 浙江大学, 香港中文大学 论文主题: 人形机器人…...
ES6从入门到精通:前言
ES6简介 ES6(ECMAScript 2015)是JavaScript语言的重大更新,引入了许多新特性,包括语法糖、新数据类型、模块化支持等,显著提升了开发效率和代码可维护性。 核心知识点概览 变量声明 let 和 const 取代 var…...
【WiFi帧结构】
文章目录 帧结构MAC头部管理帧 帧结构 Wi-Fi的帧分为三部分组成:MAC头部frame bodyFCS,其中MAC是固定格式的,frame body是可变长度。 MAC头部有frame control,duration,address1,address2,addre…...
MongoDB学习和应用(高效的非关系型数据库)
一丶 MongoDB简介 对于社交类软件的功能,我们需要对它的功能特点进行分析: 数据量会随着用户数增大而增大读多写少价值较低非好友看不到其动态信息地理位置的查询… 针对以上特点进行分析各大存储工具: mysql:关系型数据库&am…...
【JavaWeb】Docker项目部署
引言 之前学习了Linux操作系统的常见命令,在Linux上安装软件,以及如何在Linux上部署一个单体项目,大多数同学都会有相同的感受,那就是麻烦。 核心体现在三点: 命令太多了,记不住 软件安装包名字复杂&…...
优选算法第十二讲:队列 + 宽搜 优先级队列
优选算法第十二讲:队列 宽搜 && 优先级队列 1.N叉树的层序遍历2.二叉树的锯齿型层序遍历3.二叉树最大宽度4.在每个树行中找最大值5.优先级队列 -- 最后一块石头的重量6.数据流中的第K大元素7.前K个高频单词8.数据流的中位数 1.N叉树的层序遍历 2.二叉树的锯…...
【网络安全】开源系统getshell漏洞挖掘
审计过程: 在入口文件admin/index.php中: 用户可以通过m,c,a等参数控制加载的文件和方法,在app/system/entrance.php中存在重点代码: 当M_TYPE system并且M_MODULE include时,会设置常量PATH_OWN_FILE为PATH_APP.M_T…...
SQL Server 触发器调用存储过程实现发送 HTTP 请求
文章目录 需求分析解决第 1 步:前置条件,启用 OLE 自动化方式 1:使用 SQL 实现启用 OLE 自动化方式 2:Sql Server 2005启动OLE自动化方式 3:Sql Server 2008启动OLE自动化第 2 步:创建存储过程第 3 步:创建触发器扩展 - 如何调试?第 1 步:登录 SQL Server 2008第 2 步…...
vue3 daterange正则踩坑
<el-form-item label"空置时间" prop"vacantTime"> <el-date-picker v-model"form.vacantTime" type"daterange" start-placeholder"开始日期" end-placeholder"结束日期" clearable :editable"fal…...
【安全篇】金刚不坏之身:整合 Spring Security + JWT 实现无状态认证与授权
摘要 本文是《Spring Boot 实战派》系列的第四篇。我们将直面所有 Web 应用都无法回避的核心问题:安全。文章将详细阐述认证(Authentication) 与授权(Authorization的核心概念,对比传统 Session-Cookie 与现代 JWT(JS…...
