一张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的单元格
函数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表格中。
这个问题就涉及到如何将数据保存到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文件
数据量小的时候可以采用上面的步骤,如果数据量大一些就采用下面的方式,下面的方式其实就是对第三步进行改写。
前面两步已经将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