《Pyramid Real Image Denoising Network》阅读笔记

一、论文

Pyramid Real Image Denoising Network

摘要—尽管深卷积神经网络(CNN)表现出了出色的建模特定噪声和降噪能力,但它们在现实世界中的噪点图像上仍然表现不佳。 主要原因是现实世界中的噪声更加复杂多样。 为了解决盲降噪问题,本文提出了一个新的金字塔实像降噪网络(PRIDNet),它分为三个阶段。 首先,噪声估计阶段使用通道注意机制来重新校准输入噪声的通道重要性。 其次,在多尺度降噪阶段,利用金字塔池提取多尺度特征。 第三,特征融合阶段采用核选择运算来自适应融合多尺度特征。在两个真实嘈杂照片的数据集上进行的实验表明,在定量测量和视觉感知质量方面,与最新的去噪器相比,我们的方法可以实现竞争性能。

• 通道注意:通道注意机制用于提取的噪声特征,可自适应地重新校准频道重要性。
• 多尺度特征提取:我们设计了金字塔降噪结构,其中每个分支都关注一个尺度的特征。 得益于此,我们可以同时提取全局信息和保留局部细节,从而为后续的全面去噪做好准备。
• 特征自适应融合:在级联多尺度特征中,每个通道代表一个尺度特征。 我们介绍一个内核选择模块。 通过线性组合融合具有不同卷积核大小的多个分支,从而允许通过大小不同的核来表达不同的特征图。

二、网络结构

 A.网络架构

如图2所示,我们的模型包括三个阶段:噪声估计阶段,多尺度降噪阶段和特征融合阶段。 输入的噪点图像按三个阶段依次处理。 由于所有操作在空间上都是不变的,因此它足够健壮,可以处理任意大小的输入图像。为了避免信息丢失,在馈入下一级之前,将第一级的输出与其输入连接起来,然后进入第二级。

B.噪声估计阶段

这个阶段着重于从输入噪声图像中提取判别特征,这被认为是对噪声水平的估计[8]。 我们采用无池和批处理规范化的普通五层全卷积子网,每次卷积后都会部署ReLU。 在每个卷积层中,要素通道的数量设置为32(最后一层除外(1或3)),并且卷积核大小为3×3。在阶段的最后一层之前,插入了通道注意力模块[11],以明确校准特征通道之间的相互依赖性。 如图3所示,通道权重的集合是我们的目标,它用于重新缩放输入特征图以生成重新校准的特征。 我们首先使用全局平均池(GAP)将U的全局信息压缩到信道描述符中。 然后,紧随其后的是两个完全连接的层(FC),中间层的通道数设置为2。

通道注意模块的最终输出(表示为)是通过

扫描二维码关注公众号,回复: 11926945 查看本文章

C.多尺度降噪阶段

金字塔池化的概念广泛应用于场景解析[12],图像压缩等领域。 据我们所知,它从未在图像去噪中使用过。 周等。  [13]表明,CNN的经验接受域要比理论域小得多,尤其是在高层上,这意味着在提取特征时全局信息没有完全整合。 相反,为了消除覆盖整个图像的噪声,将目标块与整个图像中的相似内容相匹配具有很大的帮助。

 为了减轻这个问题,我们开发了一个五层金字塔。通过五种并行方式,将输入特征图下采样为不同大小,从而帮助分支获得相对比例不同的接收场,以同时捕获原始,本地和全局信息。 合并内核分别设置为1×1、2×2、4×4、8×8和16×16。 然后,每个合并的特征之后都是U-Net [14],它是由深度编码-解码和跳过连接组成的网络,因为研究表明,连续的上采样和下采样有助于对任务进行降噪。 请注意,五个U-Net不共享权重。 在该阶段的最后,通过双线性插值将多级去噪特征上采样到相同的大小,然后将它们连接在一起。

D.特征融合阶段

为了在级联多尺度结果中为每个通道选择大小不同的内核,受[15]的启发,引入了内核选择模块。 内核选择模块的详细信息如图4所示。给定的特征图由内核大小分别为3、5和7的三个并行卷积进行,得到。 我们首先通过元素求和来整合来自所有分支的信息:

然后收缩,然后通过GAP和两个FC进行扩展,其操作与频道注意模块中的操作相同,但最后没有Sigmoid。  FC2的三个输出由Softmax进行操作,它们像通道控制机制一样跨通道地应用于分支:

其中α,β和γ分别表示的软注意力向量。 注意,αc是α的第c个元素,βc和γc同样。 最终输出特征图V是通过将各种内核及其注意力权重相结合来计算的:

其中α,β和γ需要满足,并且。 最后,我们利用1×1卷积层将尺寸压缩为1或3以进行特征融合。

三、结论

为了进行训练,我们从智能手机图像去噪数据集(SIDD)[16]的原始RGB空间和sRGB空间中利用了320对图像(噪声和地面真实)。

在本文中,提出了一种PRIDNet,用于对真实图像进行盲降噪。 拟议的网络包括三个连续阶段。 第一阶段探讨要素渠道的相对重要性。 在第二阶段,开发了金字塔池化以去除多尺度特征。 在最后阶段,介绍了自适应核选择的操作以进行特征融合。定性和定量实验均表明我们的方法具有竞争优势。

参考资料:

四、代码‘

https://github.com/491506870/PRIDNet 代码

import tensorflow as tf
import tensorflow.contrib.slim as slim
from tflearn.layers.conv import global_avg_pool


def lrelu(x):
    return tf.maximum(x * 0.2, x)


def upsample_and_concat(x1, x2, output_channels, in_channels):
    pool_size = 2
    deconv_filter = tf.Variable(tf.truncated_normal([pool_size, pool_size, output_channels, in_channels], stddev=0.02))
    deconv = tf.nn.conv2d_transpose(x1, deconv_filter, tf.shape(x2), strides=[1, pool_size, pool_size, 1])

    deconv_output = tf.concat([deconv, x2], 3)
    deconv_output.set_shape([None, None, None, output_channels * 2])

    return deconv_output


def unet(input):
    conv1 = slim.conv2d(input, 32, [3, 3], rate=1, activation_fn=lrelu)
    conv1 = slim.conv2d(conv1, 32, [3, 3], rate=1, activation_fn=lrelu)
    conv1 = slim.conv2d(conv1, 32, [3, 3], rate=1, activation_fn=lrelu)
    conv1 = slim.conv2d(conv1, 32, [3, 3], rate=1, activation_fn=lrelu)
    pool1 = slim.max_pool2d(conv1, [2, 2], padding='SAME')

    conv2 = slim.conv2d(pool1, 64, [3, 3], rate=1, activation_fn=lrelu)
    conv2 = slim.conv2d(conv2, 64, [3, 3], rate=1, activation_fn=lrelu)
    conv2 = slim.conv2d(conv2, 64, [3, 3], rate=1, activation_fn=lrelu)
    conv2 = slim.conv2d(conv2, 64, [3, 3], rate=1, activation_fn=lrelu)
    pool2 = slim.max_pool2d(conv2, [2, 2], padding='SAME')

    conv3 = slim.conv2d(pool2, 128, [3, 3], rate=1, activation_fn=lrelu)
    conv3 = slim.conv2d(conv3, 128, [3, 3], rate=1, activation_fn=lrelu)
    conv3 = slim.conv2d(conv3, 128, [3, 3], rate=1, activation_fn=lrelu)
    conv3 = slim.conv2d(conv3, 128, [3, 3], rate=1, activation_fn=lrelu)
    pool3 = slim.max_pool2d(conv3, [2, 2], padding='SAME')

    conv4 = slim.conv2d(pool3, 256, [3, 3], rate=1, activation_fn=lrelu)
    conv4 = slim.conv2d(conv4, 256, [3, 3], rate=1, activation_fn=lrelu)
    conv4 = slim.conv2d(conv4, 256, [3, 3], rate=1, activation_fn=lrelu)
    conv4 = slim.conv2d(conv4, 256, [3, 3], rate=1, activation_fn=lrelu)
    pool4 = slim.max_pool2d(conv4, [2, 2], padding='SAME')

    conv5 = slim.conv2d(pool4, 512, [3, 3], rate=1, activation_fn=lrelu)
    conv5 = slim.conv2d(conv5, 512, [3, 3], rate=1, activation_fn=lrelu)
    conv5 = slim.conv2d(conv5, 512, [3, 3], rate=1, activation_fn=lrelu)
    conv5 = slim.conv2d(conv5, 512, [3, 3], rate=1, activation_fn=lrelu)

    up6 = upsample_and_concat(conv5, conv4, 256, 512)
    conv6 = slim.conv2d(up6, 256, [3, 3], rate=1, activation_fn=lrelu)
    conv6 = slim.conv2d(conv6, 256, [3, 3], rate=1, activation_fn=lrelu)
    conv6 = slim.conv2d(conv6, 256, [3, 3], rate=1, activation_fn=lrelu)

    up7 = upsample_and_concat(conv6, conv3, 128, 256)
    conv7 = slim.conv2d(up7, 128, [3, 3], rate=1, activation_fn=lrelu)
    conv7 = slim.conv2d(conv7, 128, [3, 3], rate=1, activation_fn=lrelu)
    conv7 = slim.conv2d(conv7, 128, [3, 3], rate=1, activation_fn=lrelu)

    up8 = upsample_and_concat(conv7, conv2, 64, 128)
    conv8 = slim.conv2d(up8, 64, [3, 3], rate=1, activation_fn=lrelu)
    conv8 = slim.conv2d(conv8, 64, [3, 3], rate=1, activation_fn=lrelu)
    conv8 = slim.conv2d(conv8, 64, [3, 3], rate=1, activation_fn=lrelu)

    up9 = upsample_and_concat(conv8, conv1, 32, 64)
    conv9 = slim.conv2d(up9, 32, [3, 3], rate=1, activation_fn=lrelu)
    conv9 = slim.conv2d(conv9, 32, [3, 3], rate=1, activation_fn=lrelu)
    conv9 = slim.conv2d(conv9, 32, [3, 3], rate=1, activation_fn=lrelu)

    conv10 = slim.conv2d(conv9, 1, [1, 1], rate=1, activation_fn=None)
    #out = tf.depth_to_space(conv10, 2)
    return conv10


def feature_encoding(input):
    conv1 = slim.conv2d(input, 32, [3, 3], rate=1, activation_fn=lrelu, scope='fe_conv1')
    conv2 = slim.conv2d(conv1, 32, [3, 3], rate=1, activation_fn=lrelu, scope='fe_conv2')
    conv3 = slim.conv2d(conv2, 32, [3, 3], rate=1, activation_fn=lrelu, scope='fe_conv3')
    conv4 = slim.conv2d(conv3, 32, [3, 3], rate=1, activation_fn=lrelu, scope='fe_conv4')
    conv4 = squeeze_excitation_layer(conv4, 32, 2)
    output = slim.conv2d(conv4, 1, [3, 3], rate=1, activation_fn=lrelu, scope='fe_conv5')

    return output


def avg_pool(feature_map):
    ksize = [[1, 1, 1, 1], [1, 2, 2, 1], [1, 4, 4, 1], [1, 8, 8, 1], [1, 16, 16, 1]]
    pool1 = tf.nn.avg_pool(feature_map, ksize=ksize[0], strides=ksize[0], padding='VALID')
    pool2 = tf.nn.avg_pool(feature_map, ksize=ksize[1], strides=ksize[1], padding='VALID')
    pool3 = tf.nn.avg_pool(feature_map, ksize=ksize[2], strides=ksize[2], padding='VALID')
    pool4 = tf.nn.avg_pool(feature_map, ksize=ksize[3], strides=ksize[3], padding='VALID')
    pool5 = tf.nn.avg_pool(feature_map, ksize=ksize[4], strides=ksize[4], padding='VALID')

    return pool1, pool2, pool3, pool4, pool5


def all_unet(pool1, pool2, pool3, pool4, pool5):
    unet1 = unet(pool1)
    unet2 = unet(pool2)
    unet3 = unet(pool3)
    unet4 = unet(pool4)
    unet5 = unet(pool5)

    return unet1, unet2, unet3, unet4, unet5


def resize_all_image(unet1, unet2, unet3, unet4, unet5):
    resize1 = tf.image.resize_images(images=unet1, size=[tf.shape(unet1, out_type=tf.int32)[1],tf.shape(unet1, out_type=tf.int32)[2]], method=tf.image.ResizeMethod.BILINEAR)
    resize2 = tf.image.resize_images(images=unet2, size=[tf.shape(unet1, out_type=tf.int32)[1],tf.shape(unet1, out_type=tf.int32)[2]], method=tf.image.ResizeMethod.BILINEAR)
    resize3 = tf.image.resize_images(images=unet3, size=[tf.shape(unet1, out_type=tf.int32)[1],tf.shape(unet1, out_type=tf.int32)[2]], method=tf.image.ResizeMethod.BILINEAR)
    resize4 = tf.image.resize_images(images=unet4, size=[tf.shape(unet1, out_type=tf.int32)[1],tf.shape(unet1, out_type=tf.int32)[2]], method=tf.image.ResizeMethod.BILINEAR)
    resize5 = tf.image.resize_images(images=unet5, size=[tf.shape(unet1, out_type=tf.int32)[1],tf.shape(unet1, out_type=tf.int32)[2]], method=tf.image.ResizeMethod.BILINEAR)

    return resize1, resize2, resize3, resize4, resize5


def to_clean_image(feature_map, resize1, resize2, resize3, resize4, resize5):
    concat = tf.concat([feature_map, resize1, resize2, resize3, resize4, resize5], 3)
    sk_conv1 = slim.conv2d(concat, 7, [3, 3], rate=1, activation_fn=lrelu)
    sk_conv2 = slim.conv2d(concat, 7, [5, 5], rate=1, activation_fn=lrelu)
    sk_conv3 = slim.conv2d(concat, 7, [7, 7], rate=1, activation_fn=lrelu)
    sk_out = selective_kernel_layer(sk_conv1, sk_conv2, sk_conv3, 4, 7)
    output = slim.conv2d(sk_out, 1, [3, 3], rate=1, activation_fn=None)

    return output


def squeeze_excitation_layer(input_x, out_dim, middle):
    squeeze = global_avg_pool(input_x)
    excitation = tf.layers.dense(squeeze, use_bias=True, units=middle)
    excitation = tf.nn.relu(excitation)
    excitation = tf.layers.dense(excitation, use_bias=True, units=out_dim)
    excitation = tf.nn.sigmoid(excitation)
    excitation = tf.reshape(excitation, [-1, 1, 1, out_dim])
    scale = input_x * excitation
    return scale


def selective_kernel_layer(sk_conv1, sk_conv2, sk_conv3, middle, out_dim):
    sum_u = sk_conv1 + sk_conv2 + sk_conv3
    squeeze = global_avg_pool(sum_u)
    squeeze = tf.reshape(squeeze, [-1, 1, 1, out_dim])
    z = tf.layers.dense(squeeze, use_bias=True, units=middle)
    z = tf.nn.relu(z)
    a1 = tf.layers.dense(z, use_bias=True, units=out_dim)
    a2 = tf.layers.dense(z, use_bias=True, units=out_dim)
    a3 = tf.layers.dense(z, use_bias=True, units=out_dim)

    before_softmax = tf.concat([a1, a2, a3], 1)
    after_softmax = tf.nn.softmax(before_softmax, dim=1)
    a1 = after_softmax[:, 0, :, :]
    a1 = tf.reshape(a1, [-1, 1, 1, out_dim])
    a2 = after_softmax[:, 1, :, :]
    a2 = tf.reshape(a2, [-1, 1, 1, out_dim])
    a3 = after_softmax[:, 2, :, :]
    a3 = tf.reshape(a3, [-1, 1, 1, out_dim])

    select_1 = sk_conv1 * a1
    select_2 = sk_conv2 * a2
    select_3 = sk_conv3 * a3

    out = select_1 + select_2 + select_3

    return out


def network(in_image):
    feature_map = feature_encoding(in_image)
    feature_map_2 = tf.concat([in_image, feature_map], 3)
    pool1, pool2, pool3, pool4, pool5 = avg_pool(feature_map_2)
    unet1, unet2, unet3, unet4, unet5 = all_unet(pool1, pool2, pool3, pool4, pool5)
    resize1, resize2, resize3, resize4, resize5 = resize_all_image(unet1, unet2, unet3, unet4, unet5)
    out_image = to_clean_image(feature_map_2, resize1, resize2, resize3, resize4, resize5)

    return out_image

代码为tensorflow版本,但写的也比较清楚,论文中的UNet没有画出详细结构,看一下代码中就可以了。

猜你喜欢

转载自blog.csdn.net/LiuJiuXiaoShiTou/article/details/107766038