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

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

2.5. Оператор Лапласа

Математически оператор Лапласа представляет сумму квадратов вторых частных производных функции \Delta f = \frac{\partial^2 f}{\partial x^2} + \frac{\partial^2 f}{\partial y^2}. Дискретный аналог оператора Лапласа используется при обработке изображений, в частности, для определения ребер объектов на изображении. Ребра формируются из множества пикселей, в которых оператор Лапласа принимает нулевые значения, т.к. нули вторых производных функции соответствуют экстремальным перепадам интенсивности.

Библиотека OpenCV содержит функцию Laplacian, обеспечивающую вычисление оператора Лапласа [7].

void Laplacian(const Mat& src, Mat& dst, int ddepth, 
    int ksize=1, double scale=1, double delta=0, 
    int borderType=BORDER_DEFAULT) 
    

Параметры функции:

  • src, dst, ddepth, scale, delta, borderType имеют тот же смысл, что и при вызове функции Sobel.
  • kSize – размер апертуры для вычисление второй производной, является положительным четным числом. При использовании значения по умолчанию kSize=1 применяется апертура размером 3x3 и ядро представляется матрицей:
    L=\begin{bmatrix} 0 & 1 & 0 \\ 1 & -4 & 1 \\ 0 & 1 & 0\end{bmatrix}

Приведем пример программы, которая обеспечивает поиск ребер объектов на изображения с помощью оператора Лапласа. Перед непосредственным применением оператора выполняется сглаживание с использованием фильтра Гаусса (GaussianBlur) и преобразование исходного изображения в оттенки серого (cvtColor). Заметим, что результирующие значения оператора Лапласа записываются в матрицу, глубина которой аналогично примеру с фильтром Собеля отличается от глубины исходного изображения, поэтому перед отображением выполняется преобразование полученной матрицы в 8-битную целочисленную посредством вызова функции convertScaleAbs.

#include <stdio.h> 
#include <opencv2/opencv.hpp> 
 
using namespace cv; 
 
const char helper[] = 
  $quot;Sample_Laplacian.exe <img_file>\n\ 
  \t<img_file> - image file name\n$quot;; 
 
int main(int argc, char* argv[]) 
{ 
  const char *initialWinName = $quot;Initial Image$quot;, 
    *laplacianWinName = $quot;Laplacian$quot;; 
  Mat img, grayImg, laplacianImg, laplacianImgAbs; 
  int ddepth = CV_16S; 
  if (argc < 2) 
  { 
    printf($quot;%s$quot;, helper); 
    return 1; 
  } 
  // загрузка изображения 
  img = imread(argv[1], 1); 
  // сглаживание с помощью фильтра Гаусса 
  GaussianBlur(img, img, Size(3,3), 
      0, 0, BORDER_DEFAULT); 
  // преобразование в оттенки серого 
  cvtColor(img, grayImg, CV_RGB2GRAY); 
  // применение оператора Лапласа 
  Laplacian(grayImg, laplacianImg, ddepth); 
  convertScaleAbs(laplacianImg, laplacianImgAbs); 
 
  // отображение результата 
  namedWindow(initialWinName, CV_WINDOW_AUTOSIZE); 
  namedWindow(laplacianWinName, CV_WINDOW_AUTOSIZE); 
  imshow(initialWinName, img); 
  imshow(laplacianWinName, laplacianImgAbs); 
  waitKey(); 
 
  // закрытие окон 
  destroyAllWindows(); 
  // осовобождение памяти 
  img.release(); 
  grayImg.release(); 
  laplacianImg.release(); 
  laplacianImgAbs.release(); 
  return 0; 
}   
    

Далее показан результат выделения ребер с использованием оператора Лапласа (рис. 7.8) на тестовом изображении, показанном ранее (рис. 7.2, слева).

Результат выделения ребер с использованием оператора Лапласа

Рис. 7.8. Результат выделения ребер с использованием оператора Лапласа

2.6. Детектор ребер Канни

Детектор ребер Канни [4, 8, 9] предназначен для поиска границ объектов на изображении. Детектор строится на основании оператора Собеля и включает несколько этапов:

  1. Удаление шума на изображении посредством применения фильтра Гаусса с ядром размера 5:
    L=\frac{1}{159}\begin{bmatrix} 2 & 4 & 5 & 4 & 2 \\4 & 9 & 12 & 9 & 4 \\5 & 12 & 15 & 12 & 5 \\4 & 9 & 12 & 9 & 4 \\ 2 & 4 & 5 & 4 & 2\end{bmatrix}
  2. Вычисление первых производных (магнитуд и направлений) функции интенсивности пикселей по горизонтальному и вертикальному направлениям посредством применения оператора Собеля с ядрами G_{x} и G_{y} (см. раздел 2.4). Направления градиентов округляются до одного из возможных значений 0^0, 45^0, 90^0, 135^0.
  3. Отбор пикселей, которые потенциально принадлежат ребру с использованием процедуры non-maximum suppression [4]. Пиксели, которым соответствуют вектора производных по направлениям, являющиеся локальными максимумами, считаются потенциальными кандидатами на принадлежность ребру.
  4. Двойное отсечение (гистерезис). Выделяются "сильные" и "слабые" ребра. Пиксели, интенсивность которых превышает максимальный порог, считаются пикселями, принадлежащими "сильным" ребрам. Принимается, что пиксели с интенсивностью, входящей в интервал от минимального до максимального порогового значения, принадлежат "слабым" ребрам. Пиксели, интенсивность которых меньше минимального порога, отбрасываются из дальнейшего рассмотрения. Результирующие ребра содержат пиксели всех "сильных" ребер и те пиксели "слабых" ребер, чья окрестность содержит хотя бы один пиксель "сильных" ребер.

Детектор Канни реализован в библиотеке OpenCV [7] в виде отдельной функции, прототип которой приведен далее.

void Canny(const Mat& image, Mat& edges, double threshold1, 
  double threshold2, int apertureSize=3, 
  bool L2gradient=false) 
    

Функция принимает на вход следующие параметры:

  • image – одноканальное 8-битное изображение.
  • edges – результирующая карта ребер, представляется матрицей, размер которой совпадает с размером исходного изображения.
  • threshold1, threshold2 – параметры алгоритма, пороговые значения для отсечения.
  • apertureSize – размер апертуры для применения оператора Собеля.
  • L2gradient – флаг, который указывает, по какой норме будет вычисляться магнитуда градиента. Принимает истинное значение, если используется норма L_{2} (корень квадратный из суммы квадратов частных производных), в противном случае L_{1} (сумма модулей частных производных). Как правило, нормы L_{1} достаточно, и вычисляется она быстрее в связи с отсутствием вызова функции sqrt.

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

#include <stdio.h> 
#include <opencv2/opencv.hpp> 
 
using namespace cv; 
 
const char helper[] = 
  $quot;Sample_Canny.exe <img_file>\n\ 
  \t<img_file> - image file name\n$quot;; 
 
int main(int argc, char* argv[]) 
{
  const char *cannyWinName = $quot;Canny detector$quot;; 
  Mat img, grayImg, edgesImg; 
  double lowThreshold = 70, uppThreshold = 260; 
  if (argc < 2) 
  { 
    printf($quot;%s$quot;, helper); 
    return 1; 
  } 
 
  // загрузка изображения 
  img = imread(argv[1], 1); 
  // удаление шумов 
  blur(img, img, Size(3,3)); 
  // преобразование в оттенки серого 
  cvtColor(img, grayImg, CV_RGB2GRAY); 
  // применение детектора Канни 
  Canny(grayImg, edgesImg, lowThreshold, uppThreshold); 
 
  // отображение результата 
  namedWindow(cannyWinName, CV_WINDOW_AUTOSIZE); 
  imshow(cannyWinName, edgesImg); 
  waitKey(); 
 
  // закрытие окон 
  destroyAllWindows(); 
 
  // осовобождение памяти 
  img.release(); 
  grayImg.release(); 
  edgesImg.release(); 
  return 0; 
} 
    

На рисунке (рис. 7.9) показан результат применения детектора Канни к тестовому изображению (рис. 7.2, слева).

Результат выделения ребер объекта с использованием детектора Канни

Рис. 7.9. Результат выделения ребер объекта с использованием детектора Канни
Александра Максимова
Александра Максимова

При прохождении теста 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).

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