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

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

Первая производная (изображение в центре): локальные экстремумы указывают на край.
Вторая производная (изображение справа): область пересечения нуля указывает на край.
Изображение адаптировано автором. Источник: Laplace Operator (OpenCV documentation).
Это позволяет применять нулевые значения второй производной как дополнительный критерий для обнаружения краев. Однако следует быть осторожным, поскольку другие точки на изображении тоже могут давать вторую производную равную нулю, но не являясь краями. Для устранения таких ситуаций рекомендуется задействовать дополнительные фильтры.
В целом, края на изображении приводят к очень резким областям пересечения нуля.
Определение
Оператор Лапласа формально задается следующей формулой:

Как видно, оператор Лапласа представляет собой сумму вторых производных по направлениям 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.
Лапласиан Гаусса
Сначала импортируем необходимые библиотеки и загрузим входное изображение:
import cv2
import numpy as np
import matplotlib.pyplot as pltБудем использовать пример изображения с несколькими монетами:

Прочитаем изображение:
image = cv2.imread('data/input/coins.png')
image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)Мы преобразовали входное изображение в оттенки серого, чтобы позже вычислять производные относительно изменений интенсивности.
Как обсуждалось ранее, перед применением оператора Лапласа сначала используем гауссов фильтр:
image = cv2.GaussianBlur(image, (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 берется абсолютное значение.
![Функция cv2.convertScaleAbs() отображает значения пикселей в интервал [0, 255].](https://contributor.insightmediagroup.io/wp-content/uploads/2025/11/convertScaleAbs.png)
Обнаружение пересечения нуля
К сожалению, документация OpenCV демонстрирует только пример применения Лапласа, но не показывает, как использовать результаты для обнаружения краев по пересечению нуля. Ниже приведен простой фрагмент кода для этого:
laplacian_sign = np.sign(laplacian_signed)
zero_crossings = np.zeros_like(laplacian_sign, dtype=bool)
for shift in [-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("Laplacian (signed)")
plt.axis('off')
plt.colorbar(im1, fraction=0.05, pad=0.05, label='Pixel value')
plt.subplot(1, 3, 2)
im2 = plt.imshow(laplacian_absolute, cmap='gray', vmin=laplacian_absolute.min(), vmax=laplacian_absolute.max())
plt.title("Laplacian (absolute)")
plt.axis('off')
plt.colorbar(im2, fraction=0.05, pad=0.05, label='Pixel value')
plt.subplot(1, 3, 3)
im2 = plt.imshow(edges, cmap='gray', vmin=edges.min(), vmax=edges.max())
plt.title("Edges from zero-crossings")
plt.axis('off')
plt.colorbar(im2, fraction=0.05, pad=0.05, label='Pixel value')
plt.tight_layout()
plt.show()Вот результат вывода:

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