Sobel滤芯大尺寸

时间:2012-03-05 13:59:30

标签: image-processing computer-vision edge-detection sobel

我正在使用尺寸为3x3的sobel滤镜来计算图像导数。看一下互联网上的一些文章,似乎sobel过滤器的大小为5x5和7x7的内核也很常见,但我无法找到它们的内核值。

有人可以让我知道尺寸为5x5和7x7的sobel滤镜的内核值吗?此外,如果有人可以共享一个方法来生成内核值,那将非常有用。

提前致谢。

7 个答案:

答案 0 :(得分:36)

其他来源似乎给出了较大内核的不同定义。例如,Intel IPP library将5x5内核作为

1  2 0  -2 -1
4  8 0  -8 -4
6 12 0 -12 -6
4  8 0  -8 -4
1  2 0  -2 -1

直观地说,这对我来说更有意义,因为你更关注靠近中心的元素。它还具有3x3内核的自然定义,易于扩展以生成更大的内核。也就是说,在我的简短搜索中,我发现了5x5内核的3种不同定义 - 所以我怀疑(正如Paul所说)较大的内核是临时的,所以这绝不是明确的答案。

3x3内核是平滑内核和渐变内核的外积,在Matlab中这就像

sob3x3 = [ 1 2 1 ]' * [1 0 -1]

可以通过将3x3内核与另一个平滑内核进行卷积来定义更大的内核

sob5x5 = conv2( [ 1 2 1 ]' * [1 2 1], sob3x3 )

您可以重复此过程以逐渐获得更大的内核

sob7x7 = conv2( [ 1 2 1 ]' * [1 2 1], sob5x5 )
sob9x9 = conv2( [ 1 2 1 ]' * [1 2 1], sob7x7 )
...

还有很多其他的写作方式,但我认为这可以准确地解释发生了什么。基本上,你从一个方向的平滑内核和另一个方向的导数的有限差分估计开始,然后只应用平滑直到你得到你想要的内核大小。

因为它只是一系列卷积,所有好的属性都有,(交换性,关联性等)可能对你的实现有用。例如,您可以将5x5内核简单地分离为平滑和衍生组件:

sob5x5 = conv([1 2 1],[1 2 1])' * conv([1 2 1],[-1 0 1])

请注意,为了成为“适当的”衍生估算器,3x3 Sobel应按比例缩小1/8:

sob3x3 = 1/8 * [ 1 2 1 ]' * [1 0 -1]

并且每个较大的内核需要按1/16的额外因子进行缩放(因为平滑内核未规范化):

sob5x5 = 1/16 * conv2( [ 1 2 1 ]' * [1 2 1], sob3x3 )
sob7x7 = 1/16 * conv2( [ 1 2 1 ]' * [1 2 1], sob5x5 )
...

答案 1 :(得分:16)

更新2018年4月23日:似乎以下链接中定义的内核不是真正的Sobel内核(5x5及更高版本) - 它们可以做一个合理的边缘检测工作,但它们不应该是称为Sobel内核。有关更准确和全面的摘要,请参阅Daniel’s answer。 (我将在这里留下这个答案,因为(a)它与各个地方有联系,(b)接受的答案不容易被删除。)

谷歌似乎发现了很多结果,例如 http://rsbweb.nih.gov/nih-image/download/user-macros/slowsobel.macro建议以下内核为3x3,5x5,7x7和9x9:

3×3:

1   0   -1
2   0   -2
1   0   -1

5×5:

2   1   0   -1  -2
3   2   0   -2  -3
4   3   0   -3  -4
3   2   0   -2  -3
2   1   0   -1  -2

的7x7:

3   2   1   0   -1  -2  -3
4   3   2   0   -2  -3  -4
5   4   3   0   -3  -4  -5
6   5   4   0   -4  -5  -6
5   4   3   0   -3  -4  -5
4   3   2   0   -2  -3  -4
3   2   1   0   -1  -2  -3

9x9的:

4   3   2   1   0   -1  -2  -3  -4
5   4   3   2   0   -2  -3  -4  -5
6   5   4   3   0   -3  -4  -5  -6
7   6   5   4   0   -4  -5  -6  -7
8   7   6   5   0   -5  -6  -7  -8
7   6   5   4   0   -4  -5  -6  -7
6   5   4   3   0   -3  -4  -5  -6
5   4   3   2   0   -2  -3  -4  -5
4   3   2   1   0   -1  -2  -3  -4

<击>

答案 2 :(得分:3)

Sobel梯度滤波器发生器

(这个答案是指上面@Daniel给出的analysis。)

Gx[i,j] = i / (i*i + j*j)

Gy[i,j] = j / (i*i + j*j)

这是一个重要的结果,是一个比original paper更好的解释。它应该写在Wikipedia或某处,因为它似乎也优于任何其他关于互联网上可用问​​题的讨论。

然而,如所声称的那样,对于大小大于5 * 5的滤波器,整数值表示实际上并不实际。使用64位整数,可以准确表达最大15 * 15的Sobel滤波器。

这是前四个;结果应除以&#34;权重&#34;,以便将图像区域的梯度(如下所示)标准化为值1。

1 2 3 4 5
1 2 3 4 5
1 2 3 4 5
1 2 3 4 5
1 2 3 4 5

Gx(3):

-1/2  0/1  1/2           -1  0  1
-1/1    0  1/1   * 2 =   -2  0  2
-1/2  0/1  1/2           -1  0  1

weight = 4               weight = 8

Gx(5):

-2/8 -1/5  0/4  1/5  2/8             -5  -4   0   4   5
-2/5 -1/2  0/1  1/2  2/5             -8 -10   0  10   8
-2/4 -1/1    0  1/1  2/4   * 20 =   -10 -20   0  20  10
-2/5 -1/2  0/1  1/2  2/5             -8 -10   0  10   8
-2/8 -1/5  0/4  1/5  2/8             -5  -4   0   4   5

weight = 12                          weight = 240

Gx(7):

-3/18 -2/13 -1/10   0/9  1/10  2/13  3/18             -130 -120  -78    0   78  120  130
-3/13  -2/8  -1/5   0/4   1/5   2/8  3/13             -180 -195 -156    0  156  195  180
-3/10  -2/5  -1/2   0/1   1/2   2/5  3/10             -234 -312 -390    0  390  312  234
 -3/9  -2/4  -1/1     0   1/1   2/4   3/9   * 780 =   -260 -390 -780    0  780  390  260
-3/10  -2/5  -1/2   0/1   1/2   2/5  3/10             -234 -312 -390    0  390  312  234
-3/13  -2/8  -1/5   0/4   1/5   2/8  3/13             -180 -195 -156    0  156  195  180
-3/18 -2/13 -1/10   0/9  1/10  2/13  3/18             -130 -120  -78    0   78  120  130

weight = 24                                           weight = 18720

Gx(9):

-4/32 -3/25 -2/20 -1/17  0/16  1/17  2/20  3/25  4/32                -16575  -15912  -13260   -7800       0    7800   13260   15912   16575
-4/25 -3/18 -2/13 -1/10   0/9  1/10  2/13  3/18  4/25                -21216  -22100  -20400  -13260       0   13260   20400   22100   21216
-4/20 -3/13  -2/8  -1/5   0/4   1/5   2/8  3/13  4/20                -26520  -30600  -33150  -26520       0   26520   33150   30600   26520
-4/17 -3/10  -2/5  -1/2   0/1   1/2   2/5  3/10  4/17                -31200  -39780  -53040  -66300       0   66300   53040   39780   31200
-4/16  -3/9  -2/4  -1/1     0   1/1   2/4   3/9  4/16   * 132600 =   -33150  -44200  -66300 -132600       0  132600   66300   44200   33150
-4/17 -3/10  -2/5  -1/2   0/1   1/2   2/5  3/10  4/17                -31200  -39780  -53040  -66300       0   66300   53040   39780   31200
-4/20 -3/13  -2/8  -1/5   0/4   1/5   2/8  3/13  4/20                -26520  -30600  -33150  -26520       0   26520   33150   30600   26520
-4/25 -3/18 -2/13 -1/10   0/9  1/10  2/13  3/18  4/25                -21216  -22100  -20400  -13260       0   13260   20400   22100   21216
-4/32 -3/25 -2/20 -1/17  0/16  1/17  2/20  3/25  4/32                -16575  -15912  -13260   -7800       0    7800   13260   15912   16575

weight = 40                                                          weight = 5304000

下面附带的Ruby程序将计算Sobel滤波器和任何大小的相应权重,尽管整数值滤波器对于大于15 * 15的大小不太可能有用。

#!/usr/bin/ruby

# Sobel image gradient filter generator
# by <ian_bruce@mail.ru> -- Sept 2017
# reference:
# https://stackoverflow.com/questions/9567882/sobel-filter-kernel-of-large-size


if (s = ARGV[0].to_i) < 3 || (s % 2) == 0
    $stderr.puts "invalid size"
    exit false
end

s /= 2


n = 1

# find least-common-multiple of all fractional denominators
(0..s).each { |j|
    (1..s).each { |i|
        d = i*i + j*j
        n = n.lcm(d / d.gcd(i))
    }
}


fw1 = format("%d/%d", s, 2*s*s).size + 2
fw2 = format("%d", n).size + 2


weight = 0
s1 = ""
s2 = ""

(-s..s).each { |y|
    (-s..s).each { |x|
        i, j = x, y   # "i, j = y, x" for transpose
        d = i*i + j*j
        if (i != 0)
            if (n * i % d) != 0   # this should never happen
                $stderr.puts "inexact division: #{n} * #{i} / ((#{i})^2 + (#{j})^2)"
                exit false
            end
            w = n * i / d
            weight += i * w
        else
            w = 0
        end
        s1 += "%*s" % [fw1, d > 0 ? "%d/%d" % [i, d] : "0"]
        s2 += "%*d" % [fw2, w]
    }
    s1 += "\n" ; s2 += "\n"
}


f = n.gcd(weight)

puts s1

puts "\nweight = %d%s" % [weight/f, f < n ? "/%d" % (n/f) : ""]

puts "\n* #{n} =\n\n"

puts s2

puts "\nweight = #{weight}"

答案 3 :(得分:2)

我快速攻击算法以生成任何奇数大小的Sobel内核&gt; 1,基于@Paul R给出的例子:

    public static void CreateSobelKernel(int n, ref float[][] Kx, ref float[][] Ky)
    {
        int side = n * 2 + 3;
        int halfSide = side / 2;
        for (int i = 0; i < side; i++)
        {
            int k = (i <= halfSide) ? (halfSide + i) : (side + halfSide - i - 1);
            for (int j = 0; j < side; j++)
            {
                if (j < halfSide)
                    Kx[i][j] = Ky[j][i] = j - k;
                else if (j > halfSide)
                    Kx[i][j] = Ky[j][i] = k - (side - j - 1);
                else
                    Kx[i][j] = Ky[j][i] = 0;
            }
        }
    }

希望它有所帮助。

答案 4 :(得分:2)

这是使用numpy和@Daniel答案使用python 3进行的简单解决方案。

def custom_sobel(shape, axis):
    """
    shape must be odd: eg. (5,5)
    axis is the direction, with 0 to positive x and 1 to positive y
    """
    k = np.zeros(shape)
    p = [(j,i) for j in range(shape[0]) 
           for i in range(shape[1]) 
           if not (i == (shape[1] -1)/2. and j == (shape[0] -1)/2.)]

    for j, i in p:
        j_ = int(j - (shape[0] -1)/2.)
        i_ = int(i - (shape[1] -1)/2.)
        k[j,i] = (i_ if axis==0 else j_)/float(i_*i_ + j_*j_)
    return k

它返回内核(5,5),如下所示:

Sobel x:
   [[-0.25 -0.2   0.    0.2   0.25]
    [-0.4  -0.5   0.    0.5   0.4 ]
    [-0.5  -1.    0.    1.    0.5 ]
    [-0.4  -0.5   0.    0.5   0.4 ]
    [-0.25 -0.2   0.    0.2   0.25]]


Sobel y:
   [[-0.25 -0.4  -0.5  -0.4  -0.25]
    [-0.2  -0.5  -1.   -0.5  -0.2 ]
    [ 0.    0.    0.    0.    0.  ]
    [ 0.2   0.5   1.    0.5   0.2 ]
    [ 0.25  0.4   0.5   0.4   0.25]]

如果有人知道在python中更好的方法,请告诉我。我还是个新手;)

答案 5 :(得分:0)

谢谢大家,我将尝试使用@Adam Bowen的第二个变体,为此变体采用Sobel5x5,7x7,9x9 ...矩阵的C#代码(如果你发现bug或者可以优化代码,可能会出现错误 - 写一下有):

    static void Main(string[] args)
    {
        float[,] Sobel3x3 = new float[,] {
            {-1, 0, 1},
            {-2, 0, 2},
            {-1, 0, 1}};

        float[,] Sobel5x5 = Conv2DforSobelOperator(Sobel3x3);
        float[,] Sobel7x7 = Conv2DforSobelOperator(Sobel5x5);
        Console.ReadKey();
    }

    public static float[,] Conv2DforSobelOperator(float[,] Kernel)
    {
        if (Kernel == null)
            throw new Exception("Kernel = null");

        if (Kernel.GetLength(0) != Kernel.GetLength(1))
            throw new Exception("Kernel matrix must be Square matrix!");

        float[,] BaseMatrix = new float[,] {
            {1, 2, 1},
            {2, 4, 2},
            {1, 2, 1}};

        int KernelSize = Kernel.GetLength(0);
        int HalfKernelSize = KernelSize / 2;
        int OutSize = KernelSize + 2;

        if ((KernelSize & 1) == 0) // Kernel_Size must be: 3, 5, 7, 9 ...
            throw new Exception("Kernel size must be odd (3x3, 5x5, 7x7...)");

        float[,] Out = new float[OutSize, OutSize];
        float[,] InMatrix = new float[OutSize, OutSize];

        for (int x = 0; x < BaseMatrix.GetLength(0); x++)
            for (int y = 0; y < BaseMatrix.GetLength(1); y++)
                InMatrix[HalfKernelSize + x, HalfKernelSize + y] = BaseMatrix[x, y];

        for (int x = 0; x < OutSize; x++)
            for (int y = 0; y < OutSize; y++)
                for (int Kx = 0; Kx < KernelSize; Kx++)
                    for (int Ky = 0; Ky < KernelSize; Ky++)
                    {
                        int X = x + Kx - HalfKernelSize;
                        int Y = y + Ky - HalfKernelSize;

                        if (X >= 0 && Y >= 0 && X < OutSize && Y < OutSize)
                            Out[x, y] += InMatrix[X, Y] * Kernel[KernelSize - 1 - Kx, KernelSize - 1 - Ky];
                    }
        return Out;
    }

Results (NormalMap)it copy there,这个方法 - №2,@ Paul R metod - №1。现在我正在使用last,因为它可以提供更平滑的结果,并且使用this代码生成内核很容易。

答案 6 :(得分:0)

Daniel's answer的Matlab实现:

kernel_width = 9;

halfway = floor(kernel_width/2);
step = -halfway : halfway;

i_matrix = repmat(step,[kernel_width 1]);
j_matrix = i_matrix';

gx = i_matrix ./ ( i_matrix.*i_matrix + j_matrix.*j_matrix );
gx(halfway+1,halfway+1) = 0; % deals with NaN in middle

gy = gx';