简介
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*26
和1*255*52*52
.(255:表示每个格点输出三个预测框,每个预测框包含85个元素,一共是255个元素) - yolov3解码过程包括五个阶段:
- 缩小先验框
- 生成网格
- 生成预测框
- 非极大值抑制
- 显示预测框
- 解码过程包含的尺寸变换:
- 原尺寸变换为416*416
- 为了在特征图上确定预测框的大小和位置,将先验框缩小(416/13, 416/26, 416/52)倍
- 在特征图上生成网格,根据输出张量,确定预测框的位置和大小;然后将坐标和宽高信息放大(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.可选择从中心调整图像大小和裁剪图像,减去平均值,按比例因子缩放值,交换蓝色和红色通道
- Mat cv::dnn::blobFromImage(+
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() // 用于选择子数组的可选掩码
) - 功能:查找数组中的全局最小值和最大值(和位置).
- void cv::minMaxLoc(
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个选择的索引
) - 功能:在给定框和相应分数的情况下执行非最大抑制
- void cv::dnn::NMSBoxes(
yaml:用来写配置文件的语言
- YAML,是一个可读性高,用来表达数据序列化的格式
- YAML,是
YAML Ain't a Markup Language
的递归缩写 - 使用场景:
- 由于实现简单,解析成本很低,YAML特别适合在脚本语言中使用
- YAML,比较适合做序列化,因为它是宿主语言数据类型直转的
- YAML做配置文件也不错.写YAML要比写XML快得多,无需关注标签或引号,并且比ini文档功能更强