一、resize()函数介绍
resize()函数是专门用来调整图片的大小的,其原理就是通过不同的插值方式对图像进行处理,这些插值方式将在后续介绍到,首先介绍resize()函数的原型:
resize( InputArray src, OutputArray dst,Size dsize, double fx = 0, double fy = 0,int interpolation = INTER_LINEAR );
第一个参数为输入图像;
第二个参数为输出图像,通常情况下我们并不对其进行初始化,而是利用后续的dsize参数在程序中自动计算其尺寸;
第三个参数为输出图像希望的大小,如果它设置为0,将通过后续的fx,fy参数经由下述公式计算得来:
dsize=Size(round(fx*src.cols),round(fy*src.rows))
第四个参数为沿水平方向的缩放系数,有默认值0,取零的话则通过dsize参数经由下列公式计算得来:
fx=(double)dsize.width/src.cols
第五个参数为沿竖直方向的缩放系数,有默认值0,取零的话则通过dsize参数经由下列公式计算得来:
fy=(double)dsize.height/src.rows
第六个参数为插值类型的选择,可选方案有INTER_NEAREST 、INTER_LINEAR、INTER_CUBIC、INTER_AREA 、INTER_LANCZOS4。默认值INTER_LINEAR,一般也推荐选用INTER_LINEAR。后续将介绍这就这几种插值方法的原理。
二、插值方法介绍
(具体每个插值的实现方法可参见参考文献中其他博主的思路)
1.最近邻插值
最近邻插值的原理就是将放大或者缩小后的图片相应像素点直接乘以比例系数(应该为缩放系数的倒数)对应到原图中的相应像素点,如果存在小数的话则采用四舍五入或者直接舍去小数的方法。
假设resize后的图片某像素位置为
(
i
′
,
j
′
)
(i',j')
(i′,j′),分别乘以比例系数之后再分成整数部分和小数部分(i、j整数,u、v小数)为
(
i
+
u
,
j
+
v
)
(i+u,j+v)
(i+u,j+v),那如果采用四舍五入的方式,则按下图判断其对应原图像素值,落在A区取
(
i
,
j
)
(i,j)
(i,j),B区取
(
i
+
1
,
j
)
(i+1,j)
(i+1,j):
如果采用直接舍去的方式则不论u、v的值,直接对应原图
(
i
,
j
)
(i,j)
(i,j),这也是opencv中采用的最近邻插值方法。
2.双线性插值
双线性插值是使用映射回原图的像素点周围4个点来对待求像素点进行拟合,距离像素点越近的权重值越大。假设resize后的图片某像素位置为
(
i
′
,
j
′
)
(i',j')
(i′,j′),分别乘以比例系数之后再分成整数部分和小数部分(i、j整数,u、v小数)为
(
i
+
u
,
j
+
v
)
(i+u,j+v)
(i+u,j+v),那么实际拟合出的该像素值应该满足如下关系:
f
′
(
i
′
,
j
′
)
=
(
1
−
u
)
(
1
−
v
)
f
(
i
,
j
)
+
u
(
1
−
v
)
f
(
i
+
1
,
j
)
+
(
1
−
u
)
v
f
(
i
,
j
+
1
)
+
u
v
f
(
i
+
1
,
j
+
1
)
;
f'(i',j')=(1-u)(1-v)f(i,j)+u(1-v)f(i+1,j)+(1-u)vf(i,j+1)+uvf(i+1,j+1);
f′(i′,j′)=(1−u)(1−v)f(i,j)+u(1−v)f(i+1,j)+(1−u)vf(i,j+1)+uvf(i+1,j+1);
也就是映射后的点为P时,先在X方向上对Q11、Q21,Q12、Q22分别进行一次线性插值
f
(
R
1
)
=
(
X
2
−
X
)
(
X
2
−
X
1
)
∗
f
(
Q
11
)
+
(
X
−
X
1
)
(
X
2
−
X
1
)
∗
f
(
Q
21
)
f(R_1)={(X_2-X)\over(X_2-X1)}*f(Q_{11})+{(X-X_1)\over(X_2-X_1)}*f(Q_{21})
f(R1)=(X2−X1)(X2−X)∗f(Q11)+(X2−X1)(X−X1)∗f(Q21)
f
(
R
2
)
=
(
X
2
−
X
)
(
X
2
−
X
1
)
∗
f
(
Q
12
)
+
(
X
−
X
1
)
(
X
2
−
X
1
)
∗
f
(
Q
22
)
f(R_2)={(X_2-X)\over(X_2-X1)}*f(Q_{12})+{(X-X_1)\over(X_2-X_1)}*f(Q_{22})
f(R2)=(X2−X1)(X2−X)∗f(Q12)+(X2−X1)(X−X1)∗f(Q22)
再在Y方向上对R1,R2进行一次线性插值得到上述公式:
f
(
Q
)
=
(
Y
2
−
Y
)
(
Y
2
−
Y
1
)
∗
f
(
R
1
)
+
(
Y
−
Y
1
)
(
Y
2
−
Y
1
)
∗
f
(
R
2
)
f(Q)={(Y_2-Y)\over(Y_2-Y1)}*f(R_{1})+{(Y-Y_1)\over(Y_2-Y_1)}*f(R_{2})
f(Q)=(Y2−Y1)(Y2−Y)∗f(R1)+(Y2−Y1)(Y−Y1)∗f(R2)
但是在opencv的双线性插值运算中对resize后像素点的映射进行了调整,不再是直接乘以比例系数,而是通过下述公式映射:
f
(
i
+
j
,
u
+
v
)
=
f
(
(
i
′
+
0.5
)
f
x
−
0.5
,
(
j
′
+
0.5
)
f
y
−
0.5
)
f(i+j,u+v)=f({(i'+0.5){}\over{f_x}}-0.5,{(j'+0.5){}\over{f_y}}-0.5)
f(i+j,u+v)=f(fx(i′+0.5)−0.5,fy(j′+0.5)−0.5)
3.双三次插值
双三次插值和双线性插值函数类似,也是用映射后点周围16个对待求像素点进行拟合,但此时每个点所占的权重就不再是根据距离远近确定了,而是根据一个三次多项式S(x)逼近理论最佳插值函数得到:
W
(
X
)
=
{
(
a
+
2
)
∣
X
∣
3
−
(
a
+
3
)
∣
X
∣
2
+
1
if
∣
X
∣
≤
1
a
∣
X
∣
3
−
5
a
∣
X
∣
2
+
8
a
∣
X
∣
−
4
a
if
1
<
∣
X
∣
<
2
0
otherwise
W(X)=\begin{cases}{(a+2)|X|^3-(a+3)|X|^2+1} &\text{if } {|X|\le1}\\{a|X|^3-5a|X|^2+8a|X|-4a} &\text{if } {1<|X|<2}\\{ 0} &\text{otherwise}\end{cases}
W(X)=⎩⎪⎨⎪⎧(a+2)∣X∣3−(a+3)∣X∣2+1a∣X∣3−5a∣X∣2+8a∣X∣−4a0if ∣X∣≤1if 1<∣X∣<2otherwise
通常a设置为-0.5或者0.75,opencv中设置为0.75;
而后同样的思路得到resize后像素值计算公式:
f
′
(
i
′
,
j
′
)
=
∑
i
=
0
3
∑
j
=
0
3
f
(
x
i
,
y
j
)
W
(
x
−
x
i
)
W
(
y
−
y
i
)
;
f'(i',j')=\displaystyle\sum_{i=0}^3\sum_{j=0}^3f(x_i,y_j)W(x-x_i)W(y-y_i);
f′(i′,j′)=i=0∑3j=0∑3f(xi,yj)W(x−xi)W(y−yi);
4.区域插值
未解决
5.兰索斯插值
未解决
参考文献
最近邻插值、双线性插值、双三次插值
OpenCV中resize函数五种插值算法的实现过程
图像超分(插值方法)最近邻、双线性、双三次插值算法
双三次插值算法(bicubic interpolation)与图形学和计算方法的关系
opencv中的resize 函数 的理解以及引申