cv2.resize(src, dsize[, dst[, fx[, fy[, interpolation]]]]) → dst
interpolation 选项 所用的插值方法
INTER_NEAREST 最近邻插值
INTER_LINEAR 双线性插值(默认设置)
INTER_AREA 使用像素区域关系进行重采样。 它可能是图像抽取的首选方法,因为它会产生无云纹理的结果。 但是当图像缩放时,它类似于INTER_NEAREST方法。
INTER_CUBIC 4x4像素邻域的双三次插值
INTER_LANCZOS4 8x8像素邻域的Lanczos插值
附上自己写的实验代码:
import cv2
pic = cv2.imread('./Elegent_Girl.jpg')
pic = cv2.resize(pic, (400, 400), interpolation=cv2.INTER_CUBIC)
cv2.imshow('', pic)
cv2.waitKey(0)
cv2.destroyAllWindows()
Note:
使用cv2.resize时,参数输入是 宽×高×通道 ,与以往操作不同,需要注意。具体参见opencv: cv2.resize 探究(源码)。
原文:https://blog.csdn.net/JNingWei/article/details/78218837
在播放视频时,常遇到视频尺寸与画布尺寸不一致的情况。为了让视频按比例填充画布,需要对视频中的每一帧图像做缩放处理。缩放就是在原图的基础上做插值计算,从而增加或减少像素点的数量。常见的插值方式有最近点插值
,线性插值
,兰索斯插值
。下面简要介绍,并对比三种插值方式的结果。
在一维空间中,最近点插值就相当于四舍五入取整。在二维图像中,像素点的坐标都是整数,该方法就是选取离目标点最近的点。计算方式如下:
假设原图为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。
详情可参考:
https://zh.wikipedia.org/wiki/%E5%8F%8C%E7%BA%BF%E6%80%A7%E6%8F%92%E5%80%BC
该方法生成的图像比较平滑,如下图所示:
线性插值
一维的线性插值,是在目标点的左边和右边各取一个点做插值,这两个点的权重是由线性函数计算得到。而一维的兰索斯插值是在目标点的左边和右边各取四个点做插值,这八个点的权重是由高阶函数计算得到。详细原理可查阅:
https://en.wikipedia.org/wiki/Lanczos_resampling
二维的兰索斯插值在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为目标图高度。执行完毕后,把结果显示到屏幕上。
以上三种方法的对比图如下:
对比图1
对比图2
将上面的对比图放大后可以发现,线性插值的结果较最近点插值更平滑,兰索斯插值的结果较线性插值更清晰。
运行环境:iphone5s,ios8.3
运行程序:自研播放器demo
以上三种插值算法渲染每帧图像时,占用CPU时间都是40ms左右。由于这三种算法都是在GPU上实现,其对应的CPU代码相同,结果与预期相符。
占用GPU时间如下所示:
| 最近点插值 | 线性插值 | 兰索斯插值
-------|-------| -------|-------
每帧图像平均占用的GPU时间(ms) | 6 | 6 | 12
兰索斯插值算法占用GPU的平均时间为12ms,是其它两种算法的两倍,由于该算法中shader代码执行了两遍,结果也与预期相符。由于GPU与CPU是异步执行,大部分视频帧率不超过30,因此GPU上多出的6ms不会造成性能瓶颈。
注:GPUImage中的兰索斯插值实现有误,本文是参考OpenCV实现的。
作者:梧桐光影
链接:https://www.jianshu.com/p/8ae52a88ca61
來源:简书
简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。