拉普拉斯高斯算子是用于检测斑点还是边缘?

9
以下代码提供自(已要求删除链接)。但我想知道它是如何工作的。我不确定这是否被视为边缘检测或斑点检测,因为维基百科将高斯拉普拉斯算子(LoG)列为斑点检测
此外,有人能解释并提供更深入的解释为什么要计算绝对值以及在focus_stack()函数中发生了什么吗?
#   Compute the gradient map of the image
def doLap(image):

    # YOU SHOULD TUNE THESE VALUES TO SUIT YOUR NEEDS
    kernel_size = 5         # Size of the laplacian window
    blur_size = 5           # How big of a kernal to use for the gaussian blur
                            # Generally, keeping these two values the same or very close works well
                            # Also, odd numbers, please...

    blurred = cv2.GaussianBlur(image, (blur_size,blur_size), 0)
    return cv2.Laplacian(blurred, cv2.CV_64F, ksize=kernel_size)

#
#   This routine finds the points of best focus in all images and produces a merged result...
#
def focus_stack(unimages):
    images = align_images(unimages)

    print "Computing the laplacian of the blurred images"
    laps = []
    for i in range(len(images)):
        print "Lap {}".format(i)
        laps.append(doLap(cv2.cvtColor(images[i],cv2.COLOR_BGR2GRAY)))

    laps = np.asarray(laps)
    print "Shape of array of laplacians = {}".format(laps.shape)

    output = np.zeros(shape=images[0].shape, dtype=images[0].dtype)

    abs_laps = np.absolute(laps)
    maxima = abs_laps.max(axis=0)
    bool_mask = abs_laps == maxima
    mask = bool_mask.astype(np.uint8)
    for i in range(0,len(images)):
        output = cv2.bitwise_not(images[i],output, mask=mask[i])

    return 255-output
2个回答

13
hkchengrex的回答相当全面,但我并不完全同意。也许我有点过于在意正确的术语。探测器是指在被检测物位置产生强响应的东西。 拉普拉斯高斯(LoG)不是边缘检测器,因为它在(接近*)边缘处具有零交叉点。但它可以用来构建一个边缘检测器。这样构建的边缘检测器是Marr-Hildreth边缘检测器。因此,它经常被归类为边缘检测器。对我来说,它是一种线检测器
拉普拉斯是二阶导数的总和(海森矩阵的迹)。使用LoG卷积的图像与使用高斯卷积的图像的拉普拉斯相同:
img * [ d^2/dx^2 G(x,y) + d^2/dy^2 G(x,y) ] = d^2/dx^2 [ img * G(x,y) ] + d^2/dy^2 [ img * G(x,y) ]

因此,LoG在图像的极值点(二阶导数最大的位置)产生强烈响应。这发生在“斑点”的峰值和线条的脊线上。
让我们来看这个简单的测试图像:

image with blocks, lines and dots

并对其应用 LoG:

LoG of image above

这里,中灰色的像素值为0。可以看到,它在细线和小点上有强烈(负)的反应。在较宽物体的边缘周围也有中等的反应(边缘内部为负,外部为正)。零交叉点接近边缘位置。
我们可以对此图像进行阈值处理以便检测细线和小点。

LoG < 65

(将幅度阈值化会产生相同的结果)。我们可以降低阈值,以便看到中等响应发生在感兴趣的边缘周围:

abs(LoG) < 20

要获取边缘,需要不止一个简单的阈值。相反,可以通过对梯度幅度(一阶导数在边缘位置处很强)进行阈值处理来获取边缘:

gradmag < 50

梯度幅值不适用于检测直线,因为它检测的是直线上的两个边缘,而不是直线本身。上面计算的梯度幅值使用高斯导数计算(Sobel是另一个选项,但不如高斯精确)。

请注意,Canny边缘检测器基于梯度幅值,它添加了非极大值抑制和滞后阈值处理,使检测变得细且有意义。


*二阶导数在拐点处(可以视为边缘的真实位置)有零点。然而,拉普拉斯算子是二阶导数之和。如果你想到沿梯度方向的二阶导数,其零点将被很好地定位。但是现在加上垂直方向上的第二个导数(沿着边缘)。这个二阶导数沿着直线边缘为零,沿着凸曲边缘为负(例如圆的边缘),沿着凹曲边缘为正。因此,将这两个值相加会导致零点在曲边缘上偏移,曲率越强,零点偏离其真实位置就越大。


非常感谢。我有一个快速的问题...您是在说上面的代码构建了Marr-Hildreth边缘检测器吗? - john
@john:不,它并没有构建Marr-Hildreth边缘检测器。hkcgengrex已经回答了代码的作用部分,我不觉得有必要重复。你应该接受那个答案。我的回答只是为了澄清“边缘检测器”的含义,并提供一些关于LoG的更多信息。基本上,我只回答了标题中的问题。 :) - Cris Luengo
我非常感谢你的帮助,Cris。 - john

6

编辑:Cris Luengo 是对的。忽略有关边缘检测器的部分。


Laplacian of Gaussian(LoG)可以用作斑点检测器和边缘检测器。我将跳过详细的数学和理论,我认为你可以在书籍或一些网站这里这里这里上阅读它们。
为了看到它为什么可以同时用作两者,让我们看看它的图形和内核。

enter image description here enter image description here

如果您有一个半径为3且值为1的blob位于核心位置,并且背景值为0,那么您将获得非常强烈的(负)响应。如果正确设置半径,则可以很清楚地进行blob检测。
边缘检测怎么样?它不像Sobel算子一样给出梯度和边缘的强烈响应。Sobel算子不能给出准确的边缘,因为梯度通常在几个像素上升和下降。然后您的边缘将会是几个像素宽。为了使其本地化更加准确,我们可以找到具有最大(或最小)梯度的像素。这意味着它的二阶导数(Laplacian)应该等于零,在该点处有一个零交叉。

BeforeAfter

您可以看到处理后的图像有一个明暗带。零交叉点是边缘。为了通过内核查看这一点,请尝试手动在内核上滑动一个完美的步进边缘,以查看响应如何变化。
对于您的第二个问题,我猜测绝对值正在尝试找到亮斑和暗斑(亮斑,暗背景;暗斑,亮背景),因为它们分别给出强烈的负响应和强烈的正响应。然后在每个像素位置上在所有图像中找到最大值。对于每个输出像素,它使用具有最大响应的图像中的像素作为输出。我认为他的理由是具有强烈脉冲(小斑点)的像素处于焦点。
他使用bitwise_not作为复制机制。它将由掩码指定的一些像素设置为源图像的按位非。最终,您将拥有由不同来源的像素组成的output,除了所有这些像素都经历了按位非。要恢复真实图像,只需再次进行“NOT”操作,如NOT(NOT(x)) = x255-x正是这样做的。我认为copyTo也可以工作,不确定为什么他选择了其他方式。
图片取自http://fourier.eng.hmc.edu/e161/lectures/gradient/node8.html

我并不是要让你删掉一半的回答……它从技术上讲都是正确的,只是你所称呼的“边缘检测器”的命名不准确。把那部分删掉后,我再也不会矛盾地考虑是否给你点赞了。 :) - Cris Luengo
非常感谢。我对代码有一个问题:为什么代码的最后一行返回255-输出? - john
@hkchengrex非常感谢你。你能解释一下我该如何使用copyTo函数吗?我在这个主题上提了另一个问题这里 - john

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接