本机系统配置:ThinkPadT570、Windows10、QT5.12.2(QtCreater4.8.2)
需求如下:我们需要用qt导入16位深的tiff灰度图,但用QImage只能导入8位深的tiff灰度图,如果用qt的QImage导入16位深的灰度图,图像数据会被强制转换成argb格式的图像,数据就被更改了,所以我需要自己编写一个解析tiff文件的功能,我翻阅了很多博客,其中如下链接给我的帮助最大:
https://blog.csdn.net/chenlu5201314/article/details/56276903
上述博客作为详细解析tiff文件结构的说明文档,写的非常详细,我也是根据上面的内容,自己编写了一个解析tiff文件的类(当然功能很少,只能解析符合特定条件的tiff文件),具体如下:
头文件:
#ifndef MYTIFLIB_H
#define MYTIFLIB_H
//************************************************************
//by Bruce Xu
//注:该类只解析特定的tiff文件!
//1.解析的tiff文件中只存在一幅图,如果文件中存在多幅图,本类不支持解析!
//2.图像数据为8位或16位深度的灰度图,如果是其他类型的图片,本类不支持解析!
//3.图片没有被压缩过!
//************************************************************
#include <QObject>
#include <QVector>
#include <QString>
#include <QFile>
struct MY_IFH//Image File Head
{
qint16 nByteOrder;//TIF标记,其值为0x4D4D或0x4949
qint16 nVersion;//版本号,其值恒为0x2A
qint32 nOffset2FirstIFD;//第一个IFD的偏移量
};
struct MY_DE//Directory Entry
{
qint16 nTagID;//本属性的标签编号
qint16 nType;//本属性值的数据类型
qint32 nLength;//该种类型的数据的个数,而不是某个数据的长度
qint32 nValueOffset;//tagID代表的变量值相对文件开始处的偏移量,但如果变量值占用的空间不多于4个字节(例如只有1个Integer类型的值),那么该值就直接存放在valueOffset中,没必要再另外指向一个地方了。
};
struct MY_IFD//image file directory
{
qint16 nIDNum;//本IFD中DE的数量
QVector<MY_DE> vMyDE;//本IFD中的DE
};
struct MY_ImgInfo//图片属性信息
{
qint32 nHeight;//图像的宽度
qint32 nWidth;//图像的高度
qint16 nDepth;//图像的深度
bool bCompressed;//图像是否被压缩
qint32 nDataOffset;//图像第一个像素数据距离的偏移多少字节
qint32 nDataSize;//图像数据字节总数
};
struct MY_TIFF//Image File Struct
{
MY_IFH tMyIFH;//Image File Head
MY_IFD tMyIFD;//放image file directory的容器
QVector<unsigned char> vUcharData;//如果是8位深的灰度图用unsigned char 放数据
QVector<unsigned short> vShortData;//如果是16位深的灰度图用short* 放数据
MY_ImgInfo tImgInfo;//图片属性信息
};
class MyTifLib
{
public:
MyTifLib();
~MyTifLib();
bool ParseTIFF(QString sFilePath, MY_TIFF &myTIFF);
};
#endif // MYTIFLIB_H
源文件:
#include "mytiflib.h"
MyTifLib::MyTifLib()
{
}
MyTifLib::~MyTifLib()
{
}
bool MyTifLib::ParseTIFF(QString sFilePath, MY_TIFF &myTIFF)
{
QFile file(sFilePath);
if(!file.open(QIODevice::ReadOnly))
{
return false;
}
QByteArray t = file.readAll();
file.close();
memcpy((char*)&myTIFF.tMyIFH.nByteOrder,t.data(),2*sizeof(char));
memcpy((char*)&myTIFF.tMyIFH.nVersion,t.data()+2,2*sizeof(char));
memcpy((char*)&myTIFF.tMyIFH.nOffset2FirstIFD,t.data()+4,4*sizeof(char));
memcpy((char*)&myTIFF.tMyIFD.nIDNum,t.data()+myTIFF.tMyIFH.nOffset2FirstIFD,2*sizeof(char));
myTIFF.tMyIFD.vMyDE.resize(myTIFF.tMyIFD.nIDNum);
for (int i=0;i<myTIFF.tMyIFD.nIDNum;i++)
{
memcpy((char*)&myTIFF.tMyIFD.vMyDE[i].nTagID,t.data()+myTIFF.tMyIFH.nOffset2FirstIFD+2+12*i,2*sizeof(char));
memcpy((char*)&myTIFF.tMyIFD.vMyDE[i].nType,t.data()+myTIFF.tMyIFH.nOffset2FirstIFD+2+12*i+2,2*sizeof(char));
memcpy((char*)&myTIFF.tMyIFD.vMyDE[i].nLength,t.data()+myTIFF.tMyIFH.nOffset2FirstIFD+2+12*i+4,4*sizeof(char));
memcpy((char*)&myTIFF.tMyIFD.vMyDE[i].nValueOffset,t.data()+myTIFF.tMyIFH.nOffset2FirstIFD+2+12*i+8,4*sizeof(char));
if(myTIFF.tMyIFD.vMyDE[i].nTagID == 0x0100)//表示图像宽度
{
myTIFF.tImgInfo.nWidth = myTIFF.tMyIFD.vMyDE[i].nValueOffset;
}
if(myTIFF.tMyIFD.vMyDE[i].nTagID == 0x0101)//表示图像高
{
myTIFF.tImgInfo.nHeight = myTIFF.tMyIFD.vMyDE[i].nValueOffset;
}
if(myTIFF.tMyIFD.vMyDE[i].nTagID == 0x0102)//表示图像每个像素深度,即占多少位宽
{
myTIFF.tImgInfo.nDepth = myTIFF.tMyIFD.vMyDE[i].nValueOffset;
}
if(myTIFF.tMyIFD.vMyDE[i].nTagID == 0x0103)//表示图像数据是否压缩
{
if(myTIFF.tMyIFD.vMyDE[i].nValueOffset == 5)
{
myTIFF.tImgInfo.bCompressed = true;
}
else
{
myTIFF.tImgInfo.bCompressed = false;
}
}
if(myTIFF.tMyIFD.vMyDE[i].nTagID == 0x0111)//图像数据起始字节相对于文件开始处的偏移量
{
myTIFF.tImgInfo.nDataOffset = myTIFF.tMyIFD.vMyDE[i].nValueOffset;
}
if(myTIFF.tMyIFD.vMyDE[i].nTagID == 0x0117)//图像数据字节总数
{
myTIFF.tImgInfo.nDataSize = myTIFF.tMyIFD.vMyDE[i].nValueOffset;
}
}
if(myTIFF.tImgInfo.bCompressed)
{
return false;
}
if(myTIFF.tImgInfo.nDepth == 16)
{
//TODO:
myTIFF.vShortData.resize(myTIFF.tImgInfo.nWidth*myTIFF.tImgInfo.nHeight);
if(myTIFF.vShortData.size()<1)
{
return false;
}
memcpy((char*)&myTIFF.vShortData[0],t.data()+myTIFF.tImgInfo.nDataOffset,myTIFF.tImgInfo.nDataSize*sizeof(char));
}
else if(myTIFF.tImgInfo.nDepth == 12)
{
//TODO:
myTIFF.vShortData.resize(myTIFF.tImgInfo.nWidth*myTIFF.tImgInfo.nHeight);
if(myTIFF.vShortData.size()<1)
{
return false;
}
memcpy((char*)&myTIFF.vShortData[0],t.data()+myTIFF.tImgInfo.nDataOffset,myTIFF.tImgInfo.nDataSize*sizeof(char));
}
else if(myTIFF.tImgInfo.nDepth == 8)
{
//TODO:
myTIFF.vUcharData.resize(myTIFF.tImgInfo.nWidth*myTIFF.tImgInfo.nHeight);
if(myTIFF.vUcharData.size()<1)
{
return false;
}
memcpy((char*)&myTIFF.vUcharData[0],t.data()+myTIFF.tImgInfo.nDataOffset,myTIFF.tImgInfo.nDataSize*sizeof(char));
}
else
{
return false;
}
return true;
}
用法:
首先包含该类头文件
#include “”
然后在需要用的地方添加如下代码:
MyTifLib m_MyTifLib;//定义一个类对象
MY_TIFF myTIFF;//定义一个tiff数据结构体
m_MyTifLib.ParseTIFF(“需要解析的tiff文件路径的QString类型的字符串”,myTIFF);//解析tiff文件
这时候,你要的tiff文件中的图像数据就在myTIFF.vShortData或者myTIFF.vUcharData里面了。
注:如果需要解析的图像是8位深的,则数据存放在myTIFF.vUcharData里面
如果需要解析的图像是16位深的,则数据存放在myTIFF.vShortData里面
源码地址:https://download.csdn.net/download/weixin_43935474/11188206