共计 5472 个字符,预计需要花费 14 分钟才能阅读完成。
图像含糊是由相机或拍摄对象挪动、对焦不精确或应用光圈配置不当导致的图像不清晰。为了取得更清晰的照片,咱们能够应用相机镜头的首选焦点从新拍摄同一张照片,或者应用深度学习常识重现含糊的图像。因为我的特长不是摄影,只能抉择应用深度学习技术对图像进行去模糊解决!
在开始这个我的项目之前,本文假设读者应该理解深度学习的基本概念,例如神经网络、CNN。还要略微相熟一下 Keras、Tensorflow 和 OpenCV。
有各种类型的含糊——静止含糊、高斯含糊、均匀含糊等。但咱们将专一于高斯含糊图像。在这种含糊类型中,像素权重是不相等的。含糊在核心处较高,在边缘处依照钟形曲线缩小。
数据集
在开始应用代码之前,首先须要的是一个由 2 组图像组成的数据集——含糊图像和洁净图像。目前可能没有现成的数据集能够应用,然而就像咱们下面所说的,如果你有 opencv 的根底这个对于咱们来说是十分个简略的,只有咱们有原始图像,应用 opencv 就能够本人生成训练须要的数据集。
这里我的数据集大小约为 50 张图像(50 张洁净图像和 50 张含糊图像),因为只是演示目标所以只抉择了大量图像。
编写代码
曾经筹备好数据集,能够开始编写代码了。
依赖项
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
%matplotlib inline
import random
import cv2
import os
import tensorflow as tf
from tqdm import tqdm
这里导入了 tqdm 库来帮忙我创立进度条,这样能够晓得运行代码须要多长时间。
导入数据
good_frames = '/content/drive/MyDrive/mini_clean'
bad_frames = '/content/drive/MyDrive/mini_blur'
当初创立了 2 个列表。咱们将应用 keras 预处理库读取“.jpg”、“jpeg”或“.png”类型的图像,并转换为数组。这里图像尺寸为 128×128。
clean_frames = []
for file in tqdm(sorted(os.listdir(good_frames))):
if any(extension in file for extension in ['.jpg', 'jpeg', '.png']):
image = tf.keras.preprocessing.image.load_img(good_frames + '/' + file, target_size=(128,128))
image = tf.keras.preprocessing.image.img_to_array(image).astype('float32') / 255
clean_frames.append(image)
clean_frames = np.array(clean_frames)
blurry_frames = []
for file in tqdm(sorted(os.listdir(bad_frames))):
if any(extension in file for extension in ['.jpg', 'jpeg', '.png']):
image = tf.keras.preprocessing.image.load_img(bad_frames + '/' + file, target_size=(128,128))
image = tf.keras.preprocessing.image.img_to_array(image).astype('float32') / 255
blurry_frames.append(image)
blurry_frames = np.array(blurry_frames)
导入模型库
from keras.layers import Dense, Input
from keras.layers import Conv2D, Flatten
from keras.layers import Reshape, Conv2DTranspose
from keras.models import Model
from keras.callbacks import ReduceLROnPlateau, ModelCheckpoint
from keras.utils.vis_utils import plot_model
from keras import backend as K
random.seed = 21
np.random.seed = seed
将数据集拆分为训练集和测试集
当初咱们按 80:20 的比例将数据集分成训练和测试集。
x = clean_frames;
y = blurry_frames;
from sklearn.model_selection import train_test_split
x_train, x_test, y_train, y_test = train_test_split(x, y, test_size=0.2, random_state=42)
查看训练和测试数据集的形态
print(x_train[0].shape)
print(y_train[0].shape)
r = random.randint(0, len(clean_frames)-1)
print(r)
fig = plt.figure()
fig.subplots_adjust(hspace=0.1, wspace=0.2)
ax = fig.add_subplot(1, 2, 1)
ax.imshow(clean_frames[r])
ax = fig.add_subplot(1, 2, 2)
ax.imshow(blurry_frames[r])
下面的代码能够查看来自训练和测试数据集的图像,例如:
上面初始化一些编写模型时须要用到的参数
# Network Parameters
input_shape = (128, 128, 3)
batch_size = 32
kernel_size = 3
latent_dim = 256
# Encoder/Decoder number of CNN layers and filters per layer
layer_filters = [64, 128, 256]
编码器模型
自编码器的构造咱们以前的文章中曾经具体介绍过屡次了,这里就不具体阐明了
inputs = Input(shape = input_shape, name = 'encoder_input')
x = inputs
首先就是输出(图片的数组),获取输出后构建一个 Conv2D(64) – Conv2D(128) – Conv2D(256) 的简略的编码器,编码器将图片压缩为 (16, 16, 256),该数组将会是解码器的输出。
for filters in layer_filters:
x = Conv2D(filters=filters,
kernel_size=kernel_size,
strides=2,
activation='relu',
padding='same')(x)
shape = K.int_shape(x)
x = Flatten()(x)
latent = Dense(latent_dim, name='latent_vector')(x)
这里的 K.int_shape() 将张量转换为整数元组。
实例化编码器模型,如下
encoder = Model(inputs, latent, name='encoder')
encoder.summary()
解码器模型
解码器模型相似于编码器模型,但它进行相同的计算。解码器以将输出解码回 (128, 128, 3)。所以这里的将应用 Conv2DTranspose(256) – Conv2DTranspose(128) – Conv2DTranspose(64)。
latent_inputs = Input(shape=(latent_dim,), name='decoder_input')
x = Dense(shape[1]*shape[2]*shape[3])(latent_inputs)
x = Reshape((shape[1], shape[2], shape[3]))(x)for filters in layer_filters[::-1]:
x = Conv2DTranspose(filters=filters,
kernel_size=kernel_size,
strides=2,
activation='relu',
padding='same')(x)
outputs = Conv2DTranspose(filters=3,
kernel_size=kernel_size,
activation='sigmoid',
padding='same',
name='decoder_output')(x)
解码器如下:
decoder = Model(latent_inputs, outputs, name='decoder')
decoder.summary()
整合成自编码器
自编码器 = 编码器 + 解码器
autoencoder = Model(inputs, decoder(encoder(inputs)), name='autoencoder')
autoencoder.summary()
最初然而十分重要的是在训练咱们的模型之前须要设置超参数。
autoencoder.compile(loss='mse', optimizer='adam',metrics=["acc"])
我抉择损失函数为均方误差,优化器为 adam,评估指标为准确率。而后还须要定义学习率调整的打算,这样能够在指标没有改良的状况下升高学习率,
lr_reducer = ReduceLROnPlateau(factor=np.sqrt(0.1),
cooldown=0,
patience=5,
verbose=1,
min_lr=0.5e-6)
学习率的调整须要在训练的每个轮次都调用,
callbacks = [lr_reducer]
训练模型
history = autoencoder.fit(blurry_frames,
clean_frames,
validation_data=(blurry_frames, clean_frames),
epochs=100,
batch_size=batch_size,
callbacks=callbacks)
运行此代码后,可能须要大概 5-6 分钟甚至更长时间能力看到最终输入,因为咱们设置了训练轮次为 100,
最初后果
当初曾经胜利训练了模型,让咱们看看咱们的模型的预测,
print("\n Input Ground Truth Predicted Value")
for i in range(3):
r = random.randint(0, len(clean_frames)-1)
x, y = blurry_frames[r],clean_frames[r]
x_inp=x.reshape(1,128,128,3)
result = autoencoder.predict(x_inp)
result = result.reshape(128,128,3)
fig = plt.figure(figsize=(12,10))
fig.subplots_adjust(hspace=0.1, wspace=0.2)
ax = fig.add_subplot(1, 3, 1)
ax.imshow(x)
ax = fig.add_subplot(1, 3, 2)
ax.imshow(y)
ax = fig.add_subplot(1, 3, 3)
plt.imshow(result)
能够看到该模型在去模糊图像方面做得很好,并且简直可能取得原始图像。因为咱们只用了 3 层的卷积架构,所以如果咱们应用更深的模型,还有一些超参数的调整应该会取得更好的后果。
为了查看训练的状况,能够绘制损失函数和准确率的图表,能够通过这些数据做出更好的决策。
损失的变动
plt.figure(figsize=(12,8))
plt.plot(history.history['loss'])
plt.plot(history.history['val_loss'])
plt.legend(['Train', 'Test'])
plt.xlabel('Epoch')
plt.ylabel('Loss')
plt.xticks(np.arange(0, 101, 25))
plt.show()
能够看到损失显着缩小,而后从第 80 个 epoch 开始停滞不前。
准确率
plt.figure(figsize=(12,8))
plt.plot(history.history['acc'])
plt.plot(history.history['val_acc'])
plt.legend(['Train', 'Test'])
plt.xlabel('Epoch')
plt.ylabel('Accuracy')
plt.xticks(np.arange(0, 101, 25))
plt.show()
这里能够看到准确率显着进步,如果训练更多轮,它可能会进一步提高。因而,能够尝试减少 epoch 大小并查看准确率是否的确进步了,或者减少早停机制,让训练主动进行
总结
咱们获得了不错的准确率,为 78.07%。对于理论的利用本文只是开始, 例如更好的网络架构, 更多的数据, 和超参数的调整等等, 如果你有什么改良的想法也欢送留言
https://www.overfit.cn/post/d9b6d1a979a444f39c34edc47c647be6
作者:Chandana Kuntala