模型部署之——yolov5、yolov7等基于anchor-based解码后处理过程

提示:本文为半成品暂时禁止转载。


前言

假设输入为640*640的图像,经过fpn-pan结构得到20 * 20, 40 * 40, 80 * 80三种尺度的特征图:
20 * 20的特征层对应的anchor是[116,90], [156,198], [373,326];
40 * 40的特征层对应的anchor是[30,61], [62,45], [59,119];
80 * 80的特征层对应的anchor是[10,13], [16,30], [33,23];
三个特征层上的缩放32、16、8。


提示:以下是本篇文章正文内容,下面案例可供参考
首先计算20*20特征层的操作

一、先求出anchor在不同特征层上的缩放

计算方式: [116,90],[156,198],[373,326] / 32
结果: scaled_anchors = [(3.625, 2.8125), (4.875, 6.1875), (11.65625, 10.1875)]

二、修改输入维度

原始维度为: batch_size, 3 * (4 + 1 + num_classes), 20, 20
修改后的output维度:batch_size, 3 * (4 + 1 + num_classes), 20, 20 => batch_size, 3, 20, 20, 4 + 1 + num_classes 即:[4, 3, 20, 20, 85]
需要注意的是:一般输入255那个维度是3*85得出来的,而不是85*3得出来的,因此在维度转换的时候需要现将255先resize为3*85,然后再做permute维度变换,这点在写板端代码的时候需要特别注意。

三、对输出x,y,w,h,conf,pred做sigmod

x = sigmod(output[...,0])   维度为:[4, 3, 20, 20]
y = sigmod(output[...,1])   维度为:[4, 3, 20, 20]
w = sigmod(output[...,2])  维度为:[4, 3, 20, 20]
h = sigmod(output[...,3])   维度为:[4, 3, 20, 20]
conf = sigmod(output[...,4])  维度为:[4, 3, 20, 20]
pred = sigmod(output[...,5:]) 维度为:[4, 3, 20, 20, 80]

四、生成网格,先验框中心,网格左上角的坐标矩阵

grid_x = [[[[ 0.,  1.,  2.,  ..., 17., 18., 19.],
      [ 0.,  1.,  2.,  ..., 17., 18., 19.],
      [ 0.,  1.,  2.,  ..., 17., 18., 19.],
      ...,
      [ 0.,  1.,  2.,  ..., 17., 18., 19.],
      [ 0.,  1.,  2.,  ..., 17., 18., 19.],
      [ 0.,  1.,  2.,  ..., 17., 18., 19.]]]

第三和第四个维度填充的是 [ 0., 1., 2., …, 17., 18., 19.] 重复20次的矩阵,后面batch和 3 都只是重复把这个矩阵增加维度而已

grid_y = [[[[ 0.,  0.,  0.,  ...,  0.,  0.,  0.],
      [ 1.,  1.,  1.,  ...,  1.,  1.,  1.],
      [ 2.,  2.,  2.,  ...,  2.,  2.,  2.],
      ...,
      [17., 17., 17.,  ..., 17., 17., 17.],
      [18., 18., 18.,  ..., 18., 18., 18.],
      [19., 19., 19.,  ..., 19., 19., 19.]]]

注:grid_y类似grid_x的转置。

五、处理anchor

将步骤一中的scaled_anchors的w取出来形成二维矩阵 3*1

anchor_w = [[ 3.6250],
    [ 4.8750],
    [11.6562]]

将步骤一中的scaled_anchors的h取出来形成二维矩阵 3*1

	anchor_h =[[ 2.8125],
    [ 6.1875],
    [10.1875]]

将anchor_w扩充维度为 [4, 3, 20, 20]

anchor_w = [[[[3.6250,  3.6250,  3.6250,  ...,  3.6250,  3.6250,  3.6250],
      [ 3.6250,  3.6250,  3.6250,  ...,  3.6250,  3.6250,  3.6250],
      [ 3.6250,  3.6250,  3.6250,  ...,  3.6250,  3.6250,  3.6250],
      ...,
      [ 3.6250,  3.6250,  3.6250,  ...,  3.6250,  3.6250,  3.6250],
      [ 3.6250,  3.6250,  3.6250,  ...,  3.6250,  3.6250,  3.6250],
      [ 3.6250,  3.6250,  3.6250,  ...,  3.6250,  3.6250,  3.6250]],

     [[ 4.8750,  4.8750,  4.8750,  ...,  4.8750,  4.8750,  4.8750],
      [ 4.8750,  4.8750,  4.8750,  ...,  4.8750,  4.8750,  4.8750],
      [ 4.8750,  4.8750,  4.8750,  ...,  4.8750,  4.8750,  4.8750],
      ...,
      [ 4.8750,  4.8750,  4.8750,  ...,  4.8750,  4.8750,  4.8750],
      [ 4.8750,  4.8750,  4.8750,  ...,  4.8750,  4.8750,  4.8750],
      [ 4.8750,  4.8750,  4.8750,  ...,  4.8750,  4.8750,  4.8750]],

     [[11.6562, 11.6562, 11.6562,  ..., 11.6562, 11.6562, 11.6562],
      [11.6562, 11.6562, 11.6562,  ..., 11.6562, 11.6562, 11.6562],
      [11.6562, 11.6562, 11.6562,  ..., 11.6562, 11.6562, 11.6562],
      ...,
      [11.6562, 11.6562, 11.6562,  ..., 11.6562, 11.6562, 11.6562],
      [11.6562, 11.6562, 11.6562,  ..., 11.6562, 11.6562, 11.6562],
      [11.6562, 11.6562, 11.6562,  ..., 11.6562, 11.6562, 11.6562]]]

第三个和第四个维度填充的是 3.6250 形成形同元素的 20*20 矩阵。

第二个维度填充的是anchor_w 下一个元素形成相同元素的 20*20 矩阵。

anchor_h的操作也是一样的

anchor_h = [[[[ 2.8125,  2.8125,  2.8125,  ...,  2.8125,  2.8125,  2.8125],
      [ 2.8125,  2.8125,  2.8125,  ...,  2.8125,  2.8125,  2.8125],
      [ 2.8125,  2.8125,  2.8125,  ...,  2.8125,  2.8125,  2.8125],
      ...,
      [ 2.8125,  2.8125,  2.8125,  ...,  2.8125,  2.8125,  2.8125],
      [ 2.8125,  2.8125,  2.8125,  ...,  2.8125,  2.8125,  2.8125],
      [ 2.8125,  2.8125,  2.8125,  ...,  2.8125,  2.8125,  2.8125]],

     [[ 6.1875,  6.1875,  6.1875,  ...,  6.1875,  6.1875,  6.1875],
      [ 6.1875,  6.1875,  6.1875,  ...,  6.1875,  6.1875,  6.1875],
      [ 6.1875,  6.1875,  6.1875,  ...,  6.1875,  6.1875,  6.1875],
      ...,
      [ 6.1875,  6.1875,  6.1875,  ...,  6.1875,  6.1875,  6.1875],
      [ 6.1875,  6.1875,  6.1875,  ...,  6.1875,  6.1875,  6.1875],
      [ 6.1875,  6.1875,  6.1875,  ...,  6.1875,  6.1875,  6.1875]],

     [[10.1875, 10.1875, 10.1875,  ..., 10.1875, 10.1875, 10.1875],
      [10.1875, 10.1875, 10.1875,  ..., 10.1875, 10.1875, 10.1875],
      [10.1875, 10.1875, 10.1875,  ..., 10.1875, 10.1875, 10.1875],
      ...,
      [10.1875, 10.1875, 10.1875,  ..., 10.1875, 10.1875, 10.1875],
      [10.1875, 10.1875, 10.1875,  ..., 10.1875, 10.1875, 10.1875],
      [10.1875, 10.1875, 10.1875,  ..., 10.1875, 10.1875, 10.1875]]]]

六、解码步骤三中的输出,得到新的x,y,w,h

将输出的 x y w h 进一步按照下面公式进行解码

    #   x  0 ~ 1 => 0 ~ 2 => -0.5 ~ 1.5 + grid_x
    #   y  0 ~ 1 => 0 ~ 2 => -0.5 ~ 1.5 + grid_y
    #   w  0 ~ 1 => 0 ~ 2 => 0 ~ 4 * anchor_w
    #   h  0 ~ 1 => 0 ~ 2 => 0 ~ 4 * anchor_h 

x, y, w, h, grid_x, grid_y, anc

YOLOv8后处理cpp是指在使用YOLOv8目标检测算法进行物体检测后,对检测结果进行进一步处理的过程。这个后处理cpp的主要目的是对检测结果进行筛选、分类和定位,以获得更准确的物体检测结果。 具体来说,后处理cpp通常包括以下几个步骤: 首先,对检测结果进行筛选,去除低置信度的边界框。一般会设置一个阈值,只保留置信度高于该阈值的边界框,以提高检测的准确性。 然后,对筛选后的边界框进行分类,判断每个边界框内的物体属于哪个类别。一般会使用预先训练好的分类模型来进行分类,根据物体的特征来确定其类别。这样可以将检测结果与已知类别进行匹配,得到更加具体的物体信息。 接下来,对分类后的边界框进行定位,确定物体在图像中的准确位置。这一步可以通过计算边界框的中心点坐标和宽高等参数来实现。通过定位,可以更精确地确定物体在图像中的位置。 最后,根据物体检测的结果,可以进行一系列的应用,比如目标跟踪、行为分析、智能监控等。这些应用可以根据具体情况进行选择和扩展,以满足不同的需求。 总之,YOLOv8后处理cpp通过筛选、分类和定位等步骤,对物体检测的结果进行进一步处理,提高检测的准确性和可用性。这个后处理cpp能够帮助我们更好地理解和利用物体检测的结果,为各种应用场景提供更精确的物体信息。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值