Опубликован: 20.08.2013 | Уровень: для всех | Доступ: платный | ВУЗ: Новосибирский Государственный Университет
Самостоятельная работа 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).

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

Сергей Кротов
Сергей Кротов
Россия
Дмитрий Донсков
Дмитрий Донсков
Россия, Москва, Московский Авиационный Институт