在opencv中结合使用Scharr衍生物

时间:2017-09-10 12:44:22

标签: c++ opencv image-processing

关于Scharr衍生产品及其OpenCV实施,我几乎没有问题。

我对具有(3X3)内核的二阶图像导数感兴趣。 我开始使用Sobel二阶导数,但未能在图像中找到一些细线。在this page底部阅读Sobel和Charr比较后,我决定通过更改此行来尝试Scharr:

Sobel(gray, grad, ddepth, 2, 2, 3, scale, delta, BORDER_DEFAULT);

到这一行:

Scharr(img, gray, ddepth, 2, 2, scale, delta, BORDER_DEFAULT );

我的问题是,似乎cv :: Scharr允许一次只执行一个偏导数的第一阶,所以我得到以下错误:

错误:( - 215)dx> = 0&& dy> = 0&&函数getScharrKernels中的dx + dy == 1

(参见断言行here

遵循此限制,我对Scharr衍生产品几个问题

  1. 使用高阶Scharr衍生物是否被视为不良做法?为什么OpenCV选择断言dx+dy == 1
  2. 如果我要为每个轴调用两次Scharr,那么结合结果的正确方法是什么? 我目前正在使用:

    addWeighted( abs_grad_x, 0.5, abs_grad_y, 0.5, 0, grad );

    但我不确定这个Sobel功能如何结合两个轴以及它应该以什么顺序对所有4个衍生物进行。

  3. 如果我通过使用4个不同的内核计算(dx = 2,dy = 2)导数,我想通过将所有4个内核统一为1来减少处理时间,然后将其应用于图像(I假设这就是cv :: Sobel所做的事情。是否有合理的方法来创建这样的组合Shcarr内核并将其与我的图像进行卷积?

  4. 谢谢!

1 个答案:

答案 0 :(得分:3)

  1. 我从未阅读原始的Scharr论文(论文在German),所以我不知道为什么 Scharr()函数不允许更高阶导数。也许是因为我在下面#3中提出的第一点?

  2. Scharr函数应该是一个派生函数。并且多变量函数f(x) = f(x0, ..., xN)总导数

    df/dx = dx0*df/dx0 + ... + dxN*df/dxN
    

    即,每个部分的总和乘以变化。在图像的情况下,当然,输入中的变化dx是单个像素,因此它等于1.换句话说,只是对部分求和;没有把它们加权一半。您可以使用addWeighted()和1来作为权重,或者您可以将它们相加,但为了确保您不会使图像饱和,您需要转换为浮点数或16位图像第一。然而,如果你试图获得梯度而不是导数,那么计算衍生物的欧几里德幅度也很常见。

    然而,这只是一阶导数。对于更高的订单,您需要应用一些连锁裁决。有关组合第二个订单的详细信息,请参阅here

  3. 请注意,一阶导数的优化内核不一定是二阶导数的最佳内核。 Scharr本人有一篇关于优化二阶导数内核的论文,你可以阅读它here

    话虽如此,滤波器分为xy方向,以制作线性可分离滤波器,这基本上将您的2d卷积问题转换为具有较小内核的两个1d卷积。考虑SobelScharr内核:对于x方向,它们都只有两侧的单列具有相同的值(除了一个是负数)。在内核中滑动内核时,在第一个位置,您将第一列和第三列乘以内核中的值。然后两步之后,你将第三个和第五个相乘。但第三个已经计算好了,所以这很浪费。相反,由于两边都是相同的,只需将每列乘以向量,因为您知道需要这些值,然后您可以在第1列和第3列中查找结果的值并减去它们。

    简而言之,我不认为你可以将它们与内置的可分离滤波器功能结合起来,因为某些值有时是正值,否则是负值;并且知道何时线性应用滤波器的唯一方法是单独进行。但是,我们可以检查应用这两个过滤器的结果,看看它们如何影响单个像素,构建2D内核,然后与OpenCV进行卷积。

  4. 假设我们有一个3x3图像:

    image
    =====
    a b c 
    d e f 
    g h i
    

    我们有Scharr内核:

    kernel_x
    ========
    -3    0   3
    -10   0   10
    -3    0   3
    
    kernel_y
    ========
    -3   -10  -3
     0    0    0
     3    10   3
    

    将每个内核应用于此图像的结果为我们提供了:

    image * kernel_x
    ================
    -3a  -10b  -3c 
    +0d  +0e   +0f
    +3g  +10h  +3i  
    
    image * kernel_y  
    ================
    -3a   +0b  +3c 
    -10d  +0e  +10f 
    -3g   +0h  +3i
    

    将这些值相加并放入像素e。由于这两者的总和是总导数,因此我们在一天结束时将所有这些值加到像素e中。

    image * kernel_x + image * kernel y
    ===================================
    -3a -10b -3c           +3g +10h +3i
    -3a      +3c -10d +10f -3g      +3i
    +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
    -6a -10b +0c -10d +10f +0g +10h +6i
    

    如果我们乘以内核

    ,这就是我们得到的结果
    kernel_xy
    =============
    -6   -10   0
    -10   0    10
     0    10   6
    

    因此,有一个2D内核可以执行单阶导数。注意一切有趣吗?它只是增加了两个内核。那令人惊讶吗?不是,x(a+b) = ax + bx。现在我们可以将其传递给filter2D() 计算衍生物的加法。这实际上是否会产生相同的结果?

    import cv2
    import numpy as np
    
    img = cv2.imread('cameraman.png', 0).astype(np.float32)
    
    kernel = np.array([[-6, -10, 0],
                       [-10, 0, 10],
                       [0, 10, 6]])
    
    total_first_derivative = cv2.filter2D(img, -1, kernel)
    
    scharr_x = cv2.Scharr(img, -1, 1, 0)
    scharr_y = cv2.Scharr(img, -1, 0, 1)
    
    print((total_first_derivative == (scharr_x + scharr_y)).all())
    
      

    是的。现在我想你可以做两次。