Графика на индексированном изображении

Я получаю ошибку:

"Графический объект не может быть создан из изображения, имеющего индексированный формат пикселей".

в функции:

public static void AdjustImage(ImageAttributes imageAttributes, Image image)
{
        Rectangle rect = new Rectangle(0, 0, image.Width, image.Height);

        Graphics g = Graphics.FromImage(image);       
        g.InterpolationMode = InterpolationMode.HighQualityBicubic;
        g.DrawImage(image, rect, 0, 0, image.Width, image.Height, GraphicsUnit.Pixel, imageAttributes);
        g.Dispose();
}

Я хотел бы спросить вас, как я могу это исправить?

4 ответа

Решение

Ссылаясь на это, это может быть решено путем создания пустого растрового изображения с такими же размерами и правильного PixelFormat и рисования на этом растровом изображении.

// The original bitmap with the wrong pixel format. 
// You can check the pixel format with originalBmp.PixelFormat
Bitmap originalBmp = new (Bitmap)Image.FromFile("YourFileName.gif");

// Create a blank bitmap with the same dimensions
Bitmap tempBitmap = new Bitmap(originalBmp.Width, originalBmp.Height);

// From this bitmap, the graphics can be obtained, because it has the right PixelFormat
using(Graphics g = Graphics.FromImage(tempBitmap))
{
    // Draw the original bitmap onto the graphics of the new bitmap
    g.DrawImage(originalBmp, 0, 0);
    // Use g to do whatever you like
    g.DrawLine(...);
}

// Use tempBitmap as you would have used originalBmp
return tempBitmap;

Самый простой способ - создать новое изображение, например так:

Bitmap EditableImg = new Bitmap(IndexedImg);

Он создает новое изображение точно так же, как оригинал со всем его содержимым.

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

Для тех, кто все еще видит это все эти годы спустя... правильный способ нарисовать изображение на существующем (8-битном) проиндексированном изображении это:

  • Перейдите по всем пикселям изображения, которое вы хотите вставить, и для каждого цвета найдите наиболее близкое соответствие в цветовой палитре целевого изображения и сохраните его индекс в байтовом массиве.
  • Откройте массив вспомогательных байтов индексированного изображения, используя LockBits, и вставьте в него ваши совпадающие байты в нужном месте, циклически перебирая соответствующие индексы, используя высоту и шаг изображения.

Это не легкая задача, но, безусловно, возможно. Если вставленное изображение также проиндексировано и содержит более 256 пикселей, вы можете ускорить процесс, выполнив сопоставление цветов на палитре, а не на реальных данных изображения, затем получите вспомогательные байты из другого проиндексированного изображения и переназначьте их используя созданное отображение.

Обратите внимание, что все это относится только к восьмибитным. Если ваше изображение является четырехбитным или однобитным, самый простой способ обработать его - сначала преобразовать его в 8-битный, чтобы вы могли обрабатывать его как один байт на пиксель, а затем преобразовывать обратно.

Для получения дополнительной информации см. Как я могу работать с 1-битными и 4-битными изображениями?

Хотя принятый ответ работает, он создает новое 32-битное изображение ARGB из индексированного растрового изображения.

Чтобы управлять индексированными растровыми изображениями напрямую, вы можете использовать эту библиотеку (внимание: бессовестная самореклама). ЕгоGetReadWriteBitmapDataрасширение позволяет создавать доступный для записи управляемый метод доступа даже для индексированных форматов пикселей.

И тогда вы можете использовать один изDrawIntoметоды, которые можно использовать аналогично Graphics.DrawImage. Конечно, поскольку целевое растровое изображение индексируется, операция рисования должна квантовать пиксели, используя цвета целевой палитры, но есть своего рода перегрузки, которые могут использовать сглаживание для сохранения большего количества деталей изображения.

Пример использования (см. дополнительные примеры по ссылкам выше):

      using (IReadWriteBitmapData indexedTarget = myIndexedBitmap.GetReadWriteBitmapData())
using (IReadableBitmapData source = someTrueColorBitmap.GetReadableBitmapData())
{
    // or DrawIntoAsync if you want to use async-await
    source.DrawInto(indexedTarget, targetRect, OrderedDitherer.Bayer8x8);
}

Примеры изображений:

Все изображения ниже были созданы с помощью PixelFormat.Format8bppIndexedформат с палитрой по умолчанию, а значок 256x256 и радуга с альфа-градиентом были нарисованы друг над другом. Обратите внимание, что смешивание максимально используется с доступной палитрой.

Отказ от ответственности : Конечно, библиотека также имеет некоторые ограничения по сравнению с Graphics, например, нет методов рисования формы. Но в худшем случае вы все равно можете использовать принятый ответ, а затем вызватьConvertPixelFormatметод в конце, если вам нужно получить индексированный результат.

Другие вопросы по тегам