sobel算子实现原理和c++实现sobel()检测边缘函数

一、sobel算子原理

Sobel算子的原理是以一维高斯算子的二项式近似为基础的,也就是对于二项式展开式的系数而言,可以作为非归一化的高斯平滑算子。

又因为sobel算子的原理是先对图像进行非归一化的高斯平滑,然后再进行差分,所以3阶的Sobel边缘检测算子可以直接通过矩阵运算得到:
在这里插入图片描述

其中第一个式子是n等于2时展开式的高斯平滑算子,也就是二项式展开式的系数,第二个式子表示差分。
最后得到的结果就是3阶sobel边缘检测算子。

至于n阶的sobel算子,是由窗口为n的非归一化的高斯算子(即n-1阶的二项式展开式的系数)和窗口为n的差分算子通过full卷积得到的,现在只要求出窗口为n的差分算子就可以了。

他的构建方法为在n-2阶的二项式展开式的系数两侧补上一个零,然后利用后向差分得到。
举个栗子:
构建4阶的差分算子,令二项式指数为 4-2 = 2,然后计算展开式的系数,即图左,两侧补零后,即图右
在这里插入图片描述
接着后向差分:
在这里插入图片描述
差分后的结果是:
在这里插入图片描述
该结构即为4阶的差分算子。
然后和4阶的平滑算子full卷积,即可得到 4 * 4 的sobel。

通过上述描述,可以清楚的发现,高阶的sobel边缘检测算子是可以分离的,也就是水平方向上的卷积和垂直方向上的卷积可以分别计算出来。

二、sobel算子代码的实现思路

了解了sobel算子的原理,目的是为了充分了解sobel边缘检测算子的原理之后,通过codeblocks来编码实现sobel边缘检测算子。
梳理以上sobel边缘检测算子所需要的功能:
第一个就是阶乘函数,因为高斯平滑算子的近似项为二项式展开式;
接着利用阶乘函数表示出来二项式展开式来定义平滑算子函数;
又因为差分算子可以利用平滑算子得到,所以可以利用平滑算子函数获得平滑算子之后对Mat直接操作来构建差分算子,得到差分算子函数;
现在已经得到了平滑算子以及差分算子,所以可以定义sobel函数来完成图像矩阵与sobel核的卷积。

这里的卷积运算,指的是二维离散卷积,属于基于两个矩阵的一种计算的方式。

假设有两个矩阵I和K,第一步是先将K逆时针翻转180度得到Ki,第二步,Ki沿着I按照先从行开始再进行列的顺序移动,每移动到一个固定的位置的时候,对应的位置就相乘得到一个乘积,然后整体求和,在整个移动的过程中,将对应位置积的和依次存入一个新的矩阵C中,该矩阵就是I和K full卷积的结果,其中K称为卷积核,或者卷积掩码。对于sobel函数的卷积运算,不仅仅属于full卷积,而且还属于same卷积。same卷积可以得到和原图像的高、宽相等的卷积结果,如何做到的呢,通常是在计算的过程中给Ki指定一个特殊点,然后将该点循环的移动到图像矩阵的(a,b)处(这里需满足a,b属于原图像的高、宽范围内的数据),接着依然是对应位置的元素逐个相乘,最后对所有的乘积解都求和来作为图像矩阵在(a,b)处的输出数据。

关于卷积运算,在OpenCV中并没有直接给出相关的函数,不过可以利用两个函数来间接实现。
第一步,利用flip()函数将输入的卷积核逆时针翻转180度。
第二步,利用filter2D()函数完成接下来的矩阵移动操作。

所以为了实现sobel函数不仅需要平滑算子函数以及差分算子函数,还需要卷积运算函数(将flip()函数和filter2D()函数封装后)。又因为sobel边缘检测算子属于可分离的卷积运算,所以还需要分别定义两个离散二维卷积,一个是先进行垂直方向上的绝技,一个是先进行水平方向上的卷积。
综上可知,需要定义四个主要的函数来完成sobel算子检测边缘的任务,另外还需要定义四个辅助的卷积函数一同完成任务

三、 sobel算子的实现代码

#主要函数的核心代码如下:

//得到平滑算子的函数
Mat getPascalSmooth(int n)
{
    Mat pascalSmooth = Mat::zeros(Size(n,1),CV_32FC1);
    for(int i = 0; i < n; i++)
    {
        //利用二项式展开式公式
        pascalSmooth.at<float>(0,i) = factorial(n-1) / (factorial(i) * factorial(n-1-i));
    }
    return pascalSmooth;
}

//得到差分算子的函数
Mat getPascalDiff(int n)
{
    Mat pascalDiff = Mat::zeros(Size(n,1), CV_32FC1);
    //差分算子的前身是n-2阶的二项式展开式,与平滑算子相差1
    Mat pascalSmooth_previous = getPascalSmooth(n - 1);
    //利用差分算子的获得过程,改变Mat
    for (int i = 0; i < n; i++)
    {
        if(i == 0)
            //补0后反向差分一定是1
            pascalDiff.at<float>(0,i) = 1;
        else if( i == n - 1)
        //补0后反向差分一定是-1
            pascalDiff.at<float>(0, i) = -1;
        else
            //实现反向差分
            pascalDiff.at<float>(0, i) = 
pascalSmooth_previous.at<float>(0, i) -
            pascalSmooth_previous.at<float>(0, i-1);
    }
    return pascalDiff;
}


//卷积运算
void conv2D(InputArray src, InputArray kernel,OutputArray dst, int ddepth,
            Point anchor = Point(-1,-1), int borderType = BORDER_DEFAULT )
{
    //卷积运算的第一步:卷积核逆时针翻转180度
    Mat kernelFlip;
    flip(kernel, kernelFlip, -1);
    //卷积运算第二步
    filter2D(src, dst , ddepth, kernelFlip, anchor, 0.0, borderType);

}


//可分离的离散二维卷积,先进行垂直方向上的卷积,然后进行水平方向上的卷积
void sepConv2D_Y_X(InputArray src, OutputArray src_kerY_kerX, int ddepth,
                   InputArray kernelY, InputArray kernelX, Point anchor = Point(-1,-1),
                   int borderType = BORDER_DEFAULT)
                   {
                       //输入矩阵与垂直方向上的卷积核的卷积
                       Mat src_kerY;
                       conv2D(src, kernelY, src_kerY, ddepth, anchor, borderType);
                       //上面得到的卷积结果,接着和水平方向上的卷积核卷积
                       conv2D(src, kernelX, src_kerY_kerX, ddepth, anchor, borderType);

                   }


//可分离的离散二维卷积,先进行水平方向上的卷积,然后进行垂直方向上的卷积
void sepConv2D_X_Y(InputArray src, OutputArray src_kerX_kerY, int ddepth,
                   InputArray kernelX, InputArray kernelY, Point anchor = Point(-1,-1),
                   int borderType = BORDER_DEFAULT)
                   {
                       //输入矩阵与水平方向上的卷积核的卷积
                       Mat src_kerX;
                       conv2D(src, kernelX, src_kerX, ddepth, anchor, borderType);
                       //上面得到的卷积结果,接着和垂直方向上的卷积核卷积
                       conv2D(src, kernelY, src_kerX_kerY, ddepth, anchor, borderType);

                   }



//定义sobel函数,完成图像矩阵与sobel核的卷积
Mat sobel(Mat image, int x_flag, int y_flag, int winSize, int borderType)
{	
    //sobel 卷积核的窗口大小为大于3 的奇数
    CV_Assert(winSize >= 3 && winSize % 2 == 1);
    //利用函数得到平滑算子
    Mat pascalSmooth = getPascalSmooth(winSize);
    //利用函数得到差分算子
    Mat pascalDiff = getPascalDiff(winSize);
    Mat image_con_sobel;
    //当 x_flag != 0时,返回图像与水平方向上的sobel核的卷积
    if( x_flag != 0)
    {
        //根据可分离卷积核的性质
        //先进行一维垂直方向上的平滑,在进行一维水平方向上的差分
        sepConv2D_Y_X(image, image_con_sobel, CV_32FC1, 
pascalSmooth.t(),pascalDiff, Point(-1, -1), borderType);
    }
    //当 x_flag == 0 且 y_flag != 0 时,返回图像与垂直方向上的sobel核的卷积
    if(x_flag == 0 && y_flag != 0 )
    {
        //根据可分离卷积核的性质
        //先进行一维水平方向上的平滑,在进行一维垂直方向上的差分
        sepConv2D_X_Y(image, image_con_sobel, CV_32FC1, 
pascalSmooth,pascalDiff.t(), Point(-1,-1), borderType);
    }
    return image_con_sobel;
}
四、sobel()的使用

sobel()函数的使用效果如下:

原图
原图

效果图
效果图

以上内容参考:
《OpenCV算法精解》

附加

OpenCV中的sobel()函数

Sobel(src,dst,ddepth,dx,dy,ksize=3,scale=1,delta=0,borderType=BORDER_DEFAULT)

实现了边缘检测的功能。
其中
src表示输入矩阵;
dst表示输出矩阵,即src与sobel核得到的卷积;
ddepth表示输出矩阵的数据类型;
dx,dy表示src与差分方向为水平方向上的Sobel核卷积(dx不为0)以及src与差分方向为垂直方向上的Sobel核卷积(dx为0,dy不为0);
ksize表示sobel核的尺寸,值一般取奇数;
scale表示比例系数,呈现的比例;
delta表示平移系数;
borderType表示边界扩充的类型,默认值是ORDER_DEFAULT。

  • 1
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
【资源说明】 基于C++Opencv图像处理函数的静态车道线检测项目源码+数据+报告.zip 实现** ### **2.1 基本思路: 去噪,边缘提取,车道检测** 但在刚刚开始的时候无从下手,因为选择太多了。 比如说去噪音可以高斯滤波,均值滤波,中值滤波。 边缘提取你又可以拉普拉斯锐化,sobel算子,Robert算子,scharr算子,candy检测。 于是我干脆把这些实现一个遍,再慢慢选择好了。 于是自实现了以下算法: 1. 彩色图像转灰度 2. 图像翻转,获得负片 3. 线性灰度转换 4. 对数灰度转换 5. 指数灰度转换 6. 方框滤波 7. 均值滤波 8. 高斯滤波 9. 中值滤波 10. 最值滤波 11. 拉普拉斯锐化 12. 漫水填充 13. 阈值分割 14. 自适应阈值分割 15. candy边缘检测 16. scharr边缘检测 17. sobel边缘检测 18. Robert边缘检测 19. 霍夫直线检测 20. 直方图均衡化 **所有算法源码均定义位于minicv.h下,最下面有接口参数说明;所有实现位于minicv.cpp下,关键步骤有注释** 【备注】 1.项目代码均经过功能验证ok,确保稳定可靠运行。欢迎下载使用体验! 2.主要针对各个计算机相关专业,包括计算机科学、信息安全、数据科学与大数据技术、人工智能、通信、物联网等领域的在校学生、专业教师、企业员工。 3.项目具有丰富的拓展空间,不仅可作为入门进阶,也可直接作为毕设、课程设计、大作业、初期项目立项演示等用途。 4.当然也鼓励大家基于此进行二次开发。在使用过程中,如有问题或建议,请及时沟通。 5.期待你能在项目中找到乐趣和灵感,也欢迎你的分享和反馈!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值