一、前言
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
源码源码:
二、训练集标注
老师备课阶段——图片是课程内容,标注框时知识点。
在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)图像标注
具体的备课内容——知识点就是标注方框信息。
-
准备
下载完成后是一个可执行exe文件,注意存放到一个非中文的路径下。
同时,我创建了三个文件夹:- photos:存放原图片
这里我存放了5张小兔子的图片,可参考Python爬取百度图片并保存到本地。
- Annotations:存放xml标注信息文件
- JPEGImages:存放压缩图片
- photos:存放原图片
-
标注
运行labelImage,调整到合适大小;熟记使用6步骤:
打开文件夹(photos)-》设置保存文件夹(annotations)
设置自动保存(view -》auto Saving)-》标注框(Create RectBox)并命名
快捷键A保存xml-》快捷键D下一张
附:(labelImg快捷键表)
标注示例(轮廓对齐原则):
(建议从左下角标注到右上角,因为在图片读取中顺序是(x1,y1)到(x2,y2))
标签英文!!!因为后面在python读取不出错
快捷键A,D之后:Annotations
文件夹保存了我们标记的文件,(之后我们训练时会用到这个文件夹)
问:yolo需要标注多少张图片呢?
答:取决于你的数据集,简单的几千张
就够,复杂的就要比较大了。
说明:YOLOv3的样本都不需要负样本
,只需要你标出目标物体就行了。但是为了提高准确率需要注意一下三点了:
- 对目标的大小也没多大要求,但是不能太小(比如小到只有七八个像素点)
- 样本足够多(各种大小、角度最好都来点)
- 标注的时候一定要仔细,不要图快,不然后面要返工(亲身经历),bounding box要刚好圈住目标物体(当然你想同时识别物体的局部的话那么同一局部的图片也要足够多)
- 在复杂的场景下面,也可以添加负样本,也就是说一张图片里面没有目标物体,样本对应的标签只要给一个空的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)训练
课后作业
-
下载源码
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上查看)
-
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
-
-
运行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同学的学习效果,接下来安排考试。 这是测试阶段
。
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
视频检测结果:
相信我已经掌握 图像标注、训练、识别 全部流程!
继续修改、训练,直到得到满意的分数!
- 警告:
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