当前位置:   article > 正文

YOLOv5-Lite:更少的参数、更高的精度、更快的检测速度(附C++部署分享)

调整哪些参数可以提高yolov5的训练速度

关注并星标

从此不迷路

计算机视觉研究院

84e49825fc408537e5d69f5e96252345.gif

f65344bce38b58ef5ccb5434016281e5.gif

公众号ID|ComputerVisionGzq

学习群|扫码在主页获取加入方式

计算机视觉研究院专栏

作者:Edison_G

Yolov5-Lite,更轻更快易于部署的网络。

一、YOLOV5-Lite

43804e41b7bd98995ecd80e3299d0925.png bdf55167cdc49eaa7cab0184785268fd.png

1、Backbone与Head

54f58b0fc7149894f179211ab5399c2d.png

YOLOv5-Lite的网络结构的Backbone主要使用的是含Shuffle channel的Shuffle block组成;

检测 Head 依旧用的是 YOLOv5 head,但用的是其简化版的 YOLOv5 head

Shuffle block示意图如下:

f3c321769fe639890800d80d59747a15.png

YOLOv5 backbone:在原先U版的 YOLOv5 Backbone中,作者在特征提取的上层结构中采用了4次slice操作组成了Focus层

78d26b14fd39c25ebf37fa118d9484a0.png

YOLOv5 head:

7efe718ec3c38b51647a750d06efe6f7.png

2、Focus

在讨论Focus的作用之前,先了解两个概念:

参数数量(params):关系到模型大小,单位通常是M,通常参数用float32表示,所以模型大小是参数数量的4倍。

计算量(FLOPs):即浮点运算数,可以用来衡量算法/模型的复杂度,这关系到算法速度,大模型的单位通常为G,小模型单位通常为M;通常只考虑乘加操作的数量,而且只考虑Conv和FC等参数层的计算量,忽略BN和PReLU等,一般情况下,Conv和FC层也会忽略仅纯加操作的计算量,如bias偏置加和shoutcut残差加等,目前技术有BN和CNN可以不加bias。

params计算公式

Kh × Kw × Cin × Cout

FLOPs计算公式

Kh × Kw × Cin × Cout × H × W = 即(当前层filter × 输出的feature map)= params × H × W

总所周知,图片在经过Focus模块后,最直观的是起到了下采样的作用,但是和常用的卷积下采样有些不一样,可以对Focus的计算量和普通卷积的下采样计算量进行做个对比:

在yolov5s的网络结构中,可以看到,Focus模块的卷积核是3 × 3,输出通道是32:

aecb6563d23d4f626511ee1552b211df.png

那么做个对比:

普通下采样:即将一张640×640×3的图片输入3×3的卷积中,步长为2,输出通道32,下采样后得到320 × 320 × 32的特征图,那么普通卷积下采样理论的计算量为:

FLOPs(conv)=3×3×3×32×320×320=88473600(不考虑bias情况下) params参数量(conv)=3×3×3×32+32+32=928(后面两个32分别为bias和BN层参数)

Focus:将640×640×3的图像输入Focus结构,采用切片操作,先变成320×320×12的特征图,再经过3×3的卷积操作,输出通道32,最终变成320×320×32的特征图,那么Focus理论的计算量为:

FLOPs(Focus)=3×3×12×32×320×320=353894400(不考虑bias情况下) params参数量(Focus)=3×3×12×32+32+32=3520(为了呼应上图输出的参数量,将后面两个32分别为bias和BN层的参数考虑进去,通常这两个占比比较小可以忽略)

可以明显的看到,对于单层卷积来进行对比来看Focus的计算量和参数量要比普通卷积要多一些,是普通卷积的4倍,但是下采样时没有信息的丢失。

6ec2a983714e908ebe47a192c9c690a4.png

实际上有3层,所以经过改进后参数量其实还是变少了的,也确实达到了提速的效果,同时下采样时没有信息的丢失.

对于Focus层,在一个正方形中每 4 个相邻像素,并生成一个具有 4 倍通道数的feature map,类似与对上级图层进行了4次下采样操作,再将结果concat到一起,最主要的功能还是在不降低模型特征提取能力的前提下,对模型进行降参和加速。

e5da8ec7a7670e0f8827cc2bf00b85c0.png

Focus的python实现如下所示:

  1. class Focus(nn.Module):
  2.     # Focus wh information into c-space
  3.     def __init__(self, c1, c2, k=1, s=1, p=None, g=1, act=True):  # ch_in, ch_out, kernel, stride, padding, groups
  4.         super(Focus, self).__init__()
  5.         self.conv = Conv(c1 * 4, c2, k, s, p, g, act)      # 这里输入通道变成了4
  6.     def forward(self, x):  # x(b,c,w,h) -> y(b,4c,w/2,h/2)
  7.         return self.conv(torch.cat([x[..., ::2, ::2], x[..., 1::2, ::2], x[..., ::21::2], x[..., 1::21::2]], 1))

参数量计算如下:

  1. cuda _CudaDeviceProperties(name='Tesla T4', major=7, minor=5, total_memory=15079MB, multi_processor_count=40)
  2.       Params       FLOPS    forward (ms)   backward (ms)                   input                  output
  3.         7040       23.07           62.89           87.79       (163640640)      (1664320320)
  4.         7040       23.07           15.52           48.69       (163640640)      (1664320320)
  5. cuda _CudaDeviceProperties(name='Tesla T4', major=7, minor=5, total_memory=15079MB, multi_processor_count=40)
  6.       Params       FLOPS    forward (ms)   backward (ms)                   input                  output
  7.         7040       23.07           11.61           79.72       (163640640)      (1664320320)
  8.         7040       23.07           12.54           42.94       (163640640)      (1664320320)

从上可以看出,Focus层确实在参数降低的情况下,对模型实现了加速。

但!这个加速是有前提的,必须在GPU的使用下才可以体现这一优势,对于云端部署这种处理方式,GPU不太需要考虑缓存的占用,即取即处理的方式让Focus层在GPU设备上十分work。

对于的芯片,特别是不含GPU、NPU加速的芯片,频繁的slice操作只会让缓存占用严重,加重计算处理的负担。同时,在芯片部署的时候,Focus层的转化对新手极度不友好。

二、轻量化的理念

shufflenetv2的设计理念,在计算资源有限的边缘端,有着重要的意义,它提出模型轻量化的4条原则:

  1. 同等通道大小可以最小化内存访问量

  2. 过量使用组卷积会增加MAC

  3. 网络过于碎片化(特别是多路)会降低并行度

  4. 不能忽略元素级操作(比如shortcut和Add)

‍‍‍‍‍‍

三、YOLOv5-Lite设计理念

  1. 摘除Focus层,避免多次采用slice操作

  2. 避免多次使用C3 Leyer以及高通道的C3 Layer(C3 Leyer是YOLOv5作者提出的CSPBottleneck改进版本,它更简单、更快、更轻,在近乎相似的损耗上能取得更好的结果。但C3 Layer采用多路分离卷积,测试证明,频繁使用C3 Layer以及通道数较高的C3 Layer,占用较多的缓存空间,减低运行速度)

  1. class C3(nn.Module):
  2.     # CSP Bottleneck with 3 convolutions
  3.     def __init__(self, c1, c2, n=1, shortcut=True, g=1, e=0.5):  # ch_in, ch_out, number, shortcut, groups, expansion
  4.         super(C3, self).__init__()
  5.         c_ = int(c2 * e)  # hidden channels
  6.         self.cv1 = Conv(c1, c_, 11)
  7.         self.cv2 = Conv(c1, c_, 11)
  8.         self.cv3 = Conv(2 * c_, c2, 1
  9.         self.m = nn.Sequential(*[Bottleneck(c_, c_, shortcut, g, e=1.0for _ in range(n)])
  10.         # self.m = nn.Sequential(*[CrossConv(c_, c_, 31, g, 1.0, shortcut) for _ in range(n)])
  1. 对yolov5 head进行通道剪枝,剪枝细则参考G1

  2. 摘除shufflenetv2 backbone的1024 conv 和 5×5 pooling

这是为imagenet打榜而设计的模块,在实际业务场景并没有这么多类的情况下,可以适当摘除,精度不会有太大影响,但对于速度是个大提升,在消融实验中也证实了这点。

四、Tengine部署YOLOv5-Lite

依照顺序调用Tengine核心API如下:

1. init_tengine

初始化Tengine,该函数在程序中只要调用一次即可。

2. create_graph

创建Tengine计算图。

3. prerun_graph

预运行,准备计算图推理所需资源。设置大小核,核个数、核亲和性、数据精度都在这里。

  1. struct options
  2. {
  3.   int num_thread;//核个数设置,
  4.   int cluster;//大小核设置,可选TENGINE_CLUSTER_[ALL,BIG,MEDIUM,LITTLE]
  5.   int precision;//精度设置,TENGINE_MODE_[FP32,FP16,HYBRID_INT8,UINT8,INT8]
  6.   uint64_t affinity;//核亲和性掩码,绑定具体核,
  7. };
4. run_graph

启动Tengine计算图推理。

5. postrun_graph

停止运行graph,并释放graph占据的资源。

6. destroy_graph

销毁graph。

694470ec53188495daf4f4e1eb0033d8.png

1、图像自适应缩放

在训练阶段,比如网络输入的尺寸608×608,但我数据的尺寸是大小不一的,一般方法是直接同一缩放到标准尺寸,然后填充黑边,如下图所示:

56a0aba0364674bda470d67dbe34f54f.png

但如果填充的比较多,则存在信息冗余,影响推理速度。Yolov5在推理阶段,采用缩减黑边的方式,来提高推理的速度。在代码datasets.py的letterbox函数中进行了修改,对原始图像自适应的添加最少的黑边。eg:“比如我1000×800的图片不是直接缩放到608×608的大小,而是计算608/1000=0.608 然后缩放至608×486的大小,然后计算608-486=122 然后np.mod(122,32)取余数得到26,再平均成13填充到图片高度两端,最后是608×512。”

8d9bc33a6abce5755afc734f088ae4e6.png
  1. def letterbox(img, new_shape=(640640), color=(114114114), auto=True, scaleFill=False, scaleup=True, stride=32):
  2.     # Resize and pad image while meeting stride-multiple constraints
  3.     shape = img.shape[:2]  # current shape [height, width]
  4.     if isinstance(new_shape, int):
  5.         new_shape = (new_shape, new_shape)
  6.     # Scale ratio (new / old)
  7.     r = min(new_shape[0] / shape[0], new_shape[1] / shape[1])
  8.     if not scaleup:  # only scale down, do not scale up (for better test mAP)
  9.         r = min(r, 1.0)
  10.     # Compute padding
  11.     ratio = r, r  # width, height ratios
  12.     new_unpad = int(round(shape[1] * r)), int(round(shape[0] * r))
  13.     dw, dh = new_shape[1] - new_unpad[0], new_shape[0] - new_unpad[1]  # wh padding
  14.     if auto:  # minimum rectangle
  15.         dw, dh = np.mod(dw, stride), np.mod(dh, stride)  # wh padding
  16.     elif scaleFill:  # stretch
  17.         dw, dh = 0.00.0
  18.         new_unpad = (new_shape[1], new_shape[0])
  19.         ratio = new_shape[1] / shape[1], new_shape[0] / shape[0]  # width, height ratios
  20.     dw /= 2  # divide padding into 2 sides
  21.     dh /= 2
  22.     if shape[::-1] != new_unpad:  # resize
  23.         img = cv2.resize(img, new_unpad, interpolation=cv2.INTER_LINEAR)
  24.     top, bottom = int(round(dh - 0.1)), int(round(dh + 0.1))
  25.     left, right = int(round(dw - 0.1)), int(round(dw + 0.1))
  26.     img = cv2.copyMakeBorder(img, top, bottom, left, right, cv2.BORDER_CONSTANT, value=color)  # add border
  27.     return img, ratio, (dw, dh)

C++版本如下:

  1. void get_input_data_focus(const char* image_file, float* input_data, int img_h, int img_w, const float* mean, const float* scale)
  2. {
  3.     cv::Mat sample = cv::imread(image_file, 1);
  4.     cv::Mat img;
  5.     const int target_size = 640;
  6.     int imge_w = img.cols;
  7.     int imge_h = img.rows;
  8.     int w = imge_w;
  9.     int h = imge_h;
  10.     float scale_im = 1.f;
  11.     if (w > h)
  12.     {
  13.         scale_im = (float)target_size / w;
  14.         w = target_size;
  15.         h = h * scale_im;
  16.     }
  17.     else
  18.     {
  19.         scale_im = (float)target_size / h;
  20.         h = target_size;
  21.         w = w * scale_im;
  22.     }
  23.     cv::cvtColor(sample, img, cv::COLOR_BGR2RGB);
  24.     cv::resize(img, img, cv::Size(w, h));
  25.     // pad to target_size rectangle
  26.     int wpad = (w + 31) / 32 * 32 - w;
  27.     int hpad = (h + 31) / 32 * 32 - h;
  28.     cv::Mat in_pad;
  29.     cv::copy_make_border(img, in_pad, hpad / 2, hpad - hpad / 2, wpad / 2, wpad - wpad / 2, cv::BORDER_CONSTANT, 114.f);
  30.     img.convertTo(img, CV_32FC3);
  31.     float* img_data = (float*)img.data;
  32.     /* nhwc to nchw */
  33.     for (int h = 0; h < img_h; h++)
  34.     {
  35.         for (int w = 0; w < img_w; w++)
  36.         {
  37.             for (int c = 0; c < 3; c++)
  38.             {
  39.                 int in_index = h * img_w * 3 + w * 3 + c;
  40.                 int out_index = c * img_h * img_w + h * img_w + w;
  41.                 input_data[out_index] = (img_data[in_index] - mean[c]) * scale[c];
  42.             }
  43.         }
  44.     }
  45. }
a15da35b765c1896fe6229d10ff2b39c.png

2、模型加载和推理

  1. /* set runtime options */
  2.     struct options opt;
  3.     opt.num_thread = num_thread;
  4.     opt.cluster = TENGINE_CLUSTER_ALL;
  5.     opt.precision = TENGINE_MODE_FP32;
  6.     opt.affinity = 0;
  7.     /* inital tengine */
  8.     if (init_tengine() != 0)
  9.     {
  10.         fprintf(stderr, "Initial tengine failed.\n");
  11.         return -1;
  12.     }
  13.     fprintf(stderr, "tengine-lite library version: %s\n", get_tengine_version());
  14.     /* create graph, load tengine model xxx.tmfile */
  15.     graph_t graph = create_graph(nullptr, "tengine", model_file);
  16.     if (graph == nullptr)
  17.     {
  18.         fprintf(stderr, "Create graph failed.\n");
  19.         return -1;
  20.     }

3 获取推理结果

  1. /* yolov5 postprocess */
  2.     // 0: 1, 3, 20, 20, 85
  3.     // 1: 1, 3, 40, 40, 85
  4.     // 2: 1, 3, 80, 80, 85
  5.     tensor_t p8_output = get_graph_output_tensor(graph, 00);
  6.     tensor_t p16_output = get_graph_output_tensor(graph, 10);
  7.     tensor_t p32_output = get_graph_output_tensor(graph, 20);
  8.     float* p8_data = (float*)get_tensor_buffer(p8_output);
  9.     float* p16_data = (float*)get_tensor_buffer(p16_output);
  10.     float* p32_data = (float*)get_tensor_buffer(p32_output);
  11.     /* postprocess */
  12.     const float prob_threshold = 0.55;
  13.     const float nms_threshold = 0.5;
  14.     std::vector<Object> proposals;
  15.     std::vector<Object> objects8;
  16.     std::vector<Object> objects16;
  17.     std::vector<Object> objects32;
  18.     std::vector<Object> objects;
  19.     generate_proposals(32, p32_data, prob_threshold, objects32, letterbox_cols, letterbox_rows);
  20.     proposals.insert(proposals.end(), objects32.begin(), objects32.end());
  21.     generate_proposals(16, p16_data, prob_threshold, objects16, letterbox_cols, letterbox_rows);
  22.     proposals.insert(proposals.end(), objects16.begin(), objects16.end());
  23.     generate_proposals(8, p8_data, prob_threshold, objects8, letterbox_cols, letterbox_rows);
  24.     proposals.insert(proposals.end(), objects8.begin(), objects8.end());
  25.     qsort_descent_inplace(proposals);
  26.     std::vector<int> picked;
  27.     nms_sorted_bboxes(proposals, picked, nms_threshold);

4 后处理

  1. static void nms_sorted_bboxes(const std::vector<Object>& faceobjects, std::vector<int>& picked, float nms_threshold)
  2. {
  3.     picked.clear();
  4.     const int n = faceobjects.size();
  5.     std::vector<float> areas(n);
  6.     for (int i = 0; i < n; i++)
  7.     {
  8.         areas[i] = faceobjects[i].rect.area();
  9.     }
  10.     for (int i = 0; i < n; i++)
  11.     {
  12.         const Object& a = faceobjects[i];
  13.         int keep = 1;
  14.         for (int j = 0; j < (int)picked.size(); j++)
  15.         {
  16.             const Object& b = faceobjects[picked[j]];
  17.             // intersection over union
  18.             float inter_area = intersection_area(a, b);
  19.             float union_area = areas[i] + areas[picked[j]] - inter_area;
  20.             // float IoU = inter_area / union_area
  21.             if (inter_area / union_area > nms_threshold)
  22.                 keep = 0;
  23.         }
  24.         if (keep)
  25.             picked.push_back(i);
  26.     }
  27. }
  28. static void generate_proposals(int stride, const float* feat, float prob_threshold, std::vector<Object>& objects,
  29.                                int letterbox_cols, int letterbox_rows)
  30. {
  31.     //static float anchors[18] = {10, 13, 16, 30, 33, 23, 30, 61, 62, 45, 59, 119, 116, 90, 156, 198, 373, 326};
  32.     static float anchors[18] = {101316303323,
  33.                                 3061624559119,
  34.                                 11690156198373326};
  35.     int anchor_num = 3;
  36.     int feat_w = letterbox_cols / stride;
  37.     int feat_h = letterbox_rows / stride;
  38.     int cls_num = 80;
  39.     int anchor_group;
  40.     if (stride == 8)
  41.         anchor_group = 1;
  42.     if (stride == 16)
  43.         anchor_group = 2;
  44.     if (stride == 32)
  45.         anchor_group = 3;
  46.     for (int h = 0; h <= feat_h - 1; h++)
  47.     {
  48.         for (int w = 0; w <= feat_w - 1; w++)
  49.         {
  50.             for (int a = 0; a <= anchor_num - 1; a++)
  51.             {
  52.                 //process cls score
  53.                 int class_index = 0;
  54.                 float class_score = -FLT_MAX;
  55.                 for (int s = 0; s <= cls_num - 1; s++)
  56.                 {
  57.                     float score = feat[a * feat_w * feat_h * (cls_num + 5) + h * feat_w * (cls_num + 5) + w * (cls_num + 5) + s + 5];
  58.                     if (score > class_score)
  59.                     {
  60.                         class_index = s;
  61.                         class_score = score;
  62.                     }
  63.                 }
  64.                 //process box score
  65.                 float box_score = feat[a * feat_w * feat_h * (cls_num + 5) + (h * feat_w) * (cls_num + 5) + w * (cls_num + 5) + 4];
  66.                 float final_score = sigmoid(box_score) * sigmoid(class_score);
  67.                 if (final_score >= prob_threshold)
  68.                 {
  69.                     int loc_idx = a * feat_h * feat_w * (cls_num + 5) + h * feat_w * (cls_num + 5) + w * (cls_num + 5);
  70.                     float dx = sigmoid(feat[loc_idx + 0]);
  71.                     float dy = sigmoid(feat[loc_idx + 1]);
  72.                     float dw = sigmoid(feat[loc_idx + 2]);
  73.                     float dh = sigmoid(feat[loc_idx + 3]);
  74.                     float pred_cx = (dx * 2.0f - 0.5f + w) * stride;
  75.                     float pred_cy = (dy * 2.0f - 0.5f + h) * stride;
  76.                     float anchor_w = anchors[(anchor_group - 1) * 6 + a * 2 + 0];
  77.                     float anchor_h = anchors[(anchor_group - 1) * 6 + a * 2 + 1];
  78.                     float pred_w = dw * dw * 4.0f * anchor_w;
  79.                     float pred_h = dh * dh * 4.0f * anchor_h;
  80.                     float x0 = pred_cx - pred_w * 0.5f;
  81.                     float y0 = pred_cy - pred_h * 0.5f;
  82.                     float x1 = pred_cx + pred_w * 0.5f;
  83.                     float y1 = pred_cy + pred_h * 0.5f;
  84.                     Object obj;
  85.                     obj.rect.x = x0;
  86.                     obj.rect.y = y0;
  87.                     obj.rect.width = x1 - x0;
  88.                     obj.rect.height = y1 - y0;
  89.                     obj.label = class_index;
  90.                     obj.prob = final_score;
  91.                     objects.push_back(obj);
  92.                 }
  93.             }
  94.         }
  95.     }
  96. }

5 绘图

  1. static void draw_objects(const cv::Mat& bgr, const std::vector<Object>& objects)
  2. {
  3.     static const char* class_names[] = {
  4.             "person""bicycle""car""motorcycle""airplane""bus""train""truck""boat""traffic light",
  5.             "fire hydrant""stop sign""parking meter""bench""bird""cat""dog""horse""sheep""cow",
  6.             "elephant""bear""zebra""giraffe""backpack""umbrella""handbag""tie""suitcase""frisbee",
  7.             "skis""snowboard""sports ball""kite""baseball bat""baseball glove""skateboard""surfboard",
  8.             "tennis racket""bottle""wine glass""cup""fork""knife""spoon""bowl""banana""apple",
  9.             "sandwich""orange""broccoli""carrot""hot dog""pizza""donut""cake""chair""couch",
  10.             "potted plant""bed""dining table""toilet""tv""laptop""mouse""remote""keyboard""cell phone",
  11.             "microwave""oven""toaster""sink""refrigerator""book""clock""vase""scissors""teddy bear",
  12.             "hair drier""toothbrush"};
  13.     cv::Mat image = bgr.clone();
  14.     for (size_t i = 0; i < objects.size(); i++)
  15.     {
  16.         const Object& obj = objects[i];
  17.         fprintf(stderr, "%2d: %3.0f%%, [%4.0f, %4.0f, %4.0f, %4.0f], %s\n", obj.label, obj.prob * 100, obj.rect.x,
  18.                 obj.rect.y, obj.rect.x + obj.rect.width, obj.rect.y + obj.rect.height, class_names[obj.label]);
  19.         cv::rectangle(image, obj.rect, cv::Scalar(25500));
  20.         char text[256];
  21.         sprintf(text, "%s %.1f%%", class_names[obj.label], obj.prob * 100);
  22.         int baseLine = 0;
  23.         cv::Size label_size = cv::getTextSize(text, cv::FONT_HERSHEY_SIMPLEX, 0.52, &baseLine);
  24.         int x = obj.rect.x;
  25.         int y = obj.rect.y - label_size.height - baseLine;
  26.         if (y < 0)
  27.             y = 0;
  28.         if (x + label_size.width > image.cols)
  29.             x = image.cols - label_size.width;
  30.         cv::rectangle(image, cv::Rect(cv::Point(x, y), cv::Size(label_size.width, label_size.height + baseLine)),
  31.                       cv::Scalar(255255255), -1);
  32.         cv::putText(image, text, cv::Point(x, y + label_size.height), cv::FONT_HERSHEY_SIMPLEX, 0.5,
  33.                     cv::Scalar(000));
  34.     }
  35.     cv::imwrite("yolov5s_out.jpg", image);
  36. }
  37. /* yolov5 draw the result */
  38.     float scale_letterbox;
  39.     int resize_rows;
  40.     int resize_cols;
  41.     if ((letterbox_rows * 1.0 / img.rows) < (letterbox_cols * 1.0 / img.cols))
  42.     {
  43.         scale_letterbox = letterbox_rows * 1.0 / img.rows;
  44.     }
  45.     else
  46.     {
  47.         scale_letterbox = letterbox_cols * 1.0 / img.cols;
  48.     }
  49.     resize_cols = int(scale_letterbox * img.cols);
  50.     resize_rows = int(scale_letterbox * img.rows);
  51.     int tmp_h = (letterbox_rows - resize_rows) / 2;
  52.     int tmp_w = (letterbox_cols - resize_cols) / 2;
  53.     float ratio_x = (float)img.rows / resize_rows;
  54.     float ratio_y = (float)img.cols / resize_cols;
  55.     int count = picked.size();
  56.     fprintf(stderr, "detection num: %d\n", count);
  57.     objects.resize(count);
  58.     for (int i = 0; i < count; i++)
  59.     {
  60.         objects[i] = proposals[picked[i]];
  61.         float x0 = (objects[i].rect.x);
  62.         float y0 = (objects[i].rect.y);
  63.         float x1 = (objects[i].rect.x + objects[i].rect.width);
  64.         float y1 = (objects[i].rect.y + objects[i].rect.height);
  65.         x0 = (x0 - tmp_w) * ratio_x;
  66.         y0 = (y0 - tmp_h) * ratio_y;
  67.         x1 = (x1 - tmp_w) * ratio_x;
  68.         y1 = (y1 - tmp_h) * ratio_y;
  69.         x0 = std::max(std::min(x0, (float)(img.cols - 1)), 0.f);
  70.         y0 = std::max(std::min(y0, (float)(img.rows - 1)), 0.f);
  71.         x1 = std::max(std::min(x1, (float)(img.cols - 1)), 0.f);
  72.         y1 = std::max(std::min(y1, (float)(img.rows - 1)), 0.f);
  73.         objects[i].rect.x = x0;
  74.         objects[i].rect.y = y0;
  75.         objects[i].rect.width = x1 - x0;
  76.         objects[i].rect.height = y1 - y0;
  77.     }
  78.     draw_objects(img, objects);
  79.     /* release tengine */
  80.     postrun_graph(graph);
  81.     destroy_graph(graph);
  82.     release_tengine();

6 可视化推理结果

ba92bace258dc5fa6e15f5f66c4fb1de.png 4c0604d0b162b6eef86a3947a0802f90.png

五、YOLOv5-Lite对比结果

938d1212e7f698ffd48f80aab6230270.png 7344d2ea0d7bd5e3c3379493ad3d0155.png 199fea8cce46eab6369f4e76006c9c7a.png

六、参考

[1].https://blog.csdn.net/weixin_45829462/article/details/119767896
[2].https://github.com/OAID/Tengine
[3].https://github.com/ppogg/YOLOv5-Lite

© THE END 

转载请联系本公众号获得授权

6116ba6b65ba051d2a0a9876e2b2cb9c.gif

计算机视觉研究院学习群等你加入!

计算机视觉研究院主要涉及深度学习领域,主要致力于人脸检测、人脸识别,多目标检测、目标跟踪、图像分割等研究方向。研究院接下来会不断分享最新的论文算法新框架,我们这次改革不同点就是,我们要着重”研究“。之后我们会针对相应领域分享实践过程,让大家真正体会摆脱理论的真实场景,培养爱动手编程爱动脑思考的习惯!

c9a3c2e137c9e154578e91f9e66afac9.png

扫码关注

计算机视觉研究院

公众号ID|ComputerVisionGzq

学习群|扫码在主页获取加入方式

 往期推荐 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/我家自动化/article/detail/147364
推荐阅读
相关标签