1 В избранное 0 Ответвления 0

OSCHINA-MIRROR/monkeycc-YOLOv6

Присоединиться к Gitlife
Откройте для себя и примите участие в публичных проектах с открытым исходным кодом с участием более 10 миллионов разработчиков. Приватные репозитории также полностью бесплатны :)
Присоединиться бесплатно
Клонировать/Скачать
Test_NCNN_speed.md 17 КБ
Копировать Редактировать Web IDE Исходные данные Просмотреть построчно История
gitlife-traslator Отправлено 30.11.2024 06:51 51eee55

YOLOv6 в NCNN

В этом руководстве объясняется, как преобразовать модель YOLOv6 в формат NCNN, а также некоторые распространённые проблемы, которые могут возникнуть в процессе преобразования. Это руководство охватывает упаковку и отладку на платформе lite.ai.toolkit в среде macOS.

0. Подготовка среды

На данный момент есть два пути для преобразования модели в формат NCNN: первый путь — из PyTorch в ONNX в NCNN, второй путь — из PyTorch в TorchScript в ONNX в NCNN.

  • Первый путь: сборка NCNN.
  • Второй путь: сборка NCNN и PNNX. Если вы не хотите собирать PNNX, возможно, попробуйте: релизы PNNX (https://github.com/pnnx/pnnx/releases).

1. Подготовьте что-нибудь ещё

  • Подготовьте оригинальный файл .pt в каталоге ./path/to/yolov6.

  • (Путь 2) Подготовьте файл export_pt.py в каталоге ./path/to/yolov6/deploy. И вы должны изменить код согласно следующему руководству.

2. Преобразование

2.1 Путь ONNX->NCNN

  • Экспортируйте модель ONNX следующей командой:
python deploy/ONNX/export_onnx.py --weights ./path/to/yolov6s.pt --device 0 --simplify --batch [1 или 32]
  • Используйте инструмент onnx2ncnn для преобразования модели ONNX в формат NCNN:
./onnx2ncnn ./path/to/yolov6s.onnx ./path/to/save/yolov6s.param /path/to/save/yolov6s.bin

2.2 Путь PNNX->NCNN

  • Измените файл export_pt.py следующим образом:
Показать/скрыть export.py
#!/usr/bin/env python3
# -*- coding:utf-8 -*-
import argparse
import sys
import os
import torch
import torch.nn as nn

ROOT = os.getcwd()
if str(ROOT) not in sys.path:
    sys.path.append(str(ROOT))

from yolov6.models.yolo import *
from yolov6.models.effidehead import Detect
from yolov6.layers.common import *
from yolov6.utils.events import LOGGER
from yolov6.utils.checkpoint import load_checkpoint

if __name__ == '__main__':
    parser = argparse.ArgumentParser()
    parser.add_argument('--weights', type=str, default='./yolov6s.pt', help='weights path')
    parser.add_argument('--half', action='store_true', help='FP16 half-precision export')
    parser.add_argument('--device', default='cpu', help='cuda device, i.e. 0 or 0,1,2,3 or cpu')
    parser.add_argument('--inplace', action='store_true', help='set Detect() inplace=True')
    args = parser.parse_args()
    print(args)

    cuda = args.device != 'cpu' and torch.cuda.is_available()
    device = torch.device(f'cuda:{args.device}' if cuda else 'cpu')
    assert not (device.type == 'cpu' and args.half), '--half only compatible with GPU export, i.e. use --device 0'
    model = load_checkpoint(args.weights, map_location=device, inplace=True, fuse=True)  # load FP32 model
    for layer in model.modules():
        if isinstance(layer, RepVGGBlock):
            layer.switch_to_deploy()

    if args.half:
        model = model.half()
    model.eval()
    for k, m in model.named_modules():
        if isinstance(m, Conv):
            if isinstance(m.act, nn.SiLU):
                m.act = SiLU()
        elif isinstance(m, Detect):
            m.inplace = args.inplace

    x = torch.rand(1, 3, 512, 512)
    mod = torch.jit.trace(model, x)
    mod.save("your_filename.pt")

  • Затем запустите export_pt.py в оболочке:
python ./path/to/yolov6/deploy/export_pt.py --weights ./path/to/yolov6s.pt

Приведённый выше код выдаёт ошибку о том, что он не может вывести список. Чтобы исправить это, измените функцию forward модели в файле yolov6/models/yolo.py так, чтобы она возвращала x только если export_mode равен True, в противном случае возвращала список [x, featmaps].

  • Скопируйте сгенерированный новый файл .pt в каталог, где находится скрипт pnnx, и затем выполните следующую команду:
./path/to/pnnx ./path/to/generate.pt inputshape=[1,3,640,640] #windows
./path/to/pnnx

* *Примечание:* В ответе сохранены оригинальное форматирование текста и спецсимволы. **Бинарная операция** sub_0: 2 1 169 167 171 0=1.

**Разделение:** splitncnn_18: 1 2 171 172 173.

**Бинарная операция**: add_1: 2 1 170 168 174 0=0.

**Разделение**: splitncnn_19: 1 2 174 175 176.

**Бинарная операция:** add_2: 2 1 172 175 177 0=0.

**Бинарная операция:** div_3: 1 1 177 178 0=3 1=1 2=2.000000e+00.

**Бинарная операция:** sub_4: 2 1 176 173 179 0=1.

**Конкатенация:** cat_10: 2 1 178 179 180 0=1.

**Данные памяти:** pnnx_fold_stride_tensor.1: 0 1 181 0=1 1=5440.

**Бинарная операция:** mul_5: 2 1 180 181 182 0=2.

**Данные памяти**: pnnx_fold_925: 0 1 183 0=1 1=5440.

**Перестановка**: permute_193: 1 1 156 184 0=1.

**Конкатенация**: cat_11: 3 1 180 183 184 out0 0=1. #origin: Конкатенация: cat_11: 3 1 182 183 184 out0 0=1.

Эта модификация означает, что некоторые операции в заголовке должны быть добавлены к постобработке используемой структуры. Далее мы будем использовать lite.ai.toolkit в качестве примера для объяснения.

* Модифицируйте файл ./path/to/lite.ai.Toolkit/examples/lite/cv/test_lite_yolov6.cpp следующим образом:

    #include "lite/lite.h"

    static void test_onnxruntime(std::string onnx)//сохраняем onnx для сравнения, если нужно изменить onnx, соответствующий файл заголовка и код
    {
    #ifdef ENABLE_ONNXRUNTIME
    std::string onnx_path = "../../../hub/onnx/cv/" + onnx;
    std::string test_img_path = "../../../examples/lite/resources/test_lite_yolov5_2.jpg";//переключаемся на путь тестового изображения
    std::string save_img_path = "../../../logs/test_oxr_yolov6_1.jpg";

    // 2. Тестирование конкретного движка ONNXRuntime
    lite::onnxruntime::cv::detection::YOLOv6 *yolov6 =
        new lite::onnxruntime::cv::detection::YOLOv6(onnx_path);

    std::vector<lite::types::Boxf> detected_boxes;
    cv::Mat img_bgr = cv::imread(test_img_path);
    yolov6->detect(img_bgr, detected_boxes, 0.5);

    lite::utils::draw_boxes_inplace(img_bgr, detected_boxes);

    cv::imwrite(save_img_path, img_bgr);

    std::cout << "ONNXRuntime Version Detected Boxes Num: " << detected_boxes.size() << std::endl;

    delete yolov6;
    #endif
    }

    static void test_ncnn(std::string ncnn_param, std::string ncnn_bin)
    {
    #ifdef ENABLE_NCNN
    std::string param_path = "../../../hub/ncnn/cv/" + ncnn_param;
    std::string bin_path = "../../../hub/ncnn/cv/" + ncnn_bin;
    std::string test_img_path = "../../../examples/lite/resources/test_lite_yolov5_2.jpg"; //переключаемся на путь тестового изображения
    std::string save_img_path = "../../../logs/test_ncnn_yolov6_2.jpg";

    // 4. Тестирование конкретного движка NCNN
    lite::ncnn::cv::detection::YOLOv6 *yolov6 =
        new lite::ncnn::cv::detection::YOLOv6(param_path, bin_path);

    std::vector<lite::types::Boxf> detected_boxes;
    cv::Mat img_bgr = cv::imread(test_img_path);
    yolov6->detect(img_bgr, detected_boxes);

    lite::utils::draw_boxes_inplace(img_bgr, detected_boxes);

    cv::imwrite(save_img_path, img_bgr);

    std::cout << "NCNN Version Detected Boxes Num: " << detected_boxes.size() << std::endl;

    delete yolov6;
    #endif
    }

    static void test_lite(std::string onnx, std::string ncnn_param, std::string ncnn_bin)
    {
    test_onnxruntime(onnx);
    test_ncnn(ncnn_param, ncnn_bin);
    }

    int main(__unused int argc, __unused char *argv[])
    {
    std::string onnx = argv[1];
    std::string ncnn_param = argv[2];
    std::string ncnn_bin = argv[3];
    test_lite(onnx, ncnn_param, ncnn_bin);
    return 0;
    }

* Измените файл ./path/to/lite.ai.Toolkit/lite/ncnn/cv/ncnn_yolov6.h, строки 28-29, на разрешение ввода модели ncnn. **lite/ncnn/core/ncnn_core.h**

namespace ncnncv {
class LITE_EXPORTS NCNNYOLOv6 {
private:
    ncnn::Net *net = nullptr;
    const char *log_id = nullptr;
    const char *param_path = nullptr;
    const char *bin_path = nullptr;
    std::vector<const char*> input_names;
    std::vector<const char*> output_names;
    std::vector<int> input_indexes;
    std::vector<int> output_indexes;

public:
    explicit NCNNYOLOv6(const std::string &_param_path,
                       const std::string &_bin_path,
                       unsigned int _num_threads = 1,
                       int _input_height = 512,
                       int _input_width = 512); //
    ~NCNNYOLOv6();

private:
    // nested classes
    typedef struct GridAndStride {
        int grid0;
        int grid1;
        int stride;
    } YOLOv6Anchor;

    typedef struct {
        float r;
        int dw;
        int dh;
        int new_unpad_w;
        int new_unpad_h;
        bool flag;
    } YOLOv6ScaleParams;

private:
    const unsigned int num_threads; // initialize at runtime.
    const int input_height; // 640/320
    const int input_width; // 640/320

    const char* class_names[80] = {
        "person", "bicycle", "car", "motorcycle", "airplane", "bus", "train", "truck", "boat", "traffic light",
        "fire hydrant", "stop sign", "parking meter", "bench", "bird", "cat", "dog", "horse", "sheep", "cow",
        "elephant", "bear", "zebra", "giraffe", "backpack", "umbrella", "handbag", "tie", "suitcase", "frisbee",
        "skis", "snowboard", "sports ball", "kite", "baseball bat", "baseball glove", "skateboard", "surfboard",
        "tennis racket", "bottle", "wine glass", "cup", "fork", "knife", "spoon", "bowl", "banana", "apple",
        "sandwich", "orange", "broccoli", "carrot", "hot dog", "pizza", "donut", "cake", "chair", "couch",
        "potted plant", "bed", "dining table", "toilet", "tv", "laptop", "mouse", "remote", "keyboard",
        "cell phone", "microwave", "oven", "toaster", "sink", "refrigerator", "book", "clock", "vase",
        "scissors", "teddy bear", "hair drier", "toothbrush"
    };
    enum NMS {
        HARD = 0, BLEND = 1, OFFSET = 2
    };
    const float mean_vals[3] = {0.f, 0.f, 0.f}; // RGB
    const float norm_vals[3] = {1.0 / 255.f, 1.0 / 255.f, 1.0 / 255.f};
    static constexpr const unsigned int max_nms = 30000;

protected:
    NCNNYOLOv6(const NCNNYOLOv6&) = delete; //
    NCNNYOLOv6(NCNNYOLOv6&&) = delete; //
    NCNNYOLOv6 &operator=(const NCNNYOLOv6&) = delete; //
    NCNNYOLOv6 &operator=(NCNNYOLOv6 &&) = delete; //

private:
    void print_debug_string();

    void transform(const cv::Mat &mat_rs, ncnn::Mat &in);

    void resize_unscale(const cv::Mat &mat,
                        cv::Mat &mat_rs,
                        int target_height,
                        int target_width,
                        YOLOv6ScaleParams &scale_params);

    void generate_anchors(const int target_height,
                            const int target_width,
                            std::vector<int>& strides,
                            std::vector<YOLOv6Anchor>& anchors);

    void generate_bboxes(const YOLOv6ScaleParams& scale_params,
                            std::vector<types::Boxf>& bbox_collection,
                            ncnn::Extractor& extractor,
                            float score_threshold, int img_height,
                            int img_width); // rescale & exclude

    void nms(std::vector<types::Boxf> &input, std::vector<types::Boxf> &output,
            float iou_threshold, unsigned int topk, unsigned int nms_type);

public:
    void detect(const cv::Mat& mat, std::vector<types::Boxf>& detected_boxes,
                float score_threshold = 0.5f, float iou_threshold = 0.45f,
                unsigned int
    );
}
} **Текст написан на языке C++.**

**topk = 100, unsigned int nms_type = NMS::OFFSET);**

    };
    }

    #endif //LITE_AI_TOOLKIT_NCNN_CV_NCNN_YOLOV6_H
</details>
<br>
* Modify ./path/to/lite.ai.Toolkit/lite/ncnn/cv/ncnn_yolov6.cpp

<details>
    <summary>Show/Hide ncnn_yolov6.cpp</summary>

    //
    // Created by DefTruth on 2022/6/25.
    //

    #include "ncnn_yolov6.h"
    #include "lite/utils.h"

    using ncnncv::NCNNYOLOv6;


    NCNNYOLOv6::NCNNYOLOv6(const std::string &_param_path,
                        const std::string &_bin_path,
                        unsigned int _num_threads,
                            int _input_height,
                            int _input_width) :
        log_id(_param_path.data()), param_path(_param_path.data()),
        bin_path(_bin_path.data()), num_threads(_num_threads),
        input_height(_input_height), input_width(_input_width)
    {
    net = new ncnn::Net();
    // init net, change this setting for better performance.
    net->opt.use_fp16_arithmetic = false;
    net->opt.use_vulkan_compute = false; // default
    // setup Focus in yolov5
    // net->register_custom_layer("YoloV5Focus", YoloV5Focus_layer_creator);
    net->load_param(param_path);
    net->load_model(bin_path);
    #ifdef LITENCNN_DEBUG
    this->print_debug_string();
    #endif
    }

    NCNNYOLOv6::~NCNNYOLOv6()
    {
    if (net) delete net;
    net = nullptr;
    }

    void NCNNYOLOv6::transform(const cv::Mat &mat_rs, ncnn::Mat &in)
    {
    // BGR NHWC -> RGB NCHW
    in = ncnn::Mat::from_pixels(mat_rs.data, ncnn::Mat::PIXEL_BGR2RGB, input_width, input_height);
    in.substract_mean_normalize(mean_vals, norm_vals);
    }

    // letterbox
    void NCNNYOLOv6::resize_unscale(const cv::Mat &mat, cv::Mat &mat_rs,
                                    int target_height, int target_width,
                                    YOLOv6ScaleParams &scale_params)
    {
    if (mat.empty()) return;
    int img_height = static_cast<int>(mat.rows);
    int img_width = static_cast<int>(mat.cols);

    mat_rs = cv::Mat(target_height, target_width, CV_8UC3,
                    cv::Scalar(114, 114, 114));
    // scale ratio (new / old) new_shape(h,w)
    float w_r = (float) target_width / (float) img_width;
    float h_r = (float) target_height / (float) img_height;
    float r = std::min(w_r, h_r);
    // compute padding
    int new_unpad_w = static_cast<int>((float) img_width * r); // floor
    int new_unpad_h = static_cast<int>((float) img_height * r); // floor
    int pad_w = target_width - new_unpad_w; // >=0
    int pad_h = target_height - new_unpad_h; // >=0

    int dw = pad_w / 2;
    int dh = pad_h / 2;

    // resize with unscaling
    cv::Mat new_unpad_mat;
    // cv::Mat new_unpad_mat = mat.clone(); // may not need clone.
    cv::resize(mat, new_unpad_mat, cv::Size(new_unpad_w, new_unpad_h));
    new_unpad_mat.copyTo(mat_rs(cv::Rect(dw, dh, new_unpad_w, new_unpad_h)));

    // record scale params.
    scale_params.r = r;
    scale_params.dw = dw;
    scale_params.dh = dh;
    scale_params.new_unpad_w = new_unpad_w;
    scale_params.new_unpad_h = new_unpad_h;
    scale_params.flag = true;
    }

    void NCNNYOLOv6::detect(const cv::Mat &mat, std::vector<types::Boxf> &detected_boxes,
                            float score_threshold, float iou_threshold,
                            unsigned int topk, unsigned int nms_type)
    {
    if (mat.empty()) return;
    int img_height = static_cast<int>(mat.rows);
    int img_width = static_cast<int>(mat.cols);
    // resize & unscale
    cv::Mat mat_rs;
    YOLOv6ScaleParams scale_params;
    this->resize_unscale(mat, mat_rs, input_height, input_width, scale_params);

    // 1. make input tensor
    ncnn::Mat input;
    this->transform(mat_rs, input);
    // 2. inference & extract
    auto extractor = net->create_extractor();
    extractor.set_light_mode(false);  // default
    extractor.set_num_threads(num_threads);
    extractor.input("in0", input);
    // 3.rescale & exclude.
    std::vector<types::Boxf> bbox_collection;
    this->generate_bboxes(scale_params, bbox_collection, extractor,

В приведённом тексте описывается класс NCNNYOLOv6 на языке C++, который используется для обнаружения объектов на изображениях с помощью модели YOLOv6. В тексте также описываются методы класса, такие как transform, resize_unscale и detect.

Опубликовать ( 0 )

Вы можете оставить комментарий после Вход в систему

1
https://api.gitlife.ru/oschina-mirror/monkeycc-YOLOv6.git
git@api.gitlife.ru:oschina-mirror/monkeycc-YOLOv6.git
oschina-mirror
monkeycc-YOLOv6
monkeycc-YOLOv6
main