Внимание!

Эта публикация перенесена в раздел уроков по адресу Графические фильтры на основе попиксельной обработки изображений (Часть 1).
К ней прикреплена новая отдельная ветка комментариев форума, которую вы можетет найти после текста публикации.
Обсуждение публикации рекуомендуется вести по новому адресу, который указан выше.

Графические фильтры на основе попиксельной обработки изображений (Часть 1)

Всем привет! В этом уроке я расскажу, как создать негатив и сепию.

Пример изображения после обработки:




Негатив

Негатив – это фильтр, который инвертирует цвета. Приступим к написанию кода. Сначала создадим основу для всех наших фильтров. Создадим новый класс с именем Filter, и впишем в него следующие функции и свойства:
Picture (свойство) – возвращает изображение.
Height (свойство) – возвращает высоту изображения.
Width (свойство) – возвращает ширину изображения.
BytesPerPixel (свойство) – возвращает количество байт на пиксель.
Safe_IMG_Scan0 (свойство) – возвращает управляемый указатель (IntPtr) на массив пикселей.
Unsafe_IMG_Scan0 (свойство) – возвращает неуправляемый указатель (*byte) на массив пикселей.
AllPixelsBytes (свойство)-возвращает количество байт, занимаемое всеми пикселями.
Unlock (функция) – разблокирует массив пикселей.
GetPixel (функция) – возвращает структуру RGB_Color со значениями цветов.
SetPixel (функция) – присваивает пикселю с координатами X,Y цвет, который задан в структуре RGB_Color.

Структура RGB_Color:
/*http://esate.ru, Alexei16*/


public struct RGB_Color
{
   public RGB_Color(int Red, int Green, int Blue)
   {
      R = Red;
      G = Green;
      B = Blue;
   }
   public int R, G, B;
}


Реализация класса Filter:
/*http://esate.ru, Alexei16*/


public class Filter
{ 
   private Bitmap UPicture = null;
   private BitmapData BmpData = null;
   private unsafe byte* Begin = (byte*)IntPtr.Zero;
   private int BytesPerPix = 0;

   public Filter(Bitmap MainBitmap)
   {
      if (MainBitmap != null)
      {
        UPicture = (Bitmap)MainBitmap.Clone();
        switch (UPicture.PixelFormat)
        {
           case PixelFormat.Format24bppRgb:
              {
                BytesPerPix = 3;
                break;
              }
           case PixelFormat.Format32bppArgb:
              {
                BytesPerPix = 4;
                break;
              }
           default:
              {
                throw new NotSupportedException("Формат пикселей не соответствует   
                                       стандарту");
              }
        } 
        BmpData = UPicture.LockBits(new Rectangle(0, 0, UPicture.Width, UPicture.Height),
               ImageLockMode.ReadWrite, UPicture.PixelFormat);
        unsafe
        {
              Begin = (byte*)BmpData.Scan0;
        }
      }
      else
        throw new ArgumentException("Неверный параметр #1");
   }

   public Bitmap Picture
   {
      get { return UPicture; }
   }
   public int Height
   {
      get { return UPicture.Height; }
   }
   public int Width
   {
      get { return UPicture.Width; }
   }
   public int BytesPerPixel
   {
      get { return BytesPerPix; }
   }
   public IntPtr Safe_IMG_Scan0
   {
      get { return BmpData.Scan0; }
   }
   public unsafe byte* Unsafe_IMG_Scan0
   {
      get { return Begin; }
   }
   public int AllPixelsBytes
   {
      get { return UPicture.Width * UPicture.Height * BytesPerPix; }
   }
   public void UnLock()
   {
      UPicture.UnlockBits(BmpData);
   }
   public unsafe RGB_Color GetPixel(int X, int Y)
   {
      RGB_Color Pixel = new RGB_Color();
      int IDX = (Y * UPicture.Width + X) * BytesPerPix; //Вычисляем позицию пикселя
      Pixel.B = *(Begin + (IDX + 0)); //B
      Pixel.G = *(Begin + (IDX + 1)); //G
      Pixel.R = *(Begin + (IDX + 2)); //R
      return Pixel;
   }
   public unsafe void SetPixel(RGB_Color CL,int X,int Y)
   {
      int IDX = (Y * UPicture.Width + X) * BytesPerPix; //Вычисляем позицию пикселя
      *(Begin + (IDX + 0)) = Convert.ToByte(CL.B); //B
      *(Begin + (IDX + 1)) = Convert.ToByte(CL.G); //G
      *(Begin + (IDX + 2)) = Convert.ToByte(CL.R); //R
   }
}

Теперь начнём реализовывать негатив,используя класс Filter.Создадим новый класс с названием
Negative :
/*http://esate.ru, Alexei16*/


public class Negative
{
}

Добавим функцию ProcessImage:
/*http://esate.ru, Alexei16*/


public static unsafe Bitmap ProcessImage(Filter Main)
{
   for (int I = 0; I < Main.AllPixelsBytes; I += Main.BytesPerPixel)
   {
      *(Main.Unsafe_IMG_Scan0 + (I + 0)) = (byte)(255 - (*(Main.Unsafe_IMG_Scan0 + I + 0))); //B  
      *(Main.Unsafe_IMG_Scan0 + (I + 1)) = (byte)(255 - (*(Main.Unsafe_IMG_Scan0 + I + 1))); //G  
      *(Main.Unsafe_IMG_Scan0 + (I + 2)) = (byte)(255 - (*(Main.Unsafe_IMG_Scan0 + I + 2))); //R  
   }
   Main.UnLock();
   return Main.Picture;        
}

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

Сепия

Сразу перейдём к ёё реализации.В этой функции мы снова будем использовать класс Filter и неуправляемые указатели.Создадим класс Sepia:
/*http://esate.ru, Alexei16*/


public class Sepia
{
}

И добавим в него функцию ProcessImage :
/*http://esate.ru, Alexei16*/


public static unsafe Bitmap ProcessImage(Filter Main)
{
   RGB_Color TMP = new RGB_Color();
   byte Tone = 0;
   
   for (int I = 0; I < Main.AllPixelsBytes; I += Main.BytesPerPixel)
   {
      TMP.B = *(Main.Unsafe_IMG_Scan0 + (I + 0)); //B
      TMP.G = *(Main.Unsafe_IMG_Scan0 + (I + 1)); //G
      TMP.R = *(Main.Unsafe_IMG_Scan0 + (I + 2)); //R

      Tone = (byte)(0.299 * TMP.R + 0.587 * TMP.G + 0.114 * TMP.B);
      //Вычисляем новый цвет

      //Нормализуем цвет
      *(Main.Unsafe_IMG_Scan0 + (I + 2)) = (byte)((Tone > 206) ? 255 : Tone + 49); //R  
      *(Main.Unsafe_IMG_Scan0 + (I + 1)) = (byte)((Tone < 14) ? 0 : Tone - 14); //G  
      *(Main.Unsafe_IMG_Scan0 + (I + 0)) = (byte)((Tone < 56) ? 0 : Tone - 56); //B  
   }
   Main.UnLock();
   return Main.Picture;
}

Использование в своих приложениях

• Негатив
/*http://esate.ru, Alexei16*/


Bitmap Test1 = Negative.ProcessImage(new Filter(TestBitmap)) //TestBitmap - это ваша картинка





• Сепия
/*http://esate.ru, Alexei16*/


Bitmap Test2 = Sepia.ProcessImage(new Filter(TestBitmap)) //TestBitmap - это ваша картинка





Готово!
0       647        24.12.2010        13

Внимание!

Эта публикация перенесена в раздел уроков по адресу Графические фильтры на основе попиксельной обработки изображений (Часть 1).
К ней прикреплена новая отдельная ветка комментариев форума, которую вы можетет найти после текста публикации.
Обсуждение публикации рекуомендуется вести по новому адресу, который указан выше.

0  
25.12.2010 00:00:00
Отлично! Спасибо за урок, ждем продолжения!
0  
16.01.2011 00:00:00
Спасибо, очень понятно все написано.
Только при создании негатива опечатка в коде:
Main.Unsafe_IMG_Scan0 +0
а должно быть
Main.Unsafe_IMG_Scan0 +I

исправьте, пожалуйста…
0  
16.01.2011 00:00:00
Исправил…
0  
06.06.2011 00:00:00
А можете исходник скинуть? (Если не сложно конечно)
0  
06.06.2011 00:00:00
Могу скинуть .cs файл, с реализацией, но без GUI и т.д
0  
06.06.2011 00:00:00
0  
06.06.2011 00:00:00
почта: dima9192_91@mail.ru
0  
30.03.2012 00:00:00
Пожалуйста скинь мне исходник если не сложно
почта: gusinec.com@tut.by
0  
29.03.2012 00:00:00
Пожалуйста скинь мне исходник если не сложно
почта: gusinec.com@tut.by
0  
31.03.2012 00:00:00
Скинул…
0  
28.05.2012 00:00:00
отличный урок!
я не опытный в этой части программирования, но мне почти все было понятно.

Скинь мне исходник, пожалуйста.
почта bydanila@gmail.com
0  
28.05.2012 00:00:00
ммм прикольненько, чет только сейчас захотелось поработать над картинками
вот только мне подход не нравится
что negative и sepia как класс используются Negative.ProcessImage(new Filter(TestBitmap))
по мне лучше Picture.filter(«negative»);
ну или FilterManager.Negative(Picture);
но тут каждому свое
0  
03.06.2012 00:00:00
Ты прав, каждому своё))
^