FPN网络的上采样过程用到了最近邻插值(转置卷积也是一种上采样方法,具体在其他问题中介绍),Mask R-CNN的 ROI Align 中用到了双线性插值,本科选修课上课时候听了一下,现在却忘了,又得花时间捡起来……
其实图像插值分为图像内插值和图像间插值两种。
图像内插值的主要应用是对图像进行放大以及旋转等操作,是从低分辨率的图像生成高分辨率图像的过程,用以恢复图像中丢失的信息,使得图像更清晰,细节信息更多,相邻像素之间的过渡更加有连续性。也就是单帧图像的图像重建过程。
而图像间插值,也叫图像的超分辨率重建,是指在一图像序列之间再生出若干幅新的图像,可应用于医学图像序列切片和视频序列之间的的插值。
我们这里主要讨论的是图像内插值,常用的图像内插值算法有:最近邻插值、双线性插值、三次内插法。
这是最简单的一种插值方法,不需要计算。算法是:在待求像素的四邻域中,将距离待求像素最近的邻像素的灰度值赋给待求像素。下面举个例子:如图所示,我们要求坐标为 ( i + u , j + v ) (i+u, j+v) (i+u,j+v) 的像素灰度值,其中 i 和 j 为正整数,$0 < u, v < 1 $ 。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XxCJX3XI-1671027509152)(https://p-blog.csdn.net/images/p_blog_csdn_net/coy_wang/EntryImages/20091217/最邻近元.png)]
那么当 ( i + u , j + v ) (i+u, j+v) (i+u,j+v) 落在 A 区时,即 u < 0.5 u < 0.5 u<0.5 , v < 0.5 v < 0.5 v<0.5 ,则将左上角的像素的灰度值赋给待求像素,其他区域同理。不过图中没有说明当 u = 0.5 u = 0.5 u=0.5 或者 v = 0.5 v = 0.5 v=0.5 的时候应该取那个值,这我们在写程序的时候自己定义就可以,赋哪边的值都差别不大。
这种方法计算量小,但可能会造成插值生成的图像灰度上的不连续,在灰度变化的地方可能会出现明显的锯齿感。
上面是在数字图像处理中,遇到像素坐标为小数时的取值问题,最开始我们也提到了 FPN 的从上往下的旁路中的上采样也使用到了最近邻插值方法,这里的上采样是使得特征图的宽与高变成原来的 2 倍,那跟我们这里提到的小数坐标运算问题不太一样,它是直接先逐行复制,再逐列复制,具体的表现我们使用 keras 中的 UpSampling2D 函数来可视化下就很清晰了:
#!/usr/bin/env/python3
# _*_ coding = utf-8 _*_
from keras.layers import UpSampling2D
import numpy as np
import tensorflow as tf
x=np.array([1,2,3,4])
x=x.reshape(1,2,2,1)
x=tf.convert_to_tensor(x)
y=UpSampling2D(size=(2,2))(x)
with tf.Session() as sess:
print(y.eval())
运行下这段代码就可以看到我们的输入为:
[ 1 2 3 4 ] \left[ \begin{matrix} 1 & 2\\ 3 & 4\end{matrix}\right ] [1324]
输出为:
[ 1 1 2 2 1 1 2 2 3 3 4 4 3 3 4 4 ] \left[ \begin{matrix} 1 & 1 & 2 & 2\\ 1 & 1 & 2 & 2 \\ 3 & 3 & 4& 4 \\ 3 & 3 & 4& 4 \end{matrix}\right ] ⎣⎢⎢⎡1133113322442244⎦⎥⎥⎤
双线性插值法是利用待求像素四个相邻像素的灰度在两个方向上做线性插值,也就是做两次线性变换。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-h4Mf0vv7-1671027509153)(https://p-blog.csdn.net/images/p_blog_csdn_net/coy_wang/EntryImages/20091217/双线性同插.png)]
如上图所示,我们使用 f ( i , j ) f(i,j) f(i,j) 表示在坐标 ( i , j ) (i,j) (i,j) 的像素值大小,现在要插值求坐标为 ( i + u , j + v ) (i+u, j+v) (i+u,j+v) 的像素值,其中 i , j , u , v i, j, u, v i,j,u,v 的定义与上面相同。
我们需要先求 f ( i , j + v ) f(i, j+v) f(i,j+v) ,因为 f ( i , j ) f(i,j) f(i,j) 到 f ( i , j + 1 ) f(i, j+1) f(i,j+1) 的灰度变化我们假设为线性关系,则:
f ( i , j + v ) = [ f ( i , j + 1 ) − f ( i , j ) ] ∗ v + f ( i , j ) f(i,j+v)= [f(i,j+1) - f(i,j)]*v+f(i,j) f(i,j+v)=[f(i,j+1)−f(i,j)]∗v+f(i,j)
同理:
f ( i + 1 , j + v ) = [ f ( i + 1 , j + 1 ) − f ( i + 1 , j ) ] ∗ v + f ( i + 1 , j ) f(i+1,j+v)= [f(i+1,j+1) - f(i+1,j)]*v+f(i+1,j) f(i+1,j+v)=[f(i+1,j+1)−f(i+1,j)]∗v+f(i+1,j)
上面我们已经求得了在 j 方向上的插值结果,接着在上面的基础上再计算 i 方向的插值结果即可:
f ( i + u , j + v ) = [ f ( i + 1 , j + v ) − f ( i , j + v ) ] ∗ u + f ( i , j + v ) f(i+u, j+v) = [f(i+1,j+v) - f(i, j+v)]*u + f(i,j+v) f(i+u,j+v)=[f(i+1,j+v)−f(i,j+v)]∗u+f(i,j+v)
可见这个方法比最近邻方法复杂,计算量更大,但没有灰度不连续的缺点,结果基本令人满意,只是图像轮廓可能会有一点模糊。
这个方法利用三次多项式求逼近理论上的最佳插值函数,待求像素 ( x , y ) (x,y) (x,y) 的灰度值由其周围 16 个灰度值加权内插得到。计算公式有点小复杂,没那么直观。(这里就先参考博客里的记录一下,没太理解,需要了解的看看参考资料有简单介绍)
效果:最近邻插值算法 < 双线性插值 < 三次插值
速度:最近邻插值算法 > 双线性插值 > 三次插值
关于上采样方法总结(插值和深度学习)
常用的几种图像插值算法