图像Lanczos3滤波——C实现

Lanczos 滤波器,因发明者而得名,在信号处理领域,主要用在增加采样率和低通滤波,在图像处理领域用于图像缩放、旋转,其可以在减小锯齿、锐度、振铃效应( aliasing, sharpness, and minimal ringing)取得最好的平衡。
滤波函数如下:
L ( x ) = { sinc ⁡ ( x )   sinc ⁡ ( x / a ) if  − a < x < a , 0 otherwise . {\displaystyle L(x)={\begin{cases}\operatorname {sinc} (x)\,\operatorname {sinc} (x/a)&{\text{if}}\ -a<x<a,\\0&{\text{otherwise}}.\end{cases}}} L(x)={sinc(x)sinc(x/a)0if a<x<a,otherwise.
等价于:
L ( x ) = { 1 if  x = 0 , a sin ⁡ ( π x ) sin ⁡ ( π x / a ) π 2 x 2 if  − a ≤ x < a  and  x ≠ 0 , 0 otherwise . {\displaystyle L(x)={\begin{cases}1&{\text{if}}\ x=0,\\{\dfrac {a\sin(\pi x)\sin(\pi x/a)}{\pi ^{2}x^{2}}}&{\text{if}}\ -a\leq x<a\ {\text{and}}\ x\neq 0,\\0&{\text{otherwise}}.\end{cases}}} L(x)=1π2x2asin(πx)sin(πx/a)0if x=0,if ax<a and x̸=0,otherwise.
当a取3时,就是Lanczos3滤波器。
插值公式:
S ( x ) = ∑ i = ⌊ x ⌋ − a + 1 ⌊ x ⌋ + a s i L ( x − i ) S(x) = \sum_{i=\lfloor x \rfloor - a + 1}^{\lfloor x \rfloor + a} s_{i} L(x - i) S(x)=i=xa+1x+asiL(xi)
a是滤波尺寸, ⌊ x ⌋ \lfloor x \rfloor x是向下取整。
性质:
1、a>0,Lanczos 核函数对每个点都是连续,并且其倒数也是连续,因此 S ( x ) S(x) S(x)连续,其倒数连续;
2、Lanczos 核函数在整数点值为零,除了 x = 0,核函数为1,相当于原始x处信号值不变,仅仅对小数位置进行插值;
二维的情形:
L ( x , y ) = L ( x ) L ( y ) {\displaystyle L(x,y)=L(x)L(y)} L(x,y)=L(x)L(y)
算法实现:
代码实现参考https://github.com/richgel999/imageresampler

#define  M_PI       3.14159265358979323846
//sinc函数值
static double sinc(double x)
{
	x = (x * M_PI);
	if ((x < 0.01f) && (x > -0.01f))
		return 1.0f + x*x*(-1.0f / 6.0f + x*x*1.0f / 120.0f);
	return sin(x) / x;
}

对于0附近的sinc函数值,采用其泰勒展开式计算:
sinc ⁡ ( x ) = sin ⁡ ( x ) x = ∑ n = 0 ∞ ( − x 2 ) n ( 2 n + 1 ) ! \operatorname {sinc} (x)={\frac {\sin(x)}{x}}=\sum _{n=0}^{\infty }{\frac {\left(-x^{2}\right)^{n}}{(2n+1)!}} sinc(x)=xsin(x)=n=0(2n+1)!(x2)n

//计算Lanczos3核函数
static float clip(double t)
{
	const float eps = .0000125f;
	if (fabs(t) < eps)
		return 0.0f;
	return (float)t;
}

static inline float lancos(float t)
{
	if (t < 0.0f)
		t = -t;

	if (t < 3.0f)
		return clip(sinc(t) * sinc(t / 3.0f));
	else
		return (0.0f);
}
//x方向的插值:
static inline float lancos3_resample_x(uint8_t** arr,int src_w, int src_h, int y, int x,float xscale)
{
	float s = 0;
	float coef_sum = 0.0f;
	float coef;
	float pix;
	int i;

	int l, r;
	float c;
	float hw;
	//对于缩小的情况hw相当于扩大了领域像素个数,这里如果不作此处理,最终缩小图片效果跟最近领域插值法没多少区别,其效果相当于先低通滤波,再插值
	if (xscale > 1.0f)
		hw = 3.0f;
	else
		hw = 3.0f / xscale;

	c = (float)x / xscale;
	l = (int)floor(c - hw);
	r = (int)ceil(c + hw);

	if (y < 0)y = 0;
	if (y >= src_h)y = src_h - 1;
	if (xscale > 1.0f)xscale = 1.0f;
	for (i = l; i <= r; i++)
	{   
		x = i;
		if (i < 0)x = 0;
		if (i >= src_w)x = src_w - 1;
		pix = arr[y][x];
		coef = lancos((c-i)*xscale);
		s += pix * coef;
		coef_sum += coef;
	}
	s /= coef_sum; 
	return s;
}

void img_resize_using_lancos3(uint8_2d* src, uint8_2d* dst)
{
	int src_rows, src_cols;
	int dst_rows, dst_cols;
	int i, j;
	if (!src || !dst)return;
	uint8_t** src_arr;
	uint8_t** dst_arr;
	float xratio;
	float yratio;
	float pix;
	int val;
	int k;
	float hw;
	src_arr = src->arr;
	dst_arr = dst->arr;
	src_rows = src->rows;
	src_cols = src->cols;
	dst_rows = dst->rows;
	dst_cols = dst->cols;

	xratio = (float)(dst_cols) / (float)src_cols;
	yratio = (float)(dst_rows) / (float)src_rows;

	float scale = 0.0f;
	if (yratio > 1.0f)
	{
		hw = 3.0;
		scale = 1.0f;
	}
	else
	{
		hw = 3.0 / yratio;
		scale = yratio;
	}
	
	for (i = 0; i < dst_rows; i++)
	{
		for (j = 0; j < dst_cols; j++)
		{
			int t, b;
			float c;

			float s = 0;
			float coef_sum = 0.0f;
			float coef;
			float pix;

			c = (float)i / yratio;
			t = (int)floor(c - hw);
			b = (int)ceil(c + hw);
            //先对x方向插值,再进行y方向插值。
			for (k = t; k <= b; k++)
			{
				pix = lancos3_resample_x(src_arr, src_cols,src_rows, k, j,xratio);
				coef = lancos((c - k)*scale);
				coef_sum += coef;
				pix *= coef;
				s += pix;
			}
			val = (int)s / coef_sum;
			if (val < 0)val = 0;
			if (val > 255)val = 255;
			dst_arr[i][j] = val;
		}
	}
}

测试代码:

void test_lancos3_resize(dai_image* img, float factor)
{
	uint8_2d* r = NULL;
	uint8_2d* g = NULL;
	uint8_2d* b = NULL;

	uint8_2d* r1 = NULL;
	uint8_2d* g1 = NULL;
	uint8_2d* b1 = NULL;

	dai_image* img1 = NULL;
	if (!img)return;
	split_img_data(img, &r, &g, &b);
	int w, h;
	int w1, h1;
	w = img->width;
	h = img->height;
	w1 = factor*w;
	h1 = factor*h;

	r1 = create_uint8_2d(h1, w1);
	g1 = create_uint8_2d(h1, w1);
	b1 = create_uint8_2d(h1, w1);

	img_resize_using_lancos3(r, r1);
	img_resize_using_lancos3(g, g1);
	img_resize_using_lancos3(b, b1);

	merge_img_data(r1, g1, b1, &img1);
	if (img1)
	{
		img1->type = EJPEG;
		dai_save_image("resize_lancos3.jpg", img1);
		dai_destroy_image(&img1);
	}
	destroy_uint8_2d(&r);
	destroy_uint8_2d(&g);
	destroy_uint8_2d(&b);
	destroy_uint8_2d(&r1);
	destroy_uint8_2d(&g1);
	destroy_uint8_2d(&b1);
}

效果图:
原图:
图像Lanczos3滤波——C实现_第1张图片
缩小3倍的图:

图像Lanczos3滤波——C实现_第2张图片
另外,缩小之后,图像相对变暗,这时,可以采用gama校正改变亮度。

参考文献:
1、http://www.realitypixels.com/turk/computergraphics/ResamplingFilters.pdf
2、https://en.wikipedia.org/wiki/Lanczos_resampling
3、https://github.com/richgel999/imageresampler

你可能感兴趣的:(图像处理)