当前位置: 首页 > 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=像素当量*  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=(像素当量(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

相关文章:

  • 内存管理--3.用幻灯片讲解C++手动内存管理
  • memory动态内存管理学习之unique_ptr
  • 探究Vue源码:深入理解diff算法
  • Codeforces Round 950 (Div. 3)
  • Zemax中FFT PSF和惠更斯PSF的区别?
  • GA/T 1400视频汇聚平台EasyCVR级联后,平台显示无通道是什么原因?
  • 【JavaScript脚本宇宙】创造声音的魔法:深入了解Web音频处理库
  • Spring Data Jpa 实现批量插入或更新
  • 【职业思考】程序员应该有什么职业素养?
  • 怎么排查native层的bug
  • DevOps后时代,构建基于价值流的平台化工程
  • f-stack和DPDK
  • hadoop疑难问题解决_NoClassDefFoundError: org/apache/hadoop/fs/adl/AdlFileSystem
  • 强化学习面试题
  • Sui Generis如何为艺术家弥合Web3的鸿沟
  • 【407天】跃迁之路——程序员高效学习方法论探索系列(实验阶段164-2018.03.19)...
  • 【跃迁之路】【477天】刻意练习系列236(2018.05.28)
  • 2017年终总结、随想
  • Android组件 - 收藏集 - 掘金
  • Angular4 模板式表单用法以及验证
  • go语言学习初探(一)
  • Iterator 和 for...of 循环
  • JavaScript 事件——“事件类型”中“HTML5事件”的注意要点
  • Laravel 中的一个后期静态绑定
  • MYSQL如何对数据进行自动化升级--以如果某数据表存在并且某字段不存在时则执行更新操作为例...
  • ReactNativeweexDeviceOne对比
  • Spring Boot MyBatis配置多种数据库
  • Spring技术内幕笔记(2):Spring MVC 与 Web
  • vue脚手架vue-cli
  • Vue小说阅读器(仿追书神器)
  • 如何解决微信端直接跳WAP端
  • 深度学习在携程攻略社区的应用
  • LevelDB 入门 —— 全面了解 LevelDB 的功能特性
  • 回归生活:清理微信公众号
  • ​​​【收录 Hello 算法】10.4 哈希优化策略
  • ###STL(标准模板库)
  • #AngularJS#$sce.trustAsResourceUrl
  • #FPGA(基础知识)
  • (20050108)又读《平凡的世界》
  • (delphi11最新学习资料) Object Pascal 学习笔记---第5章第5节(delphi中的指针)
  • (js)循环条件满足时终止循环
  • (webRTC、RecordRTC):navigator.mediaDevices undefined
  • (过滤器)Filter和(监听器)listener
  • (四)docker:为mysql和java jar运行环境创建同一网络,容器互联
  • (原創) X61用戶,小心你的上蓋!! (NB) (ThinkPad) (X61)
  • (原創) 如何動態建立二維陣列(多維陣列)? (.NET) (C#)
  • (转)setTimeout 和 setInterval 的区别
  • .bat批处理(八):各种形式的变量%0、%i、%%i、var、%var%、!var!的含义和区别
  • .NET 6 Mysql Canal (CDC 增量同步,捕获变更数据) 案例版
  • .net 7 上传文件踩坑
  • .net refrector
  • .NET 表达式计算:Expression Evaluator
  • .NET 服务 ServiceController
  • .net 写了一个支持重试、熔断和超时策略的 HttpClient 实例池
  • .NET 依赖注入和配置系统