Silver-Fang / Image5D

5D图像读写库,全网最快第三方OIR读入器
6 stars 1 forks source link

请问有tiff/bigtiff写入的demo/example吗 #1

Closed zhusihan-python closed 1 year ago

zhusihan-python commented 1 year ago

埃博拉酱您好,请问该库有tiff/bigtiff写入的demo/example吗 例如把一个c++的三维数组写成多层的tiff/bigtiff格式 想感受下该库的写入到底有多快,但是对c++不熟悉,不知道从何下手

zhusihan-python commented 1 year ago

tag v1.1.4 是最后一个有<Mex工具>文件夹的版本

zhusihan-python commented 1 year ago

找到Mex工具.h头文件之后,因为方法名都是中文,还需要在visual studio中把项目文件编码设置成GB2312 先开启高级保存选项,再打开文件逐一设置保存 保存完成之后,编译成功

Silver-Fang commented 1 year ago

这个库公开的API主要是面向MATLAB的。如果你想直接面向C++使用,建议编译为静态库,然后在调用方项目中包含IOmeTiff读写器.h,以及该头文件所依赖的其它头文件。这是一个兼容各种TIFF格式的接口类。 Demo目前是没有,不过我会把它列入后续更新计划。对于你问的将三维数组写出TIFF的情景,可以编写类似于这样的代码:

#include <IOmeTiff读写器.h>
int main()
{
const 颜色 通道颜色[] = {颜色{.整数值 = -1}}; //-1表示白色
IOmeTiff读写器* const 读写器 = IOmeTiff读写器::覆盖创建(L"文件路径.tif",像素类型::UINT16,图像宽度,图像高度,1,图像层数,1,通道颜色,维度顺序::XYCZT); //像素类型应该跟你的数组数据类型一致
读写器->写出像素(像素数组); //像素数组应该是uint16_t[]类型,而不是uint16_t[][][]类型。如果你的原始数据是后者,应该将其展开为一维数组,按照维度从低到高XYZ的顺序。
delete 读写器;
return 0;
}

这个代码Demo是我现写的,没有经过测试,只是反映一个思路,你需要根据你的具体应用进行修改。

关于语言编码,建议更改计算机设置,使用UTF-8编码。首先搜索并打开”区域“ image image 选择【管理】选项卡,然后【更改系统区域设置】 image 勾选【Beta版:使用 Unicode UTF-8 提供全球语言支持】,【确定】以后重启电脑。 GB2312是一种过时的字符编码,不建议在新的软件开发项目中使用。

zhusihan-python commented 1 year ago

感谢您的回复! 我将工程修改成编译静态库 image 但是产出物看起来更少,没有生成IOmeTiff读写器.lib文件 image 请问是项目哪里设置不对吗,或者是要在文件里修改公开的API

尝试新建了一个项目,想把IOmeTiff读写器.cpp放在项目里一起编译了,用main.cpp文件替换了dllmain.cpp文件,其他的文件和配置保持一致,编译过了,报链接错误 main文件的内容

#include "pch.h"
#include <IOmeTiff读写器.h>
int main()
{
    uint16_t 图像宽度 = 10;
    uint16_t 图像高度 = 10;
    uint16_t 图像层数 = 3;
    const char 像素数组[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
                            1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
                            1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
    const 颜色 通道颜色[] = { 颜色{.整数值 = -1} }; //-1表示白色
    IOmeTiff读写器* const 读写器 = IOmeTiff读写器::覆盖创建(L"E:/data/teapot_big_fast.tif", 像素类型::UINT16, 图像宽度, 图像高度, 1, 图像层数, 1, 通道颜色, 维度顺序::XYCZT); //像素类型应该跟你的数组数据类型一致
    读写器->写出像素(像素数组); //像素数组应该是uint16_t[]类型,而不是uint16_t[][][]类型。如果你的原始数据是后者,应该将其展开为一维数组,按照维度从低到高XYZ的顺序。
    delete 读写器;
    return 0;
}

image 其中一部分报错内容

严重性 代码  说明  项目  文件  行   禁止显示状态
错误  LNK2001 无法解析的外部符号 __imp_get_function_ptr    TiffWrite   E:\projects\Image5D\TiffWrite\Image5D异常.obj 1   
错误  LNK2001 无法解析的外部符号 "void __cdecl 异常输出补全(class matlab::mex::MexIORange<class std::_Vector_iterator<class std::_Vector_val<struct std::_Simple_types<class matlab::data::Array> > > > &)" (?异常输出补全@@YAXAEAV?$MexIORange@V?$_Vector_iterator@V?$_Vector_val@U?$_Simple_types@VArray@data@matlab@@@std@@@std@@@std@@@mex@matlab@@@Z) TiffWrite   E:\projects\Image5D\TiffWrite\MexFunction.obj   1   
错误  LNK2001 无法解析的外部符号 mexFunctionAdapter    TiffWrite   E:\projects\Image5D\TiffWrite\MexFunction.obj   1   
错误  LNK2001 无法解析的外部符号 mexDestroyMexFunction TiffWrite   E:\projects\Image5D\TiffWrite\MexFunction.obj   1   
错误  LNK2001 无法解析的外部符号 mexCreateMexFunction  TiffWrite   E:\projects\Image5D\TiffWrite\MexFunction.obj   1   
Silver-Fang commented 1 year ago

首先把静态库扩展名改成lib 然后,带有Mex字段的cpp文件应当从项目中排除,因为这些是面向MATLAB的 最后你似乎没有安装pugixml这个库,可以在NuGet里找一下

zhusihan-python commented 1 year ago

设置配置类型为静态库(lib)后, 设置扩展名为.lib 63f5fd0d664e9d3e2cae73e1fd2d730 移除带有Mex字段的cpp及头文件,移除导入了<Mex工具.h>的代码,移除Oir和Image5DAPI相关的cpp及头文件 Image5DMex项目生成,得到Image5DMex.lib 安装pugixml nuget install pugixml 需要使用pugixml.1.13.0\build\native\lib\x64\v143\dynamic\Release目录下的lib文件pugixml.lib 新建项目TiffWriter, 配置属性-->常规-->c++语言标准,选c++ latest 配置属性-->c/c++-->常规-->附加包含目录,配置头文件

F:\projects\pugixml-1.13\src
E:\projects\Image5D\Image5DMex

配置属性-->链接器-->输入-->附加依赖项,配置依赖库

Rpcrt4.lib
Bcrypt.lib
E:\projects\Image5D\+Image5D\+internal\private\Image5DMex.lib
E:\projects\pugixml.1.13.0\build\native\lib\x64\v143\dynamic\Release\pugixml.lib

项目生成成功 image

zhusihan-python commented 1 year ago

现在想测试下真实的图片数据写入,再请问下该项目中的通道颜色是什么概念呢,是否类似RGB和RGBA的颜色通道,-1表示白色是指该通道所有像素点都是255的值吗 例如我现在有一个图片数据的buffer,结构是这样的,本身只有一层(一个维度),不是金字塔结构的 image 我想生成一个多层的tiff, 是直接转换成一维数组,传入写出像素就可以,还是需要另外处理呢 在matlab里转成图片矩阵,是需要这样处理的,将rgb,rgb,rgb转换成rrr,ggg,bbb,否则生成的图像是错乱的

%imgdata是1*N(w*h*3)的矩阵
chan_r = imgdata(1:3:end);
chan_g = imgdata(2:3:end);
chan_b = imgdata(3:3:end);
imgdata_combine = [chan_r, chan_g, chan_b];
mid_imgdata = reshape(imgdata_combine, [width, height, 3]);
T=affine2d([0 1 0;1 0 0;0 0 1]);
new_imgdata=imwarp(mid_imgdata,T);

另外附上matlab写多层tiff/svs的方法,写入也比较快,比libtiff库快很多,但是内存占用很高,还需要安装matlab runtime,因此我更偏向于纯c++的TiffWriter

function writesvs(imgdata, width, height, file_name)
    T=affine2d([0 1 0;1 0 0;0 0 1]);%构造空间变换结构T.这里为转置变换矩阵
    new_imgdata=imwarp(reshape([imgdata(1:3:end), imgdata(2:3:end), imgdata(3:3:end)], [width, height, 3]),T); 
    size1 = fix(size(new_imgdata));
    size2 = fix(size(new_imgdata)/2);
    size3 = fix(size(new_imgdata)/4);
    size4 = fix(size(new_imgdata)/8);

    t = Tiff(file_name,'w');
    %写40X的图像
    tagstruct.ImageDescription = "Aperio Image Library |AppMag = 40|MPP = 0.265018";
    tagstruct.ImageLength = size1(1);
    tagstruct.ImageWidth = size1(2);
    tagstruct.Photometric = Tiff.Photometric.RGB;
    tagstruct.BitsPerSample = 8;
    tagstruct.SamplesPerPixel = 3;
    tagstruct.RowsPerStrip = 16;
    tagstruct.PlanarConfiguration = Tiff.PlanarConfiguration.Chunky;
    tagstruct.Software = 'MATLAB';
    tagstruct.TileWidth = 240;
    tagstruct.TileLength = 240;
    tagstruct.Compression = 7;
    tagstruct.JPEGQuality = 85;
    setTag(t,tagstruct)

    write(t,imresize(new_imgdata, [size1(1) size1(2)]));
    writeDirectory(t);

    %写20X的图像
    tagstruct2.ImageDescription = "Aperio Image Library |AppMag = 20|MPP = 0.51";
    tagstruct2.ImageLength = size2(1);
    tagstruct2.ImageWidth = size2(2);
    tagstruct2.Photometric = Tiff.Photometric.RGB;
    tagstruct2.BitsPerSample = 8;
    tagstruct2.SamplesPerPixel = 3;
    tagstruct2.RowsPerStrip = 16;
    tagstruct2.PlanarConfiguration = Tiff.PlanarConfiguration.Chunky;
    tagstruct2.Software = 'MATLAB';
    tagstruct2.TileWidth = 240;
    tagstruct2.TileLength = 240;
    tagstruct2.Compression = 7;
    tagstruct2.JPEGQuality = 85;
    setTag(t,tagstruct2)

    write(t,imresize(new_imgdata, [size2(1) size2(2)]));
    writeDirectory(t);

    %写10X的图像
    tagstruct3.ImageDescription = "Aperio Image Library";
    tagstruct3.ImageLength = size3(1);
    tagstruct3.ImageWidth = size3(2);
    tagstruct3.Photometric = Tiff.Photometric.RGB;
    tagstruct3.BitsPerSample = 8;
    tagstruct3.SamplesPerPixel = 3;
    tagstruct3.RowsPerStrip = 16;
    tagstruct3.PlanarConfiguration = Tiff.PlanarConfiguration.Chunky;
    tagstruct3.Software = 'MATLAB';
    tagstruct3.TileWidth = 240;
    tagstruct3.TileLength = 240;
    tagstruct3.Compression = 7;
    tagstruct3.JPEGQuality = 85;
    setTag(t,tagstruct3)

    write(t,imresize(new_imgdata, [size3(1) size3(2)]));
    writeDirectory(t);

    %写5X的图像
    tagstruct4.ImageDescription = "Aperio Image Library";
    tagstruct4.ImageLength = size4(1);
    tagstruct4.ImageWidth = size4(2);
    tagstruct4.Photometric = Tiff.Photometric.RGB;
    tagstruct4.BitsPerSample = 8;
    tagstruct4.SamplesPerPixel = 3;
    tagstruct4.RowsPerStrip = 16;
    tagstruct4.PlanarConfiguration = Tiff.PlanarConfiguration.Chunky;
    tagstruct4.Software = 'MATLAB';
    tagstruct4.TileWidth = 240;
    tagstruct4.TileLength = 240;
    tagstruct4.Compression = 7;
    tagstruct4.JPEGQuality = 85;
    setTag(t,tagstruct4)

    write(t,imresize(new_imgdata, [size4(1) size4(2)]));
    close(t);
end
Silver-Fang commented 1 year ago

关于通道颜色,并不是标准TIFF规定的概念,而是来自OME-TIFF扩展。建议你看一下README中的OME-TIFF部分。它支持任意多个颜色通道,每个通道都可以是任何颜色。它可以退化为RGB通道,但并不是通过Photometric.RGB来实现的。 根据你的描述,你的图像似乎是一个4维数组,分别是宽度X、高度Y、颜色通道C(RGB,3个)和层级Z,数组中每个值都是uint8_t类型。那么你可以将你的数组处理成从低到高XYCZ展开的一维数组。但是需要注意,在覆盖创建的时候,要将像素类型设为UINT8,维度顺序设为XYCZT,SizeT设为1,通道颜色分别设为红绿蓝。 输出的图像文件可以用ImageJ查看,ImageJ允许你将各通道分开或合并查看。 这里需要强调一下,如果你需要确保输出的图像能够被某个特定的图像查看软件识别,你需要先确认那个软件是否支持OME-TIFF扩展。因为本库输出的TIFF是使用OME扩展功能实现的颜色通道(包括RGB),而没有用到TIFF标准自带的Photometric.RGB标签。如果你的图像查看软件依赖于Photometric.RGB标签,本库暂时不能够支持。

zhusihan-python commented 1 year ago

我原来理解的是传入图片数据,<IOmeTiff读写器::覆盖创建>里会按照指定的层数,resize传入的图片数据,像上面writesvs函数一样,取原来的1/2, 1/4, 1/8,resize几次写多层 imresize(new_imgdata, [size2(1) size2(2)])

其实是要先把多层的数据都准备好,按XYCZ指定顺序放到<uint8_t* 像素数组>里面。

我在c++里获取到的是一个buffer头,以及buffer的长度

// Return a pointer to the beginning of the buffer.
void *                 dataPtr = resampleF->GetOutput()->GetPixelContainer()->GetBufferPointer();
itk::Image<PixelType, Dimension>::SizeType size = resampleF->GetOutput()->GetLargestPossibleRegion().GetSize();
long long unsigned int buffer_len = 3 * size[0] * size[1];

那我要复制buffer到数组

// https://stackoverflow.com/questions/46136409/initialising-stdarray-from-pointer-into-a-buffer-elegantly
using arrayType = std::array<uint8_t, buffer_len >;
arrayType imageArray;
std::copy_n(dataPtr , imageArray.size(), imageArray.begin());

如果我要写多层的tiff,先对原来的图像做resample,

// Pseudocode
resampleTwo = resampleF.resize(0.5);
resampleThree = resampleF.resize(0.25);
resampleFour = resampleF.resize(0.125);

再分别从resampleTwo、resampleThree、resampleFour的dataPtr 拷贝出数组,把得到的四个数组拼接成const char 像素数组[] 感觉这样内存占用应该会很高,拷贝效率是不是也很低

之前尝试在c++调用matlab的dll,可以这样setData

mwSize        mdim = buffer_len;
mwArray mdisp_image(1, mdim, mxUINT8_CLASS, mxREAL);
mdisp_image.SetData((mxUint8*)dataPtr, mdim);

如果不需要拷贝,是不是可以节省很多内存呢 我不知道c++中有没有其他的方法可以实现类似的操作,较低占用内存高效率地把buffer中的图片数据传递给<IOmeTiff读写器>

Silver-Fang commented 1 year ago

为什么要resample呢,你的原始图像和要写出的图像尺寸不同吗?原始数据到底是什么格式,我没有明白。

zhusihan-python commented 1 year ago

就是我的原始图像只有一层,如果这个时候直接保存,可以存成jpg,png格式,或者单层的tiff。 或者可以理解成就是打开了一个jpg图像数组。

我要存成多层的tiff,假设原始图像是40x的,,那我需要在把原始图像缩小成1/4,写20x的一层 把原始图像缩小成1/16,写10x的一层 最后是一个金字塔结构的图片 image

Silver-Fang commented 1 year ago

也就是说,你需要输出多张图,并且它们具有不同的尺寸?尺寸不同的图是不能拼成一个方形数组的。 如果你需要在TIFF内存储每层尺寸不同的多张图像,那你可能只能用标准libtiff,本库的快速读写是假定你的图像是一个规整的方形数组,如果不是的话就无法支持。

zhusihan-python commented 1 year ago

是的 不能拼成一个方形数组 但是他们都在一个tiff文件里 这是一个四层金字塔结构tiff的信息,用上面matlab的writesvs函数生成的 D:>tifftools dump matlab_writesvs_big.tiff

-- matlab_writesvs_big.tiff --
Header: 0x4949 <little-endian> <ClassicTIFF>
Directory 0: offset 972625398 (0x39f915f6)
  ImageWidth 256 (0x100) SHORT: 50273
  ImageLength 257 (0x101) SHORT: 48039
  BitsPerSample 258 (0x102) SHORT: <3> 8 8 8
  Compression 259 (0x103) SHORT: 7 (JPEG 7 (0x7))
  Photometric 262 (0x106) SHORT: 2 (RGB 2 (0x2))
  ImageDescription 270 (0x10E) ASCII: Aperio Image Library |AppMag = 40|MPP = 0.265018
  SamplesPerPixel 277 (0x115) SHORT: 3
  RowsPerStrip 278 (0x116) SHORT: 16
  PlanarConfig 284 (0x11C) SHORT: 1 (Chunky 1 (0x1))
  Software 305 (0x131) ASCII: SVT-1000
  TileWidth 322 (0x142) SHORT: 240
  TileLength 323 (0x143) SHORT: 240
  TileOffsets 324 (0x144) LONG: <42210> 8 2075 4142 6209 8276 10343 12410 14477 16544 18611 20678 22745 24812 26879 37780 60378 82266 104243 125002 145916 ...
  TileByteCounts 325 (0x145) LONG: <42210> 2067 2067 2067 2067 2067 2067 2067 2067 2067 2067 2067 2067 2067 10901 22598 21888 21977 20759 20914 20321 ...
  JPEGTables 347 (0x15B) UNDEFINED: <289> b'\xff\xd8\xff\xdb\x00C\x00\x05\x03\x04\x04\x04\x03\x05\x04\x04\x04\x05\x05\x05' ... (estimated quality: 85)
Directory 1: offset 1298856340 (0x4d6af994)
  ImageWidth 256 (0x100) SHORT: 25136
  ImageLength 257 (0x101) SHORT: 24019
  BitsPerSample 258 (0x102) SHORT: <3> 8 8 8
  Compression 259 (0x103) SHORT: 7 (JPEG 7 (0x7))
  Photometric 262 (0x106) SHORT: 2 (RGB 2 (0x2))
  ImageDescription 270 (0x10E) ASCII: Aperio Image Library |AppMag = 20|MPP = 0.51
  SamplesPerPixel 277 (0x115) SHORT: 3
  RowsPerStrip 278 (0x116) SHORT: 16
  PlanarConfig 284 (0x11C) SHORT: 1 (Chunky 1 (0x1))
  Software 305 (0x131) ASCII: SVT-1000
  TileWidth 322 (0x142) SHORT: 240
  TileLength 323 (0x143) SHORT: 240
  TileOffsets 324 (0x144) LONG: <10605> 972963619 972965686 972967753 972969820 972971887 972973954 972976021 972983989 973011729 973038538 973063338 973087831 973112952 973136750 973160226 973184764 973208194 973231950 973258353 973280985 ...
  TileByteCounts 325 (0x145) LONG: <10605> 2067 2067 2067 2067 2067 2067 7968 27740 26809 24800 24493 25121 23798 23476 24538 23430 23756 26403 22632 27294 ...
  JPEGTables 347 (0x15B) UNDEFINED: <289> b'\xff\xd8\xff\xdb\x00C\x00\x05\x03\x04\x04\x04\x03\x05\x04\x04\x04\x05\x05\x05' ... (estimated quality: 85)
Directory 2: offset 1403376936 (0x53a5d528)
  ImageWidth 256 (0x100) SHORT: 12568
  ImageLength 257 (0x101) SHORT: 12009
  BitsPerSample 258 (0x102) SHORT: <3> 8 8 8
  Compression 259 (0x103) SHORT: 7 (JPEG 7 (0x7))
  Photometric 262 (0x106) SHORT: 2 (RGB 2 (0x2))
  ImageDescription 270 (0x10E) ASCII: Aperio Image Library
  SamplesPerPixel 277 (0x115) SHORT: 3
  RowsPerStrip 278 (0x116) SHORT: 16
  PlanarConfig 284 (0x11C) SHORT: 1 (Chunky 1 (0x1))
  Software 305 (0x131) ASCII: SVT-1000
  TileWidth 322 (0x142) SHORT: 240
  TileLength 323 (0x143) SHORT: 240
  TileOffsets 324 (0x144) LONG: <2703> 1298941717 1298952188 1298966029 1298980721 1299003834 1299029844 1299055124 1299081650 1299107269 1299133595 1299159845 1299184166 1299210632 1299235347 1299259046 1299283876 1299309761 1299333929 1299358486 1299383097 ...
  TileByteCounts 325 (0x145) LONG: <2703> 10471 13841 14692 23113 26010 25280 26526 25619 26326 26250 24321 26466 24715 23699 24830 25885 24168 24557 24611 22555 ...
  JPEGTables 347 (0x15B) UNDEFINED: <289> b'\xff\xd8\xff\xdb\x00C\x00\x05\x03\x04\x04\x04\x03\x05\x04\x04\x04\x05\x05\x05' ... (estimated quality: 85)
Directory 3: offset 1429886248 (0x553a5528)
  ImageWidth 256 (0x100) SHORT: 6284
  ImageLength 257 (0x101) SHORT: 6004
  BitsPerSample 258 (0x102) SHORT: <3> 8 8 8
  Compression 259 (0x103) SHORT: 7 (JPEG 7 (0x7))
  Photometric 262 (0x106) SHORT: 2 (RGB 2 (0x2))
  ImageDescription 270 (0x10E) ASCII: Aperio Image Library
  SamplesPerPixel 277 (0x115) SHORT: 3
  RowsPerStrip 278 (0x116) SHORT: 16
  PlanarConfig 284 (0x11C) SHORT: 1 (Chunky 1 (0x1))
  Software 305 (0x131) ASCII: SVT-1000
  TileWidth 322 (0x142) SHORT: 240
  TileLength 323 (0x143) SHORT: 240
  TileOffsets 324 (0x144) LONG: <702> 1403399073 1403412315 1403429586 1403449065 1403468923 1403489291 1403508156 1403525771 1403545214 1403566195 1403585685 1403607586 1403627132 1403648336 1403669276 1403689086 1403707550 1403726893 1403746298 1403764216 ...
  TileByteCounts 325 (0x145) LONG: <702> 13242 17271 19479 19858 20368 18865 17615 19443 20981 19490 21901 19546 21204 20940 19810 18464 19343 19405 17918 19067 ...
  JPEGTables 347 (0x15B) UNDEFINED: <289> b'\xff\xd8\xff\xdb\x00C\x00\x05\x03\x04\x04\x04\x03\x05\x04\x04\x04\x05\x05\x05' ... (estimated quality: 85)

image

zhusihan-python commented 1 year ago

又看了一遍readme,OME规范要求将5个维度的尺寸全部记录在首IFD标签中,这隐含地要求了所有IFD图像帧必须具有相同的高度、宽度和位深度。 我想做的也不是要每一层都不一样的大小,只要有软件能解析打开,进行缩放和平移就可以。

如果满足每一层的图像高度和宽度都是一样的前提,如何创建一个数组是引用buffer里的数据,而不是拷贝一份数据,传递到<IOmeTiff读写器>呢 我看matlab封装的setData底层是这样一个函数 LIBMWMCLMCR_API_EXTERN_C int array_ref_set_numeric_mxUint8(array_ref *obj, const mxUint8* x, mwSize len); 应该是没有拷贝的动作

Silver-Fang commented 1 year ago

那你需要计算出图像的各维尺寸,用这个尺寸创建图像文件,然后用写出像素,将buffer指针传入就行了。传入指针就是不发生拷贝的。但是那之后写出到磁盘的过程无论如何肯定是要拷贝的,这个不可能避免。

zhusihan-python commented 1 year ago

集成到真实图像输出的项目,编译通过可以写入了,写出的tif文件格式解析不了,缺OME-XML tags信息,用image_scope这些软件打开,直接就崩溃退出了 这是写入的代码,输入的图像只有一层,就只写了一层

uint16_t 图像宽度 = imageSize[0];
uint16_t 图像高度 = imageSize[1];
uint16_t 图像层数 = 1;
const 颜色 通道颜色[] = { 常用颜色::红色, 常用颜色::绿色, 常用颜色::蓝色 };
wstring    wideusername;
for (int i = 0; i < outFilename.length(); ++i)
  wideusername += wchar_t(outFilename[i]);
cout << "开始覆盖创建:" << outFilename.c_str() << endl;

IOmeTiff读写器 * const 读写器 =
  IOmeTiff读写器::覆盖创建(wideusername.c_str(),
                           像素类型::UINT8,
                           图像宽度,
                           图像高度,
                           1,
                           图像层数,
                           1,
                           通道颜色,
                           维度顺序::XYCZT);
读写器->写出像素((uint8_t*)dataPtr);
cout << "写出像素finished" << endl;
delete 读写器;

这是写出图像的文件信息: D:>tifftools dump image5D.tif

-- image5D.tif --
Header: 0x4949 <little-endian> <BigTIFF>
Directory 0: offset 63 (0x3f)
  ImageWidth 256 (0x100) SHORT: 20202
  ImageLength 257 (0x101) SHORT: 12133
  BitsPerSample 258 (0x102) SHORT: 8
  Compression 259 (0x103) SHORT: 1 (None 1 (0x1))
  Photometric 262 (0x106) SHORT: 1 (MinIsBlack 1 (0x1))
  ImageDescription 270 (0x10E) ASCII: <?xml version="1.0"?>

  StripOffsets 273 (0x111) LONG8: 615
  RowsPerStrip 278 (0x116) SHORT: 12133
  StripByteCounts 279 (0x117) LONG: 245110866
  XResolution 282 (0x11A) RATIONAL: 1 1 (1)
  YResolution 283 (0x11B) RATIONAL: 1 1 (1)
  ResolutionUnit 296 (0x128) SHORT: 1 (None 1 (0x1))
  SampleFormat 339 (0x153) SHORT: 1 (uint 1 (0x1))

用pyometiff打开文件,提示没有OME-XML tags信息 image 图像数据倒是有,shape不对,传入的图像是20202 12133 3,写出的数据只有12133 * 20202 image

Silver-Fang commented 1 year ago

我不知道image_scope是否支持OME-TIFF。但是pyometiff出现问题,是因为它依赖的tifffile库存在一个问题

    def is_ome(self) -> bool:
        """Page contains OME-XML in ImageDescription tag."""
        if self.index != 0 or not self.description:
            return False
        return self.description[-4:] == 'OME>'  # and [:13] == '<?xml version'

它通过一种非常简单粗暴的方式验证OME-XML是否正确,也就是看图像描述字符串的最后4个字符是否恰好为'OME>'。这种方法显然很不鲁棒,因为本库使用pugixml库生成XML文本,生成的文本最后会多一个换行符,就因为这多出来的一个换行符就被tifffile库认为不是合法的OME-XML…… 我用ImageJ打开图像就能正常读入,ImageJ显然正确地识别了带末尾换行符的XML文本。 我上传了一个新版本,理论上应该已经去掉了换行符,你再试试看?

zhusihan-python commented 1 year ago

用fiji版本的imageJ可以打开了,看起来像是只写了一个通道,成灰度图了,而且有错乱的情况, 是不是rgbrgbrgb这种顺序的buffer不能直接传写出像素呢,我的生成代码应该有地方没处理对 image 如果是正常的图片,应该是这样 image

这是写出的tif文件 https://www.123pan.com/s/IEFUVv-iQb5d

Silver-Fang commented 1 year ago

看起来你的实际维度顺序是CXYZ,不是XYCZT。你需要重新排列你的图像维度顺序,必须与创建TIFF时指定的顺序一致。这个操作类似于MATLAB的permute函数,如果你不知道如何用C++实现,可以使用MATLAB生成C++代码,或者上网搜索C++的维度重排算法。本库暂时不能代替你完成这个操作。 但是,你可以使用像素指针功能,直接取得一个指向磁盘文件的指针。将重排结果直接写入这个指针,可以减少一次拷贝。

zhusihan-python commented 1 year ago

维度顺序XYCZT不变,把图像宽度乘以3之后,出来一个拉宽3倍的完整灰度图,是不是说明维度顺序没问题呢

uint16_t 图像宽度 = imageSize[0]*3;
uint16_t 图像高度 = imageSize[1];
uint16_t 图像层数 = 1;
const 颜色 通道颜色[] = { 常用颜色::红色, 常用颜色::绿色, 常用颜色::蓝色 };
wstring    wideusername;
for (int i = 0; i < outFilename.length(); ++i)
  wideusername += wchar_t(outFilename[i]);
cout << "开始覆盖创建:" << outFilename.c_str() << endl;

IOmeTiff读写器 * const 读写器 =
  IOmeTiff读写器::覆盖创建(wideusername.c_str(),
                           像素类型::UINT8,
                           图像宽度,
                           图像高度,
                           1, 图像层数, 1, 通道颜色, 维度顺序::XYCZT);
读写器->写出像素((uint8_t*)dataPtr);
cout << "写出像素finished" << endl;
delete 读写器;

4e53ba8c56886811aa81b4e5168b60e

Silver-Fang commented 1 year ago

你创建图像的时候指定的SizeC参数是错误的,你指定了通道颜色有红绿蓝三个,所以SizeC应该是3。 其次,你需要在Fiji的【Edit\Options\ImageJ2】对话框中勾选【Use SCIFIO when opening files (BETA!)】才能正确看到通道颜色。 image 我不知道你拉宽3倍意义何在,你可能需要重新考虑一下维度顺序的意义。 维度顺序CXYZ: (R,X0,Y0,Z0)(G,X0,Y0,Z0)(B,X0,Y0,Z0)(R,X1,Y0,Z0)(G,X1,Y0,Z0)(B,X1,Y0,Z0)(R,X0,Y1,Z0)…… 维度顺序XYCZ: (X0,Y0,Z0,R)(X1,Y0,Z0,R)(X0,Y1,Z0,R)(X1,Y1,Z0,R)……(X0,Y0,Z0,G)(X1,Y0,Z0,G)(X0,Y1,Z0,G)(X1,Y1,Z0,G)

zhusihan-python commented 1 year ago

我理解我的数据是这样,假设buffer里都是红色的像素点 rgb就是0,0,255 0,0,255,0,0,255,0,0,255,0,0,255,0,0,255,0,0,255,0,0,255,0,0,255,0,0,255,0,0,255,0,0,255,0,0,255... 我对应不上哪一部分是x,y和z,感觉是缺失这些维度的数据

我的原始数据也是从扫描仪扫出来的小图片,用itk拼接出来的一张大图,大图里的data就是这个buffer,但是图片是没有深度层级,和坐标位置x,y这些信息的,只有三个通道的像素信息, 这种情况还可以使用image5D来写ome-tiff吗

zhusihan-python commented 1 year ago

写磁盘快是真的快,固态上700m写了3s,6.74g写了16.2s

Silver-Fang commented 1 year ago

X就是宽度,Y就是高度,你只有单层的话就没有Z,C就是RGB。你的维度顺序是CXY,必须改成XYC