文章目录
1,前言
双目立体视觉的开创性工作始于20世纪的60年代中期,美国MIT的Roberts通过从数字图像中提取立方体,楔形体和菱柱体等简单规则多面体的三维结构,对对物体的形状和空间关系进行描述,把过去的简单二维图像分析推广到复杂的三维场景,标志着立体视觉的诞生。双目立体视觉是立体视觉的一种重要形式,具有效率高,精度合适、系统结构简单、成本低等优点,非常适合于制造现场的在线,非接触式产品检测和质量控制。
2,原理
人之所以感受到立体视觉,是因为人的左右眼之间有6-7cm的间隔,因此左眼和右眼看到的影像会有细微的差别,所以我们很容易判断物体的远近,以及多个物体的前后关系。双目视觉的基本原理和人眼观察世界的方式类似,双目立体视觉获取图像是通过两个不同位置的相机或者一台相机通过旋转、平移拍摄同一个场景,来获取立体图像对。
假定两个相机平行放置并且相机的内部参数相同,世界坐标系中被观测目标上的任意一点 P ( x w , y w , z w ) P(x_w,y_w,z_w) P(xw,yw,zw),在左相机坐标系中的投影点为 p ( u L , v L ) p(u_L,v_L) p(uL,vL),在右相机坐标系中相面上的投影点为 p ( u R , v R ) p(u_R,v_R) p(uR,vR)。若仅用一个相机的单目视觉系统观察,只能获知点 P ( x w , y w , z w ) P(x_w,y_w,z_w) P(xw,yw,zw)可能位于左相机光心OL与点 p ( u L , v L ) p(u_L,v_L) p(uL,vL)所构成的射线上,或位于右相机光心OR与点 p ( u R , v R ) p(u_R,v_R) p(uR,vR)所构成的射线上。但是若同时用两个相机的双目视觉系统观察,并且如果能确定像点 ( u L , v L ) (u_L,v_L) (uL,vL)、 ( u R , v R ) (u_R,v_R) (uR,vR)对应位于同一空间特征点 P ( x w , y w , z w ) P(x_w,y_w,z_w) P(xw,yw,zw),就可以通过两个像点与相机光心所构成的两直线的交点计算出点 P ( x w , y w , z w ) P(x_w,y_w,z_w) P(xw,yw,zw)的确切位置。
3,组成部分
一个完整的双目立体视觉系统通常可分为六部分,如下:
3.1,数字图像采集。
3.2 ,相机标定。
对双目立体视觉而言,对他们的标定是实现立体视觉基本而又关键的一步。通常先采用单目相机的标定方法,分别得到两个相机的内外参,再通过同一世界坐标中的一组定标点来建立两个相机之间的位置关系。双目相机标定需要注意以下几个方面。
- 相机安装的位置应考虑与被测物体的距离。两个相机之间的基线间距越大,可测距离就会越远。
- 相机位置确定后,应将两个相机固定。除了相对位置需要固定外,两个相机还应当处于完全一致的水平面,同时避免发生相对旋转与前后位置偏差。在标定及实际测量过程中,应保证相机平台的水平位置,相机之间的相对位置及相机焦距不再发生变化。
- 将标定板放置在两个相机都能够完全拍到的位置上。在给标定板变动位姿时应注意,无论无何如何移动,两个相机都应能看到所有标记点。
- 两个相机的光照环境应尽可能一致。如果因为光线的干扰造成两个相机画面一明一暗,可能会影响匹配的效果,因此应注意调节光线。
3.3,图像预处理与特征提取。
3.4 ,图像校正。
标定结束后,可以使用双目相机拍摄被测物体,获取立体图像对。此时注意不要改变相机的内部参数和位姿,避免相机内部参数和外部参数失效。
双目相机拍摄的图像也会有单目相机图像可能出现的畸变,此外还可能会由于双目立体视觉平台固定的细微偏差,造成两张图像水平不一致。这种情况非常常见,可以通过对图像进行畸形矫正、旋转平移和行对齐等调整,使两张图中的特征点处于同一位置上。
在halcon中可以使用gen_binocular_rectification_map
和map_image
算子实现立体图像对的校正。其中gen_binocular_rectification_map
算子用于生成一个映射关系,该算子需要传入通过标定得到的相机内参和双目相机的相对位置关系(外部参数),然后输出两个图像的的映射图MapL和MapR并将输出结果传递给map_image
算子。
由此可以获得相机校正后的图像对。校正后,两张图对应同一特征点的像素应当处于同一水平位置,确切的说,就是图像坐标系中的行坐标相同。这是为下一步立体匹配和提取视差做准备,因为立体匹配是取参考图像中点坐在的行,在另一张图像中的同一行进行搜索对应的像素。因此,校正图像是匹配成功的前提。如果标定效果不好,也将体现在校正图像上。如果校正后图像不够水平,或者有缺失、视野扩大等其他明显异常,则可以考虑重新对双目相机进行标定。
3.5 ,立体匹配。
为了获得测量对象的深度信息,需要先求出立体图像对的视差图,这就需要对校正后的图像进行立体匹配。立体匹配是双目视觉的关键、困难的一步。与普通的图像匹配不同,立体像对之间的差异是由相机观察点的不同引起的,而不是由其他如景物本身的变化、运动而引起。
立体匹配的原理是,通过找到一张图(如左视点图)的特征点,并且在对应的另一张图(如右视点图)中搜索该点,从而获得该点的坐标和灰度。
在halcon中可以使用binocular_disparity
算子进行立体匹配并生成视差图。
binocular_disparity (ImageRec1,ImageRec2 , Disparity, Score, 'ncc', MaskWidth, MaskHeight, TextureThresh, MinDisparity, MaxDisparity, NumLevels, ScoreThresh, Filter, SubDisparity)
参数类型 | 参数名 | 作用 |
---|---|---|
输入参数 | ImageRect1 |
校正后的左视图(单通道灰度图像),作为参考图像。 |
ImageRect2 |
校正后的右视图(单通道灰度图像),作为检测图像。 | |
输出参数 | Disparity |
视差图(16位整数/浮点类型)。视差图像中坐标为(x,y) 的点的灰度值,对应于参考图像中坐标为(x,y) 的点的灰度值与检测图像中对应该坐标点的灰度值之差。 |
Score |
输出的匹配分值图像,包含参考图像上每个点的匹配最佳结果。 | |
控制参数 | Method |
匹配算法:'correlation' (相关法)或'ncc' (归一化互相关) |
MaskWidth/Height |
匹配窗口尺寸(推荐奇数,如15×15) | |
Min/MaxDisparity |
视差搜索范围(MaxDisparity 需设为负值,如-150) |
|
TextureThresh |
表示搜索窗内灰度值的最小统计分布,这对纹理比较少的局部区域有用。 | |
ScoreThresh |
匹配分数的阈值,即视差图像中仅包含匹配分值超过该阈值的点,指的注意的是,选择不同的匹配方法,阈值的取值范围也不同。 |
3.6 ,三维重建。
三维重建的目的是由二维景物图像重构出景物的三维结构。在得到视差图像,如果要进行三维重建,可以使用一些算子计算其三维坐标。例如使用disparity_to_point_3d
算子计算选定的视差图中的点的三维坐标,也可以使用disparity_image_to_xyz
算子将整张视差图转换为3D点云图。
立体视觉三维建模流程
4,主要的算子。
4.1,get_calib_data
get_calib_data (CalibDataID, 'camera', 1, 'pose', cLPcR)
用于从校准数据模型中提取特定相机的位姿信息.
1. 功能说明
- 作用:获取索引为 1 的相机在参考坐标系(通常为索引 0 的相机坐标系)下的位姿参数(平移向量与旋转矩阵),即该相机的 外参。
- 应用场景:双目或多目系统中计算相机间的相对位姿,用于立体匹配、3D重建等任务。
2. 参数解析
参数 | 说明 |
---|---|
CalibDataID |
由 create_calib_data 创建的校准模型句柄 |
'camera' |
ItemType 类型标识符,表示操作对象为相机参数 |
1 |
ItemIdx 索引号,指定要查询的相机编号(例如双目系统中 0 代表左相机,1 代表右相机) |
'pose' |
DataName 标识符,表示要获取的数据类型为位姿(包含平移和旋转参数) |
cLPcR |
返回值变量,存储相机位姿的转换矩阵(参考坐标系 → 相机坐标系) |
3. 返回值解释
- 坐标转换:
cLPcR
表示从参考坐标系到相机坐标系的齐次变换矩阵(形式为[R|t]
),用于将参考坐标系中的点转换为相机坐标系中的点。
变换方向:- 若参考坐标系为世界坐标系(WCS),则
cLPcR
表示相机的外参(WCS → CCS)。 - 若参考坐标系为另一台相机(如双目系统中的左相机),则
cLPcR
描述双目系统的基线位姿。
- 若参考坐标系为世界坐标系(WCS),则
4. 典型调用流程
- 模型创建:
create_calib_data
创建校准模型,并设置相机初始参数和标定板数据。 - 标定执行:调用
find_marks_and_pose
提取标定点,再通过calibrate_cameras
完成标定。 - 数据提取:执行
get_calib_data
查询标定后的相机位姿参数。
5. 注意事项
数据有效性:仅在完成
calibrate_cameras
后调用此操作符才能获取有效位姿数据。 坐标系定义:
- 默认参考坐标系为索引 0 的相机坐标系,可通过校准模型配置修改参考坐标系。
- 位姿中的旋转参数遵循 HALCON 的旋转顺序(通常为 ZYX 欧拉角)。
标定板补偿:若需要校正标定板厚度对位姿的影响,可使用
set_origin_pose
调整原点位置。
4.2,gen_binocular_rectification_map
是 HALCON 中用于双目相机图像校正的核心算子,旨在生成两相机的几何映射图,使极线对齐,简化立体匹配和三维重建流程。
1. 功能与输入输出
核心功能:根据双目系统的内参和外参,生成图像到公共整流平面的映射图(
Map1
和Map2
),确保校正后图像的极线水平对齐。 输入参数:
CamParam1
、CamParam2
:双目相机的标定内参(包括畸变参数),需通过calibrate_cameras
标定获得。RelPose
:相机 2 相对于相机 1 的外参(平移和旋转矩阵),由标定过程计算得出28。SubSampling
:下采样比例(默认1:1
),用于优化计算效率。Method
:校正方法(如geometric
或photometric
),决定算法的精度和速度。MapType
:映射类型(如coord_map_sub_pix
),定义输出映射图的坐标系表示形式。
输出参数:
Map1
、Map2
:双目相机的校正映射图,用于后续map_image
的图像校正。CamParamRect1
、CamParamRect2
:校正后的相机内参(调整后的焦距、主点等)。CamPoseRect1
、CamPoseRect2
:校正后相机在公共坐标系下的位姿。
2. 典型应用流程
双目标定
使用calibrate_cameras
标定双目系统,获取CamParam1
、CamParam2
和RelPose
28。生成映射图
调用gen_binocular_rectification_map
,输入标定参数生成映射图:图像校正
利用map_image
将原始图像校正至公共坐标系:立体匹配与深度计算
通过binocular_disparity
计算视差图,结合disparity_to_distance
转换为深度信息。
3. 关键注意事项
- 参数精度要求:输入的内参(
CamParam1/2
)和外参(RelPose
)需精确,否则会导致极线未对齐,影响视差匹配结果。 - 极线约束:校正后图像中的对应点位于同一水平线,确保立体匹配仅需在水平方向搜索匹配点。
- 畸变处理:若原始图像已进行畸变校正,需通过
change_radial_distortion
调整标定参数后再使用本算子。 - 性能优化:通过
SubSampling
参数降低分辨率(如设置为2
),可提升计算速度,但可能导致精度下降。
5,代码:
* 参考案例库:board_components.hdev
* 通过双目立体视觉提取主板不同高度的元器件
*
* -------------------------------------------------------
* (1) 双目立体相机标定
*
dev_update_off ()
* Set the image path (make sure, that you have set HALCONIMAGES to the HALCON image directory)
ImgPath := 'stereo/board/'
CalDescrFile := 'caltab_30mm.descr'
* Read the first images to get their size
read_image (ImageL, ImgPath + 'calib_l_01')
read_image (ImageR, ImgPath + 'calib_r_01')
* Reopen the windows with an appropriate size
dev_close_window ()
get_image_size (ImageL, WidthL, HeightL)
dev_open_window (0, 0, WidthL, HeightL, 'black', WindowHandle1)
dev_set_draw ('margin')
dev_set_color ('green')
set_display_font (WindowHandle1, 14, 'mono', 'true', 'false')
get_image_size (ImageR, WidthR, HeightR)
dev_open_window (0, WidthL + 12, WidthL, HeightL, 'black', WindowHandle2)
dev_set_draw ('margin')
dev_set_color ('green')
set_display_font (WindowHandle2, 14, 'mono', 'true', 'false')
* Calibrate the stereo setup
* (determine the internal and external camera parameters).
*
* 设置相机内参的初始值
StartCamPar := [0.0125,0,1.48e-5,1.48e-5,WidthL / 2.0,HeightL / 2.0,WidthL,HeightL]
create_calib_data ('calibration_object', 2, 1, CalibDataID)
set_calib_data_cam_param (CalibDataID, 'all', 'area_scan_division', StartCamPar)
* 标定板描述文件:caltab_30mm.descr。
* Distance between mark centers [meter]: 0.00375
set_calib_data_calib_object (CalibDataID, 0, CalDescrFile)
*
* Read all used calibration images
read_image (ImagesL, ImgPath + 'calib_l_' + [1:15]$'02d')
read_image (ImagesR, ImgPath + 'calib_r_' + [1:15]$'02d')
* 进行双目立体相机标定
* 获取标定后的左右相机内参以及右相机在左相机坐标系的位姿
calibrate_stereo_setup (ImagesL, ImagesR, WindowHandle1, WindowHandle2, CalibDataID, StartCamPar, CalDescrFile, CamParamL, CamParamR, cLPcR, Errors)
* 生成两相机的几何映射图,使极线对齐,简化立体匹配和三维重建的过程
* 映射图用于对图像进行校正,使左右图像共极线
gen_binocular_rectification_map (MapL, MapR, CamParamL, CamParamR, cLPcR, 1, 'geometric', 'bilinear', RectCamParL, RectCamParR, CamPoseRectL, CamPoseRectR, RectLPosRectR)
*
* 校正左相机图像
map_image (ImagesL, MapL, ImagesRectL)
* 校正右相机图像
map_image (ImagesR, MapR, ImagesRectR)
* 确定校正后的左右图像是否共极线
check_epipolar_constraint (ImagesRectL, ImagesRectR, RectCamParL, RectCamParR, WindowHandle1, WindowHandle2, CalDescrFile, EpipolarError)
if (EpipolarError[1] > 0.3)
* 标定的共极线偏差过大
disp_continue_message (WindowHandle2, 'black', 'true')
stop ()
endif
disp_continue_message (WindowHandle2, 'black', 'true')
stop ()
clear_calib_data (CalibDataID)
* --------------------------------------------------------
* (2) 处理双目立体视觉
*
* Parameters from above if calibration was not performed:
* CamParamL := [0.0131205,-665.85466,1.4803422e-005,1.48e-005,155.90117,126.703971,320,240]
* CamParamR := [0.0131712,-728.9579,1.4799849e-005,1.48e-005,163.265701,119.310684,320,240]
* cLPcR := [0.15350044,-0.003732778,0.04481715,0.1736607,319.8612,359.8945,0]
* Define parameter values for binocular_disparity and binocular_distance, respectively.
MaskWidth := 17
MaskHeight := 17
TextureThresh := 5
MinDisparity := 10
MaxDisparity := 40
NumLevels := 1
ScoreThresh := 0.1
* Get stereo image pair
dev_set_window (WindowHandle1)
read_image (ImageL, ImgPath + 'board_l_01')
emphasize (ImageL, ImageL, 7, 7, 1)
* 校正左图像
map_image (ImageL, MapL, ImageRectifiedL)
dev_display (ImageRectifiedL)
dev_set_window (WindowHandle2)
read_image (ImageR, ImgPath + 'board_r_01')
emphasize (ImageR, ImageR, 7, 7, 1)
* 校正右图像
map_image (ImageR, MapR, ImageRectifiedR)
dev_display (ImageRectifiedR)
disp_message (WindowHandle1, '校正左图像', 'window', 12, 12, 'black', 'true')
disp_message (WindowHandle2, '校正右图像', 'window', 12, 12, 'black', 'true')
disp_continue_message (WindowHandle2, 'black', 'true')
stop ()
binocular_disparity (ImageRectifiedL, ImageRectifiedR, Disparity, Score, 'ncc', 11, 11, 0, -30, 30, 1, 0.5, 'none', 'none')
* 计算校准后立体图像对的距离.
binocular_distance (ImageRectifiedL, ImageRectifiedR, DistanceImage, ScoreImageDistance, RectCamParL, RectCamParR, RectLPosRectR, 'ncc', MaskWidth, MaskHeight, TextureThresh, MinDisparity, MaxDisparity, NumLevels, ScoreThresh, 'left_right_check', 'interpolation')
* Display the distance image
dev_set_window (WindowHandle1)
dev_clear_window ()
dev_display (DistanceImage)
* Correct the distance image for the tilt of the stereo camera system.
* (Attention: the distance values are not measured
* in the rectified coordinate system anymore!)
* 定义一个区域,表示参考平面上的三个区域
gen_circle (Circle, [15,208,95], [58,32,211], [8,8,8])
union1 (Circle, RegionDefiningReferencePlane)
tilt_correction (DistanceImage, RegionDefiningReferencePlane, DistanceImageCorrected)
dev_set_window (WindowHandle2)
dev_clear_window ()
dev_display (DistanceImageCorrected)
disp_message (WindowHandle1, 'Distance image', 'window', 12, 12, 'black', 'true')
disp_message (WindowHandle2, 'After tilt correction', 'window', 12, 12, 'black', 'true')
disp_continue_message (WindowHandle2, 'black', 'true')
stop ()
MinHeight := -0.0005
MaxHeight := 0.05
* 将距离转换为参考平面上方的高度.
height_range_above_reference_plane (DistanceImageCorrected, HeightAboveReferencePlaneReduced, MinHeight, MaxHeight)
visualize_height_ranges (ImageRectifiedL, HeightAboveReferencePlaneReduced, WindowHandle2, 0.0004, 0.0015, 0.0015, 0.0025, 0.0025, 0.004)
dev_set_window (WindowHandle1)
dev_clear_window ()
dev_display (HeightAboveReferencePlaneReduced)
disp_message (WindowHandle2, 'Segmented components', 'window', 12, 12, 'black', 'true')
disp_message (WindowHandle1, 'Height above reference plane', 'window', 12, 12, 'black', 'true')
6,效果
相机标定: ** 左右相机共极线 **
特征点在左右相机的Row相同
校正后的左右图像:
处理结果:标记出指定高度的元器件