Какое изображение серой шкалы с наибольшей глубиной цвета я могу экспортировать из FreeImage? - PullRequest
0 голосов
/ 22 октября 2019

В качестве контекста я работаю над созданием топографической программы, которая требует относительно экстремальных деталей. Я не ожидаю, что файлы будут маленькими, и их формально не нужно просматривать на мониторе, им просто нужно иметь очень высокое разрешение.

Я знаю, что большинство форматов изображений ограничены 8 битами на дюйм,из-за стандартных ограничений на мониторы (по разумной цене) и на восприятие человеком. Тем не менее, 2⁸ - это всего лишь 256 возможных значений, что вызывает искажения плато при восстановленном смещении. 2¹⁶ может быть достаточно близко при 65 536 возможных значениях, которых я достиг.

Я использую FreeImage и DLang для построения данных, в настоящее время на машине Linux Mint.

Однако, когда япошел до 2³², поддержка программного обеспечения, казалось, исчезла на мне. Я попробовал TIFF этой формы, и ничто, казалось, не могло его интерпретировать, либо показывать полностью (или в основном) прозрачное изображение (помня, что я не ожидал, что какой-либо монитор действительно поддерживает 2,32 оттенка канала), либо жаловался на то, чтоневозможно декодировать данные 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);
}

Спасибо за любые указатели.

1 Ответ

0 голосов
/ 24 октября 2019

Для одного канала максимальный доступный из FreeImage является 32-битным, как FIT_UINT32. Тем не менее, формат файла должен быть способен к этому, и на данный момент только TIFF кажется подходящим для этой задачи (см. Стр. 104 Stanford Documentation ). Кроме того, большинство мониторов не способны отображать более 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, и этого достаточно для меня.

Надеюсь, это поможет кому-то еще в будущем.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...