图像的平滑处理,也叫模糊是图像处理经常用到的方法。
平滑处理时需要用到一个 滤波器 。 最常用的滤波器是 线性 滤波器,线性滤波处理的输出像素值 g(i,j)是输入像素 f(i+k,j+l)的加权和 :
g(i,j) = sum_{k,l} f(i+k, j+l) h(k,l)
h(k,l) 称为 核, 它仅仅是一个加权系数。
不妨把 滤波器 想象成一个包含加权系数的窗口,当使用这个滤波器平滑处理图像时,就把这个窗口滑过图像。
其实不仅在图像处理,还有语音识别、声纹识别等诸多技术都有用到filter这个东西,本质都是尽量的过滤噪声信号的干扰,方便后续处理。看下sample code:
#include <opencv2/opencv.hpp>
using namespace std;
using namespace cv;
/// Global Variables
int DELAY_CAPTION = 1500;
int DELAY_BLUR = 100;
int MAX_KERNEL_LENGTH = 31;
Mat src; Mat dst;
char window_name[] = "Smoothing Demo";
/// Function headers
int display_caption( const char* caption );
int display_dst( int delay );
int main( void )
{
namedWindow( window_name, WINDOW_AUTOSIZE );
src = imread( "./../../../data/lena.jpg", IMREAD_COLOR );
if( display_caption( "Original Image" ) != 0 ) { return 0; }
dst = src.clone();
if( display_dst( DELAY_CAPTION ) != 0 ) { return 0; }
/// Applying Homogeneous blur
if( display_caption( "Homogeneous Blur" ) != 0 ) { return 0; }
for ( int i = 1; i < MAX_KERNEL_LENGTH; i = i + 2 )
{
blur( src, dst, Size( i, i ), Point(-1,-1) );
if( display_dst( DELAY_BLUR ) != 0 )
return 0;
}
/// Applying Gaussian blur
if( display_caption( "Gaussian Blur" ) != 0 ) { return 0; }
for ( int i = 1; i < MAX_KERNEL_LENGTH; i = i + 2 )
{
GaussianBlur( src, dst, Size( i, i ), 0, 0 );
if( display_dst( DELAY_BLUR ) != 0 )
return 0;
}
/// Applying Median blur
if( display_caption( "Median Blur" ) != 0 ) { return 0; }
for ( int i = 1; i < MAX_KERNEL_LENGTH; i = i + 2 )
{
medianBlur ( src, dst, i );
if( display_dst( DELAY_BLUR ) != 0 )
return 0;
}
/// Applying Bilateral Filter
if( display_caption( "Bilateral Blur" ) != 0 ) { return 0; }
for ( int i = 1; i < MAX_KERNEL_LENGTH; i = i + 2 )
{
bilateralFilter ( src, dst, i, i*2, i/2 );
if( display_dst( DELAY_BLUR ) != 0 )
return 0;
}
display_caption( "End: Press a key!" );
waitKey(0);
return 0;
}
/**
* @function display_caption
*/
int display_caption( const char* caption )
{
dst = Mat::zeros( src.size(), src.type() );
putText( dst, caption,
Point( src.cols/4, src.rows/2),
FONT_HERSHEY_COMPLEX, 1, Scalar(255, 255, 255) );
imshow( window_name, dst );
int c = waitKey( DELAY_CAPTION );
if( c >= 0 ) { return -1; }
return 0;
}
/**
* @function display_dst
*/
int display_dst( int delay )
{
imshow( window_name, dst );
int c = waitKey ( delay );
if( c >= 0 ) { return -1; }
return 0;
}
重点说下用到的4个滤波器:
- Homogeneous blur(归一化滤波):
最简单的滤波器, 输出像素值是核窗口内像素值的 均值 ( 所有像素加权系数相等)
核窗如下
blur这个func需要使用4个参数,其他的滤波也都是一样的,就以这个说下,
src:input
dst:output
size:kernel size
Anchor point:被平滑的点,也叫锚点,如果是(-1,-1),则该锚点处在kenrel center
- GaussianBlur(高斯滤波):
高斯滤波是很有用的滤波器它是将输入数组的每一个像素点与 高斯内核 卷积将卷积和当作输出像素值。
二维高斯函数表达式:
形状就是一维的高斯旋转360度,简单的来说就是Anchor point的像素值受到距离该点近的pixel影响大,受到距离该点远的pixel影响小,这样加权和的pixel值就是该锚点的pixel。
par4:x 方向标准方差, 如果是0则使用内核大小计算得到sigma_x
par5:y 方向标准方差, 如果是0则使用内核大小计算得到sigma_y
- medianBlur (中值滤波):
中值滤波将图像的每个像素用邻域 (以当前像素为中心的正方形区域)像素的 中值 代替
- bilateralFilter(双边滤波):
有些时候上述的滤波器不仅仅削弱了噪声, 连带着把边缘也给磨掉了。 为避免这样的情形 (至少在一定程度上 ),
我们可以使用双边滤波。类似于高斯滤波器,双边滤波器也给每一个邻域像素分配一个加权系数。 这些加权系数包含两个部分, 第一部分加权方式与高斯滤波一样,第二部分的权重则取决于该邻域像素与当前像素的灰度差值。
参数就不说了,可以从前面给的官网上找。
我先现抛出两个问题,大家可以思考一下,
1、kernel size为什么是奇数?
2、为什么这个size越大,图像就越模糊?
效果就懒得贴了,关于图像处理中具体函数怎么用其实不必纠结,我们应该关注的是积木在什么情况下可以用,背后的原理是什么,而不是积木怎么放的问题。