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:
Код:
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:
Код:
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:
Код:
public class Negative
{
}
Добавим функцию ProcessImage:
Код:
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#. В реализации нет ничего сложного, думаю, что комментарии излишни.
Использование в приложениях фильтра «негатив»:
Код:
Bitmap Test1 = Negative.ProcessImage(new Filter(TestBitmap)) //TestBitmap - это ваша картинка

Фильтр «сепия»
Сепия – это оттенок коричневого цвета, который появляется на старых черно-белых фотографиях.
Сразу перейдём к её созданию. Здесь мы снова будем использовать класс Filter и неуправляемые указатели.
Создадим класс Sepia:
Код:
public class Sepia
{
}
И добавим в него функцию ProcessImage:
Код:
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;
}
Использование в приложениях фильтра «сепия»:
Код:
Bitmap Test2 = Sepia.ProcessImage(new Filter(TestBitmap)) //TestBitmap - это ваша картинка
