超分辨率常见插值方法:最近邻插值、双线性插值、双三次插值

文章目录

    • 最近邻插值(Nearest neighbor interpolation)
    • 双线性插值(Bilinear interpolation)
    • 双三次插值(Bicubic interpolation)

最近邻插值(Nearest neighbor interpolation)

举例说明:
3X3的深度为8的256级灰度图,即高为3个象素,宽也为3个象素,每个象素的取值可以是0-255,代表该像素的亮度,255代表最亮,也就是白色,0代表最暗,即黑色。假如图像的象素矩阵如下图所示:

234 38 22
67 42 12
89 65 63

这个矩阵中,元素坐标(x,y)是这样确定的,x从左到右,从0开始,y从上到下,也是从零开始,这是图象处理中最常用的坐标系,就是这样一个坐标:
---------------------->X
|
|
|
|
|
∨Y
把这副图放大为 4X4大小的图像,那么该坐标对应源图中的坐标可以由如下公式得出:

srcX = dstX × ( srcWidth / dstWidth )
srcY = dstY × ( srcHeight / dstHeight )

好了,套用公式,就可以找到对应的原图的坐标了(0×(3/4),0×(3/4))=>(0×0.75,0×0.75)=>(0,0),找到了源图的对应坐标,就可以把源图中坐标为(0,0)处的234象素值填进去目标图的(0,0)这个位置了。

接下来,寻找目标图中坐标为(1,0)的象素对应源图中的坐标,套用公式:
(1×0.75,0×0.75)=>(0.75,0)。结果发现,得到的坐标里面竟然有小数,这时候采用的一种策略就是采用四舍五入的方法,把非整数坐标转换成整数,那么按照四舍五入的方法就得到坐标(1,0),完整的运算过程就是这样的:
(1×0.75,0×0.75)=>(0.75,0)=>(1,0)。那么就可以再填一个象素到目标矩阵中了,同样是把源图中坐标为(1,0)处的像素值38填入目标图中的坐标。

依次填完每个象素,一幅放大后的图像就诞生了,像素矩阵如下所示:

234 38 22 22
67 42 12 12
89 65 63 63
89 65 63 63

这种放大图像的方法叫做最临近插值算法,这是一种最基本、最简单的图像缩放算法,效果也是最不好的,放大后的图像有很严重的马赛克,缩小后的图像有很严重的失真;效果不好的根源就是其简单的最临近插值方法引入了严重的图像失真,比如,当由目标图的坐标反推得到的源图的的坐标是一个浮点数的时候,采用了四舍五入的方法,直接采用了和这个浮点数最接近的象素的值,这种方法是很不科学的,当推得坐标值为 0.75的时候,不应该就简单的取为1,既然是0.75,比1要小0.25 ,比0要大0.75 ,那么目标象素值其实应该根据这个源图中虚拟的点四周的四个真实的点来按照一定的规律计算出来的,这样才能达到更好的缩放效果。双线型内插值算法就是一种比较好的图像缩放算法,它充分的利用了源图中虚拟点四周的四个真实存在的像素值来共同决定目标图中的一个像素值,因此缩放效果比简单的最邻近插值要好很多。

双线性插值(Bilinear interpolation)

超分辨率常见插值方法:最近邻插值、双线性插值、双三次插值_第1张图片
双线性插值是在线性插值的基础上完成,线性插值由两点确定这条直线两个点中间的某个点的像素值。
如图,已知Q12,Q22,Q11,Q21,但是要插值的点为P点,这就要用双线性插值了,首先在x轴方向上,对R1和R2两个点进行插值,这个很简单,然后根据R1和R2对P点进行插值,这就是所谓的双线性插值。

假如我们想得到未知函数 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) f f f Q 12 = ( x 1 , y 2 ) Q_{12}=(x_1,y_2) Q12=(x1,y2) f f f Q 21 = ( x 2 , y 1 ) Q_{21}=(x_2,y_1) Q21=(x2,y1) f f f Q 22 = ( x 1 , y 1 ) Q_{22}=(x_1,y_1) Q22=(x1,y1)四个点的值。
首先在 x 方向进行线性插值,得到:
超分辨率常见插值方法:最近邻插值、双线性插值、双三次插值_第2张图片
然后在 y 方向进行线性插值,得到:
超分辨率常见插值方法:最近邻插值、双线性插值、双三次插值_第3张图片
这样就得到所要的结果 f = ( x , y ) f=(x,y) f=(x,y):
在这里插入图片描述
如果选择一个坐标系统使得 f f f的四个已知点坐标分别为 (0, 0)、(0, 1)、(1, 0) 和 (1, 1),那么插值公式就可以化简为:
在这里插入图片描述
线性插值的结果与插值的顺序无关。首先进行 y 方向的插值,然后进行 x 方向的插值,所得到的结果是一样的。
注意: 源图像和目标图像的原点(0,0)均选择左上角,然后根据插值公式计算目标图像每点像素,假设你需要将一幅5x5的图像缩小成3x3,那么源图像和目标图像各个像素之间的对应关系如下:
超分辨率常见插值方法:最近邻插值、双线性插值、双三次插值_第4张图片
只画了一行,用做示意,从图中可以很明显的看到,如果选择右上角为原点(0,0),那么最右边和最下边的像素实际上并没有参与计算,而且目标图像的每个像素点计算出的灰度值也相对于源图像偏左偏上。

那么,让坐标加1或者选择右下角为原点怎么样呢?很不幸,还是一样的效果,不过这次得到的图像将偏右偏下。

最好的方法就是,两个图像的几何中心重合,并且目标图像的每个像素之间都是等间隔的,并且都和两边有一定的边距,这也是matlab和openCV的做法。如下图:
超分辨率常见插值方法:最近邻插值、双线性插值、双三次插值_第5张图片
计算对应坐标的时候将最近邻插值中的公式改为以下即可:

srcX = (dstX + 0.5) × (srcWidth / dstWidth) - 0.5
srcY = (dstY + 0.5)× (srcHeight / dstHeight) - 0.5

所以双线性插值的过程为:
1.利用上述公式定位像素点;
2.x,y方向都分别插值,最后再插值得到中间像素值

import cv2
import numpy as np
import time

def resize(src, new_size):
    dst_w, dst_h = new_size # 目标图像宽高
    src_h, src_w = src.shape[:2] # 源图像宽高
    if src_h == dst_h and src_w == dst_w:
        return src.copy()
    scale_x = float(src_w) / dst_w # x缩放比例
    scale_y = float(src_h) / dst_h # y缩放比例

    # 遍历目标图像,插值
    dst = np.zeros((dst_h, dst_w, 3), dtype=np.uint8)
    for n in range(3): # 对channel循环
        for dst_y in range(dst_h): # 对height循环
            for dst_x in range(dst_w): # 对width循环
                # 目标在源上的坐标
                src_x = (dst_x + 0.5) * scale_x - 0.5
                src_y = (dst_y + 0.5) * scale_y - 0.5
                # 计算在源图上四个近邻点的位置
                src_x_0 = int(np.floor(src_x))
                src_y_0 = int(np.floor(src_y))
                src_x_1 = min(src_x_0 + 1, src_w - 1)
                src_y_1 = min(src_y_0 + 1, src_h - 1)

                # 双线性插值
                value0 = (src_x_1 - src_x) * src[src_y_0, src_x_0, n] + (src_x - src_x_0) * src[src_y_0, src_x_1, n]
                value1 = (src_x_1 - src_x) * src[src_y_1, src_x_0, n] + (src_x - src_x_0) * src[src_y_1, src_x_1, n]
                dst[dst_y, dst_x, n] = int((src_y_1 - src_y) * value0 + (src_y - src_y_0) * value1)
    return dst

if __name__ == '__main__':
    img_in = cv2.imread('../data/picture/timg.jpg')
    start = time.time()
    img_out = cv2.resize(img_in, (600,600))
    #print'cost %f seconds' % (time.time() - start)

    cv2.imshow('src_image', img_in)
    cv2.imshow('dst_image', img_out)
    cv2.waitKey()

双三次插值(Bicubic interpolation)

假设源图像A大小为 m ∗ n m*n mn,缩放K倍后的目标图像B的大小为 M ∗ N M*N MN,即 K = M / m K=M/m K=M/m。A的每一个像素点是已知的,B是未知的,我们想要求出目标图像B中每一像素点 ( X , Y ) (X,Y) (X,Y)的值,必须先找出像素 ( X , Y ) (X,Y) (X,Y)在源图像A中对应的像素 ( x , y ) (x,y) (x,y),再根据源图像A距离像素 ( x , y ) (x,y) (x,y)最近的16个像素点作为计算目标图像 B ( X , Y ) B(X,Y) B(X,Y)处像素值的参数,利用BiCubic基函数求出16个像素点的权重,图B像素 ( x , y ) (x,y) (x,y)的值就等于16个像素点的加权叠加。
超分辨率常见插值方法:最近邻插值、双线性插值、双三次插值_第6张图片

根据比例关系 x / X = m / M = 1 / K x/X=m/M=1/K x/X=m/M=1/K,我们可以得到 B ( X , Y ) B(X,Y) B(X,Y)在A上的对应坐标为 A ( x , y ) = A ( X ∗ ( m / M ) A(x,y)=A(X*(m/M) A(x,y)=A(X(m/M) Y ∗ ( n / N ) ) = A ( X / K , Y / K ) Y*(n/N))=A(X/K,Y/K) Y(n/N))=A(X/K,Y/K)。如图所示P点就是目标图像B在 ( X , Y ) (X,Y) (X,Y)处对应于源图像A中的位置,P的坐标位置会出现小数部分,所以我们假设 P的坐标为 P ( x + u , y + v ) P(x+u,y+v) P(x+u,y+v),其中x,y分别表示整数部分,u,v分别表示小数部分(蓝点到 a 11 a_{11} a11红点的距离)。那么我们就可以得到如图所示的最近16个像素的位置,在这里用 a ( i , j ) ( i , j = 0 , 1 , 2 , 3 ) a(i,j)(i,j=0,1,2,3) a(i,j)(i,j=0,1,2,3)来表示,如上图。
BiCubic函数:
W ( x ) = { ( a + 2 ) ∣ x ∣ 3 − ( a + 3 ) ∣ x ∣ 2 + 1 ∣ x ∣ ≤ 1 a ∣ x ∣ 3 − 5 a ∣ x ∣ 2 + 8 a ∣ x ∣ − 4 a 1 < ∣ x ∣ < 2 0 o t h e r w i s e \begin{aligned} W(x)= \left\{ \begin{array}{lr} (a+2)|x|^3-(a+3)|x|^2+1 &|x|\leq1 \\ a|x|^3-5a|x|^2+8a|x|-4a &1<|x|<2 \\ 0 &otherwise \end{array} \right. \end{aligned} W(x)= (a+2)x3(a+3)x2+1ax35ax2+8ax4a0x11<x<2otherwise
其中 a a a可取 − 0.5 -0.5 0.5 − 1 -1 1

形状为:
超分辨率常见插值方法:最近邻插值、双线性插值、双三次插值_第7张图片
我们要做的就是求出BiCubic函数中的参数x,从而获得上面所说的16个像素所对应的权重W(x)。BiCubic基函数是一维的,而像素是二维的,所以我们将像素点的行与列分开计算。BiCubic函数中的参数x表示该像素点到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的横坐标权重 i 0 = W ( 1 + u ) i_0=W(1+u) i0=W(1+u),纵坐标权重 j 0 = W ( 1 + v ) j_0=W(1+v) j0=W(1+v) a 00 a_{00} a00 B ( X , Y ) B(X,Y) B(X,Y)的贡献值为: I ( a 00 ) ∗ i 0 ∗ j 0 I(a_{00})* i_0* j_0 I(a00)i0j0。因此, a 0 X a_{0X} a0X的横坐标权重分别为 W ( 1 + u ) W(1+u) W(1+u) W ( u ) W(u) W(u) W ( 1 − u ) W(1-u) W(1u) W ( 2 − u ) W(2-u) W(2u) a y 0 a_{y0} ay0的纵坐标权重分别为 W ( 1 + v ) W(1+v) W(1+v) W ( v ) W(v) W(v) W ( 1 − v ) W(1-v) W(1v) W ( 2 − v ) W(2-v) W(2v) B ( X , Y ) B(X,Y) B(X,Y)像素值为:
B ( X , Y ) = ∑ i = 0 3 ∑ j = 0 3 a i j ∗ W ( i ) ∗ W ( j ) B(X,Y)=\sum_{i=0}^3{\sum_{j=0}^3{a_{ij}*W(i)*W(j)}} B(X,Y)=i=03j=03aijW(i)W(j)
对待插值的像素点 ( x , y ) (x,y) (x,y)(x和y可以为浮点数),取其附近的4x4邻域点 ( x i , y j ) ( i , j = 0 , 1 , 2 , 3 ) (x_i,y_j)(i,j = 0,1,2,3) (xi,yj)(i,j=0,1,2,3)。按如下公式进行插值计算:
f ( X , Y ) = ∑ i = 0 3 ∑ j = 0 3 f ( x i , y j ) ∗ W ( x − x i ) ∗ W ( y − y j ) f(X,Y)=\sum_{i=0}^3{\sum_{j=0}^3{f(x_i,y_j)*W(x-x_i)*W(y-y_j)}} f(X,Y)=i=03j=03f(xi,yj)W(xxi)W(yyj)

你可能感兴趣的:(算法,python,计算机视觉)