我踩过的坑(问题/原理)
1.数据类型需要和图像位深一一对应,否则会出现奇怪的错误(其实就是因为libtiff的内存存储原理不明白,后总结规律就是需要数据类型和位深一一对应)
8位——uint8,16位——uint16,32位——uint32
2.16位图像相较于8位图像的读入和写出方式中“*2”的问题:
(根源之一:TIFFStripSize函数返回的到底是什么??C++ TIFFStripSize函数代码示例 - 纯净天空 (vimsky.com))
(根源之二:Tiff中不同位深的图像在内存中的布局到底是怎样的?这样的布局和读写之间有着怎样的调用关系??)
(根源之三:Tiff库对图像数据存储的布局方式??)
写出时:16位和8位图像在写出时有个2倍的关系
![](https://img-blog.csdnimg.cn/img_convert/99fd1d9e501df237c621463779c753ba.png)
![](https://img-blog.csdnimg.cn/img_convert/8cd98ae16ba5e76ac6c6413f0dd6ebb5.png)
读入时:16位深图像单纯根据图像尺寸计算和实际需要申请的内存空间大小之间存在不一致的问题,猜测如果8位深图像则二者就会一致;
(在读入过程中也发现类似问题但是没有解决成功,通过内存比较也可发现是存在2倍的关系;但是如何进行读入内存,这个有待解决,(于是在读入内存的地方,采取了另一种遍历图像方式,使其透明化,避开问题))
![](https://img-blog.csdnimg.cn/img_convert/a9e4373c791b60aa1cb619fc03a317a1.png)
最终形成的代码方案(读写代码)
struct cube
{
int cubePosition = 0; // 未被赋值的都为0
vector<uint16*> cubeData; // n*512*512
};
#define cube_queue vector<cube> // cube 队列
// 读入文件名,位深,读入的目标数据结构 cube
cube read_cubeData(string fileName, size_t bits, cube& images_buffer) {
TIFF* image = nullptr;
// Open the TIFF image
if ((image = TIFFOpen(fileName.c_str(), "r")) == NULL) {
fprintf(stderr, "read_cubeData(): Could not open incoming image!!\n");
exit(42);
}
int width, height;
// 获取cube基础信息
size_t totalFrame = TIFFNumberOfDirectories(image);
TIFFGetField(image, TIFFTAG_IMAGEWIDTH, &width);
TIFFGetField(image, TIFFTAG_IMAGELENGTH, &height);
size_t stripSize = TIFFStripSize(image); // 获取每一帧的像素点数目?(解释有问题,需要查明这个值究竟是什么?)
size_t bufferSize = width * height; // 纯尺寸大小,即真正每一帧的像素点的数目
for (int s = 0; s < totalFrame; s++) {
// 申请单帧内存,此处注意16位图像在此对应的是uint16,申请内存空间大小按TIFFStripSize的返回值进行申请
uint16* sliceBuffer = new uint16[stripSize];
if (sliceBuffer == NULL) {
fprintf(stderr, "Could not allocate enough memory for the uncompressed image\n");
exit(42);
}
for (int row = 0; row < height; row++)
{
// 每次都是从当前slice第一个字节开始写入
TIFFReadScanline(image, (&(sliceBuffer)[0] + row * int(width)), row); //---按行读取
}
TIFFReadDirectory(image);
images_buffer.cubeData.push_back(sliceBuffer);
}
TIFFClose(image);
return images_buffer;
}
// 写出文件名,数据存放buffer,目标数据三维大小,目标数据位深(16位对应数组类型uint16*,8位深对应数组类型uint8*)
int write_cube(string fileName, cube imageCube, size_t dst_width, size_t dst_height, size_t dst_depth, size_t bits)
{
if (bits != 16) {
cout << "write_cube(): image's bits unsupport!!!" << endl;
return -1;
}
uint16* buffer = (uint16*)calloc((size_t)dst_width * (size_t)dst_height * (size_t)dst_depth, sizeof(uint16));
//uint8* buffer = (uint8*)calloc((size_t)dst_width * (size_t)dst_height * (size_t)dst_depth, sizeof(uint8));
if (buffer==NULL){
cout << "write_cube(): write_cube calloc is wrong!!!" << endl;
return -1;
}
for (size_t i = 0; i < dst_depth; ++i) {
for (size_t x = 0; x < (size_t)dst_width * dst_height; ++x) {
buffer[i * (size_t)dst_width * dst_height + x] = imageCube.cubeData[i][x];
}
}
TIFFSetWarningHandler(0);
TIFF* out = TIFFOpen(fileName.c_str(), "w");
cout << fileName.c_str() << endl;
if (out) {
size_t N_size = 0;
size_t nCur = 0;
do
{
TIFFSetField(out, TIFFTAG_SUBFILETYPE, FILETYPE_PAGE);
TIFFSetField(out, TIFFTAG_PAGENUMBER, (uint16)dst_depth);
TIFFSetField(out, TIFFTAG_IMAGEWIDTH, (uint16)dst_width);
TIFFSetField(out, TIFFTAG_IMAGELENGTH, (uint16)dst_height);
TIFFSetField(out, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT);
TIFFSetField(out, TIFFTAG_BITSPERSAMPLE, bits);
TIFFSetField(out, TIFFTAG_SAMPLESPERPIXEL, 1);
TIFFSetField(out, TIFFTAG_COMPRESSION, COMPRESSION_LZW);
TIFFSetField(out, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_MINISBLACK);
TIFFSetField(out, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG);
TIFFSetField(out, TIFFTAG_ROWSPERSTRIP, dst_height); // 每个strip设置为多少行
// 16位对应数组类型uint16*,8位深对应数组类型uint8*
TIFFWriteEncodedStrip(out, 0, &((uint16*)buffer)[N_size], size_t(dst_width) * dst_height * 2);
// TIFFWriteEncodedStrip(out, 0, &((uint8*)buffer)[N_size], dst_width * dst_height);
// 表示的是写第几帧
++nCur;
// N_size:表示当前访问的字符在总buffer的位置,
// 一个strip是一张图?此处可以回答,一个strip上面设置dstheight行,故一个strip其实等于一张图大小
N_size = N_size + size_t(dst_width) * dst_height;
} while (TIFFWriteDirectory(out) && nCur < dst_depth);
// 输出是out流
TIFFClose(out);
}
free(buffer);
buffer = NULL;
return 0;
}
补:存储读入的三维图像数据,也可以单纯的用一维数组进行存储(有利有弊),但是那样处理过程中的灵活性和内存空间的大小要求也更高,且读写起来差别不大,这里选择用vector进行实现;
学习资料
Libtiff代码示例:
GitHub - Mayi-Keiji/tiffTest
Libtiff库改写示例:
C++读取并保存Tiff文件(纯C++,不需要配置opencv、boost.GIL等环境)_c++ 读取tiff_TerrencePai的博客-CSDN博客
Libtiff学习示例:
翻译一下libtiff的手册 - 一杯清酒邀明月 - 博客园 (cnblogs.com)
Libtiff读写三维TIFF图像(附详细代码)_不用先生的博客-CSDN博客_libtiff读写tif