7.2 Создание графических фильтров для обработки изображений с помощью OpenGL.

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

Итак, первым делом добавьте новый пункт меню, в котором будут перечислены показанные на рисунке 1 фильтры.
Уроки OpenGL + C#: Меню с различными фильтрами Рисунок 1. Меню с различными фильтрами.
Каждому пункту меню добавьте свой обработчик. Коды этих обработчиков следуют далее:

/*http://esate.ru, Anvi*/


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 из класса слоев для данного класса, в результате чего цвета в слое будут инвертированы.

/*http://esate.ru, Anvi*/


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

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

/*http://esate.ru, Anvi*/


// инвертирование цветов 
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];
    }
  }
}

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

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

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

/*http://esate.ru, Anvi*/


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, и наша работа с фильтрами завершена.

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

/*http://esate.ru, Anvi*/


// функция обработки слоя изображения на основе полученной матрицы и дополнительных параметров 
// 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];

      }
    }
  }
}

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

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

^