Есть ли способ извлечь индекс пикселя в растровом изображении с индексированным цветом (С#)?

Я загрузил индексированное цветное изображение (8bppI) с уникальной палитрой в программу на C#, и мне нужно получить доступ к индексу цветов в этом изображении. Однако кажется, что единственной функцией доступа является Bitmap.GetPixel(x,y), которая возвращает цвет, а не индекс. Когда тот же цвет вставляется обратно в растровое изображение того же формата и палитры, информация о цвете, по-видимому, неправильно интерпретируется как индекс, и все идет к черту. Вот упрощенная версия кода для ясности проблемы:

    public void CreateTerrainMap() {
        visualization = new Bitmap(width, height, PixelFormat.Format8bppIndexed);
        visualizationLock = new LockBitmap(visualization);
        Lock();

        // "TerrainIndex.bmp" is a 256x256 indexed colour image (8bpp) with a unique palette.
        // Debugging confirms that this image loads with its palette and format intact
        Bitmap terrainColours = new Bitmap("Resources\\TerrainIndex.bmp");
        visualization.Palette = terrainColours.Palette;

        Color c;

        for (int x = 0; x < width; x++) { 
            for (int y = 0; y < height; y++) {
                if (Terrain[x, y] < SeaLevel) {
                    c = Color.FromArgb(15); // Counterintuitively, this actually gives index 15 (represented as (0,0,0,15))
                } else {
                    heatIndex = <some number between 0 and 255>;
                    rainIndex = <some number between 0 and 255>;

                    if (IsCoastal(x, y)) {
                        c = Color.FromArgb(35); // Counterintuitively, this actually gives index 35 (represented as (0,0,0,35))
                    } else {
                        // This returns an argb colour rather than an index...
                        c = terrainColours.GetPixel(rainIndex, heatIndex);
                    }
                }
                // ...and this seemingly decides that the blue value is actually an index and sets the wrong colour entirely
                visualizationLock.SetPixel(x, y, c);
            }
        }
    }

TerrainIndex выглядит следующим образом: TerrainIndex.bmp

Палитра выглядит так: Палитра

Результат должен выглядеть следующим образом: Хорошо

Но вместо этого это выглядит так: Bad

Обратите внимание, что океаны (индекс 15) и побережья (индекс 35) выглядят правильно, но все остальное взято из неправильной части палитры.

Я не могу найти полезную информацию о работе с растровыми изображениями индексированных цветов в С#. Я действительно надеюсь, что кто-то может объяснить мне, что я могу делать неправильно, или указать мне правильное направление.


person Jibbles    schedule 11.03.2020    source источник
comment
Помогает ли эта библиотека? Он имеет метод GetColorIndex, который можно использовать для любых индексированных растровых изображений. но также поддерживает Get/SetColor для форматов индексированных пикселей. См. также пример здесь.   -  person György Kőszeg    schedule 11.03.2020
comment
Файл растрового изображения bmp представляет собой массив байтов. Структуру см. в Wiki: en.wikipedia.org/wiki/BMP_file_format   -  person jdweng    schedule 11.03.2020


Ответы (1)


Я создал ответ из своего комментария. Таким образом, «родное» решение выглядит примерно так (требуется разрешение небезопасного кода):

Bitmap visualization = new Bitmap(width, height, PixelFormat.Format8bppIndexed);
visualization.Palette = GetVisualizationPalette();
BitmapData visualizationData = visualization.LockBits(new Rectangle(Point.Empty, visualization.Size),
    ImageLockMode.WriteOnly, PixelFormat.Format8bppIndexed);
try
{
    unsafe
    {
        byte* row = (byte*)visualizationData.Scan0;
        for (int y = 0; y < visualizationData.Height; y++)
        {
            for (int x = 0; x < visualizationData.Width; x++)
            {
                // here you set the 8bpp palette index directly
                row[x] = GetHeatIndex(x, y);
            }

            row += visualizationData.Stride;
        }
    }
}
finally
{
    visualization.UnlockBits(visualizationData);
}

Или вы можете использовать эти библиотеки, а затем:

using KGySoft.Drawing;
using KGySoft.Drawing.Imaging;

// ...

Bitmap visualization = new Bitmap(width, height, PixelFormat.Format8bppIndexed);
visualization.Palette = GetVisualizationPalette();
using (IWritableBitmapData visualizationData = visualization.GetWritableBitmapData())
{
    for (int y = 0; y < visualizationData.Height; y++)
    {
        IWritableBitmapDataRow row = visualizationData[y];
        for (int x = 0; x < visualizationData.Width; x++)
        {
            // setting pixel by palette index
            row.SetColorIndex(x, GetHeatIndex(x, y));

            // or: directly by raw data (8bpp means 1 byte per pixel)
            row.WriteRaw<byte>(x, GetHeatIndex(x, y));

            // or: by color (automatically applies the closest palette index)
            row.SetColor(x, GetHeatColor(x, y));
        }
    }
}

Редактировать: А для чтения пикселей/индексов вы можете использовать terrainColors.GetReadableBitmapData(), так что вы сможете использовать rowTerrain.GetColorIndex(x) или rowTerrain.ReadRaw<byte>(x) очень похожим образом.

person György Kőszeg    schedule 11.03.2020