tensorflow.keras 2.x版本DeepDream图像处理实战

本文是个人对《Deep Learning with Python》一书的学习笔记。使用 VSCode 下的 ipynb (python notebook, python 3.8.4 64-bit).

由于原书的代码使用的tensorflow,keras,scipy的版本较为古老,在新版本(tensorflow 2.x 等)条件下已无法直接运行。经过不断调整后代码能成功在新的版本下运行。

库的导入

先来看一下使用的库及版本。

import tensorflow as tf
import tensorflow.keras as keras
import numpy as np
import cv2 as cv

print("Tf version =",tf.__version__)
print("Keras version =",keras.__version__)
print("Numpy version =",np.__version__)
print("Opencv version =",cv.__version__)

输出:
Tf version = 2.3.1
Keras version = 2.4.0
Numpy version = 1.19.3
Opencv version = 4.2.0

模型建立

直接导入现成的 InceptionV3 模型,并设置 trainable = False
注意在导入前需要如下禁止 eager_execution 模式,这是 tensorflow 2.x 所需要的。

tf.compat.v1.disable_eager_execution()
model = keras.applications.inception_v3.InceptionV3(weights='imagenet',include_top=False)
model.trainable = False

构建Loss函数

注意此处用 loss = 0 初始化,这是与原书不同的地方。

如果查看 loss 的类型,会发现不是普通的float,而是Tensor:
<class ‘tensorflow.python.framework.ops.Tensor’>.
所以 K.gradients可以根据计算图求得 model.input 到 loss 的函数的梯度。


修改 layers_contibution 参数可以调整不同层的贡献从而得到不同的效果。

import tensorflow.keras.backend as K
layers_contribution = {'mixed2':3.0,'mixed3':1.0,'mixed4':0.2,'mixed5':0.5}
layer_dict = {layer.name : layer for layer in model.layers}
loss = 0   # 不要用 K.variable(0.)
for layer_name, contribution in layers_contribution.items():
    activation = layer_dict[layer_name].output
    scaling = K.prod(K.cast(K.shape(activation),'float32'))
    loss += ( contribution * K.sum(K.square(activation[:, 2:-2, 2:-2, :]))/scaling )


grads = K.gradients(loss, model.get_layer('input_1').input)[0] 
#grads = K.gradients(loss,model.input)[0]
grads /= K.maximum(K.mean(K.abs(grads)), 1e-7)

fetch_loss_and_grads = K.function([model.input], [loss, grads])


辅助函数

定义一些辅助函数。原书使用的 scipy.misc.imsave 已经被移除,这里替换成了 opencv.imwrite.

def deprocess_image(x): 
    if K.image_data_format() == 'channels_first':
        x = x.reshape((3, x.shape[2], x.shape[3]))
        x = x.transpose((1, 2, 0))
    else:
        x = x.reshape((x.shape[1], x.shape[2], 3)) 

    # 将 [-1,1] 的值线性映射到 [0,255] 的整数
    x /= 2.
    x += 0.5
    x *= 255.
    x = np.clip(x, 0, 255).astype('uint8')
    return x[:,:,[2,1,0]]  # 需要把 RGB 通道转化为cv.imwrite需要的 BGR 通道

def resize_img(img, size):
    # 注意 opencv的resize的形状参数是 (width, height)
    return np.expand_dims(cv.resize(img[0], (size[1],size[0]) ) , axis = 0)

def save_img(img, fname):
    cv.imwrite(fname , deprocess_image(np.copy(img)))

def preprocess_image(image_path): 
    img = keras.preprocessing.image.load_img(image_path)
    img = keras.preprocessing.image.img_to_array(img)
    img = np.expand_dims(img, axis=0)
    # 返回值的 shape 是 (1,height,width,3)
    return keras.applications.inception_v3.preprocess_input(img)

def gradient_ascent(x, iterations, step, max_loss=None): 
    for i in range(iterations):
        loss_value, grad_values = fetch_loss_and_grads([x])
        print('...Loss value at', i, ':', loss_value)
        if max_loss is not None and loss_value > max_loss:
            break
        x += step * grad_values
    return x
    

梯度上升(gradien_ascent)与梯度下降相反,可以认为是将结果加上梯度*系数,让图片产生了变化。


实验测试

选择图像的路径后,运行该段代码即可。
程序会先把原图缩小,从缩小后的图片开始处理。再逐步放大图片处理,直至大小和输入图像相同。

因为不涉及神经网络训练,所以运行时间不长。我的运行时间:98.1s.

step = 1e-2         # 系数
num_octave = 3      # 放大 num_octave 次
octave_scale = 1.4  # 每次放大的比例
iterations = 20     # 每次图片处理的最大次数

max_loss = 10.      # 最大允许损失值


# 修改为自己的路径
origin_dir = 'D:\\Python Projects\\Neural Network\\GAN\\DeepDream'

# img 的 shape 是 (1,height,width,3)
img = preprocess_image(origin_dir + '\\base_image.png') 

original_shape = img.shape[1:3]
successive_shapes = [original_shape] 

for i in range(1, num_octave):
    shape = tuple([int(dim / (octave_scale ** i)) for dim in original_shape])
    successive_shapes.append(shape)
    
successive_shapes = successive_shapes[::-1] 
original_img = np.copy(img) 
shrunk_original_img = resize_img(img, successive_shapes[0])
for shape in successive_shapes:
    print('Processing image shape', shape)
    img = resize_img(img, shape) 
    img = gradient_ascent(img, iterations=iterations, step=step, max_loss=max_loss)

    # 向 img 加入因为放大而失真的部分
    upscaled_shrunk_original_img = resize_img(shrunk_original_img, shape) 
    same_size_original = resize_img(original_img, shape) 
    lost_detail = same_size_original - upscaled_shrunk_original_img 
    img += lost_detail 

    shrunk_original_img = resize_img(original_img, shape)

    save_img(img, fname=origin_dir + '\\dream_at_scale_' + str(shape) + '.png')

测试用原图:
原图
最终结果:出现了很多奇幻的纹理。
结果

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值