OpenCV学习之路(十四) 图像的边缘检测

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/dashujua/article/details/82348685

目录

Canny 算子

Sobel 算子

Laplacian 算子

scharr 滤波器

简单示例代码:


Sobel 边缘检测

Laplace 边缘检测

Canny 边缘

图像边缘两侧的像素点灰度值发生明显的变化。因此可以通过图像灰度值的一阶导数或二阶导数来进行图像边缘的检测。

Canny 算子

Canny 边缘检测算法被普遍认为是最优的边缘检测算法。其旨在满足下面三个评价标准:

低错误率:检测出尽可能多实际存在的边缘,且尽可能地减少噪声产生的误差。

高定位性:检测出的边缘与图像中实际存在的边缘尽可能接近。

最小响应:每条边缘只检测一次,且可能存在的噪声不应标示为边缘。

Canny 边缘检测算法步骤:

1. 消除噪声。使用高斯滤波来去除噪声。如下图所示:

2. 计算梯度幅值和方向。可以采用 Sobel 滤波器来进行。如下图所示:

 

    

3. 非极大值抑制。排除非边缘像素,仅保留一些细线条(候选边缘)。

4. 滞后阈值。包括高阈值和低阈值。

     若某一像素位置的梯度幅值超过高阈值,该像素被保留为边缘像素。

     若某一像素位置的梯度幅值小于低阈值,该像素被排除。

      若某一像素位置的梯度幅值在两个阈值之间,该像素仅仅在连接到一个高于高阈值的像素时被保留。

通常高低阈值比在 2:1 到 3:1 之间。

Canny() 函数原型:

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

(1)第一个参数,InputArray 类型的 image,源图像。单通道 8 为图像。

(2)第二个参数,OutputArray 类型的 edges,输出图像。和源图像有相同的尺寸和深度。

(3)第三个参数,double 类型的 threshold1,第一个滞后性阈值。

(4)第四个参数,double 类型的 threshold2,第二个滞后性阈值。

(5)第五个参数,int 类型的 apertureSize。Sobel 算子的滤波器孔径大小。默认为3。

(6)第六个参数,bool 类型的 L2gradient,计算图像梯度幅值方式的标识。下方Sobel 算子说明。默认为 绝对值球法。

 

Sobel 算子

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

分别在水平方向和竖直方向上求导。综合两个方向上的微分结果来求出近似梯度值。

Sobel() 函数原型如下:

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);

(1)第一个参数,源图像。

(2)第二个参数,输出图像。

(3)第三个参数,int 类型的 ddepth。满足下列条件:

(4)第四个参数,int 类型的 dx,x方向上的差分阶数。

(5)第五个参数,int 类型的 dy,y方向上的差分阶数。

(6)第六个参数,int 类型的 ksize。滤波器孔径大小。

(7)第七个参数,double 类型的 scale,计算导数时可选的缩放因子。默认为1,表示没有缩放。

(8)第八个参数,double 类型的 delta,表示在结果存入输出图像之前可选的 delta 值,默认为0。

(9)第九个参数,int 类型的 borderType,边界模式。

 

Laplacian 算子

二阶微分算子。数学公式和滤波器如下所示:

        

Laplacian() 函数原型如下所示:

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

(1)第一个参数,源图像。

(2)第二个参数,输出图像。

(3)第三个参数,int 类型的 ddepth。满足下列条件:

(4)第四个参数,int 类型的 ksize。用于计算滤波器孔径大小。(2 * kszie + 1)

(5)第五个参数,double 类型的 scale,计算拉普拉斯值时可选的缩放因子。默认为1,表示没有缩放。

(6)第六个参数,double 类型的 delta,表示在结果存入输出图像之前可选的 delta 值,默认为0。

(7)第七个参数,int 类型的 borderType,边界模式。

  当 ksize = 1时,Laplacian() 函数采用以下 3*3 的孔径:

scharr 滤波器

Scharr() 函数。用来配合 Sobel() 算子的运算。原型如下:

void Scharr(InputArray src, OutputArray dst, int ddepth, int dx, int dy, double scale = 1, double delta = 0, int borderType = BORDER_DEFAULT);	

参考Sobel() 函数的参数说明。

Scharr(src, dst, ddepth, dx, dy, scale, delta, borderType)	

等同于

Sobel(src, dst, ddepth, dx, dy, CV_SCHARR, scale, delta, borderType)

简单示例代码:

#include<opencv2/opencv.hpp>
#include<iostream>

using namespace std;
using namespace cv;

void on_CannyEdges(int, void*);
void on_SobelEdges(int, void*);

Mat srcCannyImage, srcSobelImage, dstCannyImage, dstSobelImage;

int g_kernelSize = 3;
int g_maxKernelSize = 11;
int g_lowThreshold = 90;
int g_maxLowThresh = 100;
int ratio = 3;

// 0:Sobel 算子  1:Laplacian 算子
int g_edgeType = 1;
int g_maxEdgeType = 1;

const char* kSizeStr = "孔径大小";
const char* edgeTypeStr = "算子类型";
const char* lowThreshStr = "滞后阈值 低";
const char* highThreshStr = "滞后阈值 高";

int main()
{
	srcCannyImage = imread("cat.jpg");
	srcSobelImage = imread("cat.jpg");
	imshow("原图", srcCannyImage);

	GaussianBlur(srcCannyImage, srcCannyImage, Size(3, 3), 0);
	GaussianBlur(srcSobelImage, srcSobelImage, Size(3, 3), 0);

	cvtColor(srcCannyImage, srcCannyImage, COLOR_RGB2GRAY);
	cvtColor(srcSobelImage, srcSobelImage, COLOR_RGB2GRAY);

	namedWindow("canny检测效果图", WINDOW_AUTOSIZE);
	namedWindow("Sobel / Laplacian检测效果图", WINDOW_AUTOSIZE);

	createTrackbar(kSizeStr, "canny检测效果图", &g_kernelSize, g_maxKernelSize, on_CannyEdges);
	createTrackbar(lowThreshStr, "canny检测效果图", &g_lowThreshold, g_maxLowThresh, on_CannyEdges);

	createTrackbar(kSizeStr, "Sobel / Laplacian检测效果图", &g_kernelSize, g_maxKernelSize, on_SobelEdges);
	createTrackbar(edgeTypeStr, "Sobel / Laplacian检测效果图", &g_edgeType, g_maxEdgeType, on_SobelEdges);

	on_CannyEdges(0, 0);
	on_SobelEdges(0, 0);

	waitKey(0);
	return 0;

}

void on_CannyEdges(int, void*)
{
	Canny(srcCannyImage, dstCannyImage, g_lowThreshold, ratio * g_lowThreshold, g_kernelSize);

	Mat dst = Mat(srcCannyImage.size(), srcCannyImage.type(), Scalar::all(0));
	srcCannyImage.copyTo(dst, dstCannyImage);

	imshow("canny检测效果图", dstCannyImage);
}

void on_SobelEdges(int, void*)
{

	switch (g_edgeType)
	{
	case 0:
	{
		Mat sobel_x, sobel_y;
		Mat abs_sobel_x, abs_sobel_y;

		Sobel(srcSobelImage, sobel_x, CV_16S, 1, 0, g_kernelSize, 1, 0);
		Sobel(srcSobelImage, sobel_y, CV_16S, 0, 1, g_kernelSize, 1, 0);
		
		convertScaleAbs(sobel_x, abs_sobel_x);
		convertScaleAbs(sobel_y, abs_sobel_y);

		addWeighted(abs_sobel_x, 0.5, abs_sobel_y, 0.5, 0.0, dstSobelImage);

		imshow("Sobel / Laplacian检测效果图", dstSobelImage);
		break;
	}
	case 1:
	{
		Mat abs_laplacian;
		Laplacian(srcSobelImage, dstSobelImage, CV_16S,g_kernelSize, 1, 0);
		convertScaleAbs(dstSobelImage, abs_laplacian);

		imshow("Sobel / Laplacian检测效果图", abs_laplacian);
		break;
	}
	default:
		break;
	}

}

代码运行结果如下:

猜你喜欢

转载自blog.csdn.net/dashujua/article/details/82348685
今日推荐