关于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衍生产品几个问题:
dx+dy == 1
?如果我要为每个轴调用两次Scharr,那么结合结果的正确方法是什么? 我目前正在使用:
addWeighted( abs_grad_x, 0.5, abs_grad_y, 0.5, 0, grad );
但我不确定这个Sobel功能如何结合两个轴以及它应该以什么顺序对所有4个衍生物进行。
如果我通过使用4个不同的内核计算(dx = 2,dy = 2)导数,我想通过将所有4个内核统一为1来减少处理时间,然后将其应用于图像(I假设这就是cv :: Sobel所做的事情。是否有合理的方法来创建这样的组合Shcarr内核并将其与我的图像进行卷积?
谢谢!
答案 0 :(得分:3)
我从未阅读原始的Scharr
论文(论文在German),所以我不知道为什么 Scharr()
函数不允许更高阶导数。也许是因为我在下面#3中提出的第一点?
Scharr
函数应该是一个派生函数。并且多变量函数f(x) = f(x0, ..., xN)
的总导数是
df/dx = dx0*df/dx0 + ... + dxN*df/dxN
即,每个部分的总和乘以变化。在图像的情况下,当然,输入中的变化dx
是单个像素,因此它等于1.换句话说,只是对部分求和;没有把它们加权一半。您可以使用addWeighted()
和1来作为权重,或者您可以将它们相加,但为了确保您不会使图像饱和,您需要转换为浮点数或16位图像第一。然而,如果你试图获得梯度而不是导数,那么计算衍生物的欧几里德幅度也很常见。
然而,这只是一阶导数。对于更高的订单,您需要应用一些连锁裁决。有关组合第二个订单的详细信息,请参阅here。
请注意,一阶导数的优化内核不一定是二阶导数的最佳内核。 Scharr本人有一篇关于优化二阶导数内核的论文,你可以阅读它here。
话虽如此,滤波器分为x
和y
方向,以制作线性可分离滤波器,这基本上将您的2d卷积问题转换为具有较小内核的两个1d卷积。考虑Sobel
和Scharr
内核:对于x
方向,它们都只有两侧的单列具有相同的值(除了一个是负数)。在内核中滑动内核时,在第一个位置,您将第一列和第三列乘以内核中的值。然后两步之后,你将第三个和第五个相乘。但第三个已经计算好了,所以这很浪费。相反,由于两边都是相同的,只需将每列乘以向量,因为您知道需要这些值,然后您可以在第1列和第3列中查找结果的值并减去它们。
简而言之,我不认为你可以将它们与内置的可分离滤波器功能结合起来,因为某些值有时是正值,否则是负值;并且知道何时线性应用滤波器的唯一方法是单独进行。但是,我们可以检查应用这两个过滤器的结果,看看它们如何影响单个像素,构建2D内核,然后与OpenCV进行卷积。
假设我们有一个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())
真
是的。现在我想你可以做两次。