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 …...
19. 浮力产生原因演示
8. 浮力产生原因演示 功能介绍: 利用动画演示浮力产生的根本原因——液体对物体上下表面的压力差。界面显示浸没在水中的立方体,侧面标注上下表面的压强 (P=ρghP=\rho ghP=ρgh...
3步永久解锁IDM:从试用期烦恼到终身免费使用的完整指南
3步永久解锁IDM:从试用期烦恼到终身免费使用的完整指南 【免费下载链接】IDM-Activation-Script IDM Activation & Trail Reset Script 项目地址: https://gitcode.com/gh_mirrors/id/IDM-Activation-Script IDM Activation Script(IAS&#…...
SEO 搜索引擎营销工具如何帮助网站进行社交媒体营销_SEO 搜索引擎营销工具如何分析网站用户行为
SEO 搜索引擎营销工具如何帮助网站进行社交媒体营销 在当前数字化营销的浪潮中,SEO(搜索引擎优化)搜索引擎营销工具已经成为了许多企业和网站必不可少的工具。SEO工具不仅能够帮助网站提高在搜索引擎中的排名,还在社交媒体营销方…...
【Spring Boot 3 + Vue 3】宠物医院智能诊疗管理系统 全功能展示 | 前后端分离实战
宠物医院智能诊疗管理系统 — Spring Boot 3 Vue 3 全栈实战 项目简介基于 Spring Boot 3 Vue 3 Bootstrap 5 MyBatis-Plus MySQL 构建的宠物医院智能诊疗管理系统,采用前后端分离架构,实现了从游客浏览、在线预约、诊疗记录到物资管理的完整业务闭…...
Phi-4-Reasoning-Vision保姆级教程:Streamlit界面响应式设计与GPU状态反馈
Phi-4-Reasoning-Vision保姆级教程:Streamlit界面响应式设计与GPU状态反馈 1. 工具概览 Phi-4-Reasoning-Vision是基于微软最新多模态大模型开发的专业级推理工具,专为双卡4090环境优化设计。这个工具能让开发者轻松体验15B参数大模型的强大推理能力&a…...
Qwen3.5-2B轻量部署教程:适配Jetson/树莓派的2B多模态模型实测
Qwen3.5-2B轻量部署教程:适配Jetson/树莓派的2B多模态模型实测 1. 模型概述 Qwen3.5-2B是阿里云推出的轻量化多模态基础模型,属于Qwen3.5系列的小参数版本(20亿参数)。这个模型专为边缘计算设备优化,主打低功耗、低门…...
Open UI5 源代码解析之841:VerticalLayout.js
源代码仓库: https://github.com/SAP/openui5 源代码位置:src\sap.ui.layout\src\sap\ui\layout\VerticalLayout.js VerticalLayout 文件解析 本文围绕 VerticalLayout.js 在 OpenUI5 项目中的角色与实现展开,重点说明该控件在布局体系中的定位、元数据设计、渲染协作、…...
终极QOR监控和日志指南:保障企业应用稳定运行的完整方案
终极QOR监控和日志指南:保障企业应用稳定运行的完整方案 【免费下载链接】qor QOR is a set of libraries written in Go that abstracts common features needed for business applications, CMSs, and E-commerce systems. 项目地址: https://gitcode.com/gh_mi…...
手撕 Transformer (2):嵌入层和位置编码的实现上篇文章讲过,Transformer 可分为四个部分:输入、输出、编码器、解
嵌入层的作用:为了将文本中词汇的数字表示转换为向量表示(语义向量),这样后续神经网络就可以对其进行计算了。 1.1 代码实现 import torchimport torch.nn as nnimport mathfrom torch.autograd import Variableclass Embeddings…...
【OpenClaw全面解析:从零到精通】第032篇:OpenClaw v2026.4.1 深度解析:聊天原生任务板、SearXNG 搜索与安全护栏如何重塑 AI Agent 工作流
上一篇:[第031篇] OpenClaw 会话管理与上下文持久化深度解析:从“失忆”到长期记忆的完整解决方案 下一篇:未完待续 OpenClaw v2026.4.1 不是一个“加几个小功能”的普通补丁版,而是对 v2026.3.31 安全收紧与后台任务重构的一次前…...
