赞
踩
做图像处理的同学应该经常都会用到图像的缩放
,我们都知道图片存储的时候其实就是一个矩阵
,所以在对图像进行缩放操作的时候,也就是在对矩阵进行操作,如果想要将图片放大,这里我们就需要用到过采样算法
来扩大矩阵,利用欠采样
来缩小图像。
如上图所示,左图是原图像矩阵
,右图是扩大后的图像矩阵
,右图中的橙色点
表示的是矩阵扩大之后通过插值算法
填充的像素值。所以,这篇文章我们主要探讨的就是如何来通过插值算法来填充像素值
在opencv
中提供了一个resize
函数用来调整图像的大小,里面提供了好几种不同的插值算法,如下图所示
这里我们主要介绍最常用的前5中插值算法
,最后两种插值算法主要是应用在仿射变换
中,cv.WARP_FILL_OUTLIERS
在从src
到dst
变换的时候可能会出现异常值,通过这个设定可以将异常值的像素置0。而cv.WARP_INVERSE_MAP
是应用在仿射变换的逆变换
,从dst
到src
的变换,关于仿射变换的更多资料可以参考我的上篇文章一文搞懂仿射变换
我们通过随机生成一个5×5
的图片,然后通过不同的插值算法将其放大10倍
之后,来对比最终图片的效果。
import cv2 import numpy as np from matplotlib import pyplot as plt #随机生成一张图片用于测试插值算法 img = np.uint8(np.random.randint(0,255,size=(5,5))) height,width = img.shape #设定通过插值之后图片的size new_dimension = (50,50) def img_draw_subplot(subplot_position,img,title_name,cmap): plt.subplot(subplot_position) plt.title(title_name) plt.imshow(img,cmap) def image_interpolation(img,new_dimension,inter_method): inter_img = cv2.resize(img,new_dimension,interpolation=inter_method) return inter_img #设置cmap cmap = "gray" #最近邻插值算法 nearest_img = image_interpolation(img,new_dimension,cv2.INTER_NEAREST) #双线性插值算法,resize函数默认的插值算法 linear_img = image_interpolation(img,new_dimension,cv2.INTER_LINEAR) #三次样条插值算法 cubic_img = image_interpolation(img,new_dimension,cv2.INTER_CUBIC) #区域插值 area_img = image_interpolation(img,new_dimension,cv2.INTER_AREA) #Lanczos插值 lanczos_img = image_interpolation(img,new_dimension,cv2.INTER_LANCZOS4) img_draw_subplot(231,img,"src Image",cmap=cmap) img_draw_subplot(232,nearest_img,"Nearest interpolation",cmap=cmap) img_draw_subplot(233,linear_img,"Linear interpolation",cmap=cmap) img_draw_subplot(234,cubic_img,"cubic interpolation",cmap=cmap) img_draw_subplot(235,area_img,"Area interpolation",cmap=cmap) img_draw_subplot(236,lanczos_img,"Lanczos interpolation",cmap=cmap) plt.show()
如果大家觉得灰度图
不方便观察,我们可以通过设置plt.imshow
的cmap
参数来控制颜色,matplotlib
提供了几种不同的类别
的色彩映射方式
有序的信息
两种不同的颜色的亮度和饱和度
,在中间以不饱和的颜色相遇,通常来用绘制具有关键的中间值
或者数据偏离零
的信息中间和开始/结束
以不饱和的颜色相遇,应用于在端点出环绕的信息
。关系
和排序
的信息杂色
来来观察,这里我就随机选用了Set1
,只需要将上面代码中的cmap
改成了Set1
即可最近邻插值
和区域插值
算法的效果,而线性插值
、三次样条插值
、Lanczos插值
整体效果看起来差不多,不过细节部分
还是有所差别,接下来我们就从这几种插值算法来分析一下。最近邻插值
也称近端插值
,是一种在一维或多维空间上进行多变元插值的简单方法。插值是一种通过已知的、离散的数据点,在范围内推求新数据点的过程或方法。最近邻插值算法选择距离所求数据点最近点的值,并且根本不考虑其他相邻点的值
,从而产生一个分段常数的内插值来作为所求数据点的值。
如上图所示,黑色的×表示需要插入的值,它会选择距离它最近的
P
x
+
1
,
y
P_{x+1,y}
Px+1,y的值来作为它的值。
如果距离四个点的距离都相等,最近邻插值会如何选择?
通过上图不能发现,当插入的值距离四个点都相等时,会选择距离最近的左上角的值,这是
因为图像坐标系的原点位于左上角。
这里的线性插值其实是指双线性插值
,这种插值算法也是resize
函数中默认使用
的插值算法。
双线性插值
,也被称为双线性内插
。双线插值是对线性插值在二维坐标系上的扩展,用于对双变量函数进行插值,其核心思想是在两个方向上分别进行一次线性插值
。
为了帮助大家更好的理解双线性插值算法
,我们先来看线性插值
假设我们已知坐标
(
x
0
,
y
0
)
(x_0,y_0)
(x0,y0)与
(
x
1
,
y
1
)
(x_1,y_1)
(x1,y1),我们想要得到在区间
[
x
0
,
x
1
]
[x_0,x_1]
[x0,x1]上任意位置
x
x
x所对应
y
y
y的值,如下图所示
我们可以求出直线的方程,然后将
x
x
x坐标代入到方程就可以求出对应的
y
y
y值,通过直线方程的两点式
可以得到
y
−
y
0
x
−
x
0
=
y
1
−
y
0
x
1
−
x
0
\frac{y-y_0}{x-x_0}=\frac{y_1-y_0}{x_1-x_0}\\
x−x0y−y0=x1−x0y1−y0
然后我们根据已知的
x
x
x,将其代入上式可得
y
=
y
0
+
(
x
−
x
0
)
y
1
−
y
0
x
1
−
x
0
=
y
0
+
(
x
−
x
0
)
y
1
−
(
x
−
x
0
)
y
0
x
1
−
x
0
y=y_0+(x-x_0)\frac{y_1-y_0}{x_1-x_0}=y_0+\frac{(x-x_0)y_1-(x-x_0)y_0}{x_1-x_0}
y=y0+(x−x0)x1−x0y1−y0=y0+x1−x0(x−x0)y1−(x−x0)y0
再了解线性插值以后,我们再来看看双线性插值
假如我们想得到未知函数
f
f
f在点
P
=
(
x
,
y
)
P=(x,y)
P=(x,y)的值,假设我们已知函数
f
f
f在
Q
11
=
(
x
1
,
y
1
)
Q_{11}=(x_1,y_1)
Q11=(x1,y1),
Q
12
=
(
x
1
,
y
2
)
Q_{12}=(x_1,y_2)
Q12=(x1,y2),
Q
21
=
(
x
2
,
y
1
)
Q_{21}=(x_2,y_1)
Q21=(x2,y1)及
Q
22
=
(
x
2
,
y
2
)
Q_{22}=(x_2,y_2)
Q22=(x2,y2)四个点的值
首先在
x
x
x方向进行线性插值,利用
Q
11
Q_{11}
Q11和
Q
21
Q_{21}
Q21可以求得
R
1
R_1
R1的
y
y
y值,利用
Q
12
Q_{12}
Q12和
Q
22
Q_{22}
Q22可以求得
R
2
R_2
R2的
y
y
y值
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
2
)
≈
x
2
−
x
x
2
−
x
1
f
(
Q
12
)
+
x
−
x
1
x
2
−
x
1
f
(
Q
22
)
f(R_1)\approx\frac{x_2-x}{x_2-x_1}f(Q_{11})+\frac{x-x_1}{x_2-x_1}f(Q_{21})\\ f(R_2)\approx\frac{x_2-x}{x_2-x_1}f(Q_{12})+\frac{x-x_1}{x_2-x_1}f(Q_{22})
f(R1)≈x2−x1x2−xf(Q11)+x2−x1x−x1f(Q21)f(R2)≈x2−x1x2−xf(Q12)+x2−x1x−x1f(Q22)
细心的同学也许发现了,这个插值好像与线性插值并不是一模一样的
,所以我们用的是
≈
\approx
≈而非
=
=
=,这里其实采用的是一种加权平均算法
结合两点来计算其中一点的
y
y
y值,主要是根据计算点距离两个端点在x方向上的距离
来计算计算点y值所占的比例。
接下来,我们再利用已经计算出来的
R
1
R_1
R1和
R
2
R_2
R2来
P
P
P点的插值,可得
f
(
P
)
≈
y
2
−
y
y
2
−
y
1
f
(
R
1
)
+
y
−
y
1
y
2
−
y
1
f
(
R
2
)
=
y
2
−
y
y
2
−
y
1
(
x
2
−
x
x
2
−
x
1
f
(
Q
11
)
+
x
−
x
1
x
2
−
x
1
f
(
Q
21
)
)
+
y
−
y
1
y
2
−
y
1
(
x
2
−
x
x
2
−
x
1
f
(
Q
12
)
+
x
−
x
1
x
2
−
x
1
f
(
Q
22
)
)
=
1
(
x
2
−
x
1
)
(
y
2
−
y
1
)
(
f
(
Q
11
)
(
x
2
−
x
)
(
y
2
−
y
)
+
f
(
Q
21
)
(
x
−
x
1
)
(
y
2
−
y
)
+
f
(
Q
12
)
(
x
2
−
x
)
(
y
−
y
1
)
+
f
(
Q
22
)
(
x
−
x
1
)
(
y
−
y
1
)
)
=
1
(
x
2
−
x
1
)
(
y
2
−
y
1
)
[
x
2
−
x
x
−
x
1
]
[
f
(
Q
11
)
f
(
Q
12
)
f
(
Q
21
)
f
(
Q
22
)
]
[
y
2
−
y
y
−
y
1
]
仔细观察上面的公式不难发现,其实
P
P
P点的值等于周围四个点与P点所构成的四个对角矩形面积的加权平均
双三次插值是一种更加复杂的插值算法,是二维空间中最常用的插值算法,相对双线性插值的图像边缘更加平滑
,函数
f
f
f在点
(
x
,
y
)
(x,y)
(x,y)的值可以通过矩形网格中最近的十六个采样点的加权平均得到,这里需要使用两个多项式插值三次函数
,每个方向使用一个。
双三次插值通过以下公式进行计算:
∑
i
=
0
3
∑
j
=
0
3
a
i
j
x
i
y
j
\sum_{i=0}^{3}\sum_{j=0}^{3}a_{ij}x^{i}y^{j}
i=0∑3j=0∑3aijxiyj
计算系数
a
i
j
a_{ij}
aij的过程依赖于插值数据的特性。如果已知插值函数的导数,常用的方法就是使用四个顶点的高度以及每个顶点的三个导数。一阶导数
h
′
x
h'x
h′x与
h
′
y
h'y
h′y表示
x
x
x与
y
y
y方向的表面斜率,二阶相互导数
h
′
′
x
y
h''xy
h′′xy表示同时在
x
x
x与
y
y
y方向的斜率。这些值可以通过分别对
x
x
x与
y
y
y向量取微分得到。对于网格单元的每个顶点,将局部坐标
(
0
,
0
)
、
(
1
,
0
)
、
(
0
,
1
)
、
(
1
,
1
)
(0,0)、(1,0)、(0,1)、(1,1)
(0,0)、(1,0)、(0,1)、(1,1)代入这些方程,再解这16个方程。
看了上面这段话之后,貌似还是不太好理解,接下来我们看一个例子,双三次插值
常用的BiCubic函数如下图
上式中的
a
a
a取-0.5即可,函数图像如下
对待插值的像素点
(
x
,
y
)
(x,y)
(x,y)(
x
,
y
x,y
x,y可为浮点数),取其附近的4×4领域点
(
x
i
,
y
i
)
(x_i,y_i)
(xi,yi)其中
i
,
j
=
0
,
1
,
2
,
3
i,j=0,1,2,3
i,j=0,1,2,3。按下面的公式进行插值计算:
例如,我们需要求解
P
P
P点值,在
P
P
P点周围有16个点
首先,我们要求出当前像素与
P
P
P点的距离,比如
a
00
a_{00}
a00距离
P
(
x
+
u
,
y
+
v
)
P(x+u,y+v)
P(x+u,y+v)的距离为
(
1
+
u
,
1
+
v
)
(1+u,1+v)
(1+u,1+v),那么我们可以得到
a
00
a_{00}
a00对应的系数为
(
W
(
1
+
u
)
,
W
(
1
+
v
)
)
(W(1+u),W(1+v))
(W(1+u),W(1+v)),所以
a
11
a_{11}
a11的系数为
(
W
(
u
)
,
W
(
v
)
)
(W(u),W(v))
(W(u),W(v)),
a
22
a_{22}
a22的系数为
(
W
(
1
−
u
)
,
W
(
1
−
v
)
)
(W(1-u),W(1-v))
(W(1−u),W(1−v)),
a
33
a_{33}
a33的系数为
W
(
2
−
u
)
,
W
(
2
−
v
)
W(2-u),W(2-v)
W(2−u),W(2−v),同理可以得到剩下点的系数,再根据上面的函数就可以求出
P
P
P点的值。
关于双三次插值函数更加详细介绍可以参考:
Cubic Convolution Interpolation for Digital Image Processing
区域插值算法
主要分两种情况,缩小图像和放大图像的工作原理并不相同。
整数倍
,在调用INTER_LINEAR_EXACT
插值算法时,如果图像的宽和高的缩小比例都是2,而且图像的通道数不是2,实际上会调用INTER_AREA
。在调用INTER_LINEAR
时,如果图像的宽和高的缩小比例都是2,实际上是会调用INTER_AREA
。INTER_AREA
实际上是个box filter,类似于均值滤波器
。最近邻插值
相似。如果放大的比例不是整数倍,则会采用线性插值
。Lanczos插值属于一种模板算法,需要通过计算模板中的权重信息来计算
x
x
x对应的值。
对于一维信息,假如我们输入的点集为
X
X
X,那么,Lanczos对应有个窗口模板Window,窗口中每个位置的权重计算如下:
通常a取2或者3,当a=2时,该算法适应于图像缩小的插值。当a=3时,算法适用于图像放大的插值。根据计算出来的权重信息,然后再根据
x
x
x即可求出对应的加权平均:
对于不同的插值算法,在缩放因子
不同的时候,耗时会有所区别,具体对照如下表所示
如果要缩小图像
,推荐使用INTER_AREA
插值效果最好,如果要放大图像
,INTER_CUBIC
效果最好,但是速度较慢,可以考虑使用INTER_LINEAR
速度较快,效果也还不错。
参考:
1.http://www.1zlab.com/wiki/python-opencv-tutorial/opencv-interpolation-algrithm/
2.维基百科线性插值
3.维基百科双三次插值
4.https://blog.csdn.net/nandina179/article/details/85330552
5.https://blog.csdn.net/qq_29058565/article/details/52769497
6.https://blog.csdn.net/u010555688/article/details/24352343
7.https://zhuanlan.zhihu.com/p/38493205
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。