滤波是信号处理及图像处理中的一个基本操作,目的是选择性地提取图像中被认为传达重要信息的部分。它可以去除图像中的噪声,提取刚兴趣的视觉特征,允许图像重采样,理论基础源于广义的信号与系统理论。我们观察一副图像时,可以看到不同的灰度(或彩色值)在图像中的分布,一些灰度强度变化慢,一些灰度强度变化快。频域就是观察图像中变化的频率所构成的一种描述图像,空间域则是观察灰度分布来描述一幅图像。频域分析按照高频到低频的次序,分解图像到频率内容。低频对应区域的图像强度变化缓慢,高频变换快速,如可用来明确表示频谱的傅里叶(Fourier)变换或余弦(Cosine)变换,图像是二维的因此它包含垂直频率和水平频率。低通(Low-pass)滤波器去除图像中的高频成分,高通(High-pass)滤波器去除了低频成分。
1.概念及原理
(1)cv::blur是一个非常简单的低通滤波器,方案是将每个像素替换为相邻像素的平均值,这样就使快速的强化变换变为平缓的过渡。当滤波器的作用相当于将一个像素替换为相邻像素的加权总和时,我们称它是线性的。矩阵可以表示滤波器的不同权重,矩阵中的每一项都是对应位置的相乘因子,这个矩阵被称为核(Kernel)或掩码(Mask)。对于3x3的箱式滤波器,它对应的核是
应用一个线性滤波器就是沿着图像的每个像素移动一个核,并且把每个对应的像素乘上关联的权重。在数学上这一过程被称作卷积(Convolution)。
(2)cv::GaussianBlur高斯滤波器中,像素的权重和它离开中心像素点距离成正比。一维高斯函数公式
A:使不同权重之和为1的归一化系数。σ(西格玛):数值控制高斯函数的高度,值越大函数越平坦。想应用二维高斯滤波,可以利用高斯滤波器的分离特性先对图像的行应用一维高斯滤波器再对图像的列应用相同的一维高斯滤波器。
2.实验
源码示例
#include
#include
#include
#include
using namespace std;
using namespace cv;
int main(){
Mat image = imread("girl.jpg");
namedWindow("image");
imshow("image", image);
Mat resultBlur;
blur(image, resultBlur, Size(5, 5));
namedWindow("resultBlur");
imshow("resultBlur", resultBlur);
//高斯滤波器
Mat resultGauss;
GaussianBlur(image, resultGauss, Size(5, 5), 1.5);
namedWindow("resultGauss");
imshow("resultGauss", resultGauss);
waitKey(0);
return 0;
}
运行结果
可以看出低通滤波器效果是对图像进行模糊或平滑,因为它减弱了物体边缘处可见的快速变化。
1.概念及原理
(1)中值滤波器无法表示为一个核矩阵,即是非线性的。是将该像素及它的相邻区域组成一组数组,计算这组数组的中值并用中值替换当前的像素值。因此该滤波器在去除椒盐噪点效果甚好,但是椒盐噪点对均值滤波器的影响非常大。
2.实验
使用中值滤波器去除椒盐噪点。
源码示例
#include
#include
#include
#include
using namespace std;
using namespace cv;
void salt(cv::Mat& image, int n){
for (int k = 0; k(j, i) = 255;
}
else{
image.at(j, i)[0] = 255;
image.at(j, i)[1] = 255;
image.at(j, i)[2] = 255;
}
}
}
int main(){
Mat image = imread("girl.jpg");
salt(image, 5000);
namedWindow("image");
imshow("image", image);
//中值滤波
Mat resultMedianBlur;
medianBlur(image, resultMedianBlur, 5);
namedWindow("resultMedianBlur");
imshow("resultMedianBlur", resultMedianBlur);
waitKey(0);
return 0;
}
处理效果
1.概念及原理
(1)使用高通滤波器强调图像中的高频分量进行边缘检测。Sobel运算水平滤波器和垂直滤波器能够生成8位图像(CV_8U),在这种表示中零值对应的灰度值为128,负数表示为较暗的像素,整数表示为较亮的像素。
(2)Sobel算子是一种经典的边缘检测线性滤波器,它基于一个简单的3x3核。
我们把图像看成二维函数,Sobel算子可被认为是图像在垂直和水平方向变化的测量。在数学中称为梯度。被定义为由函数在两个正交方向上的一阶导数组成的二维向量:
由于梯度是一个二维向量,它拥有距离和方向。梯度向量的距离给出变化的幅度,通常使用欧拉距离(L2距离)。
2.实验
使用Sobel滤波器进行边缘检测。
源码示例
#include
#include
#include
#include
using namespace std;
using namespace cv;
int main(){
Mat image = imread("girl.jpg");
namedWindow("image");
imshow("image", image);
//水平滤波器
Mat sobelX;
Mat sobelY;
//8位图像(CV_8U)表示结果
Sobel(image, sobelX, CV_8U, 1, 0, 3, 0.4, 128);
Sobel(image, sobelY, CV_8U, 0, 1, 3, 0.4, 128);
namedWindow("sobelX");
imshow("sobelX", sobelX);
namedWindow("sobelY");
imshow("sobelY", sobelY);
//计算Sobel范式
Sobel(image, sobelX, CV_16S, 1, 0);
Sobel(image, sobelY, CV_16S, 0, 1);
Mat sobelNorm;
sobelNorm = abs(sobelX) + abs(sobelY);
//搜寻sobel极大值
double sobmin, sobmax;
minMaxLoc(sobelNorm, &sobmin, &sobmax);
//变换为8位图像
Mat sobelImage;
sobelNorm.convertTo(sobelImage, CV_8U, -255. / sobmax, 255);
namedWindow("sobelImage");
imshow("sobelImage", sobelImage);
//阈值化得到二值图像
Mat sobelThresholded;
threshold(sobelImage, sobelThresholded, 220, 255, THRESH_BINARY);
namedWindow("sobelThresholded");
imshow("sobelThresholded", sobelThresholded);
waitKey(0);
return 0;
}
实验结果
水平和垂直Sobel运算后的结果
Sobel滤波器范式和通过阈值(选择合适的阈值有难度)化得到的二值图
1.概念及原理
拉普拉斯是另外一只基于图像导数的高通线性滤波器,它是通过计算二阶导数来衡量图像的弯曲度。2D函数的拉普拉斯变换定义的是二阶导数之和。
最简单的形式可以使用3x3核近似描述。
2.实验
源码示例
#include
#include
#include
#include
using namespace std;
using namespace cv;
//拉普拉斯变换
class LaplacianZC{
public:
Mat img; //原图
Mat laplace; //包含Laplacian的32位浮点图像
int aperture;
public:
LaplacianZC() : aperture(3){};
//设置卷积核的大小
void setAperture(int a){
aperture = a;
}
//计算浮点数Laplacian
Mat computeLaplacian(const Mat&image){
//计算Laplacian
Laplacian(image, laplace, CV_32F, aperture);
//保留图像的局部备份(用于零点交叉)
img = image.clone();
return laplace;
}
//返回8位图像存储的Laplacian结果,零点交叉于灰度值128
//如果没有指定scale参数,那么最大值将缩放至强度255
//必须在调用它之前调用computeLaplacian
Mat getLaplacianImage(double scale = -1.0){
if (scale<0)
{
double lapmin, lapmax;
minMaxLoc(laplace, &lapmin, &lapmax);
scale = 127 / max(-lapmin, lapmax);
}
Mat laplaceImage;
laplace.convertTo(laplaceImage, CV_8U, scale, 128);
return laplaceImage;
}
//得到零点交叉的二值图像,如果相邻像素的乘积小于threshold,那么相邻交叉将被忽略
Mat getZeroCrossings(float threshold = 1.0){
//创建迭代器
Mat_::const_iterator it = laplace.begin()+laplace.step1();
Mat_::const_iterator itend = laplace.end();
Mat_::const_iterator itup = laplace.begin();
//初始化为白色的二值图像
Mat binary(laplace.size(), CV_8U, Scalar(255));
Mat_::iterator itout = binary.begin() + binary.step1();
//对输入阈值取反
threshold*= -1.0;
for (; it!=itend;++it,++itup,++itout)
{
//如果相邻像素的乘积为负数,那么符号发生改变
if (*it**(it - 1) < threshold)*itout = 0; //水平方向零点交叉
else if (*it**itup < threshold) *itout = 0; //垂直方向零点交叉
}
return binary;
}
};
int main(){
Mat image = imread("girl.jpg",0);
namedWindow("image");
imshow("image", image);
LaplacianZC laplacian;
//图像7x7拉普拉斯变换
laplacian.setAperture(7);
Mat flap = laplacian.computeLaplacian(image);
/*
laplacian.laplace = laplacian.getLaplacianImage();
namedWindow("laplace");
imshow("laplace", laplacian.laplace);
*/
//零交叉的二值图像
laplacian.laplace = laplacian.getZeroCrossings();
namedWindow("zeroCross");
imshow("zeroCross", laplacian.laplace);
waitKey(0);
return 0;
}
图像的7x7拉普拉斯变换
图像零交叉的二值图像
【OpenCV学习笔记 009】图像滤波 配套的源码下载