# 车辆目标检测 **Repository Path**: gaoyanan516/gaoyanan ## Basic Information - **Project Name**: 车辆目标检测 - **Description**: No description available - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 2 - **Forks**: 1 - **Created**: 2020-05-25 - **Last Updated**: 2023-10-10 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # 车辆检测项目 小组成员:高亚楠、黎鹏安、秦龙飞、韩啼啼 ## 1 项目说明 - 车辆检测及型号识别广泛应用于物业,交通等的管理场景中。通过在停车场出入口,路口,高速卡口等位置采集的图片数据,对车辆的数量型号等进行识别,可以以较高的效率对车型,数量信息等进行采集。通过采集的数据,在不同的场景中可以辅助不同的业务开展。如商场停车位的规划,路况规划,或者公安系统追踪肇事车辆等等。 - 课程中,学员们已经掌握了使用slim框架来对图片进行分类识别。也掌握使用slim物体检测框架来进行物体的检测和识别。 - 本项目中,要求学员们结合课程中学到的内容,实现一个车辆检测的工业级系统。 - 环境:Tensorflow-gpu1.14 python3.6 cuda10.0+cudnn7.6.5 ## 2 项目探索 - 项目方案思路1:分类和检测模型组合 - 1、使用原有数据集进行对分类网络进行训练,得到分类网络模型。检测时使用此模型进行图片分类。参考论文[《 Inception-v4, Inception-ResNet and the Impact of Residual Connections on Learning 》]( https://arxiv.org/pdf/1602.07261 ) - 2、采用数据处理步骤中得到的目标检测器对数据集进行目标检测。 - 3、将图片先传入分类模型得到分类结果,再传入检测模型得到预测边框。再将预测边框和分类结果写回原图,完成分类和检测。 - 项目方案思路2:基于CAM的分类和弱监督检测方案 - 参考论文[《Learning Deep Features for Discriminative Localization》](https://arxiv.org/abs/1512.04150) ​ 该方案适用于无检测标签的检测分类问题,论文提出在进行卷积分类时,卷积网络除了进行特征抽提,还保留了丰富的语义和位置信息,但此功能在进入分类器前经过全连接层后丧失。使用GAP(全局平均池化)代替全连接层,可以使位置信息延续到网络最后,然后再使用热图和上采样等方法将物体的区域还原到原图,在分类的同时也得到了位置检测 - 项目方案思路3:基于Grad-CAM的分类和弱监督检测方案 - 参考论文 [《Visual Explanations from Deep Networks via Gradient-based Localization》](https://arxiv.org/abs/1610.02391) - 该方案适用于无检测标签的检测分类问题,论文提出在进行卷积分类时,卷积网络除了进行特征抽提,还保留了丰富的语义和位置信息,但此功能在进入分类器前经过全连接层后丧失。使用GAP(全局平均池化)代替全连接层,可以使位置信息延续到网络最后,然后再使用热图和上采样等方法将物体的区域还原到原图,在分类的同时也得到了位置检测 。 - 项目方案思路4:基于SSD检测方案 - 参考论文 [《Single Shot MultiBox Detector》](https://arxiv.org/abs/1512.02325) - SSD结合了YOLO中的回归思想和Faster-RCNN中的Anchor机制,使用全图各个位置的多尺度区域进行回归,既保持了YOLO速度快的特性,也保证了窗口预测的跟Faster-RCNN一样比较精准。 - SSD的核心是在不同尺度的特征特征图上采用卷积核来预测一系列Default Bounding Boxes的类别、坐标偏移。 ## 3 模型选用及训练 ### 3.1 方案选取 ​ 本次项目采用两种方案 - 第一种是采用级联网络,即InceptionV4+ssd_mobilenet_v2_coco的方式,ssd_mobilenet_v2_coco是object detective中的分类检测模型,该模型已经从大量的数据集中训练得到了与训练模型,可以非常准确的得到car这个类别的bbos,将检测得到的bbox送入到InceptionV4分类网络中进行分类,最后将bbox的位置以及标签返回到检测图像当中去,完成目标的分类检测任务。 - 第二种是采用SSD分类检测模型,得益于SSD检测模型结合了YOLO中的回归思想和Faster-RCNN中的Anchor机制,使用全图各个位置的多尺度区域进行回归,既保持了YOLO速度快的特性,也保证了窗口预测的跟Faster-RCNN一样比较精准。 ### 3.2 数据预处理 #### 3.2.1数据集介绍: - 本项目提供一个车辆分类的数据集。(注意是分类数据,内部没有定位信息(bounding box)) 本项目提供的数据集分类参考数据集中的labels.txt文件:共48856张图片,其43971张作为训练集,4885张作为验证集。 数据已经预先打包成tfrecord格式,数据格式与课程中,分类模型使用的数据格式相同,打包关键代码参考"download_and_convert_flowers.py"。 - 未经过人工清洗; ![](.\md_picture\原始数据.JPG) - 对数据的理解和分析: 1、共48856张图片,其43971张作为训练集,4885张作为验证集,共763个分类。作为训练集和验证集的样本量适中,符合网络训练的要求。 2、车辆检测项目主要用到的技术是分类和检测,其中分类辨别出图片中车辆的类型,采用网络训练时需要用到分类标签;检测确定车辆的位置,采用网络训练时需要用到车辆定位边框信息(bounding box)。针对已知数据集中没有车辆定位边框标签的问题,提出以下两种思路: - 通过某种方法获取车辆定位信息,利用所学知识进行网络训练,得到检测网络模型; - 利用不需要车辆边框定位信息的模型实现车辆检测; - 数据未经人工清洗,可能含有明显错误的数据。 - 数据集大部分每张图片上只有一辆汽车,依靠这个数据集训练的模型,实现每张图片上多辆汽车的多分类检测,效果如何? #### 3.2.2数据处理方案: - 采用TfRecordToJpg.py中代码对tfrecord文件进行decode,得到JPEG格式的图片。此时需要知道对图片进行encode时所用的features的key字段,如“image”、“label”等。 - 通过网上查阅资料,找到了该数据集encode时用的key值字段; - 如果没有成功获取到encode时所用features的key字段,是否有其他技术手段可以解决该问题? - example_proto = tf.train.Example.FromString(serialized_example)features = example_proto.featuresfor key in features .keys():  print(key) - 对数据进行清洗,删除具有明显错误的图片。 - 采用ssd预训练模型结合人工labelImg工具标注标签的方法训练一个多目标检测器,可以检测一整张图片中的多辆汽车。参考论文[《 SSD: Single Shot MultiBox Detector 》 - 采用Yolo算法将图片的bbox标记出来,并使用python脚本自动生成xml文件 ### 3.3 级联网络训练 #### 3.3.1数据集准备 - 在前面已经介绍过数据的预处理,这里采用打包好的tfrecord文件进行训练 ![](.\md_picture\date_car.JPG) - 文件摆放目录 ├─inception_ssd_master ├─test1 ├─train └─inception_v4.ckpt #### 3.3.2模型训练 ###### 训练代码 ```python python vehicle_classifiction/train_image_classifier.py --dataset_name=vehicle --dataset_dir=./train --checkpoint_path=./inception_v4.ckpt --model_name=inception_v4 --checkpoint_exclude_scopes=InceptionV4/Logits,InceptionV4/AuxLogits --train_dir=./output/train --learning_rate=0.001 --optimizer=rmsprop --batch_size=32 --max_number_of_steps=15000 ``` 参数说明: dataset_name :数据集名称 dataset_dir :训练集的存放路径 checkpoint_path :预训练模型文件的存放路径 model_name :模型名称 checkpoint_exclude_scopes :不用加载预处理模型参数的网络层 train_dir :训练生成的checkpoint文件保存的路径 learning_rate :学习率 optimizer :优化器 batch_size :批次数,即每个step输入 训练过程的日志信息如下图: ![](.\md_picture\train_progress.png) ###### TensorBoard可视化 放在./output/train 的训练信息可以通过tensorboard查看,具体的使用方法是在命令行运行以下指令: ``` tensorboard --logdir=./output/train ``` 可以看到以下的类似信息: ``` TensorBoard 1.14.0 at http://XXXX:6006/ (Press CTRL+C to quit) ``` 在浏览器访问http://localhost:6006/,可以看到如下的页面: 查看SCALARS/losses: ![](.\md_picture\tensorboar1.png) 查看SCALARS/total_loss_1 ![](.\md_picture\tensorboard2.png) 查看HISTOGRAMS: ![](.\md_picture\tensorboard3.png) ###### 模型验证 ``` python vehicle_classifiction/eval_image_classifier.py --dataset_name=vehicle --dataset_dir=./train --dataset_split_name=validation --model_name=inception_v4 --checkpoint_path=./output/train --eval_dir=./output/eval --batch_size=32 --max_num_batches=300 ``` ![](.\md_picture\validation.png) 冻结模型freezed.pb导出: ``` python vehicle_classifiction/export_inference_graph.py --model_name=inception_v4 --output_file=./output/inception_v4_inf_graph.pb --dataset_dir=./train --dataset_name=vehicle 上面一步生成inception_v4_inf_graph.pb python vehicle_classifiction/freeze_graph.py --input_graph=./output/inception_v4_inf_graph.pb --input_checkpoint=./output/train/model.ckpt-202479 --output_node_names=InceptionV4/Logits/Predictions --input_binary=True --output_graph=./output/freezed.pb ``` 这里要注意output_node_names 运行pc_web下的脚本read_pb.py读取网络结构,执行命令: ``` python read.py –pb_file inception_v4_inf_graph.pb ``` ``` import/InceptionV4/Logits/Logits/BiasAdd (,) import/InceptionV4/Logits/Predictions (,) ``` 可以看出最后输出的是import/InceptionV4/Logits/Predictions #### 3.3.3 ssd_mobilenet_v2_coco检测模型 - github地址: https://github.com/tensorflow/models/blob/master/research/object_detection/g3doc/detection_model_zoo.md - 采用 ssd_mobilenet_v2_coco预训练模型用于检测出图片中car(类别是3)这个类别 以及bbox信息送入到分类网络中进行分类 #### 3.3.4 级联模型的实现关键代码 ``` def vehicle_detection_classify(image_name, detection_config, classify_config): # 加载检测模型 detection_graph = tf.Graph() with detection_graph.as_default(): od_graph_def = saved_model_pb2.SavedModel() with tf.gfile.GFile(detection_config, 'rb') as fid: serialized_graph = compat.as_bytes(fid.read()) od_graph_def.ParseFromString(serialized_graph) tf.import_graph_def(od_graph_def.meta_graphs[0].graph_def, name='') # 检测 with tf.Session(graph=detection_graph) as sess: # 加载模型用train states loader = tf.train.import_meta_graph(model_path+'.meta') loader.restore(sess, model_path) image_tensor = detection_graph.get_tensor_by_name('image_tensor:0')#图片张量 detection_boxes = detection_graph.get_tensor_by_name('detection_boxes:0')#被检测物体的边框 detection_scores = detection_graph.get_tensor_by_name('detection_scores:0')#每一个物体的置信度分值 detection_classes = detection_graph.get_tensor_by_name('detection_classes:0')#类别标签 num_detections = detection_graph.get_tensor_by_name('num_detections:0') image = Image.open(image_name) img_width,img_height = image.size image_np = image_to_numpy(image) image_np_expanded = np.expand_dims(image_np, axis=0) (boxes, scores, classes, num) = sess.run([detection_boxes, detection_scores, detection_classes, num_detections],\ feed_dict={image_tensor: image_np_expanded}) vehicle_name,vehicle_box = detection_box_accuracy(boxes, scores, classes) # 如果检测到车辆,对车辆分类,识别型号 if vehicle_name and vehicle_box: classify_graph = tf.Graph() with classify_graph.as_default(): with open(classify_config, 'rb') as f: graph_def = tf.GraphDef() graph_def.ParseFromString(f.read()) tf.import_graph_def(graph_def, name='') # 分类 image_preprocessed_list = crop_box(vehicle_box, image,image_np) image_input_tensor = tf.stack(image_preprocessed_list)#矩阵拼接 image_input = open(image_name, 'rb').read() with tf.Session(graph=classify_graph) as sess: image_input = sess.run(image_input_tensor) #softmax_tensor = sess.graph.get_tensor_by_name('final_probs:0') softmax_tensor = sess.graph.get_tensor_by_name('InceptionV4/Logits/Predictions:0') predictions = sess.run(softmax_tensor, feed_dict={'input:0': image_input}) vehicle_predict_name,vehicle_predict_box = classification_box_accuracy(predictions,vehicle_box) img_width,img_height = image.size test_image = Image.fromarray(image_np) draw = ImageDraw.Draw(test_image) use_normalized_coordinates=True for i in range(len(vehicle_predict_box)): ymin = vehicle_predict_box[i][0] xmin = vehicle_predict_box[i][1] ymax = vehicle_predict_box[i][2] xmax = vehicle_predict_box[i][3] if use_normalized_coordinates: (left,right,top,bottom) = (xmin * img_width, xmax * img_width, ymin * img_height, ymax * img_height) else: (left,right,top,bottom) = (xmin,xmax,ymin,ymax) draw.line([(left, top), (left, bottom), (right, bottom),(right, top),(left,top)], width=8,fill='cyan') try: font = ImageFont.truetype('simhei.ttf',35,encoding='utf-8') except IOError: font = ImageFont.load_default() text_width, text_height = font.getsize(vehicle_predict_name[i]) text_bottom = top margin = np.ceil(0.05 * text_height) draw.rectangle([(left, text_bottom - text_height - 2 * margin), (left + text_width,text_bottom)],fill='cyan') draw.text( (left + margin, text_bottom - text_height - margin), vehicle_predict_name[i], fill='black', font=font) im = np.array(test_image) plt.imsave(os.path.join(OUTPUT_FOLDER, os.path.basename(image_name)), im) image_height = int(img_height/2) image_width = int(img_width/2) image_detection = OUTPUT_FOLDER + '/%s' % os.path.basename(os.path.join(OUTPUT_FOLDER, image_name)) image_tag = '

' image_detection_tag = image_tag % (image_detection,image_height,image_width) show_result = '检测到的车辆型号如下:
' for name in vehicle_predict_name: show_result += name + '
' show_all_result = image_detection_tag + show_result + '
' return show_all_result # 如果没有检测到车辆,显示原图 elif not vehicle_name: plt.imsave(os.path.join(OUTPUT_FOLDER, os.path.basename(image_name)),image_np) image_height = int(img_height/2) image_width = int(img_width/2) image_detection = OUTPUT_FOLDER + '/%s' % os.path.basename(os.path.join(OUTPUT_FOLDER, image_name)) image_tag = '

' image_detection_tag = image_tag % (image_detection,image_height,image_width) show_result = '图片中没有汽车
' show_all_result = image_detection_tag + show_result + '
' return show_all_result ``` ### 3.4 SSD模型训练 #### 3.4.1数据集准备 - 在前面已经介绍过数据的预处理,这里生成新的带bbox的tfrecord文件,打包的字段关键代码如下: ```python image_format = b'JPEG' example = tf.train.Example(features=tf.train.Features(feature={ 'image/height': int64_feature(shape[0]), 'image/width': int64_feature(shape[1]), 'image/channels': int64_feature(shape[2]), 'image/shape': int64_feature(shape), 'image/object/bbox/xmin': float_feature(xmin), 'image/object/bbox/xmax': float_feature(xmax), 'image/object/bbox/ymin': float_feature(ymin), 'image/object/bbox/ymax': float_feature(ymax), 'image/object/bbox/label': int64_feature(labels), 'image/object/bbox/label_text': bytes_feature(labels_text), 'image/format': bytes_feature(image_format), 'image/encoded': bytes_feature(image_data)})) ``` ![image-20200501143352574](.\md_picture\tfrecord.png) #### 3.4.2模型训练与测试 ###### ssd训练流程 输入->输出->结果与ground truth标记样本回归损失计算->反向传播, 更新权值 ![image-20200501144353171](.\md_picture\ssd.png) ###### 训练代码 ```python """训练初始确定事项 PRE_TRAINED_PATH=./ckpt/pre_trained/ssd_300_vgg.ckpt TRAIN_MODEL_DIR=./ckpt/fine_tuning/ DATASET_DIR=./IMAGE/tfrecords/commodity_tfrecords/ 训练学习率等值的确定: 每批次训练样本数:32或者更小 网络误差函数惩罚项值:0.0005 学习率:0.001 终止学习率:0.0001 优化器选择:adam优化器 模型名称:ssd_vgg_300 """ import tensorflow as tf from datasets import dataset_factory from datasets.dataset_config import CLASSES from deployment import model_deploy from nets import nets_factory from preprocessing import preprocessing_factory from utils import train_tools slim = tf.contrib.slim DATA_FORMAT = "NHWC" # 设备的命令行参数配置 tf.app.flags.DEFINE_integer('num_clones', 1, "可用设备的GPU数量") tf.app.flags.DEFINE_boolean('clone_on_cpu', False, "是否只在CPU上运行") # 数据集相关命令行参数设置 tf.app.flags.DEFINE_string('dataset_dir', './IMAGE/tfrecords/', "训练数据集目录") tf.app.flags.DEFINE_string('dataset_name', 'car', '数据集名称参数') tf.app.flags.DEFINE_string('train_or_test', 'train', '是否是训练集还是测试集') # 网络相关配置 tf.app.flags.DEFINE_float( 'weight_decay', 0.00004, '网络误差函数惩罚项值,越小越防止过拟合.') tf.app.flags.DEFINE_string( 'model_name', 'ssd_vgg_300', '用于训练的网络模型名称') # 设备的命令行参数配置 tf.app.flags.DEFINE_integer('batch_size', 32, "每批次获取样本数") # 训练学习率相关参数 tf.app.flags.DEFINE_string( 'optimizer', 'adam', '优化器种类 可选"adadelta", "adagrad", "adam","ftrl", "momentum", "sgd" or "rmsprop".') tf.app.flags.DEFINE_string( 'learning_rate_decay_type', 'exponential', '学习率迭代种类 "fixed", "exponential", "polynomial"') tf.app.flags.DEFINE_float( 'learning_rate', 0.01, '模型初始学习率') tf.app.flags.DEFINE_float( 'end_learning_rate', 0.0005, '模型训练迭代后的终止学习率') tf.app.flags.DEFINE_integer( 'max_number_of_steps', 30000, '训练的最大步数') tf.app.flags.DEFINE_string( 'train_model_dir', './ckpt/fine_tuning/', '训练输出的模型目录') # pre-trained模型路径. tf.app.flags.DEFINE_string( 'pre_trained_model', './ckpt/pre_trained/ssd_300_vgg.ckpt', '用于fine-tune的已训练好的基础参数文件位置') FLAGS = tf.app.flags.FLAGS def main(_): if not FLAGS.dataset_dir: raise ValueError('必须指定一个TFRecords的数据集目录') # 设置打印级别 tf.logging.set_verbosity(tf.logging.DEBUG) with tf.Graph().as_default(): # 在默认的图当中进行编写训练逻辑 # DeploymentConfig以及全部参数 # 配置计算机相关情况 deploy_config = model_deploy.DeploymentConfig( num_clones=FLAGS.num_clones, # GPU设备数量 clone_on_cpu=FLAGS.clone_on_cpu, replica_id=0, num_replicas=1, # 1台计算机 num_ps_tasks=0 ) # 定义一个全局步长参数(网络训练都会这么去进行配置) # 使用指定设备 tf.device with tf.device(deploy_config.variables_device()): global_step = tf.train.create_global_step() # 2、获取图片数据,做一些处理 # 图片有什么?image, shape, bbox, label # image会做一些数据增强,大小变换 # 直接训练?需要将anchor bbox进行样本标记正负样本,目的使的GT目标样本的数量与default bboxes数量一致 # 将每个模块结果先获取 # (1) 通过数据工厂取出规范信息 dataset = dataset_factory.get_dataset(FLAGS.dataset_name, FLAGS.train_or_test, FLAGS.dataset_dir) # (2)获取网络计算的anchors结果 ssd_class = nets_factory.get_network(FLAGS.model_name) # 获取默认网络参数 ssd_params = ssd_class.default_params._replace(num_classes=CLASSES) # 初始化网络init函数 ssd_net = ssd_class(ssd_params) # 获取形状,用于输入到anchors函数参数当中 ssd_shape = ssd_net.params.img_shape # 获取anchors, SSD网络当中6层的所有计算出来的默认候选框 ssd_anchors = ssd_net.anchors(ssd_shape) # (3)获取预处理函数 image_preprocessing_fn = preprocessing_factory.get_preprocessing( FLAGS.model_name, is_training=True ) # 打印网络相关参数 train_tools.print_configuration(ssd_params, dataset.data_sources) # 2.2 # 1,slim.dataset_data_provider.DatasetDataProvider通过GET方法获取数据 # 2,对数据进行预处理 # 3,对获取出来的groundtruth标签和bbox。进行编码 # 4,获取的单个样本数据,要进行批处理以及返回队列 with tf.device(deploy_config.inputs_device()): # 给当前操作取一个作用域名称 with tf.name_scope(FLAGS.model_name + '_data_provider'): # slim.dataset_data_provider.DatasetDataProvider通过GET方法获取数据 provider = slim.dataset_data_provider.DatasetDataProvider( dataset, num_readers=4, common_queue_capacity=20 * FLAGS.batch_size, common_queue_min=10 * FLAGS.batch_size, shuffle=True ) # 通过get获取数据 # 真正获取参数 [image, shape, glabels, gbboxes] = provider.get( ['image', 'shape', 'object/label', 'object/bbox']) # 直接进行数据预处理 # image [?, ?, 3]---->[300, 300, 3] image, glabels, gbboxes = image_preprocessing_fn(image, glabels, gbboxes, out_shape=ssd_shape, data_format=DATA_FORMAT) # 对原始anchor bboxes进行正负样本标记 # 得到目标值,编码之后,返回? # 训练? 预测值类别,物体位置,物体类别概率,目标值 # 8732 anchor, 得到8732个与GT 对应的标记的anchor # gclasses:目标类别 # glocalisations:目标类别的真是位置 # gscores:是否是正负样本 gclasses, glocalisations, gscores = ssd_net.bboxes_encode(glabels, gbboxes, ssd_anchors) # print(gclasses, glocalisations) # 特征值、目标 # 批处理以及队列处理 # tensor_list:tensor列表 [tensor, tensor, ] # tf.train.batch(tensor_list, batch_size, num_threads, capacity) # [Tensor, [6], [6], [6]] 嵌套的列表要转换成单列表形式 r = tf.train.batch( train_tools.reshape_list([image, gclasses, glocalisations, gscores]), batch_size=FLAGS.batch_size, num_threads=4, capacity=5 * FLAGS.batch_size) # r应该是一个19个Tensor组成的一个列表 # print(r) # 批处理数据放入队列 # 1个r:批处理的样本, 5个设备,5个r, 5组32张图片 # 队列的目的是为了不同设备需求 batch_queue = slim.prefetch_queue.prefetch_queue(r, capacity=deploy_config.num_clones) # 3、赋值模型到不同的GPU设备,以及损失、变量的观察 # train_tools.deploy_loss_summary(deploy_config,batch_queue,ssd_net,summaries,batch_shape,FLAGS) # summarties:摘要 summaries = set(tf.get_collection(tf.GraphKeys.SUMMARIES)) # batch_shape:解析这个batch_queue的大小,指的是获取的一个默认队列大小,指上面r的大小 batch_shape = [1] + 3 * [len(ssd_anchors)] update_ops, first_clone_scope, clones = train_tools.deploy_loss_summary(deploy_config, batch_queue, ssd_net, summaries, batch_shape, FLAGS) # 4、定义学习率以及优化器 # 学习率:0.001 # 终止学习率:0.0001 # 优化器选择:adam优化器 # learning_rate = tf_utils.configure_learning_rate(FLAGS, num_samples, global_step) # FLAGS:将会用到学习率设置相关参数 # global_step: 全局步数 # optimizer = tf_utils.configure_optimizer(FLAGS, learning_rate) # learning_rate: 学习率 with tf.device(deploy_config.optimizer_device()): # 定义学习率和优化器 learning_rate = train_tools.configure_learning_rate(FLAGS, dataset.num_samples, global_step) optimizer = train_tools.configure_optimizer(FLAGS, learning_rate) # 观察学习率的变化情况,添加到summaries summaries.add(tf.summary.scalar('learning_rate', learning_rate)) # 5、计算所有设备的平均损失以及每个变量的梯度总和 train_op, summaries_op = train_tools.get_trainop(optimizer, summaries, clones, global_step, first_clone_scope, update_ops) # 配置config以及saver gpu_options = tf.GPUOptions(per_process_gpu_memory_fraction=0.8) config = tf.ConfigProto(log_device_placement=False, # 若果打印会有许多变量的设备信息出现 gpu_options=gpu_options) # saver saver = tf.train.Saver(max_to_keep=5, # 默认保留最近几个模型文件 keep_checkpoint_every_n_hours=1.0, write_version=2, pad_step_number=False) # 6、进行训练 slim.learning.train( train_op, # 训练优化器tensor logdir=FLAGS.train_model_dir, # 模型存储目录 master='', is_chief=True, init_fn=train_tools.get_init_fn(FLAGS), # 初始化参数的逻辑,预训练模型的读取和微调模型判断 summary_op=summaries_op, # 摘要 number_of_steps=FLAGS.max_number_of_steps, # 最大步数 log_every_n_steps=10, # 打印频率 save_summaries_secs=60, # 保存摘要频率 saver=saver, # 保存模型参数 save_interval_secs=600, # 保存模型间隔 session_config=config, # 会话参数配置 sync_optimizer=None) if __name__ == '__main__': tf.app.run() ``` ###### 运行训练 ```python python train_ssd_network.py --train_model_dir=./ckpt/fine_tuning/ --dataset_dir=./image/tfrecords/ —dataset_name="car" --train_or_test=train --model_name=ssd_vgg_300 --pre_trained_model=./ckpt/pre_trained/ssd_300_vgg.ckpt --weight_decay=0.0005 --optimizer=adam --learning_rate=0.001 --batch_size=16 --max_number_of_steps=30000 ``` 参数说明: train_model_dir:训练生成的checkpoint文件保存的路径 dataset_dir :训练集的存放路径 dataset_name :数据集名称 train_or_test:训练还是测试 model_name:模型名称 pre_trained_model:预训练模型文件的存放路径 weight_decay:权重损失 optimizer :优化器 learning_rate :学习率 batch_size :批次数,即每个step输入 max_number_of_steps:最大训练次数 训练过程的日志信息如下图: ![image-20200523155951298](.\md_picture\train.png) ###### TensorBoard可视化 ![image-20200501145039048](.\md_picture\tensoboard4.png) ![image-20200501145113634](.\md_picture\tensorboard5.png) ###### 模型验证 - 测试数据准备 - preprocess(数据预处理) - 模型加载 - postprocess(预测结果后期处理) - 通过 scores 筛选bbox。 - 使用nms算法筛选bbox。 - 注意bbox边界与原始图片bbox,按需修改bbox - 预测结果显示(matplotlib) ###### 测试关键代码 ```python import numpy as np import tensorflow as tf from PIL import Image from datasets.dataset_config import CLASSES # 需要用到预处理工厂,模型工厂 from nets import nets_factory from preprocessing import preprocessing_factory from test import visualization from utils.basic_tools import np_methods slim = tf.contrib.slim # 1、定义输入图片数据的占位符 image_input = tf.placeholder(tf.uint8, shape=(None, None, 3)) # 定义一个输出的形状,元组表示 net_shape = (300, 300) data_format = "NHWC" # 2、数据输入到预处理工厂当中,进行处理得到结果 preprocessing_fn = preprocessing_factory.get_preprocessing("ssd_vgg_300", is_training=False) img_pre, _, _, bbox_img = preprocessing_fn(image_input, None, None, net_shape, data_format) # img_pre是三维形状,(300, 300, 3) # 卷积神经网络要求都是四维的数据计算(1, 300, 300, 3) # 维度的扩充 image_4d = tf.expand_dims(img_pre, 0) # 3、定义SSD模型, 并输出预测结果 # reuse作用:在notebook当中运行,第一次创建新的变量为FALSE,但是重复运行cell,保留这些变量的命名,选择重用这些命名,已经存在内存当中了 # 没有消除,设置reuse=True reuse = True if 'ssd_net' in locals() else False # 网络工厂获取 ssd_class = nets_factory.get_network("ssd_vgg_300") # 网络类当中参数,类别总数(商品数据集8+1)? ssd_params = ssd_class.default_params._replace(num_classes=CLASSES) # 初始化网络 ssd_net = ssd_class(ssd_params) # 通过网络的方法获取结果 # 使用slim指定共有参数data_format,net函数里面有很多函数需要使用data_format with slim.arg_scope(ssd_net.arg_scope(data_format=data_format)): predictions, localisations, _, _ = ssd_net.net(image_4d, is_training=False, reuse=reuse) ssd_anchors = ssd_net.anchors(net_shape) # 4、定义交互式会话,初始化变量,加载模型 config = tf.ConfigProto(log_device_placement=False) sess = tf.InteractiveSession(config=config) sess.run(tf.global_variables_initializer()) # 名字步数 ckpt_filepath = r"D:/AllFile/python_work/VehicleDetection/ckpt/fine_tuning/model.ckpt-321" # 创建saver saver = tf.train.Saver() saver.restore(sess, ckpt_filepath) # 会话运行图片,输出结果 # 读取一张图片 # img = Image.open(r"D:/AI/jupyter_notebook/VehicleDetection/test/validation/img12_432x320_label_612.jpg").convert('RGB') img = Image.open(r"D:/AI/项目实战/tfrecord1_2/JPEGImages/07640.jpg").convert('RGB') # 走一个数组转换 img = np.array(img) i, p, l, box_img = sess.run([image_4d, predictions, localisations, bbox_img], feed_dict={image_input: img}) # 通过 predictions 与 select_threshold 筛选bbox classes, scores, bboxes = np_methods.ssd_bboxes_select( p, l, ssd_anchors, select_threshold=0.0045, img_shape=(300, 300), num_classes=CLASSES, decode=True) # bbox边框不能超过原图片 默认原图的相对于bbox大小比例 [0, 0, 1, 1] # bboxes = np_methods.bboxes_clip([0, 0, 1, 1], bboxes) bboxes = np_methods.bboxes_clip(box_img, bboxes) # 根据 scores 从大到小排序,并改变classes rbboxes的顺序 classes, scores, bboxes = np_methods.bboxes_sort(classes, scores, bboxes, top_k=400) # 使用nms算法筛选bbox classes, scores, bboxes = np_methods.bboxes_nms(classes, scores, bboxes, nms_threshold=.45) # 根据原始图片的bbox,修改所有bbox的范围 [.0, .0, .1, .1] # bboxes = np_methods.bboxes_resize([.0, .0, .1, .1], bboxes) bboxes = np_methods.bboxes_resize(box_img, bboxes) visualization.plt_bboxes(img, classes, scores, bboxes) ``` ### 3.5 方案实现 最后以web的形式输出结果,图片存放在statiic文件夹下,运行代码: ``` python server.py ``` ###### 检测单一目标车辆 ![](.\md_picture\one.png) ###### 检测多目标车辆 ![](.\md_picture\three.png) ###### 检测无目标车辆 ![](.\md_picture\none.png)