Лаплас встречает Гаусса — история двух операторов в области обнаружения границ
Делиться

Введение
Обнаружение признаков — это область компьютерного зрения, ориентированная на использование инструментов для обнаружения интересующих областей на изображениях. Важной особенностью большинства алгоритмов обнаружения признаков является то, что они не используют машинное обучение, что делает результаты более интерпретируемыми, а в некоторых случаях и более быстрыми.
В предыдущей статье этой серии мы узнали о краях — зонах резкого изменения интенсивности изображения. Края обычно представляют собой локальные зоны, где можно обнаружить интересующий объект.
Например, рассмотрим изображение голубого неба, над которым летит небольшой самолёт. Разница интенсивности по всему изображению будет практически нулевой, за исключением небольшой зоны пересечения самолёта с фоном голубого неба. В результате мы можем легко определить зону, в которой находится самолёт, и её форму, используя производную изображения или фильтр Собеля.

Другими словами, мы можем обнаружить края, взяв минимальные/максимальные пики первой производной функции интенсивности , что представлено изображением в середине.
Вторая производная изображения
Вернёмся к упомянутому примеру и спросим себя, что произойдёт, если взять вторую производную? Ответ показан на правом графике ниже. В этом случае минимальные/максимальные пики первой производной будут соответствовать нулям второй производной.

Это означает, что теперь мы можем использовать нулевые значения второй производной в качестве дополнительного критерия для выделения границ. Однако важно соблюдать осторожность, поскольку другие точки на изображении также могут приводить к тому, что вторая производная равна нулю, но при этом не являться границами. Для этого рекомендуется использовать другие фильтры, чтобы исключить такие случаи.
Как правило, края изображения приводят к очень резким областям пересечения нуля.
Определение
Лапласиан формально определяется следующей формулой:

Как мы видим, Лапласиан — это просто сумма вторых производных по направлениям x и y.
Лапласиан не предоставляет информацию о направлении ребра.
Дискретное приближение
Учитывая приведенную выше формулу Лапласа для непрерывного случая, попытаемся получить приближение для дискретного случая изображений.
Для этого предположим, что для части изображения ниже мы хотим вычислить лапласиан для пикселя I₂₂. Вычислим производную по оси X.

Для этого мы дважды используем формулу производной из предыдущей статьи, выбрав два значения для Δx: -1 и 1. Получаем:
- Для Δx = -1: dI / dx = I₂₃ – I₂₂
- Для Δx = 1: dI / dx = I₂₂ – I₂₁
Для вычисления первой производной мы, по сути, вычисляем разность между соседними пикселями. Аналогичную логику можно применить и для второй производной, которую неформально можно назвать разностью разностей.
Следовательно, мы можем взять две только что вычисленные нами разности и найти их разность. Более формально, это было бы то же самое, если бы мы применили стандартную формулу производной, приравняв Δx к -1. Получаем:
- d2I / dx = (I₂₃ – I₂₂) – (I₂₂ – I₂₁) = I₂₃ – 2I₂₂ + I₂₁
Отлично! Мы вычислили вторую производную по оси X. Аналогичную процедуру можно выполнить для второй производной по оси Y. Получим следующее выражение:
- d2I / dy = (I₃₂ – I₂₂) – (I₂₂ – I₁₂) = I₃₂ – 2I₂₂ + I₁₂
Наконец, осталось только сложить обе производные. Получаем:
- d2I / dx + d2I / dy = I₁₂ + I₂₁ + I₂₃ + I₃₂ – 4I₂₂
Весь процесс вычисления Лапласа можно визуализировать с помощью диаграммы ниже:

На основе полученного результата мы также можем построить свёрточное ядро 3×3 для Лапласа:

Интересно, что сумма элементов этого ядра равна 0, что означает, что если входное изображение постоянно, то применение фильтра даст матрицу с нулевыми элементами. Это логичный результат, поскольку интенсивность не меняется.
В отличие от ядер Собеля и Шарра, ядро Лапласа обнаруживает изменения интенсивности в обоих направлениях . Достаточно применить ядро 3×3 к любому изображению, и оператор Лапласа выдаст конечные скалярные значения, представляющие изменения интенсивности.
Напомним, что при использовании операторов Собеля и Шарра ядра применялись по отдельности к осям X и Y, а затем рассчитывались их величины.
Обнаружение диагонального края
Мы вывели ядро, представляющее собой сумму вторых производных по осям X и Y. Следовательно, этот метод хорошо подходит для обнаружения как горизонтальных, так и вертикальных рёбер. Но что, если рёбро на изображении имеет диагональное направление? Мы это пока не учитывали.
По этой причине представленное выше ядро обычно слегка корректируется с учётом диагонального направления. Один из наиболее распространённых вариантов — использовать следующее ядро:

Изотропия
Мы видим, что матрица Лапласа симметрична, что делает её изотропной. Изотропия — это свойство, согласно которому ядро инвариантно к вращению, то есть результат применения изотропного фильтра к изображению и его повёрнутой версии одинаков.
Шум
Ядро Лапласа, которое мы рассматривали выше, хорошо подходит для обнаружения границ. Однако мы не учли другой аспект, который мог бы помешать нам эффективно применять этот фильтр в реальной жизни: шум .
В начале статьи мы видели несколько графиков изменения интенсивности изображения по оси X, где мы строили первую и вторую производные интенсивности изображения. Фактически, эти графики были построены для идеального изображения, в котором нет шума.
Если присутствует шум, мы можем оказаться в ситуации, подобной той, что изображена на диаграмме ниже.

График слева иллюстрирует более реалистичный сценарий значений интенсивности вдоль оси изображения. Хотя шум не очень сильный, он всё же быстро флуктуирует в локальных областях. В то же время мы используем производные для обнаружения тех же быстрых изменений, что создаёт проблему.
В конечном итоге, взятие первой производной приведёт к изменению интенсивности, как показано на графике справа. Очевидно, что нормальное обнаружение границ с помощью производных невозможно при наличии таких колебаний.
Фильтр Гаусса
Фильтр Гаусса — это метод подавления шума в изображении, позволяющий применять лапласиан или другой оператор обнаружения границ без ограничений.
Формально гауссово распределение задается следующей формулой (где μ = 0):

Функция Гаусса g(x) отображена ниже на верхнем правом графике:

Умножение функции интенсивности I(x) на гауссову функцию g(x) приводит к сглаживанию интенсивности в целом, что упрощает анализ. Пример показан на диаграмме слева внизу.
Учитывая гладкую форму функции, мы можем затем взять первую производную, точки экстремума которой будут указывать на ребра (нижняя правая диаграмма).
Производная гауссовой функции
Мы можем заметить, что взятие первой производной изображения является линейной операцией, поэтому мы можем разложить наши вычисления следующим образом:

Это позволяет нам заранее вычислить первую производную гауссовой функции, а затем умножить ее на функцию интенсивности, что оптимизирует вычисления.
Лапласиан Гаусса
Мы можем применить тот же приём ко второй производной, о котором мы говорили в начале статьи, для обнаружения границ. В результате мы можем заранее вычислить вторую производную гауссовой функции, а затем умножить её на функцию интенсивности. Результат показан на нижнем правом графике:

Полученная функция называется лапласианом гауссовой функции и широко используется для обнаружения контуров, а также устойчива к шумам на изображении.
Вторую производную гауссовой функции (нижнее левое изображение) часто называют перевернутым «мексиканским сомбреро» из-за ее большого сходства по форме со шляпой сомбреро.
OpenCV
Изучив теорию обнаружения границ с использованием вторых производных изображений, настало время рассмотреть, как фильтры Лапласа могут быть реализованы в OpenCV.
Лапласиан Гаусса
Для начала импортируем необходимые библиотеки и загрузим входное изображение:
импорт cv2 импорт numpy как np импорт matplotlib.pyplot как plt
Мы будем использовать пример изображения, содержащего несколько монет:

Давайте прочитаем изображение:
изображение = cv2.imread('data/input/coins.png') изображение = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
Мы преобразовали входное изображение в формат grasycale, чтобы иметь возможность в дальнейшем брать производные относительно изменений интенсивности.
Как обсуждалось выше, перед использованием оператора Лапласа мы применим гауссовский фильтр:
изображение = cv2.GaussianBlur(изображение, (7, 7), 0)
- Второй параметр (7, 7) относится к размеру ядра.
- Третий параметр используется для определения значения стандартного отклонения σ из формулы Гаусса, приведённой выше. Здесь мы устанавливаем его равным 0, что означает, что стандартное отклонение σ будет выбрано OpenCV автоматически на основе предоставленного размера ядра.
Теперь мы готовы применить фильтр Лапласа:
laplacian_signed = cv2.Laplacian(image, cv2.CV_16S, ksize=3)
Здесь мы указываем тип выходных данных как cv2.CV_16S, что эквивалентно типу short, со значениями в диапазоне [-32768, 32767]. Мы делаем это, поскольку фильтр Лапласа может выдавать значения, выходящие за пределы стандартного пиксельного интервала [0, 255]. Если бы мы этого не сделали, выходные значения были бы ограничены диапазоном [0, 255], и мы бы потеряли информацию.
OpenCV также предоставляет полезную функцию для сопоставления необработанного вывода Лапласа (или любых других результирующих значений) со стандартным диапазоном [0, 255]:
laplacian_absolute = cv2.convertScaleAbs(laplacian_signed)
Результирующая переменная laplacian_absolute имеет ту же форму, что и laplacian_signed , но значения ограничены диапазоном [0, 255]. Преобразование выполняется с помощью функции cv2.convertScaleAbs() таким образом, чтобы сохранить информацию о пересечениях нуля:
- Значения, абсолютное значение которых больше 255, ограничиваются 255.
- Для значений от -255 до 255 берется абсолютное значение.

Обнаружение пересечения нуля
К сожалению, в документации OpenCV представлен только пример применения лапласиана, но не показано, как использовать его результаты для обнаружения пересечения нулевого уровня. Ниже приведён простой фрагмент кода, реализующий это:
laplacian_sign = np.sign(laplacian_signed) zero_crossings = np.zeros_like(laplacian_sign, dtype=bool) для сдвига в [-5, 5]: zero_crossings |= (np.roll(laplacian_sign, shift, axis=0) * laplacian_sign < 0) zero_crossings |= (np.roll(laplacian_sign, shift, axis=1) * laplacian_sign < 0) threshold = 20 edges = np.uint8(zero_crossings & (np.abs(laplacian_signed) > threshold)) * 255
Проще говоря, мы создаём переменную zero_crossings , которая содержит информацию о том, является ли пиксель в позиции (x, y) кандидатом на нулевое пересечение. В нашем коде мы считаем пиксель кандидатом на нулевое пересечение, если его знак (+ или -) отличается от знака любого сдвинутого пикселя на пять позиций по горизонтали или вертикали.
Мы могли бы выбрать другую константу сдвига (не обязательно 5) или даже объединить несколько из них и/или также учесть диагональные сдвиги.
Чтобы исключить нерелевантные нулевые пересечения, мы оставляем только те, абсолютное значение лапласиана которых превышает определенный порог (20).
Визуализация
Наконец, давайте составим график изображений, полученных в ходе нашего анализа:
plt.figure(figsize=(12, 4)) plt.subplot(1, 3, 1) im1 = plt.imshow(laplacian_signed, cmap='gray', vmin=laplacian_signed.min(), vmax=laplacian_signed.max()) plt.title(«Лапласиан (знаковый)») plt.axis('off') plt.colorbar(im1, fraction=0.05, pad=0.05, label='Значение пикселя') plt.subplot(1, 3, 2) im2 = plt.imshow(laplacian_absolute, cmap='gray', vmin=laplacian_absolute.min(), vmax=laplacian_absolute.max()) plt.title(«Лапласиан (абсолютный)») plt.axis('off') plt.colorbar(im2, дробь=0.05, площадку=0.05, метку='Значение пикселя') plt.subplot(1, 3, 3) im2 = plt.imshow(edges, cmap='gray', vmin=edges.min(), vmax=edges.max()) plt.title(«Ребра от нулевых пересечений») plt.axis('off') plt.colorbar(im2, дробь=0.05, площадку=0.05, метку='Значение пикселя') plt.tight_layout() plt.show()
Вот какой результат мы получаем на выходе:

Как видите, мы успешно обнаружили контуры на правом бинарном изображении. Следующим шагом может быть, например, применение метода OpenCV cv2.findContours() для обнаружения контуров и их дальнейшего анализа.
Учитывая простую форму знакового лапласианского вывода, мы могли бы также использовать его для определения границ монет.
Заключение
Используя знания о производных изображений из предыдущей части, эта статья анализирует роль второй производной и обнаружения пересечения нуля. Это также стало отличным способом познакомиться с фильтрами Лапласа и Гаусса, которые можно легко реализовать с помощью OpenCV для обнаружения контуров.
Ресурсы
- Градиенты изображений | OpenCV
- Оператор Лапласа | OpenCV
- Дискретный оператор Лапласа | Википедия
- Нормальное распределение | Википедия
Все изображения, если не указано иное, принадлежат автору.
Источник: towardsdatascience.com



























