QT5.14读取raw原生图像数据并快速的保存到execl中

需求概要

一张raw原生图像,图像数据是按照16bit的方式进行存储每一个像素点的值,其中有高14bit是有效位,raw图像的格式是3000*2000;
由于要查看这张图象的数据值,raw图像需要特定的软件打开,比较麻烦,也不容易保存数据,所以现在需要对raw图像的数据读取出来,然后将其数据保存到我们比较常见的execl表格中。

涉及知识点

根据这个需求,由于在windows环境下查看的比较多,所以选择QT进行开发一个小的软件,通过选择读取的raw图像,然后确定该图像的参数:大小size;每个像素点所占内存bit位,有效数据bit位。
1.大小size:为什么要有这个size设定呢?因为raw原生图像的数据存储特性是顺序存储的,没有行和列的概念,相当于一块内存,内阁bit顺序存储。
2.像素点所占bit位:顾名思义,就是一个像素值,在内存中用几个bit位进行存储,有的10bit,有的12bit,有的14bit,最多的是16bit,这和感光芯片的内部抓图的程序所设置的有关。
3.有效数据bit位:就是这每个像素点的存储bit中,有多少是有效的数据,也就是真是的数据。

此外还要完成一下这几点的问题:

QT如何读取文件?
QT如何将读取的数据保存到execl表格中?
QT如何快速的将几百万的数据保存到execl表格中?

整体的全局变量

int wight_data = 3000;
int hight_data = 2000;
QString file_name = ""; 
QString formal_data = "16bit"; //数据所占bit位 
QString formal_data_pixel = "RAW14"; //真实数据的格式 低位无效补零
//execl表格的全局变量初始化定义
QAxObject *pApplication; //execl文件
QAxObject *workbooks; //execl文件的workbooks
QAxObject *workbook; //workbooks的workbook
QAxObject *worksheets; //workbook的worksheets
QAxObject *worksheet; //worksheets的worksheet
QAxObject *pRange; //worksheet的单元格

QT如何读取文件?

函数fopen()进行打开文件
函数fread()进行读取文件数据
代码

void MainWindow::get_raw_file(QString file_raw_path,int formal_code,int **B)
{
	unsigned short *ptr_data = new (unsigned short); //新建一个数据存放对象 unsigned short 占两个字节 16bit 
	QString file_name = QFileDialog::getOpenFileName(this,"选择RAW文件",“./); //从当前文件路径查找选择你要转换的raw原生图像
	QByteArray by = file_name.toLatin1(); //该原生图像路径QString进行转换为QByteArray 
	char * file_name_path = by.data();       //将QByteArray转换为char * 形式 
	FILE *fp = fopen(file_name_path,"rb");  //打开选择的raw图像,以 只读模型打开 bit的内容
	for (int i = 0;i < hight_data;i++)  //  size的尺寸控制 改为行列形式  hight_data为行数 2000
	{
		for(int j = 0;j<wight_data;j++)  //wight_data为列数 3000
		{
			fread(ptr_data;1,2,fp);   //读取的数据存放的指针  每次读取的字节数  读取次数  要读的文件指针 (本次是每次读取一个字节,读取两次,因为一个字节占8bit 两次是16bit )
			data_pixel = *ptr_data >> formal_code;  //读取到的二进制数据进行取出真是数据右移几位,因为本次是高14bit有效,所以右移两位
			                         // formal_code 是根据上位机选择几位有效数据进行编码的右移几位的方式
			B[i[j] = data_pixel; //将处理后的数据放在一个新开辟的二维数组中,以行列的形式存放
		}
	}
	fclose(fp);//关闭打开的raw文件
	delete ptr_data; //与new对应 删除这个对象,防止内存泄露和出现野指针的可能
}

以上代码就是打开文件和读取文件的真实bit数据 然后存放在B这个二维数组中。
下面就开始将这些输入写道execl表格中。

QT如何将读取的数据保存到execl表格中?

这个问题就涉及到如何将数据保存到execl表格中了。对于这个问题,有以下几个基础函数需要提前编写出来:
1.确定execl表格名字
根据当前时间来确定execl文件名字
代码:

QString MainWindow::file_path_name_get()
{
	QDateTime qdata = QDateTime::currentDateTime();
	QString datetime = qdata.toString("yyyy-MM--dd_hhmmss");//年-月-日_时分秒
	QString file_name_execl = QDir::currentPath() +"/"+datetime+".xlsx"; //程序根目录下的路径下 文件名字是年月日时分秒 后缀名是xlsx
	return file_name_execl;
}

2.新建execl表格

void MainWindow::newExecl(const QString &fileName)
{
	//线程开始的时候要初始化COM库 要不然容易线程出错
	HRESULT r = OleInitialize(0);
	if(r != S_OK && r != S_FALSE)
	{
		qWarning("QT:could not initialize OLE(error %x)\n",(unsigned int)r);
	}
	pApplication = new QAxObject(this);
	pApplication->setControl("Execl.Application");   //连接Execl控件
	if(pApplication == NULL){
		qWarning("pApplication\n");
		qDebug() << "新建失败" << endl;
	}
	pApplication->dynamicCall("SetVisible(bool)", false);//false不显示窗体
	pApplication->setProperty("DisplayAlerts", false);//不显示任何警告信息。
    pWorkBooks = pApplication->querySubObject("Workbooks"); //获取工作薄集合
    pWorkBook = pApplication->querySubObject("ActiveWorkBook");
    QFile file(fileName);
    if (!file.exists()) //如果文件不存在 就开始新建该文件
    {
    	qDebug() << "文件开始新建" << endl;
        pWorkBooks->dynamicCall("Add");
        pWorkBook = pApplication->querySubObject("ActiveWorkBook");
    }
    else{
		qDebug() << "新建前文件已存在" << endl;
    }
    pSheets = pWorkBook->querySubObject("Sheets");
    pSheet = pSheets->querySubObject("Item(int)", 1);
    qDebug() << "Execl 新建完成" << endl;
}

3.增加一个worksheet

void MainWindow::appendSheet(const QString &sheetName,int cnt)
{
    QAxObject *pLastSheet = pSheets->querySubObject("Item(int)", cnt);
    worksheets->querySubObject("Add(QVariant)", pLastSheet->asVariant());
    worksheet = worksheets->querySubObject("Item(int)", cnt);
    pLastSheet->dynamicCall("Move(QVariant)", worksheet->asVariant());
    worksheet->setProperty("Name", sheetName);
}

4.向Execl单元格中写入数据

//此种方式是一个个向Execl里面的单元格写,每写一次,调用一次本函数  数据量很大的时候速度很慢
void MainWindow::setCellValue(int row, int column, const QString &value)
{
    QAxObject *pRange = worksheet->querySubObject("Cells(int,int)", row, column);
    pRange->dynamicCall("Value", value);
}

5.保存Execl文件

void MainWindow::saveExcel(const QString &fileName)
{
    pWorkBook->dynamicCall("SaveAs(const QString &)",QDir::toNativeSeparators(fileName));
}

6.释放Execl文件

void MainWindow::freeExcel()
{
    if (pApplication != NULL){
        pApplication->dynamicCall("Quit()");
        delete pApplication;
        pApplication = NULL;
    }
}

=整体过程=
1.获取execl文件名字
2.新建execl文件
3.向单元格中写入数据
4.保存execl文件
5.释放execl文件

数据量小的时候可以采用上面的步骤,如果数据量大一些就采用下面的方式,下面的方式其实就是对第三步进行改写。

QT如何快速的将几百万的数据保存到execl表格中?

前面两步已经将raw图像的数据读取出来,存放到了二维数组B中,并且完成了execl表格操作的基本函数。下面针对数据量偏大一些的数据进行批量写入到execl表格中。
本方法仍然使用的是QAxObject这种方式方法。
代码:

bool MainWindow::setAllCellsValue(QList<QList<QVariant>> &b_date)
{
    if(b_date.size() <= 0){
        return false;
    }
    int row = b_date.size();
    int col = b_date.at(0).size();
    QString rangStr;
    //把列数转换位Execl的字母列表
    convertToColName(col,rangStr);
    rangStr += QString::number(row);
    rangStr = "A1:" + rangStr;
    qDebug()<<rangStr; // A1:HTV1000
    QAxObject *range = this->worksheet->querySubObject("Range(const QString&)",rangStr);
    if(NULL == range || range->isNull()){
        return false;
    }
    bool succ = false;
    QVariant var;
    // 将 QList> 转换为 QVariant 数据
    castListListVariant2Variant(cells,var);
    succ = range->setProperty("Value", var);
    delete range;
    return succ;
}

以上代码中所支持的基本函数如下:
采用递归的形式,把列数转换位Execl的字母列表

void MainWindow::convertToColName(int data, QString &res)
{
    Q_ASSERT(data>0 && data<65535);
    int tempData = data / 26;
    if(tempData > 0){
        int mode = data % 26;
        convertToColName(mode,res);
        convertToColName(tempData,res);
    }
    else{
        res=(to26AlphabetString(data)+res);
    }
}

将 QList < QList < QVariant > > 转换为 QVariant 数据

void MainWindow::castListListVariant2Variant(QList<QList<QVariant>> b_date,QVariant  var)
{
	QVariantList vars;
	const int rows = b_date.size();
	for (int i = 0; i < rows; ++i ){
		vars.append(QVariant(b_date[i]));
	}
	var = QVariant(vars);
}

数字转换为字母

QString MainWindow::to26AlphabetString(int data)
{
	QChar ch = data + 0x40;
	return QString(ch);
}

以上的代码就是替换第三步的内容。

整体测试

void MainWindow::on_start_pressed()
{
	//新建二维数组B
	int **B = new int*[hight_data];
	for(int i = 0;i < highr_data; i++){
		B[i] = new int[wight_data];
	}
	qDebug() << "------开始转换-------" << endl;
	QString file_path_execl = file_path_name_execl();
	nexExecl(file_path_execl );
	qDebug() << "---读取raw文件开始---" << endl;
	get_raw_file(file_name,formal_code,B);
	qDebug() << "---读取raw文件结束---" << endl;
	//将 int **B的二维数组内容转换为 QList> 数据
	QList<QList<QVariant>> b_date;
	for (int i = 0;i < hight_data;i++) {
		QList<QVariant> rows;
		for(int j = 0;j<wight_data;j++) {
			rows.append(B[i][j]);
		}
		b_date.append(rows);
	}
	qDebug() << "--开始写入Execl文件--" << endl;
	bool fankui = setAllCellsValue(b_date);
	if(fankui == false){
		qDebug() << "--写入execl文件失败--" << endl;
	}else{
		qDebug() << "--写入execl文件成功--" << endl;
	}
	qDebug() << "------转换结束-------" << endl;
	saveExecl(file_name_execl);
	freeExecl();
	//把内存释放掉
	for(int i = 0;i < highr_data; i++){
		delete[] B[i];
	}
	delete[] B;
}

备注

为了可以使用QAxObject 控件
在pro文件中添加:

QT += axcontainer

为了使用qDebug()和时间模块 还有可以自主选择本电脑文件
在mainwindow.cpp中添加头文件:

#include 
#include 
#include 
#include 
#include 

前文中所说的全局变量的声明和初始化代码在mainwindow.hpp中

private:
	Ui::MainWindow *ui;
	//初始化数据
	int wight_data = 1920;
	int hight_data = 1080;
	QString file_name = ""; 
	QString formal_data = "16bit"; //数据所占bit位 
	QString formal_data_pixel = "RAW14"; //真实数据的格式
	//execl表格的全局变量初始化定义
	QAxObject *pApplication; //execl文件
	QAxObject *workbooks; //execl文件的workbooks
	QAxObject *workbook; //workbooks的workbook
	QAxObject *worksheets; //workbook的worksheets
	QAxObject *worksheet; //worksheets的worksheet
	QAxObject *pRange; //worksheet的单元格

注意在mianwindow.hpp中用了QAxObject 来定义全局变量就要相应的添加头文件:

#include 

能够完成这个需求,从其他的借鉴了很多,这里算是一个整合,参考链接如下:
https://blog.csdn.net/a156392343/article/details/48092515
https://blog.csdn.net/czyt1988/article/details/52121360

你可能感兴趣的:(qt5,编程语言)