代码以及视频讲解
本文所涉及所有资源均在传知代码平台可获取
概述
本文复现论文 Zero-Shot Noise2Noise: Efficient Image Denoising without any Data[1] 提出的图像去噪方法。
随着深度学习的发展,各种图像去噪方法的性能不断提升。然而,目前的工作大多需要高昂的计算成本或对噪声模型的假设。为解决这个问题,该论文提出了一种自监督学习方法。该方法使用一个简单的两层卷积神经网络和噪声到噪声损失(Noise to Noise Loss),在只使用一张测试图像作为训练样本的情况下,实现了低成本高质量的图像去噪。
算法原理
论文提出的方法主要包含三个部分:成对下采样、残差损失、一致性损失。
成对下采样
该成对下采样器将原始图像下采样为长宽只有原先一半的子图。具体地,其通过将图像分割为大小为 2 × 2 的非重叠补丁,并将每个补丁的对角线像素取平均值并分配给第一个子图,然后将反对角线像素取平均值并分配给第二个子图像。该成对下采样器的示意图如下所示:
残差损失
在非自监督的情况下,损失函数一般采用噪声图像与干净图像之间平方差的形式:
L
s
u
p
(
I
n
,
I
c
)
=
∣
∣
I
n
−
I
c
∣
∣
2
2
L_{sup}(I_n,I_c)=||I_n-I_c||_2^2
Lsup(In,Ic)=∣∣In−Ic∣∣22
在自监督的情况下,没有干净图像作为训练目标,则可以将两张噪声图像子图互为训练目标,即噪声到噪声损失:
L
r
e
s
(
I
n
;
θ
)
=
1
2
(
∣
∣
(
D
1
(
I
n
)
−
f
θ
(
D
1
(
I
n
)
)
−
D
2
(
I
n
)
∣
∣
2
2
+
∣
∣
(
D
1
(
I
n
)
−
f
θ
(
D
1
(
I
n
)
)
−
D
2
(
I
n
)
∣
∣
2
2
)
L_{res}(I_n;\theta)=\frac{1}{2}(||(D_1(I_n)-f_{\theta}(D_1(I_n))-D_2(I_n)||_2^2+\\||(D_1(I_n)-f_{\theta}(D_1(I_n))-D_2(I_n)||_2^2)
Lres(In;θ)=21(∣∣(D1(In)−fθ(D1(In))−D2(In)∣∣22+∣∣(D1(In)−fθ(D1(In))−D2(In)∣∣22)
基于噪声独立性假设,可以证明这两种损失的期望值相同。
一致性损失
考虑到残差损失只使用了噪声图像子图训练模型,而测试时需要整张噪声图像作为输入,为了使网络对子图的噪声估计与对原图的噪声估计保持一致,作者还引入了一个一致性损失函数:
L
c
o
n
(
I
n
;
θ
)
=
1
2
(
∣
∣
D
1
(
I
n
)
−
f
θ
(
D
1
(
I
n
)
)
−
D
1
(
I
n
−
f
θ
(
I
n
)
)
∣
∣
2
2
+
∣
∣
D
2
(
I
n
)
−
f
θ
(
D
2
(
I
n
)
)
−
D
2
(
I
n
−
f
θ
(
I
n
)
)
∣
∣
2
2
)
L_{con}(I_n;\theta)=\frac{1}{2}(||D_1(I_n)-f_{\theta}(D_1(I_n))-D_1(I_n-f_\theta(I_n))||^2_2+\\||D_2(I_n)-f_{\theta}(D_2(I_n))-D_2(I_n-f_\theta(I_n))||^2_2)
Lcon(In;θ)=21(∣∣D1(In)−fθ(D1(In))−D1(In−fθ(In))∣∣22+∣∣D2(In)−fθ(D2(In))−D2(In−fθ(In))∣∣22)
总的损失如下所示:
L
a
l
l
(
I
n
;
θ
)
=
L
r
e
s
(
I
n
;
θ
)
+
L
c
o
n
(
I
n
;
θ
)
L_{all}(I_n;\theta)=L_{res}(I_n;\theta)+L_{con}(I_n;\theta)
Lall(In;θ)=Lres(In;θ)+Lcon(In;θ)
核心逻辑
上述方法的核心逻辑如下所示:
import torch
import torch.nn as nn
import torch.nn.functional as F
import numpy as np
def diag_sample(image):
'''下采样函数,输入图像,输出两张长宽只有原先一半的子图'''
# 分割成2x2的补丁
height = int(image.shape[2] / 2)
width = int(image.shape[3] / 2)
image_patch = image[:, :, 0: height * 2, 0: width * 2].view(image.shape[0], image.shape[1], height, 2, width, 2).permute(0, 1, 2, 4, 3, 5)
# 对角线元素取平均作为第一个子图
image_sub1 = (image_patch[:, :, :, :, 0, 0] +image_patch[:, :, :, :, 1, 1]) / 2
# 反对角线元素取平均作为第二个子图
image_sub2 = (image_patch[:, :, :, :, 0, 1] +image_patch[:, :, :, :, 1, 0]) / 2
return image_sub1, image_sub2
class NoisePredictor(nn.Module):
'''噪声估计网络,输入图像,输出估计的图像噪声'''
def __init__(self, channels=3):
super(NoisePredictor, self).__init__()
self.net = nn.Sequential(
nn.Conv2d(channels, 52, 3, padding=1),
nn.LeakyReLU(negative_slope=0.2, inplace=True),
nn.Conv2d(52, 52, 3, padding = 1),
nn.LeakyReLU(negative_slope=0.2, inplace=True),
nn.Conv2d(52, channels, 1)
)
def forward(self, x):
return self.net(x)
def train_once(image_noise, model, optimizer):
'''对模型进行一轮训练'''
# 用于计算差方和
mse_loss = nn.MSELoss(reduction='sum')
model.train()
optimizer.zero_grad()
# 生成噪声的子图
image_noise_s1, image_noise_s2 = diag_sample(image_noise)
# 估计噪声图像子图的干净图像
image_s1_clean = image_noise_s1 - model(image_noise_s1)
image_s2_clean = image_noise_s2 - model(image_noise_s2)
# 估计噪声图像的干净图像
image_clean = image_noise - model(image_noise)
# 生成噪声图像的干净图像的子图
image_clean_s1, image_clean_s2 = diag_sample(image_clean)
# 残差损失
loss_res = (mse_loss(image_s1_clean, image_noise_s2) + mse_loss(image_s2_clean, image_noise_s1)) / 2
# 一致性损失
loss_con = (mse_loss(image_s1_clean, image_clean_s1) + mse_loss(image_s2_clean, image_clean_s2)) / 2
# 总损失
loss = loss_res + loss_con
# 梯度反向传播
loss.backward()
# 更新模型参数
optimizer.step()
def add_noise(image, degree):
'''输入图像和噪声程度(0~1),输出加入噪声的图像'''
noise = np.random.normal(0, degree, image.shape)
noisy_image = np.clip(image + noise, 0, 1)
return noisy_image
以上代码仅作展示,更详细的代码文件请参见附件。
效果演示
配置环境并运行main.py脚本,效果如下:
使用方式
- 解压附件压缩包并进入工作目录。如果是Linux系统,请使用如下命令:
unzip Image_Denoising.zip
cd Image_Denoising
- 代码的运行环境可通过如下命令进行配置:
pip install -r requirements.txt
- 如果希望在本地运行程序,请运行如下命令:
python main.py
- 如果希望在线部署,请运行如下命令:
python main-flask.py
- 如果希望使用自己的文件路径或改动其他实验设置,请在文件
config.json
中修改对应参数。以下是参数含义对照表:
参数名 | 含义 |
---|---|
image | 输入的原始图像路径,默认为"dog.jpg",即我提供的样例 |
learning_rate | 学习率 |
epoch_count | 训练轮数 |
step_size | 学习率衰减周期 |
gamma | 学习率衰减比 |
degree | 噪声程度,默认为0.2,范围是0~1 |
max_bytes | 输入文件大小限制,默认为10240,即10KB,仅用于在线部署限制输入 |
(以上内容皆为原创,请勿转载)
参考文献
[1] Mansour Y, Heckel R. Zero-shot noise2noise: Efficient image denoising without any data[C]//Proceedings of the IEEE/CVF Conference on Computer Vision and Pattern Recognition. 2023: 14018-14027.