monodepth无监督卷积神经网络深度估计代码解析(三)

最后我们来看一下主函数
论文解析:https://blog.csdn.net/bofu_sun/article/details/89206531
1.
首先是调用一些库函数,同时设置运行文件时的参数

from __future__ import absolute_import, division, print_function

# only keep warnings and errors
import os
os.environ['TF_CPP_MIN_LOG_LEVEL']='1'

import numpy as np
import argparse
import re
import time
import tensorflow as tf
import tensorflow.contrib.slim as slim

from monodepth_model import *
from monodepth_dataloader import *
from average_gradients import *
# 设置参数对象
parser = argparse.ArgumentParser(description='Monodepth TensorFlow implementation.')
# 模式:训练还是测试
parser.add_argument('--mode',                      type=str,   help='train or test', default='train')
# 模型名:默认monodepth
parser.add_argument('--model_name',                type=str,   help='model name', default='monodepth')
# 编码器使用vgg网络还是残差网络
parser.add_argument('--encoder',                   type=str,   help='type of encoder, vgg or resnet50', default='vgg')
# 数据集使用:作者提供kitti和cityscapes两种
parser.add_argument('--dataset',                   type=str,   help='dataset to train on, kitti, or cityscapes', default='kitti')
# 数据集路径
parser.add_argument('--data_path',                 type=str,   help='path to the data', required=True)
# 记录数据集的文件名
parser.add_argument('--filenames_file',            type=str,   help='path to the filenames text file', required=True)
# 输入图片的高度
parser.add_argument('--input_height',              type=int,   help='input height', default=256)
# 输入图片的宽度
parser.add_argument('--input_width',               type=int,   help='input width', default=512)
# batch批量数
parser.add_argument('--batch_size',                type=int,   help='batch size', default=8)
# 周期数
parser.add_argument('--num_epochs',                type=int,   help='number of epochs', default=50)
# 学习率
parser.add_argument('--learning_rate',             type=float, help='initial learning rate', default=1e-4)
# 左右图相似权值
parser.add_argument('--lr_loss_weight',            type=float, help='left-right consistency weight', default=1.0)
# 计算损失时的参数alpha
parser.add_argument('--alpha_image_loss',          type=float, help='weight between SSIM and L1 in the image loss', default=0.85)
# 梯度损失权值,用于使得视差图尽可能连续
parser.add_argument('--disp_gradient_loss_weight', type=float, help='disparity smoothness weigth', default=0.1)
# 是否生成立体图
parser.add_argument('--do_stereo',                             help='if set, will train the stereo model', action='store_true')
# 双线性插值方式
parser.add_argument('--wrap_mode',                 type=str,   help='bilinear sampler wrap mode, edge or border', default='border')
# 是否使用反卷积层(还是使用插值的方式扩张视差图尺寸)
parser.add_argument('--use_deconv',                            help='if set, will use transposed convolutions', action='store_true')
# gpu数量
parser.add_argument('--num_gpus',                  type=int,   help='number of GPUs to use for training', default=1)
# 线程数
parser.add_argument('--num_threads',               type=int,   help='number of threads to use for data loading', default=8)
# 输出测试视差图到某路径
parser.add_argument('--output_directory',          type=str,   help='output directory for test disparities, if empty outputs to checkpoint folder', default='')
# 保存权值和日志的地址
parser.add_argument('--log_directory',             type=str,   help='directory to save checkpoints and summaries', default='')
# 加载权值地址
parser.add_argument('--checkpoint_path',           type=str,   help='path to a specific checkpoint to load', default='')
# 重新训练
parser.add_argument('--retrain',                               help='if used with checkpoint_path, will restart training from step zero', action='store_true')
# 所有记录都被打开
parser.add_argument('--full_summary',                          help='if set, will keep more data for each summary. Warning: the file can become very large', action='store_true')
# 设置参数
args = parser.parse_args()

2.np.fliplr:左右翻转

>> a=magic(3)  
a =  
     8     1     6  
     3     5     7  
     4     9     2  
>> b=fliplr(a)    %左右翻转  
b =  
     6     1     8  
     7     5     3  
     2     9     4  

详见:https://blog.csdn.net/qq_18343569/article/details/50393199

3.numpy.linspace
numpy.linspace(start, stop, num=50, endpoint=True, retstep=False, dtype=None):
在指定的间隔内返回均匀间隔的数字。

4.np.meshgrid:
二维坐标系中,X轴可以取三个值1,2,3, Y轴可以取三个值7,8, 请问可以获得多少个点的坐标?
显而易见是6个:
(1,7)(2,7)(3,7)
(1,8)(2,8)(3,8)

#coding:utf-8 import numpy as np # 坐标向量 
a = np.array([1,2,3]) # 坐标向量 
b = np.array([7,8]) # 从坐标向量中返回坐标矩阵
# 返回list,有两个元素,第一个元素是X轴的取值,第二个元素是Y轴的取值 
res = np.meshgrid(a,b) 
#返回结果: [array([ [1,2,3] [1,2,3] ]), array([ [7,7,7] [8,8,8] ])]

详见:https://blog.csdn.net/littlehaes/article/details/83543459
5. np.clip:将数字强制在一个范围内

def post_process_disparity(disp):
    _, h, w = disp.shape                       # 获取视差图的三个维度
    l_disp = disp[0,:,:]                       # 获取左视差图
    r_disp = np.fliplr(disp[1,:,:])            # 获取右视差图
    m_disp = 0.5 * (l_disp + r_disp) 
    l, _ = np.meshgrid(np.linspace(0, 1, w), np.linspace(0, 1, h))      # 在0和1之间取宽度*高度个数
    l_mask = 1.0 - np.clip(20 * (l - 0.05), 0, 1)                       # 生成左标志
    r_mask = np.fliplr(l_mask)                                          # 生成右标志
    return r_mask * l_disp + l_mask * r_disp + (1.0 - l_mask - r_mask) * m_disp           #返回???

# 记录txt文件中的行数
def count_text_lines(file_path):
    f = open(file_path, 'r')            # 打开文件
    lines = f.readlines()               # 记录行数
    f.close()                           # 关闭文件
    return len(lines)                   # 返回行数

6.tf.ConfigProto(allow_soft_placement=True)
在tf中,通过命令 “with tf.device(’/cpu:0’):”,允许手动设置操作运行的设备。如果手动设置的设备不存在或者不可用,就会导致tf程序等待或异常,为了防止这种情况,可以设置tf.ConfigProto()中参数allow_soft_placement=True,允许tf自动选择一个存在并且可用的设备来运行操作。
7. tf.train.Coordinator:Coordinator类用来管理在Session中的多个线程,可以用来同时停止多个工作线程并且向那个在等待所有工作线程终止的程序报告异常,该线程捕获到这个异常之后就会终止所有线程。使用 tf.train.Coordinator()来创建一个线程管理器(协调器)对象。
9. 训练函数

def train(params):
    """Training loop."""

    with tf.Graph().as_default(), tf.device('/cpu:0'):

        global_step = tf.Variable(0, trainable=False)                       # 记录迭代次数

        # OPTIMIZER
        num_training_samples = count_text_lines(args.filenames_file)        # 训练样本在txt文件中

        steps_per_epoch = np.ceil(num_training_samples / params.batch_size).astype(np.int32)     #设置每个周期进行迭代次数
        num_total_steps = params.num_epochs * steps_per_epoch               # 设置迭代总次数
        
        start_learning_rate = args.learning_rate                            # 设置起始learning_rate 
        # 设定调整学习率的界限,训练3/5和4/5的样本时调整学习率
        boundaries = [np.int32((3/5) * num_total_steps), np.int32((4/5) * num_total_steps)]      
        # 学习率分别调整为原来的1/2和1/4
        values = [args.learning_rate, args.learning_rate / 2, args.learning_rate / 4]       
        # 设定整个learningrate变化情况
        # tf.train.piecewise_constant:当走到一定步长时更改学习率
        learning_rate = tf.train.piecewise_constant(global_step, boundaries, values)

		# Adam 是一种反向传播方法
        opt_step = tf.train.AdamOptimizer(learning_rate)                #设置优化器,用于计算loss并进行反向传播

		# 输出总样本数和步数
        print("total number of samples: {}".format(num_training_samples))
        print("total number of steps: {}".format(num_total_steps))

		# 读取数据集文件
        dataloader = MonodepthDataloader(args.data_path, args.filenames_file, params, args.dataset, args.mode)
        left  = dataloader.left_image_batch
        right = dataloader.right_image_batch

        # split for each gpu
        # 为多个gpu分配任务
        left_splits  = tf.split(left,  args.num_gpus, 0)
        right_splits = tf.split(right, args.num_gpus, 0)

		# 梯度数组
        tower_grads  = []
        # 损失数组
        tower_losses = []
        reuse_variables = None
        with tf.variable_scope(tf.get_variable_scope()):
        	# 对于每个gpu
            for i in range(args.num_gpus):
                with tf.device('/gpu:%d' % i):
	
					# 建立monodepth模型
                    model = MonodepthModel(params, args.mode, left_splits[i], right_splits[i], reuse_variables, i)
					#记录损失
                    loss = model.total_loss
                    tower_losses.append(loss)

                    reuse_variables = True

                    grads = opt_step.compute_gradients(loss)                 #根据loss计算梯度

                    tower_grads.append(grads)                     			 # 添加梯度

        grads = average_gradients(tower_grads)                         		 # 计算batch平均梯度

        apply_gradient_op = opt_step.apply_gradients(grads, global_step=global_step)          #应用梯度

        total_loss = tf.reduce_mean(tower_losses)               			 # 计算平均损失

		# 记录学习率、损失
        tf.summary.scalar('learning_rate', learning_rate, ['model_0'])
        tf.summary.scalar('total_loss', total_loss, ['model_0'])
        summary_op = tf.summary.merge_all('model_0')

        # SESSION
		# 配置设备,自动寻找设备
        config = tf.ConfigProto(allow_soft_placement=True)
        sess = tf.Session(config=config)

        # SAVER
        # 保存日志
        summary_writer = tf.summary.FileWriter(args.log_directory + '/' + args.model_name, sess.graph)
        train_saver = tf.train.Saver()

        # COUNT PARAMS
        # 记录计算量
        total_num_parameters = 0
        for variable in tf.trainable_variables():
            total_num_parameters += np.array(variable.get_shape().as_list()).prod()
        print("number of trainable parameters: {}".format(total_num_parameters))

        # INIT
        # 初始化全局变量
        sess.run(tf.global_variables_initializer())
        sess.run(tf.local_variables_initializer())
        # 多线程管理器
        coordinator = tf.train.Coordinator()
        threads = tf.train.start_queue_runners(sess=sess, coord=coordinator)

        # LOAD CHECKPOINT IF SET
        # 保存训练好的权值等数据
        if args.checkpoint_path != '':
            train_saver.restore(sess, args.checkpoint_path.split(".")[0])

            if args.retrain:
                sess.run(global_step.assign(0))

        # GO!
        start_step = global_step.eval(session=sess)            # 获取最后的步数
        start_time = time.time()                               # 开始时间计时
        for step in range(start_step, num_total_steps):        # 开始循环训练
            before_op_time = time.time()                       # 每次训练计时
            _, loss_value = sess.run([apply_gradient_op, total_loss])      #运行网络
            duration = time.time() - before_op_time            # 记录每次运行的时间
            if step and step % 100 == 0:                       # 每训练一百次记录一下
                examples_per_sec = params.batch_size / duration                      # 每秒处理样本数量
                time_sofar = (time.time() - start_time) / 3600                       # 到现在为止所用时间,单位:小时
                training_time_left = (num_total_steps / step - 1.0) * time_sofar     # 距离训练结束剩余时间
                # 输出记录内容
                print_string = 'batch {:>6} | examples/s: {:4.2f} | loss: {:.5f} | time elapsed: {:.2f}h | time left: {:.2f}h'
                print(print_string.format(step, examples_per_sec, loss_value, time_sofar, training_time_left))
                # 添加日志内容
                summary_str = sess.run(summary_op)
                summary_writer.add_summary(summary_str, global_step=step)
            # 每训练一万次保存一次权值等数据
            if step and step % 10000 == 0:
                train_saver.save(sess, args.log_directory + '/' + args.model_name + '/model', global_step=step)

		#保存日志
        train_saver.save(sess, args.log_directory + '/' + args.model_name + '/model', global_step=num_total_steps)

10.测试函数

# 测试函数
def test(params):
    """Test function."""
	
	# 加载数据集和左右两图
    dataloader = MonodepthDataloader(args.data_path, args.filenames_file, params, args.dataset, args.mode)
    left  = dataloader.left_image_batch
    right = dataloader.right_image_batch

	# 加载模型
    model = MonodepthModel(params, args.mode, left, right)

    # SESSION
    # 设置设备
    config = tf.ConfigProto(allow_soft_placement=True)
    sess = tf.Session(config=config)

    # SAVER
    # 数据储存
    train_saver = tf.train.Saver()

    # INIT
    # 初始化变量
    sess.run(tf.global_variables_initializer())
    sess.run(tf.local_variables_initializer())
    # 配置调节器和多线程
    coordinator = tf.train.Coordinator()
    threads = tf.train.start_queue_runners(sess=sess, coord=coordinator)

    # RESTORE
    # 配置存储路径,存储权值数据
    if args.checkpoint_path == '':
        restore_path = tf.train.latest_checkpoint(args.log_directory + '/' + args.model_name)
    else:
        restore_path = args.checkpoint_path.split(".")[0]
    train_saver.restore(sess, restore_path)

	# 加载要测试的图片样本数量
    num_test_samples = count_text_lines(args.filenames_file)

	# 测试
    print('now testing {} files'.format(num_test_samples))
    disparities    = np.zeros((num_test_samples, params.height, params.width), dtype=np.float32)
    disparities_pp = np.zeros((num_test_samples, params.height, params.width), dtype=np.float32)
    # 生成视差图
    for step in range(num_test_samples):
    	# 运行左图预测
        disp = sess.run(model.disp_left_est[0])
        disparities[step] = disp[0].squeeze()
        disparities_pp[step] = post_process_disparity(disp.squeeze())

    print('done.')

    print('writing disparities.')
    # 保存视差图数据
    if args.output_directory == '':
        output_directory = os.path.dirname(args.checkpoint_path)
    else:
        output_directory = args.output_directory
    np.save(output_directory + '/disparities.npy',    disparities)
    np.save(output_directory + '/disparities_pp.npy', disparities_pp)

    print('done.')

# 主函数,执行操作并导入参数
def main(_):

    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)

    if args.mode == 'train':
        train(params)
    elif args.mode == 'test':
        test(params)

# 执行主函数
if __name__ == '__main__':
    tf.app.run()

11.tf.app.run():入口函数
如果你的代码中的入口函数不叫main(),而是一个其他名字的函数,如test(),则你应该这样写入口tf.app.run(test)
如果你的代码中的入口函数叫main(),则你就可以把入口写成tf.app.run()

你可能感兴趣的:(monodepth无监督卷积神经网络深度估计代码解析(三))