以下链接是个人关于YOLO V3所有见解,如有错误欢迎大家指出,我会第一时间纠正,如有兴趣可以加微信:17575010159 相互讨论技术。 目标检测0-00:YOLO V3目录-史上最全
一、源码目录总览tensorflow-yolov3-master-bk
├── checkpoint //保存模型的目录
├── convert_weight.py//对权重进行转换,为了模型的预训练
├── core//核心代码文件夹
│ ├── backbone.py
│ ├── common.py
│ ├── config.py//配置文件
│ ├── dataset.py//数据处理
│ ├── __init__.py
│ ├── __pycache__
│ │ ├── backbone.cpython-36.pyc
│ │ ├── common.cpython-36.pyc
│ │ ├── config.cpython-36.pyc
│ │ ├── dataset.cpython-36.pyc
│ │ ├── __init__.cpython-36.pyc
│ │ ├── utils.cpython-36.pyc
│ │ └── yolov3.cpython-36.pyc
│ ├── utils.py
│ └── yolov3.py//网络核心结构
├── data
│ ├── anchors//预训练框
│ │ ├── basline_anchors.txt
│ │ └── coco_anchors.txt
│ ├── classes//训练预测目标的种类
│ │ ├── coco.names
│ │ └── voc.names
│ ├── dataset//保存图片的相关信息:路径,box,置信度,类别编号
│ │ ├── voc_test.txt//测试数据
│ │ └── voc_train.txt//训练数据
│ └── log//存储log
│ └── events.out.tfevents.1564706916.WIN-RCRPPSUQJFP
├── docs//比较混杂
│ ├── Box-Clustering.ipynb//根据数据信息生成预选框anchors
│ ├── images
│ │ ├── 611_result.jpg
│ │ ├── darknet53.png
│ │ ├── iou.png
│ │ ├── K-means.png
│ │ ├── levio.jpeg
│ │ ├── probability_extraction.png
│ │ ├── road.jpeg
│ │ ├── road.mp4
│ │ └── yolov3.png
│ └── requirements.txt//环境搭建
├── evaluate.py//模型评估
├── freeze_graph.py//生成pb文件
├── image_demo.py//一张图片测试的demo
├── LICENSE
├── LICENSE.fuck
├── mAP//模型评估相关信息存储
│ ├── extra
│ │ ├── class_list.txt
│ │ ├── convert_gt_xml.py
│ │ ├── convert_gt_yolo.py
│ │ ├── convert_keras-yolo3.py
│ │ ├── convert_pred_darkflow_json.py
│ │ ├── convert_pred_yolo.py
│ │ ├── find_class.py
│ │ ├── intersect-gt-and-pred.py
│ │ ├── README.md
│ │ ├── remove_class.py
│ │ ├── remove_delimiter_char.py
│ │ ├── remove_space.py
│ │ ├── rename_class.py
│ │ └── result.txt
│ ├── __init__.py
│ └── main.py
├── README.md
├── scripts
│ ├── show_bboxes.py
│ └── voc_annotation.py//把xml转化为网络可以使用的txt文件
├── train.py//模型训练
└── video_demo.py//视屏测试的demo
上述没有注释部分,后续再填充,下面的讲解都是基于VOC数据
二、YOLO V3输入当我们拿到一个网络得时候,首先我们要知道一个网络得输入和输出是什么,这样我们能尽快的理解这个网络。按照大佬源码中的操作,运行scripts/voc_annotation.py之后,我们得到data/dataset中的voc_test与voc_train文件,其格式如下:
../VOC/train/VOCdevkit/VOC2007\JPEGImages\000005.jpg 263,211,324,339,8 165,264,253,372,8 241,194,295,299,8
首选是图像的路径,然后是图像中所包含box左上角和右下角的坐标以及其所属类别,即路径后面,每五个数字,可以表示一个box,以及这个box对应类别的编号。有了这样的数据信息之后我们就能进行训练了,即能够执行train.py文件。
再继续讲解之前,我们需要了解一下yolo V3的结构,yoloV3不同于之前的yolo1与yolo2,其使用了图像金字塔的思想,对一张图片进行了3次降采样,分别为8,16,32。表示我们输入的图像必须是32的倍数,不然没有办法进行32倍的降采样。如果对yolo1,与yolo2不熟悉,可以看看以下的文章: YOLOv1到YOLOv3的演变过程及每个算法详解 总的来说,其就是把一张图片分成了很多grid(网格)或者cell(细胞)。就拿yolo1来说,假设一张下面的图片: 这张图片的大小为(448,448),其被划分之后变成(7,7)。即每个每个gred都代表着(64,64)的视野。YOLO1会对每个gred进行两个box的检测,每个box所包含的信息如下:box的坐标(4个数字),该box物体的置信度(1个数字),该box所属于各种类别的概率(有多个类别就需要多好数字表示,如果VOC数据,则为20个)。所以我们最后要描述一张图像的信息需要(7,7,(4+1)*2+20)=(7,7,30)的矩阵。这样我们可以描述出来一张图片的信息,如下:
上面是YOLO1的处理,对于YOLO3的原理也是类似的,只是YOLO3采用了图像金字他的思想,做了3次变化,如假设输入一张(416,416)的图片,经过3次(8,16,32)下采样变换之后为sacel[(52,52), (26,26), (13,13)]。可以这样理解,一张图片使用3种方式进行描述:8倍下采样得到特征图,每个网格可以代表原图种8个像素的感受野。16倍下采样得到特征图,每个网格可以代表原图种16个像素的感受野。32倍下采样得到特征图,每个网格可以代表原图种32个像素的感受野(重复了一些废话,大家不要介意)。
那么同样的道理,一张图片根据不同的划分,我们使用3种方式去描述,每种方式,我们对其中的每个gred都要进行3次预测,是的,在YOLO3中,其会对每个网格进行3中预测。每次预测和YOLO1一样,需要box的坐标(4个数字),该box物体的置信度(1个数字),该box所属于各种类别的概率。那么对于 方式一8倍降采样需要[52,52,3*((4+1)+20)]=[52,52,75] 方式一16倍降采样需要[26,26,3*((4+1)+20)]=[26,26,75] 方式一32倍降采样需要[26,26,3*((4+1)+20)]=[13,13,75] 讲解到这里,也差不多了,如果想详细了解,可以通过一下两篇文章(再这里表示对大佬们的感谢) 目标检测之YoloV1论文及tensorflow实现 搞懂YOLO v1看这篇就够了
现在看看我们的网络输入需要什么,再train.py文件中可以找到如下:
self.input_data = tf.placeholder(dtype=tf.float32, name='input_data') #图片的像素
#8倍下采样之后图片对应的box(每个gred,通过anchors变换)
self.label_sbbox = tf.placeholder(dtype=tf.float32, name='label_sbbox')
#16倍下采样之后图片对应的box(每个gred,通过anchors变换)
self.label_mbbox = tf.placeholder(dtype=tf.float32, name='label_mbbox')
#32倍下采样之后图片对应的box(每个gred,通过anchors变换)
self.label_lbbox = tf.placeholder(dtype=tf.float32, name='label_lbbox')
#8倍下采样之后图片对应真实的box,没有通过anchors变换
self.true_sbboxes = tf.placeholder(dtype=tf.float32, name='sbboxes')
#16倍下采样之后图片对应真实的box,没有通过anchors变换
self.true_mbboxes = tf.placeholder(dtype=tf.float32, name='mbboxes')
#32倍下采样之后图片对应真实的box,没有通过anchors变换
self.true_lbboxes = tf.placeholder(dtype=tf.float32, name='lbboxes')
self.trainable = tf.placeholder(dtype=tf.bool, name='training')
我相信大家现在对anchors已经十分好奇了,那么他到底是什么呢?如果等不急的朋友可以观看下面链接: YOLO-v3模型参数anchor设置 其就是一个预验框,那么什么是预验框呢?简单的来说,就是我们希望训练出来的后的网络,在对数据进行预测的时候,不喜欢他预测的框是千奇百怪的,所以我们给定一些框让网络去学习,即以后预测的框,尽量和我们给出的框比较接近。这里的框表示的是长度,和宽度。后面会详细的讲解,我们继续往下,慢慢就明白是怎么回事了。
placeholder表示的是占位符,那么他的数据到底是怎么来的呢?根据train.py中的pbar = tqdm(self.trainset),一路追踪下去,可以知道其数据的预处理在core/dataset.py完成,其代码注释如下:
#! /usr/bin/env python
# coding=utf-8
#================================================================
# Copyright (C) 2019 * Ltd. All rights reserved.
#
# Editor : VIM
# File name : dataset.py
# Author : YunYang1994
# Created date: 2019-03-15 18:05:03
# Description :
#
#================================================================
import os
import cv2
import random
import numpy as np
import tensorflow as tf
import core.utils as utils
from core.config import cfg
np.set_printoptions(suppress=True, threshold=np.nan)
class Dataset(object):
"""implement Dataset here"""
def __init__(self, dataset_type):
# 数据注释文件的路径,此处为"./data/dataset/voc_test.txt"
self.annot_path = cfg.TRAIN.ANNOT_PATH if dataset_type == 'train' else cfg.TEST.ANNOT_PATH
# 数据输入图像的大小,为了增加网络的鲁棒性,使用了随机[320, 352, 384, 416, 448, 480, 512, 544, 576, 608]
# 中任意一种大小,注意,该处必须为32的倍数
self.input_sizes = cfg.TRAIN.INPUT_SIZE if dataset_type == 'train' else cfg.TEST.INPUT_SIZE
# 数据的BATCH_SIZE,由于本人电脑不好,故设置为1
self.batch_size = cfg.TRAIN.BATCH_SIZE if dataset_type == 'train' else cfg.TEST.BATCH_SIZE
# 是否开启AUG数据增强,该处为True
self.data_aug = cfg.TRAIN.DATA_AUG if dataset_type == 'train' else cfg.TEST.DATA_AUG
# 训练数据输入大小
self.train_input_sizes = cfg.TRAIN.INPUT_SIZE
# 3中下采样方式,为[8, 16, 32]
self.strides = np.array(cfg.YOLO.STRIDES)
# 训练数据的类别,使用VOC数据共20中,来自"./data/classes/voc.names"
self.classes = utils.read_class_names(cfg.YOLO.CLASSES)
# 种类的数目,针对VOC为20
self.num_classes = len(self.classes)
# 来自于"./data/anchors/basline_anchors.txt",该文件的生成于docs/Box-Clustering.ipynb
self.anchors = np.array(utils.get_anchors(cfg.YOLO.ANCHORS))
# 对每个gred(网格)预测几个box,该处为3
self.anchor_per_scale = cfg.YOLO.ANCHOR_PER_SCALE
# 一张图像中,允许存在最多的box数数目
self.max_bbox_per_scale = 150
# 加载数据,该处为训练数据,即"./data/classes/voc.names"内容
self.annotations = self.load_annotations(dataset_type)
# 计算训练样本的总数目
self.num_samples = len(self.annotations)
# 需要多个num_batchs才能完成一个EPOCHS
self.num_batchs = int(np.ceil(self.num_samples / self.batch_size))
# 用于batch的技术,达到num_batchs代表训练了一个EPOCHS
self.batch_count = 0
def load_annotations(self, dataset_type):
"""
随机读取"./data/classes/voc.names"中的内容
:param dataset_type:
:return:
"""
with open(self.annot_path, 'r') as f:
txt = f.readlines()
annotations = [line.strip() for line in txt if len(line.strip().split()[1:]) != 0]
np.random.shuffle(annotations)
return annotations
def __iter__(self):
return self
def __next__(self):
"""
数据处理的核心函数,为了形象的表达。
:return:
"""
with tf.device('/cpu:0'):
# 从给定的[320, 352, 384, 416, 448, 480, 512, 544, 576, 608]中随机选择大小
# 为了方便讲解,假设每次随机到的大小为[416,416],注意,实际并非如此
self.train_input_size = random.choice(self.train_input_sizes)
# 获得3个输出图像的大小,分别为[8,16,32]下采样之后的大小,即得到[(52,52),(26,26),(13,13)]
self.train_output_sizes = self.train_input_size // self.strides
# 用于保存一个batch图片的像素,假设输入图像为[416,416]
batch_image = np.zeros((self.batch_size, self.train_input_size, self.train_input_size, 3))
# 存储下采样的实际box信息,这里是通过anchors变换的box,其包含了52x52个gred通过anchors得到的3个box,
# 即对每张图片,进行[8,16,32]下采样,然后对其中的每个gred都画出3个box来,box的绘画根据anchors决定
batch_label_sbbox = np.zeros((self.batch_size, self.train_output_sizes[0], self.train_output_sizes[0],
self.anchor_per_scale, 5 + self.num_classes))
batch_label_mbbox = np.zeros((self.batch_size, self.train_output_sizes[1], self.train_output_sizes[1],
self.anchor_per_scale, 5 + self.num_classes))
batch_label_lbbox = np.zeros((self.batch_size, self.train_output_sizes[2], self.train_output_sizes[2],
self.anchor_per_scale, 5 + self.num_classes))
# 保存实际的box,注意这里要和gred的box分开来,这里的box是总的box,不是gred的box,分别存储的也是对应[8,16,32]采样之后的
batch_sbboxes = np.zeros((self.batch_size, self.max_bbox_per_scale, 4))
batch_mbboxes = np.zeros((self.batch_size, self.max_bbox_per_scale, 4))
batch_lbboxes = np.zeros((self.batch_size, self.max_bbox_per_scale, 4))
num = 0 #对当前的图片计算,总数为batchs
if self.batch_count
关注
打赏
最近更新
- 深拷贝和浅拷贝的区别(重点)
- 【Vue】走进Vue框架世界
- 【云服务器】项目部署—搭建网站—vue电商后台管理系统
- 【React介绍】 一文带你深入React
- 【React】React组件实例的三大属性之state,props,refs(你学废了吗)
- 【脚手架VueCLI】从零开始,创建一个VUE项目
- 【React】深入理解React组件生命周期----图文详解(含代码)
- 【React】DOM的Diffing算法是什么?以及DOM中key的作用----经典面试题
- 【React】1_使用React脚手架创建项目步骤--------详解(含项目结构说明)
- 【React】2_如何使用react脚手架写一个简单的页面?