【YOLOv8seg部署至RK3588】模型训练→转换rknn→部署全流程

需求梳理

近期做了一个Mobilenetv2图像分类的开发板部署项目,但考虑到图像分类不足以满足实际项目需求,因此需要实例分割进行部署,而之前使用YOLOv8的目标检测完成了部署,所以一并选择YOLOv8的seg。
另外,为什么图像分类没有采用YOLOv8呢?因为瑞芯微官方仓库中关于图像分类项目只有Mobilenetv2和ResNet50v2的demo,当然,图像分类的后处理比较简单,如果有需要,想统一用一套的YOLOv8目标检测&图像分类&实例分割,可以自己简单写一个后处理流程即可。

一、模型训练

YOLOv8n-seg训练可以直接在github下载官方的预训练权重,也可以直接用自定义或官方的yaml文件训练。
在此,选择yaml文件训练,同时使用python脚本进行训练命令设置,脚本内容如下:

在这里插入图片描述
这里需要注意一下,因为后续要把训练得到的PT模型,转成ONNX再转成RKNN模型,而转ONNX的过程中,需要在瑞芯微在Github上官方的ultralytics_yolov8下进行,所以我们在训练PT模型时的ultralytics版本需要和后续转换的ultralytics_yolov8的ultralytics版本一致。
ultralytics_yolov8的ultralytics版本如下:

在这里插入图片描述
是8.0.151版本的,所以我们训练用的ultralytics版本也要用8.0.151版本,如下所示:

在这里插入图片描述
但这里说一下,训练所用的ultralytics版本不是一定要8.0.151,只要不是较新的版本就行。
如果训练时用的是最新的ultralytics版本,比如我之前用的8.2.82版本:

在这里插入图片描述
在8.2.82训练后再去ultralytics_yolov8下转换时,就会报错如下:

在这里插入图片描述
所以一定要注意!这里附上ultralytics8.0.151的Github链接

另外,这里记录一个小问题,之前在目标检测项目中,300epoch足以完成模型拟合,但是在实例分割的训练中,是很不够的,需要更多轮数,建议1000epoch,博主这里是到了900+epoch才完成拟合:

在这里插入图片描述
当然也可以把50次epoch训练无提升终止训练关闭,epoch次数多多益善,我这里直接拉到10000次,最终在992次提前终止,最好结果为942th epoch。

二、模型转换

1. PT转ONNX

模型转换不要在自己的ultralytics官方的文件夹下,那样转出的onnx模型只有一个输出,无法再转出符合要求的RKNN模型。因此,在训练得到PT模型后,要下载瑞芯微的ultralytics_yolov8,并在其中进行ONNX转换。
这里说一下为什么要在瑞芯微的ultralytics_yolov8中进行转换:主要是为了后续适配瑞芯微官方的ultralytics_yolov8的demo。
而ultralytics_yolov8相较ultralytics做以下改动:

1、修改输出结构,移除后处理结构(后处理结构对量化不友好)(但是量化后得到的score往往集中在几个固定的值,这也是量化的缺陷之一,但是速度会快很多)
2、 dfl结构在NPU处理上性能不佳,移至模型外部的后处理阶段,此操作大部分情况下可提升推理性能。
3、模型输出分支新增置信度的总和,用于后处理阶段加速阈值筛选。
注:以上移除的操作,均需要在外部使用CPU进行相应的处理.(对应的后处理代码可以在rknn_model_zoo中找到)

在ultralytics_yolov8中,修改ultralytics/cfg/default.yaml中的model参数为自己的PT模型路径:

在这里插入图片描述

再运行python ultralytics/engine/exporter.py,即可转换出onnx模型
正确的ONNX模型的结构如下:应该有13个output:

在这里插入图片描述

这里要提一下,如果你的分割类别只有一类,并且把yaml文件中的80个类别删掉改成自己的一个类别后,你的onnx模型结构会和官方不同,如下所示:
↓:这是只有一个类别的onnx

在这里插入图片描述
这是80个类别的onnx:

在这里插入图片描述
转换后的rknn模型也会在对应的位置有所不同:
↓:一个类别

在这里插入图片描述
↓80个类别:

在这里插入图片描述
如果你不想在官方demo上做修改,在编译成功后直接就运行的话,那么就保留之前的80个类别,然后把其中一个类别改成自己的。
我的具体做法是:在自己的地毯分割数据集中,加入coco128数据集,同时把coco128中txt标签中0的序号(person)全部删掉,然后在地毯分割的数据集标注中,全都标为0,这样我们做出的数据集就和coco128完全一样,只是把原coco中的person类别改成carpet地毯类,并且转换出的onnx模型结构和官方的完全一致。

2. ONNX转RKNN

转RKNN的PC设备、环境创建等内容,可以参考我的这篇文章中关于rknn-toolkit2的讲解,就是下载toolkit2后用于环境安装。
然后下载rknn-model-zoo-2.1.0,在目录rknn-model-zoo/examples/yolov8_seg/python下执行convert.py进行转换,先把转出来的onnx模型放到python目录下,然后可以打开convert.py修改下转出的rknn模型的保存路径:

在这里插入图片描述
然后在终端输入:python convert.py 9_6onnx.onnx rk3588
结果如下:

在这里插入图片描述
在model文件夹下看到我们已经转出的模型,用netron打开后看到结构与官网模型完全一致:

在这里插入图片描述

三、RK3588部署

这个部分比较简单,没有什么技巧,把rknn_model_zoo/examples/yolov8_seg/cpp/rknpu2下的postprocess.cc、yolov8_seg.cc和cpp下的main.cc作为源文件,然后把cmake时缺少的头文件在rknn_model_zoo中检索一下即可找到,复制到include文件夹即可,最后再把librknnrt.so动态链接库放到目录中,修改下CMakeLists.txt,我的CMakeLists.txt如下:

cmake_minimum_required(VERSION 3.10)

project(yolov8_seg)

if (ENABLE_ASAN)
	message(STATUS "BUILD WITH ADDRESS SANITIZER")
	set (CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -fno-omit-frame-pointer -fsanitize=address")
	set (CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -fno-omit-frame-pointer -fsanitize=address")
	set (CMAKE_LINKER_FLAGS_DEBUG "${CMAKE_LINKER_FLAGS_DEBUG} -fno-omit-frame-pointer -fsanitize=address")
endif ()

add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/../../../3rdparty/ 3rdparty.out)
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/../../../utils/ utils.out)

#opencv
if(CMAKE_SIZEOF_VOID_P EQUAL 8)
message(STATUS "64bit")
set (TARGET_LIB_ARCH lib64)
else()
message(STATUS "32bit")
set (TARGET_LIB_ARCH lib)
endif()
if (CMAKE_SYSTEM_NAME STREQUAL "Android")
  set(OpenCV_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../../../3rdparty/opencv/opencv-android-sdk-build/sdk/native/jni/abi-${CMAKE_ANDROID_ARCH_ABI})
else()
    if(TARGET_LIB_ARCH STREQUAL "lib")
      set(OpenCV_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../../../3rdparty/opencv/opencv-linux-armhf/share/OpenCV)
    else()
      set(OpenCV_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../../../3rdparty/opencv/opencv-linux-aarch64/share/OpenCV)
    endif()
endif()
find_package(OpenCV REQUIRED)
message(STATUS OpenCV_DIR=${OpenCV_DIR})
message(STATUS OpenCV_LIBS=${OpenCV_LIBS})

set(CMAKE_INSTALL_RPATH "$ORIGIN/../lib")

file(GLOB SRCS ${CMAKE_CURRENT_SOURCE_DIR}/*.cc)

#dma
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../../../3rdparty/allocator/dma)

#drm
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../../../3rdparty/allocator/drm)

if (TARGET_SOC STREQUAL "rk1808" OR TARGET_SOC STREQUAL "rv1109" OR TARGET_SOC STREQUAL "rv1126")
    set(postprocess_file rknpu1/postprocess.cc)
    set(yolov8_seg_file rknpu1/yolov8_seg.cc)
    #matmul
else()
    set(postprocess_file rknpu2/postprocess.cc)
    set(yolov8_seg_file rknpu2/yolov8_seg.cc)
endif()

add_executable(${PROJECT_NAME}
    main.cc
    ${postprocess_file}
    ${yolov8_seg_file}
)

if (TARGET_SOC STREQUAL "rk1808" OR TARGET_SOC STREQUAL "rv1109" OR TARGET_SOC STREQUAL "rv1126")
  target_link_libraries(${PROJECT_NAME}
      fileutils
      imageutils
      imagedrawing
      ${OpenCV_LIBS}    
      ${LIBRKNNRT}
  )
else()
  target_link_libraries(${PROJECT_NAME}
      fileutils
      imageutils
      imagedrawing
      ${OpenCV_LIBS}    
      ${LIBRKNNRT}
  )
endif()

if (CMAKE_SYSTEM_NAME STREQUAL "Android")
    target_link_libraries(${PROJECT_NAME}
    log
)
endif()

if (CMAKE_SYSTEM_NAME STREQUAL "Linux")
    set(THREADS_PREFER_PTHREAD_FLAG ON)
    find_package(Threads REQUIRED)
    target_link_libraries(${PROJECT_NAME} Threads::Threads)
endif()

target_include_directories(${PROJECT_NAME} PRIVATE
    ${CMAKE_CURRENT_SOURCE_DIR}
    ${LIBRKNNRT_INCLUDES}
    ${LIBTIMER_INCLUDES}
)

install(TARGETS ${PROJECT_NAME} DESTINATION .)
install(FILES ${LIBRKNNRT} DESTINATION lib)
install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/../model/bus.jpg DESTINATION model)
install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/../model/coco_80_labels_list.txt DESTINATION model)

file(GLOB RKNN_FILES "${CMAKE_CURRENT_SOURCE_DIR}/../model/*.rknn")
install(FILES ${RKNN_FILES} DESTINATION model)

在终端输入cmake . 编译生成Makefile,再输入make生成可执行文件yolov8_seg。
然后在终端执行./yolov8_seg ./model/9_6rknn.rknn ./inputimage/xxx.jpg
即可完成分割。
我用两张图片进行测试,结果如下:
原图1:

在这里插入图片描述
分割结果1:

在这里插入图片描述
原图2:

在这里插入图片描述
分割结果2:

在这里插入图片描述
分割结果与耗时如下:

在这里插入图片描述
最后提示一下:如果A类别在训练时的图片是640×640的,那么在开发板上推理时也要640×640的,不然会检测和分割不到目标。我的carpet类别的训练、转换、推理的图片小都是1920×1080的。当然,这个问题可以在源文件中修改解决,如果不想修改,就需要注意这个问题,防止检测和分割不出目标。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值