首页 > 其他分享 >29.边缘检测

29.边缘检测

时间:2023-04-11 14:13:58浏览次数:29  
标签:函数 阈值 检测 数据类型 29 像素 边缘 图像

1、边缘检测原理

  图像的边缘指的是图像中像素灰度值突然发生变化的区域,如果将图像的每一行像素和每一列像素都描述成一个关于灰度值的函数,那么图像的边缘对应在灰度值函数中是函数值突然变大的区域。函数值的变化趋势可以用函数的导数描述。当函数值突然变大时,导数也必然会变大,而函数值变化较为平缓区域,导数值也比较小,因此可以通过寻找导数值较大的区域去寻找函数中突然变化的区域,进而确定图像中的边缘位置。图5-27给出一张含有边缘的图像,图像每一行的像素灰度值变化可以用图中下方的曲线表示。

 

通过像素灰度值曲线可以看出图像边缘位于曲线变化最陡峭的区域,对灰度值曲线求取一阶导数可以得到图5-28中所示的曲线,通过曲线可以看出曲线的最大值区域就是图像中的边缘。

由于图像是离散的信号,我们可以用临近的两个像素差值来表示像素灰度值函数的导数:

图像的边缘有可能是由高像素值变为低像素值,也有可能是由低像素值变成高像素值,因此为了在图像中同时表示出这两种边缘信息,需要将计算的结果求取绝对值。

代码清单5-22 convertScaleAbs()函数函数原型
void cv::convertScaleAbs(InputArray  src,
                            OutputArray  dst,
                            double  alpha = 1,
                            double  beta = 0 
                            )
  • src:输入矩阵。
  • dst:计算绝对值后输入矩阵。
  • alpha:缩放因子,默认参数为只求取绝对值不进行缩放。
  • beta:在原始数据上添加的偏值,默认参数表示不增加偏值。

该函数可以求取矩阵中所有数据的绝对值。函数前两个参数分别为输入、输出矩阵,两个参数可以是相同的变量。函数第三个和第四个参数为对绝对值的缩放和原始数据上的偏移。函数的计算原理如式:

图像的边缘包含X方向的边缘和Y方向的边缘,因此分别求取两个方向的边缘后,对两个方向的边缘求取并集就是整幅图像的边缘,即将图像两个方向边缘结果相加得到整幅图像的边缘信息。为了验证这种滤波方式对于图像边缘提取的效果,在代码清单5-23中给出了利用filter2D()函数实现图像边缘检测的算法,检测的结果在图5-29中给出。需要说明的是,由于求取边缘的结果可能会有复数,不在原始图像的CV_8U的数据类型内,因此滤波后的图像数据类型不要用“-1”,而应该改为CV_16S。

代码清单5-23 myEdge.cpp图像边缘检测
#include <opencv2\opencv.hpp>
#include <iostream>

using namespace cv;
using namespace std;

int main()
{
  //创建边缘检测滤波器
  Mat kernel1 = (Mat_<float>(1, 2) << 1, -1);  //X方向边缘检测滤波器
  Mat kernel2 = (Mat_<float>(1, 3) << 1, 0, -1);  //X方向边缘检测滤波器
  Mat kernel3 = (Mat_<float>(3, 1) << 1, 0, -1);  //X方向边缘检测滤波器
  Mat kernelXY = (Mat_<float>(2, 2) << 1, 0, 0, -1);  //由左上到右下方向边缘检测滤波器
  Mat kernelYX = (Mat_<float>(2, 2) << 0, -1, 1, 0);  //由右上到左下方向边缘检测滤波器

  //读取图像,黑白图像边缘检测结果较为明显
  Mat img = imread("equalLena.png", IMREAD_ANYCOLOR);
  if (img.empty())
  {
    cout << "请确认图像文件名称是否正确" << endl;
    return -1;
  }
  Mat result1, result2, result3, result4, result5, result6;

  //检测图像边缘
  //以[1 -1]检测水平方向边缘
  filter2D(img, result1, CV_16S, kernel1);
  convertScaleAbs(result1, result1);

  //以[1 0 -1]检测水平方向边缘
  filter2D(img, result2, CV_16S, kernel2);
  convertScaleAbs(result2, result2);

  //以[1 0 -1]'检测由垂直方向边缘
  filter2D(img, result3, CV_16S, kernel3);
  convertScaleAbs(result3, result3);

  //整幅图像的边缘
  result6 = result2 + result3;
  //检测由左上到右下方向边缘
  filter2D(img, result4, CV_16S, kernelXY);
  convertScaleAbs(result4, result4);

  //检测由右上到左下方向边缘
  filter2D(img, result5, CV_16S, kernelYX);
  convertScaleAbs(result5, result5);

  //显示边缘检测结果
  imshow("result1", result1);
  imshow("result2", result2);
  imshow("result3", result3);
  imshow("result4", result4);
  imshow("result5", result5);
  imshow("result6", result6);
  waitKey(0);
  return 0;
}

2、Laplacian算子

  Laplacian算子具有各方向同性的特点,能够对任意方向的边缘进行提取,具有无方向性的优点,因此使用Laplacian算子提取边缘不需要分别检测X方向的边缘和Y方向的边缘,只需要一次边缘检测即可。Laplacian算子是一种二阶导数算子,对噪声比较敏感,因此常需要配合高斯滤波一起使用。

代码清单5-30 Laplacian()函数原型
1.void cv::Laplacian(InputArray  src,
2.                           OutputArray  dst,
3.int  ddepth,
4.int  ksize = 1,
5.double  scale = 1,
6.double  delta = 0,
7.int  borderType = BORDER_DEFAULT
8.                           )
  • src:输入原图像,可以是灰度图像或彩色图像。
  • dst:输出图像,与输入图像src具有相同的尺寸和通道数
  • ddepth:输出图像的数据类型(深度),根据输入图像的数据类型不同拥有不同的取值范围,具体的取值范围在表5-1给出,当赋值为-1时,输出图像的数据类型自动选择。
  • ksize:滤波器的大小,必须为正奇数。
  • scale:对导数计算结果进行缩放的缩放因子,默认系数为1,表示不进行缩放。
  • delta:偏值,在计算结果中加上偏值。
  • borderType:像素外推法选择标志,取值范围在表3-5中给出,默认参数为BORDER_DEFAULT,表示不包含边界值倒序填充。

  该函数利用Laplacian算子提取图像中的边缘信息,与Soble()函数相同,函数的前两个参数分别为输入图像和输出图像,第三个参数为输出图像的数据类型,这里需要注意由于提取边缘信息时有可能会出现负数,因此不要使用CV_8U数据类型的输出图像,否则会使得图像边缘提取不准确。函数第四个参数是滤波器尺寸的大小,必须是正奇数,当该参数的值大于1时,该函数通过Sobel算子计算出图像X方向和Y方向的二阶导数,将两个方向的导数求和得到Laplacian算子,其计算公式如式所示。

 

3、Canny算法

  Canny算法不容易受到噪声的影响,能够识别图像中的弱边缘和强边缘,并结合强弱边缘的位置关系,综和给出图像整体的边缘信息。Canny边缘检测算法是目前最优越的边缘检测算法之一,该方法的检测过程分为以下5个步骤:

  • Step1:使用高斯滤波平滑图像,减少图像中噪声。一般情况下使用式(5.23)所示的5×5的高斯滤波器。

  

  • Step2:计算图像中每个像素的梯度方向和幅值。首先通过Sobel算子分别检测图像X方向的边缘和Y方向的边缘,之后利用式(5.24)计算梯度的方向和幅值。

    为了简便,梯度方向常取值0°、45°、90°和135°这个四个角度之一。

  

  • Step3:应用非极大值抑制算法消除边缘检测带来的杂散响应。首先将当前像素的梯度强度与沿正负梯度方向上的两个像素进行比较, 如果当前像素的梯度强度与另外两个像素梯度强度相比最大,则该像素点保留为边缘点,否则该像素点将被抑制。

  • Step4:应用双阈值法划分强边缘和弱边缘。将边缘处的梯度值与两个阈值进行比较,如果某像素的梯度幅值小于较小的阈值,则会被去除掉;如果某像素的梯度幅值大于较小阈值但小于较大阈值,则将该像素标记为弱边缘;如果某像素的梯度幅值大于较大阈值,则将该像素标记为强边缘。

  • Step5:消除孤立的弱边缘。在弱边缘的8邻域范围寻找强边缘,如果8邻域内存在强边缘,则保留该弱边缘,否则将删除弱边缘,最终输出边缘检测结果。

代码清单5-32 Canny()函数原型
1.  void cv::Canny(InputArray  image,
2.                    OutputArray  edges,
3.                    double  threshold1,
4.                    double  threshold2,
5.                    int  apertureSize = 3,
6.                    bool  L2gradient = false 
7.                    )
  • image:输入图像,必须是CV_8U的单通道或者三通道图像。

  • edges:输出图像,与输入图像具有相同尺寸的单通道图像,且数据类型为CV_8U。

  • threshold1:第一个滞后阈值。

  • threshold2:第二个滞后阈值。

  • apertureSize:Sobel算子的直径。

  • L2gradient:计算图像梯度幅值方法的标志,幅值的两种计算方式如式(5.25)所示。

  

  该函数利用Canny算法提取图像中的边缘信息。第一个参数是需要提取边缘的输入图像,目前只支持数据类型为CV_8U的图像,输入图像可以是灰度图像或者彩色图像。第二个参数是边缘检测结果的输出图像,图像是数据类型为为CV_8U的单通道灰度图像。函数第三个和第四个参数是Canny算法中用于区分强边缘和弱边缘的两个阈值,两个参数不区分较大阈值和较小阈值,函数会自动比较区分两个阈值的大小,不过一般情况下,较大阈值与较小阈值的比值在2:1到3:1之间。函数最后一个参数是计算梯度幅值方法的选择标志,无特殊需求的情况下,使用默认值即可。

代码清单5-33 myCanny.cpp利用Canny算法提取图像边缘
1.  #include <opencv2\opencv.hpp>
2.  #include <iostream>
3.  
4.  using namespace cv;
5.  using namespace std;
6.  
7.  int main()
8.  {
9.    //读取图像,黑白图像边缘检测结果较为明显
10.    Mat img = imread("equalLena.png", IMREAD_ANYDEPTH);
11.    if (img.empty())
12.    {
13.      cout << "请确认图像文件名称是否正确" << endl;
14.      return -1;
15.    }
16.    Mat resultHigh, resultLow, resultG;
17.  
18.    //大阈值检测图像边缘
19.    Canny(img, resultHigh, 100, 200, 3);
20.  
21.    //小阈值检测图像边缘
22.    Canny(img, resultLow, 20, 40, 3);
23.  
24.    //高斯模糊后检测图像边缘
25.    GaussianBlur(img, resultG, Size(3, 3), 5);
26.    Canny(resultG, resultG, 100, 200, 3);
27.  
28.    //显示图像
29.    imshow("resultHigh", resultHigh);
30.    imshow("resultLow", resultLow);
31.    imshow("resultG", resultG);
32.    waitKey(0);
33.    return 0;
34.  }

 

标签:函数,阈值,检测,数据类型,29,像素,边缘,图像
From: https://www.cnblogs.com/okmai77xue/p/17305978.html

相关文章

  • js检测页面关闭事件
    window.onbeforeunload=onbeforeunload_handler;window.onunload=onunload_handler;//页面关闭之前触发,在onunload事件之前触发,可以禁止onunload事件的触发functiononbeforeunload_handler(){varwarning="确认退出?";debugger......
  • Divisors UVA - 294
    求区间[L,R]的整数中哪一个的正约数最多。  一个数因数分解后,它的约数Cnt=(a[j]+1)的乘积,j是每个因数的个数 #include<iostream>#include<cstring>#include<cmath>#include<algorithm>usingnamespacestd;constintM=1e5+30;#defineintlonglo......
  • Android 短视频和图片无读写权限TargetApi>=29解决方案
    一、背景        目前很多公司在适配API29,也就是targetSdkVersion=29的权限适配。不仅是权限的适配,还有政策的要求。目前就有很多大公司已收到工信部要求,不给读写权限:android.permission.WRITE_EXTERNAL_STORAGE和android.permission.READ_EXTERNAL_STORAGE      ......
  • 异常检测 | 迁移学习《Anomaly Detection in IR Images of PV Modules using Supervis
    论文信息论文标题:AnomalyDetectioninIRImagesofPVModulesusingSupervisedContrastiveLearning论文作者:AbhayRawat, IshaDua, SauravGupta, RahulTallamraju 论文来源:LukasBommes,MathisHoffmann,ClaudiaBuerhop-Lutz,TobiasPickel,JensHauch,Christ......
  • 3500/15 133292-01 到达时间预测解决方案阶段
    3500/15133292-01到达时间预测解决方案阶段今天,墨西哥的公共交通系统不具有提供信息来确定公共汽车的到达时间或者知道到达用户的公共汽车站的下一辆公共汽车上是否有空位的功能。这一信息与墨西哥城市密切相关,那里每天都有数百万用户需要移动。所提出的解决方案基于这样的事实......
  • 核酸检测机器人
    1.设计一个至少六自由度的机器人,至少包括--个伸缩关节,要求机器人工作空间能够满足左图工作空间的要求(自己定义机械臂安装位置要求能够满足抓取指定空间范围内操作)2.根据自己设计的机器人建立DH坐标系及齐次变换矩阵,并设置合理的关节转动极限,分析和绘制工作空间,关节必须要有合......
  • 透过Gartner最新报告,认识“超级边缘”
    当下,酝酿能量的超级边缘。最近,我们在谈视频化狂飙、谈AIGC颠覆、谈算力动能不足,很少谈及边缘。但“边缘”恰恰与这一切相关,且越发密不可分,它是未来技术发展的极大影响因子。 “到2025年,超过70%的组织将为其⾄少⼀个边缘计算系统,部署超⼤规模云边缘解决⽅案,并会结合其云部署......
  • Gartner最新报告,分析超大规模边缘解决方案
    当下,酝酿能量的超级边缘。最近,我们在谈视频化狂飙、谈AIGC颠覆、谈算力动能不足,很少谈及边缘。但“边缘”恰恰与这一切相关,且越发密不可分,它是未来技术发展的极大影响因子。“到2025年,超过70%的组织将为其⾄少⼀个边缘计算系统,部署超⼤规模云边缘解决⽅案,并会结合其云部署。这......
  • Qt音视频开发36-超时检测和自动重连的设计
    一、前言如果网络环境正常设备正常,视频监控系统一般都是按照正常运行下去,不会出现什么问题,但是实际情况会很不同,奇奇怪怪七七八八的问题都会出现,就比如网络出了问题都有很多情况(交换机故障、网线故障、带宽故障等),所以监控系统在运行过程中,还得做超时检测,超过规定的时间没有收到数......
  • SATA Mode:Optane without Raid导致lsblk检测不到磁盘
     本人是Win(WD512G)+Fedora(Lexar256G),在FedoraLinux中无法显示Windows的硬盘,但Fedora是自带NTFS驱动的,不存在文件系统不兼容。       (实际上影响不止于此,安装ubuntu会提示请关闭Raid功能,安装CentOS/deepin会完全检测不到任何硬盘,导致无法安装。本人当初无法解决,......