小言_互联网的博客

【Yolo3】一文掌握图像标注、训练、识别(Keras+TensorFlow-gpu)

5357人阅读  评论(0)

一、前言

yolo是一种简易快捷的目标检测算法,它可以对图像做识别和目标检测,因为比一般算法快速,特别是到了v3版本, 也可以对视频做实时识别。

在前一篇文章【Yolo3】入门目标检测实验–Python+Opencv2+dnn中,我们通过官方的模型检测出来了“鸟”:

之前的模型支持检测以下物体:

人 自行车 汽车 摩托车 飞机 巴士 火车 卡车 船 红绿灯 消防栓 站牌 停车咪表 板凳 鸟 猫 狗 
马 羊 牛 象 熊 斑马 长颈鹿 背包 雨伞 手袋 领带 手提箱 飞碟 滑雪 单板滑雪 运动的球 
风筝 棒球棒 棒球手套 滑板 冲浪板 网球拍 瓶 酒杯 杯 叉 刀 勺 碗 香蕉 苹果 三明治 橙 
花椰菜 胡萝卜 热狗 披萨 甜甜圈 蛋糕 椅子 沙发 盆栽植物  床 餐桌  厕所 电视 笔记本
鼠标 遥控 键盘 手机  微波 烤箱 烤面包 片 冰箱 本书 时钟 花瓶 剪刀 泰迪熊 吹风机 牙刷

如果需要检测更多的物体该怎么办呢?——本篇阐述如何制作自己的训练集。

一个小故事来描述yolo图像识别 的过程:

我们当老师,yolo当学生,学习过程分为——备课、教学、考试:
1.老师需要备课、整理每个单元的知识点 (图像标注)
2.老师上课教学(生成索引)
3.学生做课后作业(图像训练)
4.考试测评(图像识别)

图片就是课程内容、标注框就是知识点。

先说断,后不乱

环境:windows10 + anaconda3(conda4.8.2)+ labelImg1.8.1 + VSCode
版本:python3.6.0 + opencv4.1.0 + yolo3 +keras 2.3.1 +tensorflow-gpu2.1.0

环境安装记录:
【GPU】win10 (1050Ti)+anaconda3+python3.6+CUDA10.0+tensorflow-gpu2.1.0
库:numpy1.18.2、Pillow7.0.0、matplotlib 、python-opencv4.2.0

源码源码:

https://gitee.com/cungudafa/keras-yolo3

二、训练集标注

老师备课阶段——图片是课程内容,标注框时知识点。

在yolo中,使用的是网格标注:

我们需要使用到图片标注工具:labelImg,需要安装一下这个软件(支持windows和Ubantu)

标注后的文件保存为xml形式,是这样的:

1. 图像标注

老师备课,准备教学素材。 LabelImg就是老师的备课工具!

(1)下载LabelImg

LabelImg 是一个可视化的图像标定工具。Faster R-CNN,YOLO,SSD等目标检测网络所需要的数据集,均需要借此工具标定图像中的目标。生成的 XML 文件是遵循 PASCAL VOC 的格式的。

下载地址1:https://github.com/tzutalin/labelImg/releases(可以源码安装、也可以直接下载exe免安装版本)
我下载的是windows免安装版本,下载如果很慢,可以使用以下链接:
下载地址2:链接: https://pan.baidu.com/s/1kwwO5VxLMpAuKFvckPpHyg 提取码: 2557
好人一生平安!

(2)图像标注

具体的备课内容——知识点就是标注方框信息。

  1. 准备
    下载完成后是一个可执行exe文件,注意存放到一个非中文的路径下。

    同时,我创建了三个文件夹:

  2. 标注
    运行labelImage,调整到合适大小;熟记使用6步骤:
    打开文件夹(photos)-》设置保存文件夹(annotations)
    设置自动保存(view -》auto Saving)-》标注框(Create RectBox)并命名
    快捷键A保存xml-》快捷键D下一张


    附:(labelImg快捷键表)

    标注示例(轮廓对齐原则):

    (建议从左下角标注到右上角,因为在图片读取中顺序是(x1,y1)到(x2,y2))
    标签英文!!!因为后面在python读取不出错

    快捷键A,D之后:Annotations文件夹保存了我们标记的文件,(之后我们训练时会用到这个文件夹)

问:yolo需要标注多少张图片呢?

答:取决于你的数据集,简单的几千张就够,复杂的就要比较大了。


说明:YOLOv3的样本都不需要负样本,只需要你标出目标物体就行了。但是为了提高准确率需要注意一下三点了:

  1. 对目标的大小也没多大要求,但是不能太小(比如小到只有七八个像素点)
  2. 样本足够多(各种大小、角度最好都来点)
  3. 标注的时候一定要仔细,不要图快,不然后面要返工(亲身经历),bounding box要刚好圈住目标物体(当然你想同时识别物体的局部的话那么同一局部的图片也要足够多)
  4. 复杂的场景下面,也可以添加负样本,也就是说一张图片里面没有目标物体,样本对应的标签只要给一个空的txt文件就行了。比例的话我觉占总样本的一半吧。

现在可以疯狂标注了! 毕竟一个老师教会yolo学生识字,要不断备课。老师不容易呀!

2.生成索引

老师开始上课啦,yolo同学要认真听讲。这是数据预处理阶段

(1)VOC结构

本学期的课程目录,由老师整理的备课而来。

VOC全称Visual Object Classes,出自The PASCAL Visual Object Classes(VOC)Challenge,这个挑战赛从2005年开始到2012年,每年主办方都会提供一些图片样本供挑战者识别分类。
文件目录:

图片源于 | チン昶

我们在工程目录下按照层级新建VOC目录,并把我们标注的内容复制到工程目录下:

(2)生成voc索引

上课:老师把每个单元的知识板书在黑板上 (获取xml内容到txt中)

将voc数据集生成索引txt保存在ImageSets/Main目录下:
在工程yolov3/VOCdevkit/VOC2007/目录下生成:test.txt, train.txt, val.txt

"""
voc_annotation.py
# 生成voc索引

VOCdevkit/VOC2007/
    Annotations/   ---xml文件
    ImageSets/
        Layout/
        Main/
            train.txt/       ---voc训练索引
            test.txt/        ---voc测试索引
            trainval.txt/    ---voc训练测试索引
            val.txt/         ---voc验证索引
        Segmentation/
    JPEGImages/    ---图片
    
"""

import os
import random

trainval_percent = 0.1
train_percent = 0.9
VOC_path = 'D:/myworkspace/JupyterNotebook/yolov3/VOCdevkit/VOC2007/'
xmlfilepath = os.path.join(VOC_path, 'Annotations')
txtsavepath = os.path.join(VOC_path, 'ImageSets/Main')
total_xml = os.listdir(xmlfilepath)

num = len(total_xml)
list = range(num)
tv = int(num * trainval_percent)
tr = int(tv * train_percent)
trainval = random.sample(list, tv)
train = random.sample(trainval, tr)

ftrainval = open(VOC_path+'ImageSets/Main/trainval.txt', 'w')
ftest = open(VOC_path+'ImageSets/Main/test.txt', 'w')
ftrain = open(VOC_path+'ImageSets/Main/train.txt', 'w')
fval = open(VOC_path+'ImageSets/Main/val.txt', 'w')

for i in list:
    name = total_xml[i][:-4] + '\n'
    if i in trainval:
        ftrainval.write(name)
        if i in train:
            ftest.write(name)
        else:
            fval.write(name)
    else:
        ftrain.write(name)

ftrainval.close()
ftrain.close()
fval.close()
ftest.close()

我这里示范有 蝴蝶、鱼、兔子

(3)生成yolo索引

上课:同学们记笔记——抄黑板上的板书、勾画重点笔记 (txt补充标记框内容)

在工程目录yolov3/下生成:test.txt, train.txt, val.txt。注意:classes是要训练的对象。

"""
# 生成yolo索引
# yolo_annotation.py(修改于官方的voc_annotation.py)

model_data/
        voc_class.txt/   ---配置文件,保存所有对象信息
        train.txt/       ---yolo训练索引
        test.txt/        ---yolo测试索引
        trainval.txt/    ---yolo训练测试索引
        val.txt/         ---yolo验证索引

"""

import xml.etree.ElementTree as ET
from os import getcwd

sets = [('2007', 'train'), ('2007', 'val'), ('2007', 'test')]

classes = ["fish", "butterfly", "rabbit"]  # 你要训练的对象

# 1.保存所有对象信息
classes_file = open('model_data/voc_class.txt', 'w')
for idx in classes:
    classes_file.write(str(idx))
    classes_file.write('\n')
classes_file.close()


def convert_annotation(year, image_id, list_file):
    """Annotations中xml加上真实框信息
    """
    in_file = open('VOCdevkit/VOC%s/Annotations/%s.xml' % (year, image_id))
    tree = ET.parse(in_file)
    root = tree.getroot()

    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 = (int(xmlbox.find('xmin').text), int(xmlbox.find('ymin').text),
             int(xmlbox.find('xmax').text), int(xmlbox.find('ymax').text))
        list_file.write(" " + ",".join([str(a)
                                        for a in b]) + ',' + str(cls_id))


wd = getcwd()  # 获得当前的工作目录

for year, image_set in sets:
    image_ids = open('VOCdevkit/VOC%s/ImageSets/Main/%s.txt' %
                     (year, image_set)).read().strip().split()  # 读取VOC目录下txt中图片路径信息
    # 2.model_data目录下yolo索引txt中加上真实框标注信息
    list_file = open('model_data/%s.txt' % (image_set), 'w')
    for image_id in image_ids:
        list_file.write('%s/VOCdevkit/VOC%s/JPEGImages/%s.jpg' %
                        (wd, year, image_id))  # 3.VOC目录下voc索引txt中加上图片路径信息
        # 4.Annotations中xml加上真实框信息
        convert_annotation(year, image_id, list_file)
        list_file.write('\n')
    list_file.close()

查看文件可以发现:yolo索引是图片地址+真实框位置 ,classes此处仅标记了fish:

全部标记:根据你的需要修改

classes = ["fish","butterfly","rabbit"] # 你要训练的对象


这里存放在model_data目录:

3.图像训练

下课了:现在是yolo学生开始做课后作业,对学习的内容加以巩。 这个过程叫做迁移学习

(1)转换权重文件

课后例题讲解

将 DarkNet 的.weights文件转换成 Keras 的.h5文件
准备三个文件:convert.py , yolov3.cfg , yolov3.weights

有教程是在这里修改了yolov3.cfg,会造成nan现象,这里没有修改用原cfg

执行语句:

python convert.py -w yolov3.cfg yolov3.weights model_data/yolo_weights.h5

生成h5文件存放位置:model_data/yolo_weights.h5 ,后面训练时会用到。

先验参数:yolo_anchors.txt

anchors是SPP(spatial pyramid pooling)思想的逆向,即将不同尺寸的输入resize成为相同尺寸的输出。所以SPP的逆向就是,将相同尺寸的输出,倒推得到不同尺寸的输入。

参考:YOLO-v3模型参数anchor设置
我这里也没有修改,用的原始值

(2)训练

课后作业

  1. 下载源码
    train.py (我也实在不明白,只好啃github源码了)

    参考1:https://github.com/qqwweee/keras-yolo3
    (Python 3.5.2 +Keras 2.1.5+ tensorflow 1.6.0)
    参考2:https://github.com/bubbliiiing/yolo3-keras
    (Keras2.1.15 + tensorflow1.13.1)
    上面两种参考都是tensorflow-gpu1.x版本,食用时有出入修改,后面讲到。

    根据参考2我下载相应辅助函数:(nets为 darknet53 网络模型,utils为图像加载辅助函数)

    下载的训练函数:(全部源码可以在我gitee上查看)

  2. tf- gpu 2.1.0 版本遇到的问题及修改

    • 修改1:128行左右(用到tf1的config函数和session函数)

      #原版 config = tf.ConfigProto()
      config = tf.compat.v1.ConfigProto(allow_soft_placement=True)
      
      #原版 set_session(tf.Session(config=config)) 
      tf.compat.v1.keras.backend.set_session(tf.compat.v1.Session(config=config))  #注意 ,这里为tensorflow2.0版本,与第1.0有差距。
      
    • 修改2:(module 'keras.backend' has no attribute 'control_flow_ops')

      D:\mydownload\Anaconda3\envs\tensorflow\Lib\site-packages\keras\
      backend\ __ init__.py添加:

      from .load_backend import control_flow_ops
      

      backend\tensorflow_backend.py添加:

      from tensorflow.python.ops import control_flow_ops
      
    • 修改3:('Model'object has no attribute '_get_distribution_strategy')
      修改,记得备份:D:\mydownload\Anaconda3\envs\tensorflow\Lib\site-packages\
      tensorflow_core\python\keras\ callbacks.py

      参考链接

    • 修改4:文件目录不存在,手动新建—项目根目录\logs\train\plugins\profile

  3. 运行ok了,

    之前因为我生成.h5时,修改了yolov3.cfg,出现nan现象:

    ctrl+c中断,别担心,我们每轮h5都有保存,可以继续学习:
    参考Keras 搭建自己的yolo3目标检测平台,就是把train.py中:

    • initial_epoch改为你预先训练的轮数
    • 预训练模型model_data/yolo_weights.h5改为logs下已训练保存的版本

    防止中途断掉前功尽弃。

    成功运行效果:
    最终loss降到了21.5,效果不是很理想;在5,6最大到10左右,才是进行预测的最好模型!

拥有模型就等于完成了作业,骄傲!

(3)优化

学霸开始发言: (这里,这里和那里都可以用更优的算法)

学渣的做法: (玩命标注和训练)

  • 步骤1 :调整图像标注 (增加图片的丰富性)
  • 步骤3 :修改train.py 的训练次数epochs

参考学习:【YOLO】使用VOC数据集训练自己的YOLOv3模型(Keras/TensorFlow)

三、图像识别

老师需要检查yolo同学的学习效果,接下来安排考试。 这是测试阶段
yolo同学要加油啊!

参考https://github.com/qqwweee/keras-yolo3/blob/master/yolo.py
可直接运行yolo.py 和yolo_vedio.py

这里yolo.py调用了nets和utils辅助函数、注意模型相关路径、调整置信度是绘图的关键(避免检测不到框或者绘制很多框):

这里单独测试:predict.py
(注意你的图片路径)

也可以直接检测:

python yolo_video.py --input D:\myworkspace\dataset\test.mp4

最后,我自己的检测效果:65%的准确率,哈哈哈有待加强!

(这里用model_data/yolo_weights.h5原始模型,效果是100%🤣)

python yolo_video.py --input D:\myworkspace\dataset\xuanya.mp4

视频检测结果:


相信我已经掌握 图像标注、训练、识别 全部流程!

继续修改、训练,直到得到满意的分数!


  1. 警告:FutureWarning: Passing (type, 1) or '1type' as a synonym of type is deprecated; in a future version of numpy, it will be understood as (type, (1,)) / '(1,)type'.
    解决:参考链接

转载:https://blog.csdn.net/cungudafa/article/details/105074825
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场