常见插值算法--拉格朗日插值、三次卷积插值、三次样条插值、兰克索斯插值
写在前面
本文简单介绍了几种常见的插值算法并附带了相应的python代码,本文公式使用latex编写,如有错误欢迎评论指出,如果谁知道如何修改latex字号也欢迎留言
关于一维、二维和多维插值
三次卷积插值、拉格朗日两点插值(线性插值)、兰克索斯插值在二维插值时改变x和y方向的计算顺序不影响最终结果,这三个也是图像缩放插值时常用的插值算法,而其他插值在改变计算顺序时会产生明显差异,多维的情况笔者没有尝试,读者可以自行尝试或推导
在待求像素的四邻像素中,将距离待求像素最近的像素值赋给待求像素
$p_{11}$ $p_{12}$
$p$
$p_{21}$ $p_{22}$
python代码
1 def NN_interpolation(srcImg, dstH, dstW): 2 scrH, scrW, _ = srcImg.shape 3 dstImg = np.zeros((dstH, dstW, 3), dtype=np.uint8) 4 for i in range(dstH - 1): 5 for j in range(dstW - 1): 6 scrX = round(i * (scrH / dstH)) 7 scrY = round(j * (scrW / dstW)) 8 dstImg[i, j] = srcImg[scrX, scrY] 9 return dstImg
拉格朗日插值(Lagrange Interpolation)
$拉格朗日插值法需要找到k个p_i(x)函数,使得每个函数分别在在x_i处取值为1,其余点取值为0,则y_ip_i(x)可以保证在x_i处取值为y_i,在其余点取值为0,因此L_k(x)能恰好经过所有点,这样的多项式被称为拉格朗日插值多项式,记为$
$L_k(x)=\sum_{i=1}^ky_ip_i(x)$
$p_i(x)=\prod_{j \neq i}^{1 \leq j \leq k}\frac{x-x_j}{x_i-x_j}$
$以四点即三次图像插值为例,因为横坐标间隔为1,则设四个点横坐标为-1、0、1和2,可得p_1(x)、p_2(x)、p_3(x)和p_4(x)$
$假设y_1、y_2、y_3和y_4分别为1、2、-1、4,则可得拉格朗日函数如下图所示,待插值点横坐标范围为[0,1]$
在K=2时
在k=2时,也被称为线性插值
通用公式
$p_1=\frac{x-x_2}{x_1-x_2}$
$p_2=\frac{x-x_1}{x_2-x_1}$
\begin{align}
L_2x &= p_1y_1+p_2y_2 \nonumber \\
&= \frac{x-x_2}{x_1-x_2}y_1 + \frac{x-x_1}{x_2-x_1}y_2 \nonumber
\end{align}
图像插值
像素分布如图所示
$p_{11}$ $p_{12}$
$p$
$p_{21}$ $p_{22}$
$即当x_{i+1}=x_i+1时,设p与p_{11}的横纵坐标差分别为dx和dy$
\begin{align}
L_2x &= \frac{x-x_2}{x_1-x_2}y_1 + \frac{x-x_1}{x_2-x_1}y_2 \nonumber \\
&= (x_2-x)y_1+(x-x_1)y_2 \nonumber \\
&= (1-dx)y_1+dxy_2 \nonumber \\
&= (y_2-y_1)dx+y_1 \nonumber
\end{align}
$L_2'x=y_2-y_1$
在K=3时
通用公式
$p_1=\frac{x-x_2}{x_1-x_2}\frac{x-x_3}{x_1-x_3}$
$p_2=\frac{x-x_1}{x_2-x_1}\frac{x-x_3}{x_2-x_3}$
$p_3=\frac{x-x_1}{x_3-x_1}\frac{x-x_2}{x_3-x_2}$
\begin{align}
L_3x &= p_1y_1+p_2y_2+p_3y_3 \nonumber \\
&= \frac{x-x_2}{x_1-x_2}\frac{x-x_3}{x_1-x_3}y_1+\frac{x-x_1}{x_2-x_1}\frac{x-x_3}{x_2-x_3}y_2+\frac{x-x_1}{x_3-x_1}\frac{x-x_2}{x_3-x_2}y_3 \nonumber
\end{align}
图像插值
像素分布如图所示
$p_{11}$ $p_{12}$ $p_{13}$
$p_{21}$ $p_{22}$ $p_{23}$
$p$
$p_{31}$ $p_{32}$ $p_{33}$
$即当x_{i+1}=x_i+1时,设p与p_{11}的横纵坐标差分别为dx和dy$
\begin{align}
L_3x &= \frac{x-x_2}{x_1-x_2}\frac{x-x_3}{x_1-x_3}y_1 + \frac{x-x_1}{x_2-x_1}\frac{x-x_3}{x_2-x_3}y_2 + \frac{x-x_1}{x_3-x_1}\frac{x-x_2}{x_3-x_2}y_3 \nonumber \\
&= \frac{-dx(1-dx)}{(-1)\cdot(-2)}y_1 + \frac{-(1+dx)(1-dx)}{1\cdot(-1)}y_2 + \frac{(1+dx)dx}{2\cdot 1}y_3 \nonumber \\
&= (\frac{1}{2}d^2x-\frac{1}{2}dx)y_1 - (d^2x-1)y_2 + (\frac{1}{2}d^2x+\frac{1}{2}dx)y_3 \nonumber \\
&= d^2x(\frac{1}{2}y_1-y_2+\frac{1}{2}y_3)+dx(-\frac{1}{2}y_1+\frac{1}{2}y_3)+y_2 \nonumber
\end{align}
$L_3'x=dx(y_1-2y_2+y_3)+(\frac{1}{2}y_3-\frac{1}{2}y_1)$
在K=4时
通用公式
$p_1=\frac{x-x_2}{x_1-x_2}\frac{x-x_3}{x_1-x_3}\frac{x-x_4}{x_1-x_4}$
$p_2=\frac{x-x_1}{x_2-x_1}\frac{x-x_3}{x_2-x_3}\frac{x-x_4}{x_2-x_4}$
$p_3=\frac{x-x_1}{x_3-x_1}\frac{x-x_2}{x_3-x_2}\frac{x-x_4}{x_3-x_4}$
$p_4=\frac{x-x_1}{x_4-x_1}\frac{x-x_2}{x_4-x_2}\frac{x-x_3}{x_4-x_3}$
\begin{align}
L_4x &= p_1y_1+p_2y_2+p_3y_3+p_4y_4 \nonumber \\
&= \frac{x-x_2}{x_1-x_2}\frac{x-x_3}{x_1-x_3}\frac{x-x_4}{x_1-x_4}y_1 + \frac{x-x_1}{x_2-x_1}\frac{x-x_3}{x_2-x_3}\frac{x-x_4}{x_2-x_4}y_2 + \frac{x-x_1}{x_3-x_1}\frac{x-x_2}{x_3-x_2}\frac{x-x_4}{x_3-x_4}y_3 + \frac{x-x_1}{x_4-x_1}\frac{x-x_2}{x_4-x_2}\frac{x-x_3}{x_4-x_3}y_4 \nonumber
\end{align}
图像插值
$p_{11}$ $p_{12}$ $p_{13}$ $p_{14}$
$p_{21}$ $p_{22}$ $p_{23}$ $p_{24}$
$p$
$p_{31}$ $p_{32}$ $p_{33}$ $p_{34}$
$p_{41}$ $p_{42}$ $p_{43}$ $p_{44}$
$即当x_{i+1}=x_i+1时,设p与p_{11}的横纵坐标差分别为dx和dy$
\begin{align}
L_4x &= \frac{x-x_2}{x_1-x_2}\frac{x-x_3}{x_1-x_3}\frac{x-x_4}{x_1-x_4}y_1
+ \frac{x-x_1}{x_2-x_1}\frac{x-x_3}{x_2-x_3}\frac{x-x_4}{x_2-x_4}y_2
+ \frac{x-x_1}{x_3-x_1}\frac{x-x_2}{x_3-x_2}\frac{x-x_4}{x_3-x_4}y_3
+ \frac{x-x_1}{x_4-x_1}\frac{x-x_2}{x_4-x_2}\frac{x-x_3}{x_4-x_3}y_4 \nonumber \\
&= \frac{dx[-(1-dx)][-(2-dx)]}{(-1)\cdot(-2)\cdot(-3)}y_1
+ \frac{(1+dx)[-(1-dx)][-(2-dx)]}{1\cdot(-1)\cdot(-2)}y_2
+ \frac{(1+dx)dx[-(2-dx)]}{2\cdot 1\cdot(-1)}y_3
+ \frac{(1+dx)dx[-(1-dx)]}{3\cdot 2\cdot 1}y_4 \nonumber \\
&= \frac{d^3x-3d^2x+2dx}{-6}y1 + \frac{d^3x-2d^2x-dx+2}{2}y_2 + \frac{d^3x-d^2x-2dx}{-2}y_3 + \frac{d^3x-dx}{6}y_4 \nonumber \\
&= d^3x(-\frac{1}{6}y_1+\frac{1}{2}y_2-\frac{1}{2}y_3+\frac{1}{6}y_4)+d^2x(\frac{1}{2}y_1-y_2+\frac{1}{2}y_3)+dx(-\frac{1}{3}y_1-\frac{1}{2}y_2+y_3-\frac{1}{6}y_4)+y_2 \nonumber
\end{align}
\begin{align}
L_4'x &= d^2x(-\frac{1}{2}y_1+\frac{3}{2}y_2-\frac{3}{2}y_3+\frac{1}{2}y_4)+dx(y_1-2y_2+y_3)+(-\frac{1}{3}y_1-\frac{1}{2}y_2+y_3-\frac{1}{6}y_4) \nonumber \\
&= -[\frac{1}{2}d^2x(y_1-3y_2+3y_3-y_4)-dx(y_1-2y_2+y_3)+\frac{1}{6}(2y_1+3y_2-6y_3+y_4)] \nonumber
\end{align}
python代码
插值核计算的时候乘法和加减法计算的顺序不同可能会导致结果存在细微的差异,读者可以自行研究一下
1 class BiLagrangeInterpolation: 2 @staticmethod 3 def LagrangeInterpolation2(x, y1, y2): 4 f1 = 1 - x 5 f2 = x 6 result = y1 * f1 + y2 * f2 7 return result 8 9 @staticmethod 10 def LagrangeInterpolation3(x, y1, y2, y3): 11 f1 = (x ** 2 - x) / 2.0 12 f2 = 1 - x ** 2 13 f3 = (x ** 2 + x) / 2.0 14 result = y1 * f1 + y2 * f2 + y3 * f3 15 return result 16 17 @staticmethod 18 def LagrangeInterpolation4(x, y1, y2, y3, y4): 19 f1 = - (x ** 3 - 3 * x ** 2 + 2 * x) / 6.0 20 f2 = (x ** 3 - 2 * x ** 2 - x + 2) / 2.0 21 f3 = - (x ** 3 - x ** 2 - 2 * x) / 2.0 22 f4 = (x ** 3 - x) / 6.0 23 result = y1 * f1 + y2 * f2 + y3 * f3 + y4 * f4 24 return result 25 26 def biLag2_2(self, srcImg, dstH, dstW): 27 dstH, dstW = int(dstH), int(dstW) 28 srcH, srcW, _ = srcImg.shape 29 srcImg = np.pad(srcImg, ((1, 1), (1, 1), (0, 0)), 'edge') 30 dstImg = np.zeros((dstH, dstW, 3), dtype=np.uint8) 31 for dstY in range(dstH): 32 for dstX in range(dstW): 33 for channel in [0, 1, 2]: 34 # p11 p12 35 # p 36 # p21 p22 37 # 储存为 p(y, x) 38 p = [dstY * srcH / dstH, dstX * srcW / dstW] 39 p11 = [math.floor(p[0]), math.floor(p[1])] 40 p12 = [p11[0], p11[1] + 1] 41 42 p21 = [p11[0] + 1, p11[1]] 43 p22 = [p21[0], p12[1]] 44 45 diff_y, diff_x = p[0] - p11[0], p[1] - p11[1] 46 r1 = self.LagrangeInterpolation2(diff_x, srcImg[p11[0], p11[1], channel], srcImg[p12[0], p12[1], channel]) 47 r2 = self.LagrangeInterpolation2(diff_x, srcImg[p21[0], p21[1], channel], srcImg[p22[0], p22[1], channel]) 48 49 c = self.LagrangeInterpolation2(diff_y, r1, r2) 50 51 dstImg[dstY, dstX, channel] = np.clip(c, 0, 255) 52 return dstImg 53 54 def biLag3_3(self, srcImg, dstH, dstW): 55 dstH, dstW = int(dstH), int(dstW) 56 srcH, srcW, _ = srcImg.shape 57 srcImg = np.pad(srcImg, ((1, 1), (1, 1), (0, 0)), 'edge') 58 dstImg = np.zeros((dstH, dstW, 3), dtype=np.uint8) 59 for dstY in range(dstH): 60 for dstX in range(dstW): 61 for channel in [0, 1, 2]: 62 # p11 p12 p13 63 # 64 # p21 p22 p23 65 # p 66 # p31 p32 p33 67 # 储存为 p(y, x) 68 p = [dstY * srcH / dstH, dstX * srcW / dstW] 69 p22 = [math.floor(p[0]), math.floor(p[1])] 70 p21 = [p22[0], p22[1] - 1] 71 p23 = [p22[0], p22[1] + 1] 72 73 p11 = [p21[0] - 1, p21[1]] 74 p12 = [p11[0], p22[1]] 75 p13 = [p11[0], p23[1]] 76 77 p31 = [p21[0] + 1, p21[1]] 78 p32 = [p31[0], p22[1]] 79 p33 = [p31[0], p23[1]] 80 81 diff_y, diff_x = p[0] - p22[0], p[1] - p22[1] 82 r1 = self.LagrangeInterpolation3(diff_x, srcImg[p11[0], p11[1], channel], srcImg[p12[0], p12[1], channel], srcImg[p13[0], p13[1], channel]) 83 r2 = self.LagrangeInterpolation3(diff_x, srcImg[p21[0], p21[1], channel], srcImg[p22[0], p22[1], channel], srcImg[p23[0], p23[1], channel]) 84 r3 = self.LagrangeInterpolation3(diff_x, srcImg[p31[0], p31[1], channel], srcImg[p32[0], p32[1], channel], srcImg[p33[0], p33[1], channel]) 85 86 c = self.LagrangeInterpolation3(diff_y, r1, r2, r3) 87 88 dstImg[dstY, dstX, channel] = np.clip(c, 0, 255) 89 return dstImg 90 91 def biLag4_4(self, srcImg, dstH, dstW): 92 dstH, dstW = int(dstH), int(dstW) 93 srcH, srcW, _ = srcImg.shape 94 srcImg = np.pad(srcImg, ((1, 2), (1, 2), (0, 0)), 'edge') 95 dstImg = np.zeros((dstH, dstW, 3), dtype=np.uint8) 96 for dstY in range(dstH): 97 for dstX in range(dstW): 98 for channel in [0, 1, 2]: 99 # p11 p12 p13 p14 100 # 101 # p21 p22 p23 p24 102 # p 103 # p31 p32 p33 p34 104 # 105 # p41 p42 p43 p44 106 # 储存为 p(y, x) 107 p = [dstY * srcH / dstH, dstX * srcW / dstW] 108 p22 = [math.floor(p[0]), math.floor(p[1])] 109 p21 = [p22[0], p22[1] - 1] 110 p23 = [p22[0], p22[1] + 1] 111 p24 = [p22[0], p22[1] + 2] 112 113 p11 = [p21[0] - 1, p21[1]] 114 p12 = [p11[0], p22[1]] 115 p13 = [p11[0], p23[1]] 116 p14 = [p11[0], p24[1]] 117 118 p31 = [p21[0] + 1, p21[1]] 119 p32 = [p31[0], p22[1]] 120 p33 = [p31[0], p23[1]] 121 p34 = [p31[0], p24[1]] 122 123 p41 = [p21[0] + 2, p21[1]] 124 p42 = [p41[0], p22[1]] 125 p43 = [p41[0], p23[1]] 126 p44 = [p41[0], p24[1]] 127 128 diff_y, diff_x = p[0] - p22[0], p[1] - p22[1] 129 r1 = self.LagrangeInterpolation4(diff_x, srcImg[p11[0], p11[1], channel], srcImg[p12[0], p12[1], channel], srcImg[p13[0], p13[1], channel], srcImg[p14[0], p14[1], channel]) 130 r2 = self.LagrangeInterpolation4(diff_x, srcImg[p21[0], p21[1], channel], srcImg[p22[0], p22[1], channel], srcImg[p23[0], p23[1], channel], srcImg[p24[0], p24[1], channel]) 131 r3 = self.LagrangeInterpolation4(diff_x, srcImg[p31[0], p31[1], channel], srcImg[p32[0], p32[1], channel], srcImg[p33[0], p33[1], channel], srcImg[p34[0], p34[1], channel]) 132 r4 = self.LagrangeInterpolation4(diff_x, srcImg[p41[0], p41[1], channel], srcImg[p42[0], p42[1], channel], srcImg[p43[0], p43[1], channel], srcImg[p44[0], p44[1], channel]) 133 134 c = self.LagrangeInterpolation4(diff_y, r1, r2, r3, r4) 135 136 dstImg[dstY, dstX, channel] = np.clip(c, 0, 255) 137 return dstImg
三次卷积插值法(Cubic Convolution Interpolation)
$使用上图中的卷积核进行加权平均计算,卷积核为u(s),四个等距(距离为1)的采样点记为x_0、x_1、x_2和x_3,采样数值记为y_0、y_1、y_2和y_3,且保证四个点均在[-2,2]区间上,计算得到g(x),假设y_1、y_2、y_3和y_4分别为1、2、-1、4,则可得三次卷积插值函数如下图所示,待插值点横坐标范围为[0,1]$
公式推导
$设u(s)=\begin{cases} A_1|s|^3+B_1|s|^2+C_1|s|+D_1, &0<|s|<1 \\ A_2|s|^3+B_2|s|^2+C_2|s|+D_2, &1<|s|<2 \\ 1, &s=0 \\ 0, &otherwise \end{cases}$
$\because函数在s=0,1,2处连续$
$\therefore\begin{cases} 1=u(0^+)=D_1 \\ 0=u(1^-)=A_1+B_1+C_1+D_1 \\ 0=u(1^+)=A_2+B_2+C_2+D_2 \\ 0=u(2^-)=8A_2+4B_2+2C_2+D_2 \end{cases} (1)$
$\because函数在s=0,1,2处导函数连续$
$\therefore\begin{cases} u'(0^-)=u'(0+) \\ u'(1^-)=u'(1+) \\ u'(2^-)=u'(2+)\end{cases} \Rightarrow \begin{cases} -C_1=C_1 \\ 3A_1+2B_1+C_1=3A_2+2B_2+C_2 \\ 12A_2+4B_2+C+2=0 \end{cases} ~~~~ (2)$
$联立方程组(1)(2),设A_2=a,解得$
$\begin{cases} A_1=a+2 \\ B_1=-(a+3) \\ C_1=0 \\ D_1=1 \\ A_2=a \\ B_2=-5a \\ C_2=8a \\ D_2=-4a \end{cases}\Rightarrow u(s)=\begin{cases} (a+2)|s|^3-(a+3)|s|^2+1, &0<|s|<1 \\ A_2|s|^3+B_2|s|^2+C_2|s|+D_2, &1<|s|<2\\ 1, &s=0 \\ 0, &otherwise \end{cases}$
$\because g(x)=\sum_kC_ku(s+j-k), ~~~~k=j-1,j, j+1,j+2且0<s<1$
$又\because \begin{cases}\begin{align} u(s+1)&=as^3-2as^2+as \nonumber \\ u(s)&=(a+2)s^3-(a+3)s^2+1 \nonumber \\ u(s-1)&=-(a+2)s^3+(2a+3)s^2-as \nonumber \\ u(s-2)&=-as^3+as^2 \nonumber \end{align}\end{cases}$
$\begin{align} \therefore g(x) &= C_{j-1}u(s+1)+C_{j}u(s)+C_{j+1}u(s-1)+C_{j+2}u(s-2) \nonumber \\ &= C_{j-1}(as^3-2as^2+as)+C_j[(a+2)s^3-(a+3)s^2+1]+C_{j+1}[-(a+2)s^3+(2a+3)s^2-as]+C_{j+2}[-a^3+as^2] \nonumber \\ &= s^3[aC_{j-1}+(a+2)C_j-(a+2)C_{j+1}-aC_{j+2}]+s^2[-2aC_{j-1}-(a+3)C_j+(2a+3)C_{j+1}+aC_{j+2}]+s[aC_{j-1}-aC_{j+1}]+C_j \nonumber \end{align} ~~(3)$
$f在x_j处泰勒展开得到$
$f(x)=f(x_j)+f'(x_j)(x-x_j)+\frac{1}{2}f''(x_j)(x-x_j)^2+\cdots$
$\therefore \begin{cases} f(x_{j+1})=f(x_j)+f'(x_j)(x_{j+1}-x_j)+\frac{1}{2}f''(x_j)(x_{j+1}-x_j)^2+\cdots \\ f(x_{j+2})=f(x_j)+f'(x_j)(x_{j+2}-x_j)+\frac{1}{2}f''(x_j)(x_{j+2}-x_j)^2+\cdots \\ f(x_{j-1})=f(x_j)+f'(x_j)(x_{j-1}-x_j)+\frac{1}{2}f''(x_j)(x_{j-1}-x_j)^2+\cdots \end{cases}$
$令x_{j+1}-x_j=h$
$\because x_{i+1}=x_i+1$
$\therefore x_{j+2}-x_j=2h,x_{j-1}-x_j=-h$
$\therefore \begin{cases} f(x_{j+2})=f(x_j)+2f'(x_j)h+2f''(x_j)h^2+\cdots \\ f(x_{j+1})=f(x_j)+f'(x_j)h+\frac{1}{2}f''(x_j)h^2+\cdots \\ f(x_{j-1})=f(x_j)-f'(x_j)h+\frac{1}{2}f''(x_j)h^2+\cdots \end{cases}$
$\therefore \begin{cases} c_{j-1}=f(x_j)-f'(x_j)h+\frac{1}{2}f''(x_j)h^2+o(h^3) \\ c_j=f(x_j) \\ c_{j+1}=f(x_j)+f'(x_j)h+\frac{1}{2}f''(x_j)h^2+o(h^3) \\ c_{j+2}=f(x_j)+2f'(x_j)h+2f''(x_j)h^2+o(h^3) \end{cases} ~~ (4) $
$将(4)代入(3),得$
$g(x)=-(2a+1)[2hf'(x_j)+h^2f''(x_j)]s^3+[(6a+3)hf'(x_j)+\frac{4a+3}{2}h^2f''(x_j)]s^2-2ahf'(x_j)s+f(x_j)+o(h^3)$
$\because h=1,s=x-x_J$
$\therefore sh=x-x_j$
$\begin{align}\therefore f(x)&= f(x_j)+f'(x_j)(x-x_j)+\frac{1}{2}f''(x_j)(x-x_j)^2+o(h^3) \nonumber \\ &= f(x_j)+f'(x_j)sh+\frac{1}{2}f''(x_j)s^2h^2+o(h^3) \nonumber \end{align}$
$\therefore f(x)-g(x)=(2a+1)[2hf'(x_j)+h^2f''(x_j)]s^3-(2a+1)[3hf'(x_j)+h^2f''(x_j)]s^2+[(2a+1)hf'(x_j)]s+o(h^3)$
$\because 期望f(x)-g(x)趋于0$
$\therefore 2a+1=0 \Rightarrow a=-\frac{1}{2}$
$\therefore u(s)=\begin{cases} \frac{3}{2}|s|^3-\frac{5}{2}|s|^2+1, &0<|s|<1 \\ -\frac{1}{2}|s|^3+\frac{5}{2}|s|^2-4|s|+2, &1<|s|<2 \\ 1, &s=0 \\ 0, &otherwise \end{cases}$
$\therefore g(s)=s^3[-\frac{1}{2}c_{j-1}+\frac{3}{2}c_j-\frac{3}{2}c_{j+1}+\frac{1}{2}c_{j+2}]+s^2[c_{j-1}-\frac{5}{2}c_j+2c_{j+1}-\frac{1}{2}c_{j+2}]+s[-\frac{1}{2}c_{j-1}+\frac{1}{2}c_{j+1}]+c_j$
图像插值
$p_{11}$ $p_{12}$ $p_{13}$ $p_{14}$
$p_{21}$ $p_{22}$ $p_{23}$ $p_{24}$
$p$
$p_{31}$ $p_{32}$ $p_{33}$ $p_{34}$
$p_{41}$ $p_{42}$ $p_{43}$ $p_{44}$
python代码
1 class BiCubicConvInterpolation: 2 @staticmethod 3 def CubicConvInterpolation1(p0, p1, p2, p3, s): 4 # 用g(s)公式计算,已经将四个u(s)计算完毕并整理 5 # as^3 + bs^2 + cs + d 6 a = 0.5 * (-p0 + 3.0 * p1 - 3.0 * p2 + p3) 7 b = 0.5 * (2.0 * p0 - 5.0 * p1 + 4.0 * p2 - p3) 8 c = 0.5 * (-p0 + p2) 9 d = p1 10 return d + s * (c + s * (b + s * a)) 11 12 @staticmethod 13 def CubicConvInterpolation2(s): 14 # 用u(s)公式计算 15 s = abs(s) 16 if s <= 1: 17 return 1.5 * s ** 3 - 2.5 * s ** 2 + 1 18 elif s <= 2: 19 return -0.5 * s ** 3 + 2.5 * s ** 2 - 4 * s + 2 20 else: 21 return 0 22 23 def biCubic1(self, srcImg, dstH, dstW): 24 # p11 p12 p13 p14 25 # 26 # p21 p22 p23 p24 27 # p 28 # p31 p32 p33 p34 29 # 30 # p41 p42 p43 p44 31 dstH, dstW = int(dstH), int(dstW) 32 scrH, scrW, _ = srcImg.shape 33 srcImg = np.pad(srcImg, ((1, 1), (1, 1), (0, 0)), 'edge') 34 dstImg = np.zeros((dstH, dstW, 1), dtype=np.uint8) 35 for dstY in range(dstH): 36 for dstX in range(dstW): 37 for channel in [0]: 38 y = dstY * scrH / dstH 39 x = dstX * scrW / dstW 40 y1 = math.floor(y) 41 x1 = math.floor(x) 42 43 array = [] 44 for i in [-1, 0, 1, 2]: 45 temp = self.CubicConvInterpolation1(srcImg[y1 + i, x1 - 1, channel], 46 srcImg[y1 + i, x1, channel], 47 srcImg[y1 + i, x1 + 1, channel], 48 srcImg[y1 + i, x1 + 2, channel], 49 x - x1) 50 array.append(temp) 51 52 temp = self.CubicConvInterpolation1(array[0], array[1], array[2], array[3], y - y1) 53 dstImg[dstY, dstX, channel] = np.clip(temp, 0, 255) 54 55 return dstImg 56 57 def biCubic2(self, srcImg, dstH, dstW): 58 # p11 p12 p13 p14 59 # 60 # p21 p22 p23 p24 61 # p 62 # p31 p32 p33 p34 63 # 64 # p41 p42 p43 p44 65 dstH, dstW = int(dstH), int(dstW) 66 scrH, scrW, _ = srcImg.shape 67 srcImg = np.pad(srcImg, ((1, 1), (1, 1), (0, 0)), 'edge') 68 dstImg = np.zeros((dstH, dstW, 3), dtype=np.uint8) 69 for dstY in range(dstH): 70 for dstX in range(dstW): 71 for channel in [0, 1, 2]: 72 y = dstY * scrH / dstH 73 x = dstX * scrW / dstW 74 y1 = math.floor(y) 75 x1 = math.floor(x) 76 77 array = [] 78 for i in [-1, 0, 1, 2]: 79 temp = 0 80 for j in [-1, 0, 1, 2]: 81 temp += srcImg[y1 + i, x1 + j, channel] * self.CubicConvInterpolation2(x - (x1 + j)) 82 array.append(temp) 83 84 temp = 0 85 for i in [-1, 0, 1, 2]: 86 temp += array[i + 1] * self.CubicConvInterpolation2(y - (y1 + i)) 87 dstImg[dstY, dstX, channel] = np.clip(temp, 0, 255) 88 89 return dstImg
三次样条插值
$在n-1个区间上寻找n-1个三次曲线,使其满足相邻曲线在端点处值相等、一阶导数相等,二阶导数相等,在加以边界条件后可得每个曲线的方程,然后沿x轴依次偏移对应的距离即可得到插值结果,如仅需要特定范围内的结果,则可以大幅减少计算量$
公式推导
$设S_i(x)=a_i+b_i(x-x_i)+c_i(x-x_i)^2+d_i(x-x_i)^3, ~~~~i=0,1,...,n-1$
$则 \begin{cases} S_i'(x)=b_i+2c_i(x-x_i)+3d_i(x-x_i)^2\\ S_i''(x)=2c_i+6d_i(x-x_i)\\ S_i'''(x)=6d_i\\ \end{cases} ~~~~i=0,1,...,n-1$
$设h_i(x)=x_{i+1}-x_i,可得$
$\begin{cases} S_i(x)=a_i+b_ih_i+c_ih_i^2+d_ih_i^3\\ S_i'(x)=b_i+2c_ih_i+3d_ih_i^2\\ S_i''(x)=2c_i+6d_ih_i\\ S_i'''(x)=6d_i\\ \end{cases} ~~~~i=0,1,...,n-1$
$\because S_i(x)过点(x_i,y_i)$
$\therefore S_i(x)=a_i=y+i, ~~~~i=0,1,...,n-1 ~~~~~~(1)$
$\because S_i(x)与S_{i+1}(x)在X_{i+1}处相等$
$\therefore S_i(x_{i+1})=S_{i+1}(x_{i+1})$
$\Rightarrow a_i+b_ih_i+c_ih_i^2+d_ih_i^3=y_{i+1}, ~~~~i=0,1,...,n-2~~~~~~(2)$
$\because S_i'(x)与S_{i+1}'(x)在X_{i+1}处相等$
$\therefore S_i'(x)-S_{i+1}'(x)=0$
$\Rightarrow b_i+2c_ih_i+3d_ih_i^2-b_{i+1}=0~~~~~~(3)$
$\because S_i''(x)与S_{i+1}''(x)在X_{i+1}处相等$
$\therefore S_i''(x)-S_{i+1}''(x)=0$
$\Rightarrow 2c_i+6d_ih_i-2c_{i+1}=0, ~~~~i=0,1,...,n-2~~~~~~(4)$
$设m_i=S_i(x_i)=2c_i,即c_i=\frac{1}{2}m_i, ~~~~i=0,1,...,n-1~~~~~~(5)$
$将(5)代入(4),得$
$2c_i+6d_ih_i-2c_{i+1}=0$
$\Rightarrow m_i+6h_id_i-m_{i+1}=0$
$\Rightarrow d_i=\frac{m_{i+1}-m_i}{6h_i}, ~~~~i=0,1,...,n-2~~~~~~(6)$
$将(1)(5)(6)代入(2),得$
\begin{align}
&a_i+b_ih_i+c_ih_i^2+d_ih_i^3=y_{i+1} \nonumber \\
\Rightarrow&y_i+b_ih_i+\frac{1}{2}m_ih_i^2+\frac{m_{i+1}-m_i}{6h_i}h_i^3=y_{i+1} \nonumber \\
\Rightarrow&b_i=\frac{y_{i+1}-y_i}{h_i}-\frac{1}{2}m_ih_i-\frac{1}{6}(m_{i+1}-m_i)h_i \nonumber \\
\Rightarrow&b_i=\frac{y_{i+1}-y_i}{h_i}-\frac{1}{3}m_ih_i-\frac{1}{6}m_{i+1}h_i, ~~~~i=0,1,...,n-2~~~~~~(7) \nonumber
\end{align}
$将(5)(6)(7)代入(3),得$
\begin{align}
&\frac{y_{i+1}-y{i}}{h_i}-\frac{1}{3}m_ih_i-\frac{1}{6}m_{i+1}h_i+2\cdot\frac{1}{2}m_ih_i+3\frac{m_{i+1}-m_i}{6h_i}h_i^2-(\frac{y_{i+2}-y_{i+1}}{h_{i+1}}-\frac{1}{3}m_{i+1}h_{i+1}-\frac{1}{6}m_{i+2}h_{i+1})=0 \nonumber \\
\Rightarrow&\frac{y_{i+1}-y{i}}{h_i}-\frac{1}{3}m_ih_i-\frac{1}{6}m_{i+1}h_i+m_ih_i+\frac{1}{2}(m_{i+1}-m_i)h_i-\frac{y_{i+2}-y_{i+1}}{h_{i+1}}+\frac{1}{3}m_{i+1}h_{i+1}+\frac{1}{6}m_{i+2}h_{i+1}=0 \nonumber \\
\Rightarrow&m_ih_i(-\frac{1}{3}+1-\frac{1}{2})+m_{i+1}h_i(-\frac{1}{6}+\frac{1}{2})+\frac{1}{3}m_{i+1}h_{i+1}+\frac{1}{6}m_{i+2}h_{i+1}=\frac{y_{i+2}-y_{i+1}}{h_{i+1}}-\frac{y_{i+1}-y_{i}}{h_{i}} \nonumber \\
\Rightarrow&\frac{1}{6}(m_ih_i+2m_{i+1}h_i+2m_{i+1}h_{i+1}+m_{i+2}h_{i+1})=\frac{y_{i+2}-y_{i+1}}{h_{i+1}}-\frac{y_{i+1}-y_{i}}{h_{i}} \nonumber \\
\Rightarrow&m_ih_i+2m_{i+1}(h_i+h_{i+1})+m_{i+2}h_{i+1}=6(\frac{y_{i+2}-y_{i+1}}{h_{i+1}}-\frac{y_{i+1}-y_{i}}{h_{i}}), ~~~~i=0,1,...,n-2~~~~~~(8) \nonumber
\end{align}
$由(8)可知i=0,1,...,n-2,则有m_0,m_1,...,m_n,需要两个额外条件方程组才有解$
自然边界(Natural)
$m_0=0,m_n=0$
\begin{bmatrix} \tiny
1 & 0 & 0 & 0 & 0 & \cdots & 0\\
h_0 & 2(h_0+h_1) & h_1 & 0 & 0 & \cdots & 0\\
0 & h_1 & 2(h_1+h_2) & h_2 & 0 & \cdots & 0\\
0 & 0 & h_2 & 2(h_2+h_3) & h_3 & \cdots & 0\\
\vdots& & & \ddots & \ddots & \ddots & \vdots\\
0 & \cdots & & & h_{n-2} & 2(h_{n-2}+h_{n-1}) & h_{n-1}\\
0 & \cdots & & & 0 & 0 & 1
\end{bmatrix}
\begin{bmatrix}
m_0\\m_1\\m_2\\m_3\\\vdots\\m_{n-1}\\m_n
\end{bmatrix}=6
\begin{bmatrix}
0\\
\frac{y_2-y_1}{h_1}-\frac{y_1-y_0}{h_0}\\
\frac{y_3-y_2}{h_2}-\frac{y_2-y_1}{h_1}\\
\frac{y_4-y_3}{h_3}-\frac{y_3-y_2}{h_2}\\
\vdots\\
\frac{y_n-y_{n-1}}{h_{n-1}}-\frac{y_{n-1}-y_{n-2}}{h_{n-2}}\\
0
\end{bmatrix}
固定边界(Clamped)
\begin{align}
&\begin{cases}
S_0'(x_0)=A\\
S_{n-1}'(x_n)=B
\end{cases} \nonumber \\
\Rightarrow&\begin{cases}
b_0=A\\
b_{n-1}+2c_{n-1}h_{n-1}+3d_{n-1}h_{n-1}^2=B
\end{cases} \nonumber \\
\Rightarrow&\begin{cases}
A=\frac{y_1-y_0}{h_0}-\frac{h_0}{2}m_0-\frac{h_0}{6}(m_1-m_0)\\
B=\frac{y_n-y_{n-1}}{h_{n-1}}-\frac{1}{3}m_{n-1}h_{n-1}+m_{n-1}h_{n-1}+\frac{1}{2}m_nh_{n-1}-\frac{1}{2}m_{n-1}h_{n-1}
\end{cases} \nonumber \\
\Rightarrow&\begin{cases}
2h_0m_0+h_0m_1=6(\frac{y_1-y_0}{h_0}-A)\\
h_{n-1}m_{n-1}+2h_{n-1}m_{n}=6(B-\frac{y_n-y_{n-1}}{h_{n-1}})
\end{cases} \nonumber \\
\end{align}
\begin{bmatrix}
2 & 1 & 0 & 0 & 0 & \cdots & 0\\
h_0 & 2(h_0+h_1) & h_1 & 0 & 0 & \cdots & 0\\
0 & h_1 & 2(h_1+h_2) & h_2 & 0 & \cdots & 0\\
0 & 0 & h_2 & 2(h_2+h_3) & h_3 & \cdots & 0\\
\vdots& & & \ddots & \ddots & \ddots & \vdots\\
0 & \cdots & & & h_{n-2} & 2(h_{n-2}+h_{n-1}) & h_{n-1}\\
0 & \cdots & & & 0 & 1 & 2
\end{bmatrix}
\begin{bmatrix}
m_0\\m_1\\m_2\\m_3\\\vdots\\m_{n-1}\\m_n
\end{bmatrix}=6
\begin{bmatrix}
\frac{y_1-y_0}{h_0}-A\\
\frac{y_2-y_1}{h_1}-\frac{y_1-y_0}{h_0}\\
\frac{y_3-y_2}{h_2}-\frac{y_2-y_1}{h_1}\\
\frac{y_4-y_3}{h_3}-\frac{y_3-y_2}{h_2}\\
\vdots\\
\frac{y_n-y_{n-1}}{h_{n-1}}-\frac{y_{n-1}-y_{n-2}}{h_{n-2}}\\
B-\frac{y_n-y_{n-1}}{h_{n-1}}
\end{bmatrix}
非节点边界(Not-A-Knot)
\begin{align}
&\begin{cases}
S_0'''(x_1)=S_1'''(x_1)\\
S_{n-2}'''(x_{n-1})=S_{n-1}'''(x_{n-1})
\end{cases} \nonumber \\
\Rightarrow&\begin{cases}
6\cdot\frac{m_1-m_0}{6h_0}=6\cdot\frac{m_2-m_1}{6h_1}\\
6\cdot\frac{m_{n-1}-m_{n-2}}{6h_{n-2}}=6\cdot\frac{m_n-m_{n-1}}{6h_{n-1}}
\end{cases} \nonumber \\
\Rightarrow&\begin{cases}
h_1(m_1-m_0)=h_0(m_2-m_1)\\
h_{n-1}(m_{n-1}-m_{n-2})=h_{n-2}(m_n-m_{n-1})
\end{cases} \nonumber \\
\Rightarrow&\begin{cases}
-h_1m_0+(h_1+h_0)m_1-h_0m_2=0\\
-h_{n-1}m_{n-2}+(h_{n-1}+h_{n-2})m_{n-1}-h_{n-2}m_n=0
\end{cases} \nonumber \\
\end{align}
\begin{bmatrix}
-h_1 & h_0+h_1 & -h_0 & 0 & 0 & \cdots & 0\\
h_0 & 2(h_0+h_1) & h_1 & 0 & 0 & \cdots & 0\\
0 & h_1 & 2(h_1+h_2) & h_2 & 0 & \cdots & 0\\
0 & 0 & h_2 & 2(h_2+h_3) & h_3 & \cdots & 0\\
\vdots& & & \ddots & \ddots & \ddots & \vdots\\
0 & \cdots & & & h_{n-2} & 2(h_{n-2}+h_{n-1}) & h_{n-1}\\
0 & \cdots & & & -h_{n-1} & h_{n-1}+h_{n-2} & -h_{n-2}
\end{bmatrix}
\begin{bmatrix}
m_0\\m_1\\m_2\\m_3\\\vdots\\m_{n-1}\\m_n
\end{bmatrix}=6
\begin{bmatrix}
0\\
\frac{y_2-y_1}{h_1}-\frac{y_1-y_0}{h_0}\\
\frac{y_3-y_2}{h_2}-\frac{y_2-y_1}{h_1}\\
\frac{y_4-y_3}{h_3}-\frac{y_3-y_2}{h_2}\\
\vdots\\
\frac{y_n-y_{n-1}}{h_{n-1}}-\frac{y_{n-1}-y_{n-2}}{h_{n-2}}\\
0
\end{bmatrix}
在n=4时
通用公式
自然边界
\begin{bmatrix}
1 & 0 & 0 & 0 \\
h_0 & 2(h_0+h_1) & h_1 & 0 \\
0 & h_1 & 2(h_1+h_2) & h_2 \\
0 & 0 & 0 & 1 \\
\end{bmatrix}
\begin{bmatrix}
m_0\\m_1\\m_2\\m_3
\end{bmatrix}=6
\begin{bmatrix}
0\\
\frac{y_2-y_1}{h_1}-\frac{y_1-y_0}{h_0}\\
\frac{y_3-y_2}{h_2}-\frac{y_2-y_1}{h_1}\\
0
\end{bmatrix}
固定边界
\begin{bmatrix}
2 & 1 & 0 & 0 \\
h_0 & 2(h_0+h_1) & h_1 & 0 \\
0 & h_1 & 2(h_1+h_2) & h_2 \\
0 & 0 & 1 & 2 \\
\end{bmatrix}
\begin{bmatrix}
m_0\\m_1\\m_2\\m_3
\end{bmatrix}=6
\begin{bmatrix}
\frac{y_1-y_0}{h_0}-A\\
\frac{y_2-y_1}{h_1}-\frac{y_1-y_0}{h_0}\\
\frac{y_3-y_2}{h_2}-\frac{y_2-y_1}{h_1}\\
B-\frac{y_3-y_2}{h_2}
\end{bmatrix}
非节点边界
\begin{bmatrix}
-h_1 & h_0+h_1 & -h_0 & 0 \\
h_0 & 2(h_0+h_1) & h_1 & 0 \\
0 & h_1 & 2(h_1+h_2) & h_2 \\
0 & -h_2 & h_1+h_2 & -h_1 \\
\end{bmatrix}
\begin{bmatrix}
m_0\\m_1\\m_2\\m_3
\end{bmatrix}=6
\begin{bmatrix}
0\\
\frac{y_2-y_1}{h_1}-\frac{y_1-y_0}{h_0}\\
\frac{y_3-y_2}{h_2}-\frac{y_2-y_1}{h_1}\\
0
\end{bmatrix}
图像插值
$x_{i+1}-x_i=1 \Rightarrow h_i(x)=1$
$在n=4时,即四个点时如下所示$
$p_{11}$ $p_{12}$ $p_{13}$ $p_{14}$
$p_{21}$ $p_{22}$ $p_{23}$ $p_{24}$
$p$
$p_{31}$ $p_{32}$ $p_{33}$ $p_{34}$
$p_{41}$ $p_{42}$ $p_{43}$ $p_{44}$
自然边界(可用TDMA或化简计算)
\begin{bmatrix}
1 & 0 & 0 & 0 \\
1 & 4 & 1 & 0 \\
0 & 1 & 4 & 1 \\
0 & 0 & 0 & 1 \\
\end{bmatrix}
\begin{bmatrix}
m_0\\m_1\\m_2\\m_3
\end{bmatrix}=6
\begin{bmatrix}
0\\
y_0+y_2-2y_1\\
y_1+y_3-2y_2\\
0
\end{bmatrix}
固定边界(只能用TDMA计算)
\begin{bmatrix}
2 & 1 & 0 & 0 \\
1 & 4 & 1 & 0 \\
0 & 1 & 4 & 1 \\
0 & 0 & 1 & 2 \\
\end{bmatrix}
\begin{bmatrix}
m_0\\m_1\\m_2\\m_3
\end{bmatrix}=6
\begin{bmatrix}
y_1-y_0-A\\
y_0+y_2-2y_1\\
y_1+y_3-2y_2\\
y_2-y_3+B
\end{bmatrix}
非节点边界(只能化简计算)
\begin{bmatrix}
-1 & 2 & -1 & 0 \\
1 & 4 & 1 & 0 \\
0 & 1 & 4 & 1 \\
0 & -1 & 2 & -1 \\
\end{bmatrix}
\begin{bmatrix}
m_0\\m_1\\m_2\\m_3
\end{bmatrix}=6
\begin{bmatrix}
0\\
y_0+y_2-2y_1\\
y_1+y_3-2y_2\\
0
\end{bmatrix}
python代码
1 class BiSplineInterpolation: 2 @staticmethod 3 def TDMA(a, b, c, d): 4 n = len(d) 5 6 c[0] = c[0] / b[0] 7 d[0] = d[0] / b[0] 8 9 for i in range(1, n): 10 coef = 1.0 / (b[i] - a[i] * c[i - 1]) 11 c[i] = coef * c[i] 12 d[i] = coef * (d[i] - a[i] * d[i - 1]) 13 14 for i in range(n - 2, -1, -1): 15 d[i] = d[i] - c[i] * d[i + 1] 16 17 return d 18 19 @staticmethod 20 def Simplified_Natural4(y1, y2, y3, y4): 21 # 四点自然边界化简公式 22 d1 = y1 + y3 - 2 * y2 23 d2 = y2 + y4 - 2 * y3 24 25 k0 = 0 26 k1 = (4 * d1 - d2) * 0.4 27 k2 = (4 * d2 - d1) * 0.4 28 k3 = 0 29 30 return [k0, k1, k2, k3] 31 32 @staticmethod 33 def Simplified_Not_A_Knot4(y1, y2, y3, y4): 34 # 四点非节点边界化简公式 35 d1 = y1 + y3 - 2 * y2 36 d2 = y2 + y4 - 2 * y3 37 38 k0 = 2 * d1 - d2 39 k1 = d1 40 k2 = d2 41 k3 = 2 * d2 - d1 42 43 return [k0, k1, k2, k3] 44 45 # TDMA矩阵说明 46 # a0 和 c3 没有实际意义,占位用 47 # a0 [b0 c0 0 0 ] [x0] [d0] 48 # [a1 b1 c1 0 ] [x1] = [d1] 49 # [0 a2 b2 c2] [x2] [d2] 50 # [0 0 a3 b3] c3 [x3] [d3] 51 52 def SplineInterpolationNatural4(self, x, y1, y2, y3, y4): 53 # 用TDMA计算 54 # matrix_a = [0, 1, 1, 0] 55 # matrix_b = [1, 4, 4, 1] 56 # matrix_c = [0, 1, 1, 0] 57 # matrix_d = [0, 6 * (y1 + y3 - 2 * y2), 6 * (y2 + y4 - 2 * y3), 0] 58 # matrix_x = self.TDMA(matrix_a, matrix_b, matrix_c, matrix_d) 59 60 # 化简计算 61 matrix_x = self.Simplified_Natural4(y1, y2, y3, y4) 62 63 a = y2 64 b = y3 - y2 - matrix_x[1] / 3.0 - matrix_x[2] / 6.0 65 c = matrix_x[1] / 2.0 66 d = (matrix_x[2] - matrix_x[1]) / 6.0 67 68 s = a + b * x + c * x * x + d * x * x * x 69 return s 70 71 def SplineInterpolationClamped4(self, x, y1, y2, y3, y4): 72 # 仅有TDMA计算,无法化简 73 A, B = 1, 1 74 75 matrix_a = [0, 1, 1, 1] 76 matrix_b = [2, 4, 4, 2] 77 matrix_c = [1, 1, 1, 0] 78 matrix_d = [6 * (y2 - y1 - A), 6 * (y1 + y3 - 2 * y2), 6 * (y2 + y4 - 2 * y3), 6 * (B - y4 + y3)] 79 matrix_x = self.TDMA(matrix_a, matrix_b, matrix_c, matrix_d) 80 81 a = y2 82 b = y3 - y2 - matrix_x[1] / 3.0 - matrix_x[2] / 6.0 83 c = matrix_x[1] / 2.0 84 d = (matrix_x[2] - matrix_x[1]) / 6.0 85 86 s = a + b * x + c * x * x + d * x * x * x 87 return s 88 89 def SplineInterpolationNotAKnot4(self, x, y1, y2, y3, y4): 90 # 无法使用TDMA计算 91 matrix_x = self.Simplified_Not_A_Knot4(y1, y2, y3, y4) 92 93 a = y2 94 b = y3 - y2 - matrix_x[1] / 3.0 - matrix_x[2] / 6.0 95 c = matrix_x[1] / 2.0 96 d = (matrix_x[2] - matrix_x[1]) / 6.0 97 98 s = a + b * x + c * x * x + d * x * x * x 99 return s 100 101 def biSpline4(self, srcImg, dstH, dstW): 102 dstH, dstW = int(dstH), int(dstW) 103 srcH, srcW, _ = srcImg.shape 104 srcImg = np.pad(srcImg, ((1, 2), (1, 2), (0, 0)), 'edge') 105 dstImg = np.zeros((dstH, dstW, 3), dtype=np.uint8) 106 for dstY in range(dstH): 107 for dstX in range(dstW): 108 for channel in [0, 1, 2]: 109 # p11 p12 p13 p14 110 # 111 # p21 p22 p23 p24 112 # p 113 # p31 p32 p33 p34 114 # 115 # p41 p42 p43 p44 116 # 储存为 p(y, x) 117 p = [dstY * srcH / dstH, dstX * srcW / dstW] 118 p22 = [math.floor(p[0]), math.floor(p[1])] 119 p21 = [p22[0], p22[1] - 1] 120 p23 = [p22[0], p22[1] + 1] 121 p24 = [p22[0], p22[1] + 2] 122 123 p11 = [p21[0] - 1, p21[1]] 124 p12 = [p11[0], p22[1]] 125 p13 = [p11[0], p23[1]] 126 p14 = [p11[0], p24[1]] 127 128 p31 = [p21[0] + 1, p21[1]] 129 p32 = [p31[0], p22[1]] 130 p33 = [p31[0], p23[1]] 131 p34 = [p31[0], p24[1]] 132 133 p41 = [p21[0] + 2, p21[1]] 134 p42 = [p41[0], p22[1]] 135 p43 = [p41[0], p23[1]] 136 p44 = [p41[0], p24[1]] 137 138 diff_y, diff_x = p[0] - p22[0], p[1] - p22[1] 139 r1 = self.SplineInterpolationNatural4(diff_x, srcImg[p11[0], p11[1], channel], srcImg[p12[0], p12[1], channel], srcImg[p13[0], p13[1], channel], srcImg[p14[0], p14[1], channel]) 140 r2 = self.SplineInterpolationNatural4(diff_x, srcImg[p21[0], p21[1], channel], srcImg[p22[0], p22[1], channel], srcImg[p23[0], p23[1], channel], srcImg[p24[0], p24[1], channel]) 141 r3 = self.SplineInterpolationNatural4(diff_x, srcImg[p31[0], p31[1], channel], srcImg[p32[0], p32[1], channel], srcImg[p33[0], p33[1], channel], srcImg[p34[0], p34[1], channel]) 142 r4 = self.SplineInterpolationNatural4(diff_x, srcImg[p41[0], p41[1], channel], srcImg[p42[0], p42[1], channel], srcImg[p43[0], p43[1], channel], srcImg[p44[0], p44[1], channel]) 143 144 c = self.SplineInterpolationNatural4(diff_y, r1, r2, r3, r4) 145 146 dstImg[dstY, dstX, channel] = np.clip(c, 0, 255) 147 return dstImg
Lanzos插值
同样的是卷积的思路,只是卷积核变成了下式
L(x)=\begin{cases}
sinc(x)sinc(x/a), &-a<x<a \\
0, &otherwise
\end{cases}
将三次卷积插值的代码稍作修改即可
1 class BiLanczosInterpolation: 2 @staticmethod 3 def LanczosConvKernel(s, a): 4 if s == 0: 5 return 1 6 elif abs(s) <= a: 7 return a * np.sin(np.pi * s) * np.sin(np.pi * s / a) / (pow(np.pi, 2) * pow(s, 2)) 8 else: 9 return 0 10 11 def biLanczos4(self, srcImg, dstH, dstW): 12 # p11 p12 p13 p14 13 # 14 # p21 p22 p23 p24 15 # p 16 # p31 p32 p33 p34 17 # 18 # p41 p42 p43 p44 19 dstH, dstW = int(dstH), int(dstW) 20 scrH, scrW, _ = srcImg.shape 21 srcImg = np.pad(srcImg, ((1, 1), (1, 1), (0, 0)), 'edge') 22 dstImg = np.zeros((dstH, dstW, 1), dtype=np.uint8) 23 convKernelIndex = [-1, 0, 1, 2] 24 for dstY in range(dstH): 25 for dstX in range(dstW): 26 for channel in [0]: 27 y = dstY * scrH / dstH 28 x = dstX * scrW / dstW 29 y1 = math.floor(y) 30 x1 = math.floor(x) 31 32 array = [] 33 for i in convKernelIndex: 34 temp = 0 35 for j in convKernelIndex: 36 temp += srcImg[y1 + i, x1 + j, channel] * self.LanczosConvKernel(x - (x1 + j), 2) 37 array.append(temp) 38 39 temp = 0 40 for i in convKernelIndex: 41 temp += array[i + 1] * self.LanczosConvKernel(y - (y1 + i), 2) 42 dstImg[dstY, dstX, channel] = np.clip(temp, 0, 255) 43 44 return dstImg 45 46 def biLanczos6(self, srcImg, dstH, dstW): 47 # p11 p12 p13 p14 p15 p16 48 # 49 # p21 p22 p23 p24 p25 p26 50 # 51 # p31 p32 p33 p34 p35 p36 52 # p 53 # p41 p42 p43 p44 p45 p46 54 # 55 # p51 p52 p53 p54 p55 p56 56 # 57 # p61 p62 p63 p64 p65 p66 58 dstH, dstW = int(dstH), int(dstW) 59 scrH, scrW, _ = srcImg.shape 60 srcImg = np.pad(srcImg, ((2, 2), (2, 2), (0, 0)), 'edge') 61 dstImg = np.zeros((dstH, dstW, 1), dtype=np.uint8) 62 convKernelIndex = [-2, -1, 0, 1, 2, 3] 63 for dstY in range(dstH): 64 for dstX in range(dstW): 65 for channel in [0]: 66 y = dstY * scrH / dstH 67 x = dstX * scrW / dstW 68 y1 = math.floor(y) 69 x1 = math.floor(x) 70 71 array = [] 72 for i in convKernelIndex: 73 temp = 0 74 for j in convKernelIndex: 75 temp += srcImg[y1 + i, x1 + j, channel] * self.LanczosConvKernel(x - (x1 + j), 3) 76 array.append(temp) 77 78 temp = 0 79 for i in convKernelIndex: 80 temp += array[i + 1] * self.LanczosConvKernel(y - (y1 + i), 3) 81 dstImg[dstY, dstX, channel] = np.clip(temp, 0, 255) 82 83 return dstImg 84 85 def biLanczos8(self, srcImg, dstH, dstW): 86 # p11 p12 p13 p14 p15 p16 p17 p18 87 # 88 # p21 p22 p23 p24 p25 p26 p27 p28 89 # 90 # p31 p32 p33 p34 p35 p36 p37 p38 91 # 92 # p41 p42 p43 p44 p45 p46 p47 p48 93 # p 94 # p51 p52 p53 p54 p55 p56 p57 p58 95 # 96 # p61 p62 p63 p64 p65 p66 p67 p68 97 # 98 # p71 p72 p73 p74 p75 p76 p77 p78 99 # 100 # p81 p82 p83 p84 p85 p86 p87 p88 101 dstH, dstW = int(dstH), int(dstW) 102 scrH, scrW, _ = srcImg.shape 103 srcImg = np.pad(srcImg, ((3, 3), (3, 3), (0, 0)), 'edge') 104 dstImg = np.zeros((dstH, dstW, 1), dtype=np.uint8) 105 convKernelIndex = [-3, -2, -1, 0, 1, 2, 3, 4] 106 for dstY in range(dstH): 107 for dstX in range(dstW): 108 for channel in [0]: 109 y = dstY * scrH / dstH 110 x = dstX * scrW / dstW 111 y1 = math.floor(y) 112 x1 = math.floor(x) 113 114 array = [] 115 for i in convKernelIndex: 116 temp = 0 117 for j in convKernelIndex: 118 temp += srcImg[y1 + i, x1 + j, channel] * self.LanczosConvKernel(x - (x1 + j), 4) 119 array.append(temp) 120 121 temp = 0 122 for i in convKernelIndex: 123 temp += array[i + 1] * self.LanczosConvKernel(y - (y1 + i), 4) 124 dstImg[dstY, dstX, channel] = np.clip(temp, 0, 255) 125 126 return dstImg
感谢
如何直观地理解拉格朗日插值法? https://www.zhihu.com/question/58333118 图像处理中的三次卷积插值(Cubic Convolution) https://blog.csdn.net/shiyimin1/article/details/80141333 Bicubic interpolation https://en.wikipedia.org/wiki/Bicubic_interpolation Cubic convolution interpolation for digital image processing (1981) https://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.320.776 三次样条插值(Cubic Spline Interpolation)及代码实现(C语言) https://www.cnblogs.com/xpvincent/archive/2013/01/26/2878092.html Spline interpolation https://en.wikipedia.org/wiki/Spline_interpolation Lanczos resampling https://en.wikipedia.org/wiki/Lanczos_resampling