一、论文
《Real Image Denoising with Feature Attention》
深度卷积神经网络在包含空间不变噪声(合成噪声)的图像上表现更好; 但是,它们的性能仅限于真实照片,并且需要多级网络建模。 为了提高去噪算法的实用性,本文提出了一种采用模块化架构的新型单级盲实像去噪网络(RIDNet)。 我们在残差结构上使用残差来缓解低频信息的流动,并关注功能以开发信道相关性。 此外,根据19种最新算法对三个合成和四个真实噪声数据集进行的量化指标和视觉质量评估,证明了我们RIDNet的优越性。
贡献:
- 当前基于CNN的真实图像去噪方法采用两阶段模型。 我们提出了第一个仅使用一个阶段即可提供最新结果的模型。
- 据我们所知,我们的模型是第一个将特征关注纳入去噪的模型。
- 大多数当前模型连续连接权重层; 因此增加深度将无助于提高性能[21,41]。 同样,这样的网络13155可能遭受消失的梯度[11]。 我们提供了一个模块化网络,其中增加模块数量有助于提高性能。
- 我们对三个合成图像数据集和四个真实图像噪声数据集进行了实验,以表明我们的模型在合成图像和真实图像上实现了定量和定性的最新结果。
二、网络结构
我们提出的模型包含四个EAM块。 除增强残差块中的最后一个Conv层和注意单元中的最后一个Conv层(内核大小为1×1)外,每个卷积层的内核大小均设置为3×3。 实现相同大小的输出要素图。 每个卷积层的通道数固定为64,但功能关注点的缩小比例除外。 16倍减少了这些Conv层; 因此只有四个特征图。 最终的卷积层根据输入输出三个或一个特征图。 至于运行时间,我们的方法需要大约0.2秒来处理512×512图像。
三、代码
下载:https://github.com/saeed-anwar/RIDNet
import math
import torch
import torch.nn as nn
import torch.nn.functional as F
from torch.autograd import Variable
def default_conv(in_channels, out_channels, kernel_size, bias=True):
return nn.Conv2d(
in_channels, out_channels, kernel_size,
padding=(kernel_size//2), bias=bias)
class MeanShift(nn.Conv2d):
def __init__(self, rgb_range, rgb_mean, rgb_std, sign=-1):
super(MeanShift, self).__init__(3, 3, kernel_size=1)
std = torch.Tensor(rgb_std)
self.weight.data = torch.eye(3).view(3, 3, 1, 1)
self.weight.data.div_(std.view(3, 1, 1, 1))
self.bias.data = sign * rgb_range * torch.Tensor(rgb_mean)
self.bias.data.div_(std)
self.requires_grad = False
class BasicBlock(nn.Sequential):
def __init__(
self, in_channels, out_channels, kernel_size, stride=1, bias=False,
bn=True, act=nn.ReLU(True)):
m = [nn.Conv2d(
in_channels, out_channels, kernel_size,
padding=(kernel_size//2), stride=stride, bias=bias)
]
if bn: m.append(nn.BatchNorm2d(out_channels))
if act is not None: m.append(act)
super(BasicBlock, self).__init__(*m)
class ResBlock(nn.Module):
def __init__(
self, conv, n_feat, kernel_size,
bias=True, bn=False, act=nn.ReLU(True), res_scale=1):
super(ResBlock, self).__init__()
m = []
for i in range(2):
m.append(conv(n_feat, n_feat, kernel_size, bias=bias))
if bn: m.append(nn.BatchNorm2d(n_feat))
if i == 0: m.append(act)
self.body = nn.Sequential(*m)
self.res_scale = res_scale
def forward(self, x):
res = self.body(x).mul(self.res_scale)
res += x
return res
class Upsampler(nn.Sequential):
def __init__(self, conv, scale, n_feat, bn=False, act=False, bias=True):
m = []
if (scale & (scale - 1)) == 0: # Is scale = 2^n?
for _ in range(int(math.log(scale, 2))):
m.append(conv(n_feat, 4 * n_feat, 3, bias))
m.append(nn.PixelShuffle(2))
if bn: m.append(nn.BatchNorm2d(n_feat))
if act: m.append(act())
elif scale == 3:
m.append(conv(n_feat, 9 * n_feat, 3, bias))
m.append(nn.PixelShuffle(3))
if bn: m.append(nn.BatchNorm2d(n_feat))
if act: m.append(act())
else:
raise NotImplementedError
super(Upsampler, self).__init__(*m)
import torch.nn as nn
from model import ops
from model import common
def make_model(args, parent=False):
return RIDNET(args)
class CALayer(nn.Module):
def __init__(self, channel, reduction=16):
super(CALayer, self).__init__()
self.avg_pool = nn.AdaptiveAvgPool2d(1)
self.c1 = ops.BasicBlock(channel , channel // reduction, 1, 1, 0)
self.c2 = ops.BasicBlockSig(channel // reduction, channel , 1, 1, 0)
def forward(self, x):
y = self.avg_pool(x)
y1 = self.c1(y)
y2 = self.c2(y1)
return x * y2
class Block(nn.Module):
def __init__(self, in_channels, out_channels, group=1):
super(Block, self).__init__()
self.r1 = ops.Merge_Run_dual(in_channels, out_channels)
self.r2 = ops.ResidualBlock(in_channels, out_channels)
self.r3 = ops.EResidualBlock(in_channels, out_channels)
#self.g = ops.BasicBlock(in_channels, out_channels, 1, 1, 0)
self.ca = CALayer(in_channels)
def forward(self, x):
r1 = self.r1(x)
r2 = self.r2(r1)
r3 = self.r3(r2)
#g = self.g(r3)
out = self.ca(r3)
return out
class RIDNET(nn.Module):
def __init__(self, args):
super(RIDNET, self).__init__()
n_feats = args.n_feats
kernel_size = 3
reduction = args.reduction
rgb_mean = (0.4488, 0.4371, 0.4040)
rgb_std = (1.0, 1.0, 1.0)
self.sub_mean = common.MeanShift(args.rgb_range, rgb_mean, rgb_std)
self.add_mean = common.MeanShift(args.rgb_range, rgb_mean, rgb_std, 1)
self.head = ops.BasicBlock(3, n_feats, kernel_size, 1, 1)
self.b1 = Block(n_feats, n_feats)
self.b2 = Block(n_feats, n_feats)
self.b3 = Block(n_feats, n_feats)
self.b4 = Block(n_feats, n_feats)
self.tail = nn.Conv2d(n_feats, 3, kernel_size, 1, 1, 1)
def forward(self, x):
s = self.sub_mean(x)
h = self.head(s)
b1 = self.b1(h)
b2 = self.b2(b1)
b3 = self.b3(b2)
b_out = self.b4(b3)
res = self.tail(b_out)
out = self.add_mean(res)
f_out = out + x
return f_out
四、相关资料
[降噪][ICCV 2019] Real Image Denoising
[去噪论文]--ICCV2019-Real Image Denoising with Feature Attention