С# Преобразование 32bpp изображения в 8bpp

Я пытаюсь преобразовать изображение скриншота 32bpp в формат 8bpp (или 4bpp или 1bpp) с помощью С#. Я уже рассмотрел несколько ответов stackoverflow на похожие темы, и большинство из них предлагает варианты с использованием следующего кода:

public static Bitmap Convert(Bitmap oldbmp) 
{
    Bitmap newbmp = new Bitmap(oldbmp.Width, oldbmp.Height, PixelFormat.Format8bppIndexed);

    Graphics gr = Graphics.FromImage(newbmp);

    gr.PageUnit = GraphicsUnit.Pixel;
    gr.DrawImageUnscaled(oldbmp, 0, 0);

    return newbmp;
}

Однако, когда это выполняется, я получаю исключение: A graphics object cannot be created from an image that has an indexed pixel format. Я понимаю, что изображения 8, 4 и 1bpp имеют сопоставления таблиц цветов, а не сами пиксели изображения (как в 32 или 16bpp-изображениях), поэтому я предполагаю, что я где-то пропускаю какой-то шаг преобразования, но я довольно новичок в С# с фона на С++) и предпочли бы, чтобы это можно было использовать с использованием собственных вызовов С#, а не прибегать к PInvoking BitBlt и GetDIBits и т.д. Кто-нибудь может помочь мне решить эту проблему? Спасибо.

EDIT: я должен указать, что мне нужно, чтобы это было обратно совместимо с .NET framework 2.0

Ответы

Ответ 1

GDI + в целом имеет очень плохую поддержку индексированных форматов пикселей. Нет простого способа конвертировать изображение с 65536 или 16 миллионами цветов в один, который имеет только 2, 16 или 256. Цвета должны быть удалены из исходного изображения, и это преобразование с потерями, которое может иметь очень плохие результаты. Для этого есть несколько алгоритмов, ни одна из них не идеальна для каждого изображения. Это задание для графического редактора.

Есть один трюк, который я нашел. GDI + имеет кодировщик изображений для файлов GIF. Что графический формат, который имеет только 256 цветов, кодировщик должен ограничивать количество цветов. Он использует алгоритм сглаживания, подходящий для фотографий. У этого есть умение генерировать сетку, вы будете менее чем взволнованы, когда это произойдет. Используйте его так:

public static Image Convert(Bitmap oldbmp) {
    using (var ms = new MemoryStream()) {
        oldbmp.Save(ms, ImageFormat.Gif);
        ms.Position = 0;
        return Image.FromStream(ms);
    }
}

Возвращаемое изображение имеет формат пикселя 8bpp с позициями палитры, вычисленными кодером. При необходимости вы можете использовать его для растрового изображения. Безусловно, лучше всего просто не беспокоиться об индексированных форматах. Они относятся к каменному возрасту вычислений, когда память сильно ограничена. Или используйте профессиональный графический редактор.

Ответ 2

AForge библиотека отлично справляется с использованием Оттенки серого.

var bmp8bpp = Grayscale.CommonAlgorithms.BT709.Apply(bmp);

Этот класс является базовым классом для масштабирования изображений [...] Фильтр принимает цветные изображения 24, 32, 48 и 64 bpp и производит 8 (если источником является 24 или 32 bpp изображения) или 16 (если источник 48 или 64 bpp image) Изображение в черно-белом режиме bpp.

Ответ 3

Отрицательный шаг означает, что изображение снизу вверх (инвертировано). Просто используйте абсолют шага, если вы не заботитесь. Я знаю, что работает для 24bpp-изображений, не подозревая, работает ли он для других.

Ответ 4

Вы можете использовать System.Windows.Media.Imaging в AssemblyCore Assembly, чтобы посмотреть здесь для получения дополнительной информации