图像处理边缘算法_c++ 数字图像处理canny边缘算法非极大值抑制 - CSDN
精华内容
参与话题
  • 图像处理+边缘检测算法

    万次阅读 2018-04-03 16:41:15
    一、边缘检测算子类别 常见边缘检测算子:Roberts 、Sobel 、Prewitt、Laplacian、Log/Marr、Canny、Kirsch、Nevitia二、一阶微分算子:Roberts 、Sobel 、Prewitt Robert算子是第一个边缘检测算子,提出者...

    一、边缘检测算子类别 

         常见边缘检测算子:Roberts 、Sobel 、Prewitt、Laplacian、Log/Marr、Canny、Kirsch、Nevitia

    二、一阶微分算子:Roberts 、Sobel 、Prewitt

            Robert算子是第一个边缘检测算子,提出者Lawrence Roberts in 1963。

            Sobel边缘算子,当年作者并没有公开发表过论文,仅仅是在一次博士生课题讨论会(1968)上提出("A 3x3 Isotropic Gradient Operator for Image Processing"),后在1973年出版的一本专著("Pattern Classification and Scene Analysis")的脚注里作为注释出现和公开的。提出者Irwin Sobel。

            Prewitt算子来自J.M.S. Prewitt "Object Enhancement and Extraction" in "Picture processing and Psychopictorics", Academic Press,1970。

            我们看这三种边缘检测算子模板及写成差分的形式



     Roberts算子



    Sobel算子



    Prewitt算子

    图 4 一阶微分算子

           如何计算边缘幅值与方向?以Sobel算子为例。3*3 Sobel两个方向的算子在图像上滑动,模板与其覆盖的图像3*3区域9个像素进行卷积,求和后得到此方向的边缘检测幅值。





    f(x,y)为图像,Gx和Gy分别是水平和竖直方向算子的卷积结果,G则是最终得到的边缘幅值,θ值则是边缘方向。当然G的计算有时简化为

      

    或者

     

    求幅值时是有多种选择的,一般根据具体应用选择用水平还是竖直或是两个方向同时检测。

            另外,需要说明的是,Sobel算子还有一种变种,是各向同性Sobel算子,其模板为



    图 5 各向同性Sobel算子

    Sobel各向同性算子的权值比普通Sobel算子的权值更准确。为什么?模板的权值是离中心位置越远则权值(看绝对值)影响越小,如上图,把模板看成是9个小正方形,小正方形边长为1,则虚线三角形的斜边长为,下直角边长为1,则如果(0,0)位置权值绝对值大小为1,则按照距离关系,位置(1,0)处的权值绝对值大小应该为才是准确的。

    三、 二阶微分算子:Laplacian、Log/Marr

            拉普拉斯算子来自拉普拉斯变换,而Log算子又称Marr算子,由David Courtnay Marr和Ellen Hildreth(1980)共同提出,计算神经学创始人Marr在1980年正式发表论文时,因换白血病早逝,后面设立Marr奖以此纪念其贡献,现在每两年的ICCV(与ECCV,CVPR并称计算机视觉三大顶级会议)会评出一个Marr奖。这两种算子模板如下:


    Laplacian算子(两种模板)


    Log算子

    图 6 二阶微分算子


    拉普拉斯算子数学公式是

    写成差分形式为


            Log边缘检测则是先进行高斯滤波再进行拉普拉斯算子检测,然后找过零点来确定边缘位置,很多时候我们只是知道Log 5*5模板如上图所示,但是具体是怎么得到的?下面进行推导。

    二维高斯公式是


    按拉普拉斯算子公式求x,y方向的二阶偏导后为


    这里x,y不能看成模板位置,应看成是模板其他位置到中心位置的距离。那么写成


    这里x0,y0就是模板中心位置,x,y是模板其他位置,对于5*5模板,则x0=2,y0 = 2,那对于模板中(0,0)位置的权值,即把x= 0,y= 0,x0= 2,y0 = 2带入上式,另= 1,得到约等于0.0175,这样得到


    通过取整变符号,且模板总和为0,得到图6所示的模板。

            另外,这里模板大小是如何取的?通常高斯分布中,在(-3,3)的范围内就覆盖了绝大部分区域,所以模板大小一般取dim = 1 + 6(在SIFT特征中,其中的高斯模糊也是这样取),dim如果为小数,则取不小于dim的最小整数,当然实际使用时没有这么严格,如上面我们取=1时,模板大小取5*5。那同一个尺寸的模板中的权值调整就是的变化得到的,变化到一定程度,模板尺寸大小改变(这个是个人理解,欢迎拍砖羡慕)。

    四、非微分边缘检测算子:Canny

           Canny边缘检测大家应该很熟悉,这里列出步骤,并给出一个详细介绍的链接Canny算子

        1.      彩色图像转换为灰度图像
        2.      对图像进行高斯模糊
        3.      计算图像梯度,根据梯度计算图像边缘幅值与角度(这里其实用到了微分边缘检测算子来计算梯度幅值方向)
        4.      非最大信号压制处理(边缘细化)
        5.      双阈值边缘连接处理

        6.      二值化图像输出结果

    五、方向算子Kirsch(8个3*3模板),Nevitia (12个5*5模板)

            这两个算子是利用多个方向的子模板进行分别计算,最后取幅值最大的那个为最终边缘幅值,方向即最大幅值对应的那个方向。

    六、各边缘检测算子对比


    参考文献:

    1、http://blog.csdn.net/xiaojiegege123456/article/details/7714863

    2、http://blog.csdn.net/yanmy2012/article/details/8110316

    3、http://blog.csdn.net/langb2014/article/details/45667921

    4、https://blog.csdn.net/tigerda/article/details/61192943

    展开全文
  • 主流图像边缘检测算法

    万次阅读 2019-08-09 11:28:53
    本文内容包括:图像噪声添加与去除、几种滤波算法【高斯滤波,方框滤波,均值滤波,中值滤波,双边滤波,小波变换】、几种检测算法【Canny算子、Sobel算子、Laplace算子、Roberts算子、Susan Krisch Prewitt角点检测...

    前言:最近在学习关于图像边缘检测技术,更新此博文仅为 了记录个人学习过程,也供有共同志趣的朋友参考!

    本文内容包括:图像噪声添加与去除、几种滤波算法【高斯滤波,方框滤波,均值滤波,中值滤波,双边滤波,小波变换】、几种检测算法【Canny算子、Sobel算子、Laplace算子、Roberts算子、Krisch算子、Prewitt算子、Susan角点检测算法】、MFC图形界面实现等。


    作者博客地址:永春小站
    文中所有代码均在本人电脑上正常运行!

    测试环境:VS2013+opevCV2.49


    先上一张最终效果图
    这里写图片描述


    目录:
    一、图像噪声
    1.图像噪声分类
    <1>噪声分类
    <2>滤波算法

    2.给图像添加噪声
    <1>高斯白噪声
    <2>椒盐噪声
    <3>编程实现

    二、滤波算法
    1.关于图像滤波

    2.图像滤波分类

    3.滤波算法介绍
    <1>高斯滤波
    <2>方框滤波
    <3>均值滤波
    <4>中值滤波
    <5>双边滤波

    三、图像增强
    1.对数增强算法
    2.指数增强算法
    3.高曝光
    4.高反差

    四、边缘检测
    1.Canny算子
    2.Sobel算子
    3.Laplace算子
    4.Roberts算子
    5.Krisch算子
    6.Prewitt算子

    7.角点检测算法
    <1>Susan角点检测

    五、MFC实现
    1.框架搭建
    2.功能实现

    六、结论


    一、图像噪声

    1.图像噪声分类

    <1>图像噪声分类
    噪声的分类和该噪声的分布符合什么模型有关,常见的噪声有高斯白噪声、椒盐噪声、泊松分布噪声、指数分布噪声等。

    <2>图像滤波器
    图像滤波器有空域滤波器,比如均值滤波器、中值滤波器、低通滤波器、高斯滤波等;频域滤波器,比如小波变换、傅里叶变换、余弦变换等;形态学滤波器,主要是通过膨胀和腐蚀等形态学操作进行去噪。
    一般平时见的比较多是是高斯白噪声,像用均值滤波、中值滤波、高斯滤波可以去噪。除此以外,像椒盐噪声,一般用中值滤波基本可以去噪。

    2.给图像添加噪声
    以上说了图像噪声的分类,以下则针对常见的高斯白噪声和椒盐噪声进行详解

    <1>高斯白噪声
    高斯白噪声中的高斯是指概率分布是正态函数,而白噪声是指它的二阶矩不相关,一阶矩为常数,是指先后信号在时间上的相关性。这是考查一个信号的两个不同方面的问题。

    <2>椒盐噪声
    椒盐噪声其实是椒噪声和盐噪声的统称,椒噪声即图像中灰度值为255的像素点,盐噪声则为0。

    <3>编程实现

    /*-----------CreateNoise类-------------------*/
    //CreateNoise.h文件
    //功能:产生图像噪声
    //使用:调用成员函数void AddGaussianNoise(Mat&)添加高斯噪声
    //调用成员函数void AddPepperNoise(Mat&int n)添加高斯噪声
    //调用成员函数void AddSaitNoise(Mat&,int n)添加高斯噪声
    
    #ifndef _CREATENOISE
    #define _CREATENOISE
    
    #include <opencv2/core/core.hpp>
    #include <opencv2/highgui/highgui.hpp>
    #include <iostream>
    
    #define TWO_PI 6.2831853071795864769252866
    
    using namespace cv;
    using namespace std;
    
    class CreateNoise{
    private:
    	double generateGaussianNoise();//创建高斯噪声函数原型
    
    public:
    	void AddPepperNoise(Mat &image, int n);//添加椒噪声
    	void AddSaltNoise(Mat &image, int n);//添加盐噪声
    	void AddGaussianNoise(Mat&);//添加高斯噪声
    };
    
    #endif
    
    //CreateNoise.cpp文件
    /*给图像添加高斯噪声*/
    #include"com.fanyu.createNoise.h"
    
    //产生高斯噪声
    double CreateNoise::generateGaussianNoise(){
    	static bool hasSpare = false;
    	static double rand1, rand2;
    
    	if (hasSpare)
    	{
    		hasSpare = false;
    		return sqrt(rand1) * sin(rand2);
    	}
    
    	hasSpare = true;
    
    	rand1 = rand() / ((double)RAND_MAX);
    	if (rand1 < 1e-100) rand1 = 1e-100;
    	rand1 = -2 * log(rand1);
    	rand2 = (rand() / ((double)RAND_MAX)) * TWO_PI;
    
    	return sqrt(rand1) * cos(rand2);
    }
    
    //高斯噪声
    void CreateNoise::AddGaussianNoise(Mat& I){
    	// accept only char type matrices
    	CV_Assert(I.depth() != sizeof(uchar));
    
    	int channels = I.channels();
    
    	int nRows = I.rows;
    	int nCols = I.cols * channels;
    
    	if (I.isContinuous()){
    		nCols *= nRows;
    		nRows = 1;
    	}
    
    	int i, j;
    	uchar* p;
    	for (i = 0; i < nRows; ++i){
    		p = I.ptr<uchar>(i);
    		for (j = 0; j < nCols; ++j){
    			double val = p[j] + generateGaussianNoise() * 32;
    			if (val < 0)
    				val = 0;
    			if (val > 255)
    				val = 255;
    
    			p[j] = (uchar)val;
    
    		}
    	}
    
    }
    
    //盐噪声
    void CreateNoise::AddSaltNoise(Mat &image, int n) {//n为噪点数目
    	int i, j;
    	for (int k = 0; k<n / 2; k++) {
    
    		// rand() is the random number generator
    		i = std::rand() % image.cols; // % 整除取余数运算符,rand=1022,cols=1000,rand%cols=22
    		j = std::rand() % image.rows;
    
    		if (image.type() == CV_8UC1) { // gray-level image
    
    			image.at<uchar>(j, i) = 255; //at方法需要指定Mat变量返回值类型,如uchar等
    
    		}
    		else if (image.type() == CV_8UC3) { // color image
    
    			image.at<cv::Vec3b>(j, i)[0] = 255; //cv::Vec3b为opencv定义的一个3个值的向量类型
    			image.at<cv::Vec3b>(j, i)[1] = 255; //[]指定通道,B:0,G:1,R:2
    			image.at<cv::Vec3b>(j, i)[2] = 255;
    		}
    	}
    }
    
    //椒噪声
    void CreateNoise::AddPepperNoise(Mat &image, int n) {//n为噪点数目
    
    	int i, j;
    	for (int k = 0; k<n; k++) {
    
    		// rand() is the random number generator
    		i = std::rand() % image.cols; // % 整除取余数运算符,rand=1022,cols=1000,rand%cols=22
    		j = std::rand() % image.rows;
    
    		if (image.type() == CV_8UC1) { // gray-level image
    
    			image.at<uchar>(j, i) = 0; //at方法需要指定Mat变量返回值类型,如uchar等
    
    		}
    		else if (image.type() == CV_8UC3) { // color image
    
    			image.at<cv::Vec3b>(j, i)[0] = 0; //cv::Vec3b为opencv定义的一个3个值的向量类型
    			image.at<cv::Vec3b>(j, i)[1] = 0; //[]指定通道,B:0,G:1,R:2
    			image.at<cv::Vec3b>(j, i)[2] = 0;
    		}
    	}
    }
    

    二、图像滤波

    1.图像滤波说明【来源搜狗词条】

    图像滤波,即在尽量保留图像细节特征的条件下对目标像的噪声进行抑制,是图像预处理中不可缺少的操作,其处理效果的好坏将直接影响到后续图像处理和分析的有效性和可靠性。
    由于成像系统、传输介质和记录设备等的不完善,数字图像在其形成、传输记录过程中往往会受到多种噪声的污染。另外,在图像处理的某些环节当输入的像对象并不如预想时也会在结果图像中引入噪声。这些噪声在图像上常表现为一引起较强视觉效果的孤立像素点或像素块。一般,噪声信号与要研究的对象不相关它以无用的信息形式出现,扰乱图像的可观测信息。对于数字图像信号,噪声表为或大或小的极值,这些极值通过加减作用于图像像素的真实灰度值上,在图像造成亮、暗点干扰,极大降低了图像质量,影响图像复原、分割、特征提取、图识别等后继工作的进行。要构造一种有效抑制噪声的滤波机必须考虑两个基本问题能有效地去除目标和背景中的噪声;同时,能很好地护图像目标的形状、大小及特定的几何和拓扑结构特征。

    2.滤波器分类
    对于滤波器的分类按需求可有多种分类方式,滤波方法也各不相同,在此主要根据图像噪声处理介绍一种分类:

    不同的滤波器用于不同的噪声,很难说某一个降噪滤波器能符所有的噪声。
    首先,说一下噪声的类型吧,噪声的分类和该噪声的分布符合什么模型有关,常见的噪声有高斯白噪声、椒盐噪声、泊松分布噪声、指数分布噪声等。
    其次,采用的滤波器有空域滤波器,比如均值滤波器、中值滤波器、低通滤波器、高斯滤波等;频域滤波器,比如小波变换、傅里叶变换、余弦变换等;形态学滤波器,主要是通过膨胀和腐蚀等形态学操作进行去噪。
    第三,对应场合。一般平时见的比较多是是高斯白噪声,像用均值滤波、中值滤波、高斯滤波可以去噪。还有在低照度下,比如晚上拍照时的图像,一般属于泊松分布的噪声,可以采用一些3d去噪算法,比如效果不错的BM3D算法。像椒盐噪声,一般用中值滤波基本可以去噪。

    作者:chaoren xiongmao
    链接:http://www.zhihu.com/question/20095134/answer/14158067
    来源:知乎
    著作权归作者所有,转载请联系作者获得授权。

    3.滤波算法
    <1>高斯滤波算法
    openCV中封装了高斯滤波函数

    C++: void GaussianBlur(InputArray src,OutputArray dst, Size ksize, double sigmaX, double sigmaY=0, intborderType=BORDER_DEFAULT )  
    

    第一个参数,InputArray类型的src,输入图像,即源图像,填Mat类的对象即可。它可以是单独的任意通道数的图片,但需要注意,图片深度应该为CV_8U,CV_16U, CV_16S, CV_32F 以及 CV_64F之一。
    第二个参数,OutputArray类型的dst,即目标图像,需要和源图片有一样的尺寸和类型。比如可以用Mat::Clone,以源图片为模板,来初始化得到如假包换的目标图。
    第三个参数,Size类型的ksize高斯内核的大小。其中ksize.width和ksize.height可以不同,但他们都必须为正数和奇数。或者,它们可以是零的,它们都是由sigma计算而来。
    第四个参数,double类型的sigmaX,表示高斯核函数在X方向的的标准偏差。
    第五个参数,double类型的sigmaY,表示高斯核函数在Y方向的的标准偏差。若sigmaY为零,就将它设为sigmaX,如果sigmaX和sigmaY都是0,那么就由ksize.width和ksize.height计算出来。
    为了结果的正确性着想,最好是把第三个参数Size,第四个参数sigmaX和第五个参数sigmaY全部指定到。
    第六个参数,int类型的borderType,用于推断图像外部像素的某种边界模式。有默认值BORDER_DEFAULT,我们一般不去管它。
      
    应用:

    /*线性滤波:高斯滤波*/
    bool GaussianBlur(String srcAdress, String dstAdress = "E:/opencv/averageFilter.jpg"){
    
    	Mat srcImage = imread(srcAdress, 1);
    	Mat dstImage;
    	if (!srcImage.data) {
    		cout << "图像读取出错!";
    		return false;
    	}
    
    	//滤波操作
    	GaussianBlur(srcImage, dstImage, Size(7,7),0,0);
    
    	//导出
    	imwrite(dstAdress, dstImage);
    
    	//显示
    	imshow("原图:", srcImage);
    	imshow("高斯滤波效果:", dstImage);
    
    	waitKey(0);
    
    	return true;
    }
    

    效果展示:
    这里写图片描述

    <2>方框滤波算法
    openCV中封装该函数,函数原型为

    C++: void boxFilter(InputArray src,OutputArray dst, int ddepth, Size ksize, Point anchor=Point(-1,-1), boolnormalize=true, int borderType=BORDER_DEFAULT )  
    

    参数详解:
    第一个参数,InputArray类型的src,输入图像,即源图像,填Mat类的对象即可。该函数对通道是独立处理的,且可以处理任意通道数的图片,但需要注意,待处理的图片深度应该为CV_8U, CV_16U, CV_16S, CV_32F 以及 CV_64F之一。
    第二个参数,OutputArray类型的dst,即目标图像,需要和源图片有一样的尺寸和类型。
    第三个参数,int类型的ddepth,输出图像的深度,-1代表使用原图深度,即src.depth()。
    第四个参数,Size类型(对Size类型稍后有讲解)的ksize,内核的大小。一般这样写Size( w,h )来表示内核的大小( 其中,w 为像素宽度, h为像素高度)。Size(3,3)就表示3x3的核大小,Size(5,5)就表示5x5的核大小
    第五个参数,Point类型的anchor,表示锚点(即被平滑的那个点),注意他有默认值Point(-1,-1)。如果这个点坐标是负值的话,就表示取核的中心为锚点,所以默认值Point(-1,-1)表示这个锚点在核的中心。
    第六个参数,bool类型的normalize,默认值为true,一个标识符,表示内核是否被其区域归一化(normalized)了。
    第七个参数,int类型的borderType,用于推断图像外部像素的某种边界模式。有默认值BORDER_DEFAULT,我们一般不去管它。

    编程实现

    /*线性滤波:方框滤波*/
    int kenelValue = 3;
    int MaxkenelValue = 50;
    String srcAdress, dstAdress;
    //回调函数
    static void callBackboxFilter(int, void*){
    
    	Mat srcImage = imread(srcAdress,1);
    	Mat dstImage;
    	if (!srcImage.data){
    		cout << "图像读取出错";
    		return;
    	}
    
    	//滤波操作
    	boxFilter(srcImage, dstImage, -1, Size(kenelValue+1, kenelValue+1));
    
    	//显示
    	imshow("方框滤波效果预览:", dstImage);
    
    }
    //创建进度条
    int boxFilter(String ssrcAdress, String sdstAdress = "E:/opencv/boxFilter.jpg"){
    	
    	srcAdress = ssrcAdress;
    	dstAdress=sdstAdress;
    
    	namedWindow("方框滤波效果预览:",WINDOW_AUTOSIZE);
    	
    	//创建进度条
    	createTrackbar("内核大小:", "方框滤波效果预览:", &kenelValue, MaxkenelValue, callBackboxFilter);
    	
    	//回调
    	callBackboxFilter(kenelValue, 0);
    	
    	waitKey(0);
    
    	return 0;
    }
    

    效果展示
    这里写图片描述

    <3>均值滤波算法
    openCV中封装了均值滤波算法函数

    C++: void blur(InputArray src, OutputArraydst, Size ksize, Point anchor=Point(-1,-1), int borderType=BORDER_DEFAULT )  
    

    参数详解
    第一个参数,InputArray类型的src,输入图像,即源图像,填Mat类的对象即可。该函数对通道是独立处理的,且可以处理任意通道数的图片,但需要注意,待处理的图片深度应该为CV_8U, CV_16U, CV_16S, CV_32F 以及 CV_64F之一。
    第二个参数,OutputArray类型的dst,即目标图像,需要和源图片有一样的尺寸和类型。比如可以用Mat::Clone,以源图片为模板,来初始化得到如假包换的目标图。
    第三个参数,Size类型(对Size类型稍后有讲解)的ksize,内核的大小。一般这样写Size( w,h )来表示内核的大小( 其中,w 为像素宽度, h为像素高度)。Size(3,3)就表示3x3的核大小,Size(5,5)就表示5x5的核大小
    第四个参数,Point类型的anchor,表示锚点(即被平滑的那个点),注意他有默认值Point(-1,-1)。如果这个点坐标是负值的话,就表示取核的中心为锚点,所以默认值Point(-1,-1)表示这个锚点在核的中心。
    第五个参数,int类型的borderType,用于推断图像外部像素的某种边界模式。有默认值BORDER_DEFAULT,我们一般不去管它。

    应用

    /*线性滤波:均值滤波*/
    bool blur(String srcAdress, String dstAdress = "E:/opencv/averageFilter.jpg"){
    
    	Mat srcImage = imread(srcAdress, 1);
    	Mat dstImage;
    	if (!srcImage.data) {
    		cout << "图像读取出错!";
    		return false;
    	}
    
    	//滤波操作
    	blur(srcImage, dstImage, Size(7, 7));
    
    	//导出
    	imwrite(dstAdress, dstImage);
    
    	//显示
    	imshow("原图:", srcImage);
    	imshow("均值滤波效果:", dstImage);
    
    	waitKey(0);
    
    	return true;
    }
    

    效果
    这里写图片描述

    <4>中值滤波算法
    openCV封装了该算法

    C++: void medianBlur(InputArray src,OutputArray dst, int ksize) 
    

    参数详解:
    第一个参数,InputArray类型的src,函数的输入参数,填1、3或者4通道的Mat类型的图像;当ksize为3或者5的时候,图像深度需为CV_8U,CV_16U,或CV_32F其中之一,而对于较大孔径尺寸的图片,它只能是CV_8U。
    第二个参数,OutputArray类型的dst,即目标图像,函数的输出参数,需要和源图片有一样的尺寸和类型。我们可以用Mat::Clone,以源图片为模板,来初始化得到如假包换的目标图。
    第三个参数,int类型的ksize,孔径的线性尺寸(aperture linear size),注意这个参数必须是大于1的奇数,比如:3,5,7,9 …

    应用

    /*非线性滤波:中值滤波*/
    bool medianBlur(String src,String dst){
    	Mat srcImage = imread(src,1);
    	Mat dstImage;
    	if (!srcImage.data)
    		return false;
    
    	//滤波操作[注意:中间值取奇数]
    	medianBlur(srcImage,dstImage,7);
    
    	//显示
    	imshow("原图:",srcImage);
    	imshow("中值滤波效果图:",dstImage);
    
    	//导出
    	imwrite(dst,dstImage);
    
    	waitKey(0);
    
    	return true;
    }
    

    效果
    这里写图片描述

    <5>双边滤波算法
    openCV封装了该函数

    C++: void bilateralFilter(InputArray src, OutputArraydst, int d, double sigmaColor, double sigmaSpace, int borderType=BORDER_DEFAULT)
    

    参数详解:
    第一个参数,InputArray类型的src,输入图像,即源图像,需要为8位或者浮点型单通道、三通道的图像。
    第二个参数,OutputArray类型的dst,即目标图像,需要和源图片有一样的尺寸和类型。
    第三个参数,int类型的d,表示在过滤过程中每个像素邻域的直径。如果这个值我们设其为非正数,那么OpenCV会从第五个参数sigmaSpace来计算出它来。
    第四个参数,double类型的sigmaColor,颜色空间滤波器的sigma值。这个参数的值越大,就表明该像素邻域内有更宽广的颜色会被混合到一起,产生较大的半相等颜色区域。
    第五个参数,double类型的sigmaSpace坐标空间中滤波器的sigma值,坐标空间的标注方差。他的数值越大,意味着越远的像素会相互影响,从而使更大的区域足够相似的颜色获取相同的颜色。当d>0,d指定了邻域大小且与sigmaSpace无关。否则,d正比于sigmaSpace。
    第六个参数,int类型的borderType,用于推断图像外部像素的某种边界模式。注意它有默认值BORDER_DEFAULT。

    应用:

    /*非线性滤波:双边滤波*/
    bool bilateralFilter(String src, String dst){
    	Mat srcImage = imread(src, 1);
    	Mat dstImage;
    	if (!srcImage.data)
    		return false;
    
    	//滤波操作
    	bilateralFilter(srcImage, dstImage, 25*2,25*2*2,25/2*2);
    
    	//显示
    	imshow("原图:", srcImage);
    	imshow("双边滤波效果图:", dstImage);
    
    	//导出
    	imwrite(dst, dstImage);
    
    	waitKey(0);
    
    	return true;
    }
    

    效果:[由图可见,双边滤波对椒盐噪声是无作用的]
    这里写图片描述

    三、图像增强
    1.对数增强算法
    2.指数增强算法
    3.高曝光
    4.高反差
    以上功能集成在下面的cpp文件中

    /************************************************************
    *  图像增强算法【在主函数main中调用即可】
    *  使用说明:
    *   
    *
    *  @author zha_yongchun
    *  @version 2016.8.5.001
    *************************************************************/
    
    #include<iostream>  
    #include <opencv2/core/core.hpp>  
    #include <opencv2/highgui/highgui.hpp>  
    #include <opencv2/imgproc/imgproc.hpp> 
    
    using namespace cv;
    using namespace std;
    
    /*对数图像增强算法*/
    //对数图像增强是图像增强的一种常见方法,其公式为: S = c log(r + 1),
    //其中c是常数(以下算法c = 255 / (log(256)),这样可以实现整个画面的亮度增大。
    void LogEnhance(IplImage* img, IplImage* dst)
    {
    	// 由于oldPixel:[1,256],则可以先保存一个查找表
    	uchar lut[256] = { 0 };
    
    	double temp = 255 / log(256);
    
    	for (int i = 0; i<255; i++)
    	{
    		lut[i] = (uchar)(temp* log(i + 1) + 0.5);
    	}
    
    	for (int row = 0; row <img->height; row++)
    	{
    		uchar *data = (uchar*)img->imageData + row* img->widthStep;
    		uchar *dstData = (uchar*)dst->imageData + row* dst->widthStep;
    
    		for (int col = 0; col<img->width; col++)
    		{
    			for (int k = 0; k<img->nChannels; k++)
    			{
    				uchar t1 = data[col*img->nChannels + k];
    				dstData[col*img->nChannels + k] = lut[t1];
    			}
    		}
    	}
    }
    
    
    /*指数图像增强算法*/
    //指数图像增强的表达为:S = cR^r,通过合理的选择c和r可以压缩灰度范围,
    //算法以c = 1.0 / 255.0, r = 2实现。
    void ExpEnhance(IplImage* img, IplImage* dst)
    {
    	// 由于oldPixel:[1,256],则可以先保存一个查找表
    	uchar lut[256] = { 0 };
    
    	double temp = 1.0 / 255.0;
    
    	for (int i = 0; i<255; i++)
    	{
    		lut[i] = (uchar)(temp*i*i + 0.5);
    	}
    
    	for (int row = 0; row <img->height; row++)
    	{
    		uchar *data = (uchar*)img->imageData + row* img->widthStep;
    		uchar *dstData = (uchar*)dst->imageData + row* dst->widthStep;
    
    		for (int col = 0; col<img->width; col++)
    		{
    			for (int k = 0; k<img->nChannels; k++)
    			{
    				uchar t1 = data[col*img->nChannels + k];
    				dstData[col*img->nChannels + k] = lut[t1];
    			}
    		}
    	}
    }
    
    /*曝光过度问题处理*/
    //对于曝光过度问题,可以通过计算当前图像的反相(255 - image),
    //然后取当前图像和反相图像的较小者为当前像素位置的值。
    // 过度曝光原理:图像翻转,然后求原图与反图的最小值
    void ExporeOver(IplImage* img, IplImage* dst)
    {
    	for (int row = 0; row <img->height; row++)
    	{
    		uchar *data = (uchar*)img->imageData + row* img->widthStep;
    		uchar *dstData = (uchar*)dst->imageData + row* dst->widthStep;
    		for (int col = 0; col<img->width; col++)
    		{
    			for (int k = 0; k<img->nChannels; k++)
    			{
    				uchar t1 = data[col*img->nChannels + k];
    				uchar t2 = 255 - t1;
    				dstData[col*img->nChannels + k] = min(t1, t2);
    			}
    		}
    	}
    }
    
    
    /*高反差保留*/
    //高反差保留主要是将图像中颜色、明暗反差较大两部分的交界处保留下来,
    //比如图像中有一个人和一块石头,那么石头的轮廓线和人的轮廓线以及面部、
    //服装等有明显线条的地方会变被保留,儿其他大面积无明显明暗变化的地方
    //则生成中灰色。其表达形式为:dst = r*(img - Blur(img))。
    Mat HighPass(Mat img)
    {
    	Mat temp;
    	GaussianBlur(img, temp, Size(7, 7), 1.6, 1.6);
    
    	int r = 3;
    	Mat diff = img + r*(img - temp); //高反差保留算法
    	return diff;
    }
    
    

    效果图:
    对数增强
    这里写图片描述

    指数增强
    这里写图片描述

    高曝光
    这里写图片描述

    高反差
    这里写图片描述

    重头戏来了!!!
    上面讲了那么多,其实都只是在为图像检测做准备,以下就是本文的重点!

    四、边缘检测
    1.Canny算子
    关于Canny算子原理请参考百度百科
    http://baike.baidu.com/link?url=ExVCDbWfd9DPB20z2SzPjB4kHDcxHkJD25lVQIDL-cZSc9HVeTNWPCTLHsCveobPulIUaX6HEu8z0lbsGRfi9a

    首先来看openCV中的Canny函数

    C++: void Canny(InputArray image,OutputArray edges, double threshold1, double threshold2, int apertureSize=3,bool L2gradient=false )  
    

    参数详解
    第一个参数,InputArray类型的image,输入图像,即源图像,填Mat类的对象即可,且需为单通道8位图像。
    第二个参数,OutputArray类型的edges,输出的边缘图,需要和源图片有一样的尺寸和类型。
    第三个参数,double类型的threshold1,第一个滞后性阈值。
    第四个参数,double类型的threshold2,第二个滞后性阈值。
    第五个参数,int类型的apertureSize,表示应用Sobel算子的孔径大小,其有默认值3。
    第六个参数,bool类型的L2gradient,一个计算图像梯度幅值的标识,有默认值false。

    应用:

    /************************************************************
    *  边缘检测算法函数之Canny算子【在主函数main中调用即可】
    *  使用说明:
    *    两个轨迹条分别表示Canny算子中的滞后阈值和高斯滤波内核值
    *
    *  @author zha_yongchun
    *  @version 2016.8.5.001
    *************************************************************/
    
    #include<iostream>  
    #include <opencv2/core/core.hpp>  
    #include <opencv2/highgui/highgui.hpp>  
    #include <opencv2/imgproc/imgproc.hpp> 
    
    using namespace cv;
    using namespace std;
    
    
    
    /*----------------------------------------【Canny算子篇】------------------------------------------*/
    /**Canny算子检测
     *1.原理:
     *(1)图像边缘检测必须满足两个条件:一是能有效地抑制噪声,而是必须尽量精确的确定边缘的位置。
     *(2)根据对信噪比与定位乘积进行测度,得到最优化逼近算子,即Canny算子。
     *(3)先平滑后求导
     *
     *2.Canny的目标是找到一个最优的边缘检测算法,最优的含义是:
     *(1)好的检测—算法能够尽可能多的标识出图像中的实际边缘
     *(2)好的定位—标识出的边缘要尽可能与实际图像中的实际边缘尽可能接近。
     *(3)最小响应—图像中的边缘只能标志一次,并且可能存在的图像杂讯不因该标志为边缘。
     *
     *3.Canny边缘检测步骤:
     *(1)去噪
     *任何边缘检测算法都不可能在未经处理的原始数据上很好地處理,所以第一步是对原始数据与高斯 mask 作卷积,得到的
     *图像与原始图像相比有些轻微的模糊(blurred)。这样,单独的一个像素杂讯在经过高斯平滑的图像上变得几乎没有影响。
     *(2)用一阶偏导的有限差分来计算梯度的幅值和方向。
     *(3)对梯度幅值进行非极大值抑制。
     *仅仅得到全局的梯度并不足以确定边缘,因此为确定边缘,必须保留局部梯度最大的点,而抑制非极大值。解决方法:利用梯度的方向。
     *(4)用双阈值算法检测和连接边缘。
     *减少假边缘段数量的典型方法是对N[i,j]使用一个阈值。将低于阈值的所有值赋零值。但问题是如何选取阈值?
     *解决方法:双阈值算法。双阈值算法对非极大值抑制图象作用两个阈值τ1和τ2,且2τ1≈τ2,从而可以得到两个阈值边缘图象N1[i,j]
     *和N2[i,j]。由于N2[i,j]使用高阈值得到,因而含有很少的假边缘,但有间断(不闭合)。双阈值法要在N2[i,j]中把边缘连接成轮
     *廓,当到达轮廓的端点时,该算法就在N1[i,j]的8邻点位置寻找可以连接到轮廓上的边缘,这样,算法不断地在N1[i,j]中收集边缘,
     *直到将N2[i,j]连接起来为止。
     *在连接边缘的时候,用数组模拟队列的实现。以进行8-连通域搜索。
     */
    
    /*---------------------函数使用-------------------------*/
    //参数初始化
    int thresholdValue1 = 270;//滞后性阈值1
    int kenelValueCanny = 4;//降噪内核值
    const static int MaxKenelValueCanny = 10;
    const static int MaxThresholdValue1 = 300;
    String srcAdressCanny;
    
    //回调函数
    static void callBackCanny(int, void*){
    
    	Mat srcImage = imread(srcAdressCanny, 3);
    	Mat dstImage, grayImage, denoiseImage,cannyImage;
    	if (!srcImage.data){
    		cout << "图像读取出错";
    		return;
    	}
    
    	//创建与srcImage同类型和大小的矩阵 
    	dstImage.create(srcImage.size(), srcImage.type());
    
    	//将原图像转换为灰度图像  
    	cvtColor(srcImage, grayImage, CV_BGR2GRAY);
    
    	//高斯滤波降噪
    	GaussianBlur(grayImage, denoiseImage, Size(kenelValueCanny * 2 + 1, kenelValueCanny * 2 + 1), 0, 0);
    		
    	//使用Canny算子
    	Canny(denoiseImage, cannyImage, thresholdValue1, thresholdValue1 * 2 / 3, 3);
    	
    	//将dstImage内的所有元素设置为0
    	dstImage = Scalar::all(0);
    
    	//以cannyImage作为掩码,将原图srcImage拷到目标图dstImage中
    	srcImage.copyTo(dstImage, cannyImage);
    
    	//显示
    	imshow("原图", srcImage);
    	imshow("Canny算子边缘检测效果预览:", dstImage);
    	imwrite("E:/opencv/Canny.jpg",dstImage);
    }
    
    //创建进度条
    void Canny(String ssrcAdress){
    
    	srcAdressCanny = ssrcAdress;
    
    	namedWindow("Canny算子边缘检测效果预览:", WINDOW_AUTOSIZE);
    
    	//创建进度条
    	createTrackbar("内核值:", "Canny算子边缘检测效果预览:", &kenelValueCanny, MaxKenelValueCanny, callBackCanny);
    	createTrackbar("阈值一:", "Canny算子边缘检测效果预览:", &thresholdValue1, MaxThresholdValue1, callBackCanny);
    	
    	//回调
    	callBackCanny(kenelValueCanny, 0);
    	callBackCanny(thresholdValue1, 0);
    
    	waitKey(0);
    	
    }
    
    

    效果:
    这里写图片描述

    2.Sobel算子
    Sobel 算子是一个主要用作边缘检测的离散微分算子 (discrete differentiation operator)。 它Sobel算子结合了高斯平滑和微分求导,用来计算图像灰度函数的近似梯度。在图像的任何一点使用此算子,将会产生对应的梯度矢量或是其法矢量。

    sobel算子的wikipedia:
    http://zh.wikipedia.org/wiki/索貝爾算子

    sobel算子相关概念,还可以参看这篇博文:
    http://www.cnblogs.com/lancidie/archive/2011/07/17/2108885.html

    openCV中的Sobel算子详解

    C++: void Sobel (  
    InputArray src,//输入图  
     OutputArray dst,//输出图  
     int ddepth,//输出图像的深度  
     int dx,  
     int dy,  
     int ksize=3,  
     double scale=1,  
     double delta=0,  
     int borderType=BORDER_DEFAULT );  
    

    参数说明:
    第一个参数,InputArray 类型的src,为输入图像,填Mat类型即可。
    第二个参数,OutputArray类型的dst,即目标图像,函数的输出参数,需要和源图片有一样的尺寸和类型。
    第三个参数,int类型的ddepth,输出图像的深度,支持如下src.depth()和ddepth的组合:
    若src.depth() = CV_8U, 取ddepth =-1/CV_16S/CV_32F/CV_64F
    若src.depth() = CV_16U/CV_16S, 取ddepth =-1/CV_32F/CV_64F
    若src.depth() = CV_32F, 取ddepth =-1/CV_32F/CV_64F
    若src.depth() = CV_64F, 取ddepth = -1/CV_64F
    第四个参数,int类型dx,x 方向上的差分阶数。
    第五个参数,int类型dy,y方向上的差分阶数。
    第六个参数,int类型ksize,有默认值3,表示Sobel核的大小;必须取1,3,5或7。
    第七个参数,double类型的scale,计算导数值时可选的缩放因子,默认值是1,表示默认情况下是没有应用缩放的。我们可以在文档中查阅getDerivKernels的相关介绍,来得到这个参数的更多信息。
    第八个参数,double类型的delta,表示在结果存入目标图(第二个参数dst)之前可选的delta值,有默认值0。
    第九个参数, int类型的borderType,我们的老朋友了(万年是最后一个参数),边界模式,默认值为BORDER_DEFAULT。这个参数可以在官方文档中borderInterpolate处得到更详细的信息。

    应用:

    /************************************************************
    *  边缘检测算法函数之Sobel算子【在主函数main中调用即可】
    *  使用说明:
    *    
    *
    *  @author zha_yongchun
    *  @version 2016.8.6.001
    *************************************************************/
    
    #include <opencv2/opencv.hpp>  
    #include<opencv2/highgui/highgui.hpp>  
    #include<opencv2/imgproc/imgproc.hpp> 
    
    using namespace cv;
    using namespace std;
    
    
    
    /*----------------------------------------【Sobel算子篇】------------------------------------------*/
    
    bool Sobel(String src, String dst){
    	
    	Mat srcImage = imread(src,3);
    	Mat grad_Image,grayImage;
    	Mat abs_grad_Image, dstImage;
    	if (!srcImage.data) 
    		return false;
    
    	//将原图像转换为灰度图像  
    	cvtColor(srcImage, grayImage, CV_BGR2GRAY);
    
    	//求(x,y)方向梯度
    	Sobel( grayImage, grad_Image, CV_16S, 1, 1, 3, 1, 1, BORDER_DEFAULT );  
    	convertScaleAbs(grad_Image, abs_grad_Image);
    
    	//显示
    	imshow("原图:", srcImage);
    	imshow("Sobel函数效果图:", abs_grad_Image);
    
    	//导出
    	imwrite(dst, abs_grad_Image);
    
    	waitKey(0);
    
    	return true;
    }
    
    

    效果
    这里写图片描述

    3.Laplace算子
    Laplacian 算子是n维欧几里德空间中的一个二阶微分算子,定义为梯度grad()的散度div()。因此如果f是二阶可微的实函数,则f的拉普拉斯算子定义为:

    (1) f的拉普拉斯算子也是笛卡儿坐标系xi中的所有非混合二阶偏导数求和:
    (2) 作为一个二阶微分算子,拉普拉斯算子把C函数映射到C函数,对于k ≥ 2。表达式(1)(或(2))定义了一个算子Δ :C® → C®,或更一般地,定义了一个算子Δ : C(Ω) → C(Ω),对于任何开集Ω。

    关于Laplace算子的相关概念阐述,可以参看这篇博文:
    http://www.cnblogs.com/xfzhang/archive/2011/01/19/1939020.html
    Laplace算子的wikipedia:
    http://zh.wikipedia.org/wiki/拉普拉斯算子

    openCV中的Laplace函数

    C++: void Laplacian(InputArray src,OutputArray dst, int ddepth, int ksize=1, double scale=1, double delta=0, intborderType=BORDER_DEFAULT );  
    

    第一个参数,InputArray类型的image,输入图像,即源图像,填Mat类的对象即可,且需为单通道8位图像。
    第二个参数,OutputArray类型的edges,输出的边缘图,需要和源图片有一样的尺寸和通道数。
    第三个参数,int类型的ddept,目标图像的深度。
    第四个参数,int类型的ksize,用于计算二阶导数的滤波器的孔径尺寸,大小必须为正奇数,且有默认值1。
    第五个参数,double类型的scale,计算拉普拉斯值的时候可选的比例因子,有默认值1。
    第六个参数,double类型的delta,表示在结果存入目标图(第二个参数dst)之前可选的delta值,有默认值0。
    第七个参数, int类型的borderType,边界模式,默认值为BORDER_DEFAULT。这个参数可以在官方文档中borderInterpolate()处得到更详细的信息。

    应用

    /************************************************************
    *  边缘检测算法函数之Laplace算子【在主函数main中调用即可】
    *  使用说明:
    *    
    *
    *  @author zha_yongchun
    *  @version 2016.8.5.001
    *************************************************************/
    
    #include<iostream>  
    #include <opencv2/core/core.hpp>  
    #include <opencv2/highgui/highgui.hpp>  
    #include <opencv2/imgproc/imgproc.hpp> 
    
    using namespace cv;
    using namespace std;
    
    
    
    /*----------------------------------------【Laplace算子篇】------------------------------------------*/
    
    bool Laplacian(int kenelValue,String src, String dst){
    
    	Mat srcImage, dstImage, grayImage, abs_dstImage,gaussImage;
    	
    	srcImage = imread(src,3);
    	if (!srcImage.data)
    		return false;
    
    	//转为灰度图
    	cvtColor(srcImage,grayImage,CV_RGB2GRAY);
    
    	//高斯滤波
    	GaussianBlur(grayImage, gaussImage, Size(kenelValue,kenelValue), 0, 0, BORDER_DEFAULT);
    
    	//使用Laplace算子
    	Laplacian(gaussImage, abs_dstImage, CV_16S, 3, 1, 0, BORDER_DEFAULT);
    
    	//计算绝对值,并将结果转换成8位  
    	convertScaleAbs(abs_dstImage,dstImage );
    
    	//显示
    	imshow("原图:",srcImage);
    	imshow("Laplace算法:",dstImage);
    
    	//导出
    	imwrite(dst,dstImage);
    
    	waitKey(3000);
    	return true;
    }
    
    

    效果
    这里写图片描述

    4.Roberts算子
    这个函数在openCV库中没有,因此需要自己来实现
    具体代码如下

    /************************************************************
    *  边缘检测算法函数之Robers算子【在主函数main中调用即可】
    *  使用说明:
    *    
    *
    *  @author zha_yongchun
    *  @version 2016.8.5.001
    *************************************************************/
    
    #include<iostream>  
    #include <opencv2/core/core.hpp>  
    #include <opencv2/highgui/highgui.hpp>  
    #include <opencv2/imgproc/imgproc.hpp> 
    
    using namespace cv;
    using namespace std;
    
    // roberts算子实现
    cv::Mat roberts(cv::Mat srcImage)
    {
    	cv::Mat dstImage = srcImage.clone();
    	int nRows = dstImage.rows;
    	int nCols = dstImage.cols;
    	for (int i = 0; i < nRows - 1; i++)
    	{
    		for (int j = 0; j < nCols - 1; j++)
    		{
    			// 根据公式计算
    			int t1 = (srcImage.at<uchar>(i, j) -
    				srcImage.at<uchar>(i + 1, j + 1)) *
    				(srcImage.at<uchar>(i, j) -
    				srcImage.at<uchar>(i + 1, j + 1));
    			int t2 = (srcImage.at<uchar>(i + 1, j) -
    				srcImage.at<uchar>(i, j + 1)) *
    				(srcImage.at<uchar>(i + 1, j) -
    				srcImage.at<uchar>(i, j + 1));
    			// 计算对角线像素差
    			dstImage.at<uchar>(i, j) = (uchar)sqrt(t1 + t2);
    		}
    	}
    	return dstImage;
    }
    
    void Robers(String str,String dst){
    	Mat strImage = imread(str,0);
    	Mat dstImage;
    	if (!strImage.data)
    		return;
    
    	imshow("原图",strImage);
    
    	dstImage=roberts(strImage);//调用robers算子
    
    	imshow("Robers算子实现",dstImage);
    	imwrite(dst,dstImage);
    
    	cvWaitKey(0);
    }
    

    效果
    这里写图片描述

    5.Krisch算子
    Kirsch算子是R.Kirsch提出来一种边缘检测新算法,它采用8个模板对图像上的每一个像素点进行卷积求导数,这8个模板代表8个方向,对图像上的8个特定边缘方向作出最大响应,运算中取最大值作为图像的边缘输出(上述算法中用到的8个模板在下面的实现代码中给出)。为了便于读者理解该算法的实现,这里我们给出实现该算法的函数代码,读者可以稍加改动应用到自己的项目中去。[1]
    int ntemplate[8][9]={{5,5,5,-3,0,-3,-3,-3,-3},
      {-3,5,5,-3,0,5,-3,-3,-3},
      {-3,-3,5,-3,0,5,-3,-3,5},
      {-3,-3,-3,-3,0,5,-3,5,5},
      {-3,-3,-3,-3,0,-3,5,5,5},
      {-3,-3,-3,5,0,-3,5,5,-3},
      {5,-3,-3,5,0,-3,5,-3,-3},
      {5,5,-3,5,0,-3,-3,-3,-3}};//3×3
    用这个8个模版来计算每个边缘方向的最大响应值作为边缘特征值-边缘强度。
    详情参见百度百科
    http://baike.baidu.com/link?url=g4TjG-zN4bTLIJmo8wppKe-Ov1STWrL5HON_XiPL3izUNbG-bi0xisrFsHdtIdAFKuY2dTOXvOZmV1rOq-TpVq

    实现代码

    /************************************************************
    *  边缘检测算法函数之Krisch检测算子【在主函数main中调用即可】
    *  使用说明:
    *
    *
    *  @author zha_yongchun
    *  @version 2016.8.5.001
    *************************************************************/
    
    #include<iostream>  
    #include <opencv2/core/core.hpp>  
    #include <opencv2/highgui/highgui.hpp>  
    #include <opencv2/imgproc/imgproc.hpp> 
    
    using namespace cv;
    using namespace std;
    
    void Krisch(char* path)
    {
    	IplImage *src;
    	src = cvLoadImage(path, 0); //0 单通道加载
    	if (!src)
    		return;
    	IplImage *dst = cvCloneImage(src);
    	int x, y;
    	float a, b, c, d;
    	float p1, p2, p3, p4, p5, p6, p7, p8, p9;
    	uchar* ps = (uchar*)src->imageData; //ps为指向输入图片数据的指针
    	uchar* pd = (uchar*)dst->imageData; //pd为指向输出图片数据的指针
    	int w = dst->width;
    	int h = dst->height;
    	int step = dst->widthStep;
    
    	for (x = 0; x<w - 2; x++)      //取以(x+1,y+1)为中心的9个邻域像素  1 4 7
    	{                                                            // 2 5 8
    		for (y = 0; y<h - 2; y++)                                     // 3 6 9
    		{
    			p1 = ps[y*step + x];
    			p2 = ps[y*step + (x + 1)];
    			p3 = ps[y*step + (x + 2)];
    			p4 = ps[(y + 1)*step + x];
    			p5 = ps[(y + 1)*step + (x + 1)];
    			p6 = ps[(y + 1)*step + (x + 2)];
    			p7 = ps[(y + 2)*step + x];
    			p8 = ps[(y + 2)*step + (x + 1)];
    			p9 = ps[(y + 2)*step + (x + 2)];//得到(i+1,j+1)周围九个点的灰度值
    			a = fabs(float(-5 * p1 - 5 * p2 - 5 * p3 + 3 * p4 + 3 * p6 + 3 * p7 + 3 * p8 + 3 * p9));    //计算4个方向的梯度值
    			b = fabs(float(3 * p1 - 5 * p2 - 5 * p3 + 3 * p4 - 5 * p6 + 3 * p7 + 3 * p8 + 3 * p9));
    			c = fabs(float(3 * p1 + 3 * p2 - 5 * p3 + 3 * p4 - 5 * p6 + 3 * p7 + 3 * p8 - 5 * p9));
    			d = fabs(float(3 * p1 + 3 * p2 + 3 * p3 + 3 * p4 - 5 * p6 + 3 * p7 - 5 * p8 - 5 * p9));
    			a = (a>b) ? a : b;                                         //取各个方向上的最大值作为边缘强度
    			a = (a>c) ? a : c;
    			a = (a>d) ? a : d;
    			pd[(y + 1)*step + (x + 1)] = a;
    		}
    	}
    	cvNormalize(dst, dst, 0, 255, CV_MINMAX); //归一化处理
    
    	cvNamedWindow("krisch", 0);      /* 定义一个窗口名为src的显示窗口 */
    	cvShowImage("krisch", dst);    /* 在src窗口中,显示src指针所指的图像 */
    	cvWaitKey(0);                      /* 无限等待,即图像总显示 */
    	cvDestroyAllWindows();   /* 销毁窗口 */
    	cvReleaseImage(&dst);
    	cvReleaseImage(&src);
    }
    

    效果:
    这里写图片描述

    6.Prewitt算子
    Prewitt算子是一种一阶微分算子的边缘检测,利用像素点上下、左右邻点的灰度差,在边缘处达到极值检测边缘,去掉部分伪边缘,对噪声具有平滑作用 。其原理是在图像空间利用两个方向模板与图像进行邻域卷积来完成的,这两个方向模板一个检测水平边缘,一个检测垂直边缘。

    实现代码

    /************************************************************
    *  边缘检测算法函数之Prewitt算子【在主函数main中调用即可】
    *  使用说明:
    *
    *
    *  @author zha_yongchun
    *  @version 2016.8.5.001
    *************************************************************/
    
    #include <opencv2/highgui/highgui.hpp>  
    #include "cv.h"
    #include <stdlib.h>
    #include <stdio.h>
    #include <cmath>
    #include <cxcore.h>
    
    using namespace cv;
    using namespace std;
    
    void Prewitt(char *path)
    {
    	//-------prewitt算子 适用灰度图 ----
    	IplImage *src = cvLoadImage(path, -1);
    	if (!src)
    		return;
    	IplImage *gray = cvCreateImage(cvGetSize(src), 8, 1);
    	if (src->nChannels == 1)
    	{
    		//复制
    		cvCopy(src, gray);
    	}
    	else
    	{
    		//色彩空间转换,转换类型为CV_BGR2GRAY
    		cvCvtColor(src, gray, CV_BGR2GRAY);
    	}
    	IplImage *dst = NULL;
    	dst = cvCreateImage(cvGetSize(gray), 8, 1);
    	//定义prewitt算子的模板
    	float prewittx[9] =
    	{
    		-1, 0, 1,
    		-1, 0, 1,
    		-1, 0, 1
    	};
    	float prewitty[9] =
    	{
    		1, 1, 1,
    		0, 0, 0,
    		-1, -1, -1
    	};
    	CvMat px;
    	px = cvMat(3, 3, CV_32F, prewittx);
    	CvMat py;
    	py = cvMat(3, 3, CV_32F, prewitty);
    
    	//为输出图像申请空间
    	IplImage *dstx = cvCreateImage(cvGetSize(gray), 8, 1);
    	IplImage *dsty = cvCreateImage(cvGetSize(gray), 8, 1);
    
    	//对图像使用模板,自动填充边界 cvCvtColor(src,gray,CV_RGB2GRAY);
    	cvFilter2D(gray, dstx, &px, cvPoint(-1, -1));
    	cvFilter2D(gray, dsty, &py, cvPoint(-1, -1));
    
    	//计算梯度,范数为2,注意学习指针的使用方法
    	int i, j, temp;
    	float tempx, tempy;  //定义为浮点型是为了避免sqrt函数引起歧义
    	uchar* ptrx = (uchar*)dstx->imageData;
    	uchar* ptry = (uchar*)dsty->imageData;
    	for (i = 0; i<src->width; i++)
    	{
    		for (j = 0; j<src->height; j++)
    		{
    			tempx = ptrx[i + j*dstx->widthStep];   //tempx,tempy表示的是指针所指向的像素
    			tempy = ptry[i + j*dsty->widthStep];
    			temp = (int)sqrt(tempx*tempx + tempy*tempy);
    			dst->imageData[i + j*dstx->widthStep] = temp;
    		}
    	}
    	cvReleaseImage(&dstx);
    	cvReleaseImage(&dsty);
    	cvNamedWindow("Original Image", 0);
    	cvShowImage("Original Image", src);
    	cvNamedWindow("Prewitt", 0);      /* 定义一个窗口名为src的显示窗口 */
    	cvShowImage("Prewitt", dst);    /* 在src窗口中,显示src指针所指的图像 */
    	cvWaitKey(0);                      /* 无限等待,即图像总显示 */
    	cvDestroyAllWindows();   /* 销毁窗口 */
    	cvReleaseImage(&dst);
    	cvReleaseImage(&src);
    	cvReleaseImage(&gray);
    }
    

    效果图
    这里写图片描述

    7.角点检测算法
    角点是图像很重要的特征,对图像图形的理解和分析有很重要的作用。
    目前的角点检测算法可归纳为3类:基于灰度图像的角点检测、基于二值图像的角点检测、基于轮廓曲线的角点检测。

    <1>SUSAN是Smith和Brady提出的一种图像处理方法,该算法是基于像素领域包含若干元素的近似圆形模板,对每个像素基于该模板领域的图像灰度计算角点响应函数(CRF)的数值,如果大于某阈值且为局部极大值,则认为该点为角点。角点的精度与圆形模板大小无关,圆形模板越大,检测的角点数越多,则计算量也越大,本文圆形模板包含37个元素,该近似圆形模板结构如图1所示。

    1)Susan角点检测实现代码

    /************************************************************
    *  边缘检测算法函数之Susan角点检测算子【在主函数main中调用即可】
    *  使用说明:
    *
    *
    *  @author zha_yongchun
    *  @version 2016.8.5.001
    *************************************************************/
    
    #include<iostream>  
    #include <opencv2/core/core.hpp>  
    #include <opencv2/highgui/highgui.hpp>  
    #include <opencv2/imgproc/imgproc.hpp> 
    
    using namespace cv;
    using namespace std;
    
    
    void Susan(char* path)
    {
    	int height, width, step, channels;
    	int i, j, k, same, max, min, thresh, sum;
    	uchar*data0, *data1;
    	//char *filename="result.bmp";
    	IplImage* Img, *nimg; //声明IplImage指针
    	//载入图像
    	Img = cvLoadImage(path, 0); //单通道形式加载
    	if (!Img)
    		return;
    	nimg = cvCreateImage(cvGetSize(Img), 8, 1);
    	height = Img->height;
    	width = Img->width;
    	step = Img->widthStep / sizeof(uchar);
    	channels = Img->nChannels;
    	data0 = (uchar*)Img->imageData;
    	data1 = (uchar*)nimg->imageData;
    
    	printf("Processing a %d X %d image with %d channels\n", width, height, channels);
    	int OffSetX[37] = { -1, 0, 1,
    		-2, -1, 0, 1, 2,
    		-3, -2, -1, 0, 1, 2, 3,
    		-3, -2, -1, 0, 1, 2, 3,
    		-3, -2, -1, 0, 1, 2, 3,
    		-2, -1, 0, 1, 2,
    		-1, 0, 1 };
    	int OffSetY[37] = { -3, -3, -3,
    		-2, -2, -2, -2, -2,
    		-1, -1, -1, -1, -1, -1, -1,
    		0, 0, 0, 0, 0, 0, 0,
    		1, 1, 1, 1, 1, 1, 1,
    		2, 2, 2, 2, 2,
    		3, 3, 3 };
    
    	max = min = data0[0];
    	//for(i=0;i<height;i++)
    	// for(j=0;j<width;j++)
    	//{
    	// if(data0[i*step+j]>max) max = data0[i*step+j];
    	//if(data0[i*step+j]<min)   min = data0[i*step+j];
    	//   }
    	for (i = 3; i<height - 3; i++)
    	for (j = 3; j<width - 3; j++)
    	{
    		same = 0; sum = 0;
    		for (k = 0; k<37; k++)
    		{
    			sum += data0[(i + OffSetY[k])*step + (j + OffSetX[k])];
    			thresh = sum / 37;
    			if (abs(data0[(i + OffSetY[k])*step + (j + OffSetX[k])] - data0[i*step + j]) <= thresh)
    				same++;
    			if (same<18)
    				data1[i*step + j] = 255;
    			else
    				data1[i*step + j] = 0;
    		}
    	}
    
    	cvNamedWindow("SUSAN", 0);      /* 定义一个窗口名为src的显示窗口 */
    	cvShowImage("SUSAN", nimg);    /* 在src窗口中,显示src指针所指的图像 */
    	cvWaitKey(0);                      /* 无限等待,即图像总显示 */
    	cvDestroyWindow("SUSAN");   /* 销毁窗口 */
    	cvReleaseImage(&nimg);
    
    }
    

    效果
    这里写图片描述

    以上叙述了关于图像检测过程中的去噪,增强和检测过程,也给出了实现的代码,为程序的统一可观性,博主用MFC实现了整个程序的界面操作。

    五、MFC实现
    1.框架搭建
    由以上讲解,图像检测的步骤,大致可分噪声种类确定,去除噪声,边缘增强及检测过程。基于这样的过程,此MFC程序通过菜单栏形式也将内容划分为了这样四个部分:
    加噪–>滤波–>增强–>检测
    这里写图片描述

    然后按照易于对比的原则,将窗口划分为五个主要部分,分别显示四个过程的实时图片和选择功能栏
    这里写图片描述

    接下来进行菜单功能的拓展
    文件
    这里写图片描述

    添加噪声
    这里写图片描述

    滤波
    这里写图片描述

    检测
    这里写图片描述

    完整程序界面
    这里写图片描述

    运行结果图
    这里写图片描述

    2.功能实现
    函数的功能实现主要包括了两个部分,其一是MFC界面实现,其二是模块功能代码,其一仅需要基本的MFC基础知识,在此不作解释,其二主要代码则在前文论及。程序源码可到该地址下载

    六、小结
    项目的完成少不了查阅大量的文献以及与他人合作,在此次完成的过程中一个人还是遇到了许多困扰,有一句话最重要:理论结合实践!

    在给出的MFC框架中,有几项功能仍未实现,目前仍在完善中,如有错误,恳请指正!


    项目代码github地址: https://github.com/yooongchun/EdgeDetection

    展开全文
  • 图像处理】 常用边缘检测算法对比分析

    万次阅读 多人点赞 2018-01-19 23:08:07
    在数字图像中,边缘是指图像局部变化最显著的部分,边缘主要存在于目标与目标,目标与背景之间,是图像局部特性的不连续性,如灰度的突变、纹理结构的图标、颜色的图标等。尽管图像边缘点产生的原因各不相同,但...
    边缘的定义
    首先介绍什么是边缘。在数字图像中,边缘是指图像局部变化最显著的部分,边缘主要存在于目标与目标,目标与背景之间,是图像局部特性的不连续性,如灰度的突变、纹理结构的图标、颜色的图标等。尽管图像的边缘点产生的原因各不相同,但他们都是图形上灰度不连续或灰度几句辩护的点,图像边缘分为阶跃状、斜坡状和屋顶状。

    边缘检测的基本方法
    一般图像边缘检测方法主要有如下四个步骤
    图像滤波
    传统边缘检测算法主要是基于图像强度的一阶和二阶导数,但导数的计算对噪声很敏感,因此必须使用滤波器来改善与噪声有关的边缘检测器的性能。需要指出的是,大多数滤波器在降低噪声的同时也造成了了边缘强度的损失,因此,在增强边缘和降低噪声之间需要一个折衷的选择。
    图像增强
    增强边缘的基础是确定图像各点邻域强度的变化值。增强算法可以将邻域(或局部)强度值有显著变化的点突显出来。边缘增强一般是通过计算梯度的幅值来完成的。
    图像检测
    在图像中有许多点的梯度幅值比较大,而这些点在特定的应用领域中并不都是边缘,所以应该用某种方法来确定哪些点是边缘点。最简单的边缘检测判断依据是梯度幅值。
    图像定位
    如果某一应用场合要求确定边缘位置,则边缘的位置可在子像素分辨率上来估计,边缘的方位也可以被估计出来。近20多年来提出了许多边缘检测算子,在这里我们仅讨论集中常见的边缘检测算子。

    常见边缘检测算子分析
    1)  差分边缘检测
    处理数字图像的离散域时,可以用图像的一阶差分来代替图像函数的导数。定义二维离散图像函数在 X 轴方向的一阶差分为:
    f(i+1,j)-f(i,j)                          (2.3.1)

    Y轴方向上的一阶差分定义为:

    f(i,j+1)-f(i,j)       (2.3.2) 

    利用图像灰度的一阶导数算子在灰度值变化显著的地方得到的极值来检测边缘点。它在某一个点的值就代表了该点的边缘强度值,可通过设置阈值来进一步得到边缘图像。但用差分的方法进行边缘检测必须使差分的方向和边缘的方向相垂直,这就需要对图像的不同方向分别进行差分运算,增加了运算量。一般可将边缘分为水平边缘、垂直边缘和对角线边缘:

    图像算法 - 常用边缘检测算法对比分析 - dingmz_frc - dingmz_frc的博客
    显然,差分边缘是最原始、最基础的方法,这种算子具有方向性,并且由于计算不方便等原因,在现在已经很少应用了,但其思想还是很多其他算法的基础。

    2)Reborts算子

    Reboerts算子是一种利用局部差分来寻找边缘的算子,Roberts 梯度算子所采用的是对角方向相邻两像素值之差,算子形式如下:

    Gx = f(i,j) - f(i-1,j-1)           (2.3.3)
    Gy = f(i-1,j) - f(i,j-1)           (2.3.4)
    |G(x,y)| = sprt(Gx^2-Gy^2)         (2.3.5)

    Roberts梯度算子对应的卷积模版为:

    图像算法 - 常用边缘检测算法对比分析 - dingmz_frc - dingmz_frc的博客
    用以上两个卷积算子与图像运算后,可求出图像的梯度幅值 G ( x,y),然后选择

    适当的阈值τ ,若 G ( x,y)>τ,则 (i  ,j)为边缘点,否则,判断 (i  ,j)为非边缘点。由此得到一个二值图像 { g (i,j)},即边缘图像。Roberts 算子采用的是用对角线方向上相邻两像素的差近似梯度幅值来检测边缘,它的定位精度高,对于水平和垂直方向的边缘,检测效果较好,而对于有一定倾角的斜边缘,检测效果则不理想,存在着许多的漏检。另外,在含噪声的情况下,Roberts 算子不能有效的抑制噪声,容易产生一些伪边缘。因此,该算子适合于对低噪声且具有陡峭边缘的图像提取边缘。

    Roberts算子采用对角线方向相邻两像素之差近似梯度幅值检测边缘。检测水平和垂直边缘的效果好于斜向边缘,定位精度高,对噪声敏感

    3)Sobel算子

    Sobel算子在边缘检测算子扩大了其模版,在边缘检测的同时尽量削弱了噪声。其模版大小为3×3,其将方向差分运算与局部加权平均相结合来提取边缘。在求取图像梯度之前,先进行加权平均,然后进行未分,加强了对噪声的一致。Sobel算子所对应的卷积模版为:

    图像算法 - 常用边缘检测算法对比分析 - dingmz_frc - dingmz_frc的博客
     图像中的每个像素点和以上水平和垂直两个卷积算子做卷积运算后,再计算得到

    梯度幅值 G ( x,y),然后选取适当的阈值τ ,若 G ( x,y)>τ,则 (i  ,j)为边缘点,否则,判断 (i  ,j)为非边缘点。由此得到一个二值图像 { g (i,j)},即边缘图像。Sobel 算子在空间上比较容易实现,不但产生较好的边缘检测效果,同时,由于其引入了局部平均,使其受噪声的影响也较小。若使用较大的邻域,抗噪性会更好,但也增加了计算量,并且得到的边缘比较粗。在对精度要求不是很高的场合下,Sobel 算子是一种较为常用的边缘检测算法。

    ——————————————————————————————————

    转载请注明出处:  http://blog.csdn.net/tianhai110

    索贝尔算子Sobel operator)主要用作边缘检测,在技术上,它是一离散性差分算子,用来运算图像亮度函数的灰度之近似值。在图像的任何一点使用此算子,将会产生对应的灰度矢量或是其法矢量

     

    Sobel卷积因子为:

     

    该算子包含两组3x3的矩阵,分别为横向及纵向,将之与图像作平面卷积,即可分别得出横向及纵向的亮度差分近似值。如果以A代表原始图像,GxGy分别代表经横向及纵向边缘检测的图像灰度值,其公式如下:

     

     

    具体计算如下:

    Gx = (-1)*f(x-1, y-1) + 0*f(x,y-1) + 1*f(x+1,y-1)

          +(-2)*f(x-1,y) + 0*f(x,y)+2*f(x+1,y)

          +(-1)*f(x-1,y+1) + 0*f(x,y+1) + 1*f(x+1,y+1)

    = [f(x+1,y-1)+2*f(x+1,y)+f(x+1,y+1)]-[f(x-1,y-1)+2*f(x-1,y)+f(x-1,y+1)]

     

    Gy =1* f(x-1, y-1) + 2*f(x,y-1)+ 1*f(x+1,y-1)

          +0*f(x-1,y) 0*f(x,y) + 0*f(x+1,y)

          +(-1)*f(x-1,y+1) + (-2)*f(x,y+1) + (-1)*f(x+1, y+1)

    = [f(x-1,y-1) + 2f(x,y-1) + f(x+1,y-1)]-[f(x-1, y+1) + 2*f(x,y+1)+f(x+1,y+1)]

     

    其中f(a,b), 表示图像(a,b)点的灰度值;

     

    图像的每一个像素的横向及纵向灰度值通过以下公式结合,来计算该点灰度的大小:

     

    通常,为了提高效率 使用不开平方的近似值:

     

    如果梯度G大于某一阀值 则认为该点(x,y)为边缘点。

     

    然后可用以下公式计算梯度方向:

     

     

     

    Sobel算子根据像素点上下、左右邻点灰度加权差,在边缘处达到极值这一现象检测边缘。对噪声具有平滑作用,提供较为精确的边缘方向信息,边缘定位精度不够高。当对精度要求不是很高时,是一种较为常用的边缘检测方法。
      

     


    ——————————————————————————


    4)Prewitt 算子

    同 Sobel 算子相似,Prewitt 算子也是一种将方向的差分运算和局部平均相结合的方法,也是取水平和垂直两个卷积核来分别对图像中各个像素点做卷积运算,所不同的是,Sobel 算子是先做加权平均然后再微分,Prewitt 算子是先平均后求微分,其对应的卷积模版为:

    图像算法 - 常用边缘检测算法对比分析 - dingmz_frc - dingmz_frc的博客
    图像中的每个像素点和以上水平和垂直两个卷积算子做卷积运算后,再计算得到
    梯度幅值 G ( x,y),然后选取适当的阈值τ ,若 G ( x,y)>τ,则 (i  ,j)为边缘点,否则,判断 (i  ,j)为非边缘点。由此得到一个二值图像 { g (i,j)},即边缘图像。
    在此基础上,有人提出了改进的Prewitt算子,将其扩展到八个方向,依次用这些边缘模板去检测图像,与被检测区域最为相似的样板给出最大值。用这个最大值作为算子的输出值 P[ i ,j],这样就可将边缘像素检测出来。八个方向的 Prewitt 算子模板及其所对应的边缘方向如下所示:
    Prewitt 算子通过对图像上的每个像素点的八方向邻域的灰度加权差之和来进行检测边缘,对噪声有一定抑制作用,抗噪性较好,但由于采用了局部灰度平均,因此容易检测出伪边缘,并且边缘定位精度较低。
    ——————————————————————————————————————

    附带知识:

    普利维特算子(Prewitt operate) 

    sobel边缘检测外 还有Prewitt算子, 它的卷积因子如下:

     

    其他计算 sobel差不多;

    Prewitt算子利用像素点上下、左右邻点灰度差,在边缘处达到极值检测边缘。对噪声具有平滑作用,定位精度不够高。

     

    罗伯茨交叉边缘检测(Roberts Cross operator

    卷积因子如下:

     

    灰度公式为:

     

    近似公式为:

     

    具体计算如下:

    G(x,y)=abs(f(x,y)-f(x+1,y+1))+abs(f(x,y+1)-f(x+1,y))

     

    灰度方向 计算公式为:

     

    ——————————————————————
    5)Kirsch 算子
    Kirsch 算子是一种 3×3 的非线性方向算子。其基本思想是希望改进取平均值的过程,从而尽量使边缘两侧的像素各自与自己同类的像素取平均值,然后再求平均值之差,来减小由于取平均值所造成的边缘细节丢失。通常采用八方向 Kirsch 模板的方法进行检测,取其中最大的值作为边缘强度,而将与之对应的方向作为边缘方向。常用的八方向 Kirsch 模板如下所示:
    图像算法 - 常用边缘检测算法对比分析 - dingmz_frc - dingmz_frc的博客
    实际的应用中,通常都是利用简单的卷积核来计算方向差分的,不同的算子对应着不同的卷积核。它们在图像的像素点上所产生的两个方向的偏导数用均方值或者绝对值求和的形式来近似代替梯度幅值,然后选取一个合适的阈值,用所得到的梯度幅值和所设定的阈值进行比较来判断边缘点。若大于所取的阈值,则判断为边缘点;否则,判断为非边缘点。很显然,在提取边缘的过程中,阈值的选取特别重要,尤其在含噪图像中,阈值的选择要折衷考虑噪声造成的伪边缘和有效边缘的丢失。
    6)Laplace 算子
    拉普拉斯算子是不依赖于边缘方向的二阶导数算子,它是一个标量而不是向
    量,具有旋转不变即各向同性的性质。若只关心边缘点的位置而不需要了解一其周围的实际灰度差时,一般选择该算子提取图像的边缘。Laplace算子的定义为:
    图像算法 - 常用边缘检测算法对比分析 - dingmz_frc - dingmz_frc的博客
    用差分方程近似二阶偏倒数的结果如下:

    图像算法 - 常用边缘检测算法对比分析 - dingmz_frc - dingmz_frc的博客
     将这两个式子合并,可以得到近似Laplace算子的模版:
    图像算法 - 常用边缘检测算法对比分析 - dingmz_frc - dingmz_frc的博客
    当Laplace算子输出出现过零点时就表明有边缘存在,其中忽略无意义的过零点(均匀零区)。原则上,过零点的位置精度可以通过线性内插方法精确到子像素分辨率。但是拉普拉斯算子在图像边缘检测中并不常用。主要原因有:任何包含有二阶导数的算子比只包含有一阶导数的算子更易受噪声的影响,一阶导数很小的局部峰值也能导致二阶导数过零点,所以Laplace算子对噪声具有无法接受的敏感性; Laplace算子的幅值产生双边元,这是复杂的分割不希望有的结果;最后,Laplace算子不能检测边缘的方向。为了避免噪声的影响,必须采用特别有效的滤波方法。所以,人们提出了改进的功LOG算子。
    7)LOG算子(高斯拉普拉斯算子)
    LOG算子基本思想是:先在一定的范围内做平滑滤波,然后再利用差分算子来检测在相应尺度上的边缘。滤波器的选择要考虑以下两个因素:其一是滤波器在空间上要求平稳,即要求空间位置误差 Δ x要小;其二是平滑滤波器本身要求是带通滤波器,并且在有限的带通内是平稳的,即要求频域误差 Δω 要小。根据信号处理中的测不准原理, Δx 和 Δ ω是相互矛盾的,而达到测不准下限的滤波器就是高斯滤波器。Marr 和 Hildreth 提出的这种差分算子是各向同性的拉普拉斯二阶差分算子。该边缘检测器的基本特征是:
    (1) 所用的平滑滤波器是高斯滤波器
    (2) 增强步骤采用的是二阶导数(即二维拉普拉斯函数)
    (3) 边缘检测的判据是二阶导数过零点并且对应一阶导数的极大值
    该方法的特点是先用高斯滤波器与图像进行卷积,既平滑了图像又降低了噪声,使孤立的噪声点和较小的结构组织被滤除。然而由于对图像的平滑会导致边缘的延展,因此只考虑那些具有局部梯度极大值的点作为边缘点,这可以用二阶导数的零交叉来实现。拉普拉斯函数可用作二维二阶导数的近似,因为它是一种标量算子。为了避免检测出非显著的边缘,所以应该选择一阶导数大于某一阈值的零交叉点来作为边缘点。实际应用中,常用的LOG算子的模版为:
    图像算法 - 常用边缘检测算法对比分析 - dingmz_frc - dingmz_frc的博客
    说明: 高斯平滑运算不但可以滤除噪声,还会导致图像中的边缘和其它尖锐不连续部分模糊,而模糊程度取决于空间尺度因子σ 的大小。σ 越大,高斯滤波对噪声的滤除效果越好,但同时也会丢失重要的边缘信息,影响到边缘检测器的性能。如果σ 较小,又可能导致平滑作用不完全而留有较多的噪声。因此在实际应用中,要根据情况选择适当的σ。
    8) Canny算子
    1986年,Canny从边缘检测算子应该满足的三个准则出发,推导出了最优边
    缘检测算子Canny算子,该算子是目前理论上相对最完善的一种边缘检测算法。
    Canny提出的评价边缘检测性能优劣的三个准则分别是:
    (1)好的信噪比准则。即将非边缘点判为边缘点的概率要低,将边缘点判为非边缘点的概率要低;
    (2)好的定位性能准则。即检测出的边缘点要尽可能在实际边缘的中心;
    (3)单边缘响应准则。即单一边缘具有唯一响应,单一边缘产生的多个响
    应的概率要低,并且对虚假边缘的响应应得到最大抑制。
    利用Canny算子检测边缘的土体算法如下:
    (1)用式所示的高斯函数h(r)对图像进行平滑滤波,去除图像中的噪声。
    (2)在每一点计算出局部梯度和边缘方向,可以利用Sobel算子、Roberts算子等来计算。边缘点定义为梯度方向上其强度局部最大的点。
    (3)对梯度进行“非极大值抑制”。在第二步中确定的边缘点会导致梯度幅度图像中出现脊。然后用算法追踪所有脊的顶部,并将所有不在脊的顶部的像素设为零,以便在输出中给出一条细线。
    (4)双阐值化和边缘连接。脊像素使用两个闽值Tl和竹做阂值处理,其中Tl<T2.值大于竹的脊像素称为强边缘像素,Tl和T2之间的脊像素称为弱边缘像素。由于边缘阵列孔是用高闽值得到的,因此它含有较少的假边缘,但同时也损失了一些有用的边缘信息。而边缘阵列Tl的闽值较低,保留了较多信息。因此,可以以边缘阵列几为基础,用边缘阵列Tl进行补充连接,最后得到边缘图像。
    Canny算子也存在不足之处:
    (1)为了得到较好的边缘检测结果,它通常需要使用较大的滤波尺度,这样容易丢失一些细节
    (2)Canny算子的双阈值要人为的选取,不能够自适应

    实验与结果分析
    实验在MATLAB R2007a平台下进行,实验的结果如下图所示
    图像算法 - 常用边缘检测算法对比分析 - dingmz_frc - dingmz_frc的博客
    图像算法 - 常用边缘检测算法对比分析 - dingmz_frc - dingmz_frc的博客
        
     其他博文参照http://wenku.baidu.com/view/ce3aa547be1e650e52ea9945.html
                             http://wenku.baidu.com/view/d521aee9172ded630b1cb6ed.html


    数字图像处理

    几种边缘检测算子的比较

    边缘检测是图像处理和计算机视觉中的基本问题,边缘检测的目的是标识数字图像中亮度变化明显的点。图像属性中的显著变化通常反映了属性的重要事件和变化。这些包括深度上的不连续、表面方向不连续、物质属性变化和场景照明变化。边缘检测是图像处理和计算机视觉中,尤其是特征提取中的一个研究领域。图像边缘检测大幅度地减少了数据量,并且剔除了可以认为不相关的信息,保留了图像重要的结构属性。有许多方法用于边缘检测,它们的绝大部分可以划分为两类:基于查找一类和基于零穿越的一类。基于查找的方法通过寻找图像一阶导数中的最大和最小值来检测边界,通常是将边界定位在梯度最大的方向。基于零穿越的方法通过寻找图像二阶导数零穿越来寻找边界,通常是Laplacian过零点或者非线性差分表示的过零点。

    人类视觉系统认识目标的过程分为两步:首先,把图像边缘与背景分离出来;然后,才能知觉到图像的细节,辨认出图像的轮廓。计算机视觉正是模仿人类视觉的这个过程。因此在检测物体边缘时,先对其轮廓点进行粗略检测,然后通过链接规则把原来检测到的轮廓点连接起来,同时也检测和连接遗漏的边界点及去除虚假的边界点。图像的边缘是图像的重要特征,是计算机视觉、模式识别等的基础,因此边缘检测是图象处理中一个重要的环节。然而,边缘检测又是图象处理中的一个难题,由于实际景物图像的边缘往往是各种类型的边缘及它们模糊化后结果的组合,且实际图像信号存在着噪声。噪声和边缘都属于高频信号,很难用频带做取舍。

    这就需要边缘检测来进行解决的问题了。边缘检测的基本方法有很多,一阶的有Roberts Cross算子,Prewitt算子,Sobel算子,Canny算子, Krisch算子,罗盘算子;而二阶的还有Marr-Hildreth,在梯度方向的二阶导数过零点。现在就来简单介绍一下各种算子的算法

       1.1   Roberts算子

         Roberts算子是一种利用局部差分算子寻找边缘的算子,它有下式给出:

    x,y)=

    其中、、 分别为4领域的坐标,且是具有整数像素坐标的输人图像;其中的平方根运算使得该处理类似于人类视觉系统中发生的过程。

          Roberts算子是2X2算子模板。图1所示的2个卷积核形成了Roberts算子。图象中的每一个点都用这2个核做卷积。

    1

    0

    0

    -1

    0

    1

    -1

    0

         


    1    Roberts算子

      1.2    Sobel算子

          Sobel算子是一种一阶微分算子,它利用像素邻近区域的梯度值来计算1个像素的梯度,然后根据一定的绝对值来取舍。它由下式给出:

     

    Sobel算子是3*3算子模板。图2所示的2个卷积核dx  dy形成Sobel算子。一个核对通常的垂直边缘响应最大,而另一个核对水平边缘响应最大。2个卷积的最大值作为该点的输出值。运算结果是一幅边缘幅度图像。

    -1

    0

    1

    -2

    0

    2

    -1

    0

    1

    1

    2

    1

    0

    0

    0

    -1

    -2

    -1

    2      Sobel算子

         Prewitt算子

         Prewitt算子由下式给出:

     

    Prewitt算子是3*3算子模板。图3所示的2个卷积核dx ,dy.形成了Prewitt算子。与Sobel算子的方法一样,图像中的每个点都用这2个核进行卷积,取最大值作为输出值。Prewitt算子也产生一幅边缘幅度图像。

    -1

    0

    1

    -1

    0

    1

    -1

    0

    1

    1

    1

    1

    0

    0

    0

    -1

    -1

    -1

      1.3 Canny算子

         Canny算子是是一阶算子。其方法的实质是用1个准高斯函数平滑运算fs=f(x,y)*G(x,y),然后以带方向的一阶微分算子定位导数最大值

         平滑后fs(xy)的梯度可以使用2*2的一阶有限差分近似式:

    P[i,j]≈(fs[i,j+1]-fs[i,j]+fs[i+1,j+1]-fs[i+1,j])/2

    Q[i,j] ≈(fs[i,j]-fs[i+1,j]+fs[i,j+1]-fs[i+1,j+1])/2

    在这个2x2正方形内求有限差分的均值,便于在图像中的同一点计算二和y的偏导数梯度。幅值和方向角可用直角坐标极坐标的坐标转化来计算:

    M[i,j]=

     

    M[i,j]反映了图象的边缘强度;反映了边缘的方向。使得M}i,j}取得局部最大值的方向角,就反映了边缘的方向。

    Canny算子也可用高斯函数的梯度来近似,在理论上很接近4个指数函数的线性组合形成的最佳边缘算子。在实际工作应用中编程较为复杂且运算较慢。

     

    2.1 LOG滤波器

    LOG滤波器又称Marr-Hildreth模板或算子

    =

    式中:G(x,y)是对图像进行处理时选用的平滑函数(Gaussian函数);x,y为整数坐标; σ为高斯分布的均方差。对平滑后的图像fs(fs=f(x,y)*G(x,y))做拉普拉斯变换,得:

    h(x,y)=G(x,y)

    即先对图象平滑,后拉氏变换求二阶微分,等效于把拉氏变化作用于平滑函数,得到1个兼有平滑和二阶微分作用的模板,再与原来的图像进行卷积。用Marr-Hildreth模板与图像进行卷积的优点在于,模板可以预先算出,实际计算可以只进行卷积。

    LOG滤波器有以下特点:

    (1)通过图象平滑,消除了一切尺度小于σ的图像强度变化;

    (2)若用其它微分法,需要计算不同方向的微分,而它无方向性,因此可以节省计算量;

    (3)它定位精度高,边缘连续性好,可以提取对比度较弱的边缘点。

    LOG滤波器也有它的缺点:当边缘的宽度小于算子宽度时,由于过零点的斜坡融合将会丢失细节。

    LOG滤波器有无限长的拖尾,若取得很大尺寸,将使得计算不堪重负。但随着:r=的增加,LOG滤波器幅值迅速下降,当r大于一定程度时,可以忽略模板的作用,这就为节省计算量创造了条件。实际计算时,常常取n* n大小的LOG滤波器,近似n=3σ。另外,LOG滤波器可以近似为两个指数函数之差,即DOG ( Difference Of two Gaussians functions):

    DOG(σ1,σ2)=-

         σ1/σ2=1.6时,DOG代替LOG减少了计算量。

       

    几种算子的比较

    Robert算子定位比较精确,但由于不包括平滑,所以对于噪声比较敏感。

    Prewitt算子和Sobel算子都是一阶的微分算子,而前者是平均滤波,后者是加权平均滤波且检测的图像边缘可能大于2个像素。这两者对灰度渐变低噪声的图像有较好的检测效果,但是对于混合多复杂噪声的图像,处理效果就不理想了。

    LOG滤波器方法通过检测二阶导数过零点来判断边缘点。LOG滤波器中的a正比于低通滤波器的宽度,a越大平滑作用越显著,去除噪声越好,但图像的细节也损失越大,边缘精度也就越低。所以在边缘定位精度和消除噪声级间存在着矛盾,应该根据具体问题对噪声水平和边缘点定位精度要求适当选取。

    讨论和比较了几种常用的边缘检测算子。梯度算子计算简单,但精度不高,只能检测出图像大致的轮廓,而对于比较细的边缘可能会忽略。Prewitt 和Sobel 算子比Roberts 效果要好一些。LOG 滤波器和Canny 算子的检测效果优于梯度算子,能够检测出图像较细的边缘部分。不同的系统,针对不同的环境条件和要求,选择合适的算子来对图像进行边缘检测。

     


    展开全文
  • 图像边缘算法原理

    千次阅读 2018-03-23 19:13:02
    原文地址 ...应用欧几里得距离算法,欧几里得算法是计算m维空间中两个点之间的距离,在图像处理中,就是三维的,其实就是求三维空间中两点之间的距离,然后让这个距离跟一个阀值比较(本例子中阀值=16)...

    原文地址

    图像边缘算法原理
    将当前像素与相邻的下部和右部的像素进行比较,如果与这两个像素点都相似,就将当前的像素设置为黑色,如果与这两个像素点都不相似,就将当前的像素设置为白色,否则为一个中间色。怎么判断像素相似呢?应用欧几里得距离算法,欧几里得算法是计算m维空间中两个点之间的距离,在图像处理中,就是三维的,其实就是求三维空间中两点之间的距离,然后让这个距离跟一个阀值比较(本例子中阀值=16),如果这个像素与下部像素相似并且和右部像素相似,就把这个像素设置成黑色;如果这个像素与下部像素不相似并且和右部像素也不相似,就把这个像素设置成白色;剩余的两种情况就把这个像素设置成灰度值为125。
    三维空间中两点之间的距离公式:
    点A(x1,y1,z1),点B(x2,y2,z2)
    AB^2=(x1-x2)^2+(y1-y2)^2+(z1-z2)^2
    AB就是所求的两点之间的距离。

    直接上代码(python3):

    import cv2
    import numpy as np
    # 得到三维空间中两点之间的距离
    def get_EuclideanDistance(x, y):
        myx = np.array(x)
        myy = np.array(y)
        return np.sqrt(np.sum((myx - myy) * (myx - myy)))
    
    if __name__ == '__main__':
        fn = "E:\\1\\pic\\wallpaper.jpg"
        img = cv2.imread(fn)
        w = img.shape[1]
        h = img.shape[0]
        myimg2 = np.zeros((h, w, 3), np.uint8)
        black = np.array([0, 0, 0])
        white = np.array([255, 255, 255])
        centercolor = np.array([125, 125, 125])
        for y in range(0, h - 1):
            for x in range(0, w - 1):
                mydown = img[y+1, x, :]
                myright = img[y, x+1, :]
                myhere = img[y, x, :]
                if get_EuclideanDistance(myhere, mydown) > 16 and get_EuclideanDistance(myhere, myright) > 16:
                    myimg2[y, x, :] = black
                elif get_EuclideanDistance(myhere, mydown) <= 16 and get_EuclideanDistance(myhere, myright) <= 16:
                    myimg2[y, x, :] = white
                else:
                    myimg2[y, x, :] = centercolor
    
        cv2.imshow('Img', myimg2)
        cv2.imwrite("E:\\1\\pic\\72.jpg", myimg2)
        cv2.waitKey()
        cv2.destroyAllWindows()
    展开全文
  • Canny边缘检测Canny算法介绍Canny算法原理第一步:高斯模糊第二步:Sobel梯度算子第三步:非极大值抑制(NMS)第四步:双阈值连接视频流Canny边缘检测(阈值可调) Canny算法介绍 Canny边缘检测算法是一种多级边缘...
  • 图像处理常用算法(基础)

    万次阅读 多人点赞 2018-08-12 23:15:22
    图像灰度不同,边界处一般会有明显的边缘,利用此特征可以分割图像。需要说明的是:边缘和物体间的边界并不等同,边缘指的是图像中像素的值有突变的地方,而物体间的边界指的是现实场景中的存在于物体之间的边界。...
  • 边缘检测算法——图像处理

    万次阅读 2016-11-02 15:49:01
    1.Sobel边缘检测算法 该算子包含两组3*3的矩阵,分别为图像横向及纵向,将之与图像作平面卷积,即可分别得出横向及纵向的亮度差分近似值。如果以A代表原始图像,Gx及Gy分别代表经横向及纵向边缘检测的图像灰度值,...
  • 在图像识别中,如果可以将图像感兴趣的物体或区别分割出来,无疑可以增加我们图像识别的准确率,传统的数字图像处理中的分割方法多数基于灰度值的两个基本性质 不连续性、 以灰度突变为基础分割一副图像,比如...
  • [图像处理]sobel算子边缘检测算法

    万次阅读 2018-09-16 21:38:05
    边缘检测算法图像处理中最为基本的问题。其目的是标志图像出亮度变化明显的点,从而反映出图像中重要变化。   先介绍一下Sobel算子: Sobel 算子是像素图像边缘检测中最重要的算子之一,该算子包含两组3x3的...
  • 图像边缘提取算法

    千次阅读 2020-05-31 21:36:58
    1一种改进的基于Canny算子的图像边缘提取算法 针对Canny边缘检测算子用高斯函数作为滤波器会造成缓变边缘丢失及假边缘现象,提出用GCV阈值的小波滤波方法代替高斯滤波器来平滑图像,以有效地去除图像中的噪声,然后...
  • 一些基本数字图像处理算法

    千次阅读 2020-05-10 19:45:46
    一些基本数字图像处理算法 所有的图像算法都在DIPAlgorithm类中,并且所有算法都为抽象成员函数。我已经按照java注释规范为所有方法添加使用说明注释,具体实现可见于DIPAlgorithm.java,这里只做算法说明。 1 图像...
  • FPGA图像处理边缘检测算法的实现 1.背景知识 边缘检测是图像处理和计算机视觉中的基本问题,边缘检测的目的是标识数字图像中亮度变化明显的点。图像属性中的显著变化通常反映了属性的重要事件和变化。 这些包括(i...
  • 图像处理算法其实都很简单

    万次阅读 多人点赞 2018-05-03 16:07:12
     线性滤波可以说是图像处理最基本的方法,它可以允许我们对图像进行处理,产生很多不同的效果。做法很简单。首先,我们有一个二维的滤波器矩阵(有个高大上的名字叫卷积核)和一个要处理的二维图像。然后,对于图像...
  • 边缘提取算子 一阶:Roberts算子、Sobel算子、Prewitt算子、Kirsch算子、Robinson算子 二阶: Laplacian算子、Canny算子、Marr-Hildreth(LoG算子) Roberts 算子 在(i+1/2,j+1/2)处差分 转化为模板即为 ...
  • 【数字图像处理基础算法

    千次阅读 2020-09-08 08:34:53
    主要把图像处理的一部分经典基础算法梳理并实现了一遍,主要是用VS2013+OpenCV3.1.0做的。 主要目的:一是为了把基础巩固一下,以便更高效地看论文;二是为了工作基础。 因为以前在学习和研究的过程中发现自己基础...
  • 图像处理基础算法归类

    千次阅读 2013-09-26 23:23:05
    从事图像处理这个行当也已经有很长一段时间,对于一个自动化毕业的本科生,确实是磕磕绊绊的走到现在了,前期的基础图像处理还基本上能够轻松上手,越到后面的机器学习算法,用到的数学知识也就越深了,所以越到后面...
  • 图像处理常用算法总结

    万次阅读 2015-12-02 21:26:41
    5、图像分割(灰度、颜色、频谱特征、纹理特征、空间特征) 6、变换(空域和频域、几何变换、色度变换) 7、几何形态分析(Blob分析) (形状、大小、长度、面积、边缘、圆形度位置、方向、数量、连通性等) 8、匹配...
  • 数字图像处理中常用图像分割算法有哪些? 1.多数的图像分割算法 2.图像边缘分割 3.图像阈值分割 4.基于区域的分割 5.形态学分水岭算法 多数的图像分割算法 均是基于灰度值的不连续和相似的性质。在前者中,...
  • 坚持写下去,虽然简单,但希望可以帮助到别人,有不足之处还望指教目的:对图像采用3种边缘检测算子进行处理,比较处理后的结果,并用边缘增强算法图像增强。一、基本原理1.1图像边缘 图像边缘图像最基本的特征...
  • OpenCV图像边缘检测算法

    千次阅读 2018-07-22 10:53:54
    图像边缘检测的两种方法,第一种使用OpenCV提供的API,第二种手动写一个简单的算法实现.先放原图如下: 1.使用OpenCV提供的方法 import cv2 import numpy as np import random img = cv2.imread('image0.jpg', 1) ...
1 2 3 4 5 ... 20
收藏数 46,184
精华内容 18,473
关键字:

图像处理边缘算法