Есть ли способ извлечь индекс пикселя в индексированном цветном растровом изображении (C#)? - PullRequest
1 голос
/ 11 марта 2020

Я загрузил индексированное цветное изображение (8bppI) с уникальной палитрой в программу C#, и мне нужно получить доступ к index цветов в этом изображении. Однако единственной функцией доступа, по-видимому, является 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

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

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

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

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

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

1 Ответ

0 голосов
/ 11 марта 2020

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

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) очень похожим образом.

...