简介

yolov3算法思想

  • yolo的作者将目标检测问题视为回归问题:首先将整幅图划分为S*S的网格,如果目标框的中心店落在这个网格中,那么这个网格就负责预测这个目标
  • 每一个网格都会预测bounding box(边界框), confidence(置信度)以及class probability map(类概率图)
    • bounding box, 包含四个值:x, y, w, h其中(x, y)代表预测框的中心点, (w, h)代表预测框的宽和高
    • confidence, 表示预测框包含目标的可能性,训练时的真值为预测框和真值框的IOU
    • class probability map, 表明这个目标所属类别的置信度

yolov3网络结构

  • yolov3在3个scale的特征图上分别预测不同大小的目标,即在8倍,16倍和32倍的特征图上进行预测,也就是说如果我们的输入为416416,那么yolov3预测时采用的特征图的大小分别为5252, 3232, 1313
  • 总之,yolov3在3中不同尺度的特征图上进行检测,因此如果我们输入416*416大小的图像,它将产生三种不同的输出形状张量: 13*13*255, 26*26*255和52*52*255

残差模块

  • 残差模块(Residual module)最显著的特点是使用了一种shortcut机制,以缓解因增加神经网络中的深度而导致的梯度消失的问题,从而使神经网络更易于训练
  • 它主要使用identity mapping在输入和输出之间建立连接

特征图分析

  • 要详细了解yolo的输出含义,首先需要了解什么是特征图
  • 在讨论CNN网络结构的时候,总经常使用的一个词汇叫做feature map,简单地说,输入图像与卷积核进行卷积操作后就可以获得图像特征.
  • 一般来说,当输入图像经过CNN提取特征时,特征图的数量(卷积核的数量)将增加,同时空间信息将减少,当然提取到的特征也会越来越抽象.随着网络变深,特征图的空间尺寸越来越小,但是通道数目越来越大,这是CNN的特性
  • 当CNN网络自下而上从输入图像中提取特征时,生成的特征图通常在空间尺寸上越来越小,在通道数目上越来越深.这个特性与ROI(感兴趣区)到特征图的映射有关.
  • 将原始图像中的ROI映射到CNN网络空间后,特征图上的空间会变小,甚至成为一个点,但是该点的通道信息会非常丰富.该信息是CNN网络上映射的ROI区域中图像信息的特征表示
  • 由于图像中的像素在空间上紧密相连,这导致了空间上的巨大冗余.因此,通常通过减少空间维度和增加通道维度来消除这种冗余,并尝试在最小维度中获得其最重要的特征.举例:图像的ROI经过CNN映射,在特征图上仅获得一个点,但是该点有85个通道线,因此,ROI的维度已经从原来的[32, 32, 3]变为当前的85维.
  • 这实际上是yolo网络对ROI执行特征提取后获得的85维特征向量.该特征向量的前四个维度表示候选框信息,中间维度表示判断对象是否存在的概率,接下来的80个维度表示80个类别的分类概率信息.

理解输出

  • yolov3网络的输入尺寸为(m, 416, 416, 3),其中m代表每个batch中图像数目.m=1,代表每个batch处理1张输入图像
  • yolov3分3个尺度进行预测,3个尺度的特征图的大小依次为13 x 13, 26 x 26, 52 x 52
  • yolov3中每个cell预测3个bounding box,每个bounding box可以表示为6元组:(tx, ty, yw, th, pc, c)
  • 在COCO数据集中一共有80个类别,此时我们将c扩展成80维向量,这样每个bounding box可以用85维向量进行表示
  • 输入图像尺寸为416*416,将采样缩小32倍,得到特征图的大小为13*13,也就是说,将输入图像划分成13*13的网格,每个cell对应输入图像中对应32*32的区域
  • 如果每个cell在原图中包含物体真实框的中心点时,那么这个cell负责预测该目标,

理解bounding box

  • yolov3网络结构中有3个分支(3个不同尺度的特征图)被送到decode函数来进一步进行解析

NMS后处理

  • 从候选框中选择置信度最高的box
  • 计算当前框和其他框的IOU,如果IOU>iou_threshold,则移除对应的box
  • 重复上述步骤进行迭代,知道剩余框中没有和当前挑选出的box重叠iou大于阈值的框
  • 上述过程主要是为了过滤和当前框具有很大重叠度的框,针对每个目标仅保留网络预测置信度最高的那个框

yolov3的输出

  • 9个anchor会被三个输出张量平分,根据大中小三种size各自取自己的anchor
  • 每个输出y在自己的网络上都会输出3个预测框,这三个框是9除以3得到的,这是作者设置的,我们可以从输出张量的维度来看
  • 13 * 13 * 255:
    • 13 * 13 : 表示网格的数量,将图片分成13 * 13个网格
    • 255 : 3 * (5 + 80)
      • 80:表示能够识别80个物品类别
      • 5:表示位置信息和置信度(x, y, w, h, confidence)
      • 3:表示要输出3个prediction;从代码上来看,3是由num_anchors得到的

作者使用了logistics回归来对每个anchor包围的内容进行了一个目标性平分(objectness score),根据目标性平分来选择anchor prior进行predict,而不是所有的anchor prior都会有输出.

yolov3输出张量解码过程

  • yolo网络输出是一个元组,包含三个张量,代表三个不同尺度,大小是1*255*13*13, 1*255*26*261*255*52*52.(255:表示每个格点输出三个预测框,每个预测框包含85个元素,一共是255个元素)
  • yolov3解码过程包括五个阶段:
    1. 缩小先验框
    2. 生成网格
    3. 生成预测框
    4. 非极大值抑制
    5. 显示预测框
  • 解码过程包含的尺寸变换:
    1. 原尺寸变换为416*416
    2. 为了在特征图上确定预测框的大小和位置,将先验框缩小(416/13, 416/26, 416/52)倍
    3. 在特征图上生成网格,根据输出张量,确定预测框的位置和大小;然后将坐标和宽高信息放大(416/13, 416/26, 416/52)倍,在原图中显示;最后将原图恢复至原来的大小.

opencv预处理:

  • blobFromImage():
    • Mat cv::dnn::blobFromImage(+ InputArray image, // 输入图像 double scalefactor = 1.0, // 图像值的乘数 const Size& size = Size(), // 输出图像的空间大小 const Scalar& mean = Scalar(),// 带有从通道中减去的平均值的标量.如果图像具有BGR排序且swapRB为真,则值应按(mean-R, mean-G, mean-B)顺序排列 bool swapRB = false, // 表示需要交换3通道图像中的第一个和最后一个通道的标志 bool crop = false, // 指示调整大小后是否裁剪图像的标志 int ddepth = CV_32F // 输出blob的深度.选择CV_32F或者CV_8U )
    • 返回格式:NCHW
    • 从图像创建4维blob.可选择从中心调整图像大小和裁剪图像,减去平均值,按比例因子缩放值,交换蓝色和红色通道

opencv后处理:

  • minMaxLoc():
    • void cv::minMaxLoc( InputArray src, // 输入的单通道数组 double* minVal // 指针返回的最小值的指针;如果不需要,则使用NULL double* maxVal = 0, // 指针返回的最大值的指针,如果不需要,则使用NULL Point* minLoc = 0, // 指向返回的最小位置的指针(在2D情况下),如果不需要,则使用NULL Point* maxLoc = 0, // 指向返回的最大位置的指针(在2D情况下),如果不需要,则使用NULL InputArray mask = noArray() // 用于选择子数组的可选掩码 )
    • 功能:查找数组中的全局最小值和最大值(和位置).
  • NMSBoxes():
    • void cv::dnn::NMSBoxes( const std::vector& bboxes, // 一组边界框来应用NMS const std::vector& scores, // 一组对应的置信度 const float score_threshold, // 用于按分数过滤的阈值 const float nms_threshold, // 用于非最大抑制的阈值 std::vector& indices, // NMS后保存的bbox索引 const flat eta = 1.f, // 自适应阈值公式中的一个系数 const int top_k = 0 // 如果>0,则最多保留top_k个选择的索引 )
    • 功能:在给定框和相应分数的情况下执行非最大抑制

yaml:用来写配置文件的语言

  • YAML,是一个可读性高,用来表达数据序列化的格式
  • YAML,是YAML Ain't a Markup Language的递归缩写
  • 使用场景:
    • 由于实现简单,解析成本很低,YAML特别适合在脚本语言中使用
    • YAML,比较适合做序列化,因为它是宿主语言数据类型直转的
    • YAML做配置文件也不错.写YAML要比写XML快得多,无需关注标签或引号,并且比ini文档功能更强