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.pswp.cn/web/64720.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/64720.shtml
英文地址,請注明出處:http://en.pswp.cn/web/64720.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Postman[4] 環境設置

作用&#xff1a;不同的環境可以定義不同的參數&#xff0c;在運行請求時可以根據自己的需求選擇需要的環境 1.創建Environment 步驟&#xff1a; Environment-> ->命名->添加環境變量 2.使用Environment 步驟&#xff1a;Collection- >右上角選擇需要的環境

【合并區間】

問題 以數組 intervals 表示若干個區間的集合&#xff0c;其中單個區間為 intervals[i] [starti, endi] 。 請你合并所有重疊的區間&#xff0c;并返回 一個不重疊的區間數組&#xff0c;該數組需恰好覆蓋輸入中的所有區間 。示例 1&#xff1a; 輸入&#xff1a;intervals …

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;現有工作依賴于手工方法提取非語義特征。手工非語…

Redisson 分布式鎖獲取tryLock和lock的區別

問題 boolean isLock lock.tryLock(10, 30, TimeUnit.SECONDS); boolean isLock lock.lock(30, TimeUnit.SECONDS); boolean isLock lock.lock(); 三者的區別&#xff1f;&#xff1f; 這三個方法都是用于獲取 Redisson 分布式鎖的&#xff0c;但它們在獲取鎖的方式和行為…

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

git生成rsa公鑰的方法 一&#xff0c;簡介二&#xff0c;操作方法三&#xff0c;總結 一&#xff0c;簡介 在工作的過程中&#xff0c;經常需要生成rsa的密鑰&#xff0c;然后提供給別人&#xff0c;然后別人給你開通代碼下載權限。本文介紹如何在本地生成rsa的密鑰供參考。 …

Zookeeper模式安裝Kafka(含常規、容器兩種安裝方式)

一、#創作靈感# 公司使用Kafka的軟件項目較多&#xff0c;故寫技術筆記鞏固知識要點 二、軟件環境 - Kafka 3.9.0 官方下載地址&#xff1a;Kafka 3.9.0 - ZooKeeper 3.9.3 官方下載地址&#xff1a;ZooKeeper 3.9.3 - Docker Desktop 4.37 容器圖形化工具 官方下載地址…

7.傅里葉級數練習題

7.傅里葉級數練習題 設函數&#xff1a; f ( x ) { ? x , 0 ≤ x ≤ 1 2 , 2 ? 2 x , 1 2 < x < 1 , f(x) \begin{cases} -x, & 0 \leq x \leq \frac{1}{2}, \\ 2 - 2x, & \frac{1}{2} < x < 1, \end{cases} f(x){?x,2?2x,?0≤x≤21?,21?<x&…

【高項】信息系統項目管理師(二)項目管理概論

一、PMBOK的發展 項目管理知識體系&#xff08;PMBOK&#xff09;是由美國項目管理協會&#xff08;PMI&#xff09;開發的一套描述項目管理專業范圍的知識體系&#xff0c;包含了對項目管理所需的知識、技能和工具的描述。 二、項目基本要素 2.1 項目基礎 項目是為提供一項…

C++設計模式:狀態模式(自動售貨機)

什么是狀態模式&#xff1f; 狀態模式是一種行為型設計模式&#xff0c;它允許一個對象在其內部狀態發生改變時&#xff0c;動態改變其行為。通過將狀態相關的邏輯封裝到獨立的類中&#xff0c;狀態模式能夠將狀態管理與行為解耦&#xff0c;從而讓系統更加靈活和可維護。 通…

【Pytorch實用教程】循環神經網絡中使用dropout需要注意的問題

文章目錄 問題解答警告的具體含義解決方案示例代碼總結問題 UserWarning: dropout option adds dropout after all but last recurrent layer, so non-zero dropout expects num_layers greater than 1, but got dropout=0.3 and num_layers=1 warnings.warn("dropout op…

數據中臺與數據治理服務方案[50頁PPT]

本文概述了數據中臺與數據治理服務方案的核心要點。數據中臺作為政務服務數據化的核心&#xff0c;通過整合各部門業務系統數據&#xff0c;進行建模與加工&#xff0c;以新數據驅動政府管理效率提升與政務服務能力增強。數據治理則聚焦于解決整體架構問題&#xff0c;確保數據…

postgresq-自定義執行計劃(custom plan)與generic plan(通用執行計劃)

文章目錄 之前寫過一篇關于 PostgreSQL prepare sql的文章&#xff0c;但當時沒有提到generic plan(通用計劃)和custom plan(自定義計劃)這兩個概念。現在將通過舉例介紹這兩個概念。 創建測試表&#xff1a; postgres# create database demo; CREATE DATABASE postgres# \c d…

dockfile 配置 /etc/apt/source.list.d/debian.list 清華鏡像

docker:3.12.7 鏡像使用的是 debian 系統&#xff0c;比 ubuntu 更輕量。debian 系統內&#xff0c;apt 鏡像源列表位于 /etc/apt/source.list.d/debian.list&#xff08;作為對比&#xff0c;ubuntu 的鏡像列表位于 /etc/apt/source.list&#xff0c;二者語法相同&#xff09;…

程序員測試日常小工具

作為一名程序員&#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;通過邏輯判斷、循環等操作實現復雜計算的程序語言。 換…

深入淺出:AWT事件監聽器及其應用

前言 在Java的GUI編程中&#xff0c;事件處理是非常重要的一環。AWT&#xff08;Abstract Window Toolkit&#xff09;框架提供了靈活的事件處理機制&#xff0c;使得開發者能夠響應用戶的操作&#xff0c;例如點擊按鈕、鍵盤輸入、鼠標點擊等。AWT的事件監聽器就是實現這一機…

【Rust自學】8.5. HashMap Pt.1:HashMap的定義、創建、合并與訪問

8.5.0. 本章內容 第八章主要講的是Rust中常見的集合。Rust中提供了很多集合類型的數據結構&#xff0c;這些集合可以包含很多值。但是第八章所講的集合與數組和元組有所不同。 第八章中的集合是存儲在堆內存上而非棧內存上的&#xff0c;這也意味著這些集合的數據大小無需在編…

混合合并兩個pdf文件

混合兩個pdf 1、在線免費交替和混合奇數和偶數PDF頁面2、有什么軟件把兩個 PDF 交叉合并&#xff1f;3、pdfsam本地合并 如何Google翻譯的原文和譯文合并&#xff0c;&#xff08;沉浸式翻譯效果相對較好&#xff09; 1、在線免費交替和混合奇數和偶數PDF頁面 https://deftpd…

Hutool 發送 HTTP 請求的幾種常見寫法

最簡單的 GET 請求&#xff1a; String result HttpUtil.get("https://www.baidu.com");帶參數的 GET 請求&#xff1a; // 方法1: 直接拼接URL參數 String result HttpUtil.get("https://www.baidu.com?name張三&age18");// 方法2: 使用 HashMap…