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];

      }
    }
  }
}

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

Добавить комментарий
Расширенный режим добавления комментариев доступен на форуме: загрузка изображений, цитирование, форматирование текста, и т.д.
Ваше имя:
Текст сообщения:
^