【YOLOv3 decode】YOLOv3中解码理解decode_box

2023-05-16

文章目录

  • 1 解码是什么意思
  • 2 代码解读
  • 3 生成网格中心 代码详解
  • 4 按照网格格式生成先验框的宽高 代码详解
  • 5 感谢链接

1 解码是什么意思

在利用YOLOv3网络结构提取到out0、out1、out2之后,不同尺度下每个网格点上均有先验框,网络训练过程会对先验框的参数进行调整,继而得到预测框,从不同尺度下预测框还原到原图输入图像上,同时包括该框内目标预测的结果情况(预测框位置、类别概率、置信度分数),这个过程称之为解码

2 代码解读

注释主要以VOC数据集,YOLOv3 net最后一层输出进行解读。

import torch
import numpy as np

class DecodeBox():
    def __init__(self, anchors, num_classes, input_shape, anchors_mask = [[6,7,8], [3,4,5], [0,1,2]]):
        super(DecodeBox, self).__init__()
        self.anchors        = anchors
        self.num_classes    = num_classes       # int   20
        self.bbox_attrs     = 5 + num_classes   # int   25
        self.input_shape    = input_shape       # (416, 416) 元组
        #-----------------------------------------------------------#
        #   13x13的特征层对应的anchor是[116,90],[156,198],[373,326]
        #   26x26的特征层对应的anchor是[30,61],[62,45],[59,119]
        #   52x52的特征层对应的anchor是[10,13],[16,30],[33,23]
        #-----------------------------------------------------------#
        self.anchors_mask   = anchors_mask

    # ----------------------------------------------#
    #   得到out0、out1、out2不同尺度下每个网格点上的的预测情况(预测框位置、类别概率、置信度分数)
    # ----------------------------------------------#
    def decode_box(self, inputs):   # input一共有三组数据,out0,out1,out2
        outputs = []
        for i, input in enumerate(inputs):      # 一次只能对一个特征层的输出进行解码操作
            # -----------------------------------------------#
            #   输入的input一共有三个,他们的shape分别是    针对voc数据集
            #   batch_size, 75, 13, 13          batch_size, channels, weight, height
            #   batch_size, 75, 26, 26
            #   batch_size, 75, 52, 52
            # -----------------------------------------------#
            batch_size      = input.size(0)
            input_height    = input.size(2)
            input_width     = input.size(3)

            # -----------------------------------------------#
            #   输入为416x416时
            #   stride_h = stride_w = 32、16、8
            #   一个特征点对应原来图上多少个像素点
            # -----------------------------------------------#
            stride_h = self.input_shape[0] / input_height       # 输出特征图和resize之后的原图上对应步长,映射回去的操作
            stride_w = self.input_shape[1] / input_width
            #-------------------------------------------------#
            #   把先验框的尺寸调整成特征层的大小形式,用来对应两者宽和高
            #   此时获得的scaled_anchors大小是相对于特征层的,anchors是大数据kmeans聚类经验所得
            #   out0越小,stride越大,用来检测大目标
            #-------------------------------------------------#
            scaled_anchors = [(anchor_width / stride_w, anchor_height / stride_h) for anchor_width, anchor_height in self.anchors[self.anchors_mask[i]]]

            #-----------------------------------------------#
            #   输入的input一共有三个,他们的shape分别是
            #   batch_size, 3, 13, 13, 25
            #   batch_size, 3, 26, 26, 25
            #   batch_size, 3, 52, 52, 25
            #   batch_size,3*(5+num_classes),13,13 -> batch_size,3,5+num_classes,13,13 -> batch_size, 3, 13, 13, 25
            #   此处参考链接:https://blog.csdn.net/weixin_45377629/article/details/124028098
            #-----------------------------------------------#
            prediction = input.view(batch_size, len(self.anchors_mask[i]),
                                    self.bbox_attrs, input_height, input_width).permute(0, 1, 3, 4, 2).contiguous()

            #-----------------------------------------------#
            #   先验框的中心位置的调整参数
            #   x shape: torch.size([batch_size,3,13,13])
            #   y shape: torch.size([batch_size,3,13,13]) 
            #-----------------------------------------------#
            x = torch.sigmoid(prediction[..., 0])  # sigmoid可以把输出值固定到0~1之间
            y = torch.sigmoid(prediction[..., 1])   # 先验框中心点的调整只能在其右下角的网格里面
            #-----------------------------------------------#
            #   先验框的宽高调整参数
            #-----------------------------------------------#
            w = prediction[..., 2]
            h = prediction[..., 3]
            #-----------------------------------------------#
            #   获得置信度,是否有物体,有物体的概率是多少
            #-----------------------------------------------#
            conf        = torch.sigmoid(prediction[..., 4])
            #-----------------------------------------------#
            #   种类置信度,属于某类别的概率是多少
            #-----------------------------------------------#
            pred_cls    = torch.sigmoid(prediction[..., 5:])

            FloatTensor = torch.cuda.FloatTensor if x.is_cuda else torch.FloatTensor
            LongTensor  = torch.cuda.LongTensor if x.is_cuda else torch.LongTensor

            #----------------------------------------------------------#
            #   生成网格,先验框中心=网格左上角
            #   grid_x shape:torch.size([batch_size,3,13,13])
            #   grid_y shape:torch.size([batch_size,3,13,13])
            #   关于该行代码解读,详细参考本文第3节
            #----------------------------------------------------------#
            grid_x = torch.linspace(0, input_width - 1, input_width).repeat(input_height, 1).repeat(
                batch_size * len(self.anchors_mask[i]), 1, 1).view(x.shape).type(FloatTensor)
            grid_y = torch.linspace(0, input_height - 1, input_height).repeat(input_width, 1).t().repeat(
                batch_size * len(self.anchors_mask[i]), 1, 1).view(y.shape).type(FloatTensor)

            #----------------------------------------------------------#
            #   按照网格格式生成先验框的宽高
            #   batch_size,3,13,13
            #   关于该行代码解读,详细参考本文第4节
            #----------------------------------------------------------#
            anchor_w = FloatTensor(scaled_anchors).index_select(1, LongTensor([0]))
            anchor_h = FloatTensor(scaled_anchors).index_select(1, LongTensor([1]))
            anchor_w = anchor_w.repeat(batch_size, 1).repeat(1, 1, input_height * input_width).view(w.shape)
            anchor_h = anchor_h.repeat(batch_size, 1).repeat(1, 1, input_height * input_width).view(h.shape)

            #----------------------------------------------------------#
            #   利用预测结果对先验框进行调整
            #   首先调整先验框的中心,从先验框中心向右下角偏移
            #   再调整先验框的宽高。
            #----------------------------------------------------------#
            pred_boxes          = FloatTensor(prediction[..., :4].shape)
            pred_boxes[..., 0]  = x.data + grid_x
            pred_boxes[..., 1]  = y.data + grid_y
            pred_boxes[..., 2]  = torch.exp(w.data) * anchor_w
            pred_boxes[..., 3]  = torch.exp(h.data) * anchor_h

            #----------------------------------------------------------#
            #   将输出结果归一化成小数的形式
            #----------------------------------------------------------#
            _scale = torch.Tensor([input_width, input_height, input_width, input_height]).type(FloatTensor)
            output = torch.cat((pred_boxes.view(batch_size, -1, 4) / _scale,
                                conf.view(batch_size, -1, 1), pred_cls.view(batch_size, -1, self.num_classes)), -1)
            outputs.append(output.data)
        return outputs      # 得到out0、out1、out2不同尺度下每个网格点上的的预测情况(预测框位置、类别概率、置信度分数)

if __name__ == '__main__':
    anchors = [10.0, 13.0, 16.0, 30.0, 33.0, 23.0, 30.0, 61.0, 62.0, 45.0, 59.0, 119.0, 116.0, 90.0, 156.0, 198.0, 373.0, 326.0]
    # anchors: ndarray:(9, 2)
    anchors = np.array(anchors).reshape(-1,2)
    num_classes = 20    # voc类别个数
    anchors_mask = [[6, 7, 8], [3, 4, 5], [0, 1, 2]]
    input_shape = [416,416]
    bbox_util = DecodeBox(anchors, num_classes, (input_shape[0], input_shape[1]), anchors_mask)

    # ---------------------------------------------------------#
    #   将图像输入网络当中进行预测!
    # ---------------------------------------------------------#
    net = YoloBody(anchors_mask, num_classes)       # 此地YoloBody可见https://www.jianshu.com/p/27f3b967646c
    outputs = net(images)                           # 此地images表示输入图片,outputs为三个输出out0, out1, out2
    outputs = bbox_util.decode_box(outputs)         # 得到out0、out1、out2不同尺度下每个网格点上的预测情况(预测框位置、类别概率、置信度分数)

3 生成网格中心 代码详解

先验框中心=网格左上角,下面这行代码到底如何理解呢?

grid_x = torch.linspace(0, input_width - 1, input_width).repeat(input_height, 1).repeat(
                batch_size * len(self.anchors_mask[i]), 1, 1).view(x.shape).type(FloatTensor)

以宽为5,高为5, batch_size为1为例,详细解读见下方代码及输出。

import torch

if __name__ == "__main__":
    input_width = 5
    input_height = 5
    batch_size = 1
    anchors_mask = [[6,7,8], [3,4,5], [0,1,2]]
    
    a = torch.linspace(0, input_width - 1, input_width)     # torch.linspace左闭右闭
    print(a)    # 输出一个张量列表
    """
    tensor([0., 1., 2., 3., 4.])
    """
    
    b = a.repeat(input_height, 1)
    print(b)
    """
    tensor([[0., 1., 2., 3., 4.],
            [0., 1., 2., 3., 4.],
            [0., 1., 2., 3., 4.],
            [0., 1., 2., 3., 4.],
            [0., 1., 2., 3., 4.]])
    """
    c = b.repeat(batch_size * 3, 1, 1)         # len(anchors_mask[i]) = 3
    print(c)
    """
    tensor([[[0., 1., 2., 3., 4.],
         [0., 1., 2., 3., 4.],
         [0., 1., 2., 3., 4.],
         [0., 1., 2., 3., 4.],
         [0., 1., 2., 3., 4.]],

        [[0., 1., 2., 3., 4.],
         [0., 1., 2., 3., 4.],
         [0., 1., 2., 3., 4.],
         [0., 1., 2., 3., 4.],
         [0., 1., 2., 3., 4.]],

        [[0., 1., 2., 3., 4.],
         [0., 1., 2., 3., 4.],
         [0., 1., 2., 3., 4.],
         [0., 1., 2., 3., 4.],
         [0., 1., 2., 3., 4.]]])
    """
    d = c.view(batch_size, 3, input_height, input_width)         # 对已知的进行reshape
    print(d)
    """
    tensor([[[[0., 1., 2., 3., 4.],
          [0., 1., 2., 3., 4.],
          [0., 1., 2., 3., 4.],
          [0., 1., 2., 3., 4.],
          [0., 1., 2., 3., 4.]],

         [[0., 1., 2., 3., 4.],
          [0., 1., 2., 3., 4.],
          [0., 1., 2., 3., 4.],
          [0., 1., 2., 3., 4.],
          [0., 1., 2., 3., 4.]],

         [[0., 1., 2., 3., 4.],
          [0., 1., 2., 3., 4.],
          [0., 1., 2., 3., 4.],
          [0., 1., 2., 3., 4.],
          [0., 1., 2., 3., 4.]]]])
    """
    e = d.type(FloatTensor)     # 数据类型

4 按照网格格式生成先验框的宽高 代码详解

按照网格格式生成先验框的宽高,其代码如下:

#----------------------------------------------------------#
#   按照网格格式生成先验框的宽高
#   batch_size,3,13,13
#----------------------------------------------------------#
anchor_w = FloatTensor(scaled_anchors).index_select(1, LongTensor([0]))
anchor_h = FloatTensor(scaled_anchors).index_select(1, LongTensor([1]))
anchor_w = anchor_w.repeat(batch_size, 1).repeat(1, 1, input_height * input_width).view(w.shape)
anchor_h = anchor_h.repeat(batch_size, 1).repeat(1, 1, input_height * input_width).view(h.shape)

对于上面这四行代码,我们以最小特征层为例,详细理解:

import torch

if __name__ == "__main__":
    #-----------------------------------------------------------------------------#
    #   把先验框的尺寸调整成特征层的大小形式,用来对应两者宽和高
    #   此时获得的scaled_anchors大小是相对于特征层的,anchors是大数据kmeans聚类经验所得
    #   out0越小,stride越大,用来检测大目标
    #   此以最小特征层为例,batch_size, 75, 13, 13
    #-----------------------------------------------------------------------------#
    scaled_anchors = [(3.625,2.8125), (4.875,6.1875), (11.65625, 10.1875)]

    x_is_cuda = False   # x.is_cuda = False,表示没用cuda
    FloatTensor = torch.cuda.FloatTensor if x_is_cuda else torch.FloatTensor
    LongTensor  = torch.cuda.LongTensor if x_is_cuda else torch.LongTensor

    # ------------------------------#
    #   解读第 1 行anchor_w
    # ------------------------------#
    a = LongTensor([0])
    print(a)    # tensor([0])

    b = FloatTensor(scaled_anchors)
    print(b)    # 保留的小数点位数变了
    """
    tensor([[ 3.6250,  2.8125],
        [ 4.8750,  6.1875],
        [11.6562, 10.1875]])
    """
    # ----------------------------------------------------------#
    #   tensor.index_select(dim, index)
    #       dim  :表示要查找的维度,对于二维,0代表行,1代表列
    #       index:表示要索引的序列,是一个tensor对象
    #   a = tensor([0]),表示要索引的为宽
    #   a = tensor([1]),表示要索引的为高
    # ----------------------------------------------------------#
    anchor_w = b.index_select(1, a)
    print(anchor_w)     # anchor_w shape: torch.size([3,1])
    """
    tensor([[ 3.6250],
        [ 4.8750],
        [11.6562]])
    """
    
    # ------------------------------#
    #   解读第 2 行anchor_h
    #       类似上面
    # ------------------------------#
    anchor_h = b.index_select(1, LongTensor([1]))
    """
    tensor([[ 2.8125],
        [ 6.1875],
        [10.1875]])
    """
    
    # ----------------------------------------------------#
    #   解读第 3 行anchor_w
    #       w.shape 和 h.shape: torch.size([1,3,13,13])
    # ----------------------------------------------------#
    batch_size = 1      # 以batch_size=1为例
    input_height = 13   # 最小特征层输出,宽高均为13
    input_width = 13
    
    # ------------------------------------#
    #   tensor.repeat(dim1,dim2,...)
    #   复制多个tensor
    # ------------------------------------#
    c = anchor_w.repeat(batch_size, 1)
    print(c)
    """
    tensor([[ 3.6250],
        [ 4.8750],
        [11.6562]])
    若batch_size = 2, c 的结果:
    tensor([[ 3.6250],
        [ 4.8750],
        [11.6562],
        [ 3.6250],
        [ 4.8750],
        [11.6562]])
    毕竟有几张图片,先验框的宽,参数个数就应该有几倍,每张图片都有
    """
    d = c.repeat(1, 1, input_height * input_width)
    print(d.shape)          # torch.Size([1, 3, 169])
	
	# ---------------------------------------------------#
	#	每个像素点,都有三个先验框,每个先验框,都有宽
	#	有点各用各的,的感觉
	# ---------------------------------------------------#
    anchor_w = d.view(1,3,13,13)
    print(anchor_w.shape)   # torch.Size([1, 3, 13, 13]),先验框的宽就都生成了,高类似

5 感谢链接

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

【YOLOv3 decode】YOLOv3中解码理解decode_box 的相关文章

随机推荐

  • java常见面试题

    目录 基础语法 1 Java 语言的优点 xff1f 2 Java 如何实现平台无关 xff1f 3 JVM xff0c JDK 和 JRE 的区别 xff1f 4 Java 按值调用还是引用调用 xff1f 5 浅拷贝和深拷贝的区别 xf
  • 分段分页存储

    2020 4 27 在家的网课 xff0c 无聊 xff0c 记录一下分页 xff0c 分段 xff0c 段页式存储笔记 昨天刚学了分页存储 xff0c 听得我一脸懵逼 xff0c 好在课下花了很长时间才弄懂 1 分页存储管理 1 分页存储
  • 解压码

    BN00001 22kke BN00002 88cde BN00003 00ike BN00004 76cdb BN00005 09dbm BN00006 0mndc BN00007 cd78d BN00008 bdmf8 BN00009
  • 保险项目业务流程

    1 整个项目分为四分模块 xff1a 信息采集模块 信息验证 审批 生成合同 xff08 开单 xff09 信息采集模块 xff1a 包括购买保险产品 xff0c 客户个人信息 1 纸质文档给客户填写 xff0c 在回来录入系统 2 客户直
  • IDEA使用maven自定义archetype

    标题自定义archetype 在pom文件中添加archetype plugin span class token generics span class token punctuation lt span plugin span clas
  • 自定义Perperties文件内容读取

    新建properties文件放在resources目录下 properties文件内容 url span class token operator 61 span jdbc span class token operator span my
  • 如何使用Google TV设置Chromecast

    Justin Duino 贾斯汀 杜伊诺 Justin Duino Google changed up its streaming platform with the release of the Chromecast with Googl
  • 使用CSS中的Hover控制显示子元素或者兄弟元素

    lt DOCTYPE html gt lt html lang 61 34 en 34 gt lt head gt lt meta charset 61 34 UTF 8 34 gt lt meta name 61 34 viewport
  • maven项目中的jdbc连接步骤

    在maven项目pom xml中到入驱动包 xff08 以下是驱动包代码 xff09 lt dependencies gt lt https mvnrepository com artifact mysql mysql connector
  • executeUpdate()与executeQuery()的使用

    增 删 改 用executeUpdate xff08 xff09 返回值为int型 xff0c 表示被影响的行数 例子 查用executeQuery 返回的是一个集合 next xff08 xff09 表示 指针先下一行 xff0c 还有f
  • Access denied for user ''@'localhost' (using password: YES)错误解决方法

    远程登录被拒绝 xff0c 要改一个表数据的属性让他可以远程登录 解决方法如下 xff0c 执行命令 xff1a mysql gt use mysql mysql gt select host user from user 查看结果是不是r
  • 基于yolov5和Tesseract-OCR车牌识别项目 Linux系统上搭建运行(大概结构)

    项目大概分为两部分 xff0c 首先使用yolov5进行目标检测并截图 xff1b 然后对图片一系列的处理后使用Tesseract OCR进行字符识别 xff08 本文为简易版框架结构 xff0c 如果看完感兴趣可以在文末跳转看细节操作 x
  • ubuntu20.04使用微软Azure Kinect DK 实现三维重建demo记录

    本文仅为在ubuntu20 04实现Azure Kinect DK 三维重建demo xff0c 此文记录实现过程仅供学习 xff0c 同时为大家避坑 xff0c 文中参考大量文章已列至末尾 1 ros安装 2 安装微软 DK的sdk 3
  • 常见一面问题

    1 智能指针 常用的c 43 43 库 Standard Template Library STL Algorithms 算法 Containers 容器 Functions 函数 Iterators 迭代器 Boost 同样是大量C 43
  • ROS datatype/md5sum错误

    I got this error today Problem ERROR 1576785283 032878520 Client rostopic 21515 1576784759002 wants topic timestamp to h
  • 快速安装Pytorch和Torchvision

    文章目录 1 Linux下激活自己的虚拟环境并查看Python版本2 查看需要安装的Pytorch和Torchvision版本3 直接命令行安装3 1 如果不报错的话3 2 ERROR Could not install packages
  • 【Darknet-53】YOLOv3 backbone Darknet-53 详解

    文章目录 1 模型计算量与参数量2 Darknet 53网络3 感谢链接 1 模型计算量与参数量 模型计算量与参数量的计算方式主要有两种 xff0c 一种是使用thop库 xff0c 一种是使用torchsummaryX 使用pip ins
  • ubuntu 默认命令行_从命令行在Ubuntu上设置默认浏览器

    ubuntu 默认命令行 Ubuntu Linux has a default browser functionality that will automatically launch the correct browser when cl
  • 【DeeplabV3+】DeeplabV3+网络结构详解

    文章目录 1 常规卷积与空洞卷积的对比1 1 空洞卷积简介1 2 空洞卷积的优点 2 DeeplabV3 43 模型简介3 DeeplabV3 43 网络代码4 mobilenetv2网络代码5 感谢链接 聊DeeplabV3 43 网络前
  • 【YOLOv3 decode】YOLOv3中解码理解decode_box

    文章目录 1 解码是什么意思2 代码解读3 生成网格中心 代码详解4 按照网格格式生成先验框的宽高 代码详解5 感谢链接 1 解码是什么意思 在利用YOLOv3网络结构提取到out0 out1 out2之后 xff0c 不同尺度下每个网格点