0、背景
帮我室友做一个高光谱处理系统。
要求:
- GUI界面。
- 读取Matlab 的mat类型数据,具体为高光谱,三维张量。
- 在GUI界面显示当前张量,超分辨算法处理后的张量,进行对比。
思路:
- Qt 进行GUI编程。
- 加载Matlab的库 ,以读取mat类型数据。
麻烦:
- 环境搭建。
- 网上很多教程写的不清晰,尤其是mat的读取,有些是自己在Qt中创建二维mxArray,不是直接读取从Matlab中保存下来的mat文件,实用性不高。
- 没有三维张量等mat的读取示例。
1、Qt下载
在Qt官网下载,需要账号,下载开源版,否则要用钱买。
2、Matlab下载
百度Matlab破解版安装教程,安装。
3、Qt环境配置
(1)设置环境变量。
此电脑 -> 高级系统设置 -> 环境变量 -> 系统变量,Path -> 添加E:\Matlab\bin\win64
-> 确定。
Note:路径是你自己Matlab安装的路径。下面的图片看不清点开来看。
(2)设置Qt的依赖文件。
设置你的项目的 .pro文件,在最后一行加上下面这几行代码,添加mat的依赖库。
INCLUDEPATH += E:/Matlab/extern/include/
LIBS += -LE:/Matlab/extern/lib/win64/microsoft/ -llibeng
LIBS += -LE:/Matlab/extern/lib/win64/microsoft/ -llibmx
LIBS += -LE:/Matlab/extern/lib/win64/microsoft/ -llibmat
其中,注意你自己的Matlab安装位置,像我是安装在E:/Matlab/
,不要搞错了。
(3)设置头文件。
程序中依赖的部分头文件如下:
#include< stdio.h>
#include< stdlib.h>
#include"mat.h"
#include"matrix.h"
4、mat的读取
(1)mat的格式
我这里用到的高光谱是一个小的切片,因此尺寸较小,存储为mat格式,且是一个三维张量,如下图,Img数据,维度为
38
×
38
×
103
38 \times 38 \times 103
38×38×103,即宽38,高38,通道103。数据类型为single,数据类型很重要。
(2)Matlab的API,很重要。
1. matOpen:打开.mat文件。如果想打开文件“data.mat”,则MATFile pMF = matOpen(“data.mat”,”r”);
2. matClose:关闭*.mat文件。如果想关闭文件“data.mat”,则matClose(pMF);
3. mxArray类型:Matlab底层存放数据的数组,具体怎么存放的,还需自己去琢磨;
4. matGetVariable:获取变量,对应于上图中的“Img”。如果想获取变量“Img”,则mxArray* pArray = matGetVariable(pMF,”Img”);
5. matGetPr:获取变量的实部数据。如果想获取变量“pArray ”的实部,则double* pAReal = mxGetPr(pArray);
类似的还有float* pAReal=mxGetSingles(Array),需要根据mat存储的类型来读取。
6. mxGetNumberOfDimensions:返回变量的维度。如果想获取变量“pArray”的维度,则int D = mxGetNumberOfDimensions(pArray)
7. mxGetM:获取变量的行数。如果想获取变量“Img”的行数,则 int M = mxGetM(Img);此时M = 38;
8. mxGetN:获取变量在mxArray中的列数。
(3)代码,注释写的很详细。
void MainWindow::getInputMat(int Frame) //将输入数据mat加载到内存中
{
//m_InputPath下存储了文件夹里所有的高光谱,我们读取第Frame帧
QString MatPath=m_InputPath[Frame];
MATFile *Mat=matOpen(MatPath.toLatin1().data(),"r"); //读取mat
if(Mat==NULL)
return;
mxArray *Array=matGetVariable(Mat,"Img"); //绑定数据,由(1)可以看到数据包名字为"Img"
int DimNum=mxGetNumberOfDimensions(Array); //获取张量维度的数目,这里是三维张量,因此DimNum=3
auto MatSize=mxGetDimensions(Array); //获取每个维度的大小,是个数组
//std::cout<<MatSize[0]<<","<<MatSize[1]<<","<<MatSize[2]<<std::endl; //输出:38,38,103
m_InputTensorWidth=MatSize[0]; //记录输入张量的尺寸
m_InputTensorHeight=MatSize[1];
m_InputTensorChanel=MatSize[2];
//判断数据类型,可以不要,我写出来是让大家一定要注意数据类型,调用对应的函数,否则读出来是错的
bool isDouble=mxIsDouble(Array); //false
bool isComplex=mxIsComplex(Array); //false
bool isChar=mxIsChar(Array); //false
bool isSingle=mxIsSingle(Array); //true,在(1)中已经强调了数据类型
bool isEmpty=mxIsEmpty(Array); //false
//读取数据
int Cnt=0;
auto pr=mxGetSingles(Array);//根据数据类型single,调用该函数,很重要
for(int k=0;k<m_InputTensorChanel;k++) //通道
{
for(int j=0;j<m_InputTensorHeight;j++) //高度
{
for(int i=0;i<m_InputTensorWidth;i++) //宽度
{
double value=pr[Cnt++];
//m_InputTensor是自己创建的一个m_InputTensorWidth x m_InputTensorHeight x m_InputTensorChanel三维的vector,
m_InputTensor[i][j][k]=value; //至此就将mat里面的数据读进来了
}
}
}
matClose(Mat);
}
以上各变量值的截图:
5、mat的显示
我们使用一个QLabel来显示高光谱,每次都选择好要显示的通道。
//选出第Frame帧,并展示其Chanel通道,高光谱数据存在三维数组Tensor中
void MainWindow::showInputImg(int Frame,int Chanel,std::vector<std::vector<std::vector<int>>> &Tensor,QLabel* label) //更新图片
{
//m_InputPath下存储了文件夹里所有的高光谱,我们选出第Frame帧,并展示其Chanel通道
if(Frame>=0 & Frame<m_InputPath.size() && Chanel>=0 && Chanel<m_InputTensorChanel)
{
//获取当前通道的图像
QImage *Img=new QImage(m_InputTensorWidth,m_InputTensorHeight,QImage::Format_ARGB32);
for(int y=0;y<m_InputTensorHeight;y++)
{
for(int x=0;x<m_InputTensorWidth;x++)
{
int Value=Tensor[y][x][Chanel];
Img->setPixel(x,y,qRgb(Value,Value,Value));
}
}
//缩放到展示的窗口大小
QImage *ImgScaled=new QImage();
*ImgScaled=Img->scaled(label->width(),label->height()); // ,Qt::KeepAspectRatio
//展示图片
label->setPixmap(QPixmap::fromImage(*ImgScaled));
//析构
delete Img;
delete ImgScaled;
}else{
return;
}
}
效果如下:
6、参考链接
[1] SnailTyan:Matlab/C++混合编程之mxArray的访问
[2] 尘埃飞舞:QT Creator使用matlab库文件读取.mat文件数据
[3] it_xiangqiang:QT的QImage类的使用