双目测距--5 双目相机 联合 YOLOv8

news2024/12/25 0:31:03

目录

效果:

1、立体矫正不改变图像尺寸

 2、视差图尺寸与原图尺寸一致

3、视差图、深度信息图

 4、几个重要的函数

createTracker()

5、代码

main.cpp

utils.cpp


效果:

1、立体矫正不改变图像尺寸

左右相机图像立体矫正后,图像尺寸为变化,但是图像像素的位置会发生变化,如双目标定输出信息图:

 2、视差图尺寸与原图尺寸一致

运用SGBM算法得到的视差图,其尺寸与立体矫正后的图像尺寸不一致,如获取深度图代码信息输出图

 基于以上两个结果,可以用立体矫正后的图像,作为YOLOv8的输入图像。用 目标的中心点坐标即可去深度图索取深度信息。

3、视差图、深度信息图

视差图由SGBM算法获得,深度信息图由reproJectImageTo3D()函数获得

 4、几个重要的函数

createTracker()

函数 原型:

int createTrackbar(const String& trackbarname, const String& winname,
                              int* value, int count,
                              TrackbarCallback onChange = 0,
                              void* userdata = 0);

参数1:轨迹条名字

参数2:窗口名字

参数3:滑块初始位置

参数4:表示滑块达到最大位置的值

参数5:默认值为0,指向回调函数

参数6:默认值为0,用户传给回调函数的数据值

 这里用这个函数来调节SFBM算法的参数:

 

5、代码

main.cpp

#include <iostream>
#include <opencv2/opencv.hpp>
#include "detect.h"
#include <sys/time.h>

#include <vector>

using namespace std;
using namespace cv;
using namespace cv::dnn;

// 双目摄像机内参
Mat cameraMatrix_L,cameraMatrix_R;
Mat discoeff_L, discoeff_R;

// 双目摄像机外参
Mat R, T;


// 立体校正用
Mat Rl, Rr, Pl, Pr, Q;
Mat mapLx, mapLy, mapRx, mapRy;
Rect validROIL, validROIR;

// 图像
Mat rgbImageL, grayImageL;
Mat rgbImageR, grayImageR;
Mat grayRectifyL, grayRectifyR;
Mat rgbRectifyL,rgbRectifyR;


// 图像尺寸
const int imageWidth = 1280/2;
const int imageheight = 480;
Size imageSize = Size(imageWidth, imageheight);



Mat xyz;     //三维坐标(包含深度信息)
Mat disp;   // 视差图
Mat disp8;  //CV_8UC3视差图

Point origin;         //鼠标按下的起始点
Rect selection;      //定义矩形选框
bool selectObject = false;    //是否选择对象


int blockSize = 0, uniquenessRatio =0, numDisparities=0;
int P1; /* 控制视差平滑度的第一个参数,8*number_of_image_channels*blockSize*blockSize */
int P2;/*第二个参数控制视差平滑度,32*number_of_image_channels*blockSize*blockSize*/
cv::Ptr<cv::StereoSGBM> sgbm = cv::StereoSGBM::create();

// 设置相机内外参数文件路径
void setParameterPath(string path)
{
    string parameterPath1 = path + "intrisics.yml";
    FileStorage fs(parameterPath1, FileStorage::READ);
    if(fs.isOpened())
    {
        fs["cameraMatrixL"] >> cameraMatrix_L;
        fs["cameradistCoeffsL"] >> discoeff_L;
        fs["cameraMatrixR"] >> cameraMatrix_R;
        fs["cameradistCoeffsR"] >> discoeff_R;
        fs.release();

        cout<<"*****左右摄像机 内参 已读取"<<endl;
    }
    else
    {
        cout << "******" << parameterPath1
             << " can not open" << endl;
    }

    string parameterPath2 = path + "extrinsics.yml";
    fs.open(parameterPath2, FileStorage::READ);
    if(fs.isOpened())
    {
        fs["R"] >> R;
        fs["T"] >> T;
        fs["Rl"] >> Rl;
        fs["Rr"] >> Rr;
        fs["Pl"] >> Pl;
        fs["Pr"] >> Pr;
        fs["Q"] >> Q;
        fs["mapLx"] >> mapLx;
        fs["mapLy"] >> mapLy;
        fs["mapRx"] >> mapRx;
        fs["mapRy"] >> mapRy;
        fs["validROIL"] >> validROIL;
        fs["validROIR"] >> validROIR;
        fs.release();
        cout<<"*****左右摄像机  外参 已读取"<<endl<<endl<<endl;
    }
    else
    {
        cout << "******" << parameterPath2
             << " can not open" << endl;
    }
}



void Stereo_SGBM_match(int,void*) /*参数不能改变*/
{
    P1 = 8 * grayImageL.channels() * blockSize*blockSize;
    P2 = 4*P1;
    sgbm->setP1(P1);
    sgbm->setP2(P2);
    sgbm->setMinDisparity(0);//此参数决定左图中的像素点在右图匹配搜索的起点
    sgbm->setSpeckleRange(32);


    // 下面6个参数用按钮来调节
    sgbm->setNumDisparities(numDisparities*16+16); // 必须能被16整除; 视差搜索范围
    sgbm->setBlockSize(blockSize); //  > = 1的奇数 3~21
    sgbm->setDisp12MaxDiff(1); //左右一致性检测最大容许误差阈值
    sgbm->setPreFilterCap(63);
    sgbm->setUniquenessRatio(10); // 5~15
    sgbm->setSpeckleWindowSize(100); // 50~200



    // 对左右视图的左边进行边界延拓,以获取与原始视图相同大小的有效视差区域
    /* 加大numDisparities值,深度图左边会变黑,但是整图尺寸变大!很有效哦*/
    /*https://blog.csdn.net/u011574296/article/details/87546622*/
    copyMakeBorder(grayRectifyL,grayRectifyL,0,0,numDisparities,0,cv::BORDER_REPLICATE);
    copyMakeBorder(grayRectifyR,grayRectifyR,0,0,numDisparities,0,cv::BORDER_REPLICATE);
    sgbm->compute(grayRectifyL, grayRectifyR, disp); // //得出的结果是以16位符号数的形式的存储的,出于精度需要,所有的视差在输出时都扩大了16倍(2^4)
    disp = disp.colRange(numDisparities, grayRectifyL.cols).clone();


    // 视觉差图-> 深度图
    reprojectImageTo3D(disp, xyz, Q, true); //在实际求距离时,ReprojectTo3D出来的X / W, Y / W, Z / W都要乘以16(也就是W除以16),才能得到正确的三维坐标信息。
    xyz = xyz * 16;

    // 转格式
    disp.convertTo(disp8, CV_8U, 255 / ((numDisparities * 16 + 16)*16.));//计算出的视差是CV_16S格式,
    imshow("disparity", disp8);

    applyColorMap(disp8, disp8, COLORMAP_RAINBOW);
    imshow("color", disp8);
}


int main(){

    //  读取模型
    string detect_model_path = "/home/jason/PycharmProjects/pytorch_learn/yolo/ultralytics-main-yolov8/yolov8n.onnx";
    Yolov8Onnx yolov8;
    if (yolov8.ReadModel(detect_model_path))
        cout << "read Net ok!\n";
    else {
        return -1;
    }

    //生成随机颜色;每个类别都有自己的颜色
    vector<Scalar> color;
    srand((time(0)));
    for (int i=0; i<80; i++){
        int b = rand() %  256; //随机数为0~255
        int g = rand() % 256;
        int r = rand() % 256;
        color.push_back(Scalar(b,g,r));
    }

    // 读取相机内外参数/
    setParameterPath("/home/jason/work/my--camera/calibration/");

    // 相机尺寸设置
    VideoCapture capture(0);
    capture.set(CAP_PROP_FRAME_WIDTH, imageWidth*2);
    capture.set(CAP_PROP_FRAME_HEIGHT, imageheight);
    capture.set(CAP_PROP_FPS, 30);


    //在double-camera && YOLOv8窗口提供 调节SGBM算法 窗口按钮
    namedWindow("double-camera && YOLOv8", cv::WINDOW_AUTOSIZE);
    createTrackbar("NumDisparities:\n", "double-camera && YOLOv8", &numDisparities, 16, Stereo_SGBM_match);
    createTrackbar("BlockSize:\n", "double-camera && YOLOv8",&blockSize, 8, Stereo_SGBM_match);

    Mat frame;
    struct timeval t1, t2;
    double timeuse;
    while (1) {

        //
        capture>>frame;

        // 左相机图、右边相机图
        rgbImageL = frame(Rect(Point(0,0), Point(imageWidth, imageheight))).clone();
        rgbImageR = frame(Rect(Point(imageWidth, 0), Point(imageWidth*2, imageheight))).clone();
//        printf("left camera:%d * %d  right camre:%d * %d\n\n",
//               rgbImageL.rows, rgbImageL.cols, rgbImageR.rows, rgbImageR.cols);

        // 转灰度,用于SGBM
        cvtColor(rgbImageL, grayImageL, COLOR_BGR2GRAY);
        cvtColor(rgbImageR, grayImageR, COLOR_BGR2GRAY);
        imshow("before Rectify", frame);

        //remap之后,左右相机的图像已经共面并行对准了
        // 用于SGBM
        remap(grayImageL, grayRectifyL, mapLx, mapLy, INTER_LINEAR);
        remap(grayImageR, grayRectifyR, mapRx, mapRy, INTER_LINEAR);
//        printf("rectify  grayLeft :%d * %d  rectify grayRight:%d * %d\n\n",
//               grayRectifyL.rows, grayRectifyL.cols, grayRectifyR.rows, grayRectifyR.cols);

        // SGBM算法计算视差图,获得深度图
        Stereo_SGBM_match(0,0);

        // 左右相机图像,用读取的映射矩阵进行畸变矫正、立体矫正 用于YOLOv8
        remap(rgbImageL,rgbRectifyL, mapLx, mapLy, INTER_LINEAR);
        remap(rgbImageR, rgbRectifyR, mapRx, mapRy, INTER_LINEAR);
//        printf("rectify left :%d * %d  rectify right:%d * %d\n\n",
//               rgbRectifyL.rows, rgbRectifyL.cols, rgbRectifyR.rows, rgbRectifyR.cols);
        imshow("rectify L", rgbRectifyL);
        imshow("rectify R",rgbRectifyR);




        //rgbRectifyL rgbRectifyR显示在同一张图上,如果你的标定没问题,会行对准
        Mat canvas = Mat::zeros(imageheight, imageWidth*2, CV_8UC3);
        Rect l = Rect(0, 0, imageWidth, imageheight);
        Rect r = Rect(imageWidth, 0, imageWidth, imageheight);
        rgbRectifyL.copyTo(canvas(l));//左图像画到画布上
        rgbRectifyR.copyTo(canvas(r));//右图像画到画布上
        for (int  i=0; i<canvas.rows; i+=50)
        {
            Point pt1 = Point(0,i);
            Point pt2 =  Point(canvas.cols, i);
            line(canvas, pt1, pt2, Scalar(0, 255, 0), 1, 8);
        }
        imshow("after rectify",canvas);


        // YOLOv8检测
        vector<OutputDet> result;
        gettimeofday(&t1, NULL);
        bool  find = yolov8.OnnxDetect(rgbRectifyL, result);
        gettimeofday(&t2, NULL);

        vector<string> ObejctDepthInfo;
        if(find)
        {
            // 目标中心点深度信息
            for(size_t i=0; i<result.size(); i++){
                int  centerX = result[i].box.x + result[i].box.width/2;
                int centerY = result[i].box.y + result[i].box.height/2;
                Point center = Point(centerX, centerY);
                float depth = xyz.at<Vec3f>(center)[0];
                string depthInfo = to_string(depth);
                cout  << depthInfo << "\n";
                ObejctDepthInfo.push_back(depthInfo);
            }

            // 画框
            DrawPred(rgbRectifyL, result, yolov8._className, color,ObejctDepthInfo);
        }
        else {
            cout << "Don't find !\n";
        }

        // 打印检测耗时
        timeuse = (t2.tv_sec - t1.tv_sec) +
                (double)(t2.tv_usec -t1.tv_usec)/1000000; //s
        string label = "duration:" + to_string(timeuse*1000); // ms
        putText(rgbRectifyL, label, Point(30,30), FONT_HERSHEY_SIMPLEX,
                0.5, Scalar(0,0,255), 2, 8);

         //左相机图与disp8组合在一起
        Mat  combination = Mat::zeros(imageheight, imageWidth*2, CV_8UC3);
        Rect L = Rect(0, 0, imageWidth, imageheight);
        Rect R = Rect(imageWidth, 0, imageWidth, imageheight);
        rgbRectifyL.copyTo(combination(L));//左图像画到画布上
        disp8.copyTo(combination(R));//右图像画到画布上


        imshow("double-camera && YOLOv8", combination);
        if (waitKey(1)=='q')  break;

    }


    return 0;
}

utils.cpp

YOLOv8 ONNX  RUNTIME 部署代码中的utils.cpp中有一个函数需要作修改

void DrawPred(cv::Mat& img, std::vector<OutputDet> result,
              std::vector<std::string> classNames,
              std::vector<cv::Scalar> color,
              std::vector<std::string> ObejectdepthInfo)
{
    for (size_t i=0; i<result.size(); i++){
        int  left,top;
        left = result[i].box.x;
        top = result[i].box.y;

        // 框出目标
        rectangle(img, result[i].box,color[result[i].id], 2, 8);

        // 在目标框左上角标识目标类别以及概率
        string depth;
        if (ObejectdepthInfo.size() > 0){
            depth  = ObejectdepthInfo[i];
        }
        else {
            depth  = "false";

        }
        string label = classNames[result[i].id] + ":" + to_string(result[i].confidence) + "  depth:"+depth;
        int baseLine;
        Size labelSize = getTextSize(label, FONT_HERSHEY_SIMPLEX, 0.5, 1, &baseLine);
        top = max(top, labelSize.height);
        putText(img, label, Point(left, top), FONT_HERSHEY_SIMPLEX, 0.5, color[result[i].id], 2);
    }
}

参考:opencv中createTrackbar()函数用法总结(06)_opencv createtrackbar 输入多行的字符串_洛克家族的博客-CSDN博客

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/499965.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

freeRTOS中使用看门狗的一点思考

关于看门狗想必各位嵌入式软件开发的朋友应该都不会陌生的。在嵌入式软件开发中&#xff0c;看门狗常被用于监测cpu的程序是否正常在运行&#xff0c;如果cpu程序运行异常会由看门狗在达到设定的阈值时触发复位&#xff0c;从而让整个cpu复位重新开始运行。 看门狗的本质是一个…

Qt QQueue 安全的多线程队列、阻塞队列

文章目录 1. C queue 队列基本用法2. Qt QQueue 队列基本用法3. Qt QQueue 多线程队列4. Qt BlockingQueue 自定义线程安全的阻塞队列 1. C queue 队列基本用法 在C中&#xff0c;queue是一个模板类&#xff0c;用于实现队列数据结构&#xff0c;遵循先进先出的原则。 ♦ 常用…

测试3:用例

目录 1.测试用例的基本要素 2.测试用例的设计方法 1.基于需求的设计方法 2.等价类 1.概念 2.步骤: 3.例子 3.边界值 1.概念 2.步骤 3.例子 4.判定表 1.概念 2.设计测试用例 3.例子 5.正交排列 1.什么是正交表 2.测试用例 3.如何通过正交表设计测试用例 6.场景…

(3)Qt——信号槽

目录 1.信号槽的概念** 2.信号槽的连接*** 2.1自带信号 → 自带槽 2.2 自带信号 → 自定义槽 2.3 自定义信号 3. 参数传递** 3.1 全局变量 3.2 信号槽传参 4. 对应关系** 4.1 一对多 4.2 多对一 1.信号槽的概念** 信号槽指的是信号函数与槽函数的连接&#xff0c;可…

AI绘图入门 安装 stable-diffusion-webui

下面介绍了N卡&#xff0c;A卡&#xff0c;或CPU跑 stable-diffusion-webui的方法。 1.安装python 3.10.x https://www.python.org/downloads/ 2.安装Git https://git-scm.com/downloads 【非必要】打开代理工具&#xff08;比如clash&#xff09;然后在cmd配置git的http和…

软件测试相关概念

✏️作者&#xff1a;银河罐头 &#x1f4cb;系列专栏&#xff1a;JavaEE &#x1f332;“种一棵树最好的时间是十年前&#xff0c;其次是现在” 目录 需求需求的定义测试人员眼中的需求为什么需求对测试人员如此重要如何深入理解需求 测试用例定义为什么要有测试用例 软件错误…

IT服务规划设计笔记

规划设计处于整个IT服务生命周期中的前端&#xff0c;其主要目的在于&#xff1a; &#xff08;1&#xff09;设计满足业务需求的IT服务 &#xff08;2&#xff09;设计SLA、测量方法和指标 &#xff08;3&#xff09;设计服务过程及其控制方法 &#xff08;4&#xff09;规…

learn_C_deep_9 (汇编角度理解return的含义、const 的各种应用场景)

return 关键字 不知道我们大家是否有一个疑惑&#xff1a;我们下载一个大型游戏软件&#xff08;王者荣耀&#xff09;&#xff0c;都要花几个小时去下载&#xff0c;但是一旦我们游戏连输&#xff0c;想要删除这个软件的时候&#xff0c;它仅仅只需要十几秒&#xff0c;这是为…

主题建模和文本聚类:理论与实践

❤️觉得内容不错的话&#xff0c;欢迎点赞收藏加关注&#x1f60a;&#x1f60a;&#x1f60a;&#xff0c;后续会继续输入更多优质内容❤️ &#x1f449;有问题欢迎大家加关注私戳或者评论&#xff08;包括但不限于NLP算法相关&#xff0c;linux学习相关&#xff0c;读研读博…

真题详解(3FN)-软件设计(六十九)

真题详解&#xff08;构造二叉树&#xff09;-软件设计&#xff08;六十八)https://blog.csdn.net/ke1ying/article/details/130536155 学生信息学生id姓名性别1{家长ID}*班主任班级。 解析&#xff1a;当存在1对多的情况&#xff0c;要写个1{}*&#xff0c;中间用{}。 ()表…

一篇学会Gitlab搭建及使用

目录 一、Gitlab介绍 1、什么是Gitlab 二、搭建gitlab并实现ssl 1、配置yum源或下载gitlab包 2、安装依赖软件及获取GPG密钥 3、安装gitlab-ce 4、创建私有密钥 5、创建私有证书 6、创建CRT签名证书 7、利用openssl签署pem 证书 8、配置证书到gitlab 9、初始化gitla…

读书笔记:《图解CIO工作指南》

《图解CIO工作指南》第 4 版&#xff0c;日 . 野村综合研究所系统咨询事业本部 著&#xff0c;周自恒 译 大数据、云计算时代下的IT战略和IT实务 CIO工作&#xff1a;IT管理、IT架构、IT实践 以着眼企业未来的观点进行构思&#xff1a;可视化&#xff08;业务与系统&am…

初始化vue中data中的数据

当组件的根元素使用了v-if的时候, 并不会初始化data中的数据 如果想完全销毁该组件并且初始化数据,需要在使用该组件的本身添加v-if 或者是手动初始化该组件中的数据 初始化化数据的一些方法 Object.assign(this.$data, this.$options.data()) this.$data&#xff1a;当前的da…

TortoiseGit(大乌龟)安装教程(Git 图形化工具,告别手敲命令)

TortoiseGit安装教程 1. 下载TortoiseGit 官方下载地址&#xff1a;https://tortoisegit.org/download/ 自行选择下载对应版本&#xff08;大部分位64位&#xff09;&#xff0c;进行下载 2. 安装TortoiseGit 打开安装包&#xff0c;如下图所示&#xff1a; 点击 Next&…

IDM绿色最新2023中文版磁力下载工具

Internet Download Manager&#xff08;idm&#xff09;是一款优秀的多线程下载工具。它支持自动捕获剪贴板及浏览器及流媒体网站的音视频下载链接&#xff0c;还有批量队列下载、静默下载、站点抓取等众多功能选项&#xff0c;可以说是 Windows 平台上功能最为强大的多线程下载…

通过栈/队列/优先级队列/了解容器适配器,仿函数和反向迭代器

文章目录 一.stack二.queue三.deque&#xff08;双端队列&#xff09;四.优先级队列优先级队列中的仿函数手搓优先级队列 五.反向迭代器手搓反向迭代器 vector和list我们称为容器&#xff0c;而stack和queue却被称为容器适配器。 这和它们第二个模板参数有关系&#xff0c;可以…

录屏界鼻祖Camtasia 2023中文版功能介绍/下载安装激活教程

随着网络科技的迅速发展&#xff0c;所以对于电脑的使用率也就越来越高了&#xff01;然而&#xff0c;也可能跟这有关系&#xff0c;目前各种类型的软件层出不穷&#xff0c;当然也就包括了电脑录屏软件。这给我们造成了一些困难&#xff0c;究竟哪一款适合自己呢&#xff1f;…

DMA的补充笔记

DMA有两个总线&#xff1a; 1、DMA存储器总线&#xff1a;DMA通过该总线来执行存储器数据的传入和传出。 2、DMA外设总线&#xff1a;DMA通过该总线访问AHB外设&#xff08;AHB主要是针对高效率、高频宽以及快速系统模块所设计的&#xff0c;主要有Flash 存储器、复位和时钟控…

栈和队列OJ题思路分享之栈和队列互换(C语言实现)

&#x1f493;博主CSDN主页:杭电码农-NEO&#x1f493;   ⏩专栏分类:刷题分享⏪   &#x1f69a;代码仓库:NEO的学习日记&#x1f69a;   &#x1f339;关注我&#x1faf5;带你刷更多C语言和数据结构的题!   &#x1f51d;&#x1f51d; 栈和队列刷题分享二 1. 前言⚡…

基于人类反馈的强化学习(RLHF) 理论

gpt 进程 GPT-1 用的是无监督预训练 有监督微调&#xff0c;只有简单的单向语言模型任务&#xff1b;GPT-2用的是纯无监督预训练&#xff0c;使用更多的数据&#xff0c;更大的模型&#xff0c;又新增了几个辅助的训练任务&#xff1b;GPT-3 沿用了 GPT-2 的纯无监督预训练&a…