Код в ОП, кажется, берет уравнение для одномерного Лапласа Гаусса и использует его для построения двумерной радиально-симметричной функции.То есть, вдоль любого диаметра ядра функция выглядит как одномерная лапласа Гаусса.Это неправильный способ создания двумерной лапласа Гаусса.
Лаплас Гаусса определяется как как сумма производной второго порядка гауссова ядра по каждой из осей.То есть
LoG = d²/dx² G + d²/dy² G
С G
ядром Гаусса.
С Numpy вы можете построить это ядро следующим образом.Я использую гауссову отделимость для уменьшения сложности вычислений.
s = 5;
x = np.arange(np.floor(-4*s),np.ceil(4*s)+1)
g = 1/(np.sqrt(2*np.pi)*s)*np.exp(-x**2/(2*s**2))
d2g = (x**2 - s**2)/(s**4) * g
log = g * d2g[:,None] + g[:,None] * d2g
Трюки здесь: g
и d2g
являются одномерными функциями.g[:,None]
поворачивает 1D функцию на бок, так что умножение вызывает широковещательную передачу, что приводит к 2D-выводу.
Я написал ядро таким образом, а не выражал полное 2D-уравнение за один раз, потому чтоэто приводит к большой эффективности в вашем коде: свертка изображения f
с ядром log
может быть записана как:
conv(f, log) = conv(f, g * d2g[:,None] + g[:,None] * d2g)
= conv(conv(f, g), d2g[:,None]) + conv(conv(f, g[:,None]), d2g)
, то есть вместо одной свертки с большим двумерным ядром,мы вычисляем 4 свертки с относительно маленькими одномерными ядрами.Обратите внимание, что фактический порядок здесь не имеет значения:
- Один применяет одномерное ядро
g
, а в результате 1D-ядро d2g
вдоль другой оси.Эти две операции можно повернуть вспять. - Затем один повторяет этот процесс, изменяя оси, к которым применяется каждая из операций.
- Наконец, один добавляет два результата.
(Можно использовать cv2.filter2D
там, где я написал conv
. conv
просто указывает на любую функцию свертки, но корреляционная функция, такая как filter2D
, подходит, потому что все ядра симметричны.)