QT集成IntelRealSense双目摄像头3,3D显示

news2025/1/3 6:04:21

前两篇文章,介绍了如何继承intel realsense相机和opengl。

这里介绍如何给深度数据和色彩数据一块显示到opengl里面。

首先,需要了解深度数据和彩色数据是如何存储的。先说彩色数据。彩色图像一般都是RGB,也就是每个像素有三个字节,分别是RGB,如果一个图像宽度是2,高度也是2。就是一共有4个像素。如下图。像素顺序是从左上到右下,依次排列。

假设第一个像素是红色,就是(255,0,0),第二个字节是(0,255,0),第三个是(0,0,255),最后一个是(0,0,0)。这个图像数据全部的内存就是0,255,0,0,0,255,0,0,255,0,0,0。一共2*2*3=12个字节。实际就是按左右到右下的顺序,给每个点的rgb值放到一块。

依次类推,宽width,高height的图像数据,实际上就是按左上到右下,width*height个RGB数据放到了一起。就是width*height*3个字节。

然后是深度数据,深度数据是16位,就是两个字节的无符号数,实际上就是按左上到右下的顺序,给4个unsigned short值放到了一块例如 800,1000,200,30。每个unsigned short两个字节,共2*2*2=8个字节。

依次类推,宽是width,高是height的深度数据,就是按左上到右下,width*height个unsigned short放到一起,共width*height*2个字节。

有了上面的前题,在Camera类里面增加两个指针,分别存储彩色图像数据,和深度数据,增加两个整数,用于存储图像宽度和高度,以及增加两个QImage,分别是彩色和深度图像。

第一步修改Camera类,每次抓取到图像,给深度数据拷贝到自己准备的缓存区。同时创建两个用于显示的深度图和彩色图。

修改后的Camera类:

#ifndef CAMERA_H
#define CAMERA_H

#include <QObject>
#include <QThread>
#include <librealsense2/rsutil.h>
#include <librealsense2/rs.hpp>
#include <QImage>
class Camera :public QThread
{
    Q_OBJECT
public:
    Camera();
    static Camera* getInstance();//单例模式
    void startCapture();        //开始采集
    void stopCapture();         //结束采集
    void run() override;        //线程函数
    unsigned short * depthData;//深度数据缓存区,需要初始化才能使用
    unsigned char* colorData;;//存储颜色数据缓存区,需要初始化才能使用
    int width;              //宽度
    int height;             //高度
    QImage colorImage;          //色彩图片
    QImage depthImage;         //深度图片
private slots:
    void doCam(int code,QString msg);   //用于接收线程信息,并弹出消息框
signals:
    void onCameraEvent(int code,QString msg);   //线程内部发送消息
    void onFrame();                             //线程内部发送图像采集成功信号
private:
    bool isCapture;             //是否采集中
    rs2::pipeline pipe;         //采集采集对象
};

#endif // CAMERA_H
#include "camera.h"
#include <QDebug>
#include <QException>
#include <common.h>
#include <GL/glu.h>
Camera::Camera()
{
    width=height=0;//没有获取到数据之前,是0
    colorData=NULL;//没有数据之前是空
    depthData=NULL;//没有数据之前是空
    connect(this,&Camera::onCameraEvent,this,&Camera::doCam);//链接线程内部的信号
}
/**
 * @brief Camera::getInstance 单例模式
 * @return
 */
Camera* Camera::getInstance(){
    static Camera* ins=NULL;//static代码,唯一对象
    if(ins==NULL)ins=new Camera();//创建一个实力
    return ins;
}

void Camera::startCapture(){
    loading("打开摄像头",0);//显示打开摄像头弹窗,一直显示,直到成功或是失败
     this->start();//开始子线程
}
/**
 * @brief Camera::stopCapture 停止采集
 */
void Camera::stopCapture(){
    isCapture=false;//退出子线程
    pipe.stop();//停止相机
}
/**
 * @brief Camera::doCam 接收线程的消息,并弹窗显示
 * @param code 消息编码,0表示成功
 * @param msg 消息内容
 */
void Camera::doCam(int code,QString msg){
    if(code==0)success(msg);
    else error(msg);
}
/**
 * @brief Camera::run 线程函数,用于打开相机和采集图像
 */
void Camera::run(){

    try {
        pipe.start();       //尝试打开相机
    }
    catch (const std::exception& e)
    {
        emit onCameraEvent(1, e.what() ); //如果没有成功,发送给主线程一个消息
        return;//返回,不继续了
    }
    emit onCameraEvent(0,"相机打开成功"); //如果打开成功,也发送一个
    rs2::colorizer color_map;//用于将深度数据,转换为图像数据色彩数据的map,
    isCapture=true;//循环控制变量
    while(isCapture){
        try {
            rs2::frameset data= pipe.wait_for_frames(); //获取一帧数据          
            rs2::frame colo=data.get_color_frame();    //获取彩色图像
            rs2::frame dept=data.get_depth_frame();  //获取深度数据,并转换为可见图
            if(width==0 || height==0){              //如果没有初始化
                width = dept.as<rs2::video_frame>().get_width();     //获取图像宽度
                height= dept.as<rs2::video_frame>().get_height();    //获取图像高度
                depthData=(unsigned short*)malloc(2*width*height);  //内存初始化,深度数据是16位,就是两个字节。总字节数量就是宽度*高度*2
                colorData=(unsigned char*)malloc(3*width*height);   //彩色内存初始化,彩色数据RGB,就是3个字节,总字节就是宽度*高度*3
            }
            if(depthData!=NULL){
                memcpy(depthData,dept.get_data(),width*height*2);   //拷贝深度图像数据
                depthImage= QImage((uchar*)dept.apply_filter(color_map).get_data(),width,height,QImage::Format_RGB888); //深度数据创建一个图片
            }
            if(colorData!=NULL){
                memcpy(colorData,colo.get_data(),width*height*3);   //拷贝彩色图像数据
                colorImage=QImage((uchar*)colo.get_data(),width,height,QImage::Format_BGR888);//彩色数据创建一个图片
            }           

            emit onFrame(); //发送消息,数据准备好了

        }
        catch (const std::exception& e) {
            emit onCameraEvent(2,e.what());
        }
    }
}

第二步:在GLwidget类里面绘制三维数据。

具体的原理是,监听Camera类的onFrame事件,每次监听到,就重新绘制。绘制的时候,循环像素的行和列,根据行x列y计算当前像素的下下标i=x+y*width。根据下标,去深度数据里找到对应的深度。去色彩数据寻找对应的RGB值。然后在gl绘制一个点就行了。核心代码:

 Camera* cam=Camera::getInstance();//获取相机示例
    int disx=cam->width/2;//x方向偏移,到中心点
    int disy=cam->height/2;//y方向偏移,到中心点
    int disz=1000;//这个是随便写的
    glBegin(GL_POINTS);//开始绘制点
    for(int y=0;y<cam->height;y++){//循环行
        for(int x=0;x<cam->width;x++){//循环列
            int i=x+y*cam->width;//计算当前像素的下标,
            int z=cam->depthData[i];//深度
            if(z!=0){
              glColor3f(cam->colorData[i*3]/255.0,cam->colorData[i*3+1]/255.0,cam->colorData[i*3+2]/255.0); //rgb是0-255,opengl用0-1表示
              glVertex3f(x-disx,disy-y,disz-z);//绘制一个点
            }

        }
    }
    glEnd();

完整GLWidget类代码:

#ifndef GLWIGET_H
#define GLWIGET_H

#include <QObject>
#include <QGLWidget>
#include <GL/gl.h>
#include <GL/glu.h>
#include <QMouseEvent>
#include <QWheelEvent>
class GLWidget : public QGLWidget
{
    Q_OBJECT
public:
    GLWidget();
    static GLWidget* getInstance();          //单例模式
protected:
    void resizeGL(int w, int h) override;   //窗口大小改变的时候,gl重新初始化
    void initializeGL() override;           //初始化gl
    void paintGL() override;                //核心绘制
    void mousePressEvent(QMouseEvent *event) override;
    void mouseMoveEvent(QMouseEvent *event) override;
    void mouseReleaseEvent(QMouseEvent *event) override;
    void wheelEvent(QWheelEvent *event) override;
private slots:
    void onFrame();//响应数据采集到事件
private:
    double r;//相机距离中心的的半径
    double degZ;//在水平面的角度
    double degY;//在垂直面的角度
    double camx,camy,camz;//相机位置
    void refresh();//重新绘制
    QPoint old;//鼠标原始位置
    bool isPressed;//是否按下
    double rate;//用于换算弧度的比例
};

#endif // GLWIGET_H
#include "glwidget.h"
#include <qmath.h>
#include <QDebug>
#include <camera.h>
GLWidget::GLWidget()
{
    r=1000;//默认距离
    degY=degZ=0;
    camz=r*qCos(degZ);
    camx=r*qSin(degZ);
    camy=r*qSin(degY);
    connect(Camera::getInstance(),&Camera::onFrame,this,&GLWidget::onFrame);//链接相机采集到数据事件
}

/**
 * @brief GLWidget::getInstance 单例模式
 * @return
 */
GLWidget* GLWidget::getInstance(){
    GLWidget* ins=NULL;
    if(ins==NULL)ins=new GLWidget();
    return ins;
}
/**
 * @brief GLWidget::initializeGL 初始化opengl。可以步写
 */
void GLWidget::initializeGL(){

}
/**
 * @brief GLWidget::resizeGL 窗口大小改变
 * @param w
 * @param h
 */
void GLWidget::resizeGL(int w, int h){
    glViewport(0,0,w,h);            //重新适应窗口大小
    glMatrixMode (GL_PROJECTION);
    glLoadIdentity ();
    gluPerspective(60.0, (GLfloat) w/(GLfloat) h, 1, 2000.0);           //设置相机投影参数
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();

}
/**
 * @brief GLWidget::paintGL 绘制核心方法
 */
void GLWidget::paintGL(){
    glClearColor(0,0,0,0);                       //背景色
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);//清除上一次的缓存
    glLoadIdentity();  //加载单位矩阵
    gluLookAt(camx,camy,camz, 0,0,0,   0.0,1.0,0);                             //设置相机
    Camera* cam=Camera::getInstance();//获取相机示例
    int disx=cam->width/2;//x方向偏移,到中心点
    int disy=cam->height/2;//y方向偏移,到中心点
    int disz=1000;//这个是随便写的
    glBegin(GL_POINTS);//开始绘制点
    for(int y=0;y<cam->height;y++){//循环行
        for(int x=0;x<cam->width;x++){//循环列
            int i=x+y*cam->width;//计算当前像素的下标,
            int z=cam->depthData[i];//深度
            if(z!=0){
              glColor3f(cam->colorData[i*3]/255.0,cam->colorData[i*3+1]/255.0,cam->colorData[i*3+2]/255.0); //rgb是0-255,opengl用0-1表示
              glVertex3f(x-disx,disy-y,disz-z);//绘制一个点
            }
        }
    }
    glEnd();

}
/**
 * @brief GLWidget::onFrame 如果有数据,就重新绘制一下
 */
void GLWidget::onFrame(){
    this->refresh();
}
/**
 * @brief GLWidget::refresh 重新计算相机位置
 */
void GLWidget::refresh(){
    camz=r*qCos(degZ);
    camx=r*qSin(degZ);
    camy=r*qSin(degY);

    this->update();
}


void GLWidget::wheelEvent(QWheelEvent *event){
    qDebug()<<r<<event->delta();
    r+=event->delta()/20;
    if(r<20)r=20;
    if(r>1800)r=1800;
    refresh();
}
void GLWidget::mouseMoveEvent(QMouseEvent *event){
    if(isPressed){
        QPoint p=event->pos();
        degZ+=(p.x()-old.x())/5.0;
        degY+=(p.y()-old.y())/5.0;
        old=p;
        refresh();
    }
}

void GLWidget::mousePressEvent(QMouseEvent *event){

    if(event->button()==Qt::LeftButton){
        isPressed=true;
        old=event->pos();
    }
}
void GLWidget::mouseReleaseEvent(QMouseEvent *event){
    if(event->button()==Qt::LeftButton){
        isPressed=false;
    }
}

运行效果:

 PS:common文件里面有一个用于弹出提示的工具,仿的andoroid系统toast效果,实现tip,success,error,loading(等待)等效果。

完整代码见文章附件。

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

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

相关文章

PHP框架+gatewayworker实现在线1对1聊天--gatewayworker说明(2)

文章目录 gatewayworker使用说明onConnect 说明 gatewayworker使用说明 gatewayworker里只需要使用Applications\YourApp下的Events.php文件。 对文件的代码进行一下改造&#xff0c;如下&#xff0c;我们只需要用到onConnect方法&#xff0c;写法固定&#xff0c;其他方法都…

未授权访问漏洞集合

Redis未授权访问漏洞 进入vulhub目录启动靶机 进⼊⽬录&#xff1a;cd /vulhub-master/redis/4-unacc 启动&#xff1a;docker-compose up -d 检查&#xff1a;docker ps 在Kali上安装redis程序 #安装redis apt-get install redis #redis链接 redis-cli -h 124.221.58.83 -…

环,域,体,整区,理想,极大理想,

环&#xff1a; 定义&#xff1a; 加法交换群 乘法半群 分配律 域的定义&#xff1a; 加法交换群 乘法群&#xff08;去掉0元是交换群&#xff09; 分配律 Eg:比如整数集合不是域&#xff0c;因为对于乘法来说&#xff0c;去掉0后没有单位元了&#xff0c;但是是环 Eg…

Idea创建JDK17的maven项目失败

Idea创建JDK17的maven项目失败 Error occurred during initialization of VM Could not find agent library instrument on the library path, with error: Can’t find dependent libraries Possible solution: Check your maven runner VM options. Open Maven Runner setti…

基于MPPT算法的光伏并网发电系统simulink建模与仿真

目录 1.课题概述 2.系统仿真结果 3.核心程序与模型 4.系统原理简介 5.完整工程文件 1.课题概述 基于MPPT算法的光伏并网发电系统simulink建模与仿真&#xff0c;包括PV光伏发电模块&#xff0c;并网电路&#xff0c;MPPT&#xff0c;PWM等模块。 2.系统仿真结果 3.核心程…

NAT 技术如何解决 IP 地址短缺问题?

NAT 技术如何解决 IP 地址短缺问题&#xff1f; 前言 这是我在这个网站整理的笔记,有错误的地方请指出&#xff0c;关注我&#xff0c;接下来还会持续更新。 作者&#xff1a;神的孩子都在歌唱 随着互联网的普及和发展&#xff0c;IP 地址的需求量迅速增加。尤其是 IPv4 地址&…

KOI技术-事件驱动编程(Sping后端)

1 “你日渐平庸&#xff0c;甘于平庸&#xff0c;将继续平庸。”——《以自己喜欢的方式过一生》 2. “总是有人要赢的&#xff0c;那为什么不能是我呢?”——科比布莱恩特 3. “你那么憎恨那些人&#xff0c;和他们斗了那么久&#xff0c;最终却要变得和他们一样&#xff0c;…

java: JDK isn‘t specified for module ‘product-service‘问题解决

目录 问题 解决方法 1.打开File->Project Structure... 2.将Project SDK修改为17 Oracle OpenJDK 17.0.12&#xff0c;并Apply&#xff0c;OK 问题 添加module后报错&#xff1a;java: JDK isnt specified for module product-service 查看pom.xml文件也添加了对应的JDK…

慧集通iPaaS低代码平台培训-基础篇

训练使用素材&#xff1a; 1.数据源&#xff1a; 单号业务日期工厂仓库物料单位数量批次0100012022-5-1210031001030001kg500202304150100012022-5-1210031001030001kg122202304150100012022-5-1210031001030001kg1250202304150100012022-5-1210031001030002kg130202304110100…

CMS漏洞靶场攻略

DeDeCMS 环境搭建 傻瓜式安装 漏洞一&#xff1a;通过文件管理器上传WebShel 步骤⼀:访问目标靶场其思路为 dedecms 后台可以直接上传任意文件&#xff0c;可以通过⽂件管理器上传php文件获取webshell 登陆网站后台 步骤二&#xff1a;登陆到后台点击 【核心】 --》 【文件式…

计算机网络 (12)物理层下面的传输媒体

前言 计算机网络物理层下面的传输媒体是计算机网络设备之间的物理通路&#xff0c;也称为传输介质或传输媒介&#xff0c;并不包含在计算机网络体系结构中&#xff0c;而是处于物理层之下。 一、传输媒体的分类 导向型媒体&#xff1a;电磁波被导引沿着固体媒体传播。常见的导向…

光储充一体化解决方案详解。

一、光储充介绍 1、什么是光储充 “光储充”一体化&#xff0c;顾名思义&#xff0c;是由光伏发电、储能、充电集成一体、互相协调支撑的绿色充电模式。其工作原理是利用光伏发电&#xff0c;余电由储能设备存储&#xff0c;共同承担供电充电任务。在用电高峰&#xff0c;光储…

【MATLAB第111期】基于MATLAB的sobol全局敏感性分析方法二阶指数计算

【MATLAB第111期】基于MATLAB的sobol全局敏感性分析方法二阶指数计算 一、简介 在MATLAB中计算Sobol二阶效应指数通常涉及到全局敏感性分析&#xff08;Global Sensitivity Analysis, GSA&#xff09;&#xff0c;其中Sobol方法是一种流行的技术&#xff0c;用于评估模型输入…

RK3568 bsp 9 - USB调试记录

文章目录 1、环境介绍2、RK3568 USB资源介绍3、配置目标4、dts配置4.1、USB3.0 OTG4.2、USB2.0 Host 2 和 USB2.0 Host 3 5、kernel配置5.1、USB PHY CONFIG5.2、USB Host CONFIG5.3、USB OTG CONFIG5.4、USB外设CONFIG5.4.1、Mass Storage Class CONFIG5.4.2、USB HID CONFIG …

挖空的解决思路

用RagFlow解析完文档后怎么对某些实体的某些参数进行遮挡&#xff0c;给他设置预设好的可选项&#xff0c;并最终整合成文档模版&#xff0c;给我详细讲解怎么实现 解析–实体抽取&#xff08;Open NRE、UIE&#xff09;–遮挡–插入可选项–保存模版1–微调训练得模版2

【AI日记】24.12.30 kaggle 比赛 2-18

【AI论文解读】【AI知识点】【AI小项目】【AI战略思考】【AI日记】 工作 参加&#xff1a;kaggle 比赛 Regression with an Insurance Dataset时间&#xff1a;8 小时 读书 1 书名&#xff1a;教育的本质时间&#xff1a;0.5 小时评估&#xff1a;快速读完&#xff0c;收获不…

javaweb 04 springmvc

0.1 在上一次的课程中&#xff0c;我们开发了springbootweb的入门程序。 基于SpringBoot的方式开发一个web应用&#xff0c;浏览器发起请求 /hello 后 &#xff0c;给浏览器返回字符串 “Hello World ~”。 其实呢&#xff0c;是我们在浏览器发起请求&#xff0c;请求了我们…

【C++】九九乘法表编程题详解与多角度对比分析

博客主页&#xff1a; [小ᶻ☡꙳ᵃⁱᵍᶜ꙳] 本文专栏: C 文章目录 &#x1f4af;前言&#x1f4af;题目概述题目描述 &#x1f4af;老师的实现方法代码解析优点不足 &#x1f4af;我的实现方法代码解析优点不足 &#x1f4af;实现方法对比&#x1f4af;优化与扩展代码优化…

保险公司开辟新模式:智能ai搭建咨询帮助中心

随着保险行业的快速发展&#xff0c;消费者对保险服务的期望也在不断提高。从传统的电话咨询到在线客服&#xff0c;服务模式的不断升级旨在提供更加便捷、高效的客户服务。然而&#xff0c;面对日益复杂的保险产品和多样化的客户需求&#xff0c;传统的人工客服体系逐渐显露出…

雷电模拟器安装LSPosed

雷电模拟器最新版支持LSPosed。记录一下安装过程 首先到官网下载并安装最新版&#xff0c;我安装的时候最新版是9.1.34.0&#xff0c;64位 然后开启root和系统文件读写 然后下载magisk-delta-6并安装 ,这个是吾爱破解论坛提供的&#xff0c;号称适配安卓7以上所有机型&#x…