Как преобразовать 32-битное изображение RGBA в оттенки серого с сохранением альфа-канала

Я хотел бы в коде и по запросу преобразовать 32-битный объект изображения RGBA (изначально 32-битный PNG) в его 32-битный аналог в градациях серого.

Я уже прочитал несколько другие вопросы здесь, а также множество статей в Интернете. Я пробовал использовать ColorMatrix для этого, но, похоже, он не очень хорошо справляется с альфа-каналом. Пиксели, которые полностью непрозрачны, идеально подходят для оттенков серого. Кажется, что любой частично прозрачный пиксель плохо транслируется, поскольку в этих пикселях все еще есть оттенки цвета. Достаточно быть заметным.

ColorMatrix, который я использую, выглядит следующим образом:

new System.Drawing.Imaging.ColorMatrix(new float[][]{
                    new float[] {0.299f, 0.299f, 0.299f, 0, 0},
                    new float[] {0.587f, 0.587f, 0.587f, 0, 0},
                    new float[] {0.114f, 0.114f, 0.114f, 0, 0},
                    new float[] {     0,      0,      0, 1, 0},
                    new float[] {     0,      0,      0, 0, 1}
                    });

Как я читал, это довольно стандартная взвешенная матрица NTSC. Затем я использую его вместе с Graphics.DrawImage, но, как я уже сказал, частично прозрачные пиксели все еще окрашены. Я должен указать, что это отображение объекта Image через WinForms PictureBox на белом фоне. Может быть, это просто способ, которым PictureBox рисует свои изображения и обрабатывает прозрачные части? Цвета фона не влияют на это (оттенок цвета точно от исходного изображения), но, возможно, PictureBox неправильно перерисовывает прозрачные пиксели?

Я видел несколько методов, которые используют FormatConvertedBitmap вместе с OpacityMask. Я не пробовал, в основном потому, что я бы предпочел не импортировать PresentationCore.dll (не говоря уже о том, что это не будет работать в ограниченных приложениях .NET 2.0). Разве базовый материал System.Drawing. * Может выполнить эту простую процедуру? Или нет?


person Sean Hanley    schedule 16.06.2010    source источник
comment
Вы уверены, что цветовой оттенок не связан с тем, что скрывается за изображением? Пока значения RGB точно равны, никакого оттенка быть не должно. Отправьте вам определение ColorMatrix.   -  person ChrisF    schedule 16.06.2010
comment
Я уверен, что это не так. Изображение представлено через PictureBox. Фон PictureBox, конечно же, прозрачный, что на самом деле просто означает, что он использует цвет своего родителя, который в данном случае белый. Однако прозрачные края нового изображения в градациях серого по-прежнему имеют зеленый оттенок (исходное изображение было в основном зеленым).   -  person Sean Hanley    schedule 16.06.2010


Ответы (3)


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

person Dan Byström    schedule 16.06.2010
comment
Вот дерьмо, наверное, ты прав. Чувак, я действительно надеялся, что на этот раз нашел ошибку или что-то в этом роде! Но, как обычно, это просто я дурак. - person Sean Hanley; 16.06.2010

Благодаря danbystrom Праздное любопытство, я действительно перерисовывал поверх оригинала. Для всех, кто интересуется, вот исправленный метод, который я использовал:

using System.Drawing;
using System.Drawing.Imaging;

public Image ConvertToGrayscale(Image image)
{
    Image grayscaleImage = new Bitmap(image.Width, image.Height, image.PixelFormat);

    // Create the ImageAttributes object and apply the ColorMatrix
    ImageAttributes attributes = new System.Drawing.Imaging.ImageAttributes();
    ColorMatrix grayscaleMatrix = new ColorMatrix(new float[][]{
        new float[] {0.299f, 0.299f, 0.299f, 0, 0},
        new float[] {0.587f, 0.587f, 0.587f, 0, 0},
        new float[] {0.114f, 0.114f, 0.114f, 0, 0},
        new float[] {     0,      0,      0, 1, 0},
        new float[] {     0,      0,      0, 0, 1}
        });
    attributes.SetColorMatrix(grayscaleMatrix);

    // Use a new Graphics object from the new image.
    using (Graphics g = Graphics.FromImage(grayscaleImage))
    {
        // Draw the original image using the ImageAttributes created above.
        g.DrawImage(image,
                    new Rectangle(0, 0, grayscaleImage.Width, grayscaleImage.Height),
                    0, 0, grayscaleImage.Width, grayscaleImage.Height,
                    GraphicsUnit.Pixel,
                    attributes);
    }

    return grayscaleImage;
}
person Sean Hanley    schedule 16.06.2010
comment
Просто хотел сказать спасибо за это. Мне пришлось объявить тип данных grayscaleMatrix, чтобы этот метод работал автономно, но он отлично работает. Спасибо. gist.github.com/4f55bd017b69fe54785e58cf8e35b7b3 - person Christopher D. Emerson; 26.12.2018
comment
@ChrisEmerson Спасибо, я ни разу не заметил эту ошибку за все эти годы. Отредактировано и исправлено. - person Sean Hanley; 05.01.2019

Если вы конвертируете изображение в TGA, несжатый формат imaeg, вы можете использовать «RubyPixels» для непосредственного редактирования пиксельных данных, делая все, что вам заблагорассудится. Затем вы можете преобразовать его обратно в PNG.

Рекомендую делать преобразование с помощью ImageMagick, также из ruby.

person thomasfedb    schedule 16.06.2010
comment
Может быть, я не совсем понял, но меня не волнует исходный формат изображения (GIF, PNG, JPG и т. Д.) Или его преобразование для сохранения или что-то еще. Я просто указывал, что объект Image в .NET изначально был создан из PNG. Я использую этот объект Image непосредственно в среде WinForms и манипулирую его цветом для, в данном случае, отключенного вида. Я не собираюсь возвращать его обратно. Это просто для поседения на лету. Тем не менее, не могли бы вы дать ссылки на свои предложения на случай, если кому-то еще будет интересно? - person Sean Hanley; 16.06.2010
comment
Извините, в этом случае помочь вам не могу ... = ‹ - person thomasfedb; 16.06.2010