以下链接是个人关于stylegan所有见解,如有错误欢迎大家指出,我会第一时间纠正,如有兴趣可以加微信:a944284742相互讨论技术。若是帮助到了你什么,一定要记得点赞奥!因为这是对我最大的鼓励。
风格迁移0-00:stylegan-目录-史上最全:https://blog.csdn.net/weixin_43013761/article/details/100895333
在上篇博客中,我给大家注释了一下根目录下的training/training_loop.py文件,了解一下大致的流程,这小节我们就来讲解一下数据的预处理,即process_reals函数,该函数也存在于training/training_loop.py文件中,其实该函数花费了我好些心思才理解过来。:
def training_loop()
......
# 加载训练数据,其会把所有分辨率的数据都加载进来
training_set = dataset.load_dataset(data_dir=config.data_dir, verbose=True, **dataset_args)
......
......
# 获得训练数据图片和标签
reals, labels = training_set.get_minibatch_tf()
# 对训练数据的真实图片进行处理,主要把图片分成多个区域进行平滑,注意这里的reals包含多张图片,分别对应不同的分辨率,
# 其实这里说是分辨率不太合适,总的来说,他们分辨率都是1024,但是平滑插值不一样.其不是用来训练的数据,是用来求损失用的,具体细节后面分析,也属于一个比较重要的地方
reals = process_reals(reals, lod_in, mirror_augment, training_set.dynamic_range, drange_net)
现在我们来看看process_reals函数:
def process_reals(x, lod, mirror_augment, drange_data, drange_net):
"""
:param x: 该为输入的图片,(batch_size, 3, 1024, 1024)
:param lod: 该值从零开始,随着训练图片的张数,更改变为0,1,2,3,4,5,6,7,8
:param mirror_augment: # 是否进行镜像翻转
:param drange_data: #数据动态变化的范围[0,255],输入
:param drange_net: #数据动态变化的网络[-1,1],输出
:return:
"""
with tf.name_scope('ProcessReals'):
with tf.name_scope('DynamicRange'):
x = tf.cast(x, tf.float32)
# 把原来的像素先缩小到2/255然后减去1
x = misc.adjust_dynamic_range(x, drange_data, drange_net)
if mirror_augment:
with tf.name_scope('MirrorAugment'):
s = tf.shape(x)
# 随机产生(batch_size, 1, 1, 1)维度的数组,其值为0到1之间
mask = tf.random_uniform([s[0], 1, 1, 1], 0.0, 1.0)
# 对前面产生的像素,进行复制,复制之后的维度为[batch_size, 3, 1024, 1024]
mask = tf.tile(mask, [1, s[1], s[2], s[3]])
#小于0.5的返回原值,否则返回对第三维进行翻转之后的值(经过一位小伙伴的提醒,进行了修改)
x = tf.where(mask < 0.5, x, tf.reverse(x, axis=[3]))
with tf.name_scope('FadeLOD'): # Smooth crossfade between consecutive levels-of-detail.
# (batch_size, 3, 1024, 1024)
s = tf.shape(x)
# 把每张(1024, 1024)的图片图片分割成4快区域,每块区域为512*512个像素
y = tf.reshape(x, [-1, s[1], s[2]//2, 2, s[3]//2, 2])
# 对3,5维度取均值,即4个512*512的区域,每个区域都用他们的平均像素代替,注意,是一个像素代替512个,
# 所以这里已经降维,变成(3,4,4)的图片
y = tf.reduce_mean(y, axis=[3, 5], keepdims=True)
# 对像素进行复制,复原到(3,1024,1024),因为是复制而来,复原到4快区域,但是每块区域对应的像素都是之前的均值
y = tf.tile(y, [1, 1, 1, 2, 1, 2])
y = tf.reshape(y, [-1, s[1], s[2], s[3]])
# 进行像素插值,当lod为0的时候,不进行任何改变,依旧为上面计算出来的四块区域,所有像素都用均值代替
# lod越大,则越接近原图,其主要目的就是把原图损失的像素值补回来,当lod围殴10,即2的10次方插值,此时和原图一样
x = tflib.lerp(x, y, lod - tf.floor(lod))
# 和前面类似的操作,把图片区域化,使用均值像素代替,但是这里会分成factor*factor块区域,不仅仅是4快。
with tf.name_scope('UpscaleLOD'): # Upscale to match the expected input/output size of the networks.
s = tf.shape(x)
factor = tf.cast(2 ** tf.floor(lod), tf.int32)
x = tf.reshape(x, [-1, s[1], s[2], 1, s[3], 1])
x = tf.tile(x, [1, 1, 1, factor, 1, factor])
x = tf.reshape(x, [-1, s[1], s[2] * factor, s[3] * factor])
return x
其中函数adjust_dynamic_range实现如下:
def adjust_dynamic_range(data, drange_in, drange_out):
"""
:param data:
:param drange_in:[0,255]
:param drange_out:[-1,1]
:return:
"""
if drange_in != drange_out:
# scale = ([1]-[-1])/([255]-[0]) = 2/255
scale = (np.float32(drange_out[1]) - np.float32(drange_out[0])) / (np.float32(drange_in[1]) - np.float32(drange_in[0]))
# bias = (-1) - (0 * scale) = -1
bias = (np.float32(drange_out[0]) - np.float32(drange_in[0]) * scale)
# 把原来的像素先缩小到2/255然后减去1
data = data * scale + bias
return data
其实,对于上面的
with tf.name_scope('DynamicRange'):
......
if mirror_augment:
......
不是很了解,暂时不知道他为什么要这样做,如果忽略掉上面的代码,其原理很好理解的。当lod为3的时候,输出如下:
当lod为4的时候输出如下:
前面提到,当能是图片像素为1024时,几乎和原图一样,类似于下(该还和原图又差距,大约为512x512):
对于前面不懂得地方,估计是和求损失的部分有关联,当然只是猜测而已,如果有弄懂得朋友,希望能够告诉我,我也很想明白。
下面我来说说,为什么需要这种把一副图像分成多个区域,然后使用均值代替那一篇区域,其类似于下采样但是又不是下采样。再前面也提到过,目的是为了让机器分步生成图片,先生成轮廓再生成细节。这里的process_real处理的图片,估计是计算损失所使用,真相是否如此,请看后面的分析。