Sobel filter kernel of large size

后端 未结 9 776
渐次进展
渐次进展 2020-12-02 08:38

I am using a sobel filter of size 3x3 to calculate the image derivative. Looking at some articles on the internet, it seems that kernels for sobel filter for size 5x5 and 7x

相关标签:
9条回答
  • 2020-12-02 09:07

    Thanks for all, I will try second variant by @Adam Bowen, take C# code for Sobel5x5, 7x7, 9x9... matrix generaion for this variant (maybe with bugs, if you find bug or can optimize code - write it there):

        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) or it copy there, where this metod - №2, @Paul R metod - №1. Now I am using last, becouse it give more smooth result and it's easy to generate kernels with this code.

    0 讨论(0)
  • 2020-12-02 09:10

    Here is a simple solution made with python 3 using numpy and the @Daniel answer.

    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
    

    It returns the kernel (5,5) like this:

    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]]
    

    If anyone know a better way to do that in python, please let me know. I'm a newbie yet ;)

    0 讨论(0)
  • 2020-12-02 09:15

    I quickly hacked an algorithm to generate a Sobel kernel of any odd size > 1, based on the examples given by @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;
                }
            }
        }
    

    Hope it helps.

    0 讨论(0)
  • 2020-12-02 09:21

    UPDATE 23-Apr-2018: it seems that the kernels defined in the link below are not true Sobel kernels (for 5x5 and above) - they may do a reasonable job of edge detection, but they should not be called Sobel kernels. See Daniel’s answer for a more accurate and comprehensive summary. (I will leave this answer here as (a) it is linked to from various places and (b) accepted answers can not easily be deleted.)

    Google seems to turn up plenty of results, e.g. http://rsbweb.nih.gov/nih-image/download/user-macros/slowsobel.macro suggests the following kernels for 3x3, 5x5, 7x7 and 9x9:

    3x3:

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

    5x5:

    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
    

    0 讨论(0)
  • 2020-12-02 09:21

    Other sources seem to give different definitions of the larger kernels. The Intel IPP library, for example, gives the 5x5 kernel as

    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
    

    Intuitively, this makes more sense to me because you're paying more attention to the elements closer to the centre. It also has a natural definition in terms of the 3x3 kernel which is easy to extend to generate larger kernels. That said, in my brief search I've found 3 different definitions of the 5x5 kernel - so I suspect that (as Paul says) the larger kernels are ad hoc, and so this is by no means the definitive answer.

    The 3x3 kernel is the outer product of a smoothing kernel and a gradient kernel, in Matlab this is something like

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

    the larger kernels can be defined by convolving the 3x3 kernel with another smoothing kernel

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

    you can repeat the process to get progressively larger kernels

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

    there are a lot of other ways of writing it, but I think this explains exactly what is happening best. Basically, you start off with a smoothing kernel in one direction and a finite differences estimate of the derivative in the other and then just apply smoothing until you get the kernel size you want.

    Because it's just a series of convolutions, all the nice properties hold, (commutativity, associativity and so forth) which might be useful for your implementation. For example, you can trivially separate the 5x5 kernel into its smoothing and derivative components:

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

    Note that in order to be a "proper" derivative estimator, the 3x3 Sobel should be scaled by a factor of 1/8:

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

    and each larger kernel needs to be scaled by an additional factor of 1/16 (because the smoothing kernels are not normalised):

    sob5x5 = 1/16 * conv2( [ 1 2 1 ]' * [1 2 1], sob3x3 )
    sob7x7 = 1/16 * conv2( [ 1 2 1 ]' * [1 2 1], sob5x5 )
    ...
    
    0 讨论(0)
  • 2020-12-02 09:27

    Sobel gradient filter generator

    (This answer refers to the analysis given by @Daniel, above.)

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

    This is an important result, and a better explanation than can be found in the original paper. It should be written up in Wikipedia, or somewhere, because it also seems superior to any other discussion of the issue available on the internet.

    However, it is not actually true that integer-valued representations are impractical for filters of size greater than 5*5, as claimed. Using 64-bit integers, Sobel filter sizes up to 15*15 can be exactly expressed.

    Here are the first four; the result should be divided by the "weight", so that the gradient of an image region such as the following, is normalized to a value of 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
    

    The Ruby program appended below, will calculate Sobel filters and corresponding weights of any size, although the integer-valued filters are not likely to be useful for sizes larger than 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}"
    
    0 讨论(0)
提交回复
热议问题