我可以从 FreeImage 导出的最高位深度灰度图像是多少?
What is the highest bit depth greyscale image I can export from FreeImage?
作为上下文,我正在构建一个需要相对极端细节的地形程序。我不希望这些文件很小,而且它们不需要在显示器上正式查看,只需要具有非常高的分辨率即可。
我知道大多数图像格式限制为 8 bpp,这是由于两种显示器(以合理的价格)和人类感知的标准限制。然而,2⁸ 只是 256 个可能的值,这会在重建的位移中引起平稳伪影。 2¹⁶ 可能足够接近 65,536 个可能值,我已经达到了。
我正在使用 FreeImage 和 DLang 构建数据,目前在 Linux Mint 机器上。
但是,当我继续使用 2³² 时,软件支持似乎对我来说消失了。我尝试了这种形式的 TIFF,但似乎无法解释它,显示完全(或大部分)透明的图像(记住我没想到任何显示器真正支持通道的 2³² 阴影)或抱怨无法解码 RGB 数据。我想这是因为它被假定为 RGB 或 RGBA 图像。
FreeImage 对大多数用途都有很好的记录,但我现在想知道,我可以导出的最高精度单通道格式是什么,我该怎么做?谁能举个例子?在任何典型的非家用图像格式中,我真的仅限于 16 位吗?我知道这对于医学成像来说已经足够高了,但我敢肯定我不是第一个试图瞄准更高目标的人,而且我们科学界人士对我们的精度水平可能非常雄心勃勃……
我的代码是否犯了一个明显的错误?对于这种精度,我应该尝试其他方法吗?
这是我的代码。
有效的 16 位 TIFF
void writeGrayscaleMonochromeBitmap(const double width, const double height) {
FIBITMAP *bitmap = FreeImage_AllocateT(FIT_UINT16, cast(int)width, cast(int)height);
for(int y = 0; y < height; y++) {
ubyte *scanline = FreeImage_GetScanLine(bitmap, y);
for(int x = 0; x < width; x++) {
ushort v = cast(ushort)((x * 0xFFFF)/width);
ubyte[2] bytes = nativeToLittleEndian(cast(ushort)(x/width * 0xFFFF));
scanline[x * ushort.sizeof + 0] = bytes[0];
scanline[x * ushort.sizeof + 1] = bytes[1];
}
}
FreeImage_Save(FIF_TIFF, bitmap, "test.tif", TIFF_DEFAULT);
FreeImage_Unload(bitmap);
}
没有真正工作的 32 位 TIFF
void writeGrayscaleMonochromeBitmap32(const double width, const double height) {
FIBITMAP *bitmap = FreeImage_AllocateT(FIT_UINT32, cast(int)width, cast(int)height);
writeln(width, ", ", height);
writeln("Width: ", FreeImage_GetWidth(bitmap));
for(int y = 0; y < height; y++) {
ubyte *scanline = FreeImage_GetScanLine(bitmap, y);
writeln(y, ": ", scanline);
for(int x = 0; x < width; x++) {
//writeln(x, " < ", width);
uint v = cast(uint)((x/width) * 0xFFFFFFFF);
writeln("V: ", v);
ubyte[4] bytes = nativeToLittleEndian(v);
scanline[x * uint.sizeof + 0] = bytes[0];
scanline[x * uint.sizeof + 1] = bytes[1];
scanline[x * uint.sizeof + 2] = bytes[2];
scanline[x * uint.sizeof + 3] = bytes[3];
}
}
FreeImage_Save(FIF_TIFF, bitmap, "test32.tif", TIFF_NONE);
FreeImage_Unload(bitmap);
}
感谢您的指点。
对于单个通道,FreeImage 提供的最高为 32 位,如 FIT_UINT32。但是,文件格式必须能够做到这一点,目前看来,只有 TIFF 似乎可以胜任这项任务(请参阅 Stanford Documentation 第 104 页)。此外,大多数监视器无法表示每个样本超过 8 位,在极端情况下为 12 位,因此很难读回数据并使其正确呈现。
一个单元测试涉及在编组到位图之前比较字节,然后从同一位图中采样,表明数据实际上正在被编码。
要将数据压印为 16 位灰度(目前受 J2K、JP2、PGM、PGMRAW、PNG 和 TIF 支持),您可以这样做:
void toFreeImageUINT16PNG(string fileName, const double width, const double height, double[] data) {
FIBITMAP *bitmap = FreeImage_AllocateT(FIT_UINT16, cast(int)width, cast(int)height);
for(int y = 0; y < height; y++) {
ubyte *scanline = FreeImage_GetScanLine(bitmap, y);
for(int x = 0; x < width; x++) {
//This magic has to happen with the y-coordinate in order to keep FreeImage from following its default behavior, and generating
//the image upside down.
ushort v = cast(ushort)(data[cast(ulong)(((height - 1) - y) * width + x)] * 0xFFFF); //((x * 0xFFFF)/width);
ubyte[2] bytes = nativeToLittleEndian(v);
scanline[x * ushort.sizeof + 0] = bytes[0];
scanline[x * ushort.sizeof + 1] = bytes[1];
}
}
FreeImage_Save(FIF_PNG, bitmap, fileName.toStringz);
FreeImage_Unload(bitmap);
}
您当然希望针对您的目标文件类型进行调整。要导出为 48 位 RGB16,您可以这样做。
void toFreeImageColorPNG(string fileName, const double width, const double height, double[] data) {
FIBITMAP *bitmap = FreeImage_AllocateT(FIT_RGB16, cast(int)width, cast(int)height);
uint pitch = FreeImage_GetPitch(bitmap);
uint bpp = FreeImage_GetBPP(bitmap);
for(int y = 0; y < height; y++) {
ubyte *scanline = FreeImage_GetScanLine(bitmap, y);
for(int x = 0; x < width; x++) {
ulong offset = cast(ulong)((((height - 1) - y) * width + x) * 3);
ushort r = cast(ushort)(data[(offset + 0)] * 0xFFFF);
ushort g = cast(ushort)(data[(offset + 1)] * 0xFFFF);
ushort b = cast(ushort)(data[(offset + 2)] * 0xFFFF);
ubyte[6] bytes = nativeToLittleEndian(r) ~ nativeToLittleEndian(g) ~ nativeToLittleEndian(b);
scanline[(x * 3 * ushort.sizeof) + 0] = bytes[0];
scanline[(x * 3 * ushort.sizeof) + 1] = bytes[1];
scanline[(x * 3 * ushort.sizeof) + 2] = bytes[2];
scanline[(x * 3 * ushort.sizeof) + 3] = bytes[3];
scanline[(x * 3 * ushort.sizeof) + 4] = bytes[4];
scanline[(x * 3 * ushort.sizeof) + 5] = bytes[5];
}
}
FreeImage_Save(FIF_PNG, bitmap, fileName.toStringz);
FreeImage_Unload(bitmap);
}
最后,要编码 UINT32 灰度图像(目前仅限于 TIFF),您可以这样做。
void toFreeImageTIF32(string fileName, const double width, const double height, double[] data) {
FIBITMAP *bitmap = FreeImage_AllocateT(FIT_UINT32, cast(int)width, cast(int)height);
//DEBUG
int xtest = cast(int)(width/2);
int ytest = cast(int)(height/2);
uint comp1a = cast(uint)(data[cast(ulong)(((height - 1) - ytest) * width + xtest)] * 0xFFFFFFFF);
writeln("initial: ", nativeToLittleEndian(comp1a));
for(int y = 0; y < height; y++) {
ubyte *scanline = FreeImage_GetScanLine(bitmap, y);
for(int x = 0; x < width; x++) {
//This magic has to happen with the y-coordinate in order to keep FreeImage from following its default behavior, and generating
//the image upside down.
ulong i = cast(ulong)(((height - 1) - y) * width + x);
uint v = cast(uint)(data[i] * 0xFFFFFFFF);
ubyte[4] bytes = nativeToLittleEndian(v);
scanline[x * uint.sizeof + 0] = bytes[0];
scanline[x * uint.sizeof + 1] = bytes[1];
scanline[x * uint.sizeof + 2] = bytes[2];
scanline[x * uint.sizeof + 3] = bytes[3];
}
}
//DEBUG
ulong index = cast(ulong)(xtest * uint.sizeof);
writeln("Final: ", FreeImage_GetScanLine(bitmap, ytest)
[index .. index + uint.sizeof]);
FreeImage_Save(FIF_TIFF, bitmap, fileName.toStringz);
FreeImage_Unload(bitmap);
}
我还没有找到任何其他人构建的程序,它可以很容易地在监视器的可用调色板上渲染 32 位灰度图像。然而,我留下了我的检查代码,它会在顶部 DEBUG 和底部始终写出相同的数组,这对我来说已经足够一致了。
希望这对以后的其他人有所帮助。
作为上下文,我正在构建一个需要相对极端细节的地形程序。我不希望这些文件很小,而且它们不需要在显示器上正式查看,只需要具有非常高的分辨率即可。
我知道大多数图像格式限制为 8 bpp,这是由于两种显示器(以合理的价格)和人类感知的标准限制。然而,2⁸ 只是 256 个可能的值,这会在重建的位移中引起平稳伪影。 2¹⁶ 可能足够接近 65,536 个可能值,我已经达到了。
我正在使用 FreeImage 和 DLang 构建数据,目前在 Linux Mint 机器上。
但是,当我继续使用 2³² 时,软件支持似乎对我来说消失了。我尝试了这种形式的 TIFF,但似乎无法解释它,显示完全(或大部分)透明的图像(记住我没想到任何显示器真正支持通道的 2³² 阴影)或抱怨无法解码 RGB 数据。我想这是因为它被假定为 RGB 或 RGBA 图像。
FreeImage 对大多数用途都有很好的记录,但我现在想知道,我可以导出的最高精度单通道格式是什么,我该怎么做?谁能举个例子?在任何典型的非家用图像格式中,我真的仅限于 16 位吗?我知道这对于医学成像来说已经足够高了,但我敢肯定我不是第一个试图瞄准更高目标的人,而且我们科学界人士对我们的精度水平可能非常雄心勃勃……
我的代码是否犯了一个明显的错误?对于这种精度,我应该尝试其他方法吗?
这是我的代码。
有效的 16 位 TIFF
void writeGrayscaleMonochromeBitmap(const double width, const double height) {
FIBITMAP *bitmap = FreeImage_AllocateT(FIT_UINT16, cast(int)width, cast(int)height);
for(int y = 0; y < height; y++) {
ubyte *scanline = FreeImage_GetScanLine(bitmap, y);
for(int x = 0; x < width; x++) {
ushort v = cast(ushort)((x * 0xFFFF)/width);
ubyte[2] bytes = nativeToLittleEndian(cast(ushort)(x/width * 0xFFFF));
scanline[x * ushort.sizeof + 0] = bytes[0];
scanline[x * ushort.sizeof + 1] = bytes[1];
}
}
FreeImage_Save(FIF_TIFF, bitmap, "test.tif", TIFF_DEFAULT);
FreeImage_Unload(bitmap);
}
没有真正工作的 32 位 TIFF
void writeGrayscaleMonochromeBitmap32(const double width, const double height) {
FIBITMAP *bitmap = FreeImage_AllocateT(FIT_UINT32, cast(int)width, cast(int)height);
writeln(width, ", ", height);
writeln("Width: ", FreeImage_GetWidth(bitmap));
for(int y = 0; y < height; y++) {
ubyte *scanline = FreeImage_GetScanLine(bitmap, y);
writeln(y, ": ", scanline);
for(int x = 0; x < width; x++) {
//writeln(x, " < ", width);
uint v = cast(uint)((x/width) * 0xFFFFFFFF);
writeln("V: ", v);
ubyte[4] bytes = nativeToLittleEndian(v);
scanline[x * uint.sizeof + 0] = bytes[0];
scanline[x * uint.sizeof + 1] = bytes[1];
scanline[x * uint.sizeof + 2] = bytes[2];
scanline[x * uint.sizeof + 3] = bytes[3];
}
}
FreeImage_Save(FIF_TIFF, bitmap, "test32.tif", TIFF_NONE);
FreeImage_Unload(bitmap);
}
感谢您的指点。
对于单个通道,FreeImage 提供的最高为 32 位,如 FIT_UINT32。但是,文件格式必须能够做到这一点,目前看来,只有 TIFF 似乎可以胜任这项任务(请参阅 Stanford Documentation 第 104 页)。此外,大多数监视器无法表示每个样本超过 8 位,在极端情况下为 12 位,因此很难读回数据并使其正确呈现。
一个单元测试涉及在编组到位图之前比较字节,然后从同一位图中采样,表明数据实际上正在被编码。
要将数据压印为 16 位灰度(目前受 J2K、JP2、PGM、PGMRAW、PNG 和 TIF 支持),您可以这样做:
void toFreeImageUINT16PNG(string fileName, const double width, const double height, double[] data) {
FIBITMAP *bitmap = FreeImage_AllocateT(FIT_UINT16, cast(int)width, cast(int)height);
for(int y = 0; y < height; y++) {
ubyte *scanline = FreeImage_GetScanLine(bitmap, y);
for(int x = 0; x < width; x++) {
//This magic has to happen with the y-coordinate in order to keep FreeImage from following its default behavior, and generating
//the image upside down.
ushort v = cast(ushort)(data[cast(ulong)(((height - 1) - y) * width + x)] * 0xFFFF); //((x * 0xFFFF)/width);
ubyte[2] bytes = nativeToLittleEndian(v);
scanline[x * ushort.sizeof + 0] = bytes[0];
scanline[x * ushort.sizeof + 1] = bytes[1];
}
}
FreeImage_Save(FIF_PNG, bitmap, fileName.toStringz);
FreeImage_Unload(bitmap);
}
您当然希望针对您的目标文件类型进行调整。要导出为 48 位 RGB16,您可以这样做。
void toFreeImageColorPNG(string fileName, const double width, const double height, double[] data) {
FIBITMAP *bitmap = FreeImage_AllocateT(FIT_RGB16, cast(int)width, cast(int)height);
uint pitch = FreeImage_GetPitch(bitmap);
uint bpp = FreeImage_GetBPP(bitmap);
for(int y = 0; y < height; y++) {
ubyte *scanline = FreeImage_GetScanLine(bitmap, y);
for(int x = 0; x < width; x++) {
ulong offset = cast(ulong)((((height - 1) - y) * width + x) * 3);
ushort r = cast(ushort)(data[(offset + 0)] * 0xFFFF);
ushort g = cast(ushort)(data[(offset + 1)] * 0xFFFF);
ushort b = cast(ushort)(data[(offset + 2)] * 0xFFFF);
ubyte[6] bytes = nativeToLittleEndian(r) ~ nativeToLittleEndian(g) ~ nativeToLittleEndian(b);
scanline[(x * 3 * ushort.sizeof) + 0] = bytes[0];
scanline[(x * 3 * ushort.sizeof) + 1] = bytes[1];
scanline[(x * 3 * ushort.sizeof) + 2] = bytes[2];
scanline[(x * 3 * ushort.sizeof) + 3] = bytes[3];
scanline[(x * 3 * ushort.sizeof) + 4] = bytes[4];
scanline[(x * 3 * ushort.sizeof) + 5] = bytes[5];
}
}
FreeImage_Save(FIF_PNG, bitmap, fileName.toStringz);
FreeImage_Unload(bitmap);
}
最后,要编码 UINT32 灰度图像(目前仅限于 TIFF),您可以这样做。
void toFreeImageTIF32(string fileName, const double width, const double height, double[] data) {
FIBITMAP *bitmap = FreeImage_AllocateT(FIT_UINT32, cast(int)width, cast(int)height);
//DEBUG
int xtest = cast(int)(width/2);
int ytest = cast(int)(height/2);
uint comp1a = cast(uint)(data[cast(ulong)(((height - 1) - ytest) * width + xtest)] * 0xFFFFFFFF);
writeln("initial: ", nativeToLittleEndian(comp1a));
for(int y = 0; y < height; y++) {
ubyte *scanline = FreeImage_GetScanLine(bitmap, y);
for(int x = 0; x < width; x++) {
//This magic has to happen with the y-coordinate in order to keep FreeImage from following its default behavior, and generating
//the image upside down.
ulong i = cast(ulong)(((height - 1) - y) * width + x);
uint v = cast(uint)(data[i] * 0xFFFFFFFF);
ubyte[4] bytes = nativeToLittleEndian(v);
scanline[x * uint.sizeof + 0] = bytes[0];
scanline[x * uint.sizeof + 1] = bytes[1];
scanline[x * uint.sizeof + 2] = bytes[2];
scanline[x * uint.sizeof + 3] = bytes[3];
}
}
//DEBUG
ulong index = cast(ulong)(xtest * uint.sizeof);
writeln("Final: ", FreeImage_GetScanLine(bitmap, ytest)
[index .. index + uint.sizeof]);
FreeImage_Save(FIF_TIFF, bitmap, fileName.toStringz);
FreeImage_Unload(bitmap);
}
我还没有找到任何其他人构建的程序,它可以很容易地在监视器的可用调色板上渲染 32 位灰度图像。然而,我留下了我的检查代码,它会在顶部 DEBUG 和底部始终写出相同的数组,这对我来说已经足够一致了。
希望这对以后的其他人有所帮助。