论文解析:https://blog.csdn.net/bofu_sun/article/details/89206531
近期在做深度估计相关的毕业设计,发现monodepth项目比较不错,决定尝试一下,先解析代码之后再进行训练与更改。
首先是monodepth_dataloader.py文件,一个读取训练集和测试集数据的文件。
1.首先是一个返回数组长度的函数string_length_tf(t)
tf.py_func函数:它的核心是一个func函数(由用户自己定义),该函数接收numpy array作为输入,并返回numpy array类型的输出。
tf.py_func(func,inp,Tout,stateful=True,name=None):第一个参数func是我们要执行的函数,输入是numpy数组输出也是,这里是len函数;第二个参数inp是函数输入,是一个列表;第三个参数Tout制定了func函数返回numpy_array转化成tensor后的格式。也就是说这个函数的作用是将func函数的输出变为我们想要的格式。
len()函数:len函数是python的一个内置函数,输入是一个数组或字符串,返回值是数组中的元素个数。
所以string_length_tf(t)函数用于返回int64类型的数组t的元素个数。
from __future__ import absolute_import, division, print_function
import tensorflow as tf
def string_length_tf(t):
return tf.py_func(len, [t], [tf.int64])
2.接下来是MonodepthDataloader类,先看初始化函数
其中参数param的调用函数如下,可以看到是图片尺寸、编码器等的设置
params = monodepth_parameters(
encoder=args.encoder,
height=args.input_height,
width=args.input_width,
batch_size=args.batch_size,
num_threads=args.num_threads,
num_epochs=args.num_epochs,
do_stereo=args.do_stereo,
wrap_mode=args.wrap_mode,
use_deconv=args.use_deconv,
alpha_image_loss=args.alpha_image_loss,
disp_gradient_loss_weight=args.disp_gradient_loss_weight,
lr_loss_weight=args.lr_loss_weight,
full_summary=args.full_summary)
-
tf.train.string_input_producer函数用于开启一个线程,进行生成图片名队列,这样可以极大的增强gpu效率,第一个参数为文件夹名,第二个参数为是否打乱顺序。
详见http://www.sohu.com/a/148245200_115128 -
tf.cond():在TensorFlow中,tf.cond()类似于c语言中的if…else…,用来控制数据流向。
例left_image = tf.cond(do_flip > 0.5, lambda: tf.image.flip_left_right(right_image_o), lambda: left_image_o)
如果do_flip > 0.5则运行left_image = tf.image.flip_left_right(right_image_o),
否则运行left_image = left_image_o。 -
tensorflow内部含有实现图像翻转的函数为
tf.image.flip_up_down:从上向下翻转
tf.image.flip_left_right:从左到右翻转
tf.image.transpose_image:对角线翻转
tf.image.random_flip_up_down:以一定概率从上向下翻转
tf.image.random_flip_left_right:以一定概率从左到又翻转
6.tf.train.shuffle_batch([example, label], batch_size=batch_size, capacity=capacity, min_after_dequeue)。[example, label]表示样本和样本标签,这个可以是一个样本和一个样本标签,batch_size是返回的一个batch样本集的样本个数。capacity是队列中的容量。不一样的是这个参数min_after_dequeue,一定要保证这参数小于capacity参数的值,否则会出错。这个代表队列中的元素大于它的时候就输出乱的顺序的batch。也就是说这个函数的输出结果是一个乱序的样本排列的batch,不是按照顺序排列的。
https://www.jianshu.com/p/9cfe9cadde06
7.tf.stack:用于拼接两个tf 张量,拼接可以在不同的维度上进行,拼接后的新张量维度加1
x1=tf.constant([1,2,3]) # shape:(3)
x2=tf.constant([3,4,5]) # shape: (3)
# 在第0个轴上拼接
y1=tf.stack([x1,x2],0) # shape=(2*3)
print(sess.run(y1))
-------------------y1
[[1 2 3]
[3 4 5]]
-------------------
# 在第1个轴上拼接
# 此处的axis最大值为1
y2=tf.stack([x1,x2],axis=1) #shape=(3*2)
print(sess.run(y2)
-------------------y2
[[1 3]
[2 4]
[3 5]]
https://blog.csdn.net/wxtcstt/article/details/84980273
class MonodepthDataloader(object):
"""monodepth dataloader"""
def __init__(self, data_path, filenames_file, params, dataset, mode):
self.data_path = data_path #数据路径
self.params = params #类的参数图片尺寸等
self.dataset = dataset #数据集
self.mode = mode #模式,训练或者测试
self.left_image_batch = None #先定义左右图片的batchsize,先不设定具体值
self.right_image_batch = None
input_queue = tf.train.string_input_producer([filenames_file], shuffle=False) #生成文件名队列
line_reader = tf.TextLineReader()
_, line = line_reader.read(input_queue) # 获取文件中的每行的内容
split_line = tf.string_split([line]).values # 取出图片名
# we load only one image for test, except if we trained a stereo model
#如果测试非立体图那么我们只加载一张左图片
if mode == 'test' and not self.params.do_stereo:
left_image_path = tf.string_join([self.data_path, split_line[0]]) # 添加左图片路径
left_image_o = self.read_image(left_image_path) # 打开左图片
else:
left_image_path = tf.string_join([self.data_path, split_line[0]]) # 如果不是测试非立体图那么加载左右两张图片
right_image_path = tf.string_join([self.data_path, split_line[1]])
left_image_o = self.read_image(left_image_path)
right_image_o = self.read_image(right_image_path)
if mode == 'train': # 如果训练
# randomly flip images
#任意翻转图片
do_flip = tf.random_uniform([], 0, 1) # 生成0到1之间的一个数字
# 以0.5的概率左右图同时左右翻转,否则不翻转
left_image = tf.cond(do_flip > 0.5, lambda: tf.image.flip_left_right(right_image_o), lambda: left_image_o)
right_image = tf.cond(do_flip > 0.5, lambda: tf.image.flip_left_right(left_image_o), lambda: right_image_o)
# randomly augment images
#任意填充图片
do_augment = tf.random_uniform([], 0, 1) # 生成0到1之间的一个数字
left_image, right_image = tf.cond(do_augment > 0.5, lambda: self.augment_image_pair(left_image, right_image), lambda: (left_image, right_image)) # 以0.5的概率左右图执行augment_image_pair函数,否则不变
left_image.set_shape( [None, None, 3]) # 设置左右图尺寸为none*none*3
right_image.set_shape([None, None, 3])
# capacity = min_after_dequeue + (num_threads + a small safety margin) * batch_size
# 对训练集乱序放置,设置容器
min_after_dequeue = 2048
capacity = min_after_dequeue + 4 * params.batch_size
self.left_image_batch, self.right_image_batch = tf.train.shuffle_batch([left_image, right_image],
params.batch_size, capacity, min_after_dequeue, params.num_threads)
# 如果是测试立体图,将图片与翻转后的图片进行进行拼接,获得高维度图片,一个维度是原图,另一个维度是翻转后的图片。
elif mode == 'test':
self.left_image_batch = tf.stack([left_image_o, tf.image.flip_left_right(left_image_o)], 0)
self.left_image_batch.set_shape( [2, None, None, 3])
if self.params.do_stereo:
self.right_image_batch = tf.stack([right_image_o, tf.image.flip_left_right(right_image_o)], 0)
self.right_image_batch.set_shape( [2, None, None, 3])
8.捋一下思路,首先如果只测试且不生成立体图,那么只加载一张图片即可,如果进行训练或者进行测试且生成立体图,那么我们就要加载左图与右图两张图片。如果是训练,那么我们以0.5的概率左右翻转图片,同时以0.5的概率填充图片,生成训练集;如果是测试生成立体图,那么我们将左图与右图都将原图与左右翻转后的图片一起生成高维度数据。
9.填充函数:augment_image_pair函数:
首先python中**是乘方的意思
10. tf.clip_by_value的用法
tf.clip_by_value(A, min, max):输入一个张量A,把A中的每一个元素的值都压缩在min和max之间。小于min的让它等于min,大于max的元素的值等于max。
def augment_image_pair(self, left_image, right_image):
# randomly shift gamma
# 对左右图中每个像素开0.8到1.2次方
random_gamma = tf.random_uniform([], 0.8, 1.2) # 生成0.8-1.2随机数
left_image_aug = left_image ** random_gamma # 开方
right_image_aug = right_image ** random_gamma
# randomly shift brightness
random_brightness = tf.random_uniform([], 0.5, 2.0) # 生成0.5-2.0随机数
left_image_aug = left_image_aug * random_brightness # 图片乘随机数
right_image_aug = right_image_aug * random_brightness
# randomly shift color
random_colors = tf.random_uniform([3], 0.8, 1.2) # 生成三个0.8到1.2之间的随机数
white = tf.ones([tf.shape(left_image)[0], tf.shape(left_image)[1]]) # 创建与图片大小相同的数值为1的张量
color_image = tf.stack([white * random_colors[i] for i in range(3)], axis=2) # 合并图片
left_image_aug *= color_image #乘积
right_image_aug *= color_image
# saturate
left_image_aug = tf.clip_by_value(left_image_aug, 0, 1) # 将图片像素调整在0到1之间
right_image_aug = tf.clip_by_value(right_image_aug, 0, 1)
return left_image_aug, right_image_aug
11.读取图片
def read_image(self, image_path):
# tf.decode_image does not return the image size, this is an ugly workaround to handle both jpeg and png
path_length = string_length_tf(image_path)[0]
file_extension = tf.substr(image_path, path_length - 3, 3) # 将文件名后缀名提取出来
file_cond = tf.equal(file_extension, 'jpg') #判断后缀名是否为jpg
image = tf.cond(file_cond, lambda: tf.image.decode_jpeg(tf.read_file(image_path)), lambda: tf.image.decode_png(tf.read_file(image_path))) #解码
# if the dataset is cityscapes, we crop the last fifth to remove the car hood
# 对图片数据进行切割,高度取为4/5
if self.dataset == 'cityscapes':
o_height = tf.shape(image)[0]
crop_height = (o_height * 4) // 5
image = image[:crop_height,:,:]
image = tf.image.convert_image_dtype(image, tf.float32)
image = tf.image.resize_images(image, [self.params.height, self.params.width], tf.image.ResizeMethod.AREA)
return image
- tf.substr:
从字符串的 Tensor 中返回子字符串.
对于输入 Tensor 中的每个字符串,创建一个从索引 pos 开始的子字符串,总长度为 len.
如果 len 定义了一个将超出输入字符串长度的子字符串,那么使用尽可能多的字符.
如果 pos 是负数或指定的字符索引大于任何输入字符串,则会抛出一个 InvalidArgumentError.
pos 和 len 必须具有相同的形状,否则在 Op 创建时会抛出一个 ValueError.
13.//:整除,取整数部分