YOLOV3–训练数据+视频检测

背景

当今,深度学习、人工智能是一个很火的方向。深度学习更是开启了机器视觉的新方向,如果要做物体识别,目标检测,你完全不用根据不同对象而重新设计算法(这是传统机器视觉的做法),它是一个黑箱,只需要制作好数据集,学会调参就可以很快的应用到你的项目中。

为什么是yolov3

关于目标检测,深度学习的算法有很多,有 two stage 的算法,它们的识别精度会相对于one stage 的算法高,但是在识别速率上并没有那么高。典型的one stage (我理解它为单阶段,卷积之后直接推理出结果,two stage则不是直接推理出来):YOLO系列 和SSD。如下图所示。用最简单易懂的话来总结yolo系列算法的关系,特点。
yolov1结构图如下。最终一张图片经过这个算法之后,输出只有一个7* 7 *30的张量,其中每一个grid cell单独拎出来开始一个类似于一条数组,有两个box(每个box用来预测一类目标,每个box有5个元素,分别是预测框的x、y坐标、框、高、置信度(概率)),后面就是这套模型对应的各个目标的得分。因为最终的gridcell代表的是原图像的一块很大的面积,而只有两个box来预测,因而会导致小物体的识别效果不好,甚至识别不出来,所以yolov1很快就被淘汰了。


YOLO v2的改进:
1.对网络每一层做输出做归一化。这样,输出被划分到 [0,1] 闭区间。不需要每层学习数据的分布,收敛速度加快。并且,解放了dropout,使mAP值提升。
预训练阶段v1使用224 * 224的imageNet数据集进行,然后检测阶段换了分辨率为448 * 448;这需要网络对尺寸的变换进行适应,可能会导致变换过程中权值更新不完全,降低检测时的mAP值;v2在224 * 224上预训练,又使用448*448进行适应性训练,最后使用448 * 448进行测试,能够缓解这一问题的发生。前两步均在ImageNet数据集上进行,后一步在自己的数据集上进行。
2.将Darknet-19网络的全连接层和最后一个pooling层去掉,使得最后的卷积层可以有更高分辨率的特征。416 * 416代替原来448 * 448,最终输入变成13 * 13大小的feature map输出。每个grid cell 给定 9 个框,由v1的7 * 7 * 2(30维向量中的前10个特征)变为13 * 13 * 9。这样可以提高定位准确性。
采用k-means的方式对训练集的bounding boxes做聚类,试图找到合适的anchor box,发现仅选取5种box就能达到Faster RCNN的9中box的效果。
每个bounding box预测5个值:x,y,w,h和o。x,y使用sigmoid函数进行归一化到(0,1)之间,这样处理使得模型训练更加稳定;添加一个 passthrough layer 层。即将前面一层的26 * 26的feature map和本层的13 * 13的feature map进行连接,有点像ResNet。经过层层卷积和pooling,小的object可能到最后都不见了,通过连接可以有效检测小的object。进行多尺度训练,在训练时,每训练10个batch,网络就会随机选择{320,352,…,608}中的另一种size的输入。size为32的倍数。这使得该网络可以检测不同尺寸的图像。

yolov3网络模型图如下:

1.它采用了darknet53代替darknet19;
2.生成了1313255,2626255,5252255三个尺度特征图;
3.采用9个尺寸的锚框,分别大、中、小;
4.对预测边框采用了逻辑回归预测;
5.能够检测出小物体;
6.识别速度快;
7.yolov3的每个grid cell有三个box,每个grid cell可以预测3个目标,与之前不同的是,对应元素并不是yolov1中放的预测框的坐标和框高,而是预测框的坐标框高与实际打标时的框的坐标框高之间的偏移量。如下图


上图公式中tx,ty,tw,th分别是预测框的坐标宽高的偏移量,Gx,Gy,Gw,Gh分别是真实框的坐标宽高,Px,Py,Pw,Ph分别是预测框的坐标宽高,上面的公式中比上Pw,Ph、取对数是因为要相对化处理,可以理解为归一化,因为,预测框有可能是预测大物体,那框就会很大,预测小物体,框就会很小,相对化处理后就会避免这种很极端情况的发生。
训练模型是就是不断训练逼近期望值,最终得到使上面公式中tx,ty,tw,th的值为零,损失值最小的那套权重。测试时,将box中的参数代入到上式中,就可以实现逻辑回归,大概的意思是偏移量代入到逻辑回归公式,就可以实现对锚框的平移、缩放,使之与真实边界框尽可能重叠。这是他边界回归预测的一个想法。

ubantu16.04环境下

下面说一下步骤:
我试下ubantu16.04下操作的,自己安装一下该系统:
安装完后先按着这个网站操作一下,里面会告诉你关于这个一开怎么用yolov3,按着步骤跑一遍。

这是一个用yolov3来跑雪人识别的,下面是我识别的效果:



经过了上面这个步骤,你应该会对怎么用它有自己一个理解,算是入门吧。

训练自己的数据集

1.下载labalimg这个软件;
2.网上下载图片,关于自己的数据集的收集,新建一个文件夹,存放图片;

3.新建另一个文件夹,存放打标之后的文件设置好保存的路径;

4.开始漫长的打标之旅,图片收集要多一些,越多越好,w:是开始画框的快捷键,d:是下一张照片,a :是上一张照片,空格是:确认照片,记得打标之后保存;

5.在对应的文件夹中建立如下文件夹,其中JPEGlmages放的是你数据集中的照片,labels是放你打标之后的xml文件,Annottations放的是xml文件转换成的txt文件,lmageSets文件夹下新建一个main文件夹。它是一个路径,最终在main文件夹下会生成4个txt文件,指示训练集测试集的路径。


下面这个py文件是将打标后xml文件转换成txt文件,这里需要注意更改相应的文件路径。

import os
import sys
import xml.etree.ElementTree as ET
import glob
#将打标之后的xml文件转换成txt文件
def xml_to_txt(indir,outdir):os.chdir(indir)annotations = os.listdir('.')annotations = glob.glob(str(annotations)+'*.xml')for i, file in enumerate(annotations):file_save = file.split('.')[0]+'.txt'file_txt=os.path.join(outdir,file_save)f_w = open(file_txt,'w')# actual parsingin_file = open(file)tree=ET.parse(in_file)root = tree.getroot()for obj in root.iter('object'):current = list()name = obj.find('name').textxmlbox = obj.find('bndbox')xn = xmlbox.find('xmin').textxx = xmlbox.find('xmax').textyn = xmlbox.find('ymin').textyx = xmlbox.find('ymax').text#print xnf_w.write(xn+' '+yn+' '+xx+' '+yx+' '+name+'\n')#f_w.write(str(name.encode("utf-8"))+"\n")indir='/home/zj/mylabal'   #xml目录
outdir='/home/zj/darknet2(2019.12.09)/scripts/VOCdevkit/VOC2007/labels'  #txt目录xml_to_txt(indir,outdir)

下面的代码是用于分配数据集和测试集的比例的一个py文件。同样需要将路径更改成对应自己的文件路径。

import os
import random#用于分配训练集的比例权重,生成main下的各个文件
trainval_percent = 0.5
train_percent = 0.5
xmlfilepath = '/home/ZJ/文档/darknet2_2019_12_09/scripts/VOCdevkit/VOC2007/Annotations'
txtsavepath = '/home/ZJ/文档/darknet2_2019_12_09/scripts/VOCdevkit/VOC2007/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(txtsavepath+'/trainval.txt', 'w')
ftest = open(txtsavepath+'/test.txt', 'w')
ftrain = open(txtsavepath+'/train.txt', 'w')
fval = open(txtsavepath+'/val.txt', 'w')for i  in list:name=total_xml[i][:-4]+'\n'if i in trainval:ftrainval.write(name)if i in train:ftrain.write(name)else:fval.write(name)else:ftest.write(name)ftrainval.close()
ftrain.close()
fval.close()
ftest .close()

还有更改voc.data文件 ,还有name文件(在data文件夹下)。voc.data 文件存放着指示你训练集测试集的路径。name文件指示着你训练的类别,如果测试时发现标签贴错了,可以更改name文件下名称的顺序。


之后就可以开始训练了,train_yolov3.log是日志文件,用于后面打印损失函数曲线图用。

./darknet detector train cfg/voc.data cfg/yolov3-voc.cfg darknet53.conv.74 | tee visualization/train_yolov3.log     ##训练指令
./darknet detector test cfg/voc.data cfg/yolov3-voc.cfg backup/yolov3-voc_10000.weights data/80.jpg                                                                                                                 ##测试指令

开始漫长的训练之后,当训练中日志的avg参数的值小于0.1 即0.0几时可以视为训练可以结束了。在上面的测试指令中,要根据你训练得到的权重文件的路径,更改,以及你测试照片路径也要相应更改。打印loss曲线还有iou图如下,集体怎么打印网上资料很多。


测试如下:(照片是在网上找的。如有侵犯,请联系我。)


视频检测


流程如上面,代码如下,放在python文件夹下。需要将训练后的权重文件路径,视频文件路径,合成视频存放路径改成相应的路径。视频合成时,帧数要要设置好,才可以使合成视频和识别的视频匹配。这里我将识别的类别用不同颜色框起来显示。同时,yolov3处理图片是按rgb的顺序进行,opencv处理图片是bgr的顺序,所以也需要调整顺序,不然识别后的照片会颜色失真。我的处理是在运用opencv处理图片之前将图片的通道调整为bgr的顺序。

#coding=UTF-8
from ctypes import *
import math
import random
#import module named cv2 to draw
import cv2
import Imagedef sample(probs):s = sum(probs)probs = [a/s for a in probs]r = random.uniform(0, 1)for i in range(len(probs)):r = r - probs[i]if r <= 0:return ireturn len(probs)-1def c_array(ctype, values):arr = (ctype*len(values))()arr[:] = valuesreturn arrclass BOX(Structure):_fields_ = [("x", c_float),("y", c_float),("w", c_float),("h", c_float)]class DETECTION(Structure):_fields_ = [("bbox", BOX),("classes", c_int),("prob", POINTER(c_float)),("mask", POINTER(c_float)),("objectness", c_float),("sort_class", c_int)]class IMAGE(Structure):_fields_ = [("w", c_int),("h", c_int),("c", c_int),("data", POINTER(c_float))]class METADATA(Structure):_fields_ = [("classes", c_int),("names", POINTER(c_char_p))]#lib = CDLL("/home/pjreddie/documents/darknet/libdarknet.so", RTLD_GLOBAL)
lib = CDLL("/home/ZJ/文档/darknet2_2019_12_09/libdarknet.so", RTLD_GLOBAL)
lib.network_width.argtypes = [c_void_p]
lib.network_width.restype = c_int
lib.network_height.argtypes = [c_void_p]
lib.network_height.restype = c_intpredict = lib.network_predict
predict.argtypes = [c_void_p, POINTER(c_float)]
predict.restype = POINTER(c_float)set_gpu = lib.cuda_set_device
set_gpu.argtypes = [c_int]make_image = lib.make_image
make_image.argtypes = [c_int, c_int, c_int]
make_image.restype = IMAGEget_network_boxes = lib.get_network_boxes
get_network_boxes.argtypes = [c_void_p, c_int, c_int, c_float, c_float, POINTER(c_int), c_int, POINTER(c_int)]
get_network_boxes.restype = POINTER(DETECTION)make_network_boxes = lib.make_network_boxes
make_network_boxes.argtypes = [c_void_p]
make_network_boxes.restype = POINTER(DETECTION)free_detections = lib.free_detections
free_detections.argtypes = [POINTER(DETECTION), c_int]free_ptrs = lib.free_ptrs
free_ptrs.argtypes = [POINTER(c_void_p), c_int]network_predict = lib.network_predict
network_predict.argtypes = [c_void_p, POINTER(c_float)]reset_rnn = lib.reset_rnn
reset_rnn.argtypes = [c_void_p]load_net = lib.load_network
load_net.argtypes = [c_char_p, c_char_p, c_int]
load_net.restype = c_void_pdo_nms_obj = lib.do_nms_obj
do_nms_obj.argtypes = [POINTER(DETECTION), c_int, c_int, c_float]do_nms_sort = lib.do_nms_sort
do_nms_sort.argtypes = [POINTER(DETECTION), c_int, c_int, c_float]free_image = lib.free_image
free_image.argtypes = [IMAGE]letterbox_image = lib.letterbox_image
letterbox_image.argtypes = [IMAGE, c_int, c_int]
letterbox_image.restype = IMAGEload_meta = lib.get_metadata
lib.get_metadata.argtypes = [c_char_p]
lib.get_metadata.restype = METADATAload_image = lib.load_image_color
load_image.argtypes = [c_char_p, c_int, c_int]
load_image.restype = IMAGErgbgr_image = lib.rgbgr_image
rgbgr_image.argtypes = [IMAGE]predict_image = lib.network_predict_image
predict_image.argtypes = [c_void_p, IMAGE]
predict_image.restype = POINTER(c_float)def classify(net, meta, im):out = predict_image(net, im)res = []for i in range(meta.classes):res.append((meta.names[i], out[i]))res = sorted(res, key=lambda x: -x[1])return res
#检测 第三个参数image是照片的路径
def detect(net, meta, image, thresh=.5, hier_thresh=.5, nms=.45):im = load_image(image, 0, 0) #加载图片得到im对象num = c_int(0)pnum = pointer(num)predict_image(net, im)dets = get_network_boxes(net, im.w, im.h, thresh, hier_thresh, None, 0, pnum)num = pnum[0]if (nms): do_nms_obj(dets, num, meta.classes, nms);res = []for j in range(num):for i in range(meta.classes):if dets[j].prob[i] > 0:b = dets[j].bboxres.append((meta.names[i], dets[j].prob[i], (b.x, b.y, b.w, b.h)))res = sorted(res, key=lambda x: -x[1])free_image(im)free_detections(dets, num)return res# 2018.04.25      用于绘制物体框
def showPicResult(image):img = cv2.imread(image)img3=img[...,::-1]cv2.imwrite(out_img, img3)for i in range(len(r)):x1=r[i][2][0]-r[i][2][2]/2y1=r[i][2][1]-r[i][2][3]/2x2=r[i][2][0]+r[i][2][2]/2y2=r[i][2][1]+r[i][2][3]/2im = cv2.imread(out_img)#draw different color rectangl#random color#r_color = random.randint(0,255)#随机赋值#g = random.randint(0,255)#b = random.randint(0,255)#cv2.rectangle(im,(int(x1),int(y1)),(int(x2),int(y2)),(r_color,g,b),2)#画框if r[i][0] =="person":cv2.rectangle(im,(int(x1),int(y1)),(int(x2),int(y2)),(0,0,255),3)R=255g=0b=0if r[i][0] =="motor":cv2.rectangle(im,(int(x1),int(y1)),(int(x2),int(y2)),(0,255,0),3)R=0g=255b=0if r[i][0] =="car":cv2.rectangle(im,(int(x1),int(y1)),(int(x2),int(y2)),(255,0,0),3)R=0g=0b=255#cv2.rectangle(im,(int(x1),int(y1)),(int(x2),int(y2)),(255,0,0),3)#putText  贴标x3 = int(x1+5)y3 = int(y1-10)x4 = int(x1)y4 = int()font = cv2.FONT_HERSHEY_SIMPLEXif ((x3<=im.shape[0]) and (y3>=0)):im2 = cv2.putText(im, str(r[i][0])+":"+str(round((r[i][1]*100),2))+"%", (x3,y3), font, 0.8, (b,g,R) , 2)else:im2 = cv2.putText(im, str(r[i][0])+":"+str(round((r[i][1]*100),2))+"%", (int(x1),int(y1+6)), font, 0.8, (b,g,R) , 2)#This is a method that works well. #img2=im[...,::-1]cv2.imwrite(out_img, im)#img2=img[...,::-1]#cv2.imwrite(out_img, img2)#img4 = cv2.imread(out_img)#img5 = img4[...,::-1]    cv2.imshow('yolo_image_detector',cv2.imread(out_img))if __name__ == "__main__":net = load_net("/home/ZJ/文档/darknet2_2019_12_09/cfg/yolov3-voc.cfg", "/home/ZJ/文档/darknet2_2019_12_09/backup/yolov3-voc_10000.weights", 0)meta = load_meta("/home/ZJ/文档/darknet2_2019_12_09/cfg/voc.data")#加载路径out_img = "/home/ZJ/文档/darknet2_2019_12_09/video/test_result.jpg"video_tmp = "/home/ZJ/文档/darknet2_2019_12_09/video/video_tmp.jpg"#加载路径,导入对象origin_video = '/home/ZJ/文档/darknet2_2019_12_09/video/123.mp4'#读取本地视频# make a video_object and init the video objectcap = cv2.VideoCapture(origin_video)# define picture to_down' coefficient of ratioscaling_factor = 0.5#0.5count = 0img = cv2.imread(video_tmp)imgInfo = img.shapep_num = 0size = (imgInfo[1],imgInfo[0])  #获取图片宽高度信息fourcc= cv2.cv.FOURCC(*'XVID')#使用XVID编码器videoWrite = cv2.VideoWriter('/home/ZJ/文档/darknet2_2019_12_09/video/final_out_video.avi',fourcc,29,size)# 根据图片的大小,创建写入对象 (文件名,支持的编码器,5帧,视频大小(图片大小))# loop until press 'esc' or 'q'while (cap.isOpened()):# collect current frameret, frame = cap.read()if ret == True:count = count + 1#print countelse:break#detect and show per 50 frames  #照片抽帧成图片if count == 1:count = 0# resize the frameframe = cv2.resize(frame,None,fx=scaling_factor,fy=scaling_factor,interpolation=cv2.INTER_AREA)img_arr = Image.fromarray(frame)img_goal = img_arr.save(video_tmp)r = detect(net, meta,video_tmp)#检测,将结果赋值给r  r是list类型#print rfor j in range(len(r)):print "检测结果:",r[j][0], "概率",' : ', round((100*r[j][1]),2),"%"print r[j][2]print ''print '#*********************************#'#display the rectangle of the objects in windowshowPicResult(video_tmp)p_num = p_num+1fileName = "/home/ZJ/文档/darknet2_2019_12_09/video/test_result.jpg"   #循环读取所有的图片ig = cv2.imread(fileName)#b,g,r=cv2.split(ig)#img2=cv2.merge([r.g.b])#img2=ig[...,::-1]videoWrite.write(ig)# 将图片写入所创建的视频对象#print igelse:continue# wait 1ms per iteration; press Esc to jump out the loop c = cv2.waitKey(1)if (c==27) or (0xFF == ord('q')):break# release and close the display_windowprint "共检测照片数量:",p_numcap.release()

不允许上传太大的图片动图,所以,我测试了一段小视频,最终效果如下:

写在最后

做数据集的时候样本尽量大,1万张照片以上比较好,我只做了快4百张照片的一个数据集,所以有些元素并很好地没有识别出来。
本人也是刚接触目标检测,文笔有限,欢迎批评指正~
下一篇,应该是《细谈Resnet残差网络》~

YOLOV3--训练数据+视频检测相关推荐

  1. 手把手教你用YOLOv5算法训练数据和检测目标(不会你捶我)

    前言 本人从一个小白,一路走来,已能够熟练使用YOLOv5算法来帮助自己解决一些问题,早就想分析一下自己的学习心得,一直没有时间,最近工作暂时告一段落,今天抽空写点东西,一是为自己积累一些学习笔记,二 ...

  2. pytorch版本下的yolov3训练实现火焰检测

    时隔好多好多日子了,一直没写博客(小声bb,最近忙着接私活儿).马上就要开学了,害,回去就要加油干了!!! 本次教程写个pytorch版本的yolov3检测,用的火焰检测数据集,效果如下: 这就可以做 ...

  3. (YOLOv3训练超详细教程)在Ubuntu 18.04下使用YOLOv3训练数据(提供源码及文件夹)

    一.环境 Ubuntu 18.04 + CUDA10.0 +CUDNN 下方是我训练时的真实文件,可供大家参考 大家需要我的文件资源的请使用曲奇云盘下载,下面是下载链接:https://quqi.gb ...

  4. CenterNet:Objects as Points论文学习笔记+代码复现(demo+训练数据)【检测部分】

    目录 1.关键部分Heatmap了解 2.Centernet论文细节: 3.尝试复现CneterNet--INSTALL.md安装: 4.尝试复现CneterNet--跑跑demo.py: 5.尝试复 ...

  5. yolov3目标检测android,目标检测 | YOLOv3训练自己的数据全流程

    1.构建YOLOv3网络的cfg文件 该文件表示的是你的检测网络的结构,类似caffe的prototxt文件. YOLOv3的cfg文件 上篇介绍YOLOv3网络中提到的去掉上采样操作的YOLOv3c ...

  6. 实用教程!使用YOLOv3训练自己数据的目标检测

    点击我爱计算机视觉标星,更快获取CVML新技术 YOLOv3是当前计算机视觉中最为流行的实时目标检测算法之一. 昨天LearnOpenCV网站博主又发福利,post了一个清晰明了的教程,一步一步示例, ...

  7. 【YOLOv3从头训练 数据篇】

    YOLOv3从头训练 数据篇 前言 数据下载 数据可视化 标签生成 生成训练路径文件 结语 前言 最近在忙着怎么从头实现YOLOv3,从网上找了很多教程,也在GitHub上面找到了挺多的代码的,有些能 ...

  8. Tensorflow目标检测之yolov3训练自己的模型

    0 背景 Tensorflow官方目标检测给出了SSD.Faster rcnn等预训练模型,但没有给yolov3的预训练模型,但有热心的人已经实现了基于tensorflow来复现yolov3的算法,本 ...

  9. YOLOv3训练自己的数据(附优化与问题总结)

    文章目录 YOLOv3训练自己的数据附优化与问题总结 环境说明 实现YOLOV3的demo 命令简介 训练自己的数据 优化与个性化问题 2020.3.11 更新可视化和python接口 可能出现的问题 ...

最新文章

  1. TVM Operator Inventory (TOPI)简介
  2. Linux环境下的网络编程
  3. QIIME 2教程. 32如何写方法和引用Citing(2021.2)
  4. 在分类的意义上最稳定的物体是什么?
  5. k8s minikube部署hbase
  6. 学习算法你必须知道的一些基础知识(文末福利)
  7. 乐鑫ESP32开发 1.Vscode创建新工程,编译,下载烧录,监视端口,点亮一个LED
  8. strong和weak 细节
  9. Qt 设置应用程序图标(windows)
  10. HadoopLearning
  11. 虚拟机设置固定ip,ping不通,xshell无法连接
  12. 最好用音频剪辑的软件,使用方法?
  13. Unity使用Animator.CrossFade后,脚本的OnExitState函数还执行吗
  14. 相比于 Java 10 的 var, 更期待 Java 11 支持多行字符串
  15. sql实现查询两个时间之间每月的数量
  16. C语言半框,不同的镜架结构优劣大盘点
  17. 使用fir im进行内测托管
  18. RocketMQ的长轮询消费方式
  19. 19.Eager模式
  20. GraphQL标准的Java实现graphql-java概述

热门文章

  1. 终于解决qq浏览器里面播放video会带自己广告的问题了
  2. 推荐几个笔记类APP,自学提示必备
  3. 计算机组成原理实验报告JMPR,计算机组成原理实验四:组合逻辑控制器部件教学实验...
  4. 计算机网络码片序列计算问题
  5. Flask入门---@app.route()使用
  6. I2C的基础概念和框架
  7. 《王者荣耀》伤害计算(数值研究)
  8. Android开发工程师常见面试题整理
  9. 产品vs程序员:你知道www是怎么来的吗?
  10. 加州大学欧文计算机排名,2019加州大学欧文分校排名(USNews排名)