姚明,81年,97年开始接触电脑,6年的编程学习经历, 曾有4年工作经验,最终转向基础理论学习和研究, 现华中理工科技大学在读,有志于图形学领域工作发展

EMAIL:alanvincentmail@gmail.com QQ:31547735

随笔分类(34)

文章分类(99)

相册

收藏夹(6)

编程技术网站

出国留学网站

数学资源网站

图形学网站

英语资源网站

自由职业者

搜索

  •  

最新评论

双线性滤波

 

经过缩放的一小部分猫的位图图像,左图使用了最近邻插值滤波,右图是用了立方滤波。立方插值滤波类似于双线性插值
经过缩放的一小部分位图图像,左图使用了最近邻插值滤波,右图是用了立方滤波。立方插值滤波类似于双线性插值

双线性滤波是进行缩放显示的时候进行纹理平滑的一种纹理滤波方法。 在大多数情况下,纹理在屏幕上显示的时候都不会同保存的纹理一模一样,没有任何失真。正因为这样,所以一些像素要使用纹素之间的点进行表示,在这里我们假设纹素都是位于各个单元中心或者左上或者其它位置的点。双线性滤波器利用这些点在像素所表示点周围四个最近的点之间进行双线性插值

目录

[编辑]公式

在下面这些方程中,uk 与 vk 是纹理坐标,yk 是点 k 处的颜色值。不带下标的值表示像素点,带有下标 0、1、2、3 的值表示从左上到右下包围像素的纹素点,这就是线性插值方程。由于双线性方程是线性插值方程的一种特殊形式,所以 我们从较简单的线性插值方程开始分析。

y_a = y_0 + \frac{y_1-y_0}{u_1-u_0}(u-u_0) \,\!
y_b = y_2 + \frac{y_3-y_2}{u_3-u_2}(u-u_2) \,\!
y = y_b + \frac{y_b-y_a}{v_2-v_0}(v-v_0) \,\!

假设纹理是正方形的位图,并且满足

v_1 = v_0 \,\!
v_2 = v_3 \,\!
u_1 = u_3 \,\!
u_2 = u_0 \,\!
v_3 - v_0 = u_3 - u_0 = w \,\!

我们进一步定义,

U = \frac{u - u_0}{w} \,\!
V = \frac{v - v_0}{w} \,\!

这样就可以将插值方程化简为:

y_a = y_0 + (y_1-y_0)U \,\!
y_b = y_2 + (y_3-y_2)U \,\!
y = y_a + (y_b-y_a)V \,\!

代入方程,得到:

y = y_0 + (y_1 - y_0)U + (y_2 - y_0)V + (y_3 - y_2 - y_1 + y_0)UV \,\!

或者,

y = y_0(1 - U)(1 - V) + y_1 U (1 - V) + y_2 (1 - U) V + y_3 U V \,\!

这种表示相当简单。但是,如果图像只进行缩放处理,而没有旋转、扭曲、透视或者其它处理,那么使用独立的方程计算并且保存用于后面数据行计算的 yb 或者 ya 速度将更快。

[编辑]示例代码

这部分代码假设纹理是常见的正方形,没有Mipmap,并且只有一个通道的数据。只有一个通道的情况非常少见,几乎所有的纹理都是彩色的,都有红色、绿色与蓝色通道,有些还有阿尔法透明通道,所以每个 y 都要进行三到四次的计算

double getBilinearFilteredPixelColor(Texture tex, double u, double v) {
u *= tex.size;
v *= tex.size;
int x = floor(u);
int y = floor(v);
double u_ratio = u - x;
double v_ratio = v - y;
double u_opposite = 1 - u_ratio;
double v_opposite = 1 - v_ratio;
double result = (tex[x][y]   * u_opposite + tex[x+1][y]   * u_ratio) * v_opposite +
(tex[x][y+1] * u_opposite + tex[x+1][y+1] * u_ratio) * v_ratio;
return result;
}

[编辑]局限

在纹理缩减到一半或者放大一倍的范围内,双线性滤波都能够有非常好的精度。这也就是说,如果纹理在每个方向都有 256 个像素,那么将它缩减到 128 以下或者放大到 512 以上的时候,由于会丢掉太多的像素或者进行了过多的平滑处理,纹理看起来就会很差。通常,可以在缩减的过程中使用 Mipmap 来实现较好的性能;但是,在透视图中的纹理上的经过双线性滤波处理的两个不同尺寸的 mipmap 之间的过渡将非常明显。三线性滤波尽管比较复杂,但是可以使得过渡非常平滑。

为了快速说明纹理滤波中如何丢失纹素,这里有一组用来自于 8 纹素宽纹理的数字表示的盒子的中心,它们与蓝色表示的来自于 3 纹素宽的纹理表示的盒子中心的一组数字混杂在一起。红色数字表示计算 3 纹素纹理中根本不需要的纹素。

0.0625, 0.1667, 0.1875, 0.3125, 0.4375, 0.5000, 0.5625, 0.6875, 0.8125, 0.8333, 0.9375

[编辑]特殊情况

通常纹理是有限大小的,我们经常会得到坐标位于纹素坐标之外栅格之外的像素。可以用以下几种方法来处理这种情况:

  • 旋绕纹理,这样一行中的最后一个纹素将出现在该行第一个纹素的前面,一列中的最后一个纹素将出现在该列第一个纹素前面。在纹理平铺的时候这种方法可以得到最好的效果。
  • 纹理之外的区域使用一种颜色。这可能并不是一个非常了不起的想法,但是如果纹理要放到固体或者透明背景上,那么就可以使用这种方法。
  • 无限重复边界纹素。如果所设计的纹理不是要重复使用的话,这种方法可以很好地工作。

[编辑]参见

posted on 2008-01-01 08:58 姚明 阅读(2087) 评论(2)  编辑 收藏 引用 所属分类: 图形学

FeedBack:
# re: 双线性滤波 2008-06-30 13:45 stephanie
在何斌的数字图像处理中,有一个关于双线性插值算法的实现程序,里面有一部分我始终看不懂,能帮忙解释一下吗:
// 根据不同情况分别处理
if( (x < 0) ¦ ¦ (x > lWidth - 1) ¦ ¦ (y < 0) ¦ ¦ (y > lHeight - 1))
{
// 要计算的点不在源图范围内,直接返回255。
return 255;
}
else
{
if (fabs(x - lWidth + 1) <= EXP)
{
// 要计算的点在图像右边缘上(它怎么只考虑图像在右边缘和下边缘的点,那如果点在图像的左边缘和上边缘怎么办呢?)
if (fabs(y - lHeight + 1) <= EXP)
{
// 要计算的点正好是图像最右下角那一个象素,直接返回该点象素值
f1 = *((unsigned char *)lpDIBBits + lLineBytes * (lHeight - 1 - j1) + i1);
return f1;
}
else
{
// 在图像右边缘上且不是最后一点,直接一次插值即可(这里插值算法点的选取,让我很迷惑,在右边缘上不是应该X值不变,Y变的吗,那么一次插值,应该是f(x,j1)+(y-j1)[f(x,j2)-f(x,j1)]))如下的插值坐标点选取也是一样的问题,怎么都和我想的相反
f1 = *((unsigned char *)lpDIBBits + lLineBytes * (lHeight - 1 - j1) + i1);
f3 = *((unsigned char *)lpDIBBits + lLineBytes * (lHeight - 1 - j1) + i2);

// 返回插值结果
return ((unsigned char) (f1 + (y -j1) * (f3 - f1))); }
}
else if (fabs(y - lHeight + 1) <= EXP)
{
// 要计算的点在图像下边缘上且不是最后一点,直接一次插值即可
f1 = *((unsigned char *)lpDIBBits + lLineBytes * (lHeight - 1 - j1) + i1);
f2 = *((unsigned char *)lpDIBBits + lLineBytes * (lHeight - 1 - j2) + i1);

// 返回插值结果
return ((unsigned char) (f1 + (x -i1) * (f2 - f1))); }
else
{
// 计算四个最临近象素值
f1 = *((unsigned char *)lpDIBBits + lLineBytes * (lHeight - 1 - j1) + i1);
f2 = *((unsigned char *)lpDIBBits + lLineBytes * (lHeight - 1 - j2) + i1);
f3 = *((unsigned char *)lpDIBBits + lLineBytes * (lHeight - 1 - j1) + i2);
f4 = *((unsigned char *)lpDIBBits + lLineBytes * (lHeight - 1 - j2) + i2);

// 插值1
f12 = (unsigned char) (f1 + (x - i1) * (f2 - f1));

// 插值2
f34 = (unsigned char) (f3 + (x - i1) * (f4 - f3));

// 插值3
return ((unsigned char) (f12 + (y -j1) * (f34 - f12)));

  回复  更多评论
  
# re: 双线性滤波 2008-06-30 13:46 stephanie
我的邮箱:stephaniez@163.com  回复  更多评论
  

只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理