文章目录
- 前言
- 一、双线性插值
- 二、代码实现
- 总结
前言
在上一文《最近邻插值法》中我们讨论了最近邻,并且使用python实现,本章节中我们继续讨论图像缩放算法——双线性插值法,给难度升个级。
一、双线性插值
你在哪里见过下面这个图?回忆总是那么美妙,可能你已经记不清初中同桌的样子,但是老师黑板上画的一元一次方程肯定还记得。
公式(1)怎么等起来呢?当然是斜率,公式两边都是算的上图直线的斜率。
y
−
y
0
x
−
x
0
=
y
1
−
y
0
x
1
−
x
0
(1)
\Large\frac{y\ -\ y_0}{x\ -\ x_0} = \frac{y_1\ -\ y_0}{x_1\ -\ x_0} \tag{1}
x − x0y − y0=x1 − x0y1 − y0(1)
公式整理一下得到公式(2)
y
=
x
1
−
x
x
1
−
x
0
y
0
+
x
−
x
0
x
1
−
x
0
y
1
(2)
\Large y\ = \ \frac{x_1\ -\ x}{x_1\ -\ x_0} \ y_0\ + \ \frac{x\ - \ x_0}{x_1\ - \ x_0}\ y_1\tag{2}
y = x1 − x0x1 − x y0 + x1 − x0x − x0 y1(2)
根据两个已知点(x0,y0),(x1,y1)可以求出这条线的关系,用公式(2)表达。图像缩放,插入一个点应该和他周围点是有联系的。比如你坐的教室前排,那你周围坐的极有可能是学霸,通过你周围学霸的成绩是可以大概知道你的学习情况。在最近邻插值法里面没有考虑像素点之间的联系。
双线性插值法就是进行了两次插入,第一次插入的是辅助点,第二次插入的才是真实点,比如Q12,Q22得到R2,Q11,Q21得到R1,最后R1,R2得到P。为什么不叫三次插入?因为两个辅助点是x方向插入,P是y方向插入,所以是双线性插入。
{
f
(
x
,
y
1
)
=
f
(
R
1
)
≈
x
2
−
x
x
2
−
x
1
f
(
Q
11
)
+
x
−
x
1
x
2
−
x
1
f
(
Q
21
)
w
h
e
r
e
R
1
=
(
x
,
y
1
)
f
(
x
,
y
2
)
=
f
(
R
2
)
≈
x
2
−
x
x
2
−
x
1
f
(
Q
12
)
+
x
−
x
1
x
2
−
x
1
f
(
Q
22
)
w
h
e
r
e
R
2
=
(
x
,
y
2
)
(3)
\begin{cases} \large f(x, y_1)\ =\ 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}) \quad\quad\quad where\quad R_1\ = \ (x, y_1)\\ \large f(x, y_2)\ = \ 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}) \quad\quad\quad where\quad R_2\ = \ (x, y_2)\tag{3} \end{cases}
{f(x,y1) = f(R1) ≈ x2 − x1x2 − x f(Q11)+x2 − x1x − x1 f(Q21)whereR1 = (x,y1)f(x,y2) = f(R2) ≈ x2 − x1x2 − x f(Q12)+x2 − x1x − x1 f(Q22)whereR2 = (x,y2)(3)
f
(
x
,
y
)
=
f
(
P
)
≈
y
2
−
y
y
2
−
y
1
f
(
R
1
)
+
y
−
y
1
y
2
−
y
1
f
(
R
2
)
(4)
\large f(x, y)\ =\ f(P)\ \approx \ \frac{y_2\ -\ y}{y_2\ -\ y_1}\ f(R_1) + \frac{y\ -\ y_1}{y_2\ -\ y_1}\ f(R_2)\tag{4}
f(x,y) = f(P) ≈ y2 − y1y2 − y f(R1)+y2 − y1y − y1 f(R2)(4)
f
(
x
,
y
)
≈
(
x
2
−
x
)
(
y
2
−
y
)
(
x
2
−
x
1
)
(
y
2
−
y
1
)
f
(
Q
11
)
+
(
x
−
x
1
)
(
y
2
−
y
)
(
x
2
−
x
1
)
(
y
2
−
y
1
)
f
(
Q
21
)
+
(
x
2
−
x
)
(
y
−
y
1
)
(
x
2
−
x
1
)
(
y
2
−
y
1
)
f
(
Q
12
)
+
(
x
−
x
1
)
(
y
−
y
1
)
(
x
2
−
x
1
)
(
y
2
−
y
1
)
f
(
Q
22
)
(5)
\large f(x, y)\ \approx \ \frac{(x_2\ -\ x)\ (y_2\ -\ y)}{(x_2\ -\ x_1)\ (y_2\ -\ y_1)}\ f(Q_{11}) \ +\ \frac{(x\ -\ x_1)\ (y_2\ -\ y)}{(x_2\ -\ x_1)\ (y_2\ -\ y_1)}\ f(Q_{21})\\ +\ \frac{(x_2\ -\ x)\ (y\ -\ y_1)}{(x_2\ -\ x_1)\ (y_2\ -\ y_1)}\ f(Q_{12})\ +\ \frac{(x\ -\ x_1)\ (y\ -\ y_1)}{(x_2\ -\ x_1)\ (y_2\ -\ y_1)}\ f(Q_{22})\tag{5}
f(x,y) ≈ (x2 − x1) (y2 − y1)(x2 − x) (y2 − y) f(Q11) + (x2 − x1) (y2 − y1)(x − x1) (y2 − y) f(Q21)+ (x2 − x1) (y2 − y1)(x2 − x) (y − y1) f(Q12) + (x2 − x1) (y2 − y1)(x − x1) (y − y1) f(Q22)(5)
公式(5)换个形式
f
(
x
,
y
)
=
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
)
)
(6)
\large f(x, y)\ =\ \frac{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))\tag{6}
f(x,y) = (x2 − x1) (y2 − y1)1 (f(Q11)(x2 − x)(y2 − y) + f(Q21)(x − x1)(y2 − y))+ f(Q12)(x2 − x)(y − y1) + f(Q22)(x − x1)(y − y1))(6)
为什么要换成这样的形式,因为Q11,Q12,Q21,Q22四个点是相邻的,x2 - x1,y2 - y1都为1,所以上面的公式可以进一步简化为公式(7)
f
(
x
,
y
)
=
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
)
(7)
\large f(x, y)\ =\ 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)\tag{7}
f(x,y) = f(Q11)(x2 − x)(y2 − y) + f(Q21)(x − x1)(y2 − y))+ f(Q12)(x2 − x)(y − y1) + f(Q22)(x − x1)(y − y1)(7)
原图和目标图像的计算如公式(8)所示,计算的结果肯定是浮点类型,但是图像的像素点索引必须是整数,所以这里求出的坐标可以认为是虚拟坐标,也就是不存在的。我们可以用这个虚拟坐标的附近四个真实坐标推算出靠近这个虚拟坐标的坐标(注意这里是坐标,不是像素值)。为什么要用这么奇怪的公式(8),公式(8)其实是原图和目标图像的映射公式。在《最近邻插值法》里面提到了原始图像和目标图像的几何中心点需要重合,这样虚拟坐标周围的四个坐标都能参与计算。
{
s
r
c
_
x
=
(
d
e
s
_
x
+
0.5
)
∗
s
r
c
_
w
/
d
e
s
_
w
−
0.5
s
r
c
_
y
=
(
d
e
s
_
y
+
0.5
)
∗
s
r
c
_
h
/
d
e
s
_
h
−
0.5
(8)
\begin{cases} \large src\_x = (des\_x + 0.5)\ * src\_w\ /\ des\_w\ -\ 0.5\\ \large src\_y = (des\_y + 0.5)\ * src\_h\ /\ des\_h\ -\ 0.5 \end{cases}\tag{8}
{src_x=(des_x+0.5) ∗src_w / des_w − 0.5src_y=(des_y+0.5) ∗src_h / des_h − 0.5(8)
虚拟坐标可以拆分成(i+u),(j+v)的形式,u,v是虚拟坐标的小数部分,i,j是虚拟坐标的整数部分。我们想知道(i+u,j+v)的像素值就可以使用以下公式(9),因为坐标之间的关系求出来了,像素值就使用坐标点的关系求出。
f
(
i
+
u
,
j
+
v
)
=
f
(
i
,
j
)
(
1
−
u
)
(
1
−
v
)
+
f
(
i
,
j
+
1
)
(
1
−
u
)
v
+
f
(
i
+
1
,
j
)
u
(
1
−
v
)
+
f
(
i
+
1
,
j
+
1
)
u
v
(9)
\large f(i+u, j+v)\ =\ f(i,j)(1\ -\ u)(1\ -\ v)\ +\ f(i,j+1)(1\ -\ u)v\\+\ f(i+1,j)u(1\ -\ v)\ +\ f(i+1,j+1)uv\tag{9}
f(i+u,j+v) = f(i,j)(1 − u)(1 − v) + f(i,j+1)(1 − u)v+ f(i+1,j)u(1 − v) + f(i+1,j+1)uv(9)
在python代码里面只需要公式(8,9),其他的都是推演过程,同学们不要慌啊。
二、代码实现
import numpy as np
import matplotlib.pyplot as plt
image = cv2.imread("three_body.jpg")
image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
"""
根据当前坐标(x, y)最近的四个坐标像素值,求出当前(x, y)的像素值
x: 当前坐标x值(float)
y: 当前坐标y值(float)
src_img: 原图像(ndarray)
return: (x, y)的像素值(int)
"""
def four_pixels(x, y, src_img):
x = abs(x)#前面的点会出现负数,先abs一下
y = abs(y)
i, j = int(x), int(y)#x,y的整数部分
u = x - i#求出x的小数部分
v = y - j#求出y的小数部分
pixel = int((1-u) * (1-v) * src_img[i,j] + u * (1 - v) * src_img[i + 1, j] + (1-u) * v * src_img[i, j + 1] + u * v * src_img[i + 1, j + 1])#根据公式(9)
return pixel
"""
双线性插值法
src_img: 原图
des_shape: 目标图像shape
return: 缩放后图像
"""
def bilinear_interpolation(src_img, des_shape):
src_size = src_img.shape[:-1]
des_size = des_shape[:-1]
src_w, src_h = src_size
des_w, des_h = des_size
des_img = np.zeros(des_shape)
for i in range(des_w):
for j in range(des_h):
src_x = (i + 0.5) * (src_w / des_w) - 0.5#根据公式(8)
src_y = (j + 0.5) * (src_h / des_h) - 0.5#根据公式(8)
des_img[i, j, 0] = four_pixels(src_x, src_y, src_img[:, :, 0])
des_img[i, j, 1] = four_pixels(src_x, src_y, src_img[:, :, 1])
des_img[i, j, 2] = four_pixels(src_x, src_y, src_img[:, :, 2])
des_img = des_img.astype(np.uint8)
return des_img
bimage = bilinear_interpolation(image, (159, 401, 3))
plt.imshow(bimage)
还是借用三体图像,运行结果如下
总结
呼~
数学简直了,又爱又恨,但是代码就好多了。要记住,编程离不开数学,数学很重要,很重要,重要,要,切克闹。感谢你的观看,再会。