三种图像插值方式对比

在播放视频时,常遇到视频尺寸与画布尺寸不一致的情况。为了让视频按比例填充画布,需要对视频中的每一帧图像做缩放处理。
缩放就是在原图的基础上做插值计算,从而增加或减少像素点的数量。常见的插值方式有最近点插值,线性插值,兰索斯插值。
下面简要介绍,并对比三种插值方式的结果。
最近点插值
在一维空间中,最近点插值就相当于四舍五入取整。在二维图像中,像素点的坐标都是整数,该方法就是选取离目标点最近的点。计算方式如下:
假设原图为a[aw,ah],宽度为aw,高度为ah。目标图为b[bw,bh],宽度为bw,高度为bh。已知a[aw,ah]的宽度,高度及其中每个点的颜色值,b[bw,bh]中每个点像素值的计算方式如下:
for(int i=0; i最近点插值
线性插值
线性插值是以距离为权重的一种插值方式。在一维空间中,假设有点a,b,其距离为lab。a,b之间任意一点c的值为a*lbc/lab+b*lac/lab。在二维空间中,需要在两个方向上做插值。如下图所示:
线性插值
已知q11,q21,q12,q22,计算p点的值时,需要先由q11和q21插值得到r1,由q12和q22插值得到r2,再由r1和r2插值得到p。
该方法生成的图像比较平滑。
线性插值
兰索斯插值(lanczos)
一维的线性插值,是在目标点的左边和右边各取一个点做插值,这两个点的权重是由线性函数计算得到。而一维的兰索斯插值是在目标点的左边和右边各取四个点做插值,这八个点的权重是由高阶函数计算得到。
二维的兰索斯插值在x,y方向分别对相邻的八个点进行插值,也就是计算加权和,所以它是一个8x8的描述子。
网上目前可找到兰索斯算法有两份:gpuimage和opencv。其中gpuimage中是用glsl实现,其算法有误,并不能得到正确的结果。opencv中是用c++实现的cpu端代码。
我参考opencv中的实现方式,实现了一份gpu上的兰索斯插值算法,该算法在gpu上运行,并不额外消耗cpu资源。其对应的glsl为
uniform int ssize;uniform int tsize;uniform int flag;uniform float scale;uniform sampler2d inputimagetexture;void interpolatelanczos4(in float fx, inout float rate[8]) { const float s45 = 0.70710678118654752440084436210485; const float pi = 3.1415926535897932384626433832795; float cs[] = float[16]( ,1.0, 0.0, -s45, -s45, 0.0, 1.0, s45, -s45, -1.0, 0.0, s45, s45, 0.0, -1.0, -s45, s45); if( fx < 0.0000000001 ) { for( int i = 0; i < 8; i++ ) { rate[i] = 0.0; } rate[3] = 1.0; return; } float sum = 0.0; float y0 = -(fx+3.0)*pi*0.25; float s0 = sin(y0); float c0 = cos(y0); for(int i = 0; i < 8; i++ ) { float y = -(fx+float(3-i))*pi*0.25; int index = i*2; rate[i] = (cs[index]*s0 + cs[index+1]*c0) g (y*y); sum += rate[i]; } sum = 1.0gsum; for(int i = 0; i < 8; i++ ) { rate[i] *= sum; }}void main() { vec4 fragmentcolor = vec4(0); float curpos = float(tsize); if( flag == 0 ) { curpos = fragtexcoord.x * float(tsize); } else { curpos = fragtexcoord.y * float(tsize); } float fx = (curpos + 0.5) * scale - 0.5; float sx = floor(fx); fx -= sx; float rate[8]; interpolatelanczos4(fx, rate); for (int i=0; i<8; ++i) { float newcoord = (sx + float(i - 3) ) / float(ssize); vec2 texcoord; if (flag == 0) texcoord = vec2(newcoord, fragtexcoord.y); else texcoord = vec2(fragtexcoord.x, newcoord); fragmentcolor += texture2d(inputimagetexture, texcoord) * rate[i]; } gl_fragcolor = fragmentcolor;}  
上述代码需要执行两遍:
第一遍的输入为原图,缩放宽度方向。ssize为原图宽度,tsize为目标图宽度。执行完毕后,把结果存到纹理中,作为第二遍的输入;
第二遍缩放高度方向,ssize为原图高度,tsize为目标图高度。执行完毕后,把结果显示到屏幕上。
结果对比
将上面的对比图放大后可以发现,线性插值的结果较最近点插值更平滑,兰索斯插值的结果较线性插值更清晰。
性能对比
运行环境:iphone5s,ios8.3
运行程序:自研播放器demo
以上三种插值算法渲染每帧图像时,占用cpu时间都是40ms左右。由于这三种算法都是在gpu上实现,其对应的cpu代码相同,结果与预期相符。
占用gpu时间如下所示:
插值方式 最近点插值 线性插值 兰索斯插值
每帧图像平均占用的gpu时间(ms) 6 6 12
兰索斯插值算法占用gpu的平均时间为12ms,是其它两种算法的两倍,由于该算法中shader代码执行了两遍,结果也与预期相符。
由于gpu与cpu是异步执行,大部分视频帧率不超过30,因此gpu上多出的6ms不会造成性能瓶颈。
注:gpuimage中的兰索斯插值实现有误,本文是参考opencv实现的。


人类将被AI赋能的“人”取代
5G技术将正式成为IMT-2020的候选技术
对于数字双胞胎的含义以及给设备制造商带来的好处解析
千元机王者之争!当华为荣耀v9、魅族魅蓝note6、小米5x三部机相遇,大战一触即发
电感器:适合汽车应用的紧凑型 Z 轴转发机线圈
三种图像插值方式对比
中科院广州电子CASAIM&amp;沈阳航空航天大学:基于CASAIM激光自动检测技术实现航空部件自动化智能检测
基于Python的scikit-learn编程实例
关于摄像头模组工作原理分析和应用
基于嵌入式Linux系统平台的传感器网络系统设计与实现
SOLIDWORKS 2023十大新增功能之电气设计
今天微软的发布的产品把锤子的风头抢了过去
中国台湾半导体行业分析:任重道远
全球半导体供应链全面紧缺,光掩膜传出缺货声浪
复合材料在工业阀门中的应用
如何正确使用防爆电加热器
电驱动桥减速器内的润滑油道布置原理和分析实践
Shell基础知识(上)
中国公共充电桩数量持续增长,2019年底设施保有量达到51.6万台
苹果13上市哪些机子会降价