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

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

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