第一次来请先看这篇文章:【超分辨率(Super-Resolution)】关于【超分辨率重建】专栏的相关说明,包含专栏简介、专栏亮点、适配人群、相关说明、阅读顺序、超分理解、实现流程、研究方向、论文代码数据集汇总等)

文章目录

前言1. 准备数据集和数据预处理1.1 数据集选择1.2 数据预处理1.3 评估指标PSNR和SSIM1.3.1 PSNR1.3.2 SSIM

2. 定义网络结构3. 设置参数并训练模型3.1 参数设置3.2 模型训练

4. 测试训练好的模型5. 用训练好的SRCNN模型超分自己的图像数据6. 其他补充6.1 特征图可视化6.2 训练过程中PSNR折线图可视化6.3 以像素计算自己的图像输入输出前后的PSNR和SSIM

前言

论文地址:Image Super-Resolution Using Deep Convolutional Networks

论文精读:【图像超分】论文精读:Image Super-Resolution Using Deep Convolutional Networks(SRCNN)

请配合上述论文精读文章使用,效果更佳!

代码地址:图像超分辨率SRCNN和FSRCNN复现代码,除基本的网络实现外,还有特征图可视化,PSNR曲线图可视化,测试自己的图像数据等

不想理解原理,希望直接跑通然后应用到自己的图像数据的同学,请直接下载上面的代码,有训练好的模型,直接用即可。具体使用方式见代码中的README!有问题来本文评论区留言!

深度学习的模型训练一般遵循以下步骤:

准备数据集,以及数据预处理搭建网络模型设置参数并训练测试训练好的模型用训练好的模型测试自己的数据

下面让我们根据通用流程,一步一步复现SRCNN吧!目标是:输入大小为 h×w 的图像 X,输出为一个 sh×sw 的图像 Y,s 为放大倍数。论文中s取值为2,3,4。

注:放大倍数为1,则图像分辨率不变,只是图像变清晰!如果你只是想将模糊图像通过SRCNN超分后变清晰,那么请将该参数设置为1。

硬件环境:windows11+RTX 2060(比这个高肯定没问题,我这个配置本机跑500个epoch一点问题没有,一会就跑完。但超分自己的图像时,如果图像很大,可能有内存溢出的错误) 运行环境:jupyter notebook/pycharm(前者好处是分代码段运行,测试方法,适合学习用;后者适合跑完整项目用) pytorch环境:torch1.9.1+cuda11.1(其他版本没测试过,应该问题不大)

1. 准备数据集和数据预处理

1.1 数据集选择

简单回顾论文中出现过的数据集:

训练集(Train dataset):91 images、ILSVRC 2013 ImageNet测试集(Test dataset):Set5、Set14、BSD200

由于91 images的数据处理有些繁琐,对于新手不太友好,而ImageNet数据集过大,可能不利于硬件设备不好的同学,所以本文使用折中的BSD200数据集,包含200张图像训练集和200张图像测试集。

1.2 数据预处理

根据论文中第4节实验部分开头所述,RGB 颜色模式色调、色度、饱和度三者混在一起难以分开,超分只用于YCbCr的Y通道,即亮度分量。因此,需要将图像数据由RGB模型转换成 YCbCr 颜色模式,Y 是指亮度分量,Cb 表示 RGB 输入信号蓝色部分与 RGB 信号亮度值之间的差异,Cr 表示 RGB 输入信号红色部分与 RGB 信号亮度值之间的差异。

此外,通过观察BSD200数据集中的图像可知,数据集中的图像宽高并不一致,每张图像的大小也千差万别。而SRCNN要求输入图像的长宽一致,所以在训练之前,需要将图像裁剪成宽高一致的图像。采用的策略是按图像中心外扩,resize成300×300的图像。

数据预处理的代码如下:

# 判断某个文件是否是图像

# enswith判断是否以指定的.png,.jpg,.jpeg结尾的字符串

# 可以根据情况扩充图像类型,加入.bmp、.tif等

def is_image_file(filename):

return any(filename.endswith(extension) for extension in [".png", ".jpg", ".jpeg"])

# 读取图像转为YCbCr模式,得到Y通道

def load_img(filepath):

img = Image.open(filepath).convert('YCbCr')

y, _, _ = img.split()

return y

# 裁剪大小,宽高一致为300

# 如果想训练自己的数据集,请根据情况修改裁剪大小

CROP_SIZE = 300

# 封装数据集,适配后面的torch.utils.data.DataLoader中的dataset,定义成类似形式

# 类参数为图像文件夹路径和放大倍数

# __len__(self) 定义当被len()函数调用时的行为(返回容器中元素的个数)

#__getitem__(self) 定义获取容器中指定元素的行为,相当于self[key],即允许类对象可以有索引操作。

#__iter__(self) 定义当迭代容器中的元素的行为

# 返回输入图像和标签,传入DataLoader的dataset参数

class 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) # 处理放大倍数,防止用户瞎设置,本例只能设置为2,3,4,大小不变

# 数据集变换

# 还有一些其他的变换操作,如归一化等,遇到一个积累一个

self.input_transform = transforms.Compose([transforms.CenterCrop(crop_size), # 从图片中心裁剪成300*300

transforms.Resize(

crop_size // zoom_factor), # Resize, 输入应该是缩放倍数后的图像,因为先缩小后放大

transforms.Resize(

crop_size, interpolation=Image.BICUBIC), # 双三次插值

transforms.ToTensor()]) # 图像转成tensor

# label标签,超分不是分类问题,定义成一样的就行

self.target_transform = transforms.Compose(

[transforms.CenterCrop(crop_size), transforms.ToTensor()])

def __getitem__(self, index):

input = load_img(self.image_filenames[index]) # 输入是图像的Y通道,即亮度通道

target = input.copy()

input = self.input_transform(input)

target = self.target_transform(target)

return input, target

def __len__(self):

return len(self.image_filenames) # 图像个数

至此,数据预处理完成,训练之前就可以传入DataLoader的dataset参数中。

1.3 评估指标PSNR和SSIM

训练过程中,每个epoch会得到一个SRCNN的模型。那么如何评价该模型的性能呢?需要两个评价指标:PSNR和SSIM。由于训练时需要用到,所以需要提前将计算PSNR和SSIM的代码准备好。

1.3.1 PSNR

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

M

S

E

=

1

H

×

W

i

=

1

H

j

=

1

W

(

X

(

i

,

j

)

Y

(

i

,

j

)

)

2

M S E=\frac{1}{H \times W} \sum_{i=1}^{H} \sum_{j=1}^{W}(X(i, j)-Y(i, j))^{2}

MSE=H×W1​i=1∑H​j=1∑W​(X(i,j)−Y(i,j))2

P

S

N

R

=

10

log

10

(

(

2

n

1

)

2

M

S

E

)

P S N R=10 \log _{10}\left(\frac{\left(2^{n}-1\right)^{2}}{M S E}\right)

PSNR=10log10​(MSE(2n−1)2​) 其中,MSE是均方误差。两个图像对应像素位置的值相减再平方和最后取平均。n是每像素的比特数,一般取256。PSNR的单位是dB,该值越大表示图像的失真越小。

通常认为,PSNR在38以上的时候,人眼就无法区分两幅图片了。

PSNR的计算代码如下:

def psnr(loss):

return 10 * log10(1 / loss.item())

损失函数使用的就是MSE,所以这么定义。log10是python的math库中的包。

做实验的时候,PSNR一般保留两位小数。

1.3.2 SSIM

SSIM(Structural Similarity,结构相似性)由三个对比模块组成:亮度、对比度、结构。 (1) 亮度对比函数 图像平均灰度:

μ

X

=

1

H

×

M

i

=

1

H

j

=

1

M

X

(

i

,

j

)

\mu_{X}=\frac{1}{H \times M} \sum_{i=1}^{H} \sum_{j=1}^{M} X(i, j)

μX​=H×M1​i=1∑H​j=1∑M​X(i,j) 亮度对比函数:

l

(

x

,

y

)

=

2

μ

x

μ

y

+

C

1

μ

x

2

+

μ

y

2

+

C

1

l(x, y)=\frac{2 \mu_{x} \mu_{y}+C_{1}}{\mu_{x}^{2}+\mu_{y}^{2}+C_{1}}

l(x,y)=μx2​+μy2​+C1​2μx​μy​+C1​​

(2) 对比度对比函数 图像的标准差:

σ

X

=

(

1

H

+

W

1

i

=

1

H

j

=

1

M

(

X

(

i

,

j

)

μ

X

)

2

)

1

2

\sigma_{X}=\left(\frac{1}{H+W-1} \sum_{i=1}^{H} \sum_{j=1}^{M}\left(X(i, j)-\mu_{X}\right)^{2}\right)^{\frac{1}{2}}

σX​=(H+W−11​i=1∑H​j=1∑M​(X(i,j)−μX​)2)21​ 对比度对比函数:

c

(

x

,

y

)

=

2

σ

x

σ

y

+

C

2

σ

x

2

+

σ

y

2

+

C

2

c(x, y)=\frac{2 \sigma_{x} \sigma_{y}+C_{2}}{\sigma_{x}^{2}+\sigma_{y}^{2}+C_{2}}

c(x,y)=σx2​+σy2​+C2​2σx​σy​+C2​​

(3) 结构对比函数

s

(

x

,

y

)

=

σ

x

y

+

C

3

σ

x

σ

y

+

C

3

s(x, y)=\frac{\sigma_{x y}+C_{3}}{\sigma_{x} \sigma_{y}+C_{3}}

s(x,y)=σx​σy​+C3​σxy​+C3​​

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

SSIM

(

x

,

y

)

=

f

(

l

(

x

,

y

)

,

c

(

x

,

y

)

,

s

(

x

,

y

)

)

=

[

l

(

x

,

y

)

]

α

[

c

(

x

,

y

)

]

β

[

s

(

x

,

y

)

]

γ

\begin{aligned} \operatorname{SSIM}(x, y) & =f(l(x, y), c(x, y), s(x, y)) \\ & =[l(x, y)]^{\alpha}[c(x, y)]^{\beta}[s(x, y)]^{\gamma} \end{aligned}

SSIM(x,y)​=f(l(x,y),c(x,y),s(x,y))=[l(x,y)]α[c(x,y)]β[s(x,y)]γ​

其中,훼, 훽, 훾 > 0,用来调整这三个模块的重要性。 SSIM取值为[0, 1], 值越大说明图像失真越小,两幅图像越相似。

做实验的时候,SSIM一般保留四位小数。

由于python中没有封装好的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 = 255

else:

max_val = 1

if torch.min(img1) < -0.5:

min_val = -1

else:

min_val = 0

L = max_val - min_val

else:

L = val_range

padd = 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 * mu2

# 方差

sigma1_sq = F.conv2d(img1 * img1, window, padding=padd,

groups=channel) - mu1_sq

sigma2_sq = F.conv2d(img2 * img2, window, padding=padd,

groups=channel) - mu2_sq

sigma12 = F.conv2d(img1 * img2, window, padding=padd,

groups=channel) - mu1_mu2

# SSIM默认常数

C1 = (0.01 * L) ** 2

C2 = (0.03 * L) ** 2

v1 = 2.0 * sigma12 + C2

v2 = sigma1_sq + sigma2_sq + C2

cs = torch.mean(v1 / v2) # contrast sensitivity

ssim_map = ((2 * mu1_mu2 + C1) * v1) / ((mu1_sq + mu2_sq + C1) * v2)

# 平均SSIM

if size_average:

ret = ssim_map.mean()

else:

ret = ssim_map.mean(1).mean(1).mean(1)

if full:

return ret, cs

return ret

# 封装成类

class SSIM(torch.nn.Module):

def __init__(self, window_size=11, size_average=True, val_range=None):

super(SSIM, self).__init__()

self.window_size = window_size

self.size_average = size_average

self.val_range = val_range

# Assume 1 channel for SSIM

self.channel = 1

self.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.window

else:

window = create_window(self.window_size, channel).to(

img1.device).type(img1.dtype)

self.window = window

self.channel = channel

return ssim(img1, img2, window=window, window_size=self.window_size, size_average=self.size_average)

由于SSIM和PSNR都需要在训练时用于评估模型的好坏,而不是简单的给两张图像根据像素对应来计算。所以,我们将像素计算改为卷积计算,可以评估两个tensor类型的图像。其中,window_size=11、C1和C2的取值都是SSIM论文的matlab源代码的默认参数。

至此,准备工作全部完成。

2. 定义网络结构

简单回顾SRCNN模型的三层结构:

输入:处理后的低分辨率图像第一层:特征提取,卷积核大小9×9第二层:非线性映射,卷积核大小1×1,第一层到第二层卷积核数量由64变为32第三层:图像重构,卷积核大小5×5输出:放大倍数后的高分辨图像

层间结构:Conv(1,64,9) —— ReLU —— Conv(64,32,1) —— ReLU —— Conv(32,1,5)

细节:无padding(有padding影响不大,更容易适应图像大小)

SRCNN模型实现如下:

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=9, padding=9//2)

self.conv2 = nn.Conv2d(64, 32, kernel_size=1)

self.conv3 = nn.Conv2d(32, 1, kernel_size=5, padding=5//2)

self.pixel_shuffle = nn.PixelShuffle(upscale_factor)

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.pixel_shuffle(x)

return x

3. 设置参数并训练模型

3.1 参数设置

# 放大倍数

zoom_factor = 3

nb_epochs = 500

device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")

torch.manual_seed(0)

torch.cuda.manual_seed(0)

BATCH_SIZE = 4

NUM_WORKERS = 0

trainset = DatasetFromFolder(r"./data/images/train", zoom_factor)

valset = DatasetFromFolder(r"./data/images/train", zoom_factor)

trainloader = DataLoader(dataset=trainset, batch_size=BATCH_SIZE, shuffle=True, num_workers=NUM_WORKERS)

valloader = DataLoader(dataset=valset, batch_size=BATCH_SIZE,

shuffle=False, num_workers=NUM_WORKERS)

以放大倍数3为例,训练500轮,使用GPU训练。由于我的设备比较拉,所以BATCH_SIZE = 4,NUM_WORKERS = 0。如果你的设备比较好,可以修改它们。

根据【数据预处理】定义的DatasetFromFolder函数得到训练集和验证集。数据集放在与代码文件同目录下:./data/images/train。最后,使用DataLoader得到训练前所需的训练集和验证集。

如果要训练自己的数据集,将训练图像放在train文件夹下即可。

3.2 模型训练

SRCNN训练部分代码如下:

model = SRCNN(1).to(device) # 模型,net.to(device)保证用GPU训练,本地训练可以不加,服务器上一定要加

criterion = nn.MSELoss() # 损失函数为MSE

optimizer = optim.Adam(

[

{"params": model.conv1.parameters(), "lr": 0.0001},

{"params": model.conv2.parameters(), "lr": 0.0001},

{"params": model.conv3.parameters(), "lr": 0.00001},

]

) # Adam优化器,设定三个层学习率为论文中的值,最后一层更小

# 一般的优化器传参就是optim.Adam(lr=0.0001), 因为最后一层学习率不同,所以用字典形式传参。

best_psnr = 0.0

for epoch in range(nb_epochs):

# 训练

epoch_loss = 0

for iteration, batch in enumerate(trainloader): # batchsize为4,则trainloader的长度是50,遍历它

input, target = batch[0].to(device), batch[1].to(device) # 每个trainloader是由图像张量和标签构成

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)}") # 训练完计算损失

# 验证

sum_psnr = 0.0

sum_ssim = 0.0

with 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 += pr

sum_ssim += sm

print(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_psnr # 用psnr衡量模型,保存最好的

torch.save(model, r"best_model_SRCNN_3.pth")

训练500个epoch:

图像张量在传入神经网络之前的格式为:(batch_size, channel, h, w)。

4. 测试训练好的模型

用测试集测试训练好的模型代码如下:

BATCH_SIZE = 4

model_path = "best_model_SRCNN_3.pth"

testset = DatasetFromFolder(r"./data/images/test", zoom_factor) # 用BSD200中的测试图像测试模型

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 += pr

sum_ssim += sm

print(f"Test Average PSNR: {sum_psnr / len(testloader)} dB")

print(f"Test Average SSIM: {sum_ssim / len(testloader)} ")

取全部测试集计算的总平均PSNR和SSIM,结果为:

5. 用训练好的SRCNN模型超分自己的图像数据

代码如下:

# 参数设置

zoom_factor = 3 # 放大倍数

model = "best_model_SRCNN_3.pth" # 模型路径

image = "./SEM_image_test/6.jpg" # 自己的图像路径

device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")

# 读取图片

img = Image.open(image).convert('YCbCr') # PIL类型,转成YCbCr

img = img.resize((int(img.size[0] * zoom_factor), int(img.size[1] * zoom_factor)), Image.BICUBIC) # 双三次插值放大

y, cb, cr = img.split() # 划分Y,cb,cr三个通道

img_to_tensor = transforms.ToTensor() # 获得一个ToTensor对象

# view中-1的含义是该维度不明,让其自动计算, input是由torch.Tensor([1,h,w])变成torch.Tensor([1,1,h,w])

# 图像Tensor格式:(batch_size, channel, h, w)

input = img_to_tensor(y).view(1, -1, y.size[1], y.size[0]).to(device) # 将y通道变换成网络输入的tensor格式

# 输出图片

model = torch.load(model).to(device) # 载入模型

out = model(input).cpu() # 模型输出

out_img_y = out[0].detach().numpy() # 返回新的三维张量并转成numpy

out_img_y *= 255.0

out_img_y = out_img_y.clip(0, 255) # 取0-255内的值

out_img_y = Image.fromarray(np.uint8(out_img_y[0]), mode='L') # numpy转成PIL

out_img = Image.merge('YCbCr', [out_img_y, cb, cr]).convert('RGB') # 合并三个通道变成RGB格式

# 绘图显示,上面的操作都是为了plt显示图像

fig, ax = plt.subplots(1, 2, figsize=(22, 10))

ax[0].imshow(img)

ax[0].set_title("原图")

ax[1].imshow(out_img)

ax[1].set_title("SRCNN恢复结果")

# 改dpi大小得到保存的图像清晰度,越大图片文件越大,质量越高;有的办法是保存成pdf,pdf放大不失真

plt.savefig(r"./SEM_image_test/8_result.jpg",dpi=1000,bbox_inches = 'tight')

plt.show()

显示结果为: 可以看到图像清晰了不少。

6. 其他补充

6.1 特征图可视化

本小节复现论文中的图6,代码如下:

import cv2

model = SRCNN(1) # 模型

image_path = "./SEM_image_test/butterfly_GT.jpg" # 输入

gray_image = cv2.imread(image_path, cv2.IMREAD_GRAYSCALE)

out_paths = ['./SEM_image_test/conv1_feature_maps.jpg', './SEM_image_test/conv2_feature_maps.jpg','./SEM_image_test/conv3_feature_maps.jpg' ]

gray_image_tensor = transforms.ToTensor()(gray_image).unsqueeze(0)

with torch.no_grad():

conv1_output = model.conv1(gray_image_tensor) # 1,64,256,256 torch.Tensor

conv1_feature = conv1_output[0].detach().numpy() # 64,256,256 numpy

conv1_feature_maps = np.hstack([conv1_feature[0],conv1_feature[1],conv1_feature[2],conv1_feature[3]])

cv2.imshow('Conv1 Output', conv1_feature_maps)

cv2.imwrite(out_paths[0], conv1_feature_maps * 255)

with torch.no_grad():

conv2_output = model.conv2(conv1_output) # 64,32

conv2_feature = conv2_output[0].detach().numpy()

conv2_feature_maps = np.hstack([conv2_feature[0],conv2_feature[1],conv2_feature[2],conv2_feature[3]])

cv2.imshow('Conv2 Output', conv2_feature_maps)

cv2.imwrite(out_paths[1], conv2_feature_maps * 255)

cv2.waitKey(0)

cv2.destroyAllWindows()

以灰度输入,注意保证图像经过每一层的Tensor大小正确,保存前四个特征图,观察结果。

第一层的特征图:1,64,256,256

第二层特征图:1,32,256,256

横向来看,从64个特征图中可视化前四个。纵向来看,经过第一层结构不同,第二层强度不同。

6.2 训练过程中PSNR折线图可视化

用一个列表存储训练过程中的平均PSNR:

each_psnr = []

each_psnr.append(avg_psnr) # 保存一轮的psnr,画图用

横轴为epoch,训练138轮停止。epoch和psnr的关系为: 代码如下:

epochs = range(0,138)

# plot画图,设置颜色和图例,legend设置图例样式,右下,字体大小

plt.plot(epochs,each_psnr,color='red',label='SRCNN(trained on BSD200)')

plt.legend(loc = 'lower right',prop={'family' : 'Times New Roman', 'size' : 12})

# 横纵轴文字内容以及字体样式

plt.xlabel("epoch",fontproperties='Times New Roman',size = 12)

plt.ylabel("Average test PSNR (dB)",fontproperties='Times New Roman',size=12)

# 横纵轴刻度以及字体样式

plt.xlim(0,138)

plt.ylim(23,26)

plt.yticks(fontproperties='Times New Roman', size=12)

plt.xticks(fontproperties='Times New Roman', size=12)

# 网格样式

plt.grid(ls = '--')

plt.show()

6.3 以像素计算自己的图像输入输出前后的PSNR和SSIM

代码如下:

import cv2

def psnr1(original, compressed):

mse = np.mean((original - compressed) ** 2)

if mse == 0:

return 100 # PSNR 为无穷大

max_pixel = 255.0

psnr = 10 * np.log10((max_pixel ** 2) / mse)

return psnr

from skimage.metrics import structural_similarity as ssim

def ssim1(original, compressed):

grayA = cv2.cvtColor(original, cv2.COLOR_BGR2GRAY)

grayB = cv2.cvtColor(compressed, cv2.COLOR_BGR2GRAY)

score, _ = ssim(grayA, grayB, full=True)

return score

out_img = np.array(out_img)

img = np.array(img)

print("PSNR:", psnr1(img,out_img))

print("SSIM:", ssim1(img,out_img))

MSE按超分前后两张图像的每个像素计算,进而计算PSNR。SSIM使用skimage库中的结构相似性来计算。将前面的显微图像传入,得到的值为:

至此,SRCNN复现完成。网络还是有些问题,我们后续修改。重点在于深度学习流程的学习,以及快速入门。

如果本文对你有所帮助,点个赞吧!

推荐阅读

评论可见,请评论后查看内容,谢谢!!!
 您阅读本篇文章共花了: