滤波是信号和图像处理中的一种基本操作。它的目的时选择性地提取图像中某些方面的内容,这些内容在特定应用环境下传达了重要信息。
通过观察图像灰度值变化的频率来描述图像的特征,称为频域;
通过观察图像灰度分布来描述图像特征,称为空域;
有几种著名的变换法可以用来清楚地显示图像地频率成分,例如傅里叶变换或余弦变换。图像是二维的,因此频率分为两种,即垂直频率和水平频率。
在频域分析框架下,滤波器是一种放大图像中某些频段,同时滤掉其他频段的算子。
*低通滤波器
一个简单的方法是把每个像素的值替换成它周围像素的平均值。这样一来,强度的快速变化被消除,代之以更加平滑的过渡。
cv::blur函数将每个像素的值替换成该像素邻域的平均值。
效果:
代码:
int main()
{
cv::Mat image = cv::imread("zifeng.jpg");
cv::imshow("original image", image);
cv::Mat result;
cv::blur(image, result, cv::Size(4, 4));
cv::imshow("mean filtered image", result);
cvWaitKey();
}
代码:
cv::GaussianBlur(image, result, cv::Size(5, 5), 1.5);
cv::imshow("Gaussian filtered image", result);
如果用邻域像素的加权累加值来替换像素值,这种滤波器是
线性的。
1、缩减像素采样
测试图像缩小1/4后(缩小的方式是每4行像素中保留一行),再将每个像素放大4倍得到的结果:
代码:
int main()
{
cv::Mat image = cv::imread("zifeng.jpg");
cv::cvtColor(image, image, CV_BGR2GRAY);
cv::imshow("original image", image);
cv::Mat reduced(image.rows / 4, image.cols / 4,image.type());
for (int i = 0; i < reduced.rows; i++)
for (int j = 0; j < reduced.cols; j++)
reduced.at(i, j) = image.at(i * 4, j * 4);
//缩小的图像放大四倍,采用最邻近插值法
cv::Mat result;
cv::resize(reduced, result, cv::Size(), 4.0, 4.0, cv::INTER_NEAREST);
cv::imshow("badly reduced", result);
cvWaitKey();
}
可以发现,图像的质量明显下降了,看到了锯齿状的变形。这些令人讨厌的伪影是一种叫做空间假频的现象造成的,当试图在图像中包含高频成分但图像太小无法包含时,就会出现这种现象。因此,在缩小图像之前应去除它的高频成分。
效果:
锯齿效果有所改善。这是像素放大4倍的效果,实际缩小效果为:
int main()
{
cv::Mat image = cv::imread("zifeng.jpg");
cv::cvtColor(image, image, CV_BGR2GRAY);
cv::imshow("original image", image);
cv::GaussianBlur(image, image, cv::Size(5,5), 2.0);
cv::Mat reduced(image.rows / 4, image.cols / 4,image.type());
for (int i = 0; i < reduced.rows; i++)
for (int j = 0; j < reduced.cols; j++)
reduced.at(i, j) = image.at(i * 4, j * 4);
cv::Mat result;
cv::resize(reduced, result, cv::Size(), 4, 4, cv::INTER_LINEAR);
cv::imshow("result", result);
cvWaitKey();
}
自带函数同样也是先使用5*5高斯滤波器
*中值滤波器
中值滤波器属于非线性滤波器的一种,典型应用是消除椒盐噪声。
首先,我们为一副图像增加椒盐噪声:
然后进行中值滤波:
代码:
int main()
{
cv::Mat image = cv::imread("zifeng.jpg");
int i, j;
for (int count = 0; count < 3000; count++)
{
i = std::rand() % image.rows;
j = std::rand() % image.cols;
image.at(i, j)[0] = 255;
image.at(i, j)[1] = 255;
image.at(i, j)[2] = 255;
}
cv::imshow("salted image", image);
cv::Mat result;
cv::medianBlur(image, result,5);
cv::imshow("result", result);
cvWaitKey();
}
这属于一种高通滤波器,这里使用的滤波器称为Sobel滤波器。因为其只对垂直或水平方向的图像频率起作用,所以被认为是一种定向滤波器。
这种滤波器可用来实现浮雕化特效:
还可将两个结果组合得到sobel模,阈值化处理后得到图像轮廓的二值化分布图(故此算子称为边缘检测器)
代码:
int main()
{
cv::Mat image = cv::imread("zifeng.jpg");
cv::cvtColor(image, image, CV_BGR2GRAY);
cv::Mat sobelX, sobelY;
cv::Sobel(image, sobelX, CV_8U, 1, 0, 3, 0.4, 128);
cv::imshow("sobelX image", sobelX);
cv::Sobel(image, sobelY, CV_8U, 0, 1, 3, 0.4, 128);
cv::imshow("sobelY image", sobelY);
cv::Mat sobel;
sobel = abs(sobelX) + abs(sobelY);
double sobmin, sobmax;
cv::minMaxLoc(sobel, &sobmin, &sobmax);
cv::Mat sobelImage;
sobel.convertTo(sobelImage, CV_8U, -255. / sobmax, 255);
cv::imshow("sobel image", sobelImage);
cv::threshold(sobelImage, sobelImage, 36, 255, CV_THRESH_BINARY_INV);
cv::imshow("binary sobel image", sobelImage);
cvWaitKey();
}
拉普拉斯算子也是一种基于图像导数运算的高通线性滤波器,它通过计算二阶导数来度量图像函数的曲率。
效果:
代码:
LaplacianZC类:
class LaplacianZC
{
private:
cv::Mat laplace;
int aperture;
public:
LaplacianZC() :aperture(3)
{}
void setAperture(int a)
{
aperture = a;
}
cv::Mat computeLaplacian(const cv::Mat& image)
{
cv::Laplacian(image, laplace, CV_32F, aperture);
return laplace;
}
cv::Mat getLaplacianImage(double scale = -1.0)
{
if (scale < 0)
{
double lapmin, lapmax;
cv::minMaxLoc(laplace, &lapmin, &lapmax);
scale = 127 / std::max(-lapmin, lapmax);
}
cv::Mat laplaceImage;
laplace.convertTo(laplaceImage, CV_8U, scale, 128);
return laplaceImage;
}
};
main函数:
int main()
{
cv::Mat image = cv::imread("zifeng.jpg");
LaplacianZC cLaplacian;
cLaplacian.setAperture(7);
cv::Mat flap = cLaplacian.computeLaplacian(image);
cv::Mat laplaceImage = cLaplacian.getLaplacianImage();
cv::imshow("laplace image", laplaceImage);
cvWaitKey();
}