opencv学习--opencv内的6种图像边缘检测算法的实现

     如上篇博客所述,图像边缘检测算法主要有Sobel, Scarry, Canny, Laplacian,Prewitt, Marr-Hildresh,现在进行总结

    参考博客及资料 :https://www.cnblogs.com/xinxue/p/5348743.html

                                 https://blog.csdn.net/spw_1201/article/details/53667491

                                https://blog.csdn.net/qq_31531635/article/details/73382603

    1.Sobel算子

      Sobel算子是主要用于边缘检测的离散微分算子,它结合了高斯平滑和微分求导,用于计算图像灰度函数的近似梯度

      假定输入图像矩阵为 I,卷积核大小为 3x3,则水平一阶导数 Gx 和垂直一阶导数 Gy 分别为:

             

      输出的图像矩阵 G 为:

               

opencv中sobel函数的参数如下

void cv::Sobel   (     
    InputArray  src,    // 输入图像
    OutputArray  dst,   // 输出图像
    int      ddepth,    // 输出图像深度,-1 表示等于 src.depth()
    int      dx,        // 水平方向的阶数
    int      dy,        // 垂直方向的阶数
    int     ksize = 3,    // 卷积核的大小,常取 1, 3, 5, 7 等奇数
    double  scale = 1,    // 缩放因子,应用于计算结果
    double  delta = 0,    // 增量数值,应用于计算结果
    int borderType = BORDER_DEFAULT // 边界模式
)

       因为Sobel算子结合了高斯平滑和分化,因此结果会具有更多的抗噪性。

#include<opencv2\opencv.hpp>   
#include<opencv2\highgui\highgui.hpp>

using namespace std;
using namespace cv;

//边缘检测
int main()
{
    Mat img = imread("lol3.jpg");
    
    imshow("原始图", img);

    Mat grad_x, grad_y;
    Mat abs_grad_x, abs_grad_y, dst;

    //求x方向梯度
    Sobel(img, grad_x, CV_16S, 1, 0, 3, 1, 1,BORDER_DEFAULT);
    convertScaleAbs(grad_x, abs_grad_x);
    imshow("x方向soble", abs_grad_x);

    //求y方向梯度
    Sobel(img, grad_y,CV_16S,0, 1,3, 1, 1, BORDER_DEFAULT);
    convertScaleAbs(grad_y,abs_grad_y);
    imshow("y向soble", abs_grad_y);

    //合并梯度
    addWeighted(abs_grad_x, 0.5, abs_grad_y, 0.5, 0, dst);
    imshow("整体方向soble", dst);


    waitKey(0);

}

 

2. Scarry

       Scarry算法实际上是opencv中sobel算子的特殊改进情况。当内核大小为3时,Sobel内核可能产生比较明显的误差,为了解决这一问题,Opencv提供了Scharr函数,但该函数仅作用于大小为3的内核,该函数的运算与Sobel函数一样快,但结果却更加精确。 
      使用Scharr滤波器运算符计算x或y方向的图像差分。其实它的参数变量和Sobel基本上是一样的,除了没有ksize核的大小。 
void Scharr(inputArray,outputArray,int ddepth,int dx,int dy,double scale=1,double delta=0,int borderType=BORDER_DEFAULT) 
*第一个参数,输入图像。 
*第二个参数,输出图像。 
*第三个参数,输出图像深度。 
*第四个参数,x方向上的差分阶数。 
*第五个参数,y方向上的差分阶数。 
*第六个参数,计算导数值时可选的缩放因子,默认值1,表示默认情况下没用应用缩放。 
*第七个参数,表示在结果存入输出图像之前可选的delta值,默认值0。 
*第八个参数,边界模式。

程序套用sobel的即可

3. laplace

    索贝尔算子 (Sobel) 和拉普拉斯算子 (Laplace)  不同之处在于,前者是求一阶导,后者是求二阶导。

     Laplacian算子是n维欧几里德空间中的一个二阶微分算子,定义为梯度grad的散度div。由于Laplacian使用了图像梯度,它内部的代码其实是调用了Sobel算子的。

Laplacian算子是二阶微分算子。凡是二阶算子对噪声都比较敏感,在使用前要进行滤波去噪。

让一幅图像减去它的Laplacian算子可以增强对比度。
void Laplacian(inputArray,outputArray,int ddepth,int ksize=1,double scale=1,double delta=0,int borderType=BORDER_DEFAULT) 
*第一个参数,输入图像,且需为单通道8位图像。 
*第二个参数,输出的边缘图。 
*第三个参数,输出图像的图像深度。 
*第四个参数,用于计算二阶导数的滤波器的孔径尺寸大小,大小必须为正奇数,默认值为1。 
*第五个参数,计算拉普拉斯值的时候可选的比例因子,默认值为1。 
*第六个参数,表示在结果存入目标图之前可选的delta值,默认值为0。 
*第七个参数,边界模式。

对应代码

#include<opencv2\opencv.hpp>   
#include<opencv2\highgui\highgui.hpp>

using namespace std;
using namespace cv;

//边缘检测
int main()
{
    Mat img = imread("lol3.jpg");
    imshow("原始图", img);
    Mat gray, dst,abs_dst;
    //高斯滤波消除噪声
    GaussianBlur(img, img, Size(3, 3), 0, 0, BORDER_DEFAULT);
    //转换为灰度图
    cvtColor(img, gray, COLOR_RGB2GRAY);
    //使用Laplace函数
    //第三个参数:目标图像深度;第四个参数:滤波器孔径尺寸;第五个参数:比例因子;第六个参数:表示结果存入目标图
    Laplacian(gray, dst, CV_16S, 3, 1, 0, BORDER_DEFAULT);
    //计算绝对值,并将结果转为8位
    convertScaleAbs(dst, abs_dst);

    imshow("laplace效果图", abs_dst);

    waitKey(0);

}

 

4 Canny

       Canny算子检测原理是通过图像信号函数的极大值来判定图像的边缘像素点。边缘检测的算法主演是基于图像强度的一阶和二阶微分操作,但导数通常对噪声很敏感,边缘检测算法常常需要根据图像源的数据进行预处理操作,因此必须采用滤波器来改善与噪声有关的边缘检测的性能。在进行Canny算子边缘检测前,应当先对原始数据与高斯模板进行卷积操作,得到的图像与原图像相比有些模糊。通常使用高斯平滑滤波器卷积降噪。

Canny 边缘检测算子,其算法步骤大体如下:

1) 用高斯滤波器对输入图像做平滑处理 (大小为 5x5 的高斯核)

                    

2) 计算图像的梯度强度和角度方向 ( x 和 y 方向上的卷积核)

                

   角度方向近似为四个可能值,即 0, 45, 90, 135

3) 对图像的梯度强度进行非极大抑制

   可看做边缘细化:只有候选边缘点被保留,其余的点被移除

4) 利用双阈值检测和连接边缘

    若候选边缘点大于上阈值,则被保留;小于下阈值,则被舍弃;处于二者之间,须视其所连接的像素点,大于上阈值则被保留,反之舍弃    

    滞后阈值:滞后阈值需要两个阈值(高阈值和低阈值) 
(1)若某一像素位置的幅值超过高阈值,该像素被保留为边缘像素。 
(2)若某一像素位置的幅值小于低阈值,该像素被排除。 
(3)若某一像素位置的幅值在两个阈值之间,该像素仅仅在连接到一个高于高阈值的像素时被保留。 
void Canny(inputArray,outputArray,double threshold1,double threshold2,int apertureSize=3,bool L2gradient=false) 
*第一个参数,输入图像,且需为单通道8位图像。 
*第二个参数,输出的边缘图。 
*第三个参数,第一个滞后性阈值。用于边缘连接。 
*第四个参数,第二个滞后性阈值。用于控制强边缘的初始段,高低阈值比在2:1到3:1之间。 
*第五个参数,表明应用sobel算子的孔径大小,默认值为3。 
*第六个参数,bool类型L2gradient,一个计算图像梯度幅值的标识,默认值false。

#include<opencv2\opencv.hpp>   
#include<opencv2\highgui\highgui.hpp>

using namespace std;
using namespace cv;

//边缘检测
int main()
{
    Mat img = imread("lol3.jpg");
    imshow("原始图", img);
    Mat DstPic, edge, grayImage;

    //创建与src同类型和同大小的矩阵
    DstPic.create(img.size(), img.type());

    //将原始图转化为灰度图
    cvtColor(img, grayImage, COLOR_BGR2GRAY);

    //先使用3*3内核来降噪
    blur(grayImage, edge, Size(3, 3));

    //运行canny算子
    Canny(edge, edge, 3, 9, 3);

    imshow("边缘提取效果", edge);

    waitKey(0);

}

5. prewitt

       Prewitt算子是一阶边缘检测算子,该算子对噪声有抑制作用,不用进行滤波去噪。和Sobel算子相似,都是在图像空间利用两个方向模板与图像进行领域卷积来完成的,分别对水平与垂直方向边缘进行检测。

        Prewitt算子定位精度不如Sobel算子,在真正的使用中,一般不会用到这个算子,效果较差。

6.Marr—Hildreth(LOG)

      Marr—Hildreth算子以高斯函数为平滑算子,结合拉普拉斯算子提取二阶导数的零交叉理论进行边缘检测。效果更好的边缘检测器是LoG算子,它把高斯平滑滤波器和拉普拉斯锐化滤波器结合起来,先平滑掉噪声,再进行边缘检测,所以效果更好。

     LOG边缘检测--Marr-Hildreth边缘检测算法https://blog.csdn.net/bettyshasha/article/details/51757185

     具体的opencv实现

#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <iostream>
using namespace cv;
using namespace std;
void marrEdge(const Mat src, Mat& result, int kerValue, double delta)
{
	//计算LoG算子
	Mat kernel;
	//半径
	int kerLen = kerValue / 2;
	kernel = Mat_<double>(kerValue, kerValue);
	//滑窗
	for (int i = -kerLen; i <= kerLen; i++)
	{
		for (int j = -kerLen; j <= kerLen; j++)
		{
			//生成核因子
			kernel.at<double>(i + kerLen, j + kerLen) = exp(-((pow(j, 2) + pow(i, 2)) / (pow(delta, 2) * 2)))
				*((pow(j, 2) + pow(i, 2) - 2 * pow(delta, 2)) / (2 * pow(delta, 4)));
		}
	}
	//设置输入参数
	int kerOffset = kerValue / 2;
	Mat laplacian = (Mat_<double>(src.rows - kerOffset * 2, src.cols - kerOffset * 2));
	result = Mat::zeros(src.rows - kerOffset * 2, src.cols - kerOffset * 2, src.type());
	double sumLaplacian;
	//遍历计算卷积图像的拉普拉斯算子
	for (int i = kerOffset; i < src.rows - kerOffset; ++i)
	{
		for (int j = kerOffset; j < src.cols - kerOffset; ++j)
		{
			sumLaplacian = 0;
			for (int k = -kerOffset; k <= kerOffset; ++k)
			{
				for (int m = -kerOffset; m <= kerOffset; ++m)
				{
					//计算图像卷积
					sumLaplacian += src.at<uchar>(i + k, j + m)*kernel.at<double>(kerOffset + k, kerOffset + m);
				}
			}
			//生成拉普拉斯结果
			laplacian.at<double>(i - kerOffset, j - kerOffset) = sumLaplacian;
		}
	}
	for (int y = 1; y < result.rows - 1; ++y)
	{
		for (int x = 1; x < result.cols-1; ++x)
		{
			result.at<uchar>(y, x) = 0;
			//领域判定
			if (laplacian.at<double>(y - 1, x)*laplacian.at<double>(y + 1, x) < 0)
			{
				result.at<uchar>(y, x) = 255;
			}
			if (laplacian.at<double>(y, x - 1)*laplacian.at<double>(y, x + 1) < 0)
			{
				result.at<uchar>(y, x) = 255;
			}
			if (laplacian.at<double>(y + 1, x - 1)*laplacian.at<double>(y - 1, x + 1) < 0)
			{
				result.at<uchar>(y, x) = 255;
			}
			if (laplacian.at<double>(y - 1, x - 1)*laplacian.at<double>(y + 1, x + 1) < 0)
			{
				result.at<uchar>(y, x) = 255;
			}
		}
	}
}
int main()
{
	Mat srcImage = imread("D:\\3.jpg");
	if (!srcImage.data)
		return -1;
	Mat edge,srcGray;
	cvtColor(srcImage, srcGray, CV_RGB2GRAY);
	marrEdge(srcGray, edge, 9, 1.6);
	imshow("srcImage", srcImage);
	imshow("edge", edge);
	waitKey(0);
	return 0;
 
}
  • 14
    点赞
  • 202
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值