什么是我可以从FreeImage导出的最高位深度灰度图像?

问题描述 投票:0回答:1

作为背景,我正在构建一个需要相对极端细节的地形程序。我不希望文件很小,并且它们不需要正式在监视器上查看,它们只需要非常高分辨率。

我知道,大多数显示器的格式都被限制为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);
}

感谢任何指针。

d tiff grayscale freeimage bpp
1个回答
0
投票

对于单个通道,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和底部DEBUG中写出相同的数组,这对我来说足够一致。

希望这会在将来帮助其他人。

© www.soinside.com 2019 - 2024. All rights reserved.