图像分割技术之上采样技术

上采样技术定义理解:可以理解为下采样的一种逆运算。下采样一般是特征图进行conv2d卷积操作或者pooling池化操作不断的提取原特征图的信息导致特征图会越来越小。而上采样技术恰好相反。我们希望将特征图变得越来越大,也就是在原来的信息基础上又生成一些信息出来

上采样技术有以下几种:

  1. 反池化unpooling
  2. 双线性插值interpolation
  3. 转置卷积

unpooling技术又分为1、zero unpooling 2、max unpooling

  1. zero unpooling实际上就是在原来的特征图之间插0。
  2. max unpooling是max pooling的一种逆方法。例如下图用3*3的maxpooling,将原来5*5的特征图提取成了一个3*3的特征图。这个时候需要记录了每个3*3滑动窗口的最大值的位置。当我们要将3*3反池化回去的时候,在把最大值还原到原来5*5特征图的各个位置

插值技术,分为就近插值、双线性插值、双三次插值(这个还没研究先不写了)。

  1. 就近插值。原理是:图片放大后,找到每个像素对应放大前原图的位置像素。如果能够找到映射的直接取映射位置上的值。实际代码实现的时候,其实是通过公式对像素位置取整认为就是映射到取整之后的位置,直接把对应位置的值拿过来填充就行了。下面是源码实现(一个是循环实现一个是矩阵直接实现,矩阵计算会更快一些。)
    def nearest_interpolation(src, dst_size):
        src_h, src_w, channel = src.shape
        dst_w, dst_h = dst_size
        x_scale = dst_w / src_w
        y_scale = dst_h / src_h
        dst = np.zeros((dst_h, dst_w, channel), dtype=src.dtype)
        for c in range(channel):
            for dst_x in range(dst_w):
                for dst_y in range(dst_h):
            # 计算目标图的当前坐标在源图像中的位置
            # 数学关系:src_x + 0.5 = (dst_x + 0.5) / x_scale
                    src_x = int(round((dst_x + 0.5) / x_scale - 0.5))
                    src_y = int(round((dst_y + 0.5) / y_scale - 0.5))
                    src_x = src_x if src_x > 0 else 0
                    src_x = src_x if src_x < src_w else src_w - 1
                    src_y = src_y if src_y > 0 else 0
                    src_y = src_y if src_y < src_h else src_h - 1
                    dst[dst_y, dst_x, c] = src[src_y, src_x, c]
        return dst
    def nearest_interpolation_v2(src, dst_size):
        src_h, src_w, channel = src.shape
        dst_w, dst_h = dst_size
        x_scale = dst_w / src_w
        y_scale = dst_h / src_h
        dst_y, dst_x = np.mgrid[:dst_h, :dst_w]
        #实验了一下round和around貌似没有啥区别
        src_x = np.around((dst_x + 0.5) / x_scale - 0.5).astype(np.int64)
        src_y = np.around((dst_y + 0.5) / y_scale - 0.5).astype(np.int64)
        # src_x = np.around((dst_x + 0) / x_scale - 0).astype(np.int64)
        # src_y = np.around((dst_y + 0) / y_scale - 0).astype(np.int64)
        #将数据设定最大值和最小值
        src_x = np.clip(src_x, 0, src_w-1)
        src_y = np.clip(src_y, 0, src_h-1)
        dst = src[src_y, src_x].copy()
        return dst

     

  2. 双线性插值的原理是已知两个点(x1,y1),(x2,y2)以及他们对应的值f1,f2。如果(x,y)这个点在这两个点的线段上,那么他的取值也认为是在这两个点取值的线段上。(如下图中间这一段公式,当然我们也可以手推出来)

def bilinear_interpolation(src, dst_size):
    src_h, src_w, channel = src.shape
    dst_w, dst_h = dst_size
    x_scale = dst_w / src_w
    y_scale = dst_h / src_h
    dst = np.zeros((dst_h, dst_w, channel), dtype=src.dtype)
    for c in range(channel):
        for dst_x in range(dst_w):
            for dst_y in range(dst_h):
                src_x = (dst_x + 0.5) / x_scale - 0.5
                src_y = (dst_y + 0.5) / y_scale - 0.5

                src_x1 = int(src_x)
                src_y1 = int(src_y)
                src_x2 = src_x1 + 1
                src_y2 = src_y1 + 1



                src_x1 = clip(src_x1, 0, src_w - 1)
                src_x2 = clip(src_x2, 0, src_w - 1)
                src_y1 = clip(src_y1, 0, src_h - 1)
                src_y2 = clip(src_y2, 0, src_h - 1)

                y1_value = (src_x - src_x1) * src[src_y1, src_x2, c] + (src_x2 - src_x) * src[src_y1, src_x1, c]
                y2_value = (src_x - src_x1) * src[src_y2, src_x2, c] + (src_x2 - src_x) * src[src_y2, src_x1, c]
                dst[dst_y, dst_x, c] = (src_y - src_y1) * y2_value + (src_y2 - src_y) * y1_value

    return dst
def bilinear_interpolation_v2(src, dst_size):
    dtype=src.dtype
    src_h, src_w, channel = src.shape
    dst_w, dst_h = dst_size
    x_scale = dst_w / src_w
    y_scale = dst_h / src_h
    dst_y, dst_x = np.mgrid[:dst_h, :dst_w]
    # dst_y=np.expand_dims(dst_y,-1)
    # dst_x=np.expand_dims(dst_x,-1)
    src_x=(dst_x + 0.5) / x_scale - 0.5
    src_y = (dst_y + 0.5) / y_scale - 0.5
    src_x1 = ((dst_x + 0.5) / x_scale - 0.5).astype(np.int64)
    src_y1 = ((dst_y + 0.5) / y_scale - 0.5).astype(np.int64)
    src_x2=((dst_x + 0.5) / x_scale - 0.5).astype(np.int64)+1
    src_y2 = ((dst_y + 0.5) / y_scale - 0.5).astype(np.int64)+1
    src_x1=np.clip(src_x1,0,src_w-1)
    src_x2=np.clip(src_x2,0,src_w-1)
    src_y1 = np.clip(src_y1, 0, src_h - 1)
    src_y2 = np.clip(src_y2, 0, src_h - 1)

    y1_value=np.expand_dims(src_x-src_x1,-1)*src[src_y1,src_x2]+np.expand_dims(src_x2-src_x,-1)*src[src_y1,src_x1]
    y2_value=np.expand_dims(src_x-src_x1,-1)*src[src_y2,src_x2]+np.expand_dims(src_x2-src_x,-1)*src[src_y2,src_x1]
    dst=np.around((np.expand_dims(src_y-src_y1,-1)*y2_value+np.expand_dims(src_y2-src_y,-1)*y1_value).copy()).astype("uint8")

    return dst

转置卷积相对上面两种上采样方式来说,个人认为是更有意义的上采样方法。实现了真正意义的信息“增加”。装置卷积的参数是需要训练学习的。要理解转置卷积我们可以先理解以下卷积

  1. 普通卷积我们实际上可以写成矩乘的形式。卷积矩阵*原拉升后的特征向量。如下图
  2. 如图所示,原特征图拉升后是16*1的特征向量,经过矩乘后4*16矩阵点乘16*1得到4*1的向量reshape成2*2。我们要将信息还原回来可以把2*2拉成4*1让一个16*4的矩阵点乘4*1的向量得到一个16*1的向量就还原会4*4的特征图了。(此处下意识的想到了pca降维的算法,感觉升降维的操作有着异曲同工之妙)
  3. 反卷积和卷积的卷积形式对比。可参考该地址https://github.com/vdumoulin/conv_arithmetic
  4.  

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值