我先粗类的介绍一下拉普拉斯算子就是沿着x方向的二阶导数和沿着y方向的二阶导数之和。
由于图像都是离散的值,所以结果是得到一个简单的线性表达式,如下图所示
拉普拉斯算子
写成滤波器则是{0,1,0,
1,-4,1,
0,1,0}
其他还有几个常用的滤波器{1,1,1,1,-8,1,1,1,1}
以及对他们的取相反数。
拉普拉斯处理结果是得到图片的细节部分,这里我介绍一下我理解的为什么?
因为一阶导数便是数据的变化情况,二阶导数便是数据变化的速度。变化速度越快就会得到数值上越大的滤波结果。所以该滤波结果是得到了图片的细节,再将图片的细节加到原图上,那么结果就是细节部分增强,其余部分基本不变,也就是达到了锐化的效果。
接下来介绍一下实现的具体步骤。
下面是代码
#include<opencv2/opencv.hpp>
#include<iostream>
using namespace std;
using namespace cv;
void toBeOne(Mat&input,Mat&output,int index=0)
{
float max=0,min=0;
output=Mat::zeros(input.rows,input.cols,CV_8UC3);
for(int i=0;i<input.rows;i++)
{
float *ptr=input.ptr<float>(i);
for(int j=0;j<input.cols*3;j++)//图像为3通道
{
if(max<ptr[j])max=ptr[j];
if(min>ptr[j])min=ptr[j];
}
}
//取出图像中的上下限
for(int i=0;i<input.rows;i++)
{
float *ptr=input.ptr<float>(i);
uchar *optr=output.ptr<uchar>(i);
for(int j=0;j<input.cols*3;j++)
{
if(index)
{
if(ptr[j]>1){optr[j]=255;continue;}
else if(ptr[j]<0){optr[j]=0;continue;}
else optr[j]=(uchar)(ptr[j]*255);
}
else
optr[j]=(uchar)((ptr[j]-min)/(max-min)*255);
}
}
}
int main(int a,char**p)
{
Mat iinput=imread(p[1]),input,Tlaplas;
iinput.convertTo(input,CV_32F,1.0/255,0);//把图片转化为float类型,这样子可以直接进行加减而不会溢出
Mat kern=(Mat_<char>(3,3)<<1,1,1,1,-8,1,1,1,1);//滤波器
Mat laplas;
Mat output;
filter2D(input,laplas,input.depth(),kern);//使用滤波器kern对input进行相关操作,结果存储在laplas中
toBeOne(laplas,Tlaplas);//自己定义的归一函数,把float类型的laplas拉伸到类型为uchar的Tlaplas中
output=input-laplas;//如果中间的值是正的则是加号,负值则是减号
toBeOne(output,iinput,1);//把float类型的结果转化为uchar类型,把超出范围的直接裁剪
imshow("output_float",output);
imshow("input",input);
imshow("laplas",laplas);
imshow("Tlaplas",Tlaplas);
imshow("output_uchar",iinput);
waitKey();
return 0;
}
输入
输出结果