目标检测0-02:YOLO V3-网络结构输入输出解析

2023-05-16

以下链接是个人关于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 < self.num_batchs:
                while num < self.batch_size: #对每张图片进行处理
                    # 得到图片的索引
                    index = self.batch_count * self.batch_size + num
                    if index >= self.num_samples: index -= self.num_samples

                    # 获取图片的信息,包含了图片路径,boxs,以及boxs对应的类别
                    annotation = self.annotations[index]

                    # 解析得到图片像素和boxs以及类别,其会随机对数据进行一些旋转,翻转等,增加数据的多样性
                    image, bboxes = self.parse_annotation(annotation)

                    # 该函数有详细注解
                    # label_sbbox, label_mbbox, label_lbbox:对每个grep都进行了描述,其中只选区包含了box中心的grep进行预测以及loss
                    # sbboxes, mbboxes, lbboxes,# 实际的boxs中心坐标和长宽
                    label_sbbox, label_mbbox, label_lbbox, sbboxes, mbboxes, lbboxes = self.preprocess_true_boxes(bboxes)
                    """
                    print(label_sbbox.shape)
                    print(label_mbbox.shape)
                    print(label_lbbox.shape)
                    print(sbboxes.shape)
                    print(mbboxes.shape)
                    print(lbboxes.shape)

                    print(label_sbbox)
                    print(label_mbbox)
                    print(label_lbbox)
                    #print(sbboxes)
                    #print(mbboxes)
                    #print(lbboxes)
                    print(annotation)
                    """
                    # 把处理过后的每张图片加到batch中
                    batch_image[num, :, :, :] = image
                    batch_label_sbbox[num, :, :, :, :] = label_sbbox
                    batch_label_mbbox[num, :, :, :, :] = label_mbbox
                    batch_label_lbbox[num, :, :, :, :] = label_lbbox
                    batch_sbboxes[num, :, :] = sbboxes
                    batch_mbboxes[num, :, :] = mbboxes
                    batch_lbboxes[num, :, :] = lbboxes
                    num += 1
                self.batch_count += 1
                return batch_image, batch_label_sbbox, batch_label_mbbox, batch_label_lbbox, \
                       batch_sbboxes, batch_mbboxes, batch_lbboxes
            else:
                self.batch_count = 0
                np.random.shuffle(self.annotations)
                raise StopIteration

    def random_horizontal_flip(self, image, bboxes):

        if random.random() < 0.5:
            _, w, _ = image.shape
            image = image[:, ::-1, :]
            bboxes[:, [0,2]] = w - bboxes[:, [2,0]]

        return image, bboxes

    def random_crop(self, image, bboxes):

        if random.random() < 0.5:
            h, w, _ = image.shape
            max_bbox = np.concatenate([np.min(bboxes[:, 0:2], axis=0), np.max(bboxes[:, 2:4], axis=0)], axis=-1)

            max_l_trans = max_bbox[0]
            max_u_trans = max_bbox[1]
            max_r_trans = w - max_bbox[2]
            max_d_trans = h - max_bbox[3]

            crop_xmin = max(0, int(max_bbox[0] - random.uniform(0, max_l_trans)))
            crop_ymin = max(0, int(max_bbox[1] - random.uniform(0, max_u_trans)))
            crop_xmax = max(w, int(max_bbox[2] + random.uniform(0, max_r_trans)))
            crop_ymax = max(h, int(max_bbox[3] + random.uniform(0, max_d_trans)))

            image = image[crop_ymin : crop_ymax, crop_xmin : crop_xmax]

            bboxes[:, [0, 2]] = bboxes[:, [0, 2]] - crop_xmin
            bboxes[:, [1, 3]] = bboxes[:, [1, 3]] - crop_ymin

        return image, bboxes

    def random_translate(self, image, bboxes):

        if random.random() < 0.5:
            h, w, _ = image.shape
            max_bbox = np.concatenate([np.min(bboxes[:, 0:2], axis=0), np.max(bboxes[:, 2:4], axis=0)], axis=-1)

            max_l_trans = max_bbox[0]
            max_u_trans = max_bbox[1]
            max_r_trans = w - max_bbox[2]
            max_d_trans = h - max_bbox[3]

            tx = random.uniform(-(max_l_trans - 1), (max_r_trans - 1))
            ty = random.uniform(-(max_u_trans - 1), (max_d_trans - 1))

            M = np.array([[1, 0, tx], [0, 1, ty]])
            image = cv2.warpAffine(image, M, (w, h))

            bboxes[:, [0, 2]] = bboxes[:, [0, 2]] + tx
            bboxes[:, [1, 3]] = bboxes[:, [1, 3]] + ty

        return image, bboxes

    def parse_annotation(self, annotation):

        line = annotation.split()
        image_path = line[0]
        if not os.path.exists(image_path):
            raise KeyError("%s does not exist ... " %image_path)
        image = np.array(cv2.imread(image_path))
        bboxes = np.array([list(map(int, box.split(','))) for box in line[1:]])

        if self.data_aug:
            image, bboxes = self.random_horizontal_flip(np.copy(image), np.copy(bboxes))
            image, bboxes = self.random_crop(np.copy(image), np.copy(bboxes))
            image, bboxes = self.random_translate(np.copy(image), np.copy(bboxes))

        image, bboxes = utils.image_preporcess(np.copy(image), [self.train_input_size, self.train_input_size], np.copy(bboxes))
        return image, bboxes

    def bbox_iou(self, boxes1, boxes2):
        boxes1 = np.array(boxes1)
        boxes2 = np.array(boxes2)

        boxes1_area = boxes1[..., 2] * boxes1[..., 3]
        boxes2_area = boxes2[..., 2] * boxes2[..., 3]

        boxes1 = np.concatenate([boxes1[..., :2] - boxes1[..., 2:] * 0.5,
                                boxes1[..., :2] + boxes1[..., 2:] * 0.5], axis=-1)
        boxes2 = np.concatenate([boxes2[..., :2] - boxes2[..., 2:] * 0.5,
                                boxes2[..., :2] + boxes2[..., 2:] * 0.5], axis=-1)

        left_up = np.maximum(boxes1[..., :2], boxes2[..., :2])
        right_down = np.minimum(boxes1[..., 2:], boxes2[..., 2:])

        inter_section = np.maximum(right_down - left_up, 0.0)
        inter_area = inter_section[..., 0] * inter_section[..., 1]
        union_area = boxes1_area + boxes2_area - inter_area

        return inter_area / union_area

    def preprocess_true_boxes(self, bboxes):
        # 把原图像分别做8,16,32倍下采样变化。假设原始输入为416,416  3次下采样为
        # [(52,52,3,5+20),(26,26,3,5+20),(13,13,3,5+20)]
        label = [np.zeros((self.train_output_sizes[i], self.train_output_sizes[i], self.anchor_per_scale,
                           5 + self.num_classes)) for i in range(3)]

        # [(150,4),(150,4),(150,4)],存储实际得box,最多每张图片允许存在150个box
        bboxes_xywh = [np.zeros((self.max_bbox_per_scale, 4)) for _ in range(3)]

        # 对每个下采样图像得box数目进行计数
        bbox_count = np.zeros((3,))

        for bbox in bboxes: #对每一个box进行处理
            bbox_coor = bbox[:4]  # x_min, y_min, x_max, y_max
            bbox_class_ind = bbox[4] # 类别的标号

            # 转化为one_hot编码
            onehot = np.zeros(self.num_classes, dtype=np.float)
            onehot[bbox_class_ind] = 1.0

            # 对ohot概率进行平滑
            uniform_distribution = np.full(self.num_classes, 1.0 / self.num_classes)
            deta = 0.01
            smooth_onehot = onehot * (1 - deta) + deta * uniform_distribution

            # 获得原图中心点以及长宽
            bbox_xywh = np.concatenate([(bbox_coor[2:] + bbox_coor[:2]) * 0.5, bbox_coor[2:] - bbox_coor[:2]], axis=-1)

            # 先给bbox_xywh增加一个维度  按8,16,32下采样比例对中心点以及长宽进行缩放
            # bbox_xywh_scaled是包含3个下采样之后实际得boxs
            bbox_xywh_scaled = 1.0 * bbox_xywh[np.newaxis, :] / self.strides[:, np.newaxis]

            # 用来保存3个下采样的IOU
            iou = []

            # 标记为不存在正样本,如果图片中一个box都没有,认为该样本为负样本
            exist_positive = False
            for i in range(3): # 针对8,16,32下采样分别做处理
                # (3,4),对于每个网格会进行3次预测
                anchors_xywh = np.zeros((self.anchor_per_scale, 4))

                # 预测每次预测的中心点都是相同的
                anchors_xywh[:, 0:2] = np.floor(bbox_xywh_scaled[i, 0:2]).astype(np.int32) + 0.5

                # 赋值对应的anchors,这里为"./data/anchors/basline_anchors.txt"中
                # 1.25,  1.625,     2.0,   3.75,       4.125,    2.875,         i=0   8倍下采样
                # 1.875, 3.8125,    3.875, 2.8125,     3.6875,   7.4375,        i=2   16倍下采样
                # 3.625, 2.8125,    4.875, 6.1875,     11.65625, 10.1875        i=3   32倍下采样
                anchors_xywh[:, 2:4] = self.anchors[i]

                # bbox_xywh_scaled[i][np.newaxis, :]中的newaxis代表增加了一列
                # 该函数实际是利用预选的3个anchor与实际的box进行iou计算
                iou_scale = self.bbox_iou(bbox_xywh_scaled[i][np.newaxis, :], anchors_xywh)

                iou.append(iou_scale) # 添加一个下采样之后的iou_scale

                iou_mask = iou_scale > 0.3
                if np.any(iou_mask): # 3个预选框任意一个iou超过0.3表示其为一个正样本,然后进行处理
                    # 获取中心坐标
                    xind, yind = np.floor(bbox_xywh_scaled[i, 0:2]).astype(np.int32)
                    # 把中心gred特征向量清0
                    label[i][yind, xind, iou_mask, :] = 0
                    # 赋值真实xywh
                    label[i][yind, xind, iou_mask, 0:4] = bbox_xywh

                    # 类别置信度标记为1
                    label[i][yind, xind, iou_mask, 4:5] = 1.0

                    # 赋值每种类别的概率
                    label[i][yind, xind, iou_mask, 5:] = smooth_onehot

                    # 处理完当前下采样的一个box
                    bbox_ind = int(bbox_count[i] % self.max_bbox_per_scale)

                    # 赋值实际的bbox_xywh
                    bboxes_xywh[i][bbox_ind, :4] = bbox_xywh

                    # 下采样对应的bbox_count+1
                    bbox_count[i] += 1

                    # 表示该为一个正样本
                    exist_positive = True

            # 如果为负样本
            if not exist_positive:
                # 获得iou最好的anchor索引
                best_anchor_ind = np.argmax(np.array(iou).reshape(-1), axis=-1)


                best_detect = int(best_anchor_ind / self.anchor_per_scale)
                best_anchor = int(best_anchor_ind % self.anchor_per_scale)

                # 获得中心坐标
                xind, yind = np.floor(bbox_xywh_scaled[best_detect, 0:2]).astype(np.int32)

                # 把最好的的一下采样中最好的best_anchor进行赋值
                label[best_detect][yind, xind, best_anchor, :] = 0
                label[best_detect][yind, xind, best_anchor, 0:4] = bbox_xywh
                label[best_detect][yind, xind, best_anchor, 4:5] = 1.0
                label[best_detect][yind, xind, best_anchor, 5:] = smooth_onehot

                bbox_ind = int(bbox_count[best_detect] % self.max_bbox_per_scale)
                bboxes_xywh[best_detect][bbox_ind, :4] = bbox_xywh
                bbox_count[best_detect] += 1
        # 通过anchor变换的boxs
        label_sbbox, label_mbbox, label_lbbox = label
        # 真实的box
        sbboxes, mbboxes, lbboxes = bboxes_xywh
        return label_sbbox, label_mbbox, label_lbbox, sbboxes, mbboxes, lbboxes

    def __len__(self):
        return self.num_batch

通过源码的注释,我们可知道其中的label_sbbox, label_mbbox, label_lbbox, sbboxes, mbboxes, lbboxes分别为什么:

# 3个下采样对应图片每个gred的关于box的信息(只有存在box中心的gred才进行标记和预测)
label_sbbox, label_mbbox, label_lbbox 

sbboxes, mbboxes, lbboxes # 3个下采样,对应真实box位置

知道了网络的输入那么我们来分析一下输出。

YOLO V3输出

既然要知道其输出,我们当然需要去了解他的网络结构,下面是一篇比较好的文章:
YOLO v3网络结构分析
在这里插入图片描述
通过前面的分析,我们可以知道,假设输入图像为[412,412],那么其会输出三个特征向量(针对VOC数据集20分类)

# 3表示针对每个cell,预测3个box
# 4表示每个cell预测每box的xyhw,1表示每个置信度.
# 20表示分类的种类
[52,52,3*((4+1)+20)] = [52,52,75]
[26,26,3*((4+1)+20)] = [26,26,75]
[13,13,3*((4+1)+20)] = [13,13,75]

下小结我们讲解一下YOLO V3的损失函数,任何一个网络的核心,都在于其损失函数。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

目标检测0-02:YOLO V3-网络结构输入输出解析 的相关文章

  • 野火&洋桃STM32开发版学习指导完整版

    该文章是我历时一个月整理总结而成 xff0c 专门针对想要通过野火 amp 洋桃STM32开发板入门stm32的读者 由于csdn编辑限制 xff0c 该学习指导只包含文字信息 如需查看含图片的完整版可进入我的博客下载页 完整版内容详实 x
  • sumo学习——sumo的路网介绍

    2 sumo路网 这里的记录的内容 xff0c 并不以如何绘制或者如何建立一个路网体系为主 xff0c 而是较为详细的介绍 xff0c 在sumo中是如何定义路网格式的 这里所有的内容都是来自于sumo的软件说明翻译 xff0c 其中部分夹
  • webpack之devtool

    关于Devtool 该选项控制是否以及如何生成源映射 官网上给出的可选值有 xff1a 其中一些值适合开发 xff0c 一些用于生产 对于开发 xff0c 您通常需要快速的Source Maps xff0c 以bundle的大小为代价 xf
  • 汽车UDS诊断详解及Vector相关工具链使用说明——总述

    1 什么是诊断 车辆在运行过程中 xff0c 不可避免地会发生一些故障 xff0c 为了确保行车安全 xff0c 我们要求车上的ECU能够实时监测部件的运行状态 xff0c 一旦发现异常情况 xff0c 能通过点亮报警灯等方式提示驾驶员 但
  • 汽车UDS诊断详解及Vector相关工具链使用说明——2.2.7 动态定义DID(0x2C)

    1 概述 动态定义DID服务允许诊断仪在ECU内部动态定义一个临时的DID 可以通过该DID读取一段内存的数据 也可以通过改DID一次性读取多个原有DID的数据 动态定义DID既可以是支持22服务的DID 也可以是支持2A服务的周期性读取
  • 关于RTOS中信号量、互斥量、邮箱、消息队列的一些理解

    1 信号量 信号量有两种 xff1a 计数性信号量和二值信号量 xff0c 计数性信号量可以被获取多次 xff0c 二值信号量只有0和1两种状态 xff0c 只能被获取一次 信号量可以用来对资源进行保护 xff0c 防止多个任务同时访问某个
  • 英飞凌 AURIX 系列单片机的HSM详解(1)——何为HSM

    本系列的其它几篇文章 xff1a 英飞凌 AURIX 系列单片机的HSM详解 xff08 2 xff09 与HSM相关的UCB和寄存器 英飞凌 AURIX 系列单片机的HSM详解 xff08 3 xff09 开发方法 英飞凌 AURIX 系
  • DoIP(一)——基础概念

    1 DoIP概述 DoIP xff08 Diagnostic communication over Internet Protocol xff09 是基于车载以太网的诊断 xff0c 在OSI 七层模型中属于传输层 xff0c 其传输的诊断
  • DoIP(二)——报文类型

    我们上一篇文章提到 xff0c DoIP报头中有两字节的数据类型 xff08 Payload Type xff09 xff0c 代表DoIP报文类型 xff0c 本文就来详细介绍一下每一种报文类型 标准中对报文类型的定义如下 xff1a 数
  • AUTOSAR —— CAN网络管理(CanNm)

    1 网络状态 AUTOSAR网络管理节点内部有两个状态 xff0c 一个是Requested 被请求状态 xff0c 另一个是Released 被释放状态 xff0c 当节点的应用层需要使用总线进行通讯的时候 xff0c 会调用接口使得节点
  • S32K1XX系列单片机 ——(1)开发环境搭建

    本文介绍一下NXP S32系列单片机开发环境的搭建方法 xff0c 分两种 xff1a S32DS和Keil 1 S32DS S32DS是NXP开发的一款IDE xff0c 编译器是GCC xff0c 支持Lauterbach P amp
  • AUTOSAR —— S32K144 的 Fls 和 Fee 模块配置

    本文来简要介绍一下如何在EB中配置AUTOSAR Fls和Fee模块 Fls模块是Flash的驱动 xff0c 执行具体的Flash擦写读取等操作 Fee模块的全称是FlashEEPROMEmulation xff0c 即Flash模拟EE
  • NXP MCU CAN波特率(位时间)配置详解

    1 概述 本文将会详细讲解如何设置NXP MCU的CAN波特率 位时间 采样点等属性 波特率即CAN总线传输频率 xff0c 位时间是波特率的倒数 xff0c 例如波特率是500K xff0c 那么位时间 61 1 500000 61 0
  • 51单片机之蜂鸣器

    include 34 reg52 h 34 include 34 intrins h 34 typedef unsigned int ui typedef unsigned char uc define led P2 34 34 sbit
  • vnc配置

    centos版本在7 0以上的 一 安装 yum grouplist 查看是否已经安装过 yum groupinstall GNOME Desktop 如没有安装则运行命令安装 以root用户运行以下命令来安装vncserver yum i
  • 【WebAPI 验证】给 webapi加上token 验证(包括上传文件方法)

    需要给网站开发对接EMI 接口 xff0c 因此想通过Webapi 进行传递参数 xff0c 但是由于该方法不太安全 xff0c 因此不选用 xff0c 但是记录下该方法的使用 1 xff0c 创建WEBAPI 项目 xff0c 打开nug
  • 委托和事件

    一 委托与事件的区别 委托是一种用于存储方法引用的引用类型 xff0c 它定义了一种类型安全的调用回调方法的方式 事件使用委托来封装触发时将要调用的方法 xff0c 事件是一种函数成员 委托是指向一个方法的指针 xff0c 而且我们采取和调
  • OS文件/目录方法----获取当前py文件的路径

    获取当前py文件的路径 xff1a 1 直接获取 只返回当前文件的工作目录 aa 61 os getcwd print 39 当前文件的路径 39 aa 输出 当前文件的路径 G PycharmProjects SeleniumUnitte
  • vscode如何配置git-2022.10

    文章目录 1 vscode填写git配置2 打开git命令行界面 windows本地已经安装git 并配置成功 1 vscode填写git配置 在搜索框中搜索 terminal integrated automation profile w
  • LCD12864串口高级操作

    上面是LCD12864的串口通信时序图 其中RW是方向位 xff0c RS是命令数据选择位 xff0c SID为数据线 xff0c SCLK为时钟线 xff0c CS为使能端 其中CS为1时使能时序操作 xff0c 由图可以看出 xff0c

随机推荐

  • Ubuntu server 18.04 服务器配置

    文章目录 前言一 下载镜像 制作系统u盘启动盘二 安装设置三 创建用户 给普通用户root权限四 GPU驱动安装五 miniconda安装 操作虚拟环境常用指令六 pytracking环境配置总结 前言 例如 xff1a 原有centos服
  • OpenCV数据载入、显示与保存

    一 图像存储器 OpenCV提供了一个Mat类用于存储矩阵数据 Mat类用来保存矩阵类型的数据信息 xff0c 包括向量 矩阵 灰度或彩色图像等数据 Mat类分为矩阵头和指向存储数据的矩阵指针的两部分 矩阵头中包含矩阵的尺寸 存储方法 地址
  • Colab运行YOLOv5训练自己的数据集

    最近想使用YOLOv5模型训练自己的数据集 xff0c 但是没有GPU 所以白嫖一下Google的Colab 第一 xff1a 制作自己的数据集 这里给出一篇自己看过的博客写和博主自制的视频 xff0c 我觉得讲的挺好 xff0c 数据集方
  • HC05主从蓝牙通信的配置步骤

    HC05主从一体化蓝牙模块 xff0c 可以配置为一个主蓝牙和一个从蓝牙 xff0c 两个蓝牙之间实现互相通信 拿到蓝牙之后只需接VCC GND RX TX 首先让蓝牙进入AT模式 先按住蓝牙上的微动开关 xff0c 然后给蓝牙上电 蓝牙上
  • 游记_秦皇岛-北戴河两日游

    写在开篇 在北京上学 xff0c 经常收到各种关于秦皇岛 北戴河的安利 xff0c 一直想去但是都没有实现 xff0c 前段时间终于和男朋友决定用一个周末的时间 xff08 周五晚上从北京出发 xff0c 周六早上到秦皇岛 xff0c 周日
  • STM32学习之路(首篇)

    STM32的学习之路 xff0c 到目前为止学习32也有一段时间了 xff0c 学习32的过程和学51单片机的道路差不多 首先要拥有自己的32开发板 xff0c 我买的是正点原子的开发板 xff0c 商家提供了很多学习资料 xff0c 一开
  • STM32学习之路(五---IIC)

    IIC是由数据线SDA和时钟SCL构成的串行总线 xff0c 可以发送和接收数据 在CPU与被控IC之间 xff0c IC与IC之间进行双向传送 IIC总线在传送数据过程中共有三种类型的信号 xff0c 分别是 xff1a 开始信号 xff
  • PWM调速的原理

    PWM调速实质上是调节占空比 xff0c 我们都是根据占空比的大小来衡量速度 xff0c 但是为什么我们调节占空比就可以实现对速度的调节呢 xff1f 这就需要我们了解调速的本质 xff0c 我们用PWM调节速度问什么能够实现 xff1f
  • keil中显示expected expression

    在keil中出现error expected expression的错误 xff0c 网上很多人都是在 Misc Cortrols这里改一下写c99就好了 xff0c 但是我改过之后还是没好 xff0c 依然报警告 xff0c 后来我自己的
  • Linux学习(C语言学习之Gcc)

    言之者无罪 xff0c 闻之者足以戒 诗序 Linux的学习需要对C语言有一个透彻性的了解 xff0c 需要有非常好的C语言基础 xff0c Gcc是Linux中的C文件的一个编译器 xff08 当然也不只局限于C文件 xff09 Gcc最
  • 如何在 python中查询某个函数的使用方法

    在Python 中查询某个函数的使用 方法时由两种途径 xff1a 1 利用help来查询 xff0c 比如用help来查询print内置函数的使用 xff0c 直接打开Shell输入 help print 就可以了 2 利用Python官
  • 玩转电机驱动——电机编码器

    玩转电机驱动 电机编码器 文章目录 玩转电机驱动 电机编码器前言一 旋转编码器1 光学编码器2 光学旋转编码器与Arduino连接3 程序 二 Arduino Encoder h库相关知识1 硬件要求2 基本用法3 了解正交编码信号4 示例
  • 6、SYSTEM文件夹介绍

    1 delay文件夹 delay c和delay h两个文件 xff0c 其中有七个函数 xff1a void delay osschedlock void void delay osschedunlock void void delay
  • 姿态估计0-06:DenseFusion(6D姿态估计)-源码解析(2)-linemod数据集,预处理解读

    以下链接是个人关于DenseFusion 6D姿态估计 所有见解 xff0c 如有错误欢迎大家指出 xff0c 我会第一时间纠正 有兴趣的朋友可以加微信 xff1a 17575010159 相互讨论技术 若是帮助到了你什么 xff0c 一定
  • 动作识别0-02:mmaction2(SlowFast)-官方数据训练测试-ucf101

    以下链接是个人关于mmaction2 SlowFast 动作识别 所有见解 xff0c 如有错误欢迎大家指出 xff0c 我会第一时间纠正 有兴趣的朋友可以加微信 xff1a 17575010159 相互讨论技术 若是帮助到了你什么 xff
  • 如何快速找到你想要的文献

    阅读文献是深入科研和跟随前沿发展的必要条件 xff0c 大家写论文的时候也需要查阅各种文献 xff0c 引证自己的观点 很多人并不太清楚下载文献的途径 xff0c 只简单了解图书馆 xff0c 知网 xff0c sci等 xff0c 多者使
  • java中private,public,protected详解

    参照甲骨文的java文档 xff1a 点击打开链接 访问级别 访问级别修饰符确定其他类是否可以使用特定字段或调用特定方法 有两个级别的访问控制 xff1a 1 在顶级 public或package private xff08 没有显式修饰符
  • 史上最简SLAM零基础解读(10.1) - g2o(图优化)→简介环境搭建(slam十四讲第二版为例)

    本人讲解关于slam一系列文章汇总链接 史上最全slam从零开始 文末正下方中心提供了本人 联系方式 xff0c 点击本人照片即可显示 W X
  • (02)Cartographer源码无死角解析-(19) SensorBridge→雷达点云数据帧处理与坐标系变换(涉及函数重载)

    本人讲解关于slam一系列文章汇总链接 史上最全slam从零开始 xff0c 针对于本栏目讲解 02 Cartographer源码无死角解析 链接如下 02 Cartographer源码无死角解析 00 目录 最新无死角讲解 xff1a h
  • 目标检测0-02:YOLO V3-网络结构输入输出解析

    以下链接是个人关于YOLO V3所有见解 xff0c 如有错误欢迎大家指出 xff0c 我会第一时间纠正 xff0c 如有兴趣可以加微信 xff1a 17575010159 相互讨论技术 目标检测0 00 xff1a YOLO V3目录 史