sobel算子原理及opencv源码实现

sobel算子原理及opencv源码实现

简要描述

sobel算子主要用于获得数字图像的一阶梯度,常见的应用和物理意义是边缘检测。

原理

算子使用两个33的矩阵(图1)算子使用两个33的矩阵(图1)去和原始图片作卷积,分别得到横向G(x)和纵向G(y)的梯度值,如果梯度值大于某一个阈值,则认为该点为边缘点

                  sobel kernel
                           图1:卷积矩阵

        sobel convolved
                            图2:卷积运算  
                
事实上卷积矩阵也可以由两个一维矩阵卷积而成,在opencv源码中就是用两个一维矩阵卷积生成一个卷积矩阵:

                  sobel two convolved
            
                     图3:由两个一维矩阵卷积生成的矩阵

static void getSobelKernels( OutputArray _kx, OutputArray _ky,
                         int dx, int dy, int _ksize, bool 	normalize, int ktype )
{
int i, j, ksizeX = _ksize, ksizeY = _ksize;
if( ksizeX == 1 && dx > 0 )
    ksizeX = 3;
if( ksizeY == 1 && dy > 0 )
    ksizeY = 3;

CV_Assert( ktype == CV_32F || ktype == CV_64F );

_kx.create(ksizeX, 1, ktype, -1, true);
_ky.create(ksizeY, 1, ktype, -1, true);
Mat kx = _kx.getMat();
Mat ky = _ky.getMat();

if( _ksize % 2 == 0 || _ksize > 31 )
    CV_Error( CV_StsOutOfRange, "The kernel size must be odd and not larger than 31" );
std::vector<int> kerI(std::max(ksizeX, ksizeY) + 1);

CV_Assert( dx >= 0 && dy >= 0 && dx+dy > 0 );

for( int k = 0; k < 2; k++ )
{
    Mat* kernel = k == 0 ? &kx : &ky;
    int order = k == 0 ? dx : dy;
    int ksize = k == 0 ? ksizeX : ksizeY;

    CV_Assert( ksize > order );

    if( ksize == 1 )
        kerI[0] = 1;
    else if( ksize == 3 )
    {
        if( order == 0 )
            kerI[0] = 1, kerI[1] = 2, kerI[2] = 1;
        else if( order == 1 )
            kerI[0] = -1, kerI[1] = 0, kerI[2] = 1;
        else
            kerI[0] = 1, kerI[1] = -2, kerI[2] = 1;
    }
    else
    {
        int oldval, newval;
        kerI[0] = 1;
        for( i = 0; i < ksize; i++ )
            kerI[i+1] = 0;

        for( i = 0; i < ksize - order - 1; i++ )
        {
            oldval = kerI[0];
            for( j = 1; j <= ksize; j++ )
            {
                newval = kerI[j]+kerI[j-1];
                kerI[j-1] = oldval;
                oldval = newval;
            }
        }

        for( i = 0; i < order; i++ )
        {
            oldval = -kerI[0];
            for( j = 1; j <= ksize; j++ )
            {
                newval = kerI[j-1] - kerI[j];
                kerI[j-1] = oldval;
                oldval = newval;
            }
        }
    }
    Mat temp(kernel->rows, kernel->cols, CV_32S, &kerI[0]);
    double scale = !normalize ? 1. : 1./(1 << (ksize-order-1));
    temp.convertTo(*kernel, ktype, scale);
}
}

}

从opencv源码可以看出sobel的卷积矩阵在ksize==3时分别由[1,2,1]和[-1,0,1]生成。
图像的梯度值由以下公式计算而来:
             sobel cacula

一般会用近似计算公式:

             sobel nearcacul

对于原始图像4,p5的梯度值于:

             sobel near

                   sobel pic5

                     图5 原始图像

原文地址:https://www.cnblogs.com/panxiaochun/p/5630819.html