Новосибирский Государственный Университет
Опубликован: 20.08.2013 | Доступ: свободный | Студентов: 865 / 38 | Длительность: 14:11:00
Самостоятельная работа 2:

Базовые операции обработки изображений

2. Обзор возможностей модуля imgproc библиотеки OpenCV

2.1. Cвертка и линейные фильтры

Линейные фильтры – семейство самых простых фильтров изображений с точки зрения математического описания [6]. Предположим, что имеется полутоновое изображение I. Тогда любой линейный фильтр определяется вещественнозначной функцией F, заданной на растре. Данная функция называется ядром фильтра, а операция фильтрации выполняется посредством вычисления дискретной свертки:

I'=(x,y,)=\sum_{i} \sum_{j}{F(i,j)\cdotI(x+i,y+j)}

Как правило, ядро фильтра применяется к некоторой окрестности 0 точки, поэтому пределы изменения индексов i и j определяются выбранной формой и размером окрестности. Данная окрестность в некоторых источниках называется шаблоном или апертурой. В процессе вычисления свертки выполняется проход по пикселям всего изображения, шаблон накладывается на каждый текущий пиксель посредством совмещения пикселя с конкретной точкой шаблона – ведущей позицией шаблона, после чего вычисляется свертка. Необходимо отдельно обратить внимание на ситуацию, когда текущий пиксель находится на границе изображения. Указанную проблему можно решить несколькими способами:

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

Выбор решения во многом зависит от приложения, так например, зеркальное отражение на практике не совсем естественный способ.

Для вычисления сверток в библиотеке OpenCV присутствует функция filter2D.

void filter2D(const Mat& src, Mat& dst, int ddepth, 
  const Mat& kernel, 
  Point anchor=Point(-1, -1), double delta=0, 
  int borderType=BORDER_DEFAULT) 
    

Рассмотрим подробнее параметры приведенной функции.

  • src – исходное изображение.
  • dst – свертка. Имеет такое же количество каналов и глубину, что и исходное изображение.
  • ddepth – глубина результирующего изображения. Если на вход функции передано отрицательное значение, то глубина совпадает с глубиной входного изображения.
  • kernel – ядро свертки, одноканальная вещественная матрица.
  • anchor – ведущая позиция ядра. По умолчанию принимает значение (-1,-1), которое означает, что ведущая позиция расположена в центре ядра.
  • delta – константа, которая может быть добавлена к значению интенсивности после фильтрации перед непосредственной записью результата.
  • borderType – параметр, определяющий метод дополнения границы, чтобы можно было применять фильтр к граничным пикселям исходного изображения. Принимает любое значение вида BORDER_* за исключением BORDER_TRANSPARENT и BORDER_ISOLATED.

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

dst(x,y)=\sum_{0\leq x' < anchor.x \\ 0\leq y' < anchor.y}{kernel(x',y')\cdot src(x+x'-anchor.x, y+y'-anchor.y)}

В случае многоканального изображения ядро применяется к каждому каналу в отдельности.

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

#include <stdlib.h > 
#include <stdio.h > 
#include <opencv2/opencv.hpp > 
 
using namespace cv; 
 
const char helper[] = 
   "Sample_filter2D.exe <img_file >\n\ 
  \t <img_file > - image file name\n "; 
 
int main(int argc, char* argv[]) 
{ 
  // константы для определения названия окон 
  const char *initialWinName = "Initial Image ", 
    *resultWinName = "Filter2D "; 
  // константы для хранения ядра фильтра 
  const float kernelData[] = {-0.1f, 0.2f, -0.1f, 
      0.2f, 3.0f, 0.2f, 
      -0.1f, 0.2f, -0.1f}; 
  const Mat kernel(3, 3, CV_32FC1, (float *)kernelData); 
  // объекты для хранения исходного 
  // и результирующего изображений 
  Mat src, dst; 
  // проверка аргументов командной строки 
  if (argc < 2) 
  { 
    printf( "%s ", helper); 
    return 1; 
  } 
  // загрузка изображения 
  src = imread(argv[1], 1); 
  // применение фильтра 
  filter2D(src, dst, -1, kernel); 

  // отображение исходного изображения и 
  // результата применения фильтра 
  namedWindow(initialWinName, CV_WINDOW_AUTOSIZE); 
  imshow(initialWinName, src); 
  namedWindow(resultWinName, CV_WINDOW_AUTOSIZE); 
  imshow(resultWinName, dst); 
  waitKey(); 
 
  // закрытие окон 
  destroyAllWindows(); 
  // освобождение ресурсов 
  src.release(); 
  dst.release(); 
  return 0; 
} 
    

Далее на рисунке показан результат работы приведенной программы (рис. 7.1, справа). Очевидно, что применение фильтра с ядром, зафиксированным в программной коде (выделено полужирным), привело к уменьшению контраста исходного тестового изображения (рис. 7.1, слева).

Результат применения фильтра

Рис. 7.1. Результат применения фильтра

Отметим, что в случае больших ядер (размера порядка 11x11 пикселей) для вычисления свертки используется быстрое преобразование Фурье, в случае небольших ядер – прямой алгоритм. Также если ядро сепарабельное, т.е. может быть представлено в виде пары ядер, которые могут быть последовательно применены к строкам и столбцам изображения в отдельности, то предусмотрена более эффективная реализация линейного фильтра с использованием функции sepFilter2D. При вызове данная функция требует явного указания двух одномерных ядер rowKernel и columnKernel.

void sepFilter2D(const Mat& src, Mat& dst, int ddepth,
    const Mat& rowKernel, 
    const Mat& columnKernel, 
    Point anchor=Point(-1, -1), 
    double delta=0, 
    int borderType=BORDER_DEFAULT)       
    
Александра Максимова
Александра Максимова

При прохождении теста 1 в нем оказались вопросы, который во-первых в 1 лекции не рассматривались, во-вторых, оказалось, что вопрос был рассмаотрен в самостоятельно работе №2. Это значит, что их нужно выполнить перед прохождением теста? или это ошибка?
 

Алена Борисова
Алена Борисова

В лекции по обработке полутоновых изображений (http://www.intuit.ru/studies/courses/10621/1105/lecture/17979?page=2) увидела следующий фильтр:


    \begin{array}{|c|c|c|}
    \hline \\
    0 & 0 & 0 \\
    \hline \\
    0 & 2 & 0 \\
    \hline \\
    0 & 0 & 0 \\
    \hline 
    \end{array} - \frac{1}{9} \begin{array}{|c|c|c|}
    \hline \\
    0 & 0 & 0 \\
    \hline \\
    0 & 1 & 0 \\
    \hline \\
    0 & 0 & 0 \\
    \hline 
    \end{array}

В описании говорится, что он "делает изображение более чётким, потому что, как видно из конструкции фильтра, в однородных частях изображение не изменяется, а в местах изменения яркости это изменение усиливается".

Что вижу я в конструкции фильтра (скорее всего ошибочно): F(x, y) = 2 * I(x, y) - 1/9 I(x, y) = 17/9 * I(x, y), где F(x, y) - яркость отфильтрованного пикселя, а I(x, y) - яркость исходного пикселя с координатами (x, y). Что означает обычное повышение яркости изображения, при этом без учета соседних пикселей (так как их множители равны 0).

Объясните, пожалуйста, как данный фильтр может повышать четкость изображения?