【OpenCV学习笔记】之边缘检测

一、边缘检测
边缘(edge)是指图像局部强度变化最显著的部分。主要存在于目标与目标、目标与背景、区域与区域(包括不同色彩)之间,是图像分割、纹理特征和形状特征等图像分析的重要基础。
图像强度的显著变化可分为:

  • 阶跃变化函数,即图像强度在不连续处的两边的像素灰度值有着显著的差异;
  • 线条(屋顶)变化函数,即图像强度突然从一个值变化到另一个值,保持一较小行程后又回到原来的值。
图像的边缘有方向和幅度两个属性,沿边缘方向像素变化平缓,垂直于边缘方向像素变化剧烈.边缘上的这种变化可以用微分算子检测出来,通常用一阶或二阶导数来检测边缘。
【OpenCV学习笔记】之边缘检测
文章图片

(a)(b)分别是阶跃函数和屋顶函数的二维图像;(c)(d)是阶跃和屋顶函数的函数图象;
【OpenCV学习笔记】之边缘检测
文章图片

(e)(f)对应一阶倒数;(g)(h)是二阶倒数。
二、梯度算子 Gradient operators
函数f(x,y)在(x,y)处的梯度为一个向量:
【OpenCV学习笔记】之边缘检测
文章图片

计算这个向量的大小为:
【OpenCV学习笔记】之边缘检测
文章图片

近似为:
【OpenCV学习笔记】之边缘检测
文章图片

梯度的方向角为:
【OpenCV学习笔记】之边缘检测
文章图片

常见的sobel,拉普拉斯、scharr算子在前面记录过了:
这里重点分析canny算子;(一般的边缘检测的过程:滤波-增强-检测-定位)
三、Canny边缘检测的原理:
Canny算子与Marr(LoG)边缘检测方法类似(Marr大爷号称计算机视觉之父),也属于是先平滑后求导数的方法。John Canny研究了最优边缘检测方法所需的特性,给出了评价边缘检测性能优劣的三个指标:
1好的信噪比,即将非边缘点判定为边缘点的概率要低,将边缘点判为非边缘点的概率要低;
2 高的定位性能,即检测出的边缘点要尽可能在实际边缘的中心;
3 对单一边缘仅有唯一响应,即单个边缘产生多个响应的概率要低,并且虚假响应边缘应该得到最大抑制。
Canny算法就是基于满足这3个指标的最优解实现的,在对图像中物体边缘敏感性的同时,也可以抑制或消除噪声的影响。
Canny算子求边缘点具体算法步骤如下:
1. 用高斯滤波器平滑图像.
2. 用一阶偏导有限差分计算梯度幅值和方向
3. 对梯度幅值进行非极大值抑制
4. 用双阈值算法检测和连接边缘.
(1)灰度化和去噪
Canny算法通常处理的都是灰度图像,因此先把图像转变为灰度图像。
高斯滤波是一种线性平滑滤波,适用于消除高斯噪声,特别是对抑制或消除服从正态分布的噪声非常有效。滤波可以消除或降低图像中噪声的影响,使用高斯滤波器主要是基于在滤波降噪的同时也可以最大限度保留边缘信息的考虑。
(2)用一阶偏导的有限差分来计算梯度的幅值和方向。
高斯模糊的目的主要为了整体降低图像噪声,目的是为了更准确计算图像梯度及边缘计算梯度值和方向,幅值。计算图像梯度可以选择算子有Robot算子、Sobel算子、Prewitt算子等。
【【OpenCV学习笔记】之边缘检测】图像灰度值的梯度一般使用一阶有限差分来进行近似,这样就可以得图像在x和y方向上偏导数的两个矩阵。
【OpenCV学习笔记】之边缘检测
文章图片

这里采用更加简单明了的2x2的算子,其数学表达如下:
【OpenCV学习笔记】之边缘检测
文章图片

(3)对梯度幅值进行非极大值抑制。
非极大值抑制是进行边缘检测的一个重要步骤,通俗意义上是指寻找像素点局部最大值。沿着梯度方向,比较它前面和后面的梯度值进行了。
【OpenCV学习笔记】之边缘检测
文章图片

【OpenCV学习笔记】之边缘检测
文章图片

(4)用双阈值算法检测和连接边缘。
非最大信号抑制以后,输出的幅值如果直接显示结果可能会少量的非边缘像素被包含到结果中,所以要通过选取阈值进行取舍,传统的基于一个阈值的方法如果选择的阈值较小起不到过滤非边缘的作用,如果选择的阈值过大容易丢失真正的图像边缘,Canny提出基于双阈值(Fuzzy threshold)方法很好的实现了边缘选取,在实际应用中双阈值还有边缘连接的作用。双阈值选择与边缘连接方法通过假设两个阈值其中一个为高阈值TH另外一个为低阈值TL则有,
a.对于任意边缘像素低于TL的则丢弃
b.对于任意边缘像素高于TH的则保留
c.对于任意边缘像素值在TL与TH之间的,如果能通过边缘连接到一个像素大于TH而且边缘所有像素大于最小阈值TL的则保留,否则丢弃。
Opencv里面API介绍:
Canny算法介绍 – 五步:
  1. 高斯模糊 - GaussianBlur
  2. 灰度转换 - cvtColor
  3. 计算梯度 – Sobel/Scharr
  4. 非最大信号抑制
  5. 高低阈值输出二值图像
C++ voidCanny( InputArray src, // 8-bit的输入图像 OutputArray edges,// 输出边缘图像, 一般都是二值图像,背景是黑色 double threshold1,// 低阈值,常取高阈值的1/2或者1/3 double threshold2,// 高阈值 int aptertureSize,// Soble算子的size,通常3x3,取值3 bool L2gradient // 选择 true表示是L2来归一化,否则用L1归一化)

示例程序:
//canny边缘检测,canny算法: //1、高斯模糊 - GaussianBlur //2、灰度转换 - cvtColor //3、计算梯度 – Sobel / Scharr //4、非最大信号抑制 //5、高低阈值输出二值图像#include "stdafx.h" #include #include #includeusing namespace cv; int T1_value = https://www.it610.com/article/40; int max_value = 255; Mat src, dst, gray_src, dst1, dst2; const char*result ="canny detection image"; void canny_demo(int, void*); int main(int argc, char*argv) { src = https://www.it610.com/article/imread("C:\\Users\\59235\\Desktop\\imag\\girl1.jpg"); if (!src.data) { printf("could not load image...\n"); return -1; } char input[] = "input image"; namedWindow(input, CV_WINDOW_AUTOSIZE); namedWindow(result, CV_WINDOW_AUTOSIZE); imshow(input, src); GaussianBlur(src, dst, Size(3, 3), 0, 0); //高斯滤波 cvtColor(dst, gray_src, CV_BGR2GRAY); //转换为灰度图像 createTrackbar("threshold value", result, &T1_value, max_value, canny_demo); canny_demo(0, 0); waitKey(0); return 0; }void canny_demo(int, void*) { //canny算法API Canny(gray_src, dst1, T1_value, 2 * T1_value, 3, false); //InputArray src, // 8-bit的输入图像;OutputArray edges,// 输出边缘图像, 一般都是二值图像,背景是黑色; double threshold1,// 低阈值,常取高阈值的1/2或者1/3 ;double threshold2,// 高阈值;int aptertureSize,// Soble算子的size,通常3x3,取值3;bool L2gradient // 选择 true表示是L2来归一化,否则用L1归一化 imshow(result, ~dst1); dst2.create(src.size(), src.type()); //使用遮罩层,只有非零的元素才被copy的模板中 dst2 = Scalar::all(0); src.copyTo(dst2, dst1); imshow("添加颜色的result", dst2); }

效果图:
【OpenCV学习笔记】之边缘检测
文章图片

(canny算子检测边缘结果)
【OpenCV学习笔记】之边缘检测
文章图片

分析:由结果图看出,通过canny算子检测得到的图像边缘确实效果很好,基本上能过很好的体现图像的边缘信息。

    推荐阅读