OpenCV图像处理学习十六,解析图像卷积运算原理并应用Sobel算子,Scharr算子和拉普拉斯算子(Laplance)的应用

一.卷积核的概念

卷积核,通常也叫算子。用一个设定数值模板去处理一张输入图片,进行卷积运算。目的是使目标与目标之间的差距变得更大。卷积在数字图像处理中最常见的应用为锐化和边缘提取。

边缘提取:
当前景目标像素值与周边背景目标的像素值有较大差异时,可以通过卷积核对原图矩阵中的这个位置进行卷积运算,得出的值和该像素点原来的灰度值会产生显著的差异。变化的值超过我们预设的范围后,就可以将图像进行阈值处理,将图像的差异最大化,可以得到了一黑色为背景,白色线条作为边缘或形状的边缘提取效果图。

锐化算子:
通过卷积运算,可以增大矩阵每一个元素与周边元素的反差,起到锐化作用。图像锐化是补偿图像的轮廓,增强图像的边缘及灰度跳变的部分,使图像变得清晰,分为空间域处理和频域处理两类。图像锐化是为了突出图像上地物的边缘、轮廓,或某些线性目标要素的特征,也被称为边缘增强。

OpenCV图像处理学习十六,解析图像卷积运算原理并应用Sobel算子,Scharr算子和拉普拉斯算子(Laplance)的应用_第1张图片对图像求它的一阶导数
delta = f(x) – f(x-1)delta越大,说明像素在X方向变化越大,边缘信号越强

=========================================================================

二.图像卷积的运算原理

卷积核作为一个设定数值模板矩阵,输入的待处理的图像作为原型矩阵,二者进行图像卷积运算,主要是使模板矩阵的中心像素点(称之为锚点)覆盖在待计算原型矩阵元素上面,中心像素点逐一对齐原型矩阵上的像素点(边缘像素无法对齐锚点,也就无法进行卷积计算,只能通过边缘像素处理后才能进行卷积),然后计算元素值与被覆盖的卷积核中的值的乘积和。将这个和赋值给当前锚点,这就是卷积的运算过程。 ​​​

--------------------------------------------------------------------------------------------------------------------------------

第一类:Sobel算子

Sobel算子的概念
Sobel算子是离散微分算子(discrete differentiation operator),用来计算图像灰度的近似梯度。sobel算子由两个3X3的卷积核构成,分别用于计算中心像素邻域的灰度加权差。分为垂直方向和水平方向的索伯滤波器Gx 和Gy。Soble算子功能集合高斯平滑和微分求导,又被称为一阶微分算子,求导算子,在水平和垂直两个方向上求导,得到图像X方法与Y方向梯度图像,如下图。
OpenCV图像处理学习十六,解析图像卷积运算原理并应用Sobel算子,Scharr算子和拉普拉斯算子(Laplance)的应用_第2张图片参数说明:

#参数说明:
I 代表输入图像产生的图像矩阵,
Gx及Gy 分别代表经横向及纵向边缘检测的图像灰度值
G 表示图像的每一个像素的横向及纵向灰度值,有两种计算方式,通常Gx和Gy开方运算比较复杂

-------------------------------------------------------------------------------------------------------------------

Sobel算子的卷积计算过程
为计算图像x方向上的梯度图像,我们需要一个卷积核kernel(Gx)和3*3的像素图片矩阵P。

#卷积核kernel和图像 P 
Mat kernel = (Mat_(3, 3) << -1,0,+1,-2,0,+2,-1,0,+1);
Mat P       = (Mat_(3, 3) <

以卷积核模板的中心像素为锚点,将卷积核与图像上像素值一一对应进行像素遍历,卷积核上的数字相当于加权系数。利用如下公式即可计算出卷积核中心的x方向梯度。

OpenCV图像处理学习十六,解析图像卷积运算原理并应用Sobel算子,Scharr算子和拉普拉斯算子(Laplance)的应用_第3张图片

卷积计算过程为:P5 = (P3-P1)+2*(P6-P4)+(P9-P7)

OpenCV图像处理学习十六,解析图像卷积运算原理并应用Sobel算子,Scharr算子和拉普拉斯算子(Laplance)的应用_第4张图片

同样的原理,在Gy方向上的梯度计算也可以求取
卷积计算过程为:P5 = (P7-P1)+2*(P8-P2)+(P9-P3),这样就得到了垂直方向和水平方向的图像梯度Gx 和Gy,就可以求出总的图像梯度,通常取|Gx| + |Gy|的和作为总的图像梯度。

---------------------------------------------------------------------------------------------------------------------------------

Sobel算子API函数接口:

#函数API接口:

cv::Sobel (
InputArray Src // 输入图像
OutputArray dst// 输出图像,大小与输入图像一致
int depth // 输出图像深度. 
Int dx.  // X方向,几阶导数
int dy // Y方向,几阶导数. 
int ksize, SOBEL算子kernel大小,必须是1、3、5、7、
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。

enum BorderTypes {
    BORDER_CONSTANT    = 0, //使用指定像素值来填充边缘
    BORDER_REPLICATE   = 1, //用已知的边缘像素值来填充边缘
    BORDER_REFLECT     = 2, //使用已知的边缘像素值来反转填充边缘
    BORDER_WRAP        = 3, //用另外一边的像素来补偿填充
    BORDER_REFLECT_101 = 4, //使用已知的边缘像素值来反转填充边缘
    BORDER_TRANSPARENT = 5, //使用黑色进行填充,本质上就是填充0
 
    BORDER_REFLECT101  = BORDER_REFLECT_101,
    BORDER_DEFAULT     = BORDER_REFLECT_101, 
    BORDER_ISOLATED    = 16 //

--------------------------------------------------------------------------------------------------------------------------------

注意知识点:

1.图像在经过处理后,需要用cv::函数将其转回原来的uint8形式,否则将无法显示图像,而只是一副灰色的窗口。 

convertScaleAbs()函数原型:

void cv::convertScaleAbs(
2     cv::InputArray src, // 输入数组
3     cv::OutputArray dst, // 输出数组
4     double alpha = 1.0, // 乘数因子
5     double beta = 0.0 // 偏移量
6 );
   
      //结果返回uint8类型的图片

功能:实现将原图片转换为uint8类型

2.由于Sobel算子是在X轴,Y轴两个方向计算的,还需要用cv2.addWeighted()函数将其组合起来 

函数原型:

CV_EXPORTS_W void addWeighted(InputArray src1, double alpha, InputArray src2,
 
double beta, double gamma, OutputArray dst, int dtype=-1);

src1: 第一幅输入图像 
alpha: 线性混合时第一幅图像的权重 
src2: 第二幅输入图像 
beta: 第二幅输入图像的权重 
dst: 图像线性混合后的目标图像 
gamma: 添加到每一个线性叠加总和的gamma值 
dtype: 目标图像深度,当两幅图像深度相同时可以将dtype置为-1,这样目标图像的深度将与输入图像相同

其中beta - (1.0 - alpha);
对于每个像素点其计算公式如下:
dst = a×src1+b×src2+r

功能:实现以不同的权重将两幅图片叠加,对于不同的权重,叠加后的图像会有不同的透明度

=========================================================================

第二类:Scharr算子

Scharr算子比Sobel算子的值更大,是在Sorbel算子基础上改进的,因此对于灰度变化更为敏感,会得到较强的边缘强度,但是也会损失一些细节。

cv::Scharr (
InputArray Src // 输入图像
OutputArray dst// 输出图像,大小与输入图像一致
int depth // 输出图像深度. 
Int dx.  // X方向,几阶导数
int dy // Y方向,几阶导数. 
double scale  = 1
double delta = 0
int borderType = BORDER_DEFAULT
)

第一个参数,InputArray 类型的src,为输入图像,填Mat类型即可。
第二个参数,OutputArray类型的dst,即目标图像,函数的输出参数,要求与源图片有一样的尺寸和类型。
第三个参数,int类型的ddepth,输出图像的深度,目标图像的深度必须大于原图像的深度,支持如下src.depth()和ddepth的组合:
    BORDER_CONSTANT    = 0, //使用指定像素值来填充边缘
    BORDER_REPLICATE   = 1, //用已知的边缘像素值来填充边缘
    BORDER_REFLECT     = 2, //使用已知的边缘像素值来反转填充边缘
    BORDER_WRAP        = 3, //用另外一边的像素来补偿填充
    BORDER_REFLECT_101 = 4, //使用已知的边缘像素值来反转填充边缘
    BORDER_TRANSPARENT = 5, //使用黑色进行填充,本质上就是填充0


第四个参数,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。

=========================================================================

第三类:拉普拉斯(Laplance)算子

拉普拉斯算子原理

拉普拉斯算子(Laplance operator) 边缘提取的数学依据:在二阶导数的时候,最大变化处的值为零即边缘是零值。计算图像二阶导数,可以用来梯度计算、提取边缘、检测边缘。

OpenCV图像处理学习十六,解析图像卷积运算原理并应用Sobel算子,Scharr算子和拉普拉斯算子(Laplance)的应用_第5张图片

Laplacian提取边缘流程

1)高斯模糊 – 去噪声GaussianBlur()
GaussianBlur(src, dst, Size(3, 3), 0, 0); //高斯模糊
2)转灰度 - cvtColor()
cvtColor(dst, gray_src, CV_BGR2GRAY); //转灰度
3)拉普拉斯 – 二阶导数计算Laplacian()
Laplacian(gray_src, edge_image, CV_16S, 3); //Laplacian算子
4)取绝对值 - convertScaleAbs() - 此处即可得到边缘图像
convertScaleAbs(edge_image, edge_image); //取绝对值
5)再二值化阈值处理 - 增强边缘特征threshold() - 边缘图像更明显
threshold(edge_image, edge_image, 0, 255, THRESH_BINARY | THRESH_OTSU);//自动计算二值化otsu阈值,忽略输入的阈值

--------------------------------------------------------------------------------------------------------------------------------

Laplacian算子API函数接口

void Laplacian(InputArray src, OutputArray dst, int ddepth, int ksize=1, double scale=1, double delta=0, int borderType=BORDER_DEFAULT )
参数说明:

前两个是必须的参数:
第一个参数是输入的原图像,Mat类型的图片;
第二个参数是图像的深度,-1表示与原图像相同深度。目标图像深度必须大于等于原图像深度;

其后是可选的参数:
第三个参数dst目标图像,Mat类型的图片;
第四个参数ksize是算子的大小,必须为1、3、5、7。默认为1。
第五个参数scale是缩放导数的比例常数,默认无伸缩系数;
第六个参数delta是可选增量,会加到最终的dst中,默认情况下无额外的值加dst;
第七个参数borderType是判断图像边界模式。缺省cv.BORDER_DEFAULT。

=========================================================================

代码实现:

#include"stdafx.h"
#include
#include
#include
using namespace std;
using namespace cv;

int main(int argc, char**argv)
{
	Mat src, gray_src, dst, dst2;
	src = imread("F:/photo/qx.jpg");
	if (!src.data) {
		printf("can naot load the image ...\n");
		return -1;
	}
	char input_title[] = "input_picture";
	char output_title[] = "sobel demo2";
	namedWindow(input_title, WINDOW_AUTOSIZE);
	imshow(input_title, src);//输出原图像

	GaussianBlur(src, dst, Size(3, 3), 0, 0);//高斯模糊
	cvtColor(dst, gray_src, COLOR_BGR2GRAY);//输出灰度图
	imshow("gray_picture", gray_src);

	Mat xgrad, ygrad;
	Sobel(gray_src, xgrad, CV_32F, 1, 0,3);//x方向
	Sobel(gray_src, ygrad, CV_32F, 0, 1,3);//y方向

	
	
	Mat xygrad_0;
	convertScaleAbs(xgrad, xgrad);//取绝对值
	convertScaleAbs(ygrad, ygrad);
	imshow("xgrad", xgrad);
	imshow("ygrad", ygrad);        //叠加图像输出


	addWeighted(xgrad, 0.5, ygrad, 0.5, 0, xygrad_0,-1);
	imshow("sobel_addweight", xygrad_0);
	imwrite("sobel_demo1.jpg", xygrad_0);
	

	
	//手动实现融合
	Mat xygrad_2 = Mat(xgrad.size(), xgrad.type());
	int width = xgrad.cols;
	int height = ygrad.rows;
	for (int row = 0; row < height; row++) {
		for (int col = 0; col (row, col);
			int yg = ygrad.at(row, col);
			int xy = xg + yg;
			xygrad_2.at(row, col) = saturate_cast(xy);
		}
	}
	imshow(output_title, xygrad_2);
	imwrite("sobel_demo2.jpg", xygrad_2);
	

	Mat xgrad_1, ygrad_1;
	Scharr(gray_src, xgrad_1, CV_32F, 1, 0);
	Scharr(gray_src, ygrad_1, CV_32F, 0, 1);
	convertScaleAbs(xgrad_1, xgrad_1);//取绝对值
	convertScaleAbs(ygrad_1, ygrad_1);
	addWeighted(xgrad_1, 0.5, ygrad_1, 0.5, 0, dst2, -1);
	imshow("scharr_demo", dst2);
	imwrite("scharr_demo.jpg", dst2);
	imshow("schaar_xaddweight", xgrad_1);
	imshow("schaar_yaddweight", ygrad_1);



	waitKey(0);
	return 0;
}



图像处理效果:

Sorbel算子图像边缘提取部分

输入原图和灰度图 

OpenCV图像处理学习十六,解析图像卷积运算原理并应用Sobel算子,Scharr算子和拉普拉斯算子(Laplance)的应用_第6张图片

 Sorbel算子在X方向的梯度和Y方向的梯度OpenCV图像处理学习十六,解析图像卷积运算原理并应用Sobel算子,Scharr算子和拉普拉斯算子(Laplance)的应用_第7张图片

  Sorbel算子在X方向的梯度和Y方向的梯度以及addWeighted()整合后的图像

 手动融合的Sorbel图像的图像梯度效果会比ddWeighted()整合后的图像强很多

=========================================================================

Scharr算子图像边缘提取部分

 Scharr算子图像边缘提取与Sorbel算子图像边缘提取的比较,可以看出, Scharr算子提取的图像比Sorbel算子提取的图像边缘特征强度更高,具有更强的抗干扰的特性。

OpenCV图像处理学习十六,解析图像卷积运算原理并应用Sobel算子,Scharr算子和拉普拉斯算子(Laplance)的应用_第8张图片

 

 

 

你可能感兴趣的:(opencv,图像处理,opencv,图像处理,计算机视觉)