在图像处理中,高斯滤波一般用高斯模板。除此之外,还有递归高斯滤波、FFT法、重复卷积法等。
原理很简单out = IFFT2(FFT(in)*FFT(h)),其中in是输入图像,h为卷积核,out是平滑后图像。
文献《Recursive implementation of the Gaussian filter》提出该方法,其产生一个IIR滤波器,比上面三种方法都快,其运行时间与σ无关。推导过程大致是,将高斯公式g(t)的傅里叶变换G(w)进行泰勒公式展开,阶数到6取近似,将其拉普拉斯变换G(s),在变换到z域时,用前后差分技术代替双线性法。最终结论如下。
q的选择如下
以上可以认为串行方式,还有并行方式,详细见文献《Recursively implementing the Gaussian and its derivatives》,在ITK中, itkRecursiveGaussianImageFilter实现了该算法。
对图像进行高斯滤波而言,假设滤波器半径为r,我们常用的高斯模板则对于图像每个像素的算法复杂度是O(r^2)。高斯滤波器的kernel是可分离的(separable),也就是说,可以将2D的高斯kernel分解为两个1D的kernel,先沿x方向对图像进行1D高斯kernel的卷积,然后沿y方向对图像进行1D的高斯kernel卷积,最后的结果和使用一个2D高斯kernel对图像卷积效果是一样的。这样一来,针对每个像素,滤波器的算法复杂度降为O(r)。下面实现递归高斯滤波法,即上面第四种方法。完整工程代码。
/**********************************************************************
Copyright (c) Mr.Bin. All rights reserved.
For more information visit: http://blog.csdn.net/webzhuce
**********************************************************************/
#include
using namespace cv;
typedef struct
{
int N;
float sigma;
double B;
double b[4];
} GaussCoefs;
/*
* Calculate the coefficients for the recursive filter algorithm
* Fast Computation of gaussian blurring.
*/
void ComputeGaussCoefs(GaussCoefs *c, float sigma)
{
/*
* Papers: "Recursive Implementation of the gaussian filter.",
* Ian T. Young , Lucas J. Van Vliet, Signal Processing 44, Elsevier 1995.
* formula: 11b computation of q
* 8c computation of b0..b1
* 10 alpha is normalization constant B
*/
float q = 0, q2 = 0, q3 = 0;
if (sigma >= 2.5)
{
q = 0.98711 * sigma - 0.96330;
}
else if ((sigma >= 0.5) && (sigma < 2.5))
{
q = 3.97156 - 4.14554 * (float)sqrt((double)1 - 0.26891 * sigma);
}
else
{
q = 0.1147705018520355224609375;
}
q2 = q * q;
q3 = q * q2;
c->b[0] = (1.57825 + (2.44413*q) + (1.4281 *q2) + (0.422205*q3));
c->b[1] = ((2.44413*q) + (2.85619*q2) + (1.26661 *q3));
c->b[2] = (-((1.4281*q2) + (1.26661 *q3)));
c->b[3] = ((0.422205*q3));
c->B = 1.0 - ((c->b[1] + c->b[2] + c->b[3]) / c->b[0]);
c->sigma = sigma;
c->N = 3;
}
void RecursiveGausssmooth(float *in, float *out, int size, int rowstride, GaussCoefs *c)
{
/*
* Papers: "Recursive Implementation of the gaussian filter.",
* Ian T. Young , Lucas J. Van Vliet, Signal Processing 44, Elsevier 1995.
* formula: 9a forward filter
* 9b backward filter
* fig7 algorithm
*/
int i, n, bufsize;
float *w1, *w2;
/* forward pass */
bufsize = size + 3;
size -= 1;
w1 = new float[bufsize];
w2 = new float[bufsize];
w1[0] = in[0];
w1[1] = in[0];
w1[2] = in[0];
for (i = 0, n = 3; i <= size; i++, n++)
{
w1[n] = (float)(c->B*in[i*rowstride] +
((c->b[1] * w1[n - 1] +
c->b[2] * w1[n - 2] +
c->b[3] * w1[n - 3]) / c->b[0]));
}
/* backward pass */
w2[size + 1] = w1[size + 3];
w2[size + 2] = w1[size + 3];
w2[size + 3] = w1[size + 3];
for (i = size, n = i; i >= 0; i--, n--)
{
w2[n] = out[i * rowstride] = (float)(c->B*w1[n] +
((c->b[1] * w2[n + 1] +
c->b[2] * w2[n + 2] +
c->b[3] * w2[n + 3]) / c->b[0]));
}
delete[] w1;
delete[] w2;
}
/*
@function: 2D gauss filter
*/
void RecursiveGausssmooth2D(const Mat &src, Mat &dst, float sigma = 1.0f)
{
GaussCoefs c;
ComputeGaussCoefs(&c, sigma);
const int height = src.rows;
const int width = src.cols;
if (src.channels() == 1)
{
Mat srccopy;
src.convertTo(srccopy, CV_32FC1);
Mat dstcopy;
src.convertTo(dstcopy, CV_32FC1);
float* srcdata = srccopy.ptr<float>(0);
float* dstdata = dstcopy.ptr<float>(0);
for (int y = 0; y < height; y++)
{
RecursiveGausssmooth(srcdata + y * width, dstdata + y * width, width, 1, &c);
}
for (int x = 0; x < width; x++)
{
RecursiveGausssmooth(dstdata + x, dstdata + x, height, width, &c);
}
dstcopy.convertTo(dst, src.type());
}
else if (src.channels() == 3)
{
Mat srccopy;
src.convertTo(srccopy, CV_32FC3);
Mat dstcopy;
src.convertTo(dstcopy, CV_32FC3);
for (int y = 0; y < height; y++)
{
float* srcrowdata = srccopy.ptr<float>(y);
float* dstrowdata = dstcopy.ptr<float>(y);
RecursiveGausssmooth(srcrowdata, dstrowdata, width, 3, &c);
RecursiveGausssmooth(srcrowdata + 1, dstrowdata + 1, width, 3, &c);
RecursiveGausssmooth(srcrowdata + 2, dstrowdata + 2, width, 3, &c);
}
float* srcdata = srccopy.ptr<float>(0);
float* dstdata = dstcopy.ptr<float>(0);
for (int x = 0; x < width; x++)
{
RecursiveGausssmooth(dstdata + x * 3, dstdata + x * 3, height, width * 3, &c);
RecursiveGausssmooth(dstdata + x * 3 + 1, dstdata + x * 3 + 1, height, width * 3, &c);
RecursiveGausssmooth(dstdata + x * 3 + 2, dstdata + x * 3 + 2, height, width * 3, &c);
}
dstcopy.convertTo(dst, src.type());
}
else
return;
}
int main(int argc, char *argv[])
{
Mat src = imread("../RecursiveGaussian/lena.bmp", 1);
imshow("original", src);
Mat dst;
RecursiveGausssmooth2D(src, dst);
imshow(" RecursiveGaussSmooth", dst);
Mat dstImage;
GaussianBlur(src, dstImage, Size(5, 5), 2, 2);
imshow("GaussSmooth", dstImage);
waitKey(0);
return EXIT_SUCCESS;
}