二阶微分算子-拉普拉斯算子
在前面博客结尾,我们简要谈了一下二阶微分算子;对于图像;
从上面可以看出
一阶微分算子,就是求图像灰度变化曲线的导数,能够突出图像中的对象边缘;
二阶微分算子,求图像灰度变化导数的导数,对图像中灰度变化强烈的地方很敏感,从而可以突出图像的纹理结构
下面介绍几个常见二阶微分算子:
其模板为:
图(a)表示离散拉普拉斯算子的模板,图(b)表示其扩展模板,图(c)(d)则分别表示其他两种拉普拉斯的实现模板。从模板形式容易看出,如果在图像中一个较暗的区域中出现了一个亮点,那么用拉普拉斯运算就会使这个亮点变得更亮。因为图像中的边缘就是那些灰度发生跳变的区域,所以拉普拉斯锐化模板在边缘检测中很有用。一般增强技术对于陡峭的边缘和缓慢变化的边缘很难确定其边缘线的位置。但此算子却可用二次微分正峰和负峰之间的过零点来确定,对孤立点或端点更为敏感,因此特别适用于以突出图像中的孤立点、孤立线或线端点为目的的场合。同梯度算子一样,拉普拉斯算子也会增强图像中的噪声,有时用拉普拉斯算子进行边缘检测时,可将图像先进行平滑处理。
图像锐化处理的作用是使灰度反差增强,从而使模糊图像变得更加清晰。图像模糊的实质就是图像受到平均运算或积分运算,因此可以对图像进行逆运算,如微分运算能够突出图像细节,使图像变得更为清晰。由于拉普拉斯是一种微分算子,它的应用可增强图像中灰度突变的区域,减弱灰度的缓慢变化区域。因此,锐化处理可选择拉普拉斯算子对原图像进行处理,产生描述灰度突变的图像,再将拉普拉斯图像与原始图像叠加而产生锐化图像。拉普拉斯锐化的基本方法可以由下式表示:
这种简单的锐化方法既可以产生拉普拉斯锐化处理的效果,同时又能保留背景信息,将原始图像叠加到拉普拉斯变换的处理结果中去,可以使图像中的各灰度值得到保留,使灰度突变处的对比度得到增强,最终结果是在保留图像背景的前提下,突现出图像中小的细节信息。
下面实现简单的图像锐化,并调用函数实现边缘检测;
#include <opencv2/opencv.hpp> #include <iostream> using namespace cv; using namespace std; //手动实现拉普拉斯算子图像锐化 void sharpenImage1(const Mat &image, Mat &result) { result.create(image.size(), image.type());//为输出图像分配内容 /*拉普拉斯滤波核3*3 0 -1 0 -1 5 -1 0 -1 0 */ //处理除最外围一圈外的所有像素值 for (int i = 1; i<image.rows - 1; i++) { const uchar * pre = image.ptr<const uchar>(i - 1);//前一行 const uchar * cur = image.ptr<const uchar>(i);//当前行,第i行 const uchar * next = image.ptr<const uchar>(i + 1);//下一行 uchar * output = result.ptr<uchar>(i);//输出图像的第i行 int ch = image.channels();//通道个数 int startCol = ch;//每一行的开始处理点 int endCol = (image.cols - 1)* ch;//每一行的处理结束点 for (int j = startCol; j < endCol; j++) { //输出图像的遍历指针与当前行的指针同步递增, 以每行的每一个像素点的每一个通道值为一个递增量, 因为要考虑到图像的通道数 //saturate_cast<uchar>保证结果在uchar范围内 *output++ = saturate_cast<uchar>(5 * cur[j] - pre[j] - next[j] - cur[j - ch] - cur[j + ch]); } } //将最外围一圈的像素值设为0 result.row(0).setTo(Scalar(0)); result.row(result.rows - 1).setTo(Scalar(0)); result.col(0).setTo(Scalar(0)); result.col(result.cols - 1).setTo(Scalar(0)); /*/或者也可以尝试将最外围一圈设置为原图的像素值 image.row(0).copyTo(result.row(0)); image.row(image.rows-1).copyTo(result.row(result.rows-1)); image.col(0).copyTo(result.col(0)); image.col(image.cols-1).copyTo(result.col(result.cols-1));*/ } //调用OpenCV函数实现拉普拉斯算子图像锐化 void sharpenImage2(const Mat &image, Mat &result) { Mat kernel = (Mat_<float>(3, 3) << 0, -1, 0, -1, 5, -1, 0, -1, 0); filter2D(image, result, image.depth(), kernel); } //main函数 void main() { Mat mat = imread("E:\\VS2015Opencv\\vs2015\\project\\picture\\01.jpg"); Mat result1; Mat result2; Mat dst, gray; sharpenImage1(mat, result1); sharpenImage2(mat, result2); //滤波降噪 GaussianBlur(mat, mat, Size(3, 3), 0, 0, BORDER_DEFAULT); //转成灰度图 cvtColor(mat, gray, COLOR_BGR2GRAY); //运行Sobel算子,得到边缘 //求x方向梯度 //Laplacian(src, dst, CV_16S, 3, 1,0,BORDER_DEFAULT); Laplacian(mat, dst, CV_16S);//后几个参数有默认值 //由于是16位图片,需要将图片转化成为8位图形进行显示 convertScaleAbs(dst, dst); imshow("调函数检测边缘", dst); namedWindow("src"); namedWindow("手动实现"); namedWindow("opencv实现"); imshow("src", mat); imshow("手动实现", result1); imshow("opencv实现", result2); waitKey(0); }
简单介绍一下自带函数:
使用拉普拉斯进行图像锐化可参考博文:https://www.cnblogs.com/liu-jun/archive/2012/08/12/2635373.html
在前面,我们谈到拉普莱斯变化有4个模板,我们一一实现,看看具体的情况
#include <opencv2/opencv.hpp> #include <iostream> using namespace cv; using namespace std; //调用OpenV函数实现拉普拉斯算子图像锐化 /*拉普拉斯滤波核3*3 0 -1 0 -1 5 -1 0 -1 0 */ void LA(const Mat &image, Mat &result) { Mat kernel = (Mat_<float>(3, 3) << 0, -1, 0, -1, 5, -1, 0, -1, 0); filter2D(image, result, image.depth(), kernel); } /*拉普拉斯滤波核3*3 0 -1 0 -1 4 -1 0 -1 0 */ void LB(const Mat &image, Mat &result) { Mat kernel = (Mat_<float>(3, 3) << 0, -1, 0, -1, 4, -1, 0, -1, 0); filter2D(image, result, image.depth(), kernel); } /*拉普拉斯滤波核3*3 0 1 0 1 -4 1 0 1 0 */ void LC(const Mat &image, Mat &result) { Mat kernel = (Mat_<float>(3, 3) << 0, 1, 0, 1, -4, 1, 0, 1, 0); filter2D(image, result, image.depth(), kernel); } /*拉普拉斯滤波核3*3 1 1 1 1 -8 1 1 1 1 */ void LD(const Mat &image, Mat &result) { Mat kernel = (Mat_<float>(3, 3) << 1, 1, 1, 1, -8, 1, 1, 1, 1); filter2D(image, result, image.depth(), kernel); } /*拉普拉斯滤波核3*3 -1 -1 -1 -1 8 -1 -1 -1 -1 */ void LE(const Mat &image, Mat &result) { Mat kernel = (Mat_<float>(3, 3) << -1, -1, -1, -1, 8, -1, -1, -1, -1); filter2D(image, result, image.depth(), kernel); } //main函数 void main() { Mat mat = imread("E:\\VS2015Opencv\\vs2015\\project\\picture\\01.jpg"); Mat result1, A1, A2, A3; Mat result2; Mat dst, gray; //滤波降噪 GaussianBlur(mat, mat, Size(3, 3), 0, 0, BORDER_DEFAULT); LA(mat, result1); LB(mat, A1); LC(mat, A2); LD(mat, A3); LE(mat, result2); //转成灰度图 cvtColor(mat, gray, COLOR_BGR2GRAY); //运行Sobel算子,得到边缘 //求x方向梯度 //Laplacian(src, dst, CV_16S, 3, 1,0,BORDER_DEFAULT); Laplacian(mat, dst, CV_16S);//后几个参数有默认值 //由于是16位图片,需要将图片转化成为8位图形进行显示 convertScaleAbs(dst, dst); imshow("调函数检测边缘", dst); namedWindow("src"); imshow("src", mat); imshow("LA", result1); imshow("LB", A1); imshow("LC", A2); imshow("LC", A3); imshow("LD", result2); waitKey(0); }
发现不同的模板,结果也不同。