Matlab双目相机标定
Posted indigo love
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Matlab双目相机标定相关的知识,希望对你有一定的参考价值。
1 概述
现在有许多双目相机在出厂时就已经标定好了,用户拿到手后可以直接使用,例如Intel Realsense系列。但是有些相机出厂的时候并没有完成标定工作,因而这个时候就需要我们自己来标定。由于笔者曾改装过一个双目相机,最远可以测至50m,因此有一些心得体会想给大家分享一下。本文主要介绍双目相机标定的整个过程,以及导出标定数据的方法。
相机标定主要分为手动标定和自动标定,手动标定比较繁琐,这里主要介绍基于matlab工具箱的自动标定方式来对双目相机进行标定。具体的相关标定细节也可以参照这篇博客:matlab双目标定(详细过程),里面有详细介绍。
2 Matlab工具箱标定
首先需要准备一张棋盘,如下图所示。对于标定不同测距范围相机所用的棋盘方格宽度会有所不同。对于短焦双目相机(测距范围在20m以内),棋盘中方格的宽度达到20mm即可;对于长焦双目相机(测距范围在40m左右),棋盘中方格的宽度需要尽量大,否则会影响标定的精度,一般至少达到60mm。
笔者在标定长焦相机时方格长宽选择60mm。然后运行项目文件中photo.py脚本文件对棋盘进行多角度拍摄,每按下一次s键,会保存一组左右镜头的照片,照片保存的路径参数可以由用户自由设定,参数名为folder。
# !/usr/bin/python
# -*- coding: utf-8 -*-
import cv2
import time
AUTO = False # 自动拍照,或手动按s键拍照
INTERVAL = 2 # 自动拍照间隔
cap = cv2.VideoCapture(0 + cv2.CAP_DSHOW) # windows下开启摄像头是采用如下语句(微软特有):cv2.VideoCapture( camera_number + cv2.CAP_DSHOW)
cap.set(cv2.CAP_PROP_FRAME_WIDTH, 2560) # 设置双目的宽度
cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 720) # 设置双目的高度
# 显示缓存数
# print(cap.get(cv2.CAP_PROP_BUFFERSIZE))
# 设置缓存区的大小
# cap.set(cv2.CAP_PROP_BUFFERSIZE, 1)
print(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
print(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
#设置FPS
# print('setfps', cap.set(cv2.CAP_PROP_FPS, 25))
# print(cap.get(cv2.CAP_PROP_FPS))
counter = 0
utc = time.time()
folder = "images/" # 拍照文件目录
def shot(pos, frame):
global counter
path = folder + pos + "_" + str(counter) + ".jpg"
cv2.imwrite(path, frame)
print("snapshot saved into: " + path)
while True:
ret, frame = cap.read()
if not ret:
print("camera is not connected!")
break
left_frame = frame[0:720, 0:1280]
right_frame = frame[0:720, 1280:2560]
cv2.imshow("left", left_frame)
cv2.imshow("right", right_frame)
now = time.time()
if AUTO and now - utc >= INTERVAL:
shot("left", left_frame)
shot("right", right_frame)
counter += 1
utc = now
key = cv2.waitKey(1)
if key == ord("q"):
break
elif key & 0xFF == ord("s"):
shot("left", left_frame)
shot("right", right_frame)
counter += 1
cap.release()
注意尽量拍摄多组照片,这样可以提高标定效果,标定效果的好坏直接影响到测距的精度。对于短焦相机通常拍摄40组照片即可;长焦相机通常会需要更多组照片,笔者在标定长焦相机时拍摄了60组。
照片拍摄好后,进入matlab标定工具箱,如下图所示。注意:不要选择matlab2020b版本,笔者测试过该版本无法正常使用标定工具箱,可以使用matlab2020a版本。
其中Stero Camera Cailbrator为双目标定工具箱,Camera Cailbrator为单目相机标定工具箱,因此这里选择Stero Camera Cailbrator工具箱。进入工具箱以后,选择Add Images。然后选择左右相机照片的路径,Size of checkerboard square为棋盘中每一个方格的长度,单位为毫米,一定要准确测量方格的长度,如下图所示。
点击确定以后,Radial Distortion Compute选择3 Coefficients和Tangential Distortion,然后点击Calibrate进行校准。不过校准前需要剔除一些原点不一致的点,保证所有照片的原点一致。校准过程中可以可以Reprojection Errors曲线,降低误差,如下图所示。
3 导出标定数据
标定好后将标定数据导入到工作空间,点击Export Camera Parameters即可。此时我们已经拿到标定数据了,为了避免手工获取数据时出错,笔者写了一个脚本可以直接获取标定数据,并保存到表格文件中,之后直接复制粘贴即可。
rowName = cell(1,10);
rowName1,1 = '平移矩阵';
rowName1,2 = '旋转矩阵';
rowName1,3 = '相机1内参矩阵';
rowName1,4 = '相机1径向畸变';
rowName1,5 = '相机1切向畸变';
rowName1,6 = '相机2内参矩阵';
rowName1,7 = '相机2径向畸变';
rowName1,8 = '相机2切向畸变';
rowName1,9 = '相机1畸变向量';
rowName1,10 = '相机2畸变向量';
xlswrite('out.xlsx',rowName(1,1),1,'A1');
xlswrite('out.xlsx',rowName(1,2),1,'A2');
xlswrite('out.xlsx',rowName(1,3),1,'A5');
xlswrite('out.xlsx',rowName(1,4),1,'A8');
xlswrite('out.xlsx',rowName(1,5),1,'A9');
xlswrite('out.xlsx',rowName(1,6),1,'A10');
xlswrite('out.xlsx',rowName(1,7),1,'A13');
xlswrite('out.xlsx',rowName(1,8),1,'A14');
xlswrite('out.xlsx',rowName(1,9),1,'A15');
xlswrite('out.xlsx',rowName(1,10),1,'A16');
xlswrite('out.xlsx',stereoParams.TranslationOfCamera2,1,'B1'); % 平移矩阵
xlswrite('out.xlsx',stereoParams.RotationOfCamera2.',1,'B2'); % 旋转矩阵
xlswrite('out.xlsx',stereoParams.CameraParameters1.IntrinsicMatrix.',1,'B5'); % 相机1内参矩阵
xlswrite('out.xlsx',stereoParams.CameraParameters1.RadialDistortion,1,'B8'); % 相机1径向畸变(1,2,5)
xlswrite('out.xlsx',stereoParams.CameraParameters1.TangentialDistortion,1,'B9'); % 相机1切向畸变(3,4)
xlswrite('out.xlsx',stereoParams.CameraParameters2.IntrinsicMatrix.',1,'B10'); % 相机2内参矩阵
xlswrite('out.xlsx',stereoParams.CameraParameters2.RadialDistortion,1,'B13'); % 相机2径向畸变(1,2,5)
xlswrite('out.xlsx',stereoParams.CameraParameters2.TangentialDistortion,1,'B14'); % 相机2切向畸变(3,4)
xlswrite('out.xlsx',[stereoParams.CameraParameters1.RadialDistortion(1:2), stereoParams.CameraParameters1.TangentialDistortion,...
stereoParams.CameraParameters1.RadialDistortion(3)],1,'B15'); % 相机1畸变向量
xlswrite('out.xlsx',[stereoParams.CameraParameters2.RadialDistortion(1:2), stereoParams.CameraParameters2.TangentialDistortion,...
stereoParams.CameraParameters2.RadialDistortion(3)],1,'B16'); % 相机2畸变向量
标定数据文件保存的路径即为当前程序的路径,要想保存到其他路径直接修改脚本中的路径即可,导出的参数如下图所示。
将表格中的数据复制到双目相机配置文件中,其中相机1内参复制到left_camera_matrix中,相机1畸变复制到left_distortion中,相机2内参复制到right_camera_matrix中,相机2畸变复制到right_distortion中,旋转矩阵复制到R中,转移矩阵复制到T中,如下图所示。
至此,双目标定部分就已经完成。注意:标定时照片的尺寸与测距时照片的尺寸一定要保持一致。
OpenCV C++双目摄像头实现双目测距
OpenCV C++双目摄像头实现双目测距
目录
本篇博文是《双目摄像头实现双目测距(Python)》的续作,我们将搭建一个OpenCV C++版本的双目三维重建系统。由于我们只考虑三维重建实现双目测距效果,因而去除了PCL和Open3d库三维显示效果,但依然保留了视差图,深度图等可视化效果,用户可以通过鼠标点击图像,即可获得对应的世界坐标以及深度距离信息。
从效果来看,C++版本的双目测距和Python版本的效果几乎一致,性能更优,速度更快,基本可以达到工业级别测距精度,可在Linux开发板运行,非常适合应用于无人机,智能小车测距避障等场景。
来~先看一下Demo的效果图(鼠标点击,终端会打印对应距离信息):
OpenCV C++双目摄像头实现双目测距主要支持:
- 支持双USB连接线的双目摄像头
- 支持使用WLS滤波器对视差图进行滤波
- 支持双目测距(鼠标点击图像即可获得其深度距离)
- 提供配套的opencv-4.3.0和opencv_contrib-4.3.0源码 (需要自己编译)
- 相比Python版本,C++版本性能更优,速度更快,可在Linux开发板运行,非常适合应用于无人机,智能小车测距避障等场景。
- 简单运行,项目源码只在Ubuntu 18.04系统进行了验证;第三方依赖库只有opencv和opencv_contrib,如果你在Windows系统开发,请在Windows平台配置好opencv和opencv_contrib开发环境;
诚然,网上有很多C++版本双测距的代码,但项目都不是十分完整,而且恢复视差图效果也一般,难以达到商业实际应用,究其原因,主要有下面几个:
- 双目摄像头质量问题,
- 双目标定存在问题,导致校准误差较大
- 没有使用WLS滤波器对视差图进行滤波,该方法可以极大提高视差图的效果
双目测距Demo视频 |
如果你需要Python版本的双目测距, 请查看鄙人另一篇博客《双目三维重建系统(双目标定+立体校正+双目测距+点云显示)Python》
本篇将着重介绍OpenCV C++项目实现双目测距的过程,关于双目相机标定+双目校正+双目匹配等内容,请查看鄙人另一篇博客《双目三维重建系统(双目标定+立体校正+双目测距+点云显示)Python》
【项目源码下载地址】OpenCV C++双目摄像头实现双目测距
【尊重原则,转载请注明出处】https://panjinquan.blog.csdn.net/article/details/127446435
1.目录结构
.
├── configs # 相机参数文件
├── data # 相机采集的数据
├── docs # 一些文档图片
├── src # C++源码
├── build.sh # 构建build脚本
├── main.cpp # 主程序
├── CMakeLists.txt # CMake文件
└── README.md # 说明文档
2.依赖库
- 系统平台:Ubuntu 18.04
- opencv-4.3.0 (opencv-3.4.0以上亦可)
- opencv_contrib-4.3.0 (opencv_contrib-3.4.0以上亦可),WLS滤波器需要用到opencv_contrib库
opencv安装教程,请参考文章:Ubuntu18.04安装opencv和opencv_contrib
PS: 需确保opencv和opencv_contrib的版本号一致,避免版本差异导致编译错误。
项目源码只在Ubuntu 18.04系统进行了验证;第三方依赖库只有opencv和opencv_contrib,如果你在Windows系统开发,请在Windows平台配置好opencv和opencv_contrib开发环境;
3.双目相机标定
(1)双目相机标定-Python版
请参考鄙人另一篇博客,无需Matlab,即可进行相机标定:双目三维重建系统(双目标定+立体校正+双目测距+点云显示)Python
该方法双目标定完成后,会得到一个双目相机内外参数信息(stereo_cam.yml
)文件:
%YAML:1.0
---
size: !!opencv-matrix
rows: 2
cols: 1
dt: d
data: [ 640., 480. ]
K1: !!opencv-matrix
rows: 3
cols: 3
dt: d
data: [ 7.6159209686584518e+02, 0., 3.2031427422505453e+02, 0.,
7.6167321445963728e+02, 2.2467546927337131e+02, 0., 0., 1. ]
D1: !!opencv-matrix
rows: 1
cols: 5
dt: d
data: [ 3.4834574885170888e-02, -5.5261651661983137e-02,
5.7491952731614823e-04, -4.2764224824172658e-05,
1.8477350140315381e-02 ]
K2: !!opencv-matrix
rows: 3
cols: 3
dt: d
data: [ 7.6327773941976670e+02, 0., 2.8768149948082271e+02, 0.,
7.6350419442870850e+02, 2.1897333598636970e+02, 0., 0., 1. ]
D2: !!opencv-matrix
rows: 1
cols: 5
dt: d
data: [ 3.5020972475517692e-02, -4.0770660841280497e-02,
-4.4231087565750534e-04, -1.0552562170995372e-03,
-9.7749906830348537e-02 ]
R: !!opencv-matrix
rows: 3
cols: 3
dt: d
data: [ 9.9999370552351063e-01, 7.8563885326366346e-04,
3.4600122760633780e-03, -7.9503151737356746e-04,
9.9999600079883766e-01, 2.7140949167922721e-03,
-3.4578661403601796e-03, -2.7168286517956050e-03,
9.9999033095517087e-01 ]
T: !!opencv-matrix
rows: 3
cols: 1
dt: d
data: [ -6.0005833133148414e+01, 1.7047017063672587e-01,
6.0300223404957642e-01 ]
E: !!opencv-matrix
rows: 3
cols: 3
dt: d
data: [ -1.1005724987007073e-04, -6.0346296076620343e-01,
1.6883191705475561e-01, 3.9550629985097430e-01,
-1.6255182474732952e-01, 6.0007339329190145e+01,
-1.2276256904913259e-01, -6.0005727085740176e+01,
-1.6345135556766910e-01 ]
F: !!opencv-matrix
rows: 3
cols: 3
dt: d
data: [ -6.7250769136371160e-10, -3.6870834234286016e-06,
1.6143104894409041e-03, 2.4160347372858321e-06,
-9.9287680075344234e-07, 2.7862421257891157e-01,
-1.1014218394645766e-03, -2.7856049650040260e-01, 1. ]
R1: !!opencv-matrix
rows: 3
cols: 3
dt: d
data: [ 9.9997618806974742e-01, -2.0278309638726887e-03,
-6.5963016213173775e-03, 2.0367881225372914e-03,
9.9999701250432615e-01, 1.3514719999064883e-03,
6.5935413581266105e-03, -1.3648750875444691e-03,
9.9997733090723306e-01 ]
R2: !!opencv-matrix
rows: 3
cols: 3
dt: d
data: [ 9.9994547731576255e-01, -2.8407384289991728e-03,
-1.0048512373976153e-02, 2.8270879178959596e-03,
9.9999506202764499e-01, -1.3724045434755307e-03,
1.0052361397026631e-02, 1.3439216883706559e-03,
9.9994857062992937e-01 ]
P1: !!opencv-matrix
rows: 3
cols: 4
dt: d
data: [ 7.3741438842621210e+02, 0., 3.1126281356811523e+02, 0., 0.,
7.3741438842621210e+02, 2.2189782714843750e+02, 0., 0., 0., 1.,
0. ]
P2: !!opencv-matrix
rows: 3
cols: 4
dt: d
data: [ 7.3741438842621210e+02, 0., 3.1126281356811523e+02,
-4.4251577456670653e+04, 0., 7.3741438842621210e+02,
2.2189782714843750e+02, 0., 0., 0., 1., 0. ]
Q: !!opencv-matrix
rows: 4
cols: 4
dt: d
data: [ 1., 0., 0., -3.1126281356811523e+02, 0., 1., 0.,
-2.2189782714843750e+02, 0., 0., 0., 7.3741438842621210e+02, 0.,
0., 1.6664137886344466e-02, 0. ]
参数说明:
- 参数size,对应图像宽高(width,height)
- 参数K1,对应左目相机内参矩阵(3×3)
- 参数D1,对应左目相机畸变系数矩阵(5×1)
- 参数K2,对应右目相机内参矩阵(3×3)
- 参数D2,对应右目相机畸变系数矩阵(5×1)
- 参数T,对应双目相机平移向量T(3×1)
- 参数R,对应双目相机旋转矩阵R(3×3)
- 至于配置文件中的参数,如R1, R2, P1, P2, Q这些重投影矩阵,可默写即可,不用修改,这些在运行时,会重新计算。
(2)双目相机标定-Matlab版
网上已经存在很多Matlab双目相机标定的教程,请自行百度哈 ;使用Matlab工具箱进行双目相机标定后,请对应参数进行修改
需要注意的是:旋转矩阵R是(3×3)二维矩阵,而Matlab给出的是旋转向量om(1×3),请使用cv2.Rodrigues()将旋转向量转为旋转矩阵,参考下面的代码进行转换
import cv2
import numpy as np
# 定义旋转矩阵R,旋转向量om
R = [[9.9999370551606337e-01, 7.8563882630048958e-04, 3.4600144345510440e-03],
[-7.9503149273969136e-04, 9.9999600080163187e-01, 2.7140938945082542e-03],
[-3.4578682997252063e-03, -2.7168276311286426e-03, 9.9999033095047696e-01]]
R = np.asarray(R)
print(f"旋转矩阵R:\\n R")
# 把旋转矩阵R转化为旋转向量om
om, _ = cv2.Rodrigues(R)
print(f"旋转向量om:\\n om")
# 把旋转向量om转换为旋转矩阵R
R1, _ = cv2.Rodrigues(om)
print(f"旋转矩阵R1:\\n R1")
4.相机参数配置
- 双目相机标定完成后,得到了相机内外参数信息
- 根据自己相机参数定义C++的
CameraParam
即可 - 下面C++代码中,定义了双目相机CameraParam变量camera1,用户需要根据自己的双目相机,修改对应的相机内外参数。
/**
* 双目摄像头的相机参数
*/
struct CameraParam
int width; //图像的宽度width
int height; //图像的高度height
Mat cameraMatrixL; //左相机内参K1(3×3)
Mat distCoeffL; //左相机畸变系数D1(5×1)
Mat cameraMatrixR; //右相机内参K2(3×3)
Mat distCoeffR; //右相机畸变系数D2(5×1)
Mat T; //平移向量T(3×1)
Mat R; //旋转矩阵R(3×3),如果是(3×1)旋转向量,请使用cv::Rodrigues()进行变换转为(3×3)旋转矩阵R
;
/***
* 设置摄像头参数,需要根据双目摄像头标定结果进行填写
*/
static CameraParam camera1 =
640,//width
480,//height
(Mat_<double>(3, 3)
<< 7.6159209686633153e+02, 0., 3.2031427422691633e+02, 0., 7.6167321446015626e+02, 2.2467546926913309e+02, 0., 0., 1.),//cameraMatrixL
(Mat_<double>(5, 1)
<< 3.4834574887256914e-02, -5.5261651680159028e-02, 5.7491952534806736e-04, -4.2764223950233445e-05, 1.8477350164208820e-02),//distCoeffL
(Mat_<double>(3, 3)
<< 7.6327773983796783e+02, 0., 2.8768149776326379e+02, 0., 7.6350419482215057e+02, 2.1897333669573928e+02, 0., 0., 1.),
(Mat_<double>(5, 1)
<< 3.5020967512300320e-02, -4.0770565902033332e-02, -4.4231049297594003e-04, -1.0552565496142535e-03, -9.7750314807571667e-02),
(Mat_<double>(3, 1)
<< -6.0005833075452117e+01, 1.7047023105446815e-01, 6.0300273851103448e-01),
(Mat_<double>(3, 3)
<< 9.9999370551606337e-01, 7.8563882630048958e-04, 3.4600144345510440e-03, -7.9503149273969136e-04, 9.9999600080163187e-01, 2.7140938945082542e-03, -3.4578682997252063e-03, -2.7168276311286426e-03, 9.9999033095047696e-01),
;
5. 双目测距
OpenCV C++版本的双目测距与Python版本双目测距的效果几乎一致,且性能更优,速度更快,基本可以达到工业级别测距精度。由于我们只考虑三维重建实现双目测距效果,因而去除了PCL和Open3d库三维显示效果,但依然保留了视差图,深度图等可视化效果,用户可以通过鼠标点击图像,即可获得对应的世界坐标以及深度距离信息。
函数接口声明,都已经给出了详细的参数说明,为了方便大家学习,函数命名和实现逻辑与Python版本的几乎一致:
Python版本 | C++版本 |
//
// Created by pan_jinquan@163.com on 2022/10/6.
//
#ifndef CAMERA_CALIBRATION_RECONSTRUCT_CPP_STEREO_RECONSTRUCT_H
#define CAMERA_CALIBRATION_RECONSTRUCT_CPP_STEREO_RECONSTRUCT_H
#include <opencv2/opencv.hpp>
#include <iostream>
using namespace std;
using namespace cv;
cv::Mat xyz_coord; //用于存放每个像素点距离相机镜头的三维坐标
cv::Point start; //鼠标按下的起始点
cv::Rect buttonRect; //定义矩形选框
bool buttonStatus = false; //是否选择对象
/**
* 双目摄像头的相机参数
*/
struct CameraParam
int width; //图像的宽度width
int height; //图像的高度height
Mat cameraMatrixL; //左相机内参K1(3×3)
Mat distCoeffL; //左相机畸变系数D1(5×1)
Mat cameraMatrixR; //右相机内参K2(3×3)
Mat distCoeffR; //右相机畸变系数D2(5×1)
Mat T; //平移向量T(3×1)
Mat R; //旋转矩阵R(3×3),如果是(3×1)旋转向量,请使用cv::Rodrigues()进行变换转为(3×3)旋转矩阵R
;
/***
* 设置摄像头参数,需要根据双目摄像头标定结果进行填写
*/
static CameraParam camera1 = 640,//width
480,//height
(Mat_<double>(3, 3)
<< 7.6159209686633153e+02, 0., 3.2031427422691633e+02, 0., 7.6167321446015626e+02, 2.2467546926913309e+02, 0., 0., 1.),//cameraMatrixL
(Mat_<double>(5, 1)
<< 3.4834574887256914e-02, -5.5261651680159028e-02, 5.7491952534806736e-04, -4.2764223950233445e-05, 1.8477350164208820e-02),//distCoeffL
(Mat_<double>(3, 3)
<< 7.6327773983796783e+02, 0., 2.8768149776326379e+02, 0., 7.6350419482215057e+02, 2.1897333669573928e+02, 0., 0., 1.),
(Mat_<double>(5, 1)
<< 3.5020967512300320e-02, -4.0770565902033332e-02, -4.4231049297594003e-04, -1.0552565496142535e-03, -9.7750314807571667e-02),
(Mat_<double>(3, 1)
<< -6.0005833075452117e+01, 1.7047023105446815e-01, 6.0300273851103448e-01),
(Mat_<double>(3, 3)
<< 9.9999370551606337e-01, 7.8563882630048958e-04, 3.4600144345510440e-03, -7.9503149273969136e-04, 9.9999600080163187e-01, 2.7140938945082542e-03, -3.4578682997252063e-03, -2.7168276311286426e-03, 9.9999033095047696e-01),
;
/***
* 鼠标响应回调函数
* @param event
* @param x
* @param y
*/
static void onMouse(int event, int x, int y, int, void *)
if (buttonStatus)
buttonRect.x = MIN(x, start.x);
buttonRect.y = MIN(y, start.y);
buttonRect.width = std::abs(x - start.x);
buttonRect.height = std::abs(y - start.y);
switch (event)
case EVENT_LBUTTONDOWN: //鼠标左按钮按下的事件
start = Point(x, y);
buttonRect = Rect(x, y, 0, 0);
buttonStatus = true;
cout << "image(x,y)=" << start;
cout << " world coords=(x,y,depth)=" << xyz_coord.at<Vec3f>(start) << endl;
break;
case EVENT_LBUTTONUP: //鼠标左按钮释放的事件
buttonStatus = false;
if (buttonRect.width > 0 && buttonRect.height > 0)
break;
/***
* 显示图像
* @param winname 窗口名称
* @param image 图像
* @param delay 显示延迟,0表示阻塞显示
* @param flags 显示方式
*/
static void show_image(const string &winname, cv::Mat &image, int delay = 0, int flags = cv::WINDOW_AUTOSIZE)
cv::namedWindow(winname, flags);
cv::imshow(winname, image);
cv::waitKey(delay);
/***
* 读取视频文件
* @param video_file 视频文件
* @param cap 视频流对象
* @param width 设置图像的宽度
* @param height 设置图像的高度
* @param fps 设置视频播放频率
* @return
*/
bool get_video_capture(string video_file, cv::VideoCapture &cap, int width = -1, int height = -1, int fps = -1)
//VideoCapture video_cap;
cap.open(video_file);
if (width > 0 && height > 0)
cap.set(cv::CAP_PROP_FRAME_WIDTH, width); //设置图像的宽度
cap.set(cv::CAP_PROP_FRAME_HEIGHT, height); //设置图像的高度
if (fps > 0)
cap.set(cv::CAP_PROP_FPS, fps);
if (!cap.isOpened())//判断是否读取成功
return false;
return true;
/***
* 读取摄像头
* @param camera_id 摄像头ID号,默认从0开始
* @param cap 视频流对象
* @param width 设置图像的宽度
* @param height 设置图像的高度
* @param fps 设置视频播放频率
* @return
*/
bool get_video_capture(int camera_id, cv::VideoCapture &cap, int width = -1, int height = -1, int fps = -1)
//VideoCapture video_cap;
cap.open(camera_id); //摄像头ID号,默认从0开始
if (width > 0 && height > 0)
cap.set(cv::CAP_PROP_FRAME_WIDTH, width); //设置捕获图像的宽度
cap.set(cv::CAP_PROP_FRAME_HEIGHT, height); //设置捕获图像的高度
if (fps > 0)
cap.set(cv::CAP_PROP_FPS, fps);
if (!cap.isOpened()) //判断是否成功打开相机
return false;
return true;
class StereoReconstruct
public:
/***
* 构造函数,初始化StereoReconstruct
* @param camera 双目相机参数
* @param use_wls 是否使用WLS滤波器对视差图进行滤波
*/
StereoReconstruct(CameraParam camera, bool use_wls = true);
/***
* release
*/
~StereoReconstruct();
/***
* 开始双目测距任务
* @param frameL
* @param frameR
*/
void task(Mat frameL, Mat frameR, int delay = 0);
/***
* 畸变校正和立体校正
* @param imgL 左视图
* @param imgR 右视图
* @param rectifiedL 校正后左视图
* @param rectifiedR 校正后右视图
*/
void get_rectify_image(Mat &imgL, Mat &imgR, Mat &rectifiedL, Mat &rectifiedR);
/***
* 获得视差图
* @param imgL 畸变校正和立体校正后的左视图
* @param imgR 畸变校正和立体校正后的右视图
* @param dispL 返回视差图
* @param use_wls 是否使用WLS滤波器对视差图进行滤波
*/
void get_disparity(Mat &imgL, Mat &imgR, Mat &dispL, bool use_wls = true);//SGBM匹配算法
/***
* 计算像素点的3D坐标(左相机坐标系下)
* @param disp 视差图
* @param points_3d 返回三维坐标points_3d,三个通道分布表示(X,Y,Z),其中Z是深度图depth, 即距离,单位是毫米(mm)
* @param scale 单位变换尺度,默认scale=1.0,单位为毫米
*/
void get_3dpoints(Mat &disp, Mat &points_3d, float scale = 1.0);
/***
* 将输入深度图转换为伪彩色图,方面可视化
* @param depth
* @param colormap
*/
void get_visual_depth(cv::Mat &depth, cv::Mat &colormap, float clip_max = 6000.0);
/***
* 显示矫正效果
* @param rectifiedL
* @param rectifiedR
*/
void show_rectify_result(cv::Mat rectifiedL, cv::Mat rectifiedR);
/***
* 可视化视差图和深度图
* @param frameL
* @param frameR
* @param points_3d
* @param disp
* @param delay
*/
void show_2dimage(Mat &frameL, Mat &frameR, Mat &points_3d, Mat &disp, int delay);
/***
* 显示Mat的最大最小值
* @param src
* @param vmin 最小值下限
* @param vmax 最大值下限
*/
void clip(cv::Mat &src, float vmin, float vmax);
/***
* 显示Mat的最大最小值
* @param src
* @param th
* @param vmin
*/
void clip_min(cv::Mat &src, float th, float vmin);
public:
string depth_windows = "depth-color"; // 深度图的窗口名称
int use_wls; // 是否使用WLS滤波器对视差图进行滤波
Size image_size; // 图像宽高(width,height)
Rect validROIL; // 图像校正之后,会对图像进行裁剪,这里的左视图裁剪之后的区域
Rect validROIR; // 图像校正之后,会对图像进行裁剪,这里的右视图裁剪之后的区域
Mat mapLx, mapLy, mapRx, mapRy; // 映射表
Mat Rl, Rr, Pl, Pr, Q; // 校正后的旋转矩阵R,投影矩阵P, 重投影矩阵Q
cv::Ptr<cv::StereoSGBM> sgbm;
;
#endif //CAMERA_CALIBRATION_RECONSTRUCT_CPP_STEREO_RECONSTRUCT_H
6. 运行Demo
- 主程序main.cpp实现了三个Demo
- 测试demo视频文件: 这是使用摄像头录制的双目视频文件,用于测试效果双目测距的效果
- 测试双目摄像头(双USB连接线的双目摄像头):用于测试双目摄像头,需要根据自己的摄像头修改ID号
- 测试一对左右相机图像效果
//
// 双目测距Demo
// Created by pan_jinquan@163.com on 2022/10/6.
//
#include <opencv2/opencv.hpp>
#include <iostream>
#include "stereo_reconstruct.h"
/***
* 测试demo视频文件
* @return
*/
int test_video_file()
CameraParam camera = camera1;//双目相机参数
bool use_wls = true; //是否使用WLS滤波器对视差图进行滤波
StereoReconstruct *detector = new StereoReconstruct(camera, use_wls);
int imageWidth = camera1.width; //单目图像的宽度
int imageHeight = camera1.height; //单目图像的高度
string left_video = "../data/lenacv-video/left_video.avi";
string right_video = "../data/lenacv-video/right_video.avi";
VideoCapture capL, capR;
bool retL = get_video_capture(left_video, capL, imageWidth, imageHeight);
bool retR = get_video_capture(right_video, capR, imageWidth, imageHeight);
Mat frameL, frameR;
while (retL && retR)
capL >> frameL;
capR >> frameR;
if (frameL.empty() or frameR.empty()) break;
detector->task(frameL, frameR, 20);
capL.release(); //释放对相机的控制
capR.release(); //释放对相机的控制
delete detector;
return 0;
/***
* 测试双目摄像头(双USB连接线的双目摄像头)
* @return
*/
int test_camera()
CameraParam camera = camera1;//双目相机参数
bool use_wls = true; //是否使用WLS滤波器对视差图进行滤波
StereoReconstruct *detector = new StereoReconstruct(camera, use_wls);
int imageWidth = camera1.width; //单目图像的宽度
int imageHeight = camera1.height; //单目图像的高度
int camera1 = 0; //左摄像头ID号(请修改成自己左摄像头ID号)
int camera2 = 1; //右摄像头ID号(请修改成自己右摄像头ID号)
VideoCapture capL, capR;
bool retL = get_video_capture(camera1, capL, imageWidth, imageHeight);
bool retR = get_video_capture(camera2, capR, imageWidth, imageHeight);
Mat frameL, frameR;
while (retL && retR)
capL >> frameL;
capR >> frameR;
if (frameL.empty() or frameR.empty()) break;
detector->task(frameL, frameR, 20);
capL.release(); //释放对相机的控制
capR.release(); //释放对相机的控制
delete detector;
return 0;
/***
* 测试一对左右图像
* @return
*/
int test_pair_image_file()
CameraParam camera = camera1;//双目相机参数
bool use_wls = true; //是否使用WLS滤波器对视差图进行滤波
StereoReconstruct *detector = new StereoReconstruct(camera, use_wls);
Mat frameL = imread("../data/left.png", IMREAD_COLOR);
Mat frameR = imread("../data/right.png", IMREAD_COLOR);
detector->task(frameL, frameR, 0);
delete detector;
return 0;
int main()
//测试一对左右图像
test_pair_image_file();
//测试demo视频文件
test_video_file();
//测试双目摄像头(双USB连接线的双目摄像头)
test_camera();
return 0;
- 终端运行脚本:
bash build.sh
#!/usr/bin/env bash
if [ ! -d "build/" ];then
mkdir "build"
else
echo "exist build"
fi
cd build
cmake ..
make -j4
sleep 1
./Demo
7. 效果图
C++版本的双目测距与Python版本的效果几乎一致。从重建效果来看,未使用WLS滤波,其视差图出现了很多空洞,存在很多误匹配点;但使用WLS滤波后,视差图变得比较平滑,整体效果都有明显改善。
左视图 | 右视图 |
---|---|
视差图(未滤波) | 深度图(未滤波) |
视差图(滤波后) | 深度图(滤波后) |
- 运行主程序后,鼠标点击
depth-color
窗口的图像任意区域,终端会打印对应距离信息
8. 源码下载
OpenCV C++版本双目测距项目代码包含:OpenCV C++双目摄像头实现双目测距
【项目源码下载地址】OpenCV C++双目摄像头实现双目测距
- 支持双USB连接线的双目摄像头
- 支持使用WLS滤波器对视差图进行滤波
- 支持双目测距(鼠标点击图像即可获得其深度距离)
- 提供配套的opencv-4.3.0和opencv_contrib-4.3.0源码 (需要自己编译)
- 相比Python版本,C++版本性能更优,速度更快,可在Linux开发板运行,非常适合应用于无人机,智能小车测距避障等场景。
- 简单运行,项目源码只在Ubuntu 18.04系统进行了验证;第三方依赖库只有opencv和opencv_contrib,如果你在Windows系统开发,请在Windows平台配置好opencv和opencv_contrib开发环境;
如果你需要Python版本的双目测距, 请查看鄙人另一篇博客《双目三维重建系统(双目标定+立体校正+双目测距+点云显示)Python》
双目测距Demo视频 |
9.参考资料
- 双目三维重建系统(双目标定+立体校正+双目测距+点云显示)Python
- 双目摄像头实现双目测距(Python)
- 结构光三维重建-3D Scanning Software实现三维重建
- Ubuntu18.04安装opencv和opencv_contrib
以上是关于Matlab双目相机标定的主要内容,如果未能解决你的问题,请参考以下文章