基于opencv的图像:边缘检测 (完整代码+详细教程)

给出“离散拉普拉斯算子”一般形式的数学推导

离散值的导数使用差分代替:

所以:

以(x, y)为中心点,在水平和垂直方向上应用拉普拉斯算子,滤波器(对应a=1的情况)为:

这个滤波器在90°方向上是各向同性的,但在45°方向上不是。而拉普拉斯算子具有旋转不变性,所以在

°方向上也应该具有各向同性,所以再在45°方向上应用拉普拉斯算子(对应a=0.5的情况):

给出LoG形式的数学推导,并给出一个5*5大小的LoG算子

同理:

常用的5*5滤波器模板为

使用公式13,带入并归一化后可以算出:

使用Prewitt,Sobel,Canny,FDoG算子处理三张以上图片,分析并比较结果。

结果图片展示与分析

图1

图2

图3

图4

图5

prewitt算子和sobel算子都是一阶导数算子,只对图像求一阶导数,返回梯度值,而非0-1边界,所以图像较为平滑,并且有明暗灰度的变化,越明显的边界,在图中越亮,也就是梯度值越大。相比较的话, sobel算子的表现力更好一些,因为考虑了局部性原则,对像素点附近的像素附加了更大的权重,所以能更好的还原图像局部细节,如图3的左眼,图1图2的鼻梁;而prewitt算子是平均滤波,所以图像的灰度更加平均,二者在速度上没有什么差异。

Canny算子和FDoG算子都是返回的0-1边界,但是不同之处在于Canny算子进行了非极大值抑制,边界是一个像素宽的,FDoG的边界是多像素。FDoG处理过程中会将不明确的边界方向按邻居的方向来补全,所以边界具有更好的连续性,如图4中脸的左下边界。并且FDoG对于文字的边界能够几乎完整的识别出来,但是Canny的文字边界则是模糊不可辨认的,可能是由于FDoG识别图像的切线而文字较为连续。但是图4中FDoG对于山的边缘识别效果 差,山顶、边缘的山脉边界和雪线都没有识别出来,Sobel识别效果 好,山顶梯度值很高,雪线的边界明显。FDoG还倾向于把边缘连接成线,如图4右下角的草丛。图5中FDoG效果 差,将岩石的纹理全都识别为了边界,并且导致左下角草丛和山的边界不可辨认,而Canny算子效果稍好一些,Prewitt和Sobel算子效果 好。

两者也都具有一些问题,如把纹理识别为边界,如图3卫衣的帽子,这部分在Prewitt和Sobel中也具有较高的梯度值,应该是由于前面较暗,后面较亮,所以对比强烈被误认为了边界。有的边界也没有被识别出来,如图2的衣服图5后面的山脉,同样在Prewitt和Sobel算子中这个边界也并不明显,因为皮肤和白衣服的颜色、雪山和天空的颜色非常相近,所以这个边界的梯度值较小。在处理过程中,Canny的速度要慢得多,因为步骤繁琐,并且是对每个像素逐点计算的。

分析参数的影响

对FDoG的参数进行修改:

修改循环次数和有无高斯滤波

直观来看,循环次数对结果的影响并不大。仔细观察的话,可以发现随着循环次数的增多,图像识别出了更多细微的边缘。

个高斯值的差也会随之变化

也就是上式中的f(t)变化,由于没有进行非极大值抑制,边界像素点的数目就会增多,所以导致结果的线条粗细发生变化。

修改的值,论文中默认为3.0

的变化对结果的影响也不明显,主要是边界线条会有更多的延伸, 出现在最后一次积分中:

论文中也解释到控制"flow kernel"的拉长程度,所以 增大时,G会减小,然后在 后的阈值判断中,小于阈值的像素点被认为是边界,这导致了会有更多的像素点成为边界,也就表现为线条的拉伸。

-score评分与其他测试

使用论文中的两个测试用例如下,效果并不好,并且通过修改给定的那几个参数并没有显著提升。应该是在构建ETF时就没有构建好圆形的切线方向,后续的调参都是在ETF的基础上进行的,所以并没有改善结果。

F-score评分部分是与张庆阳同学合作完成的

为了测试这几种算子的分类效果,使用了伯克利大学提供的这个数据集可以用来进行图像分割和图像边缘检测,数据集中的ground truth是人工标记的。这里将数据集中给定的ground truth标注信息绘制成为黑白边界图像,如下所示。

然后对数据集中的图片应用四种边缘检测算子,与ground truth的结果进行对比,计算准确率、精准率、召回率和F-score。

准确率

精准率

召回率

F-score

Prewitt

0.883

0.914

0.967

0.940

Sobel

0.909

0.913

0.995

0.953

Canny

0.769

0.916

0.840

0.877

FDoG

0.754

0.926

0.822

0.871

FDoG+NMS

0.894

0.914

0.979

0.945

加入非极大值抑制之后,FDoG的F1-score和准确率都有了较大提升,这是因为ground truth中的边界是单像素宽的,如果不加非极大值抑制FDoG的结果会过宽,影响正确率。准确率不够高的另一个原因是 ground truth中只有真正的物体边界曲线而且有一些边界并没有标注(如上图上侧的横向边界),而不论是什么算子都会有一些纹理被识别成边界,造成误差。

对FDoG算法添加非极大值抑制

结果如下:

为了将结果边界线条变为1像素宽,需要在上面(16)(17)式之间进行非极大值抑制(NMS),也就是对图(d) 中-T~T方向上积分的结果进行非极大值抑制,使得原本作为边界的多个像素点中只保留一个作为边界,然后正常的进行-S~S方向上的积分得到结果。

在fdog.cpp中的GetFDoG()函数里添加NMS()函数:

NMS()实现的主要代码如下所示:

        max = dog[i][j]; //记录当前点的DoG值
        for (s = -half;s <= half;s++) { //对[-s, s]的点进
行比较
            x = d_x + vn[0] * s;
            y = d_y + vn[1] * s;

            if (x > (double)image_x - 1 || x<0.0 || y>(double)image_y - 1 ||
y < 0.0)
                continue;
            x1 = round(x);
            if (x1 < 0) x1 = 0; if (x1 > image_x - 1) x1 = image_x - 1;
            y1 = round(y);
            if (y1 < 0) y1 = 0; if (y1 > image_y - 1) y1 = image_y - 1;
            val = dog[x1][y1];
            
            if (val < max) {
                sign = 0;
                break;
            }
        }
        if (sign == 0) { //作为局部极小值时才会被保留,否则
255(白色)
            dog[i][j] = 255;
        }

虽然叫作非极大值抑制,但是从原本的实现中可以知道, 终结果小于阈值时才会保留为边界,否则赋值为白色(非边界)。所以实际上是需要判断当前值是否为局部 小值,如果是的话保留,否则赋一个比较大的值,让这个像素点作为白色的背景。

在实现完非极大值抑制功能后,为了得到上面比较好的结果,需要进行调参。在给定的几个参数中sm3 的效果较为明显,当把sm3调小时结果会变好,否则边界曲线会断断续续,如下所示:

思考与讨论

CS专业的学生相对于其他专业来说,对于底层知识掌握的更多、更扎实。所谓的“底层”也就是操作系统、汇编、编译原理、体系结构等知识,因为相对来说,其他专业的学生不会主动去学习这些知识。并且这部分知识比较“硬核”,在专业课里也算是比较难的部分。通常其他专业转CS会从见效快的部分学习,如Python,java,web开发等等。计算机科班出身的学生可能也会更有编程的思想,也就是会考虑如何通过写代码来实现某项计算,比如求三角函数就可以用级数展开来模拟。

CS专业对于其他专业的劣势在于他们具有自己的领域背景知识,如生物信息学需要生物知识、许多加密算法需要数学知识、银行金融类的开发也需要相应领域知识。使用编程语言本身其他专业的未必会比计科专业的差,所以在指定领域的开发中其他专业学生甚至有可能更有竞争力。尤其是一些培训班出来的学生,对某种语言的掌握程度可能会比很多计科本科生要好。这当然也是他们的劣势,因为他们大部分也是“追热点”学习, 近就有各行业的人学习人工智能,当这个热点过去后,他们可能掌握的知识就不足以应对新的要求,但是计科的学生对计算机方向的知识有比较好的学习能力,可以学习新的热点方向。

完整的代码:

https://download.csdn.net/download/qq_38735017/87380765?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522167359799616800182729355%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fdownload.%2522%257D&request_id=167359799616800182729355&biz_id=1&utm_medium=distribute.pc_search_result.none-task-download-2~download~first_rank_ecpm_v1~rank_v31_ecpm-1-87380765-null-null.pc_v2_rank_dl_default&utm_term=%E5%9F%BA%E4%BA%8Eopencv%E7%9A%84%E6%95%B0%E5%AD%97%E5%9B%BE%E5%83%8F%E5%A4%84%E7%90%86%EF%BC%9A%E8%BE%B9%E7%BC%98%E6%A3%80%E6%B5%8B%20%EF%BC%88%E5%AE%8C%E6%95%B4%E4%BB%A3%E7%A0%81%2B%E8%AF%A6%E7%BB%86%E6%95%99%E7%A8%8B%EF%BC%89&spm=1018.2226.3001.4451.1

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
OpenCV是一个开源的计算机视觉库,提供了丰富的图像处理和计算机视觉算法。其中,图像边缘检测OpenCV中的一个重要功能,用于检测图像中物体的边缘。 在OpenCV中,常用的图像边缘检测算法有以下几种: 1. Canny边缘检测算法:Canny算法是一种经典的边缘检测算法,它通过多阶段的处理来提取图像中的边缘。首先,对图像进行高斯滤波以降低噪声;然后,计算图像的梯度,并根据梯度的方向和幅值来确定边缘;最后,使用非极大值抑制和双阈值处理来提取最终的边缘。 2. Sobel算子:Sobel算子是一种基于梯度的边缘检测算子,它通过计算图像一阶或二阶导数来检测边缘。Sobel算子可以分别计算图像在水平和垂直方向上的梯度,并将两个方向上的梯度合并得到最终的边缘。 3. Laplacian算子:Laplacian算子是一种基于二阶导数的边缘检测算子,它可以检测出图像中的高频变化区域,即边缘。Laplacian算子图像进行二阶导数计算,并通过零交叉点来确定边缘。 使用OpenCV进行图像边缘检测的步骤如下: 1. 读取图像使用OpenCV的函数读取图像文件。 2. 灰度化:将彩色图像转换为灰度图像,可以使用OpenCV的函数将图像转换为灰度模式。 3. 滤波处理:对灰度图像进行滤波处理,常用的滤波方法有高斯滤波。 4. 边缘检测使用OpenCV提供的边缘检测函数,如Canny、Sobel或Laplacian等。 5. 显示结果:将检测到的边缘结果显示出来,可以使用OpenCV的函数将图像显示在窗口中。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

程序员奇奇

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值