本文是在上一篇文章乐视三合一体感摄像头Astra pro开发记录1(深度图、彩色图及点云简单显示)的基础上写的。在开学之前终于把界面的基础功能做完了,估计还有一些bug,也没时间继续调了。开学之后得憋论文了。
界面的主要功能还是Astra pro相机的彩色图、深度图以及点云显示。在之前的基础上,完善了一些比如图像和点云的保存、读取,相机的打开、关闭以及参数设置等功能。由于只是追求基本功能的实现,整个项目文件代码量算上注释也就一千行左右。依赖的第三方库有OpenCV(图像处理)、OpenNI(深度图)、PCL(点云处理)和VTK(点云显示),下面放代码:
/**
*@file camera_settings.h
*@brief 相机设置头文件
*/
#ifndef CAMERA_SETTINGS_H
#define CAMERA_SETTINGS_H
#include <QDialog>
#include <QString>
namespace Ui {
class Camera_Settings;
}
/**
* @brief The Camera_Settings class
*/
class Camera_Settings : public QDialog
{
Q_OBJECT
public:
Camera_Settings(QWidget *parent = 0);
~Camera_Settings();
/**
* @brief color_index 彩色摄像头编号
*/
static int color_index;
/**
* @brief color_resolution 彩色摄像头分辨率
*/
static QString color_resolution;
/**
* @brief color_fps 彩色摄像头帧率
*/
static QString color_fps;
/**
* @brief depth_resolution 深度摄像头分辨率
*/
static QString depth_resolution;
/**
* @brief depth_fps 深度摄像头帧率
*/
static QString depth_fps;
/**
* @brief flag_applied 相机设置应用标志
*/
static bool flag_applied;
private slots:
/**
* @brief on_pushButton_apply_clicked 应用相机设置
*/
void on_pushButton_apply_clicked();
private:
Ui::Camera_Settings *ui;
};
#endif // CAMERA_SETTINGS_H
/**
*@file cloud_generate.h
*@brief 点云生成头文件
*/
#ifndef CLOUD_GENERATE_H
#define CLOUD_GENERATE_H
#include "mainwindow.h"
/**
* @brief The Cloud_Generate class
*/
class Cloud_Generate : public QThread
{
private:
/**
* @brief run 重写QThread的run方法
*/
void run();
};
#endif // CLOUD_GENERATE_H
/**
*@file mainwindow.h
*@brief 主窗口头文件
*/
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QVTKWidget.h>
#include <QMessageBox>
#include <QAbstractButton>
#include <QTimer>
#include <QThread>
#include <QString>
#include <QFileDialog>
#include <iostream>
#include <opencv2/opencv.hpp>
#include <pcl/io/pcd_io.h>
#include <pcl/point_types.h>
#include <pcl/visualization/cloud_viewer.h>
#include <OpenNI.h>
#include <vtkAutoInit.h>
#include <vtkRenderWindow.h>
#include "blockingconcurrentqueue.h"
#include "camera_settings.h"
#include "cloud_generate.h"
VTK_MODULE_INIT(vtkRenderingOpenGL2)
VTK_MODULE_INIT(vtkInteractionStyle)
typedef pcl::PointCloud<pcl::PointXYZ> PointCloud;
typedef pcl::PointCloud<pcl::PointXYZRGB> PointCloudRGB;
QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
using namespace moodycamel;
QT_END_NAMESPACE
/**
* @brief The Astra class
*/
class Astra : public QMainWindow
{
Q_OBJECT
public:
explicit Astra(QWidget *parent = 0);
~Astra();
Ui::MainWindow *ui;
private slots:
/**
* @brief save_color_image 保存彩色图
*/
void save_color_image();
/**
* @brief save_depth_image 保存深度图
*/
void save_depth_image();
/**
* @brief save_point_cloud 保存点云
*/
void save_point_cloud();
/**
* @brief open_color_image 打开彩色图
*/
void open_color_image();
/**
* @brief open_depth_image 打开深度图
*/
void open_depth_image();
/**
* @brief open_point_cloud 打开点云
*/
void open_point_cloud();
/**
* @brief close_all 关闭所有文件
*/
void close_all();
/**
* @brief open_camera 打开相机
*/
void open_camera();
/**
* @brief close_camera 关闭相机
*/
void close_camera();
//void set_camera();
/**
* @brief show_color_image 显示彩色图
*/
void show_color_image();
/**
* @brief show_depth_image 显示深度图
*/
void show_depth_image();
/**
* @brief show_point_cloud 显示点云
*/
void show_point_cloud();
/**
* @brief help_guide 使用说明
*/
void help_guide();
/**
* @brief help_about 关于AstraGUI
*/
void help_about();
private:
/**
* @brief camera_settings 相机设置界面类指针
*/
Camera_Settings *camera_settings;
/**
* @brief capture 彩色摄像头
*/
cv::VideoCapture capture;
/**
* @brief device 深度摄像头
*/
openni::Device device;
/**
* @brief depthStream 深度流
*/
openni::VideoStream depthStream;
/**
* @brief depthMode 深度模式
*/
openni::VideoMode depthMode;
/**
* @brief viewer pclviewer窗口
*/
boost::shared_ptr<pcl::visualization::PCLVisualizer> viewer;
/**
* @brief timerColor 彩色图像显示定时器
*/
QTimer *timerColor;
/**
* @brief timerDepth 深度图像显示定时器
*/
QTimer *timerDepth;
/**
* @brief timerCloud 点云显示定时器
*/
QTimer *timerCloud;
/**
* @brief colorMat 彩色图像
*/
cv::Mat colorMat;
/**
* @brief depthMat 深度图像
*/
cv::Mat depthMat;
/**
* @brief flag_depth 深度图采集标志
*/
bool flag_depth = false;
/**
* @brief flag_cloud 点云生成标志
*/
bool flag_cloud =false;
};
#endif // MAINWINDOW_H
/**
*@file camera_settings.cpp
*@brief 相机设置源文件
*/
#include "camera_settings.h"
#include "mainwindow.h"
#include "ui_camera_settings.h"
#include "ui_mainwindow.h"
#include <QDebug>
int Camera_Settings::color_index = 0;
QString Camera_Settings::color_resolution = "640*480";
QString Camera_Settings::color_fps= "30fps";
QString Camera_Settings::depth_resolution = "640*480";
QString Camera_Settings::depth_fps = "30fps";
bool Camera_Settings::flag_applied = false;
Camera_Settings::Camera_Settings(QWidget *parent) :
QDialog(parent),
ui(new Ui::Camera_Settings)
{
ui->setupUi(this);
}
Camera_Settings::~Camera_Settings()
{
delete ui;
}
void Camera_Settings::on_pushButton_apply_clicked()
{
Astra *ptr = (Astra*)parentWidget(); //指向父窗口的指针
color_index = ui->comboBox_color_index->currentIndex();
color_resolution = ui->comboBox_color_resolution->currentText();
depth_resolution = ui->comboBox_depth_resolution->currentText();
if(color_resolution=="640*480" && depth_resolution=="640*480")
{
ptr->ui->stackedWidget->setCurrentIndex(0);
}
else if(color_resolution=="320*240" && depth_resolution=="320*240")
{
ptr->ui->stackedWidget->setCurrentIndex(1);
}
else if(color_resolution == "1280*720")
{
ptr->ui->stackedWidget->setCurrentIndex(2);
}
else
{
QMessageBox *box = new QMessageBox(QMessageBox::Warning,QString::fromLocal8Bit("警告"),QString::fromLocal8Bit("分辨率设置错误!"),QMessageBox::Ok);
if(NULL!=box->button(QMessageBox::Ok))
{
box->button(QMessageBox::Ok)->setText(QString::fromLocal8Bit("确 定"));
}
QTimer::singleShot(3000,box,SLOT(accept()));
box->exec();
return;
}
color_fps = ui->comboBox_color_fps->currentText();
depth_fps = ui->comboBox_depth_fps->currentText();
if(color_resolution != "1280*720" && color_fps != depth_fps)
{
QMessageBox *box = new QMessageBox(QMessageBox::Warning,QString::fromLocal8Bit("警告"),QString::fromLocal8Bit("帧率设置错误!"),QMessageBox::Ok);
if(NULL!=box->button(QMessageBox::Ok))
{
box->button(QMessageBox::Ok)->setText(QString::fromLocal8Bit("确 定"));
}
QTimer::singleShot(3000,box,SLOT(accept()));
box->exec();
return;
}
flag_applied = true;
this->close(); //点击应用后相机设置窗口关闭
}
/**
*@file cloud_generate.cpp
*@brief 点云生成源文件
*/
#include "cloud_generate.h"
extern PointCloud::Ptr cloud;
extern PointCloudRGB::Ptr cloudRGB;
extern BlockingConcurrentQueue<cv::Mat> bcq_colorMat;
extern BlockingConcurrentQueue<cv::Mat> bcq_depthMat;
extern BlockingConcurrentQueue<PointCloud::Ptr> bcq_cloud;
extern BlockingConcurrentQueue<PointCloudRGB::Ptr> bcq_cloudRGB;
void Cloud_Generate::run()
{
float camera_factor, camera_cx, camera_cy, camera_fx, camera_fy; //相机内参
cv::Mat colorMat, depthMat;
while(true)
{
if(bcq_depthMat.try_dequeue(depthMat))
{
if(Camera_Settings::depth_resolution == "640*480")
{
camera_factor = 1;
camera_cx = 311.0;
camera_cy = 244.0;
camera_fx = 593.0;
camera_fy = 588.0;
}
else if(Camera_Settings::depth_resolution == "320*240")
{
camera_factor = 1;
camera_cx = 311.0*0.5;
camera_cy = 244.0*0.5;
camera_fx = 593.0*0.5;
camera_fy = 588.0*0.5;
}
if(bcq_colorMat.try_dequeue(colorMat))
{
cloudRGB->clear(); //重置点云为空
//遍历深度图
for (int i = 0; i < depthMat.rows; ++i)
{
uchar* depthData = depthMat.data + i*depthMat.step;
uchar* colorData = colorMat.data + i*colorMat.step;
for (int j = 0; j < depthMat.cols; ++j)
{
if (depthData == 0)
continue;
pcl::PointXYZRGB point;
//计算点的空间坐标
point.z = double(*(depthData++)) / camera_factor;
point.x = (j - camera_cx) * point.z / camera_fx;
point.y = -(i - camera_cy) * point.z / camera_fy;
//从彩色图像中获取该点的颜色
point.b = *(colorData++);
point.g = *(colorData++);
point.r = *(colorData++);
//把该点加入到点云中
cloudRGB->points.push_back(point);
}
}
bcq_cloudRGB.enqueue(cloudRGB);
}
else
{
cloud->clear(); //重置点云为空
//遍历深度图
for (int i = 0; i < depthMat.rows; ++i)
{
uchar* depthData = depthMat.data + i*depthMat.step;
for (int j = 0; j < depthMat.cols; ++j)
{
if (depthData == 0)
continue;
pcl::PointXYZ point;
//计算点的空间坐标
point.z = double(*(depthData++)) / camera_factor;
point.x = (j - camera_cx) * point.z / camera_fx;
point.y = -(i - camera_cy) * point.z / camera_fy;
//把该点加入到点云中
cloud->points.push_back(point);
}
}
bcq_cloud.enqueue(cloud);
}
}
}
}
/**
*@file main.cpp
*@brief 主函数源文件
*/
#include "mainwindow.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Astra *w = new Astra;
w->show();
return a.exec();
}
/**
*@file mainwindow.cpp
*@brief 主窗口源文件
*/
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QDebug>
/**
* @brief cloud 无色点云
*/
PointCloud::Ptr cloud(new PointCloud);
/**
* @brief cloudRGB 彩色点云
*/
PointCloudRGB::Ptr cloudRGB(new PointCloudRGB);
/**
* @brief bcq_colorMat colorMat阻塞队列
*/
BlockingConcurrentQueue<cv::Mat> bcq_colorMat;
/**
* @brief bcq_depthMat depthMat阻塞队列
*/
BlockingConcurrentQueue<cv::Mat> bcq_depthMat;
/**
* @brief bcq_cloud cloud阻塞队列
*/
BlockingConcurrentQueue<PointCloud::Ptr> bcq_cloud;
/**
* @brief bcq_cloudRGB cloudRGB阻塞队列
*/
BlockingConcurrentQueue<PointCloudRGB::Ptr> bcq_cloudRGB;
Astra::Astra(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
connect(ui->action_save_colorMat, &QAction::triggered, [=]{this->save_color_image();});
connect(ui->action_save_depthMat, &QAction::triggered, [=]{this->save_depth_image();});
connect(ui->action_save_pointcloud, &QAction::triggered, [=]{this->save_point_cloud();});
connect(ui->action_open_colorMat, &QAction::triggered, [=]{this->open_color_image();});
connect(ui->action_open_depthMat, &QAction::triggered, [=]{this->open_depth_image();});
connect(ui->action_open_pointcloud, &QAction::triggered, [=]{this->open_point_cloud();});
connect(ui->action_close_all, &QAction::triggered, [=]{this->close_all();});
connect(ui->action_exit, &QAction::triggered, [=]{this->close();});
connect(ui->action_open_camera, &QAction::triggered, [=]{this->open_camera();});
connect(ui->action_close_camera, &QAction::triggered, [=]{this->close_camera();});
//connect(ui->action_set_camera, &QAction::triggered, [=]{this->set_camera();});
connect(ui->action_help_guide, &QAction::triggered, [=]{this->help_guide();});
connect(ui->action_help_about, &QAction::triggered, [=]{this->help_about();});
openni::OpenNI::initialize();
}
Astra::~Astra()
{
delete ui;
openni::OpenNI::shutdown();
}
void Astra::save_color_image()
{
//选择路径
QString fileName = QFileDialog::getSaveFileName(this,QString::fromLocal8Bit("保存彩色图")," ",tr(" (*.bmp *.jpg *.png)"));
//文件名为空
if (fileName.isEmpty())
{
QMessageBox *box = new QMessageBox(QMessageBox::Warning,QString::fromLocal8Bit("警告"),QString::fromLocal8Bit("文件名为空!"),QMessageBox::Ok);
if(NULL!=box->button(QMessageBox::Ok))
{
box->button(QMessageBox::Ok)->setText(QString::fromLocal8Bit("确 定"));
}
QTimer::singleShot(3000,box,SLOT(accept()));
box->exec();
return;
}
//彩色图像为空
if (colorMat.empty())
{
QMessageBox *box = new QMessageBox(QMessageBox::Warning,QString::fromLocal8Bit("警告"),QString::fromLocal8Bit("彩色图保存错误!"),QMessageBox::Ok);
if(NULL!=box->button(QMessageBox::Ok))
{
box->button(QMessageBox::Ok)->setText(QString::fromLocal8Bit("确 定"));
}
QTimer::singleShot(3000,box,SLOT(accept()));
box->exec();
return;
}
std::string filename(fileName.toLocal8Bit().data()); //支持带中文路径的读取
cv::imwrite(filename, colorMat); //将彩色图像写入磁盘
}
void Astra::save_depth_image()
{
//选择路径
QString fileName = QFileDialog::getSaveFileName(this,QString::fromLocal8Bit("保存深度图")," ",tr(" (*.bmp *.jpg *.png)"));
//文件名为空
if (fileName.isEmpty())
{
QMessageBox *box = new QMessageBox(QMessageBox::Warning,QString::fromLocal8Bit("警告"),QString::fromLocal8Bit("文件名为空!"),QMessageBox::Ok);
if(NULL!=box->button(QMessageBox::Ok))
{
box->button(QMessageBox::Ok)->setText(QString::fromLocal8Bit("确 定"));
}
QTimer::singleShot(3000,box,SLOT(accept()));
box->exec();
return;
}
//深度图为空
if (depthMat.empty())
{
QMessageBox *box = new QMessageBox(QMessageBox::Warning,QString::fromLocal8Bit("警告"),QString::fromLocal8Bit("深度图保存错误!"),QMessageBox::Ok);
if(NULL!=box->button(QMessageBox::Ok))
{
box->button(QMessageBox::Ok)->setText(QString::fromLocal8Bit("确 定"));
}
QTimer::singleShot(3000,box,SLOT(accept()));
box->exec();
return;
}
std::string filename(fileName.toLocal8Bit().data()); //支持带中文路径的读取
cv::imwrite(filename, depthMat); //将深度图像写入磁盘
}
void Astra::save_point_cloud()
{
//选择路径
QString fileName = QFileDialog::getSaveFileName(this,QString::fromLocal8Bit("保存点云")," ",tr(" (*.pcd)"));
//文件名为空
if (fileName.isEmpty())
{
QMessageBox *box = new QMessageBox(QMessageBox::Warning,QString::fromLocal8Bit("警告"),QString::fromLocal8Bit("文件名为空!"),QMessageBox::Ok);
if(NULL!=box->button(QMessageBox::Ok))
{
box->button(QMessageBox::Ok)->setText(QString::fromLocal8Bit("确 定"));
}
QTimer::singleShot(3000,box,SLOT(accept()));
box->exec();
return;
}
//点云数据为空
if (cloudRGB->size()==0 && cloud->size()==0)
{
QMessageBox *box = new QMessageBox(QMessageBox::Warning,QString::fromLocal8Bit("警告"),QString::fromLocal8Bit("点云保存错误!"),QMessageBox::Ok);
if(NULL!=box->button(QMessageBox::Ok))
{
box->button(QMessageBox::Ok)->setText(QString::fromLocal8Bit("确 定"));
}
QTimer::singleShot(3000,box,SLOT(accept()));
box->exec();
return;
}
std::string filename(fileName.toLocal8Bit().data()); //支持带中文路径的读取
if(flag_cloud)
pcl::io::savePCDFileBinary(filename,*cloud); //将无色点云写入磁盘
else
pcl::io::savePCDFileBinary(filename,*cloudRGB); //将彩色点云写入磁盘
}
void Astra::open_color_image()
{
ui->stackedWidget->setCurrentIndex(0);
//选择路径
QString fileName = QFileDialog::getOpenFileName(this,QString::fromLocal8Bit("打开彩色图"),"home",tr("(*.bmp *.jpg *.png )"));
//文件名为空
if (fileName.isEmpty())
{
QMessageBox *box = new QMessageBox(QMessageBox::Warning,QString::fromLocal8Bit("警告"),QString::fromLocal8Bit("文件名为空!"),QMessageBox::Ok);
if(NULL!=box->button(QMessageBox::Ok))
{
box->button(QMessageBox::Ok)->setText(QString::fromLocal8Bit("确 定"));
}
QTimer::singleShot(3000,box,SLOT(accept()));
box->exec();
return;
}
std::string filename(fileName.toLocal8Bit().data()); //支持带中文路径的读取
cv::Mat colorMat = cv::imread(filename,-1); //-1表示读取原始数据
//彩色图像为空或通道数不为3
if(colorMat.empty() || colorMat.channels()!=3)
{
QMessageBox *box = new QMessageBox(QMessageBox::Warning,QString::fromLocal8Bit("警告"),QString::fromLocal8Bit("彩色图打开错误!"),QMessageBox::Ok);
if(NULL!=box->button(QMessageBox::Ok))
{
box->button(QMessageBox::Ok)->setText(QString::fromLocal8Bit("确 定"));
}
QTimer::singleShot(3000,box,SLOT(accept()));
box->exec();
return;
}
//缩放图像使其适应窗口大小
cv::resize(colorMat,colorMat,cv::Size(ui->label_color_1->width(),ui->label_color_1->height()));
//cv::Mat转QImage
QImage image_color = QImage((const uchar*)colorMat.data,colorMat.cols,colorMat.rows,QImage::Format_RGB888).rgbSwapped();
//更新ui
ui->label_color_1->clear();
ui->label_color_1->setPixmap(QPixmap::fromImage(image_color));
}
void Astra::open_depth_image()
{
ui->stackedWidget->setCurrentIndex(0);
//选择路径
QString fileName = QFileDialog::getOpenFileName(this,QString::fromLocal8Bit("打开深度图"),"home",tr("(*.bmp *.jpg *.png )"));
//文件名为空
if (fileName.isEmpty())
{
QMessageBox *box = new QMessageBox(QMessageBox::Warning,QString::fromLocal8Bit("警告"),QString::fromLocal8Bit("文件名为空!"),QMessageBox::Ok);
if(NULL!=box->button(QMessageBox::Ok))
{
box->button(QMessageBox::Ok)->setText(QString::fromLocal8Bit("确 定"));
}
QTimer::singleShot(3000,box,SLOT(accept()));
box->exec();
return;
}
std::string filename(fileName.toLocal8Bit().data()); //支持带中文路径的读取
cv::Mat depthMat = cv::imread(filename,-1); //-1表示读取原始数据
//深度图像为空或通道数不为1
if(depthMat.empty() || depthMat.channels()!=1)
{
QMessageBox *box = new QMessageBox(QMessageBox::Warning,QString::fromLocal8Bit("警告"),QString::fromLocal8Bit("深度图打开错误!"),QMessageBox::Ok);
if(NULL!=box->button(QMessageBox::Ok))
{
box->button(QMessageBox::Ok)->setText(QString::fromLocal8Bit("确 定"));
}
QTimer::singleShot(3000,box,SLOT(accept()));
box->exec();
return;
}
//缩放图像使其适应窗口大小
cv::resize(depthMat,depthMat,cv::Size(ui->label_depth_1->width(),ui->label_depth_1->height()));
//cv::Mat转QImage
QImage image_depth = QImage((const uchar*)depthMat.data,depthMat.cols,depthMat.rows,QImage::Format_Grayscale8).rgbSwapped();
//更新ui
ui->label_depth_1->clear();
ui->label_depth_1->setPixmap(QPixmap::fromImage(image_depth));
}
void Astra::open_point_cloud()
{
ui->stackedWidget->setCurrentIndex(0);
//选择路径
QString fileName = QFileDialog::getOpenFileName(this,QString::fromLocal8Bit("打开点云"),"home",tr("(*.pcd)"));
//文件名为空
if (fileName.isEmpty())
{
QMessageBox *box = new QMessageBox(QMessageBox::Warning,QString::fromLocal8Bit("警告"),QString::fromLocal8Bit("文件名为空!"),QMessageBox::Ok);
if(NULL!=box->button(QMessageBox::Ok))
{
box->button(QMessageBox::Ok)->setText(QString::fromLocal8Bit("确 定"));
}
QTimer::singleShot(3000,box,SLOT(accept()));
box->exec();
return;
}
std::string filename(fileName.toLocal8Bit().data()); //支持带中文路径的读取
// PointCloud::Ptr cloud(new PointCloud);
// PointCloudRGB::Ptr cloudRGB(new PointCloudRGB);
//点云加载失败
if(pcl::io::loadPCDFile(filename,*cloudRGB)==-1)
{
QMessageBox *box = new QMessageBox(QMessageBox::Warning,QString::fromLocal8Bit("警告"),QString::fromLocal8Bit("点云打开错误!"),QMessageBox::Ok);
if(NULL!=box->button(QMessageBox::Ok))
{
box->button(QMessageBox::Ok)->setText(QString::fromLocal8Bit("确 定"));
}
QTimer::singleShot(3000,box,SLOT(accept()));
box->exec();
return;
}
//判断点云是否带有rgb信息
if(cloudRGB->points[0].r)
{
viewer.reset(new pcl::visualization::PCLVisualizer("viewer", false));
viewer->addPointCloud(cloudRGB);
ui->qvtkWidget_1->SetRenderWindow(viewer->getRenderWindow());
ui->qvtkWidget_1->update();
}
else
{
pcl::io::loadPCDFile(filename,*cloud);
viewer.reset(new pcl::visualization::PCLVisualizer("viewer", false));
viewer->addPointCloud(cloud);
ui->qvtkWidget_1->SetRenderWindow(viewer->getRenderWindow());
ui->qvtkWidget_1->update();
}
}
void Astra::close_all()
{
//清除彩色图像显示
ui->label_color_1->clear();
ui->label_color_2->clear();
ui->label_color_3->clear();
//清除深度图像显示
ui->label_depth_1->clear();
ui->label_depth_2->clear();
if(cloud->points.size()||cloudRGB->points.size())
{
//移除viewer中的点云
viewer->removeAllPointClouds();
//清除点云显示
ui->qvtkWidget_1->update();
ui->qvtkWidget_2->update();
}
}
void Astra::open_camera()
{
//弹出相机设置界面
Camera_Settings camera_settings(this);
camera_settings.setWindowTitle(QString::fromLocal8Bit("相机设置"));
camera_settings.exec();
//如果没有点击相机设置界面中的应用按钮则什么都不做
if(!Camera_Settings::flag_applied)
{
return;
}
//彩色摄像头或深度摄像头已被打开
if(capture.isOpened() || flag_depth)
{
close_camera(); //先调用关闭相机的函数
QMessageBox *box = new QMessageBox(QMessageBox::Warning,QString::fromLocal8Bit("警告"),QString::fromLocal8Bit("摄像头打开出错!"),QMessageBox::Ok);
if(NULL!=box->button(QMessageBox::Ok))
{
box->button(QMessageBox::Ok)->setText(QString::fromLocal8Bit("确 定"));
}
QTimer::singleShot(3000,box,SLOT(accept()));
box->exec();
return;
}
//如果彩色图分辨率不为1280*720则开启深度摄像头
if(Camera_Settings::color_resolution != "1280*720")
{
if (device.open(openni::ANY_DEVICE) != openni::STATUS_OK)
{
QMessageBox *box = new QMessageBox(QMessageBox::Warning,QString::fromLocal8Bit("警告"),QString::fromLocal8Bit("深度摄像头打开出错!"),QMessageBox::Ok);
if(NULL!=box->button(QMessageBox::Ok))
{
box->button(QMessageBox::Ok)->setText(QString::fromLocal8Bit("确 定"));
}
QTimer::singleShot(3000,box,SLOT(accept()));
box->exec();
goto color;
}
//创建深度流
depthStream.create(device, openni::SENSOR_DEPTH);
//配置深度流的模式
std::string depth_resolution = Camera_Settings::depth_resolution.toStdString();
int DEPTH_COLS = std::stoi(depth_resolution.substr(0,depth_resolution.size()-4));
int DEPTH_ROWS = std::stoi(depth_resolution.substr(depth_resolution.size()-3));
std::string depth_fps = Camera_Settings::depth_fps.toStdString();
int DEPTH_FPS = std::stoi(depth_fps.substr(0,depth_fps.size()-3));
depthMode.setResolution(DEPTH_COLS, DEPTH_ROWS);
depthMode.setPixelFormat(openni::PIXEL_FORMAT_DEPTH_1_MM);
depthMode.setFps(DEPTH_FPS);
depthStream.setVideoMode(depthMode);
//打开深度流
depthStream.start();
//设置深度图像采集标志位
flag_depth = true;
//创建并开启深度图像显示定时器
timerDepth = new QTimer(this);
timerDepth-> setTimerType(Qt::PreciseTimer);
connect(timerDepth, SIGNAL(timeout()), this, SLOT(show_depth_image()));
timerDepth->start(100);
}
//打开彩色摄像头
color:
//彩色摄像头打开失败
if(capture.open(Camera_Settings::color_index) != true)
{
QMessageBox *box = new QMessageBox(QMessageBox::Warning,QString::fromLocal8Bit("警告"),QString::fromLocal8Bit("彩色摄像打开出错!"),QMessageBox::Ok);
if(NULL!=box->button(QMessageBox::Ok))
{
box->button(QMessageBox::Ok)->setText(QString::fromLocal8Bit("确 定"));
}
QTimer::singleShot(3000,box,SLOT(accept()));
box->exec();
return;
}
//配置rgb流的模式
std::string color_resolution = Camera_Settings::color_resolution.toStdString();
int COLOR_COLS = std::stoi(color_resolution.substr(0,color_resolution.size()-4));
int COLOR_ROWS = std::stoi(color_resolution.substr(color_resolution.size()-3));
std::string color_fps = Camera_Settings::color_fps.toStdString();
int COLOR_FPS = std::stoi(color_fps.substr(0,color_fps.size()-3));
capture.set(cv::CAP_PROP_FRAME_WIDTH, COLOR_COLS);//宽度
capture.set(cv::CAP_PROP_FRAME_HEIGHT, COLOR_ROWS);//高度
capture.set(cv::CAP_PROP_FPS, COLOR_FPS);//帧率 帧/秒
//创建并开启彩色图像显示定时器
timerColor = new QTimer(this);
timerColor-> setTimerType(Qt::PreciseTimer);
connect(timerColor, SIGNAL(timeout()), this, SLOT(show_color_image()));
timerColor->start(100);
//创建并开启点云显示定时器
if(flag_depth)
{
timerCloud = new QTimer(this);
timerCloud-> setTimerType(Qt::PreciseTimer);
connect(timerCloud, SIGNAL(timeout()), this, SLOT(show_point_cloud()));
timerCloud->start(100);
}
//启动点云生成线程
Cloud_Generate *task = new Cloud_Generate;
task->start();
}
void Astra::close_camera()
{
//如果相机未开启则直接返回
if(!capture.isOpened())
{
return;
}
//关闭彩色图像显示定时器
timerColor->stop();
//关闭彩色摄像头
capture.release();
//清除彩色图像显示
ui->label_color_1->clear();
ui->label_color_2->clear();
ui->label_color_3->clear();
if(Camera_Settings::color_resolution != "1280*720" && flag_depth)
{
//关闭深度图像显示定时器
timerDepth->stop();
//关闭点云显示定时器
timerCloud->stop();
//关闭深度流
depthStream.stop();
depthStream.destroy();
//关闭深度摄像头
device.close();
//清除深度图像显示
ui->label_depth_1->clear();
ui->label_depth_2->clear();
//清除点云
cloud->clear();
cloudRGB->clear();
//移除viewer中的点云
viewer->removeAllPointClouds();
//清除点云显示
ui->qvtkWidget_1->update();
ui->qvtkWidget_2->update();
//设置深度图像采集标志
flag_depth = false;
}
//设置相机设置应用标志位
Camera_Settings::flag_applied = false;
}
//void Astra::set_camera()
//{
camera_settings = new Camera_Settings(this);
camera_settings->setWindowTitle(QString::fromLocal8Bit("相机设置"));
camera_settings->show();
// Camera_Settings camera_settings(this);
// camera_settings.setWindowTitle(QString::fromLocal8Bit("相机设置"));
// camera_settings.exec();
//}
void Astra::show_color_image()
{
capture >> colorMat;
//彩色图像采集为空
if(colorMat.empty())
{
timerColor->stop();
QMessageBox *box = new QMessageBox(QMessageBox::Warning,QString::fromLocal8Bit("警告"),QString::fromLocal8Bit("彩色图采集错误!"),QMessageBox::Ok);
if(NULL!=box->button(QMessageBox::Ok))
{
box->button(QMessageBox::Ok)->setText(QString::fromLocal8Bit("确 定"));
}
QTimer::singleShot(3000,box,SLOT(accept()));
box->exec();
return;
}
//彩色图和深度图呈镜像关系,所以要翻转一下
cv::flip(colorMat, colorMat, 1);
//cv::Mat转QImage
QImage image_color = QImage((const uchar*)colorMat.data, colorMat.cols, colorMat.rows, QImage::Format_RGB888).rgbSwapped();
//依据不同分辨率显示不同大小的彩色图像
if(Camera_Settings::color_resolution == "640*480")
{
ui->label_color_1->clear();
ui->label_color_1->setPixmap(QPixmap::fromImage(image_color));
}
else if(Camera_Settings::color_resolution == "320*240")
{
ui->label_color_2->clear();
ui->label_color_2->setPixmap(QPixmap::fromImage(image_color));
}
else if(Camera_Settings::color_resolution == "1280*720")
{
ui->label_color_3->clear();
ui->label_color_3->setPixmap(QPixmap::fromImage(image_color));
}
bcq_colorMat.enqueue(colorMat);
cv::waitKey(10); //防止程序卡住
}
void Astra::show_depth_image()
{
openni::VideoFrameRef depth_frame;
int iMaxDepth = depthStream.getMaxPixelValue();
openni::VideoStream* pstream = &depthStream;
int changedStreamDummy;
openni::Status ops = openni::OpenNI::waitForAnyStream(&pstream, 1, &changedStreamDummy, 50); //等待一帧
ops = depthStream.readFrame(&depth_frame);
if (ops != openni::STATUS_OK)
{
timerDepth->stop();
return;
}
//获取深度帧数据
auto depth = depth_frame.getData();
auto depthWidth = depth_frame.getWidth();
auto depthHeight = depth_frame.getHeight();
//处理并渲染深度帧数据
cv::Mat rawMat(depthHeight, depthWidth, CV_16UC1, (void*)depth);
rawMat.convertTo(depthMat, CV_8UC1, 255.0 / iMaxDepth);
//深度图采集为空
if(depthMat.empty())
{
timerDepth->stop();
QMessageBox *box = new QMessageBox(QMessageBox::Warning,QString::fromLocal8Bit("警告"),QString::fromLocal8Bit("深度图采集错误!"),QMessageBox::Ok);
if(NULL!=box->button(QMessageBox::Ok))
{
box->button(QMessageBox::Ok)->setText(QString::fromLocal8Bit("确 定"));
}
QTimer::singleShot(3000,box,SLOT(accept()));
box->exec();
return;
}
//cv::Mat转QImage
QImage image_depth = QImage((const uchar*)depthMat.data, depthMat.cols,depthMat.rows, QImage::Format_Grayscale8).rgbSwapped();
//依据不同分辨率显示不同大小的深度图像
if(Camera_Settings::depth_resolution == "640*480")
{
ui->label_depth_1->clear();
ui->label_depth_1->setPixmap(QPixmap::fromImage(image_depth));
}
else if(Camera_Settings::depth_resolution == "320*240")
{
ui->label_depth_2->clear();
ui->label_depth_2->setPixmap(QPixmap::fromImage(image_depth));
}
bcq_depthMat.enqueue(depthMat);
cv::waitKey(10); //防止程序卡住
}
void Astra::show_point_cloud()
{
if(bcq_cloudRGB.try_dequeue(cloudRGB))
{
viewer.reset(new pcl::visualization::PCLVisualizer("viewer", false));
viewer->addPointCloud(cloudRGB);
flag_cloud = false;
}
else if(bcq_cloud.try_dequeue(cloud))
{
viewer.reset(new pcl::visualization::PCLVisualizer("viewer", false));
viewer->addPointCloud(cloud);
flag_cloud = true;
}
//依据不同分辨率显示点云
if(Camera_Settings::depth_resolution == "640*480")
{
ui->qvtkWidget_1->SetRenderWindow(viewer->getRenderWindow());
ui->qvtkWidget_1->update();
}
else if(Camera_Settings::depth_resolution == "320*240")
{
ui->qvtkWidget_2->SetRenderWindow(viewer->getRenderWindow());
ui->qvtkWidget_2->update();
}
}
void Astra::help_guide()
{
QMessageBox *box = new QMessageBox(QMessageBox::Information, QString::fromLocal8Bit("使用说明"),
QString::fromLocal8Bit(" 本软件提供了Astra相机的深度图,\n 彩色图以及点云显示和保存功能。\n\n"
" 由于开发周期短和本人能力有限,\n 其他功能还尚未完善,敬请谅解。"),
QMessageBox::Ok);
if(NULL!=box->button(QMessageBox::Ok))
{
box->button(QMessageBox::Ok)->setText(QString::fromLocal8Bit("确 定"));
}
box->setStyleSheet("QLabel{min-width: 240px;min-height: 180px;}");
box->exec();
}
void Astra::help_about()
{
QMessageBox::about(NULL, QString::fromLocal8Bit("关于AstraGUI"), "by taify");
}
代码写的比较糙,但功能基本上都实现了。本来想用cmake管理工程,但发现vtk库和pcl库写在CMakeLists里面不知道为啥似乎会有冲突,两个库单独用好像都可以,只好还是用Qt自带的.pro文件来配置了。完整工程文件已上传GitHub。
另外,我把win10下Release-x64编译打包的应用程序放到百度网盘链接(链接:https://pan.baidu.com/s/160MyTBiNr0N6WbSjNLUZpw提取码:oqv3 )里面了,在另外一台win10电脑上测试过了也能凑合着用。图像如果有读不出来的情况把相机拔下来重插一下就好了。