SRCNN 图像超分辨率重建(tf2)

人工智能92

文章目录

前言

把由放大缩小的引起的导致分辨率低的图像,转换成为 分辨率高的图像。更加关注的是重构图片过程中,填充新的像素。SRCNN 呢也是将深度学习用于图像重建的鼻祖,网络结构非常简单,于是我决定来复现一下它。
代码链接:https://github.com/jiantenggei/SRCNN-Keras (包含所有资源)

一、SRCNN

SRCNN 图像超分辨率重建(tf2)
SRCNN 的网络结构特别简单,首先将一张低分辨率的图像作为输入,通过两个卷积后,还原成为高质量的图片。网络卷积运算时,保持特征图的大小和重构图片大小一致。且网络中没有线性连接。也就只有三层卷积~。

; 二、SRCNN 实现

首先在这里简述一下SRCNN 网络的训练和测试流程:
1.先将图片缩小后,再放大,制作不清晰的图片作为训练数据
2.将未处理过的原图片作为训练时的标签。
3.将图片和标签放到网络中训练。
4.测试模型时,将一张不清晰的图片输入到训练好的网络,生成的图片与清晰的原图片计算峰值信噪比。

1.模型的搭建

代码如下:

from keras.models import Sequential, model_from_json
from keras.layers.convolutional import Conv2D
from keras.layers.core import Activation

def built_model(input_shape=(33, 33, 1)):
    model = Sequential()
    model.add(Conv2D(filters=64, kernel_size=9,
                    padding='same', input_shape=input_shape))
    model.add(Activation('relu'))
    model.add(Conv2D(32, 1, padding='same'))
    model.add(Activation('relu'))
    model.add(Conv2D(input_shape[2], 5, padding='same'))
    return model

if __name__ == '__main__':
    model = built_model()
    model.summary()

最后一层把图片从多通道特征图,还原成和输入通道一致。

2.生成训练数据

1.先将图片缩小后,再放大,制作不清晰的图片作为训练数据
2.将未处理过的原图片作为训练时的标签。
代码如下:

def load_train(image_size=33, stride=33, scale=3,dirname=r'dataset\train'):
    dir_list = os.listdir(dirname)
    images = [cv2.cvtColor(cv2.imread(os.path.join(dirname,img)),cv2.COLOR_BGR2GRAY) for img in dir_list]

    images = -np.remainder(img.shape[0],scale),0:img.shape[1]-np.remainder(img.shape[1],scale)] for img in images]

    trains = images.copy()
    labels = images.copy()

    trains = [cv2.resize(img, None, fx=1/scale, fy=1/scale, interpolation=cv2.INTER_CUBIC) for img in trains]
    trains = [cv2.resize(img, None, fx=scale/1, fy=scale/1, interpolation=cv2.INTER_CUBIC) for img in trains]

    sub_trains = []
    sub_labels = []

    for train, label in zip(trains, labels):
        v, h = train.shape
        print(train.shape)
        for x in range(0,v-image_size+1,stride):
            for y in range(0,h-image_size+1,stride):
                sub_train = train[x:x+image_size,y:y+image_size]
                sub_label = label[x:x+image_size,y:y+image_size]
                sub_train = sub_train.reshape(image_size,image_size,1)
                sub_label = sub_label.reshape(image_size,image_size,1)
                sub_trains.append(sub_train)
                sub_labels.append(sub_label)

    sub_trains = np.array(sub_trains)
    sub_labels = np.array(sub_labels)
    return sub_trains, sub_labels

def load_test(scale=3,dirname=r'dataset\test'):

    dir_list = os.listdir(dirname)
    images = [cv2.cvtColor(cv2.imread(os.path.join(dirname,img)),cv2.COLOR_BGR2GRAY) for img in dir_list]
    images = -np.remainder(img.shape[0],scale),0:img.shape[1]-np.remainder(img.shape[1],scale)] for img in images]

    tests = images.copy()
    labels = images.copy()

    pre_tests = [cv2.resize(img, None, fx=1/scale, fy=1/scale, interpolation=cv2.INTER_CUBIC) for img in tests]
    tests = [cv2.resize(img, None, fx=scale/1, fy=scale/1, interpolation=cv2.INTER_CUBIC) for img in pre_tests]

    pre_tests = ,img.shape[1],1) for img in pre_tests]
    tests = ,img.shape[1],1) for img in tests]
    labels = ,img.shape[1],1) for img in labels]

    return pre_tests, tests, labels

注意:代码中采样过程(三个for 训练处) 是将一张图片,截取一个个小的区域,这样一张图片就可以产生成多个数据,弥补训练样本不足的问题。

3.训练过程:

代码如下:

from tensorflow.python.keras.saving.model_config import model_from_config
from model import built_model
from utils import load_train
from keras.optimizers import Adam

def train():

    input_shape = (33, 33, 1)
    stride = 14
    batch_size = 64
    epochs=100
    learning_rate=0.001

    srcnn_model = built_model(input_shape=input_shape)
    srcnn_model.load_weights(r'model\srcnn_weight.hdf5')
    srcnn_model.summary()

    X_train, Y_train = load_train(image_size=input_shape[0], stride=stride)
    print(X_train.shape, Y_train.shape)
    optimizer = Adam(lr=learning_rate)
    srcnn_model.compile(optimizer=optimizer, loss='mean_squared_error', metrics=['accuracy'])
    srcnn_model.fit(X_train,Y_train,epochs=epochs,batch_size=batch_size)
    srcnn_model.save(r'model/srcnn.h5')

if __name__ == '__main__':
    train()

这里计算损失用均方差,因为输入和输出都是大小一致的图片~ 只是分辨率不同。

4.测试过程

代码如下:

from model import built_model
import os
from utils import load_test,psnr
import cv2
def test():
    input_shape = (None, None, 1)
    scale = 3
    srcnn_model = built_model(input_shape=input_shape)
    srcnn_model.load_weights(r'model\srcnn_weight.hdf5')

    X_pre_test, X_test, Y_test = load_test(scale=scale)

    predicted_list = []

    for img in X_test:
        img = img.reshape(1,img.shape[0],img.shape[1],1)
        predicted=srcnn_model.predict(img)
        predicted_list.append(predicted.reshape(predicted.shape[1],predicted.shape[2],1))
    n_img = len(predicted_list)
    dirname = './result'
    for i in range(n_img):
        imgname = 'image{:02}'.format(i)
        cv2.imwrite(os.path.join(dirname,imgname+'_original.bmp'), X_pre_test[i])
        cv2.imwrite(os.path.join(dirname,imgname+'_input.bmp'), X_test[i])
        cv2.imwrite(os.path.join(dirname,imgname+'_answer.bmp'), Y_test[i])
        cv2.imwrite(os.path.join(dirname,imgname+'_predicted.bmp'), predicted_list[i])

        answer = psnr(Y_test[i],predicted_list[i])
        print(imgname+"_psnr:",answer)

if __name__ == '__main__':
    test()

X_test 存放的是不清晰的图片,用于和预测结果计算计算峰值信噪比。

总结

各种网络各种功能,喂数据的方式不同,计算损失的方式不同罢了~

Original: https://blog.csdn.net/qq_38676487/article/details/120595718
Author: 不想写代码
Title: SRCNN 图像超分辨率重建(tf2)



相关阅读

Title: TensorFlow2-基础(五):Tensor的维度变换【改变:reshape(view级别)、transpose(content级别)】【增加(expand_dims)、删除(squeeze)】

tensorFlow维度变换可分为两个级别,一个是view级,一个是content级。

  • view级维度变换:不改变数据的存储关系,比如[3,28,28,3]变换维度为[3,784,3]之后,数据本身并没有改变,属于哪一行哪一列的元素还是可以确定,可以原模原样的恢复为[3,28,28,3]。
  • content级维度变换:会改变数据的存储关系。content有"目录"的意思,即索引关系,通过[3,28,28,3]这样的索引号能够找到一个元素。当content被改变时,意味着元素之间的索引关系变了。例如[3,28,28,3]变换为[3,3,28,28]后,相当于把最后面的维度数据整个提到了前面。

基本的维度变换包含了改变视图 reshape,插入新维度 expand_dims,删除维 squeeze,交换维度 transpose,复制数据 tile 等。

一、张量的存储和视图(View)概念

张量的视图就是我们理解张量的方式,比如shape 为[2,4,4,3]的张量A,我们从逻辑上可以理解为2 张图片,每张图片4 行4 列,每个位置有RGB 3 个通道的数据;

张量的存储体现在张量在内存上保存为一段连续的内存区域,对于同样的存储,我们可以有不同的理解方式,比如上述A,我们可以在不改变张量的存储下,将张量A 理解为2 个样本,每个样本的特征为长度48 的向量。

这就是存储与视图的关系。

在存储数据时,内存并不支持这个维度层级概念,只能以平铺方式按序写入内存,因此这种层级关系需要人为管理,也就是说,每个张量的存储顺序需要人为跟踪。
为了方便表达,我们把张量shape 中相对靠左侧的维度叫做大维度,shape 中相对靠右侧的维度叫做小维度,比如[2,4,4,3]的张量中,图片数量维度与通道数量相比,图片数量叫做大维度,通道数叫做小维度。
例如:在优先写入小维度的设定下,张量 [2,4,4,3] 的内存布局为
SRCNN 图像超分辨率重建(tf2)
为了能够正确恢复出数据,必须保证张量的存储顺序与新视图的维度顺序一致,
例如根据图片数量-行-列-通道初始视图保存的张量,按照图片数量-行-列-通道(𝑏 − ℎ −w − 𝑐)的顺序可以获得合法数据。

如果按着图片数量-像素-通道(b − h ∗ w − c)的方式恢复视图,也能得到合法的数据。但是如果按着图片数量-通道-像素( − c − h ∗ w)的方式恢复数据,由于内存布局是按着图片数量-行-列-通道的顺序,视图维度与存储维度顺序相悖,提取的数据将是错乱的。

; 二、Reshape 操作

改变视图是神经网络中非常常见的操作,可以通过串联多个Reshape 操作来实现复杂
逻辑,
但是在通过Reshape 改变视图时,必须始终记住张量的存储顺序,新视图的维度顺序不能与存储顺序相悖,否则需要通过交换维度操作将存储顺序同步过来。
举个例子,对于shape 为[4,32,32,3]的图片数据,通过Reshape 操作将shape 调整为[4,1024,3],此时视图的维度顺序为 𝑏 − 𝑝𝑖𝑥𝑒𝑙 − 𝑐,张量的存储顺序为 [𝑏, ℎ, w, 𝑐]。

在 TensorFlow 中,可以通过张量的ndim 和shape 成员属性获得张量的维度数和形
状:
SRCNN 图像超分辨率重建(tf2)
通过 tf.reshape(x,new_shape),可以将张量的视图任意的合法改变
SRCNN 图像超分辨率重建(tf2)
当不知道填入什么数字合适时,可以选用 -1 来替代,由python通过其他值进行推算得知具体值
例如将shape为 [b,3,4]的输入数据转为 shape为 [b,3*4] 的数据:
SRCNN 图像超分辨率重建(tf2)

三、增删维度

1、增加维度

增加一个长度为1 的维度相当于给原有的数据增加一个新维度的概念,维度长度为1,故数据并不需要改变,仅仅是改变数据的理解方式,因此它其实可以理解为改变视图的一种特殊方式

比如:,一张28x28 灰度图片的数据保存为 shape 为[28,28]的张量,在末尾给张量增加一新维度,定义为通道数维度,此时张量的shape 变为[28,28,1]:

通过tf.expand_dims(x, axis)可在指定的axis 轴前可以插入一个新的维度:
SRCNN 图像超分辨率重建(tf2)
可以看到插入一个新维度后,数据的存储顺序并没有改变,仅仅改变了数据的视图。
SRCNN 图像超分辨率重建(tf2)
需要注意的是,tf.expand_dims 的axis 为正时,表示在当前维度之前插入一个新维度;为负时,表示当前维度之后插入一个新的维度。以[𝑏, ℎ, w, 𝑐]张量为例,不同axis 参数的实际插入位置如图所示:
SRCNN 图像超分辨率重建(tf2)

; 2、删除维度

是增加维度的逆操作,与增加维度一样,删除维度只能删除长度为1 的维度,也不会改变张量的存储。

可以通过tf.squeeze(x, axis)函数,axis 参数为待删除的维度的索引号

SRCNN 图像超分辨率重建(tf2)
如果不指定维度参数 axis,即 tf.squeeze(x),那么他会默认删除所有长度为1的维度

四、交换维度

在实现算法逻辑时,在保持维度顺序不变的条件下,仅仅改变张量的理解方式是不够的,有时需要直接调整的存储顺序,即交换维度(Transpose)。通过交换维度,改变了张量的存储顺序,同时也改变了张量的视图。

我们以[𝑏, ℎ, w, 𝑐]转换到[𝑏, 𝑐, ℎ, w]为例,介绍如何使用tf.transpose(x, perm)函数完成维度交换操作,其中 perm 表示新维度的顺序 List。

SRCNN 图像超分辨率重建(tf2)
通过tf.transpose完成维度交换后,张量的存储顺序已经改变,视图也随之改变,后续的所有操作必须基于新的存续顺序进行

; 五、数据复制

tf.tile(x, multiples)函数完成数据在指定维度上的复制操作,multiples 分别指定了每个维度上面的复制倍数,对应位置为1 表明不复制,为2 表明新长度为原来的长度的2 倍,即数据复制一份,以此类推。
SRCNN 图像超分辨率重建(tf2)

参考资料:
TensorFlow:维度变换
TensorFlow基础(7.维度变换)
【tensorFlow】维度变换
tensorflow(八):维度变换

Original: https://blog.csdn.net/u013250861/article/details/124222288
Author: u013250861
Title: TensorFlow2-基础(五):Tensor的维度变换【改变:reshape(view级别)、transpose(content级别)】【增加(expand_dims)、删除(squeeze)】