OpenCV C++双目三维重建:双目摄像头实现双目测距

这篇具有很好参考价值的文章主要介绍了OpenCV C++双目三维重建:双目摄像头实现双目测距。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

OpenCV C++双目三维重建:双目摄像头实现双目测距

目录

OpenCV C++双目三维重建:双目摄像头实现双目测距

1.目录结构

2.依赖库

 (1) Ubuntu 18.04配置开发环境

 (2) Windows配置开发环境

3.双目相机标定

 (1)双目相机标定-Python版

 (2)双目相机标定-Matlab版

4.相机参数配置

5. 双目测距

6. 运行Demo

7.双目测距的误差说明

8. 双目三维重建项目代码(C/C++版本)

(1)效果图

(2)源码下载

9. 双目三维重建项目代码(Python版本)

10. 双目三维重建项目代码(Android版本)

11.参考资料


本篇博文是《双目三维重建系统(双目标定+立体校正+双目测距+点云显示)Python​​​​​​》的续作,我们将搭建一个OpenCV C++版本的双目三维重建系统。由于我们只考虑三维重建实现双目测距效果,因而去除了PCL和Open3d库三维显示效果,但依然保留了视差图,深度图等可视化效果,用户可以通过鼠标点击图像,即可获得对应的世界坐标以及深度距离信息。

从效果来看,C++版本的双目测距和Python版本的效果几乎一致,性能更优,速度更快,基本可以达到工业级别测距精度,可在Linux开发板运行,非常适合应用于无人机,智能小车测距避障等场景。​

来~先看一下Demo的效果图(鼠标点击,终端会打印对应距离信息): 

ubuntu下opencv双目摄像头采集视频实现三维重建,三维重建,C++双目测距,opencv三维重建,c++三维重建,opencv双目测距,opencv视差图

OpenCV C++双目摄像头实现双目测距主要支持:

  • 支持双USB连接线的双目摄像头
  • 支持使用WLS滤波器对视差图进行滤波
  • 支持双目测距(鼠标点击图像即可获得其深度距离)
  • 提供配套的opencv-4.3.0和opencv_contrib-4.3.0源码 (Linux系统需要自行编译;Windows10系统已提供opencv_contrib编译文件,可直接复用,无需重新编译
  • 相比Python版本,C++版本性能更优,速度更快,可在Linux开发板运行,非常适合应用于无人机,智能小车测距避障等场景。
  • 支持Linux系统:项目源码已在Ubuntu 18.04系统验证通过(需要自行编译opencv-4.3.0和opencv_contrib-4.3.0
  • 支持Windows10系统:项目源码已在Windows10系统验证通过,配套了Visual Studio 2017项目,可直接使用
  • 其他系统平台开发,请在配置好opencv和opencv_contrib开发环境

诚然,网上有很多C++版本双测距的代码,但项目都不是十分完整,而且恢复视差图效果也一般,难以达到商业实际应用,究其原因,主要有下面几个:

  • 双目摄像头质量问题,
  • 双目标定存在问题,导致校准误差较大
  • 没有使用WLS滤波器对视差图进行滤波,该方法可以极大提高视差图的效果

双目测距Demo视频

如果你需要Python版本的双目测距, 请查看鄙人另一篇博客《双目三维重建:双目摄像头实现双目测距(Python》

本篇将着重介绍OpenCV C++项目实现双目测距的过程,关于双目相机标定+双目校正+双目匹配等内容,请查看鄙人另一篇博客 《双目三维重建系统(双目标定+立体校正+双目测距+点云显示)Python​​​​​​》

【项目源码下载地址】OpenCV C++双目摄像头实现双目测距

【尊重原则,转载请注明出处】https://blog.csdn.net/guyuealian/article/details/127446435


1.目录结构

.
├── configs          # 相机参数文件
├── data             # 相机采集的数据
├── docs             # 一些文档图片
├── src              # C++源码
├── build.sh         # 构建build脚本
├── main.cpp         # 主程序
├── CMakeLists.txt   # CMake文件
└── README.md        # 说明文档

2.依赖库

 (1) Ubuntu 18.04配置开发环境

  • 系统平台:Ubuntu 18.04
  • opencv-4.3.0 (opencv-3.4.0以上亦可)
  • opencv_contrib-4.3.0 (opencv_contrib-3.4.0以上亦可),WLS滤波器需要用到opencv_contrib库

Ubuntu 18.04 opencv安装教程,请参考文章:Ubuntu18.04安装opencv和opencv_contrib

PS: 需确保opencv和opencv_contrib的版本号一致,避免版本差异导致编译错误。

 (2) Windows配置开发环境

  • 系统平台:Windows10
  • opencv-4.3.0 (opencv-3.4.0以上亦可)
  • opencv_contrib-4.3.0 (opencv_contrib-3.4.0以上亦可),WLS滤波器需要用到opencv_contrib库

Windows opencv安装教程,请参考文章:Visual Studio 2017环境cmake编译opencv 4.3.0+opencv_contrib 4.3.0

特别说明:

  1. 鄙人是在Ubuntu 18.04平台使用CLion工具进行开发(默认编码格式utf-8),其他平台可能会出现编码格式异常的问题;特别注意,如果你在Windows系统使用Visual Studio 进行开发(默认编码格式GBK),会出现如【本地函数定义是非法的】语法异常错误等问题;解决方法也很简单,只需要在属性页命令行中,添加/utf-8即可
  2. 项目配套VS工程,可直接在Visual Studio 2017使用,其他VS版本如VS2019,VS2021需要自己配置好OpenCV路径,才能正常运行
  3. 项目提供opencv 4.3.0+opencv_contrib 4.3.0,其他版本请自行编译

ubuntu下opencv双目摄像头采集视频实现三维重建,三维重建,C++双目测距,opencv三维重建,c++三维重建,opencv双目测距,opencv视差图

项目源码已经在Ubuntu 18.04和Windows10系统进行了验证;第三方依赖库只有opencv和opencv_contrib,如果你在其他系统平台开发,请自行配置好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++版本
ubuntu下opencv双目摄像头采集视频实现三维重建,三维重建,C++双目测距,opencv三维重建,c++三维重建,opencv双目测距,opencv视差图 ubuntu下opencv双目摄像头采集视频实现三维重建,三维重建,C++双目测距,opencv三维重建,c++三维重建,opencv双目测距,opencv视差图
//
// Created by 390737991@qq.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
  1. 测试demo视频文件: 这是使用摄像头录制的双目视频文件,用于测试效果双目测距的效果
  2. 测试双目摄像头(双USB连接线的双目摄像头):用于测试双目摄像头,需要根据自己的摄像头修改ID号
  3. 测试一对左右相机图像效果
//
// 双目测距Demo
// Created by AI吃大瓜 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.双目测距的误差说明

 双目测距的误差和精度说明:

  • 有网友反馈,测量精度较差,在评估测量精度前,请严格按照博文进行相机标定,标定误差不能超过0.1,否则测距误差较大
  • 理论上双目的测量精度可以达到毫米(mm)级别,但并非无条件的,根据上式可以看出,某点像素的深度精度取决于该点处估计的视差d的精度。假设视差d的误差恒定,当测量距离越远,得到的深度精度则越差,因此使用双目相机不适宜测量太远的目标。
  • 如果想要对与较远的目标能够得到较为可靠的深度,一方面需要提高相机的基线距离,但是基线距离越大,左右视图的重叠区域就会变小,内容差异变大,从而提高立体匹配的难度,另一方面可以选择更大焦距的相机,然而焦距越大,相机的视域则越小,导致离相机较近的物体的距离难以估计。
  • 理论上,深度方向的测量误差与测量距离的平方成正比,而X/Y方向的误差与距离成正比;而距离很近时,由于存在死角,会导致难以匹配的问题;想象一下,如果你眼前放置一块物体,那你左眼只能看到物体左侧表面,右眼同理只能看到物体右侧表面,这时由于配准失败,导致视差计算失败;这个问题在基线越长,问题就越严重
  • 下图给出双目测距误差和测量距离的关系,一般有效的测量距离是0.6米到6米之间ubuntu下opencv双目摄像头采集视频实现三维重建,三维重建,C++双目测距,opencv三维重建,c++三维重建,opencv双目测距,opencv视差图

8. 双目三维重建项目代码(C/C++版本)

(1)效果图

C++版本的双目测距与Python版本的效果几乎一致。从重建效果来看,未使用WLS滤波,其视差图出现了很多空洞,存在很多误匹配点;但使用WLS滤波后,视差图变得比较平滑,整体效果都有明显改善。

左视图 右视图
ubuntu下opencv双目摄像头采集视频实现三维重建,三维重建,C++双目测距,opencv三维重建,c++三维重建,opencv双目测距,opencv视差图 ubuntu下opencv双目摄像头采集视频实现三维重建,三维重建,C++双目测距,opencv三维重建,c++三维重建,opencv双目测距,opencv视差图
视差图(未滤波) 深度图(未滤波)
ubuntu下opencv双目摄像头采集视频实现三维重建,三维重建,C++双目测距,opencv三维重建,c++三维重建,opencv双目测距,opencv视差图 ubuntu下opencv双目摄像头采集视频实现三维重建,三维重建,C++双目测距,opencv三维重建,c++三维重建,opencv双目测距,opencv视差图
视差图(滤波后) 深度图(滤波后)
ubuntu下opencv双目摄像头采集视频实现三维重建,三维重建,C++双目测距,opencv三维重建,c++三维重建,opencv双目测距,opencv视差图 ubuntu下opencv双目摄像头采集视频实现三维重建,三维重建,C++双目测距,opencv三维重建,c++三维重建,opencv双目测距,opencv视差图
  •  运行主程序后,鼠标点击depth-color窗口的图像任意区域,终端会打印对应距离信息

ubuntu下opencv双目摄像头采集视频实现三维重建,三维重建,C++双目测距,opencv三维重建,c++三维重建,opencv双目测距,opencv视差图


(2)源码下载

OpenCV C++版本双目测距项目代码包含:OpenCV C++双目摄像头实现双目测距

ubuntu下opencv双目摄像头采集视频实现三维重建,三维重建,C++双目测距,opencv三维重建,c++三维重建,opencv双目测距,opencv视差图

 【项目源码下载地址】OpenCV C++双目摄像头实现双目测距

  • 支持双USB连接线的双目摄像头
  • 支持使用WLS滤波器对视差图进行滤波
  • 支持双目测距(鼠标点击图像即可获得其深度距离)
  • 提供配套的opencv-4.3.0和opencv_contrib-4.3.0源码 (Linux系统需要自行编译;Windows10系统已提供opencv_contrib编译文件,可直接复用,无需重新编译
  • 相比Python版本,C++版本性能更优,速度更快,可在Linux开发板运行,非常适合应用于无人机,智能小车测距避障等场景。
  • 支持Linux系统:项目源码已在Ubuntu 18.04系统验证通过(需要自行编译opencv-4.3.0和opencv_contrib-4.3.0
  • 支持Windows10系统:项目源码已在Windows10系统验证通过,配套了Visual Studio 2017项目,可直接使用;其他VS版本,如VS2019,VS2021需要自己配置好OpenCV路径,才能正常运行
  • 其他系统平台开发,请在配置好opencv和opencv_contrib开发环境

9. 双目三维重建项目代码(Python版本)

如果你需要Python版本的双目测距, 请查看鄙人另一篇博客《双目三维重建系统(双目标定+立体校正+双目测距+点云显示)Python》

双目测距Demo视频


10. 双目三维重建项目代码(Android版本)

如果你需要Android版本的双目测距, 请查看鄙人另一篇博客《Android OpenCV实现双目三维重建:双目摄像头实现双目测距》

ubuntu下opencv双目摄像头采集视频实现三维重建,三维重建,C++双目测距,opencv三维重建,c++三维重建,opencv双目测距,opencv视差图       ubuntu下opencv双目摄像头采集视频实现三维重建,三维重建,C++双目测距,opencv三维重建,c++三维重建,opencv双目测距,opencv视差图文章来源地址https://www.toymoban.com/news/detail-788346.html


11.参考资料

  1. OpenCV C++双目三维重建:双目摄像头实现双目测距
  2. 双目三维重建:双目摄像头实现双目测距(Python)
  3. 双目三维重建系统(双目标定+立体校正+双目测距+点云显示)Python
  4. Ubuntu18.04安装opencv和opencv_contrib

到了这里,关于OpenCV C++双目三维重建:双目摄像头实现双目测距的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 双目摄像头Matlab参数定标

    1、安装好python3,可以在anaconda中安装python3。 2、一个合适的双目摄像头。 3、一台可以运行Matlab的电脑。 4、一张棋盘图(可A4打印,若效果不佳,则可A3打印)。 棋盘图如下图所示:需要测量小方框的边长(一般单位为毫米:mm)。 注意: 1、左、右摄像头图像中必须包含单

    2023年04月08日
    浏览(32)
  • 树莓派连接双目摄像头教程(详)

    前言:此教程为菜鸟准备好了代码,仅仅是初试水。 软件准备:Arduino IDE软件 硬件准备:树莓派 Arduin单片机 双目摄像头 树莓派桌面:1准备环境 2 Arduino IDE 3打开摄像头  1.软件准备 下载Arduino IDE软件 Windows用户可在浏览器输入网址:https://www.arduino.cc/en/Main/Software 下载安装都是

    2023年04月09日
    浏览(87)
  • 全网最简单实用Android摄像头开发,同时预览多个摄像头,双目摄像头,同时打开手机前后摄像头(红外摄像头、人脸识别、活体检测、Android Camera、缩放、焦距、旋转、镜像、截图保存)

    如果你受够了网上那些乱七八糟的代码,你可以了解下我这个,能同时打开多个摄像头,在界面上预览,并且可以取得摄像头数据,byte[] 转为 Bitmap,保存为 jpg图片。 最近我们的某个项目要加上Android人脸识别,虽然有别人写好的“考勤”、“门口闸机”这些,但不能直接用

    2024年02月08日
    浏览(70)
  • Opencv(C++)系列学习---读取视频文件和打开摄像头

    今天学习的这两个内容比较简单,话不多说,直接上代码! 目录 【1】读取视频文件 【2】摄像头读取视频 运行结果:  这段代码较为简单,有兴趣的同学也可以挑战一下,在视频读取上加个进度条,可以通过鼠标拖动进度条到指定位置,像平时我们用的视频播放器一样,主

    2024年02月16日
    浏览(51)
  • OpenCV4(C++)—— 视频和摄像头的加载、显示与保存

      视频或摄像头的加载是使用 cv::VideoCapture 类。(这个类和 ifstream 类比较相似,视频或摄像头的加载和文本文件操作是大致相同。主要步骤:(1)加载(打开)视频或视像头。(2) 判断加载是否成功。 (3)读取内容。(4)关闭。) 注意:   (1)VideoCapture类变量同时

    2024年02月06日
    浏览(37)
  • python实现opencv调用摄像头

    Python的opencv库提供了一种简单而高效的方法来处理图像,包括从摄像头中读取视频流。在本篇技术博客中,我们将介绍如何使用Python的opencv库调用IP摄像头和本地摄像头的代码。 首先,我们需要安装Python的opencv库,可以使用pip install opencv-python命令进行安装。 在使用IP摄像头之

    2024年02月11日
    浏览(46)
  • OPENCV C++(三)二值化灰度函数+调用摄像头+鼠标响应+肤色检测

    图像 目标图像 rgb转灰度 灰度图,目标图,阈值,大于阈值的转换的像素值,方法为大津法 灰度图,目标图,大于阈值的转换的像素值,自适应方法,二值化方法 虽然人物的信息丢失了很多,但是背景基本上被去掉了。丢失的人物的信息可以通过位运算等恢复。在去除背景

    2024年02月14日
    浏览(52)
  • VS2017环境下使用C++和OpenCV获取摄像头参数的代码实战

    本文将介绍如何在VS2017环境下使用C++和OpenCV查看摄像头参数。通过使用使用OpenCV库的相关函数,获取摄像头的帧率、宽度、高度、亮度、对比度和饱和度等参数。   首先,我们需要在代码中导入OpenCV库,以便使用OpenCV的相关函数。在C++中,可以使用以下代码导入OpenCV库: 接

    2024年02月07日
    浏览(53)
  • OpenCV实现摄像头图像分类(Python版)

    先安装MMEdu库! MMEdu安装:https://blog.csdn.net/zyl_coder/article/details/132483865 下面的代码请在Jupyter上运行,并自己准备数据集。若模型还未训练,请先在本地训练完模型后再进行模型推理。  注意:1.因为我们是面对摄像头的,所以通过flip对摄像头获得的内容做了左右镜像;    

    2024年02月11日
    浏览(30)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包