• [BPU部署教程] 教你搞定YOLOV5部署 (版本: 6.2)


    最近一些人问我怎么在BPU上部署yolov5,因为之前的博客[BPU部署教程] 一文带你轻松走出模型部署新手村介绍的网络都是基于Caffe的,自己的网络都是基于pytorch的,所以遇到了很多坑。鉴于这些需求,我自己研究了下部署的方式,把自己的过程整理下来供各位参考(看我这么好的份上,来个三连吧o( ̄▽ ̄)ブ)。

    在部署之前,我先说明几点

    • 本教程使用的一些文件都放在百度云(提取码:0a09 )里了,可以自行下载使用
    • yolov5的代码我用的是https://github.com/ultralytics/yolov5的master分支,目前应该是版本6.2,后续作者更新后,可以切换到6.2分支来使用。
    • 尽管本教程转换模型用的是官方模型,但各位可以训练自己的模型来转换,模型转换几乎没有啥差异,可放心食用。
    • 代码版本我应该和官方使用的不一样,X3P给的官方测试时间是45ms左右,但我转换模型时候,发现有较多的层跑在CPU,导致推理时间多很多。
    • 很多人说了后处理过程耗时太高,后处理计算在python跑的话,确实速度会慢很多,所以我以这个,顺便教各位如何利用cython来包装自己的C++代码来加速,最后测试时候,python版的后处理耗时越450ms,用cython包装后,后处理仅用9ms

    下面,开始我们的部署之旅。

    一 环境配置

    1.1 安装依赖包

    如果在当前python环境下能利用pip install onnx轻松安装onnx,那就直接配置yolov5的环境就行了。

    我电脑是windows,又不想安装虚拟机,所以环境的安装有一些限制。我最开始的python环境是3.6版本,结果安装onnx时候一堆问题。3.10有onnx的whl包,所以,建议跑模型的python环境为3.10(3.8好像也可,自己试试)。

    下面给出我部署的指令流程,对于的pytorch包我也放在百度云了。

    # 在指定路径下创建虚拟环境,我的anaconda安装在c盘,但我想把环境放在d盘,所以利用--prefix D:\Anaconda3\envs\yolov5指定路径
    conda create --prefix D:\Anaconda3\envs\yolov5 python=3.10
    # 切换虚拟环境
    conda activate D:\Anaconda3\envs\yolov5
    # 安装关键包ONNX
    pip install -i https://pypi.tuna.tsinghua.edu.cn/simple onnx
    # 安装yolov5依赖的pytorch
    pip install "torch-1.11.0+cu113-cp310-cp310-win_amd64.whl" "torchaudio-0.11.0+cu113-cp310-cp310-win_amd64.whl" "torchvision-0.12.0+cu113-cp310-cp310-win_amd64.whl" -i https://pypi.tuna.tsinghua.edu.cn/simple
    # 安装yolov5需要的包
    pip install -i https://pypi.tuna.tsinghua.edu.cn/simple matplotlib>=3.2.2 numpy>=1.18.5 opencv-python>=4.1.1 Pillow>=7.1.2 PyYAML>=5.3.1 requests>=2.23.0 scipy>=1.4.1 tqdm>=4.64.0 tensorboard>=2.4.1 pandas>=1.1.4 seaborn>=0.11.0 ipython psutil thop>=0.1.1
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    1.2 运行YoloV5

    下载百度云中提供的文件,按照如下流程操作:

    1. 解压yolov5-master.zip
    2. zidane.jpg放到yolov5-master文件夹中。
    3. yolov5s.pt放到yolov5-master/models文件夹中。
    4. 进入yolov5-master文件夹,输入python .\detect.py --weights .\models\yolov5s.pt --source zidane.jpg,代码会输出检测结果保存路径,比如我的就是Results saved to runs\detect\exp9,检测结果如下所示。
      在这里插入图片描述

    1.3 pytorch的pt模型文件转onnx

    转换的时候,一定要清楚一点,BPU的工具链没有支持onnx的所有版本的算子,即当前BPU支持onnx的opset版本为10和11。错误的opset在模型检查阶段就无法通过,在后面我给各位展示转换失败的结果。

    # 错误的转换指令
    python .\export.py --weights .\models\yolov5s.pt --include onnx
    # 正确的转换指令
    python .\export.py --weights .\models\yolov5s.pt --include onnx --opset 11
    
    • 1
    • 2
    • 3
    • 4

    转换后,控制台会输出一些log信息,转换后的模型文件在.\\models\\yolov5s.pt

    export: data=D:\05 - \01 - x3\BPUCodes\yolov5\yolov5-master\data\coco128.yaml, weights=['.\\models\\yolov5s.pt'], imgsz=[640, 640], batch_size=1, device=cpu, half=False, inplace=False, train=False, keras=False, optimize=False, int8=False, dynamic=False, simplify=False, opset=12, verbose=False, workspace=4, nms=False, agnostic_nms=False, topk_per_class=100, topk_all=100, iou_thres=0.45, conf_thres=0.25, include=['onnx']
    YOLOv5  2022-9-1 Python-3.10.4 torch-1.11.0+cu113 CPU
    
    Fusing layers... 
    YOLOv5s summary: 213 layers, 7225885 parameters, 0 gradients
    
    PyTorch: starting from models\yolov5s.pt with output shape (1, 25200, 85) (14.1 MB)
    
    ONNX: starting export with onnx 1.12.0...
    ONNX: export success  3.6s, saved as models\yolov5s.onnx (28.0 MB)
    
    Export complete (4.2s)
    Results saved to D:\05 - \01 - x3\BPUCodes\yolov5\yolov5-master\models
    Detect:          python detect.py --weights models\yolov5s.onnx
    Validate:        python val.py --weights models\yolov5s.onnx
    PyTorch Hub:     model = torch.hub.load('ultralytics/yolov5', 'custom', 'models\yolov5s.onnx')
    Visualize:       https://netron.app
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    二 ONNX模型转换

    模型转换要在docker中转换,怎么安装docker,怎么进入OE,怎么挂载硬盘,均在博客[BPU部署教程] 一文带你轻松走出模型部署新手村中详细介绍,任何细节不懂的,可以去这里面寻找。

    新建一个文件夹,我这里叫bpucodes,把前面转好的yolov5s.onnx放进这个文件夹里,百度云里也提供了相关的代码。

    docker中,进入bpucodes文件夹,开始我们的模型转换。

    2.1 模型检查

    模型检测的目的是检测有没有不支持的算子,输入指令hb_mapper checker --model-type onnx --march bernoulli2 --model yolov5s.onnx,开始检查模型,显示如下内容表示模型检查通过。(我们可以发现网络的后端部分存在一些层是运行在CPU上的,这会导致耗时多一些)
    在这里插入图片描述
    前面提到了,在pt转onnx时,要–opset 11,而我最开始转换的时候,是没有指定的,下面说一下我是怎么找到问题的(这部分为题外话,想继续下一步的话,直接跳2.2节)

    我最开始利用python .\export.py --weights .\models\yolov5s.pt --include onnx来转onnx,结果发现在BPU的OE工具链里,hb_mapper checker检查不通过,显示如下错误。
    在这里插入图片描述
    谷歌搜到了onnx支持的算子信息https://github.com/microsoft/onnxruntime/blob/main/docs/OperatorKernels.md,在我这个docker里面,onnx库的版本为1.7.0,发现MaxPool实际上是支持的啊,为啥不通过,结果我发现后面的数字8,11,12,才想起来这个是不是还要指定opset 呀。
    在这里插入图片描述
    然后,我在yolov5的export.py文件里,发现opset默认是12,难怪不通过,最后问了晟哥,得到支持的版本,最终在指令中增加--opset 11,来生成onnx,这样就通过了。
    在这里插入图片描述

    2.2 准备校准数据

    校准数据的代码参考[BPU部署教程] 一文带你轻松走出模型部署新手村中的yolov3的校准代码,整体没有太多改变,主要修改了如下两个地方(代码我放百度云了,跟前面的博客内容重复度太高)。

    # 修改输入图像大小为640x640
    img = imequalresize(img, (640, 640))
    # 指定输出的校准图像根目录
    dst_root = '/data/horizon_x3/codes/yolov5/bpucodes/calibration_data
    
    • 1
    • 2
    • 3
    • 4

    输入prepare_calibration_data.py可以得到校准数据
    在这里插入图片描述
    其实在上一节模型检查过程中,log信息已经输出网络输入的图像维度了,我也是直接用这个。
    在这里插入图片描述

    2.3 开始转换BPU模型

    转换模型需要yaml参数文件,具体含义参考yolov3的教程,这里我直接放上我的convert_yolov5s.yaml文件信息。

    model_parameters:
      onnx_model: 'yolov5s.onnx'
      output_model_file_prefix: 'yolov5s'
      march: 'bernoulli2'
    input_parameters:
      input_type_train: 'rgb'
      input_layout_train: 'NCHW'
      input_type_rt: 'nv12'
      norm_type: 'data_scale'
      scale_value: 0.003921568627451
      input_layout_rt: 'NHWC'
    calibration_parameters:
      cal_data_dir: './calibration_data'
      calibration_type: 'max'
      max_percentile: 0.9999
    compiler_parameters:
      compile_mode: 'latency'
      optimize_level: 'O3'
      debug: False
      core_num: 2  # x3p是双核BPU,所以指定为2可以速度更快
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    输入命令hb_mapper makertbin --config convert_yolov5s.yaml --model-type onnx开始转换我们的模型!校准过后会输出每一层的量化损失。
    转换成功后,得到model_output/yolov5s.bin,这个文件拿出来,拷贝到旭日X3派上使用,它也是我们上板运行所需要的模型文件。
    在这里插入图片描述

    三 上板运行

    有小伙伴说后处理部分耗时太高,所以我这里除了给出推理代码,还教各位如何利用cython封装c++来加速你的代码。

    3.1 文件准备

    下载百度云的文件,拷贝到旭日X3派开发板中,其中yolov5s.bin就是我们转换后的模型,coco_classes.names仅用在画框的时候,如果用自己的数据集的话,参考coco_classes.names创建个新的名字文件即可。

    输入sudo apt-get install libopencv-dev安装opencv库,之后进入代码根目录,输入python3 setup.py build_ext --inplace,编译后处理代码,得到lib/pyyolotools.cpython-38-aarch64-linux-gnu.so文件。

    3.2 运行推理代码

    模型推理的代码如下所示,其中yolotools.pypostprocess_yolov5为C++实现的后处理功能,推理代码在我这里保存为inference_model_bpu.py

    import numpy as np
    import cv2
    import os
    from hobot_dnn import pyeasy_dnn as dnn
    from bputools.format_convert import imequalresize, bgr2nv12_opencv
    # lib.pyyolotools为封装的库
    import lib.pyyolotools as yolotools
    
    def get_hw(pro):
        if pro.layout == "NCHW":
            return pro.shape[2], pro.shape[3]
        else:
            return pro.shape[1], pro.shape[2]
    
    def format_yolov5(frame):
        row, col, _ = frame.shape
        _max = max(col, row)
        result = np.zeros((_max, _max, 3), np.uint8)
        result[0:row, 0:col] = frame
        return result
    
    # img_path 图像完整路径
    img_path = '20220904134315.jpg'
    # model_path 量化模型完整路径
    model_path = 'yolov5s.bin'
    # 类别名文件
    classes_name_path = 'coco_classes.names'
    # 设置参数
    thre_confidence = 0.4
    thre_score = 0.25
    thre_nms = 0.45
    # 框颜色设置
    colors = [(255, 255, 0), (0, 255, 0), (0, 255, 255), (255, 0, 0)]
    
    # 1. 加载模型,获取所需输出HW
    models = dnn.load(model_path)
    model_h, model_w = get_hw(models[0].inputs[0].properties)
    print(model_h, model_w)
    
    # 2 加载图像,根据前面模型,转换后的模型是以NV12作为输入的
    # 但在OE验证的时候,需要将图像再由NV12转为YUV444
    imgOri = cv2.imread(img_path)
    inputImage = format_yolov5(imgOri)
    img = imequalresize(inputImage, (model_w, model_h))
    nv12 = bgr2nv12_opencv(img)
    
    # 3 模型推理
    t1 = cv2.getTickCount()
    outputs = models[0].forward(nv12)
    t2 = cv2.getTickCount()
    outputs = outputs[0].buffer # 25200x85x1 
    print('time consumption {0} ms'.format((t2-t1)*1000/cv2.getTickFrequency()))
    
    # 4 后处理
    image_width, image_height, _ = inputImage.shape
    fx, fy = image_width / model_w, image_height / model_h
    t1 = cv2.getTickCount()
    class_ids, confidences, boxes = yolotools.pypostprocess_yolov5(outputs[0][:, :, 0], fx, fy, 
                                                                thre_confidence, thre_score, thre_nms)
    t2 = cv2.getTickCount()
    print('post consumption {0} ms'.format((t2-t1)*1000/cv2.getTickFrequency()))
    
    # 5 绘制检测框
    with open(classes_name_path, "r") as f:
        class_list = [cname.strip() for cname in f.readlines()]
    t1 = cv2.getTickCount()
    for (classid, confidence, box) in zip(class_ids, confidences, boxes):
        color = colors[int(classid) % len(colors)]
        cv2.rectangle(imgOri, box, color, 2)
        cv2.rectangle(imgOri, (box[0], box[1] - 20), (box[0] + box[2], box[1]), color, -1)
        cv2.putText(imgOri, class_list[classid], (box[0], box[1] - 10), cv2.FONT_HERSHEY_SIMPLEX, .5, (0,0,0))
    t2 = cv2.getTickCount()
    print('draw rect consumption {0} ms'.format((t2-t1)*1000/cv2.getTickFrequency()))
    
    cv2.imwrite('res.png', imgOri)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75

    输入sudo python3 inference_model_bpu.py,推理结果保存为res.png,相关结果如下所示,可以看出,后处理部分耗时为7ms,C++和Python混编有效提升了代码的运行速度。
    在这里插入图片描述

    3.3 利用Cython封装后处理代码

    这部分介绍了利用Cython封装C++的一些过程,后续各位有什么其他功能要补充的,按照这个流程处理即可。

    3.3.1 写后处理的C++代码

    首先,我们创建个头文件yolotools.h,用来记录函数声明,方便其他代码调用。因为Cython调用时,调用C++的一些类并不方便,所以写成C语言接口更方便调用。

    后处理的函数名为postprocess_yolov5,下面我对这个函数的输入参数进行说明:

    • float *_data, int datanum:输入模型推理数据,BPU推理完一张640x640的图像后,会输出一个25200x85x1的矩阵,我们去掉1这一项,得到一个25200x85的矩阵,以它的数据起点指针作为输入,datanum为25200*85。
    • int rows, int classnum:rows表示25200,而classnum表示数据集中的类别总数,我用的这个模型是以COCO为例的,所以一共80类。值得注意的,上述的85=classnum+1(得分)+4(矩形4个参数)
    • float x_factor, float y_factor:图像的缩放因子
    • float thre_cof, float thre_score, float thre_nms:后处理参数
    • int *_detected_num, signed int **_classids, float **_confidences, signed int **_boxes:后处理输出,_detected_num表示预测的矩形框个数,其余的分别为对应得数据,并返回对应的数据指针。
    #ifndef YOLOV5_TOOLS_H
    #define YOLOV5_TOOLS_H
    
    void postprocess_yolov5(float *_data, int datanum, 
                            int rows, int classnum,
                            float x_factor, float y_factor, 
                            float thre_cof, float thre_score, float thre_nms,
                            int *_detected_num, signed int **_classids, float **_confidences, signed int **_boxes);
    // 释放由postprocess_yolov5动态分配的内存                        
    void free_postprocess_yolov5(signed int **_classids, float **_confidences, signed int **_boxes);
    #endif
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    创建yolov5postprocess.cpp来对后处理函数进行实现

    #include 
    #include 
    #include 
    #include 
    #include "yolotools.h"
    
    // 这部分的代码参考了https://github.com/ultralytics/yolov5/issues/239
    void postprocess_yolov5(float *_data, int datanum, 
                            int rows, int classnum,
                            float x_factor, float y_factor, 
                            float thre_cof, float thre_score, float thre_nms,
                            int *_detected_num, signed int **_classids, float **_confidences, signed int **_boxes)
    {
      float *data = (float*)_data;
      int dimensions = classnum + 5;
      CV_Assert(datanum == rows * dimensions);
      
      std::vector<cv::Rect> boxes;
      std::vector<int> class_ids;
      std::vector<float> confidences;
    	
      // 这里是利用多线程,加速获得目标框,实际上,这部分的多线程,仅加速2-3ms。
      // 可以利用cv::setNumThreads(1);指定线程数
      std::mutex mtx;
      cv::parallel_for_(cv::Range(0, rows), [&](const cv::Range& range)
      {
        for (int r = range.start; r < range.end; r++) //
        {
    	  float *usage_data = data + r * (classnum + 5);
          float confidence = usage_data[4];
          if (confidence >= thre_cof)
          {
            float *classes_scores  = usage_data + 5;
            cv::Mat scores(1, classnum, CV_32FC1, classes_scores);
            cv::Point class_id;
            double max_class_score;
            cv::minMaxLoc(scores, 0, &max_class_score, 0, &class_id);
            if (max_class_score > thre_score)
            {
              float x = usage_data[0], y = usage_data[1], w = usage_data[2], h = usage_data[3];
              int left = int((x - 0.5 * w) * x_factor);
              int top = int((y - 0.5 * h) * y_factor);
              int width = int(w * x_factor);
              int height = int(h * y_factor);
    
              mtx.lock();
              confidences.push_back(confidence);
              class_ids.push_back(class_id.x);
              boxes.push_back(cv::Rect(left, top, width, height));
              mtx.unlock();
            }
          }
        }
      });
      
      std::vector<int> nms_result;
      cv::dnn::NMSBoxes(boxes, confidences, thre_score, thre_nms, nms_result);
    
      *_detected_num = nms_result.size();
      *_classids = new signed int[*_detected_num];
      *_confidences = new float[*_detected_num];
      *_boxes = new signed int[*_detected_num * 4];
      for(int i = 0; i < *_detected_num; i++)
      {
        int idx = nms_result[i];
        (*_classids)[i] = class_ids[idx];
        (*_confidences)[i] = confidences[idx];
        (*_boxes)[i * 4] = boxes[idx].x;
        (*_boxes)[i * 4 + 1] = boxes[idx].y;
        (*_boxes)[i * 4 + 2] = boxes[idx].width;
        (*_boxes)[i * 4 + 3] = boxes[idx].height;
      }
    }
    
    void free_postprocess_yolov5(signed int **_classids, float **_confidences, signed int **_boxes)
    {
      if (*_classids)
      {
        delete[] *_classids;
        *_classids = nullptr;
      }
      if (*_confidences)
      {
        delete[] *_confidences;
        *_confidences = nullptr;
      }
      if (*_boxes)
      {
        delete[] *_boxes;
        *_boxes = nullptr;
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92

    3.3.2 写Cython所需的Pyx文件

    同级目录下创建pyyolotools.pyx,切记文件名不要跟某个CPP重复了,因为cython会将pyyolotools.pyx转为pyyolotools.cpp,如果有重复的话可能会导致文件被覆盖掉。

    import numpy as np
    cimport numpy as np
    from libc.string cimport memcpy
    
    # 函数声明
    cdef extern from "yolotools.h":
        void postprocess_yolov5(float*, int, 
                                int, int, 
                                float, float, 
                                float, float, float,
                                int*, signed int**, float**, signed int**)
        void free_postprocess_yolov5(signed int**, float**, signed int**)
    
    # 定义Python函数,主要补充的就是将python数据转换为C++指针,然后利用计算出的结果再转换回去
    def pypostprocess_yolov5(np.ndarray[np.float32_t, ndim=2] yolov5output, 
                            float fx, float fy,
                            float thre_cof, float thre_score, float thre_nms):
        cdef int rows = yolov5output.shape[0]
        cdef int dimensions = yolov5output.shape[1]
        
        cdef int classnum = dimensions - 5
        assert classnum > 5
    
        cdef int datanum = rows * dimensions
    
        cdef int detected_num = 0
        cdef signed int *pclassids
        cdef float *pconfidences
        cdef signed int *pboxes
        postprocess_yolov5(&yolov5output[0, 0], datanum,
                           rows, classnum, fx, fy, 
                           thre_cof, thre_score, thre_nms,
                           &detected_num, &pclassids, &pconfidences, &pboxes)
        if detected_num == 0:
            return None
        
        cdef np.ndarray[np.int32_t, ndim=1] classids = np.zeros((detected_num, ), dtype = np.int32)
        cdef np.ndarray[np.float32_t, ndim=1] confidence = np.zeros((detected_num, ), dtype = np.float32)
        cdef np.ndarray[np.int32_t, ndim=2] boxes = np.zeros((detected_num, 4), dtype = np.int32)
    
        memcpy(&classids[0], pclassids, sizeof(int) * detected_num)
        memcpy(&confidence[0], pconfidences, sizeof(float) * detected_num)
        memcpy(&boxes[0, 0], pboxes, sizeof(int) * detected_num * 4)
    
        free_postprocess_yolov5(&pclassids, &pconfidences, &pboxes);
        return (classids, confidence, boxes)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46

    3.3.3 写编译Pyx所需的python代码

    创建setup.py文件,将下面代码放进去,配置好opencv的头文件目录、库目录、以及所需的库文件。
    Extension中配置封装的函数所依赖的文件,然后在控制台输入python3 setup.py build_ext --inplace即可。

    from setuptools import setup
    from distutils.extension import Extension
    from Cython.Distutils import build_ext
    import numpy as np
    
    opencv_include = '/usr/include/opencv4/'
    opencv_lib_dirs = "/usr/lib/aarch64-linux-gnu/"
    opencv_libs = ['opencv_core', 'opencv_highgui', 'opencv_imgproc', 'opencv_imgcodecs', 'opencv_dnn']
    
    print('opencv_include: ', opencv_include)
    print('opencv_lib_dirs: ', opencv_lib_dirs)
    print('opencv_libs: ', opencv_libs)
    
    # python3 setup.py build_ext --inplace
    
    class custom_build_ext(build_ext):
        def build_extensions(self):
            build_ext.build_extensions(self)
    
    # Obtain the numpy include directory.  This logic works across numpy versions.
    try:
        numpy_include = np.get_include()
    except AttributeError:
        numpy_include = np.get_numpy_include()
    
    
    ext_modules = [
        Extension(
            "lib.pyyolotools",
            ["./yolov5postprocess.cpp",
             "./pyyolotools.pyx",],
            include_dirs = [numpy_include, opencv_include],
            language='c++',
            libraries=opencv_libs,
            library_dirs=[opencv_lib_dirs]
            ),
        ]
    
    setup(
        name='pyyolotools',
        ext_modules=ext_modules,
        cmdclass={'build_ext': custom_build_ext},
    )
    
    print('Build done')
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45

    四 总结

    上一个部署教程,主要是介绍如何利用Caffe来部署模型,这个部分,就是利用ONNX来部署。后续如果自己基于这个代码进行修改的话,也可以按照这个教程对自己的模型进行量化上板。

    此外,X3派作为个嵌入式板子,学会开发C++是非常重要的。但我个人认为所有的任务都是C++是不现实的,我们可以利用Python高效完成项目开发,对其中耗时较高的,封装C++即可。

    PS:各位千万不要被推理耗时吓到了,感觉耗时太高了,因为这个版本的模型转换时候,后面一堆层是跑在CPU上的,各位在设计自己的网络的时候也要注意下哈。

  • 相关阅读:
    2023了,学习深度学习框架哪个比较好?
    一个被华为/腾讯招聘所提到的PMP证书,在我国大体是个啥情况?
    mysql5.7免安装教程,配置my.ini详解,安装卸载mysql服务,开启远程登录,修改mysql密码
    HashMap底层详讲
    MyCat搭建读写分离
    uniapp/手机APP使用支付宝支付(服务端)
    基于PSO算法的功率角摆动曲线优化研究(Matlab代码实现)
    【大语言模型LLM】-基础语言模型和指令微调的语言模型
    并查集(畅通工程)
    R语言for循环绘图
  • 原文地址:https://blog.csdn.net/Zhaoxi_Li/article/details/126651890