1.1 Графические фильтры: «негатив» и «сепия»

Блоговая публикация пользователя: Alexei16 Эта публикация была перенесена из личного блога пользователя в общие разделы уровок сайта.
В данном уроке вы познакомитесь с графическими фильтрами «негатив» и «сепия», их особенностями и способами их настройки.

Фильтр «негатив»

Негатив – это фильтр, который инвертирует цвета. Приступим к написанию кода. Сначала создадим основу для всех наших фильтров. Создадим новый класс с именем 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#. В реализации нет ничего сложного, думаю, что комментарии излишни.

Использование в приложениях фильтра «негатив»:

Код:
/*http://esate.ru, Alexei16*/

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






Обработка изображений: Фильтр «негатив» Рисунок 1. Фильтр «негатив».

Фильтр «сепия»

Сепия – это оттенок коричневого цвета, который появляется на старых черно-белых фотографиях.

Сразу перейдём к её созданию. Здесь мы снова будем использовать класс 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 Test2 = Sepia.ProcessImage(new Filter(TestBitmap)) //TestBitmap - это ваша картинка






Обработка изображений: Фильтр «сепия» Рисунок 2. Фильтр «сепия».

Нет доступа к просмотру комментариев.

^