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

前两篇文章,介绍了如何继承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;//没有获取到数据之前,是0colorData=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位,就是两个字节。总字节数量就是宽度*高度*2colorData=(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;           //初始化glvoid 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.mzph.cn/web/64720.shtml

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

相关文章

Postman[4] 环境设置

作用&#xff1a;不同的环境可以定义不同的参数&#xff0c;在运行请求时可以根据自己的需求选择需要的环境 1.创建Environment 步骤&#xff1a; Environment-> ->命名->添加环境变量 2.使用Environment 步骤&#xff1a;Collection- >右上角选择需要的环境

SpringBoot_第二天

SpringBoot_第二天 学习目标 Mybatis整合&数据访问 使用SpringBoot开发企业项目时&#xff0c;持久层数据访问是前端页面数据展示的基础&#xff0c;SpringBoot支持市面上常见的关系库产品(Oracle,Mysql,SqlServer,DB2等)对应的相关持久层框架&#xff0c;当然除了对于关系…

SparseViT:基于稀疏编码Transformer的非语义中心、参数高效的图像篡改定位

摘要 https://arxiv.org/pdf/2412.14598 非语义特征或语义无关特征&#xff0c;与图像上下文无关但对图像篡改敏感&#xff0c;被认为是图像篡改定位&#xff08;IML&#xff09;的重要证据。由于无法获得人工标签&#xff0c;现有工作依赖于手工方法提取非语义特征。手工非语…

【git】git生成rsa公钥的方法

git生成rsa公钥的方法 一&#xff0c;简介二&#xff0c;操作方法三&#xff0c;总结 一&#xff0c;简介 在工作的过程中&#xff0c;经常需要生成rsa的密钥&#xff0c;然后提供给别人&#xff0c;然后别人给你开通代码下载权限。本文介绍如何在本地生成rsa的密钥供参考。 …

【高项】信息系统项目管理师(二)项目管理概论

一、PMBOK的发展 项目管理知识体系&#xff08;PMBOK&#xff09;是由美国项目管理协会&#xff08;PMI&#xff09;开发的一套描述项目管理专业范围的知识体系&#xff0c;包含了对项目管理所需的知识、技能和工具的描述。 二、项目基本要素 2.1 项目基础 项目是为提供一项…

数据中台与数据治理服务方案[50页PPT]

本文概述了数据中台与数据治理服务方案的核心要点。数据中台作为政务服务数据化的核心&#xff0c;通过整合各部门业务系统数据&#xff0c;进行建模与加工&#xff0c;以新数据驱动政府管理效率提升与政务服务能力增强。数据治理则聚焦于解决整体架构问题&#xff0c;确保数据…

程序员测试日常小工具

作为一名程序员&#xff0c;或者测试人员&#xff0c;日常工作最常用的工具有哪些&#xff0c;截图&#xff0c;截图漂浮&#xff0c;翻译&#xff0c;日期处理&#xff0c;api调用...&#xff0c; 当你拿到一串报文后&#xff0c;想要json转换时&#xff0c;是不是要打…

【MySQL高级】第1-4章

第1章 存储过程 1.1 什么是存储过程&#xff1f; 存储过程可称为过程化SQL语言&#xff0c;是在普通SQL语句的基础上增加了编程语言的特点&#xff0c;把数据操作语句(DML)和查询语句(DQL)组织在过程化代码中&#xff0c;通过逻辑判断、循环等操作实现复杂计算的程序语言。 换…

获取用户详细信息-ThreadLocal优化

Thread全局接口可用&#xff0c;不用再重复编写。所以为了代码的复用&#xff0c;使用Thread。把之前的内容&#xff08;函数的参数和map与username&#xff09;注释掉&#xff0c;换为Thread传过来的内容&#xff08;map与username&#xff09;。 因为Thread需要在拦截器里面…

THUCNews解压/THUCNews数据集解压出问题

省流&#xff1a;使用zip64进行解压&#xff0c;文件数目太多windows默认zip16装不下 我在使用THUCNews中文文本数据集时出现了问题&#xff0c;原数据集解压后应该包含以下两个文件夹: 其中THUCNews文件夹下有以新闻类别命名的子文件。官网下载的是一个1.56GB的zip压缩包 而我…

安卓/system/bin下命令中文说明(AI)

ATFWD-daemon&#xff1a;AT指令转发守护进程&#xff0c;用于将AT指令从应用层转发到调制解调器。 PktRspTest&#xff1a;数据包响应测试工具。 StoreKeybox&#xff1a;存储密钥盒工具&#xff0c;用于安全地存储加密密钥。 WifiLogger_app&#xff1a;WiFi日志记录应用&…

Git操作总结

可以直接看实践 总结自施磊老师课程 Git与SVN对比 svn操作流程 写代码。 从服务器拉回服务器的当前版本库&#xff0c;并解决服务器版本库与本地代码的冲突。 将本地代码提交到服务器。 Git操作流程 写代码&#xff0c; 然后添加&#xff08;add&#xff09;到暂存区。 …

直流开关电源技术及应用二

文章目录 8 PFC8.1 基本概念8.1.1 功率因数8.1.2 功率因数偏低带来的影响8.1.3 特点 8.2 有源功率因数校正原理8.2.1不连续工作模式的矫正原理恒频控制技术控制目标控制关键要素控制过程实现方式公式Boost电路和boost pfc电路的联系和区别联系区别 恒导通时间控制 8.2.2 连续工…

UNI-APP_i18n国际化引入

官方文档&#xff1a;https://uniapp.dcloud.net.cn/tutorial/i18n.html vue2中使用 1. 新建文件 locale/index.js import en from ./en.json import zhHans from ./zh-Hans.json import zhHant from ./zh-Hant.json const messages {en,zh-Hans: zhHans,zh-Hant: zhHant }…

typora+picgo core+minio自动上传图片

1. 在服务器上安装docker版本minio 创建/docker/minio文件夹 mkdir -p /docker/minio在此文件夹创建docker-compose.yml version: "3.5" services:minio:image: quay.io/minio/minio:latestcontainer_name: minioprivileged: truerestart: alwaysports:# API接口访…

论文笔记:DepthLab: From Partial to Complete

是一篇很精炼的论文&#xff0c;不知道咋总结了&#xff0c;就差全文翻译了&#xff0c;不过在这里我主要关注3D部分&#xff0c;因为他的pipeline是基于SD的&#xff0c;框图也比较清晰易懂&#xff0c;非常细节的内容可以回头看论文&#xff0c;哈哈哈&#xff0c;给作者大佬…

LeetCode--排序算法(堆排序、归并排序、快速排序)

排序算法 归并排序算法思路代码时间复杂度 堆排序什么是堆&#xff1f;如何维护堆&#xff1f;如何建堆&#xff1f;堆排序时间复杂度 快速排序算法思想代码时间复杂度 归并排序 算法思路 归并排序算法有两个基本的操作&#xff0c;一个是分&#xff0c;也就是把原数组划分成…

Debian-linux运维-docker安装和配置

腾讯云搭建docker官方文档&#xff1a;https://cloud.tencent.com/document/product/213/46000 阿里云安装Docker官方文档&#xff1a;https://help.aliyun.com/zh/ecs/use-cases/install-and-use-docker-on-a-linux-ecs-instance 天翼云常见docker源配置指导&#xff1a;htt…

Qt6.8.1 Mingw13.1 编译opencv4.10时cannot convert ‘char*‘ to ‘LPWSTR

当选择build_world时出错 G:\ForOpencv4.10\opencv-4.10.0\modules\core\src\utils\filesystem.cpp: In function cv::String cv::utils::fs::getCacheDirectory(const char*, const char*): G:\ForOpencv4.10\opencv-4.10.0\modules\core\src\utils\filesystem.cpp:442:43: e…

通过无障碍服务(AccessibilityService)实现Android设备全局水印显示

一、无障碍功能简介 首先我们先来了解下无障碍功能的官方介绍&#xff1a; 无障碍服务仅应用于帮助残障用户使用 Android 设备和应用。它们在后台运行&#xff0c;并在触发 AccessibilityEvents 时接收系统的回调。此类事件表示用户界面中的某些状态转换&#xff0c;例如焦点已…