图像超分即超分辨率,将图像从模糊的状态变清晰
本文为深度学习专业课的实验报告,完整的源码文件/数据集获取方式见文末

1.实验目标

输入大小为h×w的图像X,输出为一个sh×sw的图像 Y,s为放大倍数。

2.数据集简介

本次实验采用的是 BSDS500 数据集,其中训练集包含 200 张图像,验证集包含 100 张图像,测试集包含 200 张图像。
数据集来源:https://download.csdn.net/download/weixin_42028424/11045313

3.数据预处理

数据预处理包含两个步骤:

(1)将图片转换成YCbCr模式
由于RGB颜色模式色调、色度、饱和度三者混在一起难以分开,因此将其转换成 YcbCr 颜色模式,Y是指亮度分量,Cb表示 RGB输入信号蓝色部分与 RGB 信号亮度值之间的差异,Cr 表示 RGB 输入信号红色部分与 RGB 信号亮度值之间的差异。

(2)将图片裁剪成 300×300 的正方形
由于后面采用的神经网路输入图片要求长宽一致,而 BSDS500 数据集中的图片长宽并不一致,因此需要对其进行裁剪。这里采用的方式是先定位到每个图片中心,然后以图片中心为基准,向四个方向拓展 150 个像素,从而将图片裁剪成 300×300 的正方形。

相关代码:

def is_image_file(filename):return any(filename.endswith(extension) for extension in [".png", ".jpg", ".jpeg"])def load_img(filepath):img = Image.open(filepath).convert('YCbCr')y, _, _ = img.split()return yCROP_SIZE = 300class DatasetFromFolder(Dataset):def __init__(self, image_dir, zoom_factor):super(DatasetFromFolder, self).__init__()self.image_filenames = [join(image_dir, x)for x in listdir(image_dir) if is_image_file(x)]crop_size = CROP_SIZE - (CROP_SIZE % zoom_factor)# 从图片中心裁剪成300*300self.input_transform = transforms.Compose([transforms.CenterCrop(crop_size),transforms.Resize(crop_size // zoom_factor),transforms.Resize(crop_size, interpolation=Image.BICUBIC),# BICUBIC 双三次插值transforms.ToTensor()])self.target_transform = transforms.Compose([transforms.CenterCrop(crop_size), transforms.ToTensor()])def __getitem__(self, index):input = load_img(self.image_filenames[index])target = input.copy()input = self.input_transform(input)target = self.target_transform(target)return input, targetdef __len__(self):return len(self.image_filenames)

4.网络结构

本次实验尝试了SRCNN和FSRCNN两个网络。

4.1 SRCNN

SRCNN 由 2014 年 Chao Dong 等人提出,是深度学习在图像超分领域的开篇之作。其网络结构如下图所示:

该网络对于一个低分辨率图像,先使用双三次插值将其放大到目标大小,再通过三层卷积网络做非线性映射,得到的结果作为高分辨率图像输出。

作者对于这三层卷积层的解释:
(1)特征块提取和表示:此操作从低分辨率图像Y中提取重叠特征块,并将每个特征块表示为一个高维向量。这些向量包括一组特征图,其数量等于向量的维数。

(2)非线性映射:该操作将每个高维向量非线性映射到另一个高维向量。每个映射向量在概念上都是高分辨率特征块的表示。这些向量同样包括另一组特征图。

(3)重建:该操作聚合上述高分辨率patch-wise(介于像素级别和图像级别的区域)表示,生成最终的高分辨率图像。

各层结构:

  • 输入:处理后的低分辨率图像
  • 卷积层 1:采用 9×9 的卷积核
  • 卷积层 2:采用 1×1 的卷积核
  • 卷积层 3:采用 5×5 的卷积核
  • 输出:高分辨率图像

模型结构代码:

class SRCNN(nn.Module):def __init__(self, upscale_factor):super(SRCNN, self).__init__()self.relu = nn.ReLU()self.conv1 = nn.Conv2d(1, 64, kernel_size=5, stride=1, padding=2)self.conv2 = nn.Conv2d(64, 64, kernel_size=3, stride=1, padding=1)self.conv3 = nn.Conv2d(64, 32, kernel_size=3, stride=1, padding=1)self.conv4 = nn.Conv2d(32, upscale_factor ** 2,kernel_size=3, stride=1, padding=1)self.pixel_shuffle = nn.PixelShuffle(upscale_factor)self._initialize_weights()def _initialize_weights(self):init.orthogonal_(self.conv1.weight, init.calculate_gain('relu'))init.orthogonal_(self.conv2.weight, init.calculate_gain('relu'))init.orthogonal_(self.conv3.weight, init.calculate_gain('relu'))init.orthogonal_(self.conv4.weight)def forward(self, x):x = self.conv1(x)x = self.relu(x)x = self.conv2(x)x = self.relu(x)x = self.conv3(x)x = self.relu(x)x = self.conv4(x)x = self.pixel_shuffle(x)return x

4.2 FSRCNN

FSRCNN 由 2016 年 Chao Dong 等人提出,与 SRCNN 是相同作者。其网络结构如下图所示:

FSRCNN在SRCNN基础上做了如下改变:
1.FSRCNN直接采用低分辨的图像作为输入,不同于SRCNN需要先对低分辨率的图像进行双三次插值然后作为输入;
2.FSRCNN在网络的最后采用反卷积层实现上采样;
3.FSRCNN中没有非线性映射,相应地出现了收缩、映射和扩展;
4.FSRCNN选择更小尺寸的滤波器和更深的网络结构。

各层结构:

  • 输入层:FSRCNN不使用bicubic插值来对输入图像做上采样,它直接进入特征提取层
  • 特征提取层:采用1 × d × ( 5 × 5 )的卷积层提取
  • 收缩层:采用d × s × ( 1 × 1 ) 的卷积层去减少通道数,来减少模型复杂度
  • 映射层:采用s × s × ( 3 × 3 ) 卷积层去增加模型非线性度来实现LR → SR 的映射
  • 扩张层:该层和收缩层是对称的,采用s × d × ( 1 × 1 ) 卷积层去增加重建的表现力
  • 反卷积层:s × 1 × ( 9 × 9 )
  • 输出层:输出HR图像

模型结构代码:

class FSRCNN(nn.Module):def __init__(self, scale_factor, num_channels=1, d=56, s=12, m=4):super(FSRCNN, self).__init__()self.first_part = nn.Sequential(nn.Conv2d(num_channels, d, kernel_size=5, padding=5//2),nn.PReLU(d))self.mid_part = [nn.Conv2d(d, s, kernel_size=1), nn.PReLU(s)]for _ in range(m):self.mid_part.extend([nn.Conv2d(s, s, kernel_size=3, padding=3//2), nn.PReLU(s)])self.mid_part.extend([nn.Conv2d(s, d, kernel_size=1), nn.PReLU(d)])self.mid_part = nn.Sequential(*self.mid_part)self.last_part = nn.ConvTranspose2d(d, num_channels, kernel_size=9, stride=scale_factor, padding=9//2,output_padding=scale_factor-1)self._initialize_weights()def _initialize_weights(self):for m in self.first_part:if isinstance(m, nn.Conv2d):nn.init.normal_(m.weight.data, mean=0.0, std=math.sqrt(2/(m.out_channels*m.weight.data[0][0].numel())))nn.init.zeros_(m.bias.data)for m in self.mid_part:if isinstance(m, nn.Conv2d):nn.init.normal_(m.weight.data, mean=0.0, std=math.sqrt(2/(m.out_channels*m.weight.data[0][0].numel())))nn.init.zeros_(m.bias.data)nn.init.normal_(self.last_part.weight.data, mean=0.0, std=0.001)nn.init.zeros_(self.last_part.bias.data)def forward(self, x):x = self.first_part(x)x = self.mid_part(x)x = self.last_part(x)return x

5.评估指标

本次实验尝试了 PSNR 和 SSIM 两个指标。

5.1 PSNR

PSNR(Peak Signal to Noise Ratio)为峰值信噪比,计算公式如下:

其中,n为每像素的比特数。
PSNR 的单位是dB,数值越大表示失真越小,一般认为 PSNR 在 38 以上的时候,人眼就无法区分两幅图片了。

相关代码:

def psnr(loss):return 10 * log10(1 / loss.item())

5.2 SSIM

SSIM(Structural Similarity)为结构相似性,由三个对比模块组成:亮度、对比度、结构。

亮度对比函数

图像的平均灰度计算公式:

亮度对比函数计算公式:

对比度对比函数

图像的标准差计算公式:

对比度对比函数计算公式:

结构对比函数

结构对比函数计算公式:

综合上述三个部分,得到 SSIM 计算公式:

其中,α\alphaα,β\betaβ,γ\gammaγ > 0,用来调整这三个模块的重要性。
SSIM 函数的值域为[0, 1], 值越大说明图像失真越小,两幅图像越相似。

相关代码:
由于pytorch没有类似tensorflow类似tf.image.ssim这样计算SSIM的接口,因此根据公式进行自定义函数用来计算

"""
计算ssim函数
"""
# 计算一维的高斯分布向量
def gaussian(window_size, sigma):gauss = torch.Tensor([exp(-(x - window_size//2)**2/float(2*sigma**2)) for x in range(window_size)])return gauss/gauss.sum()# 创建高斯核,通过两个一维高斯分布向量进行矩阵乘法得到
# 可以设定channel参数拓展为3通道
def create_window(window_size, channel=1):_1D_window = gaussian(window_size, 1.5).unsqueeze(1)_2D_window = _1D_window.mm(_1D_window.t()).float().unsqueeze(0).unsqueeze(0)window = _2D_window.expand(channel, 1, window_size, window_size).contiguous()return window# 计算SSIM
# 直接使用SSIM的公式,但是在计算均值时,不是直接求像素平均值,而是采用归一化的高斯核卷积来代替。
# 在计算方差和协方差时用到了公式Var(X)=E[X^2]-E[X]^2, cov(X,Y)=E[XY]-E[X]E[Y].
def ssim(img1, img2, window_size=11, window=None, size_average=True, full=False, val_range=None):# Value range can be different from 255. Other common ranges are 1 (sigmoid) and 2 (tanh).if val_range is None:if torch.max(img1) > 128:max_val = 255else:max_val = 1if torch.min(img1) < -0.5:min_val = -1else:min_val = 0L = max_val - min_valelse:L = val_rangepadd = 0(_, channel, height, width) = img1.size()if window is None:real_size = min(window_size, height, width)window = create_window(real_size, channel=channel).to(img1.device)mu1 = F.conv2d(img1, window, padding=padd, groups=channel)mu2 = F.conv2d(img2, window, padding=padd, groups=channel)mu1_sq = mu1.pow(2)mu2_sq = mu2.pow(2)mu1_mu2 = mu1 * mu2sigma1_sq = F.conv2d(img1 * img1, window, padding=padd,groups=channel) - mu1_sqsigma2_sq = F.conv2d(img2 * img2, window, padding=padd,groups=channel) - mu2_sqsigma12 = F.conv2d(img1 * img2, window, padding=padd,groups=channel) - mu1_mu2C1 = (0.01 * L) ** 2C2 = (0.03 * L) ** 2v1 = 2.0 * sigma12 + C2v2 = sigma1_sq + sigma2_sq + C2cs = torch.mean(v1 / v2)  # contrast sensitivityssim_map = ((2 * mu1_mu2 + C1) * v1) / ((mu1_sq + mu2_sq + C1) * v2)if size_average:ret = ssim_map.mean()else:ret = ssim_map.mean(1).mean(1).mean(1)if full:return ret, csreturn retclass SSIM(torch.nn.Module):def __init__(self, window_size=11, size_average=True, val_range=None):super(SSIM, self).__init__()self.window_size = window_sizeself.size_average = size_averageself.val_range = val_range# Assume 1 channel for SSIMself.channel = 1self.window = create_window(window_size)def forward(self, img1, img2):(_, channel, _, _) = img1.size()if channel == self.channel and self.window.dtype == img1.dtype:window = self.windowelse:window = create_window(self.window_size, channel).to(img1.device).type(img1.dtype)self.window = windowself.channel = channelreturn ssim(img1, img2, window=window, window_size=self.window_size, size_average=self.size_average)

6.模型训练/测试

设定 epoch 为 500 次,保存验证集上 PSNR 最高的模型。两个模型在测试集上的表现如下表所示:

从结果可以发现,FSRCNN 的 PSNR 比 SRCNN 低,但 FSRCNN 的 SSIM 比 SRCNN 高,说明 PSNR 和 SSIM 并不存在完全正相关的关系。

训练/验证代码:

model = FSRCNN(1).to(device)
criterion = nn.MSELoss()
optimizer = optim.Adam(model.parameters(), lr=1e-2)
scheduler = MultiStepLR(optimizer, milestones=[50, 75, 100], gamma=0.1)
best_psnr = 0.0
for epoch in range(nb_epochs):# Trainepoch_loss = 0for iteration, batch in enumerate(trainloader):input, target = batch[0].to(device), batch[1].to(device)optimizer.zero_grad()out = model(input)loss = criterion(out, target)loss.backward()optimizer.step()epoch_loss += loss.item()print(f"Epoch {epoch}. Training loss: {epoch_loss / len(trainloader)}")# Valsum_psnr = 0.0sum_ssim = 0.0with torch.no_grad():for batch in valloader:input, target = batch[0].to(device), batch[1].to(device)out = model(input)loss = criterion(out, target)pr = psnr(loss)sm = ssim(input, out)sum_psnr += prsum_ssim += smprint(f"Average PSNR: {sum_psnr / len(valloader)} dB.")print(f"Average SSIM: {sum_ssim / len(valloader)} ")avg_psnr = sum_psnr / len(valloader)if avg_psnr >= best_psnr:best_psnr = avg_psnrtorch.save(model, r"best_model_FSRCNN.pth")scheduler.step()

测试代码:

BATCH_SIZE = 4
model_path = "best_model_FSRCNN.pth"
testset = DatasetFromFolder(r"./data/images/test", zoom_factor)
testloader = DataLoader(dataset=testset, batch_size=BATCH_SIZE,shuffle=False, num_workers=NUM_WORKERS)
sum_psnr = 0.0
sum_ssim = 0.0
model = torch.load(model_path).to(device)
criterion = nn.MSELoss()
with torch.no_grad():for batch in testloader:input, target = batch[0].to(device), batch[1].to(device)out = model(input)loss = criterion(out, target)pr = psnr(loss)sm = ssim(input, out)sum_psnr += prsum_ssim += sm
print(f"Test Average PSNR: {sum_psnr / len(testloader)} dB")
print(f"Test Average SSIM: {sum_ssim / len(testloader)} ")

7.实图测试

为了直观感受两个模型的效果,我用自己拍摄的图进行实图测试,效果如下:
s=1(放大倍数=1)

当放大倍数=1时,SRCNN的超分结果比FSRCNN的超分效果要更好一些,这和两个模型平均 PSNR 的数值相吻合。

s=2(放大倍数=2)


当放大倍数=2时,SRCNN 的超分结果和 FSRCNN 的超分效果相差不大。

相关代码:

# 参数设置
zoom_factor = 1
model = "best_model_SRCNN.pth"
model2 = "best_model_FSRCNN.pth"
image = "tree.png"
cuda = 'store_true'
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")# 读取图片
img = Image.open(image).convert('YCbCr')
img = img.resize((int(img.size[0] * zoom_factor), int(img.size[1] * zoom_factor)), Image.BICUBIC)
y, cb, cr = img.split()
img_to_tensor = transforms.ToTensor()
input = img_to_tensor(y).view(1, -1, y.size[1], y.size[0]).to(device)# 输出图片
model = torch.load(model).to(device)
out = model(input).cpu()
out_img_y = out[0].detach().numpy()
out_img_y *= 255.0
out_img_y = out_img_y.clip(0, 255)
out_img_y = Image.fromarray(np.uint8(out_img_y[0]), mode='L')
out_img = Image.merge('YCbCr', [out_img_y, cb, cr]).convert('RGB')model2 = torch.load(model2).to(device)
out2 = model2(input).cpu()
out_img_y2 = out2[0].detach().numpy()
out_img_y2 *= 255.0
out_img_y2 = out_img_y2.clip(0, 255)
out_img_y2 = Image.fromarray(np.uint8(out_img_y2[0]), mode='L')
out_img2 = Image.merge('YCbCr', [out_img_y2, cb, cr]).convert('RGB')# 绘图显示
fig, ax = plt.subplots(1, 3, figsize=(20, 20))
ax[0].imshow(img)
ax[0].set_title("原图")
ax[1].imshow(out_img)
ax[1].set_title("SRCNN恢复结果")
ax[2].imshow(out_img2)
ax[2].set_title("FSRCNN恢复结果")
plt.show()
fig.savefig(r"tree2.png")

源码获取

实验报告,完整的源码文件,数据集获取:
https://download.csdn.net/download/qq1198768105/85906814

【深度学习】图像超分实验:SRCNN/FSRCNN相关推荐

  1. 深度学习图像超分辨率最新综述:从模型到应用

    点击我爱计算机视觉标星,更快获取CVML新技术 今日arXiv新上论文<Deep Learning for Image Super-resolution:A Survey>,详细回顾了近年 ...

  2. 深度学习图像超分辨率开山之作SRCNN——原理分析及代码(效果基本可以达到论文中的效果)

    基于python+tensorflow下的超分辨率图像重构(效果基本可以达到论文中的效果) 论文地址:点击此处跳转 搞这篇论文时,踩了很多坑,效果优于网上的大部分代码,网上大部分代码效果离理想效果差5 ...

  3. 那些让人耳目一新的深度学习图像超分辨率重建方法

    本文章只记录(按时间),不分析网络结构.优劣.可能会有遗漏,见谅. 1. SRCNN -- ECCV2014 paper:Learning a Deep Convolutional Network f ...

  4. 深度学习在超分辨率重建上的应用SRCNN,FSRCNN,VDSR,DRCN,SRGAN

    超分辨率技术(Super-Resolution)是指从观测到的低分辨率图像重建出相应的高分辨率图像, 目前应用较多的应用场景是图像及视频分辨率提高,比如可以提高以往影视作品或图像的分辨率,提高视觉感官 ...

  5. 深度学习磁共振图像超分与重建论文阅读

    深度学习磁共振图像超分与重建算法研究 Super-resolution reconstruction of MR image with a novel residual learning networ ...

  6. 《深度学习》图像超分初识

    一:简介 图像超分(super-Resolution)是将低分辨率的图像或者视频序列恢复出高分辨率图像. 可以用在视频数字高清播放,视频监控,视频编码,图像还原和医学影像等领域,按照类别可分为单个图像 ...

  7. 图像超分中的深度学习网络

    图像超分中的深度学习网络 质量评估 操作通道 有监督算法 预上采样 后采样超分 逐步上采样 迭代上下采样 上采样的学习方式 残差块 递归学习 多路径学习 密集连接 通道注意力机制 其他卷积 像素递归网 ...

  8. SRCNN-基于深度学习的图像超分入门

    图像超分 图像超分辨率问题定义: 输入一张低分辨率图像时(low resolution,LR),通过算法,输出一张高分辨率图像(high resolution,HR) 传统的图像插值算法可以在某种程度 ...

  9. 深度学习图像融合_基于深度学习的图像超分辨率最新进展与趋势【附PDF】

    因PDF资源在微信公众号关注公众号:人工智能前沿讲习回复"超分辨"获取文章PDF 1.主题简介 图像超分辨率是计算机视觉和图像处理领域一个非常重要的研究问题,在医疗图像分析.生物特 ...

  10. 黄浴:基于深度学习的超分辨率图像技术发展轨迹一览

    作者 | 黄浴 转载自知乎 导读:近年来,使用深度学习技术的图像超分辨率(SR)取得了显著进步.本文中,奇点汽车自动驾驶首席科学家黄浴对基于深度学习技术的图像超分辨率技术进行了一次全面的总结,分析了这 ...

最新文章

  1. ExtJs4 笔记(8) Ext.slider 滚轴控件、 Ext.ProgressBar 进度条控件、 Ext.Editor 编辑控件...
  2. 超图iServer发布一个示例3D场景
  3. 【很久之前的一篇老文章】一位程序员工作10年总结的13个忠告
  4. Java包hashCode()方法及示例
  5. flutter Toast消息提示框
  6. mysql lru scan depth_如何解决mysql警告:“ InnoDB:page_cleaner:1000毫秒的预期循环用了XXX毫秒。设置可能不是最佳的”?...
  7. html标签元素分类
  8. html中div页面布局,前端入门篇(二):利用Div + CSS快速布局页面
  9. OSEK network management
  10. SQL 存储过程或语句获取月份简写
  11. 33种著名汽车标志及来历
  12. 计算机科学与工程版面费,《计算机工程与设计》版面费问题 - 论文投稿 - 小木虫 - 学术 科研 互动社区...
  13. 「2020」拼多多数据分析笔试题 | 附解答
  14. STM32系列(HAL库)——F103C8T6点亮1.44寸TFT-LCD彩屏
  15. 2021年「博客之星」参赛博主:smileNicky投票
  16. linux 下的 wchar_t
  17. 非凡网络 最新钓鱼程序 最新钓鱼源码 QQ空间钓鱼源码 淘宝钓鱼源码 ff999.cn
  18. 华硕飞行堡垒系列无线网经常显示“无法连接网络” || 一打开游戏就断网
  19. SSM+Vue+Element-UI实现网上跳蚤市场
  20. unity3d terrian tree 地形组件 草木石树无法碰撞的解决办法

热门文章

  1. si4463如何读取RSSI
  2. weka下载安装以及源码运行
  3. 计算机思维发展阶段,巩固计算思维是每个教育阶段的重要目标。
  4. 通过SAXReader解析XML
  5. npm加速器、github加速器
  6. 【Java】按要求编程输出2018年日历
  7. java判断字符串是否是空,java判断字符串是否为空的方法
  8. SQL Server新增字段并添加描述
  9. 企业业务逻辑常见风险
  10. VUE源码相关面试题汇总