Halcon双相机单标定板标定实现拼图

1.Halcon图像拼接算法在之前的文章里也写过,主要是硬拼接和特征点拼接两种方式,今天增加另一种拼接图像的方式。应用场景是多个相机联合一起拍大尺寸的物体,并且相机视野之间存在重叠区域。通过在同一个标定板上面标定,计算两个相机之间位相对外参矩阵实现拼接。

2.代码分析

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)
  • 创建标定模型,并设置参数,此模型同时可以校准两个相机
    create_calib_data ('calibration_object', 2, 1, CalibDataID)
    set_calib_data_cam_param (CalibDataID, 0, \[\], StartCamParam1)
    set_calib_data_cam_param (CalibDataID, 1, \[\], StartCamParam2)
    set_calib_data_calib_object (CalibDataID, 0, 'calplate_160mm.cpd')
  • 依次访问两个相机的标定板图片,进行标定操作
    for I := 0 to NumCalibImages - 1 by 1
    • Read the calibration images.
      read_image (ImageCam1, ImagePath + '/calib_cam_1_' + (I + 1)KaTeX parse error: Double subscript at position 56: ...+ '/calib_cam_2_̲' + (I + 1)'02d')
    • Find the calibration plate and store observations
    • in the calibration data model.
      find_calib_object (ImageCam1, CalibDataID, 0, 0, I, \[\], \[\])
      get_calib_data_observ_contours (ContoursCam1, CalibDataID, 'marks', 0, 0, I)
      find_calib_object (ImageCam2, CalibDataID, 1, 0, I, \[\], \[\])
      get_calib_data_observ_contours (ContoursCam2, CalibDataID, 'marks', 1, 0, I)
    • Display calibration images and observed contours.
      dev_set_window (WindowHandle1)
      dev_display (ImageCam1)
      dev_display (ContoursCam1)
      dev_disp_text ('Image ' + (I + 1) + '/' + NumCalibImages + ' (Camera 1)', 'window', 'top', 'left', 'black', \[\], \[\])
      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

*同时校准两个摄像头.

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 ()
  • Number of objects.
    NumObjects := 2
  • Number of images per object.
    NumObjImages := 2
  • 获取两个相机各自的标定好的参数,相机内参
    get_calib_data (CalibDataID, 'camera', 0, 'params', CamParam1)
    get_calib_data (CalibDataID, 'camera', 1, 'params', CamParam2)
  • 获取相对于第一个相机(参考相机)的位姿
    get_calib_data (CalibDataID, 'calib_obj_pose', 0,0, 'pose', Pose1)
    *设置标定板厚度4mm,注意算子需要用的单位是米m
    set_origin_pose (Pose1, 0, 0, 0.004, Pose1)
  • 获取第二台相机相对于第一台相机的位姿.
    get_calib_data (CalibDataID, 'camera', 1, 'pose', RelPose2)
    *为了获得第二个相机的绝对姿态,它的相对姿态需要
    *以被反转并与第一相机的绝对姿态相结合。
    pose_invert (RelPose2, RelPose2Inverted)
    pose_compose (RelPose2Inverted, Pose1, Pose2)

*设置目标图像宽度、高度和比例,以便整个对象

*映射后图像拟合良好。

TargetWidth := 1340

TargetHeight := 800

*像素当量(1颗像素0.0002m),注意单位是m/pixel

Scale := 0.0002

  • 设置区域,可以裁剪掉有黑边的图像区域
    Borders := 125,110,665,1222

*为了映射对象图像,需要校正相机的姿态

*再次取决于物体的厚度。这里我们使用两个不同的对象:

*一把厚度约为2.5mm的尺子和一本薄薄的小册子,可以

*近似为0mm厚度。

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')

for OIdx := 0 to NumObjects - 1 by 1

* 设置矫正图像原点位置和标定板厚度

set_origin_pose (Pose1, -0.14, -0.07, HeightCorrectionsOIdx, WorldPose1)

set_origin_pose (Pose2, -0.14, -0.07, HeightCorrectionsOIdx, WorldPose2)

*

* 生成矫正矩阵

gen_image_to_world_plane_map (Map1, CamParam1, WorldPose1, WidthCam1, HeightCam1, TargetWidth, TargetHeight, Scale, 'bilinear')

gen_image_to_world_plane_map (Map2, CamParam2, WorldPose2, WidthCam1, HeightCam1, TargetWidth, TargetHeight, Scale, 'bilinear')

*

* 矫正图像

for IIdx := 1 to NumObjImages by 1

ObjImageIdx := 2 * OIdx + IIdx

read_image (ImageCam1, ImagePath + '/obj_cam_1_' + ObjImageIdxKaTeX parse error: Double subscript at position 62: ...h + '/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, Pose1, 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, Pose2, 0.05)

*

* 通过将图像映射到世界坐标系来优化图像。

map_image (ImageCam1, Map1, ImageWorld1)

map_image (ImageCam2, Map2, ImageWorld2)

*

* 将映射的图像拼接在一起。

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, 0)

*

* Rotate image and remove the black borders for display.

rotate_image (ImageFull, ImageRotated, 12, 'constant')

gen_rectangle1 (RectangleDomain, Borders0, Borders1, Borders2, Borders3)

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



相关推荐
风之清扬1 分钟前
Agent学习之四-初识Agent CLI
人工智能·科技·学习
零依赖极客8 分钟前
Day 9·1 KV 也量化——q8 KV 把缓存与 decode 带宽压到一半
c语言·开发语言·arm开发·人工智能·缓存·矩阵
陈童学哦8 分钟前
AI术语大全
人工智能
tuanxiang10 分钟前
用去AI味工具调整大模型生成的技术文档,我踩了检测阈值的坑
人工智能
天辛大师13 分钟前
天辛大师谈AI时代的沉思录,All in AI 持续做事与放大效应
大数据·人工智能·随机森林·重构·启发式算法
不要生病了15 分钟前
Brain-JEPA:用功能梯度定位与时空遮蔽预训练 fMRI 基础模型
人工智能·深度学习
艺杯羹20 分钟前
穿透深度慢思考黑盒:DeepSeek-R1 蒸馏模型量化压缩、vLLM 极限吞吐与私网落地实战
人工智能·大模型·部署
韦韦(Carina)21 分钟前
技术博客结构化:让 AI 准确引用你的 7 条排版铁律
大数据·人工智能
Runwise创新社区21 分钟前
Anthropic多智能体架构怎么落地?Lead Agent、共享记忆与并行任务的设计边界
人工智能·架构·多智能体
angushine24 分钟前
文本转视频2
人工智能·音视频·语音识别