关于双目视觉的原理请参阅上一篇博文(https://blog.csdn.net/Ketal_N/article/details/83744626), 本文主要介绍基于halcon的双目视觉的 相机标定 及 三维重建 。
系统搭建如下:
标定程序参考了halcon中的双目标定示例binocular_calibration.hdev,标定步骤为:
1)程序的初始化设置;
2)创建标定数据模型(也可以不用标定数据模型,下面的代码就没有使用),为相机内参设置初始值;
3)描述标定对象;
4)循环读取多幅图像,提取标定板上的标记点及标定板位姿。
5)执行标定;
*6)进行图像对校正。
代码如下:
*
* 该程序演示了如何使用 find_caltab, find_marks_and_pose和binocular_calibration 算子进行双目立体视觉系统的标定
* 共50幅(左右两相机分别25幅)。标定完成后,利用对极几何对图像对进行校正。
*
* ---------------------------------------------------------
* 设置图片路径
ImgPath := 'C:/BinocualrCalibration/calibrationImgs/'
* 读取第一幅图像,并获取图像尺寸
Index := 1
read_image (ImageL, ImgPath + 'L' + Index$'02d')
read_image (ImageR, ImgPath + 'R' + Index$'02d')
* 打开合适尺寸大小的窗口
dev_close_window ()
dev_update_off ()
get_image_size (ImageL, WidthL, HeightL)
dev_open_window (0, 0, 0.4*WidthL,0.4* 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, 0.4*WidthL + 12, 0.4*WidthL, 0.4*HeightL, 'black', WindowHandle2)
dev_set_draw ('margin')
dev_set_color ('green')
* 读取标定板模型.
CaltabFile := 'caltab_30mm.descr'
caltab_points (CaltabFile, X, Y, Z)
* 为相机内参设置初始值
StartCamParL := [0.016,0,3.75e-6,3.75e-6,WidthL / 2.0,HeightL / 2.0,WidthL,HeightL]
StartCamParR := StartCamParL
* find_caltab 和 find_marks_and_pose参数设置
SizeGauss := 3
MarkThresh := 120
MinDiamMarks := 5
StartThresh := 128
DeltaThresh := 10
MinThresh := 18
Alpha := 0.9
MinContLength := 15
MaxDiamMarks := 100
* 创建数组,用于存放图像坐标系下的标定点坐标和标定板的初始位姿
RowsL := []
ColsL := []
StartPosesL := []
RowsR := []
ColsR := []
StartPosesR := []
*循环读取标定图像
for Index := 1 to 25 by 1
* 读取标定图像
read_image (ImageL, ImgPath + 'L' + Index$'02d')
read_image (ImageR, ImgPath + 'R' + Index$'02d')
* 寻找标定板
find_caltab (ImageL, CaltabL, CaltabFile, SizeGauss, MarkThresh, MinDiamMarks)
find_caltab (ImageR, CaltabR, CaltabFile, SizeGauss, MarkThresh, MinDiamMarks)
* 显示标定板区域
dev_set_window (WindowHandle1)
dev_display (ImageL)
dev_display (CaltabL)
dev_set_window (WindowHandle2)
dev_display (ImageR)
dev_display (CaltabR)
* (左相机)提取标记点和标定板位姿,并显示位姿坐标
find_marks_and_pose (ImageL, CaltabL, CaltabFile, StartCamParL, StartThresh, DeltaThresh, MinThresh, Alpha, MinContLength, MaxDiamMarks, RCoordL, CCoordL, StartPoseL)
disp_caltab (WindowHandle1, CaltabFile, StartCamParL, StartPoseL, 1)
*(右相机)提取标记点和标定板位姿,并显示位姿坐标
find_marks_and_pose (ImageR, CaltabR, CaltabFile, StartCamParR, StartThresh, DeltaThresh, MinThresh, Alpha, MinContLength, MaxDiamMarks, RCoordR, CCoordR, StartPoseR)
disp_caltab (WindowHandle2, CaltabFile, StartCamParR, StartPoseR, 1)
* 计算标记点在图像坐标系下的位姿,并估算所有图像对间的相对位姿
RowsL := [RowsL,RCoordL]
ColsL := [ColsL,CCoordL]
StartPosesL := [StartPosesL,StartPoseL]
RowsR := [RowsR,RCoordR]
ColsR := [ColsR,CCoordR]
StartPosesR := [StartPosesR,StartPoseR]
endfor
*
* 执行标定
*
binocular_calibration (X, Y, Z, RowsL, ColsL, RowsR, ColsR, StartCamParL, StartCamParR, StartPosesL, StartPosesR, 'all', CamParamL, CamParamR, NFinalPoseL, NFinalPoseR, cLPcR, Errors)
* 可以将标定结果存入文件中:
* write_cam_par (CamParamL, 'cam_left-125.dat')
* write_cam_par (CamParamR, 'cam_right-125.dat')
* write_pose (cLPcR, 'pos_right2left.dat')
* 生成校正的映射图
gen_binocular_rectification_map (MapL, MapR, CamParamL, CamParamR, cLPcR, 1, 'geometric', 'bilinear', RectCamParL, RectCamParR, CamPoseRectL, CamPoseRectR, RectLPosRectR)
* 读取已标定系统的一个图像对
read_image (ImageL, ImgPath + 'L01')
read_image (ImageR, ImgPath + 'R01')
* 校正图像对并显示
map_image (ImageL, MapL, ImageRectifiedL)
map_image (ImageR, MapR, ImageRectifiedR)
* 检查校正图像的对极约束并显示结果(包含一些对应的极线)
check_epipolar_constraint (ImageRectifiedL, ImageRectifiedR, RectCamParL, RectCamParR, WindowHandle1, WindowHandle2, CaltabFile, EpipolarError)
用采集的一对图像对来说明三维立体重建的过程。
三维重建步骤如下:
1)读取图像对
2)输入相机参数
3)校正图像对
4)计算视差
5)对视差图进行处理
6)将视差图转换为X、Y、Z分量图
7)3D点的可视化
代码如下:
* 该程序演示了如何使用'disparity_image_to_xyz'算子。
* 首先进行图像对的校正,然后计算所有视差图中所有点的 x,y,z 坐标,最后将将3D点可视化。
*
* 初始化设置
dev_close_window ()
dev_update_off ()
dev_set_preferences ('graphics_window_context_menu', 'false')
dev_set_preferences ('suppress_handled_exceptions_dlg', 'true')
*
* 定义要选择的左右图像对
ImagePath := 'C:/BinocualrCalibration/TestObject/'
Image1 := ImagePath + '/LT08'
Image2 := ImagePath + '/RT08'
*
* 定义相机参数
CamParamL := [0.0162229,-21.9492,3.7503e-006,3.75e-006,622.487,523.778,1280,960]
CamParamR := [0.0160868,-161.835,3.7492e-006,3.75e-006,615.622,514.203,1280,960]
RelPose := [0.13018,-0.000356889,0.0248832,0.434254,343.51,359.836,0]
*
* 生成视差图
* ******************************************
*
* 生成校正映射图像对
gen_binocular_rectification_map (MapL, MapR, CamParamL, CamParamR, RelPose, 1, 'geometric', 'bilinear', RectCamParL, RectCamParR, CamPoseRectL, CamPoseRectR, RectLPosRectR)
*
* 读取原图像对并进行校正,得到校正后的图像对
read_image (ImageL, Image1)
read_image (ImageR, Image2)
map_image (ImageL, MapL, ImageRectifiedL)
map_image (ImageR, MapR, ImageRectifiedR)
*
* 显示校正后的左侧图像
get_image_size (ImageRectifiedL, WidthL, HeightL)
dev_open_window (0, 0, 0.4*WidthL,0.4* HeightL, 'black', WindowHandle1)
set_display_font (WindowHandle1, 11, 'mono', 'true', 'false')
dev_display (ImageRectifiedL)
disp_message (WindowHandle1, 'Left rectified image', 'window', 10, 10, 'black', 'true')
*
* 显示校正后的右侧图像
dev_open_window (0, 0.4*WidthL + 10, 0.4*WidthL, 0.4*HeightL, 'black', WindowHandle2)
set_display_font (WindowHandle2, 11, 'mono', 'true', 'false')
dev_display (ImageRectifiedL)
disp_message (WindowHandle2, 'Right rectified image', 'window', 10, 10, 'black', 'true')
disp_continue_message (WindowHandle2, 'black', 'true')
stop ()
*
* 计算视差
binocular_disparity (ImageRectifiedL, ImageRectifiedR, DisparityImage, Score, 'ncc', 33, 33, 0, -36, 20, 1, 0.3, 'left_right_check', 'interpolation')
*
* 填充视差图中的缺陷
get_domain (DisparityImage, RegionInpainting)
complement (RegionInpainting, RegionInpainting)
full_domain (DisparityImage, DisparityImage)
harmonic_interpolation (DisparityImage, RegionInpainting, DisparityImage, 0.001)
*
* 显示视差图
dev_set_window (WindowHandle1)
dev_set_lut ('temperature')
dev_display (DisparityImage)
disp_message (WindowHandle1, 'Disparity image', 'window', 10, 10, 'black', 'true')
*
* 计算点的3D坐标
* *******************************************
*
* 将视差图转换成X,Y,Z分量图,分量图中的灰度值表示各分量的坐标
disparity_image_to_xyz (DisparityImage, X, Y, Z, RectCamParL, RectCamParR, RectLPosRectR)
*
* 3D点的可视化
* *******************************************
*
* 将点云的中心移至原点附近,并进行z方向上的尺度缩放
min_max_gray (X, X, 5, MinX, MaxX, RangeX)
min_max_gray (Y, Y, 5, MinY, MaxY, RangeY)
min_max_gray (Z, Z, 5, MinZ, MaxZ, RangeZ)
scale_image (X, X, 1.0, -MinX - 0.5 * RangeX)
scale_image (Y, Y, 1.0, -MinY - 0.5 * RangeY)
scale_image (Z, Z, 4.0, -4.0 * MinZ - 2.0 * RangeZ)
*
* 显示校正后的右图像
dev_set_window (WindowHandle2)
dev_display (ImageRectifiedR)
disp_message (WindowHandle2, 'Computed 3D points in 3D space', 'window', 10, 10, 'black', 'true')
disp_message (WindowHandle2, 'Left button: rotate', 'window', 205, 10, 'white', 'false')
disp_message (WindowHandle2, 'Right button: exit', 'window', 220, 10, 'white', 'false')
*
* 对3D点云循环旋转操作的设置
RelQuaternion := [0,0,0,1]
NumAddIn := 0
Cx := WidthL * 0.5
Cy := HeightL * 0.5
*
* 旋转循环
visualize_3D_space (DisparityImage, X, Y, Z, ImageRectifiedL, WindowHandle2, Cx, Cy, HeightL, WidthL, RelQuaternion, NumAddIn, MinX, MinY, MinZ, RangeX, RangeY, RangeZ, RectCamParL)
得到的视差图如图3所示,可以看到,目标物体的孔径周围有基础缺陷(白色部分),这是由于在输入的图像对中,目标物的孔径周围反光,显示为白色,而在计算视差图时,应尽量避免这种整块出现的单一颜色区域。
此外,还可以通过创建3D对象模型的方式将结果可视化。用到的算子为:xyz_to_object_model_3d( ), create_pose( ), visualize_object_model_3d( )。
注:3D重建的显示效果和相机标定的精度、拍摄环境、视差图的算法等因素相关,其中计算视差图用到的算子为binocular_disparity( ),包含的参数较多,需要选择合适的参数才得到较好的视差图。
ps:如有错误,谢谢指出。整理不易,转载请注明出处。