1.3 Графические фильтры: изменение тона и гаммы изображения
Что прочитать перед этим материалом
1.2 Графические фильтры: изменение яркости и контрастности изображения
В данном уроке вы научитесь изменять тон и гамму изображения.
Изменение тона изображения
Для изменения тона будет использоваться цветовую модель HSL, в которой координатами цвета являются Hue (Тон), Saturation (Насыщенность) и Lightness (Светлота).
Цветовые составляющие HSL принимают следующие значения:
- H (Тон) — [0; 360]
- S (Насыщенность) — [0; 1]
- L (Светлота) — [0; 1]
На рисунке 1 показано графическое представление модели HSL:

Для того, чтобы можно было изменять тон, нам нужно будет конвертировать цветовую модель RGB в HSL и обратно.
Напишем две функции:
- RGB -> HSL
Код:
public static HSL_Color RGB_TO_HSL(RGB_Color CL)
{
double H = 0, S = 0, L = 0;
double R = (double)CL.R / 255.0; //
double G = (double)CL.G / 255.0; // Приводим к диапазону от 0 до 1
double B = (double)CL.B / 255.0; //
double Max = Math.Max(R, Math.Max(G, B));
double Min = Math.Min(R, Math.Min(G, B));
//Вычисляем тон
if (Max == Min)
{
H = 0;
}
else if (Max == R && G >= B)
{
H = 60.0 * (G - B) / (Max - Min);
}
else if (Max == R && G < B)
{
H = 60.0 * (G - B) / (Max - Min) + 360.0;
}
else if (Max == G)
{
H = 60.0 * (B - R) / (Max - Min) + 120.0;
}
else if (Max == B)
{
H = 60.0 * (R - G) / (Max - Min) + 240.0;
}
//Вычисляем светлоту
L = (Max + Min) / 2.0;
//Вычисляем насыщенность
if (L == 0 || Max == Min)
{
S = 0;
}
else if (0 < L && L <= 0.5)
{
S = (Max - Min) / (Max + Min);
}
else if (L > 0.5)
{
S = (Max - Min) / (2 - (Max + Min));
}
return new HSL_Color(H, S, L);
}
- HSL -> RGB
Код:
public static RGB_Color HSL_TO_RGB(HSL_Color CL)
{
int R, G, B;
if (CL.S == 0)
{
R = (int)Math.Round(CL.L * 255.0); //
G = (int)Math.Round(CL.L * 255.0); //Округляем значения
B = (int)Math.Round(CL.L * 255.0); //
}
else
{
double Q = (CL.L < 0.5) ? (CL.L * (1.0 + CL.S)) : (CL.L + CL.S - (CL.L * CL.S));
double P = (2.0 * CL.L) - Q;
double HK = CL.H / 360.0;
double[] T = new double[3]; //Массив для хранения значений R,G,B
T[0] = HK + (1.0 / 3.0); // R
T[1] = HK; // G
T[2] = HK - (1.0 / 3.0); // B
for (int i = 0; i < 3; i++)
{
if (T[i] < 0) T[i] += 1.0;
if (T[i] > 1) T[i] -= 1.0;
if ((T[i] * 6) < 1)
{
T[i] = P + ((Q - P) * 6.0 * T[i]);
}
else if ((T[i] * 2.0) < 1)
{
T[i] = Q;
}
else if ((T[i] * 3.0) < 2)
{
T[i] = P + (Q - P) * ((2.0 / 3.0) - T[i]) * 6.0;
}
else
{
T[i] = P;
}
}
R = (int)(T[0] * 255.0); //
G = (int)(T[1] * 255.0); //Приводим к диапазону от 0 до 255
B = (int)(T[2] * 255.0); //
}
return new RGB_Color(R, G, B);
}
Структуры RGB_Color и HSL_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;
}
public struct HSL_Color
{
public HSL_Color(double Hue, double Saturation, double Lightness)
{
H = Hue;
S = Saturation;
L = Lightness;
}
public double H, S, L;
}
Когда всё необходимое для написания функции корректировки тона готово, можем браться за дело.
Напишем функцию тона:
Код:
public class Hue
{
public unsafe static Bitmap ProcessImage(Filter Main,int Value)
{
RGB_Color CL_RGB = new RGB_Color(); //Создаем структуры
HSL_Color CL_HSL = new HSL_Color();
for (int I = 0; I < Main.AllPixelsBytes; I += Main.BytesPerPixel) //Проходимся по каждому пикселю
{
CL_RGB.B = *(Main.Unsafe_IMG_Scan0 + (I + 0)); //Получаем значение синего
CL_RGB.G = *(Main.Unsafe_IMG_Scan0 + (I + 1)); //Получаем значение зелёного
CL_RGB.R = *(Main.Unsafe_IMG_Scan0 + (I + 2)); //Получаем значение красного
CL_HSL = RGB.RGB_TO_HSL(CL_RGB); //RGB -> HSL
CL_HSL.H = (double)Value; //Изменяем тон
CL_RGB = HSL.HSL_TO_RGB(CL_HSL); //HSL -> RGB
*(Main.Unsafe_IMG_Scan0 + (I + 0)) = (byte)CL_RGB.B;
*(Main.Unsafe_IMG_Scan0 + (I + 1)) = (byte)CL_RGB.G;
*(Main.Unsafe_IMG_Scan0 + (I + 2)) = (byte)CL_RGB.R;
}
Main.UnLock();//Разблокируем биты изображения
return Main.Picture;
}
}
Здесь мы опять использовали класс Filter, написанный нами в 1-ом уроке и неуправляемые указатели, так как это наилучший способ добиться максимальной производительности в .NET!
Использование тона в приложениях
Код:
Bitmap TestBitmap = Hue.ProcessImage(new Filter(MyImage), 280); //MyImage - это выше изображение


Изменение гаммы изображения
Повышение гаммы позволяет более отчетливо увидеть темные участки изображения.
Для коррекции гаммы сначала вычисляется таблица значений (RampTable). Таблица состоит из 255 ячеек.
Далее в зависимости от значений R, G, Bберутся данные из таблицы и присваиваются пикселю. Значения гаммы находятся в промежутке от 0.0 до 5.0.
Создадим класс Gamma и добавим в него массив байтов (RampTable):
Код:
public class Gamma
{
private static byte[] RampTable = new byte[256];
}
Теперь в него необходимо добавить функцию генерации таблицы:
Код:
private static void GenerateRampTable(float Value)//Здесь Value - это значение гаммы
{
double Gam = Math.Max(0.1, Math.Min(5.0, Value)); //Вычислям общий коэффицент гаммы,который потребуется для вычисления главного значения
double G = 1 / Gam; //Главное значение гаммы
for (int I = 0; I < 256; I++)
{
RampTable[I] = (byte)Math.Min(255, (int)(Math.Pow(I / 255.0, G) * 255 + 0.5));//Вычисляем табличные данные
}
}
Затем необходимо добавить главную функцию ProcessImage:
Код:
public static unsafe Bitmap ProcessImage(Filter Main,float Value)
{
GenerateRampTable(Value); //Генерируем гамма-таблицу
for (int I = 0; I < Main.AllPixelsBytes; I += Main.BytesPerPixel) //Проходим по каждому пикселю изображения
{
*(Main.Unsafe_IMG_Scan0 + (I + 0)) = RampTable[*(Main.Unsafe_IMG_Scan0 + (I + 0))]; //В зависимости от значения синего,ему присваивается значение из таблицы
*(Main.Unsafe_IMG_Scan0 + (I + 1)) = RampTable[*(Main.Unsafe_IMG_Scan0 + (I + 1))]; //В зависимости от значения зелёного,ему присваивается значение из таблицы
*(Main.Unsafe_IMG_Scan0 + (I + 2)) = RampTable[*(Main.Unsafe_IMG_Scan0 + (I + 2))]; //В зависимости от значения красного,ему присваивается значение из таблицы
}
Main.UnLock(); //Разблокируем биты изображения
return Main.Picture;
}
Готово.
Использование гаммы в приложениях
Код:
Bitmap TestBitmap = Gamma.ProcessImage(new Filter(MyImage), 3.4f); //MyImage - это выше изображение

