yolov8seg模型转onnx转ncnn

文章讲述了如何将Yolov8模型从PyTorch转换为ONNX格式,然后进一步转化为NCNN以供使用。在转换过程中,遇到了ONNX不支持的chunk操作问题,通过修改block.py和head.py中的forward函数解决了此问题。转换后的NCNN模型在测试时表现出色,尽管精度可能略有下降。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

yolov8是yolo的最新版本,可做图像分类,目标检测,实例分割,姿态估计。
主页地址

这里测试一个分割模型。
模型如下
请添加图片描述
选yolov8n-seg模型,转成onnx,再转ncnn测试。
yolov8s-seg的ncnn版可以直接用这个

如果用python版的onnx,可以直接用notebook转,然后下载。
python版onnx代码参考

但是用notebook版本的onnx转ncnn之后,测试图片时会报错。
可以看一下转出来的yolov8n-seg.param,里面有很多的MemoryData,不够clean.

请添加图片描述

所以需要修改一些地方。(参考资料

查了一些资料都说是要修改modules.py.
请添加图片描述
然而本地并没有modules.py,而是找到了modules文件夹
在这里插入图片描述
那么只需要在这些文件里面找到3个class并做相同的修改就可以了。
于是需要修改block.pyhead.py两个文件。

要修改3个forward函数。

block.py修改:

class C2f(nn.Module):
   ...
    def forward(self, x):
        """Forward pass through C2f layer."""
        #y = list(self.cv1(x).chunk(2, 1))
        #y.extend(m(y[-1]) for m in self.m)
        #return self.cv2(torch.cat(y, 1))
        x = self.cv1(x)
        x = [x, x[:, self.c:, ...]]      #onnx不支持chunk操作?
        x.extend(m(x[-1]) for m in self.m)
        x.pop(1)
        return self.cv2(torch.cat(x, 1))

head.py中修改了Detect和Segment class.

class Detect(nn.Module):
    ...
    def forward(self, x):
        """Concatenates and returns predicted bounding boxes and class probabilities."""
        shape = x[0].shape  # BCHW
        for i in range(self.nl):
            x[i] = torch.cat((self.cv2[i](x[i]), self.cv3[i](x[i])), 1)
        if self.training:
            return x
        elif self.dynamic or self.shape != shape:
            self.anchors, self.strides = (x.transpose(0, 1) for x in make_anchors(x, self.stride, 0.5))
            self.shape = shape

        #x_cat = torch.cat([xi.view(shape[0], self.no, -1) for xi in x], 2)
        #if self.export and self.format in ('saved_model', 'pb', 'tflite', 'edgetpu', 'tfjs'):  # avoid TF FlexSplitV ops
        #    box = x_cat[:, :self.reg_max * 4]
        #    cls = x_cat[:, self.reg_max * 4:]
        #else:
        #    box, cls = x_cat.split((self.reg_max * 4, self.nc), 1)
        #dbox = dist2bbox(self.dfl(box), self.anchors.unsqueeze(0), xywh=True, dim=1) * self.strides
        #y = torch.cat((dbox, cls.sigmoid()), 1)
        #return y if self.export else (y, x)
        pred = torch.cat([xi.view(shape[0], self.no, -1) for xi in x], 2)  #精简为这2句
        return pred

class Segment(Detect):
    ...
    def forward(self, x):
        """Return model outputs and mask coefficients if training, otherwise return outputs and mask coefficients."""
        p = self.proto(x[0])  # mask protos
        bs = p.shape[0]  # batch size

        mc = torch.cat([self.cv4[i](x[i]).view(bs, self.nm, -1) for i in range(self.nl)], 2)  # mask coefficients
        x = self.detect(self, x)
        if self.training:
            return x, mc, p
        #return (torch.cat([x, mc], 1), p) if self.export else (torch.cat([x[0], mc], 1), (x[1], mc, p))
       return (torch.cat([x, mc], 1).permute(0, 2, 1), p.view(bs, self.nm, -1)) if self.export else (
            torch.cat([x[0], mc], 1), (x[1], mc, p))

改好了之后用如下代码export onnx文件, onnx文件会导出到和pt文件同一文件夹。

from ultralytics import YOLO

# load model
model = YOLO("pt路径/yolov8n-seg.pt")

# Export model
success = model.export(format="onnx", opset=12, simplify=True)

转好onnx之后,再把onnx转成ncnn,用onnx2ncnn命令(在build ncnn时会有这个命令)
onnx2ncnn一般在ncnn/build/tools/onnx文件夹下。

$ ./onnx2ncnn onnx路径/yolov8n-seg.onnx 想保存的路径/yolov8n-seg.param 想保存的路径/yolov8n-seg.bin

对比一下修改前和修改后导出的参数yolov8n-seg.param。
可以看到clean了很多,修改前有MemoryData, 而修改之后没有了。
请添加图片描述

下面来测试一下ncnn模型,
可以用这个代码
需要修改几个地方:

    ncnn::Mat out;
    //ex.extract("output", out);
    ex.extract("output0", out);

    ncnn::Mat mask_proto;
    //ex.extract("seg", mask_proto);
    ex.extract("output1", mask_proto);

再改load_paramload_model的路径。

CMakeList.txt

cmake_minimum_required(VERSION 3.23)
project(seg_detect_ncnn)

set(CMAKE_CXX_STANDARD 14)

string(APPEND CMAKE_CXX_FLAGS " -Wall -Wextra")
string(APPEND CMAKE_C_FLAGS " -Wall -Wextra")

# 调用opencv
find_package(OpenCV REQUIRED)
# 调用openmp
FIND_PACKAGE( OpenMP REQUIRED)
if(OPENMP_FOUND)
    message("OPENMP FOUND")
    set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${OpenMP_C_FLAGS}")
    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${OpenMP_CXX_FLAGS}")
    set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${OpenMP_EXE_LINKER_FLAGS}")
endif()

# 包含ncnn的头文件
include_directories(ncnn/build/install/include/ncnn)
# 包含ncnn的链接文件
link_directories(ncnn/build/install/lib)
# 生成可执行文件
add_executable(yolo_seg_ncnn yolo_seg.cpp)
# 链接ncnn静态链接库
target_link_libraries(yolo_seg_ncnn
        ncnn
        ${OpenCV_LIBS}
        ${ONNXRUNTIME_LIB}
        ncnn/build/install/lib/libncnn.a)

然后就可以用啦(图片来自COCO数据集)

请添加图片描述

下面是直接用yolov8n-seg.pt预测出的结果,
可以看到修改了之后效果是差了一点的(其中一个dog的框没预测出来)。

请添加图片描述

### 部署YOLOv8Seg模型NCNN的指南 将YOLOv8Seg模型部署至NCNN涉及多个步骤,包括模型转换、优化以及适配目标硬件环境。以下是关于此过程的关键点: #### 1. 准备YOLOv8Seg模型 在开始之前,需确保已训练好YOLOv8Seg模型并保存为PyTorch权重文件(通常是`.pt`格式)。这是后续转换的基础[^3]。 #### 2. 转换模型ONNX格式 NCNN支持多种输入格式,其中ONNX是最常用的中间表示形式之一。可以使用以下Python脚本将YOLOv8Seg模型导出为ONNX格式: ```python from ultralytics import YOLO model = YOLO('path_to_your_model.pt') # 加载YOLOv8Seg模型 model.export(format='onnx', simplify=True) # 导出为简化后的ONNX模型 ``` 上述代码会生成一个名为`model.onnx`的文件,该文件将是下一步的重要资源[^1]。 #### 3. 将ONNX模型转换NCNN参数文件 利用NCNN工具链中的`ncnn-tools`,可进一步将ONNX模型转换NCNN所需的两个主要文件——`.param`和`.bin`。具体命令如下: ```bash ./onnx2ncnn model.onnx model.param model.bin ``` 这一步骤完成后,即可获得适用于NCNN框架的目标模型文件[^4]。 #### 4. 实现推理逻辑 完成模型转换后,在实际应用中需要编写C++代码来加载这些文件并执行预测操作。下面是一个简单的示例程序片段: ```cpp #include "net.h" using namespace ncnn; int main() { Net yolov8_seg; yolov8_seg.load_param("model.param"); yolov8_seg.load_model("model.bin"); Mat img = imread("test.jpg"); // 替换为目标图像路径 // 添加预处理与推理部分... } ``` 注意:由于YOLOv8Seg属于分割网络,因此可能还需要额外实现掩码解码等功能以满足特定需求[^5]。 ---
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

蓝羽飞鸟

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值