单图像超分辨率重建示例代码解析
昨天发了单图像超分辨率重建示例,今天来对里面主要的Python代码进行讲解,如果有补充或指正,欢迎大家在评论区留言。PS:这里的代码不要直接复制粘贴使用,由于注释的关系可能会报错,建议到示例给出的git中直接下载。
目录
一、run.py
二、data.py
三、image.py
四、 experiment.py
五、layers.py
六、metrics.py
七、models.py
八、paths.py
九、run_all.py
十、参考目录
一、run.py
import argparse
# argparse 是 Python 的命令行工具模块,用来解析从命令行输入的命令
# import argparse
# parser = argparse.ArgumentParser()
# parser.parse_args()
# 这三行是它最基本的用法,在执行 parse_args() 之前,所有追加到命令行的参数都不会生效,生效之后的
# 效果类似于python run.py -h(默认参数是-h)from functools import partial
# functools 模块为高阶函数提供支持——作用于或返回函数的函数被称为高阶函数。在该模块看来,一切可调
# 用的对象均可视为本模块中所说的“函数”。
# partial 则是一个函数装饰器,调用 partial 对象就和调用被修饰的函数 func 相同,只不过调用
# partial 对象时传入的参数个数通常少于调用 func 时传入的参数个数。 当一个函数 func 可以接收很多
# 参数,而某一次使用只需要更改其中的一部分参数,其他的某些参数都保持不变时, partial 对象就可以将
# 这些不变的对象冻结起来,这样调用 partial 对象时传入未冻结的参数, partial 对象调用 func 时连
# 同已经被冻结的参数一同传给 func 函数,从而简化了调用过程。如果调用 partial 对象时提供了更多的
# 参数,那么他们会被添加到 args 的后面,如果提供了更多的关键字参数,那么它们将扩展或覆写已经冻结
# 的关键字参数。
# 比如这个简单的函数使用 partial 对象创建一个 base 参数始终为 2 的 int()
# from functools import partial
# basetwo = partial(int, base=2)
# basetwo.__doc__ = 'Convert base 2 string to an int.'
# basetwo('10010')
# 这个新的 partial 对象 basetwo 能够将二进制的参数转化为十进制的整型结果,在调用这个 partial 对
# 象时只需要传入二进制的目标参数即可。import json
# 导入json模块,用于解码json格式或将键值对编码成json格式。JSON(JavaScript Object Notation) 是
# 一种轻量级的数据交换格式,易于人阅读和编写。
# json.dumps 用于将 Python 对象编码成 JSON 字符串。
# data = [ { 'a' : 1, 'b' : 2, 'c' : 3, 'd' : 4, 'e' : 5 } ]
# json = json.dumps(data)
# print json
# 执行结果是
# [{"a": 1, "c": 3, "b": 2, "e": 5, "d": 4}]
# 使用参数让 JSON 数据格式化输出
# print json.dumps({'a': 'Runoob', 'b': 7}, sort_keys=True, indent=4(缩进), separators=
# (',', ': # ')(分离器))
# {
# "a": "Runoob",
# "b": 7
# }
# json.loads 用于解码 JSON 数据。该函数返回 Python 字段的数据类型。
# jsonData = '{"a":1,"b":2,"c":3,"d":4,"e":5}';
# text = json.loads(jsonData)
# print text
# 输出结果为
# {u'a': 1, u'c': 3, u'b': 2, u'e': 5, u'd': 4}from keras import optimizers
# Keras是一个高层神经网络API,Keras由纯Python编写而成并基Tensorflow、Theano以及CNTK后端。
# Keras 为支持快速实验而生,能够把你的idea迅速转换为结果。它的特点是:简易和快速的原型设计
# (keras 具有高度模块化,极简,和可扩充特性);支持CNN和RNN,或二者的结合;无缝 CPU 和 GPU 切
# 换。它适用于2.7-3.6版本的 Python。
# Keras 的核心数据结构是“模型”,模型是一种组织网络层的方式。Keras 中主要的模型是 Sequential 模
# 型,Sequential 是一系列网络层按顺序构成的栈。
# optimizers 是优化器对象,是编译 Keras 模型必要的两个参数之一
# 我们先建立一个序列模型,建立一个优化器对象,然后再对其进行优化
# model = Sequential()
# 添加模型参数
# model.add(Dense(64, kernel_initializer='uniform', input_shape=(10,)))
# model.add(Activation('tanh'))
# model.add(Activation('softmax'))
# 建立优化器对象
# sgd = optimizers.SGD(lr=0.01, decay=1e-6, momentum=0.9, nesterov=True)
# SGD 随机梯度下降法
# lr:大或等于0的浮点数,学习率
# momentum:大或等于0的浮点数,动量参数
# decay:大或等于0的浮点数,每次更新后的学习率衰减值
# nesterov:布尔值,确定是否使用Nesterov动量
# model.compile(loss='mean_squared_error', optimizer=sgd)from pathlib import Path
# pathlib 类库用来取代 sys.os.path,pathlib 中的 Path 类可以创建 path 路径对象, 属于比
# os.path 更高抽象级别的对象。
# 其基本用法有:
# Path.iterdir() #遍历目录的子目录或者文件
# Path.is_dir() #判断是否是目录
# Path.glob() #过滤目录(返回生成器)
# Path.resolve() #返回绝对路径
# Path.exists() #判断路径是否存在
# Path.open() #打开文件(支持with)
# Path.unlink() #删除文件或目录(目录非空触发异常)from toolbox.data import load_set
from toolbox.models import get_model
from toolbox.experiment import Experiment
# toolbox 是源码作者单独写的一个工具文件,没有进行打包,之后会对其中的每个文件进行详细说明parser = argparse.ArgumentParser()
# 建立解析器对象
parser.add_argument('param_file', type=Path)
# 为解析器对象添加属性
args = parser.parse_args()
# 解析命令行语句
param = json.load(args.param_file.open())
# 读入 json 文件中的参数# 建立模型
scale = param['scale']
# 获取倍数参数
build_model = partial(get_model(param['model']['name']),**param['model']['params'])
# 根据 json 文件中的模型参数构建模型
if 'optimizer' in param:
# 如果 json 文件中给出了 optimizer 的参数optimizer = getattr(optimizers, param['optimizer']['name'].lower())optimizer = optimizer(**param['optimizer']['params'])# 读取它并建立一个optimizer
else:optimizer = 'adam'# 否则建立一个默认的 AdamOptimizer 类型的优化器# 读取已有的模型参数数据
load_set = partial(load_set,lr_sub_size=param['lr_sub_size'],lr_sub_stride=param['lr_sub_stride'])
# 从历史数据文件中读取模型大小及步幅# 对模型进行训练
expt = Experiment(scale=param['scale'](倍数), load_set=load_set(数据集),build_model=build_model(模型),optimizer=optimizer(优化器),save_dir=param['save_dir'](保存文件名))
expt.train(train_set=param['train_set'](训练集), val_set=param['val_set'](结果集),epochs=param['epochs'](循环次数), resume=True(是否重复))# 对模型进行评估,得到损失值和峰值信噪比等评估参数
for test_set in param['test_sets']:expt.test(test_set=test_set)
二、data.py
from functools import partialimport numpy as np
# NumPy 系统是 Python 的一种开源的数值计算扩展。这种工具可用来存储和处理大型矩阵,比 Python 自
# 身的嵌套列表(nested list structure)结构要高效的多(该结构也可以用来表示矩阵(matrix))
# NumPy(Numeric Python)提供了许多高级的数值编程工具,如:矩阵数据类型、矢量处理,以及精密的运
# 算库。专为进行严格的数字处理而产生。
# np是numpy的假名,在后续的代码中可以直接使用,如np.stack(xxx)from keras.preprocessing.image import img_to_array
from keras.preprocessing.image import load_img
# keras.preprocessing.image 是对图像进行处理的一整个工具类
# load_img用于读入图像文件
# img_to_array用于将读入的图像文件转化为数组存储,约等于numpy.asarray
# image = img_to_array(image)的结果近似于
# [[[1,2,3],[1,2,3],...,[1,2,3]],...,[[1,2,3],[1,2,3],...,[1,2,3]]]from toolbox.image import bicubic_rescale
from toolbox.image import modcrop
from toolbox.paths import data_dir
# 会在对应的文件中进行说明def load_set(name, lr_sub_size=11, lr_sub_stride=5, scale=3):
# 函数load_set(名称,大小默认=11,步长默认=5,倍数默认=3),读入图像集hr_sub_size = lr_sub_size * scale# 重建后大小hr_sub_stride = lr_sub_stride * scale# 重建后步长lr_gen_sub = partial(generate_sub_images, size=lr_sub_size,stride=lr_sub_stride)# 定义一个新函数hr_gen_sub = partial(generate_sub_images, size=hr_sub_size,stride=hr_sub_stride)lr_sub_arrays = []hr_sub_arrays = []for path in (data_dir / name).glob('*'):# path/data_dir 目录下所有文件lr_image, hr_image = load_image_pair(str(path), scale=scale)lr_sub_arrays += [img_to_array(img) for img in lr_gen_sub(lr_image)]hr_sub_arrays += [img_to_array(img) for img in hr_gen_sub(hr_image)]x = np.stack(lr_sub_arrays)# stack(arrays, axis=0),增加一维,新维度下标为0,如:# a=[[1,2,3],[4,5,6]]# np.stack(a,axis=0)# 结果为[[1 2 3] [4 5 6]](即变成两行)# 如果axis=1,则变成两列[[1 4] [2 5] [3 6]]y = np.stack(hr_sub_arrays)return x, ydef load_image_pair(path, scale=3):
# 从路径下读取一张图像,倍数默认=3image = load_img(path)image = image.convert('YCbCr')# image.convert()将图片转换成不同的格式,PIL中有九种不同模式。分别为1(两值模式,非黑即# 白),L(灰度图像),P(8位彩色图像,由调色板查出),RGB(24位彩色图像),RGBA(32位彩色图# 像,其中8位表示alpha透明度),CMYK(32位彩色图像,它的每个像素用32个bit表示,青色、洋红、# 黄色、黑色),YCbCr(24位彩色图像,Y是指亮度分量,Cb指蓝色色度分量,而Cr指红色色度分量),# I(32位整型灰色图像),F(32位浮点灰色图像)。hr_image = modcrop(image, scale)lr_image = bicubic_rescale(hr_image, 1 / scale)# 自定义函数,在image.py中进行说明return lr_image, hr_imagedef generate_sub_images(image, size, stride):
# 对像素点依次进行处理for i in range(0, image.size[0] - size + 1, stride):for j in range(0, image.size[1] - size + 1, stride):yield image.crop([i, j, i + size, j + size])# yield 的作用就是把一个函数变成一个 generator,带有 yield 的函数不再是一个普通函 # 数,Python 解释器会将其视为一个 genrator,它不会执行任何函数代码,直到对其调用 # next()(在 for 循环中会自动调用 next())才开始执行。# yield 的好处是显而易见的,它把一个函数改写为一个 generator 就获得了迭代能力,比起# 用类的实例保存状态来计算下一个 next() 的值,不仅代码简洁,而且执行流程异常清晰。# image.crop(左,上,右,下),作用为从图片的最前头拷贝一部分图片
三、image.py
import numpy as np
from PIL import Image
# PIL(Python Imaging Library)是python语言中对图像处理方面的一个开源库,其主要功能模块为Imagedef array_to_img(x, mode='YCbCr'):
# 将数据数组转换为mode类型的图片,x为输入数组,mode为输出类型默认为YCbCr型return Image.fromarray(x.astype('uint8'), mode=mode)# 返回修改格式后的图片def bicubic_rescale(image, scale):
# 双三次插值重建函数。三次卷积插值是一种更加复杂的插值方式。该算法利用待采样点周围16个点的灰度值
# 作三次插值,不仅考虑到4 个直接相邻点的灰度影响,而且考虑到各邻点间灰度值变化率的影响。三次运算
# 可以得到更接近高分辨率图像的放大效果,但也导致了运算量的急剧增加。这种算法需要选取插值基函数来
# 拟合数据。if isinstance(scale, (float, int)):size = (np.array(image.size) * scale).astype(int)return image.resize(size, resample=Image.BICUBIC)def modcrop(image, scale):
# 余差抠图函数size = np.array(image.size)size -= size % scalereturn image.crop([0, 0, *size])
四、 experiment.py
from functools import partial
from pathlib import Path
import time
# 导入时间相关库from keras import backend as K
# backend 运行后端,这里使用的是tensorflow
from keras.callbacks import CSVLogger
# CSVLogger 是把训练轮结果数据流到 csv 文件的回调函数
# keras.callbacks.CSVLogger(filename, separator=',', append=False)
# filename: csv 文件的文件名,例如 'run/log.csv'。
# separator: 用来隔离 csv 文件中元素的字符串。
# append: True:如果文件存在则增加(可以被用于继续训练)。False:覆盖存在的文件。from keras.callbacks import ModelCheckpoint
# 可以在每个训练期之后保存模型
# keras.callbacks.ModelCheckpoint(filepath, monitor='val_loss', verbose=0,
# save_best_only=False, save_weights_only=False, mode='auto', period=1)
# filepath: 字符串,保存模型的路径。
# monitor: 被监测的数据。
# verbose: 详细信息模式,0 或者 1 。
# save_best_only: 如果 save_best_only=True, 被监测数据的最佳模型就不会被覆盖。
# mode: {auto, min, max} 的其中之一。 如果 save_best_only=True,那么是否覆盖保存文件的决定就
# 取决于被监测数据的最大或者最小值。 对于 val_acc,模式就会是 max,而对于 val_loss,模式就需要
# 是 min,等等。 在 auto 模式中,方向会自动从被监测的数据的名字中判断出来。
# save_weights_only: 如果 True,那么只有模型的权重会被保存 (model.save_weights(filepath)),
# 否则的话,整个模型会被保存 (model.save(filepath))。
# period: 每个检查点之间的间隔(训练轮数)。from keras.optimizers import adam
# adam优化器
# keras.optimizers.Adam(lr=0.001, beta_1=0.9, beta_2=0.999, epsilon=None, decay=0.0,
# amsgrad=False)
# lr: float >= 0. 学习率。
# beta_1: float, 0 < beta < 1. 通常接近于 1。
# beta_2: float, 0 < beta < 1. 通常接近于 1。
# epsilon: float >= 0. 模糊因子. 若为 None, 默认为 K.epsilon()。
# decay: float >= 0. 每次参数更新后学习率衰减值。
# amsgrad: boolean. 是否应用此算法的 AMSGrad 变种,来自论文 "On the Convergence of Adam and # Beyond"。from keras.preprocessing.image import img_to_arrayimport matplotlib
# Matplotlib 是一个 Python 2D 绘图库,它可以在各种平台上以各种硬拷贝格式和交互式环境生成出具有
# 出版品质的图形。
matplotlib.use('Agg')
# 这句好像是必须的,删掉之后会报错,我看文档里面也没说为什么,暂且先不管它
import matplotlib.pyplot as plt
# 导入绘图工具pyplotimport numpy as npimport pandas as pd
# pandas 是基于 NumPy 的一种工具,该工具是为了解决数据分析任务而创建的。Pandas 纳入了大量库和一
# 些标准的数据模型,提供了高效地操作大型数据集所需的工具。pandas 提供了大量能使我们快速便捷地处理
# 数据的函数和方法。你很快就会发现,它是使 Python 成为强大而高效的数据分析环境的重要因素之一。from toolbox.data import load_image_pair
from toolbox.image import array_to_img
from toolbox.metrics import psnr
from toolbox.models import bicubic
from toolbox.paths import data_dir
# 源码作者编写的工具类,后续会继续说明class Experiment(object):
# 定义Experiment类,这个类是整个源代码体系结构中最重要的一部分def __init__(self, scale=3, load_set=None, build_model=None,optimizer='adam', save_dir='.'):# 初始化函数,建立一系列模型相关的文件保存数据,每一次建立的模型都保存为一个hdf5文件self.scale = scale# 倍数self.load_set = partial(load_set, scale=scale)# 图集self.build_model = partial(build_model, scale=scale)# 模型self.optimizer = optimizer# 优化器self.save_dir = Path(save_dir)# 保存目录self.save_dir.mkdir(parents=True, exist_ok=True)# 创建保存目录self.config_file = self.save_dir / 'config.yaml'# 创建config属性文件self.model_file = self.save_dir / 'model.hdf5'# 创建模型数据文件self.train_dir = self.save_dir / 'train'# 训练集目录self.train_dir.mkdir(exist_ok=True)# 建立训练集目录self.history_file = self.train_dir / 'history.csv'# 建立历史数据文件self.weights_dir = self.train_dir / 'weights'# 模型权重目录self.weights_dir.mkdir(exist_ok=True)# 建立模型权重目录self.test_dir = self.save_dir / 'test'# 测试集目录self.test_dir.mkdir(exist_ok=True)# 建立测试集目录def weights_file(self, epoch=None):# 创建并保存权值文件if epoch is None:# 循环次数为0return self.weights_dir / 'ep{epoch:04d}.hdf5'else:# 否则return self.weights_dir / f'ep{epoch:04d}.hdf5'@propertydef latest_epoch(self):# 读取最近的模型的数据try:# 如果文件存在,则读取并返回数据内容return pd.read_csv(str(self.history_file))['epoch'].iloc[-1]except (FileNotFoundError, pd.io.common.EmptyDataError):# 否则跳过passreturn -1def _ensure_dimension(self, array, dim):# 检验矩阵维度是否符合要求while len(array.shape) < dim:array = array[np.newaxis, ...]return arraydef _ensure_channel(self, array, c):# 检验图像通道是否符合要求return array[..., c:c+1]def pre_process(self, array):# 预处理array = self._ensure_dimension(array, 4)# 不超过4维array = self._ensure_channel(array, 0)# 无额外通道return arraydef post_process(self, array, auxiliary_array):# 后处理array = np.concatenate([array, auxiliary_array[..., 1:]], axis=-1)# 拼接数组array = np.clip(array, 0, 255)# 规范化数组,clip(数组,最小值,最大值)return arraydef inverse_post_process(self, array):# 反后处理(PS:个人看起来跟预处理没有任何区别,不知道为什么要写这么一个函数)array = self._ensure_dimension(array, 4)array = self._ensure_channel(array, 0)return arraydef compile(self, model):# 建立模型model.compile(optimizer=self.optimizer, loss='mse', metrics=[psnr])# 以默认参数构造模型return modeldef train(self, train_set='91-image', val_set='Set5', epochs=1,resume=True):# 训练模型# 读入数据并对数据进行预处理x_train, y_train = self.load_set(train_set)x_val, y_val = self.load_set(val_set)x_train, x_val = [self.pre_process(x)for x in [x_train, x_val]]y_train, y_val = [self.inverse_post_process(y)for y in [y_train, y_val]]model = self.compile(self.build_model(x_train))model.summary()# 根据结果建立新模型# Save model architecture# Currently in Keras 2 it's not possible to load a model with custom# layers. So we just save it without checking consistency.self.config_file.write_text(model.to_yaml())# 将模型参数保存到yaml文件当中以待继续训练或使用# Inherit weights# 继承模型的参数,未达到指定的训练轮次则继续训练if resume:latest_epoch = self.latest_epochif latest_epoch > -1:weights_file = self.weights_file(epoch=latest_epoch)model.load_weights(str(weights_file))initial_epoch = latest_epoch + 1else:initial_epoch = 0# Set up callbacks# 建立反馈,修改模型callbacks = []callbacks += [ModelCheckpoint(str(self.model_file))]callbacks += [ModelCheckpoint(str(self.weights_file()),save_weights_only=True)]callbacks += [CSVLogger(str(self.history_file), append=resume)]# Train# 对模型进行训练model.fit(x_train, y_train, epochs=epochs, callbacks=callbacks,validation_data=(x_val, y_val), initial_epoch=initial_epoch)# Plot metrics history# 根据结果绘制历史数据曲线,即train->history.loss和history.psnrprefix = str(self.history_file).rsplit('.', maxsplit=1)[0]df = pd.read_csv(str(self.history_file))epoch = df['epoch']for metric in ['Loss', 'PSNR']:train = df[metric.lower()]val = df['val_' + metric.lower()]plt.figure()plt.plot(epoch, train, label='train')plt.plot(epoch, val, label='val')plt.legend(loc='best')plt.xlabel('Epoch')plt.ylabel(metric)plt.savefig('.'.join([prefix, metric.lower(), 'png']))plt.close()def test(self, test_set='Set5', metrics=[psnr]):# 测试模型print('Test on', test_set)image_dir = self.test_dir / test_setimage_dir.mkdir(exist_ok=True)# 读入测试图片集,建立测试结果文件夹# Evaluate metrics on each image# 计算每张图片的psnr矩阵rows = []for image_path in (data_dir / test_set).glob('*'):# 获取指定目录下的所有文件rows += [self.test_on_image(str(image_path),str(image_dir / image_path.stem),metrics=metrics)]# 对图片文件进行测试df = pd.DataFrame(rows)# DataFrame是Python中Pandas库中的一种数据结构,它类似excel,是一种二维表,可以存放多种# 数据结构。# Compute average metrics# 计算平均值row = pd.Series()row['name'] = 'average'for col in df:if col != 'name':row[col] = df[col].mean()df = df.append(row, ignore_index=True)df.to_csv(str(self.test_dir / f'{test_set}/metrics.csv'))# 将结果保存至csv文件def test_on_image(self, path, prefix, suffix='png', metrics=[psnr]):# 对图片文件进行测试,得到loss值和psnr值# Load imageslr_image, hr_image = load_image_pair(path, scale=self.scale)# 读入图像# Generate bicubic image# 初始化插值图像x = img_to_array(lr_image)[np.newaxis, ...]bicubic_model = bicubic(x, scale=self.scale)y = bicubic_model.predict_on_batch(x)# predict_on_batch()函数在一个 batch 的样本上对模型进行测试,返回模型在一个 batch 上# 的预测结果bicubic_array = np.clip(y[0], 0, 255)# Generate output image and measure run time# 初始化输出图像,并计算运行时间x = self.pre_process(x)model = self.compile(self.build_model(x))if self.model_file.exists():model.load_weights(str(self.model_file))start = time.perf_counter()# time.perf_counter()获取时间戳y_pred = model.predict_on_batch(x)end = time.perf_counter()output_array = self.post_process(y_pred[0], bicubic_array)output_image = array_to_img(output_array, mode='YCbCr')# Record metrics# 记录矩阵row = pd.Series()row['name'] = Path(path).stemrow['time'] = end - starty_true = self.inverse_post_process(img_to_array(hr_image))for metric in metrics:row[metric.__name__] = K.eval(metric(y_true, y_pred))# Save images# 保存结果图片images_to_save = []images_to_save += [(hr_image, 'original')]images_to_save += [(output_image, 'output')]images_to_save += [(lr_image, 'input')]for img, label in images_to_save:img.convert(mode='RGB').save('.'.join([prefix, label, suffix]))return row
五、layers.py
from keras.engine.topology import Layer
# 对于简单、无状态的自定义操作,你也许可以通过 layers.core.Lambda 层来实现。但是对于那些包含了
# 可训练权重的自定义层,你应该自己实现这种层。在 keras 中,每个层都是对象,可以通过 dir ( Layer
# 对象)来查看具有哪些属性。建立一个层只需要实现三个方法:
# build(input_shape): 这是你定义权重的地方。这个方法必须设 self.built = True,可以通过调用
# super([Layer], self).build() 完成。
# call(x): 这里是编写层的功能逻辑的地方。你只需要关注传入 call 的第一个参数:输入张量,除非你希
# 望你的层支持 masking。
# compute_output_shape(input_shape): 如果你的层更改了输入张量的形状,你应该在这里定义形状变化
# 的逻辑,这让 Keras 能够自动推断各层的形状。import numpy as npimport tensorflow as tf
# TensorFlow 是一个采用数据流图(data flow graphs),用于数值计算的开源软件库。节点(Nodes)在
# 图中表示数学操作,图中的线(edges)则表示在节点间相互联系的多维数据数组,即张量(tensor)。它
# 最初由 Google 大脑小组(隶属于 Google 机器智能研究机构)的研究员和工程师们开发出来,用于机器学
# 习和深度神经网络方面的研究,但这个系统的通用性使其也可广泛用于其他计算领域。custom_layers = {}# 保存不同的层class ImageRescale(Layer):
# 重定义图像大小类def __init__(self, scale, method=tf.image.ResizeMethod.BICUBIC,trainable=False, **kwargs):# 初始化函数self.scale = scaleself.method = methodsuper().__init__(trainable=trainable, **kwargs)# 继承父类的__init__()方法def compute_size(self, shape):# 计算图像大小size = np.array(shape)[[1, 2]] * self.scalereturn tuple(size.astype(int))# 返回一个元组def call(self, x):# 修改图片大小size = self.compute_size(x.shape.as_list())return tf.image.resize_images(x, size, method=self.method)def compute_output_shape(self, input_shape):# 计算输出的形状size = self.compute_size(input_shape)return (input_shape[0], *size, input_shape[3])def get_config(self):# 得到对应的属性值config = super().get_config()config['scale'] = self.scaleconfig['method'] = self.methodreturn configcustom_layers['ImageRescale'] = ImageRescale
# 建立 ImageRescale 层class Conv2DSubPixel(Layer):
# 二维亚像素点卷积类# 卷积层的建立可以参考这个网址的说明:https://arxiv.org/abs/1609.05158def __init__(self, scale, trainable=False, **kwargs):self.scale = scalesuper().__init__(trainable=trainable, **kwargs)def call(self, t):r = self.scaleshape = t.shape.as_list()new_shape = self.compute_output_shape(shape)H, W = shape[1:3]C = new_shape[-1]t = tf.reshape(t, [-1, H, W, r, r, C])# 源码作者没有使用网站中给的转换方程4,而是使用了自己的方法,相当于将perm中的3、4进行了# 交换。他认为这样做更加自然,我并没有对此做验证。t = tf.transpose(t, perm=[0, 1, 3, 2, 4, 5]) # S, H, r, H, r, Ct = tf.reshape(t, [-1, H * r, W * r, C])return tdef compute_output_shape(self, input_shape):r = self.scaleH, W, rrC = np.array(input_shape[1:])assert rrC % (r ** 2) == 0return (input_shape[0], H * r, W * r, rrC // (r ** 2))def get_config(self):config = super().get_config()config['scale'] = self.scalereturn configcustom_layers['Conv2DSubPixel'] = Conv2DSubPixel
# 建立 Conv2DSubPixel 层
六、metrics.py
from keras import backend as K
import numpy as npdef psnr(y_true, y_pred):
# 计算峰值信噪比psnr"""Peak signal-to-noise ratio averaged over samples and channels."""mse = K.mean(K.square(y_true - y_pred), axis=(-3, -2))return K.mean(20 * K.log(255 / K.sqrt(mse)) / np.log(10))def ssim(y_true, y_pred):
# 计算结构相似性ssim"""structural similarity measurement system."""## K1, K2 are two constants, much smaller than 1K1 = 0.04K2 = 0.06## mean, std, correlationmu_x = K.mean(y_pred)mu_y = K.mean(y_true)sig_x = K.std(y_pred)sig_y = K.std(y_true)sig_xy = (sig_x * sig_y) ** 0.5## L, number of pixels, C1, C2, two constantsL = 33C1 = (K1 * L) ** 2C2 = (K2 * L) ** 2ssim = (2 * mu_x * mu_y + C1) * (2 * sig_xy * C2) * 1.0 / ((mu_x ** 2 + mu_y ** 2 + C1) * (sig_x ** 2 + sig_y ** 2 + C2))return ssim
七、models.py
from keras.layers import Conv2D
# 2维卷积
# conv2d(x, kernel, strides=(1, 1), border_mode='valid', dim_ordering='th',
# image_shape=None, filter_shape=None)
# kernel:卷积核张量
# strides:步长,长为2的tuple
# border_mode:“same”,“valid”之一的字符串
# dim_ordering:“tf”和“th”之一,维度排列顺序from keras.layers import Conv2DTranspose
# 该层是转置的卷积操作(反卷积)。需要反卷积的情况通常发生在用户想要对一个普通卷积的结果做反方向
# 的变换。例如,将具有该卷积层输出shape的tensor转换为具有该卷积层输入shape的tensor。同时保留与
# 卷积层兼容的连接模式。当使用该层作为第一层时,应提供input_shape参数。例如input_shape =
# (3,128,128)代表128*128的彩色RGB图像。
# keras.layers.convolutional.Conv2DTranspose(filters, kernel_size, strides=(1, 1),
# padding='valid', data_format=None, activation=None, use_bias=True,
# kernel_initializer='glorot_uniform', bias_initializer='zeros',
# kernel_regularizer=None, bias_regularizer=None, activity_regularizer=None,
# kernel_constraint=None, bias_constraint=None)
# filters:卷积核的数目(即输出的维度)
# kernel_size:单个整数或由两个个整数构成的list/tuple,卷积核的宽度和长度。如为单个整数,则表示# 在各个空间维度的相同长度。
# strides:单个整数或由两个整数构成的list/tuple,为卷积的步长。如为单个整数,则表示在各个空间维
# 度的相同步长。任何不为1的strides均与任何不为1的dilation_rate均不兼容
# padding:补0策略,为“valid”, “same” 。“valid”代表只进行有效的卷积,即对边界数据不处
# 理。“same”代表保留边界处的卷积结果,通常会导致输出shape与输入shape相同。
# activation:激活函数,为预定义的激活函数名(参考激活函数),或逐元素(element-wise)的Theano
# 函数。如果不指定该参数,将不会使用任何激活函数(即使用线性激活函数:a(x)=x)
# dilation_rate:单个整数或由两个个整数构成的list/tuple,指定dilated convolution中的膨胀比
# 例。任何不为1的dilation_rate均与任何不为1的strides均不兼容。
# data_format:字符串,“channels_first”或“channels_last”之一,代表图像的通道维的位置。该参数
# 是Keras 1.x中的image_dim_ordering,“channels_last”对应原本的“tf”,“channels_first”对应原
# 本的“th”。以128x128的RGB图像为例,“channels_first”应将数据组织为(3,128,128),
# 而“channels_last”应将数据组织为(128,128,3)。该参数的默认值是~/.keras/keras.json中设置的
# 值,若从未设置过,则为“channels_last”。
# use_bias:布尔值,是否使用偏置项
# kernel_initializer:权值初始化方法,为预定义初始化方法名的字符串,或用于初始化权重的初始化器。
# 参考initializers
# bias_initializer:权值初始化方法,为预定义初始化方法名的字符串,或用于初始化权重的初始化器。参
# 考initializers
# kernel_regularizer:施加在权重上的正则项,为Regularizer对象
# bias_regularizer:施加在偏置向量上的正则项,为Regularizer对象
# activity_regularizer:施加在输出上的正则项,为Regularizer对象
# kernel_constraints:施加在权重上的约束项,为Constraints对象
# bias_constraints:施加在偏置上的约束项,为Constraints对象from keras.layers import InputLayer
# 我没有找到关于InputLayer的具体说明,但是它跟keras的输入张量tensor有关from keras.models import Sequential
# 序列模型
# model = Sequential()
# model.add(Dense(32, input_shape=(500,)))
# model.add(Dense(10, activation='softmax'))
# model.compile(optimizer='rmsprop',
# loss='categorical_crossentropy',
# metrics=['accuracy'])
# optimizer:字符串(预定义优化器名)或优化器对象,参考优化器
# loss:字符串(预定义损失函数名)或目标函数,参考损失函数
# metrics:列表,包含评估模型在训练和测试时的网络性能的指标,典型用法是metrics=['accuracy']
# sample_weight_mode:如果你需要按时间步为样本赋权(2D权矩阵),将该值设为“temporal”。默认
# 为“None”,代表按样本赋权(1D权)。在下面fit函数的解释中有相关的参考内容。
# weighted_metrics: metrics列表,在训练和测试过程中,这些metrics将由sample_weight或
# clss_weight计算并赋权
# target_tensors: 默认情况下,Keras将为模型的目标创建一个占位符,该占位符在训练过程中将被目标数
# 据代替。如果你想使用自己的目标张量(相应的,Keras将不会在训练时期望为这些目标张量载入外部的
# numpy数据),你可以通过该参数手动指定。目标张量可以是一个单独的张量(对应于单输出模型),也可
# 以是一个张量列表,或者一个name->tensor的张量字典。
# kwargs:使用TensorFlow作为后端请忽略该参数,若使用Theano/CNTK作为后端,kwargs的值将会传递给
# K.function。如果使用TensorFlow为后端,这里的值会被传给tf.Session.run
# 模型在使用前必须编译,否则在调用fit或evaluate时会抛出异常import tensorflow as tffrom toolbox.layers import ImageRescale
from toolbox.layers import Conv2DSubPixeldef bicubic(x, scale=3):
# 建立双三次插值模型,通过计算周边的16个点进行插值。x为输入图片,scale为比例model = Sequential()model.add(InputLayer(input_shape=x.shape[-3:]))model.add(ImageRescale(scale, method=tf.image.ResizeMethod.BICUBIC))return modeldef srcnn(x, f=[9, 1, 5], n=[64, 32], scale=3):
# 建立超分辨卷积神经网络模型。f为卷积核大小,n为选取的维度,下同"""Build an SRCNN model.See https://arxiv.org/abs/1501.00092"""assert len(f) == len(n) + 1model = bicubic(x, scale=scale)# 先通过双三次插值放大图像c = x.shape[-1]for ni, fi in zip(n, f):# 将低分辨率图像输入三层卷积神经网络model.add(Conv2D(ni, fi, padding='same',kernel_initializer='he_normal', activation='relu'))model.add(Conv2D(c, f[-1], padding='same',kernel_initializer='he_normal'))return modeldef fsrcnn(x, d=56, s=12, m=4, scale=3):
# 建立FSRCNN模型,是SRCNN模型的改进
# ·它并不是将三次插值后的图像当做输入,而是直接将LR图像丢入到网络中,最后选用deconv进行放大在映射
# Layer进行了改进,先shrink再将其复原
# ·更多的映射layer和更小的kernel
# ·共享其中的mapping layer,如果需要训练不同的upscale model,最后仅需要fine-tuning最后的
# deconvLayer"""Build an FSRCNN model.See https://arxiv.org/abs/1608.00367"""model = Sequential()model.add(InputLayer(input_shape=x.shape[-3:]))c = x.shape[-1]f = [5, 1] + [3] * m + [1]n = [d, s] + [s] * m + [d]for ni, fi in zip(n, f):model.add(Conv2D(ni, fi, padding='same',kernel_initializer='he_normal', activation='relu'))model.add(Conv2DTranspose(c, 9, strides=scale, padding='same',kernel_initializer='he_normal'))return modeldef nsfsrcnn(x, d=56, s=12, m=4, scale=3, pos=1):
# 建立一个反卷积核不同的FSRCNN模型"""Build an FSRCNN model, but change deconv position.See https://arxiv.org/abs/1608.00367"""model = Sequential()model.add(InputLayer(input_shape=x.shape[-3:]))c = x.shape[-1]f1 = [5, 1] + [3] * posn1 = [d, s] + [s] * posf2 = [3] * (m - pos - 1) + [1]n2 = [s] * (m - pos - 1) + [d]f3 = 9n3 = cfor ni, fi in zip(n1, f1):model.add(Conv2D(ni, fi, padding='same',kernel_initializer='he_normal', activation='relu'))model.add(Conv2DTranspose(s, 3, strides=scale, padding='same',kernel_initializer='he_normal'))for ni, fi in zip(n2, f2):model.add(Conv2D(ni, fi, padding='same',kernel_initializer='he_normal', activation='relu'))model.add(Conv2D(n3, f3, padding='same',kernel_initializer='he_normal'))return modeldef espcn(x, f=[5, 3, 3], n=[64, 32], scale=3):
# 建立ESPCN模型,直接在低分辨率图像尺寸上提取特征,计算得到高分辨率图像"""Build an ESPCN model.See https://arxiv.org/abs/1609.05158"""assert len(f) == len(n) + 1model = Sequential()model.add(InputLayer(input_shape=x.shape[1:]))c = x.shape[-1]for ni, fi in zip(n, f):model.add(Conv2D(ni, fi, padding='same',kernel_initializer='he_normal', activation='tanh'))model.add(Conv2D(c * scale ** 2, f[-1], padding='same',kernel_initializer='he_normal'))model.add(Conv2DSubPixel(scale))return modeldef get_model(name):return globals()[name]
八、paths.py
from pathlib import Pathrepo_dir = Path(__file__).parents[1]
data_dir = repo_dir / 'data'
九、run_all.py
from pathlib import Path
from subprocess import run
# subprocess 是Python 2.4中新增的一个模块,它允许你生成新的进程,连接到它们的 input/output/error
# 管道,并获取它们的返回(状态)码。这个模块的目的在于替换几个旧的模块和方法。
# run() 函数是 Python 3.5 中新增的函数。执行指定的命令,等待命令执行完成后返回一个包含执行结果的
# CompletedProcess 类的实例。for param_file in Path('.').glob('*.json'):
# 获取当前路径下所有后缀名为json的文件print(f'Run {param_file.stem}')# 打印文件名称run(['python', 'run.py', str(param_file)])# 以python执行run.py,执行参数为名为param_file的文件
十、参考目录
因为在全文都有引用,故不在每个地方标注引用。
[1] Python 命令行工具 argparse 模块使用详解
[2] Python——functools
[3] Python JSON
[4] Keras:基于Python的深度学习库
[5] 优化器optimizers
[6] [pathlib]内置pathlib库的常用属性和方法
[7] [Keras] SGD 随机梯度下降优化器参数设置
[8] NumPy 教程
[9] Numpy中stack(),hstack(),vstack()函数详解
[10] Python图像处理库PIL中图像格式转换(一)
[11] Python yield 使用浅析
[12] Python第三方库matplotlib(2D绘图库)入门与进阶
[13] 【Python学习笔记】Pandas库之DataFrame
[14] TensorFlow中文社区
[15] 介绍几种常用的插值方法以及代码-双三次插值
[16] 超分辨率重建之SRCNN
[17] 图像超分辨率重建之SRCNN
[18] 【超分辨率】FSRCNN--Accelerating the Super-Resolution Convolutional Neural Network
[19] 深度学习超分辨率重建(三): TensorFlow—— ESPCN
[20] 论文笔记 —— SRCNN
[21] Python之系统交互(subprocess)
单图像超分辨率重建示例代码解析相关推荐
- 单图像超分辨率重建示例
代码的解析已经给出,现在补上:单图像超分辨率重建示例代码解析 目录 一.简介 二.前期准备 三.运行程序 四.参考目录 一.简介 图像超分辨率重建技术就是利用一组低质量.低分辨率图像(或运动序列)来产 ...
- 一文掌握图像超分辨率重建(算法原理、Pytorch实现)——含完整代码和数据
目录 一. 图像超分辨率重建概述 1. 概念 2. 应用领域 3. 研究进展 3.1 传统超分辨率重建算法 3.2 基于深度学习的超分辨率重建算法 二. SRResNet算法原理和Pytorch实 ...
- 超分辨率重建——(一)何为超分和分类
图像超分辨重建 图像超分辨率(SR)是计算机视觉中提高图像和视频分辨率的一类重要技术.图像超分辨率重建( Super-resolution Reconstruction,SR) 是由一张或多张低分辨率 ...
- 超分辨率重建生成低分辨率图像,生成降质图像公认方法代码
目录 1背景 2.BI 3.BD 4.DN 5总结 1背景 超分辨率重建中经典的生成降质图像通常使用MATLAB实现的,通常有四种方法: 类型 说明 BI bicubic-down BD blur-d ...
- 图像超分辨率重建算法,让模糊图像变清晰(附数据和代码)
一. 图像超分辨率重建概述 1. 概念 图像分辨率是一组用于评估图像中蕴含细节信息丰富程度的性能参数,包括时间分辨率.空间分辨率及色阶分辨率等,体现了成像系统实际所能反映物体细节信息的能力.相较于低 ...
- 【超分辨率实验】Matlab-使用深度学习的单图像超分辨率(Single Image Super-Resolution Using Deep Learning)
[超分辨率实验]Matlab-使用深度学习的单图像超分辨率(Single Image Super-Resolution Using Deep Learning) 此示例演示如何训练非常深的超分辨率 V ...
- 【图像超分辨率重建】——SAN论文精读笔记
2019-Second-order Attention Network for Single Image Super-Resolution(SAN) 基本信息 作者: Tao Dai, Jianrui ...
- 经典论文复现 | 基于深度学习的图像超分辨率重建
过去几年发表于各大 AI 顶会论文提出的 400 多种算法中,公开算法代码的仅占 6%,其中三分之一的论文作者分享了测试数据,约 54% 的分享包含"伪代码".这是今年 AAAI ...
- 【图像超分辨率重建】——HAN论文精读笔记
2020-Single Image Super-Resolution via a Holistic Attention Network(HAN) 基本信息 作者: Ben Niu; Weilei We ...
最新文章
- 一个爬虫的故事:这是人干的事儿?
- google custom search api 申请注册 cx key
- python下载大文件-使用python通过FTP下载大文件
- html 物流状态,css+html如何实现物流进度样式(代码示例)
- 将自己可能存放库文件的路径都加入到/etc/ld.so.conf中是明智的选择
- 数据类型(整型、浮点数、字符串、时间和日期)、切分(水平、垂直)
- 【报错笔记】在做图片上传时上传图片后可以跳转到上传成功界面,也没有报错,数据库中也传入了值,可是eclipse中webapp下怎样都无法生存目录。
- Objective-C 2.0 with Cocoa Foundation--- 5,Class类型,选择器Selector以及函数指针
- 超越 EfficientNet与MobileNetV3,NeurIPS 2020 微软NAS方向最新研究
- 马斯克光顾北京包子铺被偶遇 本人盖章:好吃!
- HTML设置不生效的原因,CSS中hover出现不生效的几个原因 ?
- go语言如何实现继承
- Carryon的字符串
- Windows 编程[11] - WM_SIZE 消息
- mysql报1840_mysql 帮助手册 以及 warning: World-writable config file 以及 ERROR 1840 (HY000) at line 24:...
- 引物设计软件_你的引物设计进行得如何了?这份PCR引物设计及软件使用技巧供你参考...
- 京东价格监控软件开发技术探讨七:如何获取京东商品评价信息
- 微分方程的基本概念(通解、特解,线素场)
- filters过滤器的简单使用
- java界面添加mid音乐,使用jfugue来演奏mid音乐
热门文章
- 关于7z各种不能用的操作解决办法 7za 7z x Error: Can not open file as archive there is no such archive
- 半桥llc 增益 matlab程序,【我已收藏】很完整的LLC谐振半桥电路分析与计算
- html实现艺术字体颜色,Word中将标题设置为艺术字,式样为艺术字库中的填充-红色,强调文字颜色2,粗糙棱台,...
- 【Three.js】简单地3D工具-Three.js建模入门基础教程
- 2017年全国研究生电子设计大赛上海赛区感触
- 如何设置文档的默认打开方式
- 【Linux 系统启动优化测试工具的使用——grabserial 】
- bmob php支付,GitHub - bmob/bmob-php-sdk: PHP SDK相关源码
- 修复win10 服务器失败怎么办,fatal error怎么解决win10 修复Win10系统问题
- 轮播图左右按钮会被选中的问题