Преобразование изображения в оттенки серого

Есть ли способ преобразовать изображение в формат оттенков серого 16 бит на пиксель, вместо того, чтобы устанавливать для каждого из компонентов r, g и b значение яркости. Сейчас у меня есть BMP из файла.

Bitmap c = new Bitmap("filename");

Мне нужен Bitmap d, то есть версия c в оттенках серого. Я вижу конструктор, который включает System.Drawing.Imaging.PixelFormat, но не понимаю, как его использовать. Я новичок в обработке изображений и соответствующих библиотеках C #, но имею умеренный опыт работы с самим C #.

Любая помощь, ссылка на онлайн-источник, подсказка или предложение будут оценены.

РЕДАКТИРОВАТЬ: d - версия c в оттенках серого.


person 0fnt    schedule 15.02.2010    source источник


Ответы (6)


«Мне нужен Bitmap d, то есть оттенки серого. Я вижу конструктор, который включает System.Drawing.Imaging.PixelFormat, но я не понимаю, как его использовать».

Вот как это сделать

Bitmap grayScaleBP = new 
         System.Drawing.Bitmap(2, 2, System.Drawing.Imaging.PixelFormat.Format16bppGrayScale);

РЕДАКТИРОВАТЬ: преобразовать в оттенки серого.

             Bitmap c = new Bitmap("fromFile");
             Bitmap d;
             int x, y;

             // Loop through the images pixels to reset color.
             for (x = 0; x < c.Width; x++)
             {
                 for (y = 0; y < c.Height; y++)
                 {
                     Color pixelColor = c.GetPixel(x, y);
                     Color newColor = Color.FromArgb(pixelColor.R, 0, 0);
                     c.SetPixel(x, y, newColor); // Now greyscale
                 }
             }
            d = c;   // d is grayscale version of c  

Более быстрая версия от switchonthecode перейдите по ссылке для полного анализа:

public static Bitmap MakeGrayscale3(Bitmap original)
{
   //create a blank bitmap the same size as original
   Bitmap newBitmap = new Bitmap(original.Width, original.Height);

   //get a graphics object from the new image
   using(Graphics g = Graphics.FromImage(newBitmap)){

       //create the grayscale ColorMatrix
       ColorMatrix colorMatrix = new ColorMatrix(
          new float[][] 
          {
             new float[] {.3f, .3f, .3f, 0, 0},
             new float[] {.59f, .59f, .59f, 0, 0},
             new float[] {.11f, .11f, .11f, 0, 0},
             new float[] {0, 0, 0, 1, 0},
             new float[] {0, 0, 0, 0, 1}
          });

       //create some image attributes
       using(ImageAttributes attributes = new ImageAttributes()){

           //set the color matrix attribute
           attributes.SetColorMatrix(colorMatrix);

           //draw the original image on the new image
           //using the grayscale color matrix
           g.DrawImage(original, new Rectangle(0, 0, original.Width, original.Height),
                       0, 0, original.Width, original.Height, GraphicsUnit.Pixel, attributes);
       }
   }
   return newBitmap;
}
person Asad    schedule 15.02.2010
comment
Извините, я должен был сказать, что в моем вопросе я хочу, чтобы версия моего исходного растрового изображения в оттенках серого. Я отредактировал сообщение, чтобы отразить это. - person 0fnt; 15.02.2010
comment
Этот метод (пиксель за пикселем слишком медленный) проверьте эту статью здесь: switchonthecode.com/tutorials/ - person Luis; 12.01.2011
comment
Как изменить этот метод, чтобы учитывать только значения RGB, а не прозрачность? Спасибо - person Martina; 07.08.2015
comment
Я сделал первое редактирование, все белое было превращено в красный, и это все еще был истинный 24-битный цвет ... Я изменил код, чтобы использовать зеленый и синий, и получившееся изображение после этого выглядело таким же, как исходное. . - person Bryan; 13.08.2015
comment
Похоже, блог с исходной статьей больше не доступен. Ссылка или пояснение к матрице? - person amhed; 27.02.2016
comment
Посмотрите на WayBack Machine. - person Dour High Arch; 10.09.2018

Bitmap d = new Bitmap(c.Width, c.Height);

for (int i = 0; i < c.Width; i++)
{
    for (int x = 0; x < c.Height; x++)
    {
        Color oc = c.GetPixel(i, x);
        int grayScale = (int)((oc.R * 0.3) + (oc.G * 0.59) + (oc.B * 0.11));
        Color nc = Color.FromArgb(oc.A, grayScale, grayScale, grayScale);
        d.SetPixel(i, x, nc);
    }
}

Таким образом, он также сохраняет альфа-канал.
Наслаждайтесь.

person Vercas    schedule 23.10.2010
comment
Я знаю, что это старый ответ, но не могли бы вы подробнее рассказать о константах, которые вы умножаете на значения R, G, B? Я вижу, что в сумме получается 1,0, но есть ли какое-нибудь объяснение, почему? - person Lars Kristensen; 05.08.2013
comment
@lukegravitt, спасибо! :) - person Lars Kristensen; 07.08.2013
comment
Теперь, когда я сам использовал это, я должен добавить, что использовать его для небольших отключенных значков или в любом случае, когда нужно заметить разницу, - плохая идея. Результат в оттенках серого ужасно близок к цветному ... Трудно различить. - person Vercas; 08.08.2013
comment
Это решение кажется медленным. Первый цикл for должен проходить по высоте, а второй цикл (внутренний) должен проходить по ширине (cols). Изображения сохраняются строка за строкой в ​​пространстве диска. Когда вы переходите от одной строки к другой, вы перескакиваете с одного места на диске на другое. При итерации по ширине вы просто переходите к следующему значению на вашем диске. Вы можете ознакомиться с темой opencv ссылка, вы всегда видите, что нужно выполнять итерацию в строках (внешний) и столбцах (внутренний цикл). - person Christian Gold; 26.09.2016
comment
Эти имена переменных цикла встречаются повсюду ... x на самом деле является координатой y, и все изображение обрабатывается столбец за столбцом, потому что цикл высоты находится внутри цикла ширины. - person Nyerguds; 24.01.2018

В классе ToolStripRenderer есть статический метод с именем CreateDisabledImage. Его использование очень простое:

Bitmap c = new Bitmap("filename");
Image d = ToolStripRenderer.CreateDisabledImage(c);

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

person Szybki    schedule 29.09.2015
comment
Ой, спасибо тебе за это. Это соответствует внешнему виду изображений в отключенных элементах управления в WinForms, благодаря чему все выглядит согласованным. - person Jimmy; 27.12.2017
comment
Изображения имеют синий оттенок. Выглядит не так уж и красиво. - person Elmue; 22.07.2021

Ни один из приведенных выше примеров не создает 8-битных (8bpp) растровых изображений. Некоторое программное обеспечение, например обработка изображений, поддерживает только 8bpp. К сожалению, библиотеки MS .NET не имеют решения. Формат PixelFormat.Format8bppIndexed выглядит многообещающим, но после множества попыток я не смог заставить его работать.

Чтобы создать настоящий 8-битный растровый файл, вам необходимо создать правильные заголовки. В конечном итоге я нашел решение Grayscale library для создания 8-битного растрового изображения (BMP). файлы. Код очень простой:

Image image = Image.FromFile("c:/path/to/image.jpg");
GrayBMP_File.CreateGrayBitmapFile(image, "c:/path/to/8bpp/image.bmp");

Код этого проекта далеко не красивый, но он работает, с одной небольшой проблемой, которую легко исправить. Автор жестко установил разрешение изображения 10х10. Программы обработки изображений этого не любят. Чтобы исправить это, откройте GrayBMP_File.cs (да, я знаю, что такое забавное именование файлов) и замените строки 50 и 51 приведенным ниже кодом. В примере устанавливается разрешение 200x200, но вы должны изменить его на правильное число.

int resX = 200;
int resY = 200;
// horizontal resolution
Copy_to_Index(DIB_header, BitConverter.GetBytes(resX * 100), 24);
// vertical resolution 
Copy_to_Index(DIB_header, BitConverter.GetBytes(resY * 100), 28);
person Brent Matzelle    schedule 23.04.2011
comment
Я хотел бы протестировать его и проголосовать за него, но сейчас у меня мало времени. Спасибо за ответ. - person 0fnt; 23.04.2011
comment
Уловка с 8bpp заключается в том, что вам нужно отредактировать необработанный внутренний массив, поддерживающий изображение. Вы можете получить это, вызвав image.LockBits(), а затем скопировав / выгрузив данные с помощью Marshal.Copy. Погуглите, чтобы получить полный процесс. Обратите внимание, шаг изображения (строка, считываемая из данных) часто превышает фактическую ширину, так как она округляется до ближайшего значения, кратного 4 байтам. - person Nyerguds; 04.04.2017

Код ниже - самое простое решение:

Bitmap bt = new Bitmap("imageFilePath");

for (int y = 0; y < bt.Height; y++)
{
    for (int x = 0; x < bt.Width; x++)
    {
        Color c = bt.GetPixel(x, y);

        int r = c.R;
        int g = c.G;
        int b = c.B;
        int avg = (r + g + b) / 3;
        bt.SetPixel(x, y, Color.FromArgb(avg,avg,avg));
    }   
}

bt.Save("d:\\out.bmp");
person Prakash Ekhande    schedule 22.01.2015

Подведем итоги по нескольким пунктам: есть несколько попиксельных параметров, которые, будучи простыми, не работают быстро.

Комментарий @Luis со ссылкой на: (в архиве) https://web.archive.org/web/20110827032809/http://www.switchonthecode.com/tutorials/csharp-tutorial-convert-a-color-image-to-grayscale превосходно.

Он пробует три разных варианта и включает время для каждого.

person jeffreypriebe    schedule 16.08.2011
comment
Третий пример в приведенной выше ссылке очень эффективен и напрямую решил мою проблему. - person ford; 11.08.2012
comment
@RichardEverett Верно. Веб-архив спешит на помощь! Фиксированный. - person jeffreypriebe; 31.01.2016