Уроки OpenGL + C#.

Реализация графических фильтров с помощью OpenGL.




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

Итак, первым делом добавьте новый пункт меню, в котором будут перечислены показанные на рисунке 1 фильтры.

Меню с различными фильтрами
Рисунок 1.


Каждому пункту меню добавьте свой обработчик. Коды этих обработчиков следуют далее

private void инвертироватьЦветаToolStripMenuItem_Click( object sender, EventArgs e)

{
ProgrammDrawingEngine.Filter_0();
}

private void применитьФильтрToolStripMenuItem_Click( object sender, EventArgs e)
{
ProgrammDrawingEngine.Filter_1();
}

private void размытиеToolStripMenuItem_Click_1( object sender, EventArgs e)
{
ProgrammDrawingEngine.Filter_2();
}

private void теснениеToolStripMenuItem_Click( object sender, EventArgs e)
{
ProgrammDrawingEngine.Filter_3();
}

private void акварелизацияToolStripMenuItem_Click( object sender, EventArgs e)
{
ProgrammDrawingEngine.Filter_4();
}


Как видно из кода, мы обращаемся к классу ProgrammDrawingEngine, а именно к функциям, реализованным в нем – filter_0, filter_1 и т.д.

Инвертирование.

Это самый простой фильтр. Функция будет вызывать функцию Inrevrs из класса слоев для данного класса, в результате чего цвета в слое будут инвертированы.

// фильтр для инвертирования цветов
public void Filter_0()
{
// вызываем функцию инвертирования класса anLayer
((anLayer)Layers[ActiveLayerNom]).Invers();
}

Реализация функции Inverse

// инвертирование цветов
public void Invers()
{
// циклами переберам все пиксели изображения
for ( int Y = 0; Y < Heigth; Y++)
{
for ( int X = 0; X < Width; X++)
{
// и инвертируем цвет установленный в RGB составляющих на обратный (255-R) (255-G) (255-B)
DrawPlace[X, Y, 0] = 255-DrawPlace[X, Y, 0];
DrawPlace[X, Y, 1] = 255-DrawPlace[X, Y, 1];
DrawPlace[X, Y, 2] = 255-DrawPlace[X, Y, 2];
}
}
}

 

Следующие фильтры будут немного сложнее в реализации.
В классе движка мы будем указывать матрицу для обработки изображения, после чего вызывать функцию осуществления преобразования на основе матрицы и дополнительных параметров, которые мы рассмотрим позднее (при рассмотрение работы самой функции).

Реализация фильтров.

Обратите внимание на то, что теснение мы выполним немного по другому (ни так как в теории) но вы можете попробовать реализовать оба варианта.

public void Filter_1()
{
// собираем матрицу
float [] mat = new float [9]; mat[0] = -0.1f;
mat[1] = -0.1f;
mat[2] = -0.1f;
mat[3] = -0.1f;
mat[4] = 1.8f;
mat[5] = -0.1f;
mat[6] = -0.1f;
mat[7] = -0.1f;
mat[8] = -0.1f;

//вызываем функцию обработки , передавая туда матрицу и дополнительные параметры.
((anLayer)Layers[ActiveLayerNom]).PixelTransformation(mat, 0, 1, false );
}

public void Filter_2()
{
// собираем матрицу
float [] mat = new float [9];

mat[0] = 0.05f;
mat[1] = 0.05f;
mat[2] = 0.05f;
mat[3] = 0.05f;
mat[4] = 0.6f;
mat[5] = 0.05f;
mat[6] = 0.05f;
mat[7] = 0.05f;
mat[8] = 0.05f;

//вызываем функцию обработки , передавая туда матрицу и дополнительные параметры.
((anLayer)Layers[ActiveLayerNom]).PixelTransformation(mat, 0, 1, false );
}

public void Filter_3()
{
// собираем матрицу
float [] mat = new float [9];

mat[0] = -1.0f;
mat[1] = -1.0f;
mat[2] = -1.0f;
mat[3] = -1.0f;
mat[4] = 8.0f;
mat[5] = -1.0f;
mat[6] = -1.0f;
mat[7] = -1.0f;
mat[8] = -1.0f;

//вызываем функцию обработки , передавая туда матрицу и дополнительные параметры.
((anLayer)Layers[ActiveLayerNom]).PixelTransformation(mat, 0, 2, true );
}

public void Filter_4()
{
// собираем матрицу
// для данного фильтра нам необзодимо будет произвести 2 преобразования

float [] mat = new float [9];

mat[0] = 0.50f;
mat[1] = 1.0f;
mat[2] = 0.50f;
mat[3] = 1.0f;
mat[4] = 2.0f;
mat[5] = 1.0f;
mat[6] = 0.50f;
mat[7] = 1.0f;
mat[8] = 0.50f;

//вызываем функцию обработки , передавая туда матрицу и дополнительные параметры.
((anLayer)Layers[ActiveLayerNom]).PixelTransformation(mat, 0, 2, true );

mat[0] = -0.5f;
mat[1] = -0.5f;
mat[2] = -0.5f;
mat[3] = -0.5f;
mat[4] = 6.0f;
mat[5] = -0.5f;
mat[6] = -0.5f;
mat[7] = -0.5f;
mat[8] = -0.5f;

//вызываем функцию обработки , передавая туда матрицу и дополнительные параметры.
((anLayer)Layers[ActiveLayerNom]).PixelTransformation(mat, 0, 1, false );
}

Теперь нам осталось рассмотреть работу функции PixelTransformation и наша работа с фильтрами завершена.
Данная функция проводит все необходимые преобразования (см. комментарии)

// функция обработки слоя изображения на основе полученной матрицы и дополнитлеьных параметров
// corr - коррекция составляющей цвета - после обработки каждого пикселя к каждой его составляющей будет
// прибавлено данное значение
// COEFF - коэфицент, реализующий усиление работы фильтра
// need_count_correction - необзодимость корректировки значения полученного пикселя после прохода фильтра.
// если данный параметра установлен , то каждая составляющая цвета, перед тем как быть преведенной к виду 0-255
// будет разделена на количество произошедших с ней преобразований. Необходимо для корректной работы некоторых фильтров.


public void PixelTransformation( float [] mat, int corr, float COEFF, bool need_count_correction)
{

// массив для получения результирующего пикселя
float [] resault_RGB = new float [3];
int count = 0;
// проходим циклом по всем пикселям слоя
for ( int Y = 0; Y < Heigth; Y++)
{
for ( int X = 0; X < Width; X++)
{

// цикл по всем составляющим (0-2, т.е. R G B)
for ( int c = 0, ax = 0, bx = 0; c < 3; c++)
{

// обнуление составляющей результата
resault_RGB[c] = 0;
// обнуление счетчика обработок
count = 0;

// 2 цикла для захвата области 3х3 вокруг обрабатываемого пикселя
for (bx = -1; bx < 2; bx++)
{

for (ax = -1; ax < 2; ax++)
{

// если мы не попали в рамки, просто используем центральный пиксель, и продолжаем цикл
if (X + ax < 0 || X + ax > Width-1 || Y + bx < 0 || Y + bx > Heigth-1)
{

// считаем составляющую в одной из точек, использую коэфицент в матрице (под номером текущей итерации),
// коэфицент усиления (COEFF) и прибовляем коррекцию (corr)

resault_RGB[c] += ( float )(DrawPlace[X, Y, c]) * mat[count] * COEFF + corr;
// счетчик обработок = ячейке матрицы с необходимым коэфицентом
count++;
// продолжаем цикл
continue;

}

// иначе, если мы укладываемся в изображение (не пересекаем границы), используем
// соседние пиксели корректирую ячейку массива параметрами ax, bx

resault_RGB[c] += ( float )(DrawPlace[X + ax, Y + bx, c]) * mat[count] * COEFF + corr;
// счетчик обработок = ячейке матрицы с необходимым коэфицентом
count++;

}

}

}

// теперь для всех составляющих - корректируем цвет
for ( int c = 0; c < 3; c++)
{

// если требуется разделить результат до приведения к 0-255,
// разделив на количество проведенных операций

if( count != 0 && need_count_correction)
{

// выполняем данное деление
resault_RGB[c] /= count;

}

// если значение меньше нуля
if (resault_RGB[c] < 0)
{

// - приравниваем к нулю
resault_RGB[c] = 0;

}

// если больше 255
if (resault_RGB[c] > 255)
{

// приравниваем к 255
resault_RGB[c] = 255;

}
// записываем в массив цветов слоя новое значение
DrawPlace[X, Y, c] = ( int )resault_RGB[c];

}

}

}
 
}

Примеры работы программы лучше смотрите сами, т.к. необходимо видеть, что было до и что стало после. Размытие можно применить несколько раз, т.к. оно очень плавно меняет изображение. Потом несколько раз применить резкость, чтобы посмотреть что получится.


Обсуждение данного урока: Создание графических фильтров для обработки изображений с помощью OpenGL.
Далее: 8.1 Теоретическое введение - cплайны.