YOLOv6 в NCNN
В этом руководстве объясняется, как преобразовать модель YOLOv6 в формат NCNN, а также некоторые распространённые проблемы, которые могут возникнуть в процессе преобразования. Это руководство охватывает упаковку и отладку на платформе lite.ai.toolkit в среде macOS.
0. Подготовка среды
На данный момент есть два пути для преобразования модели в формат NCNN: первый путь — из PyTorch в ONNX в NCNN, второй путь — из PyTorch в TorchScript в ONNX в NCNN.
1. Подготовьте что-нибудь ещё
Подготовьте оригинальный файл .pt в каталоге ./path/to/yolov6.
(Путь 2) Подготовьте файл export_pt.py в каталоге ./path/to/yolov6/deploy. И вы должны изменить код согласно следующему руководству.
2. Преобразование
2.1 Путь ONNX->NCNN
python deploy/ONNX/export_onnx.py --weights ./path/to/yolov6s.pt --device 0 --simplify --batch [1 или 32]
./onnx2ncnn ./path/to/yolov6s.onnx ./path/to/save/yolov6s.param /path/to/save/yolov6s.bin
2.2 Путь PNNX->NCNN
#!/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")
python ./path/to/yolov6/deploy/export_pt.py --weights ./path/to/yolov6s.pt
Приведённый выше код выдаёт ошибку о том, что он не может вывести список. Чтобы исправить это, измените функцию forward модели в файле yolov6/models/yolo.py так, чтобы она возвращала x только если export_mode равен True, в противном случае возвращала список [x, featmaps].
./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 )