欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

OpenCv--边缘检测(Sobel,拉普拉斯算子)

程序员文章站 2022-03-22 18:05:28
...

边缘

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

图像强度的显著变化可分为:

  • 阶跃变化函数,即图像强度在不连续处的两边的像素灰度值有着显著的差异;
  • 线条(屋顶)变化函数,即图像强度突然从一个值变化到另一个值,保持一较小行程后又回到原来的值。

图像的边缘有方向和幅度两个属性,沿边缘方向像素变化平缓,垂直于边缘方向像素变化剧烈.边缘上的这种变化可以用微分算子检测出来,通常用一阶或二阶导数来检测边缘。

OpenCv--边缘检测(Sobel,拉普拉斯算子)

(a)(b)分别是阶跃函数和屋顶函数的二维图像;(c)(d)是阶跃和屋顶函数的函数图象;(e)(f)对应一阶倒数;(g)(h)是二阶倒数。

Sobel算子

  • 离散微分算子,用来计算图像灰度的近似梯度
  • 功能集合高斯平滑和微分求导
  • 又称为一阶微分算子,求导算子,在水平和垂直方向上求导,得到图像x方向和y方向的方向梯度图像

sobel算子的表示:

OpenCv--边缘检测(Sobel,拉普拉斯算子)

梯度幅值:

1.平方开方形式

2.

OpenCv--边缘检测(Sobel,拉普拉斯算子)

用卷积模板来实现:

OpenCv--边缘检测(Sobel,拉普拉斯算子)

//Sobel_x 计算横向的梯度,  应用:检验纵向边缘,计算法线的横向偏移;
//Sobel_y 计算纵向的梯度,  应用:检验横向边缘,计算法线的纵向偏移;
	Mat h_kernel = (Mat_<int>(3, 3) << -1,0,1,-2,0,2,-1,0,1);
	filter2D(src1,dst,-1,h_kernel,Point(-1,-1));

	Mat z_kernel = (Mat_<int>(3, 3) << -1, -2, -1, 0, 0, 0, 1, 2, 1);
	filter2D(src1, src2, -1, z_kernel, Point(-1, -1));

api

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:输出的边缘检测结果图像,Mat型,大小与原图像相同。 
int 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

注:ddepth =-1时,代表输出图像与输入图像相同的深度。 


int dx:int类型dx,x 方向上的差分阶数,1或0 
int dy:int类型dy,y 方向上的差分阶数,1或0 
其中,dx=1,dy=0,表示计算X方向的导数,检测出的是垂直方向上的边缘;dx=0,dy=1,表示计算Y方向的导数,检测出的是水平方向上的边缘。 
int ksize:为进行边缘检测时的模板大小为ksize*ksize,取值为1、3、5和7,其中默认值为3。特殊情况:ksize=1时,采用的模板为3*1或1*3。 

double scale:默认1。 
double delta:默认0。 
int borderType:默认值为BORDER_DEFAULT。

当ksize=3时,Sobel内核可能产生比较明显的误差,此时,可以使用 Scharr 函数,该函数仅作用于大小为3的内核。具有跟sobel一样的速度,但结果更精确,其内核为: 
OpenCv--边缘检测(Sobel,拉普拉斯算子) 
其调用格式为: 

Scharr( src_gray, grad_x, ddepth, 1, 0, 1, 0, BORDER_DEFAULT ); 
Scharr( src_gray, grad_y, ddepth, 0, 1, 1, 0, BORDER_DEFAULT ); 

LapLace 拉普拉斯算子

拉普拉斯对噪声敏感,会产生双边效果。不能检测出边的方向。通常不直接用于边的检测,只起辅助的角色,检测一个像素是在边的亮的一边还是暗的一边利用零跨越,确定边的位置。

二维函数f(x,y)的拉普拉斯是一个二阶的微分,定义为:

OpenCv--边缘检测(Sobel,拉普拉斯算子)

其中:

OpenCv--边缘检测(Sobel,拉普拉斯算子)

可以用多种方式将其表示为数字形式。对于一个3*3的区域,经验上被推荐最多的形式是:

OpenCv--边缘检测(Sobel,拉普拉斯算子)

定义数字形式的拉普拉斯要求系数之和必为0

OpenCv--边缘检测(Sobel,拉普拉斯算子)

 

拉普拉斯是用二阶差分计算边缘的,看连续函数的情况下
在一阶微分图中极大值或极小值处,认为是边缘。
在二阶微分图中极大值和极小值之间的过 0 点,被认为是边缘。

拉普拉斯算子推导:
一阶差分:f '(x) = f(x) - f(x - 1)
二阶差分:f '(x) = (f(x + 1) - f(x)) - (f(x) - f(x - 1))
化简后:f '(x) = f(x - 1) - 2 f(x)) + f(x + 1)
提取前面的系数:[1, -2, 1]

二维的情况下,同理可得
f '(x, y) = -4 f(x, y) + f(x-1, y) + f(x+1, y) + f(x, y-1) + f(x, y+1)
提取各个系数,写成模板的形式

0,  1, 0
1, -4, 1
0,  1, 0

考虑两个斜对角的情况

1,  1, 1
1, -8, 1
1,  1, 1

这就是拉普拉斯算子,与原图卷积运算即可求出边缘。

//拉普拉斯算子  
	Mat lpls = (Mat_<int>(3,3) << 0,-1,0,-1,4,-1,0,-1,0);
	filter2D(src1,src3,-1,lpls,Point(-1,-1));

API

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

参数详解:

  1. src_gray,输入图像
  2. dst,Laplace操作结果
  3. ddepth,输出图像深度,因为输入图像一般为CV_8U,为了避免数据溢出,输出图像深度应该设置为CV_16S
  4. kernel_size,filter mask的规模,我们的mask时3x3的,所以这里应该设置为3
  5. scale,delta,BORDER_DEFAULT,默认设置就好

Sobel求边缘实例演示

  • 高斯平滑
  • 转灰度
  • 求梯度x,y
  • 综合
#include<iostream>
#include<opencv2/opencv.hpp>
using namespace std;
using namespace cv;
Mat src1, src2;
int main()
{
	src1 = imread("C:\\Users\\马迎伟\\Desktop\\heibao.jpg");
	if (src1.empty())
	{
		cout << "could not find src1" << endl;
		return -1;
	}
	namedWindow("input", CV_WINDOW_AUTOSIZE);
	imshow("input", src1);
	//Sobel求边缘 : 高斯平滑-->转灰度-->求梯度x,y-->综合
	GaussianBlur(src1,src2,Size(3,3),0,0);
	Mat gray_img,xg,yg;
	cvtColor(src2,gray_img,CV_BGR2GRAY);
	imshow("gray image",gray_img);
	//Sobel(gray_img,xg,CV_16S,1,0,3);
	Scharr(gray_img, xg, CV_16S, 1, 0, 3);
	//至关重要的一步,将负值绝对值,使得特征得以显示出来,否则会被置为0;
	//应该是根号下平方相加     ----    改成绝对值相加
	convertScaleAbs(xg,xg);
	imshow("xg",xg);
	//Sobel(gray_img,yg,CV_16S,0,1,3);
	Scharr(gray_img, yg, CV_16S, 0, 1, 3);   //边缘提取较多
	convertScaleAbs(yg, yg);
	imshow("yg",yg);
	//将 梯度按一定比例融合
	//addWeighted(xg,0.5,yg,0.5,0,dst);
	//效果比较好
	Mat dst = Mat(gray_img.size(),gray_img.type());
	printf("%d",dst.type());
	for (int row = 0; row < src1.rows; row++)
	{
		for (int col = 0; col < src1.cols; col++)
		{
			int x = xg.at<uchar>(row, col);
			int y = yg.at<uchar>(row, col);
			dst.at<uchar>(row,col) = x + y;    //类型不一样容易产生截断效应  0是8U_CHAR类型
		}
	}
	imshow("output",dst);
	waitKey(0);
	return 0;
}

Laplance算子实例

#include<iostream>
#include<opencv2/opencv.hpp>
using namespace std;
using namespace cv;
Mat src1, src2;
int main()
{
	src1 = imread("C:\\Users\\马迎伟\\Desktop\\heibao.jpg");
	if (src1.empty())
	{
		cout << "could not find src1" << endl;
		return -1;
	}
	namedWindow("input", CV_WINDOW_AUTOSIZE);
	imshow("input", src1);
	//Laplacian求边缘 : 高斯平滑-->转灰度-->拉普拉斯求二阶梯度-->取绝对值
	GaussianBlur(src1, src2, Size(3, 3), 0, 0);
	Mat gray_img,abs,dst;
	cvtColor(src2, gray_img, CV_BGR2GRAY);
	imshow("gray image", gray_img);
	Laplacian(gray_img, abs,CV_16S);
	convertScaleAbs(abs,dst);
	//取二值化,使结果表达更清晰
	threshold(dst,dst,0,255,THRESH_OTSU | THRESH_BINARY);
	imshow("output", dst);
	waitKey(0);
	return 0;
}

 

相关标签: opencv