canny对边缘进行检测,有三个原则:
1、信噪比原则:以低的错误率检测边缘,也即意味着需要尽可能准确的捕获图像中尽可能多的边缘。
2、定位精度原则:检测到的边缘应精确定位在真实边缘的中心。
3、单位缘响应原则:图像中给定的边缘应只被标记一次,并且在可能的情况下,图像的噪声不应产生假的边缘。
基本思想:首先对图像进行高斯滤波平滑图像,然后采用非极值抑制技术进行处理得到最后的边缘图像。
实现步骤:
1、高斯平滑滤波。为了去除噪声
2、计算梯度强度和方向。边缘的最重要的特征是灰度值剧烈变化,如果把灰度值看成二元函数值,那么灰度值的变化可以用二元函数的”导数“(或者称为梯度)来描述。由于图像是离散数据,导数可以用差分值来表示,差分在实际工程中就是灰度差,说人话就是两个像素的差值。一个像素点有8邻域,那么分上下左右斜对角,因此Canny算法使用四个算子来检测图像中的水平、垂直和对角边缘。算子是以图像卷积的形式来计算梯度,比如Roberts,Prewitt,Sobel等,这里选用Sobel算子来计算二维图像在x轴和y轴的差分值
3、非极大值抑制。sobel算子检测出来的边缘太粗了,我们需要抑制那些梯度不够大的像素点,只保留最大的梯度,从而达到瘦边的目的。这些梯度不够大的像素点很可能是某一条边缘的过渡点。按照高数上二维函数的极大值的定义,即对点(x0,y0)的某个邻域内所有(x,y)都有f(x,y)≤(f(x0,y0),则称f在(x0,y0)具有一个极大值,极大值为f(x0,y0)。简单方案是判断一个像素点的8邻域与中心像素谁更大,但这很容易筛选出噪声,因此我们需要用梯度和梯度方向来辅助确定。
4、用双阈值算法检测和连接边缘。双阈值法非常简单,我们假设两类边缘:经过非极大值抑制之后的边缘点中,梯度值超过T1的称为强边缘,梯度值小于T1大于T2的称为弱边缘,梯度小于T2的不是边缘。可以肯定的是,强边缘必然是边缘点,因此必须将T1设置的足够高,以要求像素点的梯度值足够大(变化足够剧烈),而弱边缘可能是边缘,也可能是噪声,如何判断呢?当弱边缘的周围8邻域有强边缘点存在时,就将该弱边缘点变成强边缘点,以此来实现对强边缘的补充。实际中人们发现T1:T2=2:1的比例效果比较好,其中T1可以人为指定,也可以设计算法来自适应的指定,比如定义梯度直方图的前30%的分界线为T1,我实现的是人为指定阈值。检查8邻域的方法叫边缘滞后跟踪,连接边缘的办法还有区域生长法等等。
//---------------------------------【头文件、命名空间包含部分】----------------------------
// 描述:包含程序所使用的头文件和命名空间
//------------------------------------------------------------------------------------------------
using namespace cv;
//-----------------------------------【main( )函数】-------------------------------------------
// 描述:控制台应用程序的入口函数,我们的程序从这里开始
//-----------------------------------------------------------------------------------------------
int main()
{
//载入原始图
Mat srcImage = imread("1.jpg"); //工程目录下应该有一张名为1.jpg的素材图
Mat srcImage1 = srcImage.clone();
//显示原始图
imshow("【原始图】Canny边缘检测", srcImage);
//----------------------------------------------------------------------------------
// 二、高阶的canny用法,转成灰度图,降噪,用canny,最后将得到的边缘作为掩码,拷贝原图到效果图上,得到彩色的边缘图
//----------------------------------------------------------------------------------
Mat dstImage, edge, grayImage;
// 【1】创建与src同类型和大小的矩阵(dst)
dstImage.create(srcImage1.size(), srcImage1.type());
// 【2】将原图像转换为灰度图像
cvtColor(srcImage1, grayImage, COLOR_BGR2GRAY);
// 【3】先用使用 3x3内核来降噪
blur(grayImage, edge, Size(3, 3));
// 【4】运行Canny算子
Canny(edge, edge, 3, 9, 3);
//【5】将g_dstImage内的所有元素设置为0
dstImage = Scalar::all(0);
//【6】使用Canny算子输出的边缘图g_cannyDetectedEdges作为掩码,来将原图g_srcImage拷到目标图g_dstImage中
srcImage1.copyTo(dstImage, edge);
//【7】显示效果图
imshow("【效果图】Canny边缘检测2", dstImage);
waitKey(0);
return 0;
}