YOLO算法之车型识别

理论知识

干货

论文链接

论文解读

darknet安装

​ 在darknet安装之前,需要预先装好cuda、cudnn和opencv环境。也可以不用装GPU版本直接编译运行使用cpu版本,但是,为了体现更好的处理效率,推荐使用GPU。

​ 安装方法直接按照官网教程就可以,链接如下:

darknet安装

车型识别

车型识别目标: 要精确识别给定车辆图片的车型信息

零碎知识点

标注软件Colabeler

参考链接1

参考链接2

yolo可视化

darknet命令总结

召回率和准确率<https://www.zhihu.com/question/19645541/answer/91694636>

mAp、P、IOU

数据集准备与处理

车型数据集共有1500张车辆照片,主要为汽车、公共汽车、卡车三种类型,拍摄时尽可能从不同角度、不同场景、不同环境下采集图片,初次之外,每种类型车型的图片量尽可能相差不大。

  1. 数据标记

在这里插入图片描述

注意:标注图片时,要时常查看标注的数据是否已经保存,并且要非常注意要勾选标注图片的类型

  1. xml文件

    [外链图片转存失败(img-FuNiXWdz-1568511457921)(/home/gavin/NoteBook/学习汇报/DeepinScreenshot_select-area_20190516185547.png)]

    注意:一定要生成指定的XML格式文件,此次主要是根据pascal xml数据制作数据标签。

  2. 生成标签

    VOCdevkit
    └── VOC2018
    ├── Annotations #存放标记生成的XML文件
    ├── ImageSets #存放训练集和验证集的照片信息(照片的名称)
    ├── JPEGImages #存放车型数据集
    └── labels  # 每张样本的标签

    labels文件格式:

    0 0.5467489919354839 0.4299395161290323 0.8933971774193548 0.5191532258064516

    • object-class:是指对象的索引,从0开始,具体代表哪个对象去obj.names配置文件中按索引查,初次之外,每个txt文件可以有多个boundbox的信息,表示圈定的图片不是一个单一类。
    • x,y:是一个坐标,需要注意的是它可不是对象左上角的坐标,而对象中心的坐标
    • width,height:是指对象的宽高

    voc_label.py 解析

    import xml.etree.ElementTree as ET
    import pickle
    import os
    from os import listdir, getcwd
    from os.path import join
    
    sets=[('2018', 'train'), ('2018', 'val')]
    
    classes = ["car", "bus", "truck"] #设置分类车型
    
    
    def convert(size, box): #此函数主要生成labels文件中归一化的值
        dw = 1./(size[0]) #用于归一化
        dh = 1./(size[1])
        x = (box[0] + box[1])/2.0 #找出对象的中心点坐标
        y = (box[2] + box[3])/2.0
        w = box[1] - box[0] # 得到真是的boundbox的长宽信息
        h = box[3] - box[2]
        x = x*dw #归一化
        w = w*dw
        y = y*dh
        h = h*dh
        return (x,y,w,h)
    
    def convert_annotation(year, image_id):#解析XMl文件
        
        in_file = open('VOCdevkit/VOC%s/Annotations/%s.xml'%(year, image_id))
        out_file = open('VOCdevkit/VOC%s/labels/%s.txt'%(year, image_id), 'w')
        tree=ET.parse(in_file)
        root = tree.getroot()
        size = root.find('size')
        w = int(size.find('width').text)
        h = int(size.find('height').text)
    
        for obj in root.iter('object'):
            
            difficult = obj.find('difficult').text
            cls = obj.find('name').text #查找是不是我们自己的类
            
            if cls not in classes or int(difficult)==1:#跳过
                continue
                
            cls_id = classes.index(cls)#得到类别标签
            
            xmlbox = obj.find('bndbox')
            b = (float(xmlbox.find('xmin').text), float(xmlbox.find('xmax').text), float(xmlbox.find('ymin').text), float(xmlbox.find('ymax').text))
            
            bb = convert((w,h), b)
            out_file.write(str(cls_id) + " " + " ".join([str(a) for a in bb]) + '\n')
    
    wd = getcwd()#获取当前路径
    
    #1.创建labels文件夹
    for year, image_set in sets: #此处第一个循环训练集 第二个循环验证集
        if not os.path.exists('VOCdevkit/VOC%s/labels/'%(year)):
            os.makedirs('VOCdevkit/VOC%s/labels/'%(year))
            
    #2.得到图片的id    
        image_ids = open('VOCdevkit/VOC%s/ImageSets/Main/%s.txt'%(year, image_set)).read().strip().split()
    #3.创建训练集和验证集文件夹  
        list_file = open('%s_%s.txt'%(year, image_set), 'w')
    #4.创建绝对路径下的图片文件路径    
        for image_id in image_ids:
            list_file.write('%s/VOCdevkit/VOC%s/JPEGImages/%s.jpg\n'%(wd, year, image_id))
    #5.xml 转 label
            convert_annotation(year, image_id)
        list_file.close()
    
    os.system("cat 2018_train.txt 2018_val.txt > train.txt")#扩充训练集数据量
    
    
    
  3. 数据预处理

    voc.data

    classes= 3 #分类类型
    train  = /home/gavin/Machine/darknet/scripts/train.txt #训练集
    valid  = /home/gavin/Machine/darknet/scripts/2018_val.txt #验证集
    names = data/voc.names #类别信息
    backup = backup #存放训练权重
    
  4. yolov3-tiny.cfg

    考虑的笔记本硬件的不足,选择了yolov3-tiny版网络模型,缺点:会丧失一定的准确度

    [net]
    # Testing
    #batch=1
    #subdivisions=1
    Training
    batch=2
    subdivisions=1
    width=416
    height=416
    channels=3
    momentum=0.9
    decay=0.0005
    angle=0
    saturation = 1.5
    exposure = 1.5
    hue=.1
    
    learning_rate=0.001
    burn_in=1000
    max_batches = 500200
    policy=steps
    steps=400000,450000
    scales=.1,.1
    
    ......
    
    [convolutional]
    size=1
    stride=1
    pad=1
    filters=24 #classes*8 
    activation=linear
    
    [yolo]
    mask = 3,4,5
    anchors = 10,14,  23,27,  37,58,  81,82,  135,169,  344,319
    classes=3#classes
    num=6
    jitter=.3
    ignore_thresh = .7
    truth_thresh = 1
    random=1
    
    [route]
    layers = -4
    
    [convolutional]
    batch_normalize=1
    filters=128
    size=1
    stride=1
    pad=1
    activation=leaky
    
    [upsample]
    stride=2
    
    [route]
    layers = -1, 8
    
    [convolutional]
    batch_normalize=1
    filters=256
    size=3
    stride=1
    pad=1
    activation=leaky
    
    [convolutional]
    size=1
    stride=1
    pad=1
    filters=24
    activation=linear
    
    [yolo]
    mask = 0,1,2
    anchors = 10,14,  23,27,  37,58,  81,82,  135,169,  344,319
    classes=3
    num=6
    jitter=.3
    ignore_thresh = .7
    truth_thresh = 1
    random=1
    
    

网络模型

在这里插入图片描述

训练模型
  1. 开始训练

    darknet : sudo ./darknet detector train cfg/voc.data cfg/yolov3-tiny.cfg

    本次训练总共7万次,已经达到了不错的效果

  2. 日志文件解析

    Region 16 Avg IOU: 0.100322, Class: 0.425682, Obj: 0.461557, No Obj: 0.488897, .5R: 0.000000, .75R: 0.000000,  count: 4
    Region 23 Avg IOU: -nan, Class: -nan, Obj: -nan, No Obj: 0.534580, .5R: -nan, .75R: -nan,  count: 0
    2: 675.922241, 673.001831 avg, 0.000000 rate, 0.177072 seconds, 8 images
    Loaded: 0.315449 seconds
    Region 16 Avg IOU: 0.142092, Class: 0.463787, Obj: 0.288284, No Obj: 0.489365, .5R: 0.000000, .75R: 0.000000,  count: 4
    Region 23 Avg IOU: -nan, Class: -nan, Obj: -nan, No Obj: 0.534735, .5R: -nan, .75R: -nan,  count: 0
    3: 673.221313, 673.023804 avg, 0.000000 rate, 0.173940 seconds, 12 images
    Loaded: 0.325054 seconds
    Region 16 Avg IOU: 0.293639, Class: 0.411060, Obj: 0.413394, No Obj: 0.489323, .5R: 0.000000, .75R: 0.000000,  count: 4
    

    其中每行的参数意义如下:
    Avg IOU:当前迭代中,预测的box与标注的box的平均交并比,越大越好,期望数值为1;
    Class: 标注物体的分类准确率,越大越好,期望数值为1;
    obj: 越大越好,期望数值为1;
    No obj: 越小越好;
    .5R: 以IOU=0.5为阈值时候的recall; recall = 检出的正样本/实际的正样本
    0.75R: 以IOU=0.75为阈值时候的recall;
    count:正样本数目。
    注:存在nan值说明该子批次没有预测到正样本,在训练开始时候有出现是正常现象

    特别的每一个批次结束之后会输出一下结果:
    3: 673.221313, 673.023804 avg, 0.000000 rate, 0.173940 seconds, 12 images
    第几批次,总损失,平均损失,当前学习率,当前批次训练时间,目前为止参与训练的图片总数

  3. 检测图片

    3.1 批量化测试效果

    darknet: ./darknet detector valid cfg/voc.data cfg/yolov3-tiny.cfg backup/yolov3-tiny_70000.weights

    注意 : 此时应该修改yolov3-tiny.cfg文件中的batch和subminibatch为1

    测试结果会存放在results文件夹下:如下

    comp4_det_test_bus.txt
    comp4_det_test_car.txt
    comp4_det_test_truck.txt
    yolo_valid.txtbus.txt
    yolo_valid.txtcar.txt
    yolo_valid.txttruck.txt

    当打开其中comp4_det_test_bus.txt:

    1039 0.008945 1075.546509 642.225098 1427.953003 852.698364
    0822 0.005433 578.461548 576.750000 2930.228027 1478.208252
    0860 0.987352 133.620972 325.751465 3403.745117 2544.819336
    图片名称 该类置信度 坐标信息

    3.2 单张测试效果

    darknet: sudo ./darknet detector test cfg/voc.data cfg/yolov3-tiny.cfg backup/yolov3-tiny_70000.weights

    汽车检测效果:

在这里插入图片描述

准确率:

在这里插入图片描述

卡车检测效果:

在这里插入图片描述

准确率:

在这里插入图片描述

公交车检测效果:

在这里插入图片描述

准确率:

在这里插入图片描述

  1. 对于一些复杂场景下的检测效果

    覆盖车辆下的检测效果

在这里插入图片描述

检测结果:未识别

只有部分车身检测效果

在这里插入图片描述

检测结果:未识别

多车辆下的检测效果

在这里插入图片描述

检测结果:此时的car 的准确率只有57%,并且对其它车辆不能够进行检测

黑夜下的检测效果

在这里插入图片描述

检测结果:未识别

结论:由于本次采集的数据未能够采集到特殊条件下的车辆照片,尤其是黑夜和不同角度下的车辆数据,而只是简单的采集了大多数正面和光线条件比较好的情况下的车辆照片,造成了对于复杂环境下未能检测的效果。后期需要如果能够加大数据集的囊括性,便能够达到很好的检测效果。

数据分析
  1. 提取数据

    import inspect
    import os
    import random
    import sys
    def extract_log(log_file,new_log_file,key_word):
        with open(log_file, 'r') as f:
          with open(new_log_file, 'w') as train_log:
      #f = open(log_file)
        #train_log = open(new_log_file, 'w')
            for line in f:
        # 去除多gpu的同步log
              if 'Syncing' in line:
                continue
        # 去除除零错误的log
              if 'nan' in line:
                continue
              if key_word in line:
                train_log.write(line)
        f.close()
        train_log.close()
     
    extract_log('yolov3.log','train_log_loss.txt','images')
    extract_log('yolov3.log','train_log_iou.txt','IOU')
    
  2. 绘制数据图

    import pandas as pd
    import numpy as np
    import matplotlib.pyplot as plt
    #%matplotlib inline
     
    lines =79755    #改为自己生成的train_log_loss.txt中的行数
    result = pd.read_csv('train_log_loss.txt', skiprows=[x for x in range(lines) if ((x%10!=9) |(x<1000))] ,error_bad_lines=False, names=['loss', 'avg', 'rate', 'seconds', 'images'])
    result.head()
     
    result['loss']=result['loss'].str.split(' ').str.get(1)
    result['avg']=result['avg'].str.split(' ').str.get(1)
    result['rate']=result['rate'].str.split(' ').str.get(1)
    result['seconds']=result['seconds'].str.split(' ').str.get(1)
    result['images']=result['images'].str.split(' ').str.get(1)
    result.head()
    result.tail()
     
    # print(result.head())
    # print(result.tail())
    # print(result.dtypes)
     
    print(result['loss'])
    print(result['avg'])
    print(result['rate'])
    print(result['seconds'])
    print(result['images'])
     
    result['loss']=pd.to_numeric(result['loss'])
    result['avg']=pd.to_numeric(result['avg'])
    result['rate']=pd.to_numeric(result['rate'])
    result['seconds']=pd.to_numeric(result['seconds'])
    result['images']=pd.to_numeric(result['images'])
    result.dtypes
     
     
    fig = plt.figure()
    ax = fig.add_subplot(1, 1, 1)
    ax.plot(result['avg'].values,label='avg_loss')
    # ax.plot(result['loss'].values,label='loss')
    ax.legend(loc='best')  #图列自适应位置
    ax.set_title('The loss curves')
    ax.set_xlabel('batches')
    fig.savefig('avg_loss')
    # fig.savefig('loss')
    

    2.1 loss 图:

在这里插入图片描述

2.2 IOU图:

在这里插入图片描述

2.3 Ap:

classescarbustruck
Ap0.330.290.32

mAP = 0.313

Recall:

2.3.1 预备工作

  1. 修改文件

修改darknet根文件下的detector.c

list *plist = get_paths("data/voc.2007.test");
char **paths = (char **)list_to_array(plist);

//修改为如下
list *plist = get_paths("scripts/train.txt");
char **paths = (char **)list_to_array(plist);
  1. 如果出现nan% 修改
//在在detector.c 542行
for(k = 0; k <l.w*l.h*l.n; ++k)
//修改为
 for(k = 0; k < nboxes; ++k)
  1. 测试命令

./darknet detector recall <data_cfg> <test_cfg> <weights>

2.3.2 显示结果

数据:

 Number Correct Total Rps/Img IOU Recall 
 2940  2933  2940	RPs/Img: 1.42	IOU: 85.75%	Recall:99.76%
 2941  2934  2941	RPs/Img: 1.42	IOU: 85.75%	Recall:99.76%
 2942  2935  2942	RPs/Img: 1.42	IOU: 85.75%	Recall:99.76%
 2943  2936  2943	RPs/Img: 1.42	IOU: 85.75%	Recall:99.76%
 2944  2937  2944	RPs/Img: 1.42	IOU: 85.76%	Recall:99.76%
 2945  2938  2945	RPs/Img: 1.42	IOU: 85.76%	Recall:99.76%
 2946  2939  2946	RPs/Img: 1.42	IOU: 85.76%	Recall:99.76%
 2947  2940  2947	RPs/Img: 1.42	IOU: 85.76%	Recall:99.76%

分析:

Number表示处理到第几张图片。

Correct表示正确的识别除了多少bbox。这个值算出来的步骤是这样的,丢进网络一张图片,网络会预测出很多bbox,每个bbox都有其置信概率,概率大于threshold的bbox与实际的bbox,也就是labels中txt的内容计算IOU,找出IOU最大的bbox,如果这个最大值大于预设的IOU的threshold,那么correct加一。

Total表示实际有多少个bbox。

Rps/img表示平均每个图片会预测出来多少个bbox。

IOU: 这个是预测出的bbox和实际标注的bbox的交集 除以 他们的并集。显然,这个数值越大,说明预测的结果越好。

Recall召回率, 意思是检测出物体的个数 除以 标注的所有物体个数。通过代码我们也能看出来就是Correct除以Total的值。

总结

​ 本次,从darknet的安装到yolo模型的实战,成功的实现了从理论到实战的转换,虽然此次检测结果未能对一些复杂环境下的车型进行识别,但是,还是能够实现对大多数条件比较好的车型进行识别,并且准确率比较高。这次实际操作也能更加对yolov3的论文中的一些知识点进行深刻理解。初次之外,通过这次的练习,也学会了一些其他知识技能,比如图片数据的标记与处理、opencv的使用等等。或许这一次的项目实战在整体性完整性上有许多未能考虑点,有许多知识点暂时还未能触及到或尚未真正理解明白,这就需要我接下来认真的重新看有关方面的知识来进行加强。

​ 总之,这次理论与实践相的融合,让我学习到了很多知识。

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值