typedef struct
{
uint16_t endian; // 字节顺序标志位,值为II或者MM:II表示小字节在前,又称为little-endian,MM表示大字节在前,又称为big-endian
uint16_t magic; // TIFF的标志位,一般都是42
uint32_t ifdOffset; // 第一个IFD的偏移量,可以在任意位置,但必须是在一个字的边界,也就是说必须是2的整数倍
}IFH;
typedef struct
{
uint16_t tag; // TAG的唯一标识
uint16_t type; // 数据类型
uint32_t size; // 4个字节数量,通过类型和数量可以确定存储此TAG的数据需要占据的字节数
uint32_t valOffset; //4个字节,tag标识的属性代表的变量值相对文件开始处的偏移量
}DE;
typedef struct
{
uint16_t size; //DE的个数
char* beginptr; //开始的指针
}Endptr;
typedef struct IFDclass
{
uint16_t n; // 表示此IFD包含了多少个DE,假设数目为n
DE* p; //n个DE
uint32_t nextIfdOffset; // 下一个IFD的偏移量,如果没有则置为0
IFDclass() :p(nullptr) {};
~IFDclass()
{
free(p);
p = nullptr;
};
}IFD;
typedef enum //枚举类型可能取值范围,各种彩色系统
{
whiteIsZeros = 0,
BlackIsZeros = 1,
RGB = 2,
RGBPalette = 3,
transparencyMask = 4,
KCMYK = 5,
YCbCr = 6,
CIELAB = 8
}PhotometricInterpretation; //光度解释
typedef struct imgInfo //图像信息
{
PhotometricInterpretation _PhotometricInterpretation; //色彩光度
uint16_t stripsPerImage; //tif中的图像个数
uint16_t* BitsPerSample; //采样位数,一个像素点一个像素点采
uint16_t channel; // 表示图像通道数
uint16_t orientation; //方向,目标
uint16_t subFileNum; //所含的图文件数目
uint32_t height; // 表示图像高度
uint32_t width; // 表示图像宽度
uint32_t* stripOffsets; // 图像条带数据起始地址存储位置相对于文件开始的位置val的保存位置,属性值偏移量,共有stripsPerImage个
uint32_t* stripByteCouts; //数据类型的数目
uint16_t Compression; //1代表没有压缩、7代表JPEG压缩、32773代表packBits压缩
uint32_t rowsPerStrip; //一共有多少行
uint32_t allBytes; //全部比特数
imgInfo() :
_PhotometricInterpretation(BlackIsZeros),
stripsPerImage(1),
channel(1),
allBytes(0),
orientation(1),
subFileNum(1),
height(0),
width(0),
Compression(1), //没压缩
rowsPerStrip(height),
BitsPerSample(nullptr),
stripOffsets(nullptr),
stripByteCouts(nullptr) {};
~imgInfo()
{
free(BitsPerSample);
BitsPerSample = nullptr;
free(stripOffsets);
stripOffsets = nullptr;
free(stripByteCouts);
stripByteCouts = nullptr;
}
}imInfo;
inline bool mashineEndian(void) // 设置开始
{
uint16_t a = 1;
uint16_t* p = &a;
return *((uint8_t*)p) == a;
}
template<typename baseType>
struct tiffTypeMap {
uint16_t typeValue = 1;
};
template<>
struct tiffTypeMap<uint8_t> {
uint16_t typeValue = 1;
};
template<>
struct tiffTypeMap<uint16_t> {
uint16_t typeValue = 3;
};
template<>
struct tiffTypeMap<uint32_t> {
uint16_t typeValue = 4;
};
template<>
struct tiffTypeMap<uint64_t> {
uint16_t typeValue = 5;
};
template<>
struct tiffTypeMap<int8_t> {
uint16_t typeValue = 6;
};
template<>
struct tiffTypeMap<int16_t> {
uint16_t typeValue = 8;
};
template<>
struct tiffTypeMap<int32_t> {
uint16_t typeValue = 9;
};
template<>
struct tiffTypeMap<int64_t> {
uint16_t typeValue = 10;
};
template<>
struct tiffTypeMap<float> {
uint16_t typeValue = 11;
};
template<>
struct tiffTypeMap<double> {
uint16_t typeValue = 12;
};
class TiffImg
{
public:
imInfo _imInfo;
uint8_t* data;
IFH _IFH;
IFD _IFD;
Endptr endptr;
TiffImg(string path);
template<typename T>
TiffImg(T, uint32_t height, uint32_t width, uint8_t channel = 1);
void getmin();
void save(string savePath = "defaultSave.tif");
~TiffImg()
{
/*save();*/
free(data);
data = nullptr;
}
template<typename T>
T& operator()(T, uint32_t row, uint32_t col, uint8_t depth = 1)
{
uint64_t sum = ((row - 1) * _imInfo.width + (col - 1)) * chanBytes(_imInfo.channel, _imInfo.BitsPerSample) + chanBytes(depth - 1, _imInfo.BitsPerSample);
return *((T*)(data + sum)); //数据类型
}
private:
bool EndianTrans; //开始点
bool read(string path, uint32_t IFDoffset = 0); //初始化
const uint16_t littleEndian = 0x4949; //摩托罗拉整数格式
const uint16_t bigEndian = 0x4d4d; //Intel整数格式
uint8_t chanBytes(uint16_t channel, uint16_t* BitsPerSample);
};
uint8_t TiffImg::chanBytes(uint16_t channel, uint16_t* BitsPerSample)
{
uint8_t res{};
for (int i = 0; i < channel; ++i)
{
res += (BitsPerSample[i] / 8 + BitsPerSample[i] % 8);
}
return res;
};
template<typename T>
TiffImg::TiffImg(T, uint32_t height, uint32_t width, uint8_t channel)
{
_imInfo.height = height;
_imInfo.width = width;
_imInfo.channel = channel;
_imInfo.BitsPerSample = sizeof(T) * 8;
endptr.beginptr = new char[1024];
endptr.size = 0;
_imInfo.stripByteCouts[0] = height * width * channel * sizeof(T);
if (_imInfo.stripByteCouts[0] % 2 != 0)
{
++_imInfo.stripByteCouts[0];
}
uint8_t typeLabel = tiffTypeMap<T>().typeValue;
//_imInfo.ImageStart[0] = sizeof(_IFH);
_imInfo.stripOffsets[0] = 22550;
data = (uint8_t*)malloc(_imInfo.stripByteCouts[0]);
EndianTrans = false;
_IFH.endian = mashineEndian ? littleEndian : bigEndian;
_IFH.magic = 42;
_IFH.ifdOffset = _imInfo.stripByteCouts[0] + sizeof(_IFH);
_IFH.ifdOffset = sizeof(_IFH);
_IFD.n = 6; //6个
_IFD.p = (DE*)malloc(_IFD.n * sizeof(DE));
memset((void*)_IFD.p, 0, _IFD.n * sizeof(DE));
DE* temp = _IFD.p;
temp->tag = 0x0100; temp->type; temp->size; temp->valOffset = _imInfo.width;
++temp; temp->tag = 0x0101; temp->type; temp->size; temp->valOffset = _imInfo.height;
++temp; temp->tag = 0x0102; temp->type = typeLabel; temp->size; temp->valOffset;
++temp; temp->tag = 0x0106; temp->type; temp->size; temp->valOffset = _imInfo.channel;
++temp; temp->tag = 0x0111; temp->type; temp->size; temp->valOffset = _imInfo.stripOffsets[0];
++temp; temp->tag = 0x0117; temp->type; temp->size; temp->valOffset = _imInfo.stripByteCouts[0];
_IFD.nextIfdOffset = 1;
}
TiffImg::TiffImg(string path)
{
_IFD.nextIfdOffset = 0;
bool hasNext = true;
_imInfo.subFileNum = 0;
_imInfo.allBytes = 0;
while (read(path, _IFD.nextIfdOffset) && hasNext)
{
ifstream frd(path, ios::in | ios::binary);
if (frd) //文件打开成功
{
auto chanBytes = [](auto channel, auto BitsPerSample)->uint8_t
{
uint8_t res{};
for (int i = 0; i < channel; ++i)
{
res += (BitsPerSample[i] / 8 + BitsPerSample[i] % 8);
}
return res;
};
auto subSum = _imInfo.height * _imInfo.width * chanBytes(_imInfo.channel, _imInfo.BitsPerSample);
auto subdat = (uint8_t*)malloc(subSum);
memset(subdat, 0, subSum);
data = subdat;
auto tripBytes = [](auto strips, auto stripByteCouts)->uint32_t
{
uint32_t res{};
for (int i = 0; i < strips; ++i)
{
res += stripByteCouts[i];
}
return res;
};
int tempsum{};
for (auto k = 0; k < _imInfo.stripsPerImage; ++k)
{
frd.seekg(static_cast<long>(_imInfo.stripOffsets[k]), ios::beg);
for (auto i = 0; i < _imInfo.stripByteCouts[k]; ++i)
{
if (_imInfo.Compression == 1)
{
frd.read(reinterpret_cast<char*>(subdat + tempsum), _imInfo.stripByteCouts[k]);
tempsum += _imInfo.stripByteCouts[k];
break;
}
else if (_imInfo.Compression == 7)
{
cerr << "JPEG压缩还没写";
}
else if (_imInfo.Compression == 32773)
{
int8_t temp{};
frd.read(reinterpret_cast<char*>(&temp), 1);
if (temp == -128)
{
}
else if (temp < 0)
{
uint8_t nextdata{};
frd.read(reinterpret_cast<char*>(&nextdata), 1);
memset(subdat + tempsum, nextdata, 1 - temp);
tempsum += 1 - temp;
++i;
}
else
{
frd.read(reinterpret_cast<char*>(subdat + tempsum), temp + 1);
tempsum += 1 + temp;
i += temp + 1;
}
}
}
if (EndianTrans)
{
cerr << "大小端转换还没写";
}
}
if (!_IFD.nextIfdOffset)
{
hasNext = false;
}
frd.close();
_imInfo.allBytes += subSum;
++_imInfo.subFileNum;
}
}
cout << "图像读取成功" << endl;
cout << "图像的高:" << _imInfo.height << endl;
cout << "图像的宽:" << _imInfo.width << endl;
cout << "是否压缩:" << _imInfo.Compression << endl;
cout << "通道数: " << _imInfo.channel << endl;
cout << "字节长度: " << _imInfo.BitsPerSample << endl;
};
uint8_t getsize(uint16_t typeLabel)
{
switch (typeLabel)
{
case 1:;//Byte
case 2:return 1; break; //文本类型,7位Ascii码加1位二进制0
case 3:; return sizeof(uint16_t); break;//2字节
case 4: return sizeof(uint32_t); break;//4字节
case 5:; return sizeof(uint64_t); break;//有理数,第一无符号Long为分子,第二个为分母部分
case 6:; return sizeof(int8_t); break;
case 8:; return sizeof(int16_t); break;
case 9:; return sizeof(int32_t); break;
case 10:; return sizeof(int64_t); break;///有理数,第一无符号Long为分子,第二个为分母部分
case 11:; return sizeof(float); break;
case 12:; return sizeof(double); break;
default:; return 1; break;
}
}
bool TiffImg::read(string path, uint32_t IfdOffset)
{
ifstream frd(path, ios::in | ios::binary);
while (frd) {
frd.seekg(IfdOffset, ios::beg);
frd.read(reinterpret_cast<char*>(&(_IFH)), sizeof(_IFH));
bool imEndian{ _IFH.endian == littleEndian }; //图像存储方式是大端(false)还是小端(true)
EndianTrans = (imEndian != mashineEndian());
auto IFD_Start = _IFH.ifdOffset;
frd.seekg(static_cast<long>(IFD_Start), ios::beg);
frd.read(reinterpret_cast<char*>(&(_IFD.n)), sizeof(_IFD.n));
if (EndianTrans) {
;
}
auto DE_n = _IFD.n;
_IFD.p = (DE*)malloc(sizeof(DE) * DE_n);
frd.read(reinterpret_cast<char*>(_IFD.p), sizeof(DE) * DE_n);
endptr.beginptr = new char[1000000];
endptr.size = 0;
auto getNdata = [path](auto& DE_ele, auto* res, auto endptr)->void
{
uint8_t typ = getsize(DE_ele.type);
auto DE_val_size = typ * DE_ele.size;
if (DE_val_size > 4)
{
ifstream fr(path, ios::in | ios::binary);
fr.seekg(static_cast<long>(DE_ele.valOffset), ios::beg);
fr.read(reinterpret_cast<char*>(res), DE_val_size);
//memcpy(&endptr->beginptr[endptr->size], reinterpret_cast<char*>(res), DE_val_size);
//endptr->size += DE_val_size;
fr.close();
}
else {
memcpy(reinterpret_cast<char*>(res), reinterpret_cast<char*>(&DE_ele.valOffset), DE_val_size);
}
};
for (uint32_t i = 0; i < DE_n; ++i)
{
DE& temp = _IFD.p[i];
switch (temp.tag)
{
case 0x00FE:
{
if (temp.valOffset & 1)
{
}
if (temp.valOffset & 2)
{
}
if (temp.valOffset & 4)
{
}
break;
}
case 0x00FF: {
if (temp.valOffset == 1)
{
}
if (temp.valOffset == 2)
{
}
if (temp.valOffset == 3)
{
}
break;
}
case 0x0100: getNdata(temp, &_imInfo.width,&endptr); break;// 图像宽度存储位置
case 0x0101: getNdata(temp, &_imInfo.height, &endptr); break;// 图像高度存储位置
case 0x0102: //每个通道的Bit数 SHORT ,长度为SamplesPerPixel
{
_imInfo.channel = temp.size;
_imInfo.BitsPerSample = new uint16_t[_imInfo.channel];
getNdata(temp, _imInfo.BitsPerSample, &endptr);
break;
}
case 0x0103: // 图像是否压缩 05表示压缩 short、1
{
getNdata(temp, &_imInfo.Compression, &endptr);
break;
}
case 0x0106: // 表示图像的种类
{
_imInfo._PhotometricInterpretation = (PhotometricInterpretation)_IFD.p[i].valOffset;
break;
}
case 0x0111: // 图像条带数据起始地址存储位置相对于文件开始的位置val的保存位置
{
_imInfo.stripsPerImage = _IFD.p[i].size;
_imInfo.stripOffsets = new uint32_t[_imInfo.stripsPerImage];
getNdata(temp, _imInfo.stripOffsets, &endptr);
break;
}
case 0x0112:
{
getNdata(temp, &_imInfo.orientation, &endptr);
break;
}
case 0x0114: break;
case 0x0115:getNdata(_IFD.p[i], &_imInfo.channel, &endptr); break;
case 0x0116:
{
getNdata(temp, &_imInfo.rowsPerStrip, &endptr);
break;
}
case 0x0117:
{
_imInfo.stripsPerImage = _IFD.p[i].size;
_imInfo.stripByteCouts = new uint32_t[_imInfo.stripsPerImage];
getNdata(temp, _imInfo.stripByteCouts, &endptr);
break;
}
case 284: break;
case 0x0128:
break;
case 0x011A:
break;
case 0x011B:
break;
default: break;
}
}
frd.read(reinterpret_cast<char*>(&_IFD.nextIfdOffset), sizeof(_IFD.nextIfdOffset));
auto comf = frd.tellg();
frd.seekg(0L,ios::end);
auto come = frd.tellg();
frd.seekg(comf);
frd.read(endptr.beginptr, come - comf);
endptr.size = come - comf;
frd.close();
return true;
}
return false;
}
void TiffImg::save(string savePath)
{
ofstream fd{ savePath, ios::out | ios::binary };
if (fd.is_open())
{
fd.seekp(0L, ios::beg);
if (EndianTrans)
{
uint16_t Endian = (_IFH.endian == littleEndian ? bigEndian : littleEndian);
fd.write((char*)(&(Endian)), sizeof(_IFH.endian));
}
else
{
fd.write((char*)&(_IFH.endian), sizeof(_IFH.endian));
}
fd.write((char*)&(_IFH.magic), sizeof(_IFH.magic));
fd.write((char*)&(_IFH.ifdOffset), sizeof(_IFH.ifdOffset));
fd.seekp(_IFH.ifdOffset, ios::beg);
fd.write((char*)&(_IFD.n), sizeof(_IFD.n));
fd.write((char*)(_IFD.p), sizeof(DE) * _IFD.n);
fd.write((char*)(&(_IFD.nextIfdOffset)), sizeof(_IFD.nextIfdOffset));
fd.write(endptr.beginptr, endptr.size);
fd.seekp(_imInfo.stripOffsets[0], ios::beg);
fd.write((char*)(data), _imInfo.allBytes); //所有图像数据定向输入到data
fd.close();
}
}
图像处理之tiff图像读写(纯C++不调库)
于 2022-06-21 15:28:57 首次发布