yolov8seg 瑞芯微RKNN部署

这篇具有很好参考价值的文章主要介绍了yolov8seg 瑞芯微RKNN部署。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

首先是把yolov8的onnx模型转成rknn模型,这里用的是yolov8n-seg.
转模型代码如下,这段是python代码:

if __name__ == '__main__':

    platform = 'rkXXXX' #写自己的型号
    exp = 'yolov8n_seg'
    Width = 640
    Height = 640
    MODEL_PATH = './onnx_models/yolov8n-seg.onnx'
    NEED_BUILD_MODEL = True
    # NEED_BUILD_MODEL = False
    im_file = './dog_bike_car_640x640.jpg'

    # Create RKNN object
    rknn = RKNN(verbose=False)

    OUT_DIR = "rknn_models"
    RKNN_MODEL_PATH = '{}/{}.rknn'.format(OUT_DIR,exp)
    if NEED_BUILD_MODEL:
        DATASET = './dataset.txt'
        rknn.config(mean_values=[[0, 0, 0]], std_values=[[255, 255, 255]], target_platform="rkXXXX")
        # Load model
        print('--> Loading model')
        ret = rknn.load_onnx(MODEL_PATH)
        if ret != 0:
            print('load model failed!')
            exit(ret)
        print('done')

        # Build model
        print('--> Building model')
        ret = rknn.build(do_quantization=True, dataset=DATASET)
        if ret != 0:
            print('build model failed.')
            exit(ret)
        print('done')

        # Export rknn model
        if not os.path.exists(OUT_DIR):
            os.mkdir(OUT_DIR)
        print('--> Export RKNN model: {}'.format(RKNN_MODEL_PATH))
        ret = rknn.export_rknn(RKNN_MODEL_PATH)
        if ret != 0:
            print('Export rknn model failed.')
            exit(ret)
        print('done')
    else:
        ret = rknn.load_rknn(RKNN_MODEL_PATH)

    rknn.release()

运行成功之后会得到yolov8n_seg.rknn模型。

往下是cpp代码。
运行rknn模型,不是零copy的话用这段代码。

//设置outputs数组保存结果
rknn_output outputs[io_num.n_output];  //长度为2的数组
memset(outputs, 0, sizeof(outputs));
for (int i = 0; i < io_num.n_output; i++) {
  outputs[i].want_float = 0;
}

ret = rknn_run(ctx, NULL); //运行rknn模型
//这里不是零copy方法,需要用到rknn_outputs_get函数
ret = rknn_outputs_get(ctx, io_num.n_output, outputs, NULL);

零copy方法用这段代码。

ret = rknn_run(ctx, nullptr);

memcpy(y0, output_mems[0]->virt_addr, output_attrs[0].n_elems * sizeof(char)); //outputs[0]copy到y0
memcpy(y1, output_mems[1]->virt_addr, output_attrs[1].n_elems * sizeof(char));

下面来看下rknn模型运行结果outputs的结构。
具体这个结果怎么来的以及详细含义的请参考此处。
outputs的size为2,其中outputs[0]的size是8400*176,8400是box个数,
176包含了3个部分,64是box坐标,80是类别prob, 32是mask coeff.
outputs[1]是proto_type, size为32 * 160 * 160.

want_float是这个结果是否输出为float,这里设0表示还是int输出,后面再反量化。
数据部分保存在buf里,它是(void*), 用的时候cast到(int8_t*).

yolov8seg 瑞芯微RKNN部署,DeepLearning,YOLO

现在需要写后处理,通过outputs得到目标框和mask.

主要思路如下:

1. 反量化

rknn模型输出的是量化过后的int8_t型,而计算时需要反量化成float型,
先看看rknn模型是怎么量化的。

  rknn_tensor_attr output_attrs[io_num.n_output];
  memset(output_attrs, 0, sizeof(output_attrs));
  for (int i = 0; i < io_num.n_output; i++) {
    output_attrs[i].index = i;
    //rknn_query:查询模型与sdk的相关信息
    //output_attrs:存放返回结果的结构体变量
    ret                   = rknn_query(ctx, RKNN_QUERY_OUTPUT_ATTR, &(output_attrs[i]), sizeof(rknn_tensor_attr));
    dump_tensor_attr(&(output_attrs[i]));
  }

dump结果如下,可以看到模型输出的type为int8, 量化用的是affine变换,
也就是 output / scale + zp.

yolov8seg 瑞芯微RKNN部署,DeepLearning,YOLO

那么反量化时就需要 (output - zp) * scale

//反量化计算
static float deqnt_affine_to_f32(int8_t qnt, int32_t zp, float scale) { return ((float)qnt - (float)zp) * scale; }

2. box部分

8400 * 176, 其中8400是box个数,176包含了3个部分,64是box坐标,80是类别prob, 32是mask coeff.
8400个box部分取出每个box的80个类别prob, 选出最大的类别prob就是这个box的label,
类别prob > 阈值时说明有效,
每176的向量中前64个数据,每16个一组,一共4组,可得到box的(l,t,r,b).
用8400个anchor的中心点与(l,t,r,b)运算可得到box的(x0, y0, x1, y1).

另外把每个box的32维mask coeff保存起来。

注意判断类别prob是否 > 阈值时,要先把类别prob做反量化。

for (int i = 0; i < box_num; i++) { //遍历8400个box
    ...
    // find label with max score
	int label = -1;
	float score = -FLT_MAX;
	//找到最大score和对应的label
	for (int k = 0; k < num_class; k++)  //80个类别prob
	{
	    float confidence = deqnt_affine_to_f32(score_ptr[k], zp, scale);  //反量化,int转float
	    if (confidence > score)
	    {
	        label = k;
	        score = confidence;
	    }
	}
	float box_prob = sigmoid(score); 
	       
	if (box_prob >= prob_threshold){
        ... 
    	softmax(bbox_pred);  //对4x16中的每16个一组求softmax,因为作了softmax,就不再需要对每个数据作反量化
    	...
		//anchor中心点
		float pb_cx = (grid_strides[i].grid0 + 0.5f) * grid_strides[i].stride;
		float pb_cy = (grid_strides[i].grid1 + 0.5f) * grid_strides[i].stride;
		
		float x0 = pb_cx - pred_ltrb[0]; //center_x - l
		float y0 = pb_cy - pred_ltrb[1]; //center_y - t
		float x1 = pb_cx + pred_ltrb[2]; //center_x + r
		float y1 = pb_cy + pred_ltrb[3]; //center_y + b
		
		Object obj;
		obj.rect.x = x0;
		obj.rect.y = y0;
		obj.rect.width = x1 - x0;
		obj.rect.height = y1 - y0;
		obj.label = label;
		obj.prob = box_prob;
		obj.mask_feat.resize(32);
		
		std::copy(bbox_ptr + 64 + num_class, bbox_ptr + 64 + num_class + 32, obj.mask_feat.begin());
		objects.push_back(obj);
	}
}

上面是用类别prob的阈值过滤了第一波box,
然后还要用nms再过滤一波。

nms_sorted_bboxes(proposals, picked, nms_threshold); //picked里面保存的是proposals的下标

3. mask部分

mask由mask_coeff 和 proto_type进行矩阵乘得到。
mask_coeff保存在上面objects中每个obj的mask_feat里面。
注意上面的mask_feat还没有作反量化。

outputs[1]是数据开头的指针,
为了方便后面的矩阵乘,resize等操作,需要把它转为cv::Mat.

注意如果是int8型的Mat,它会自动把数值限制到[0,255],
而显然mask_feat里面是有负数的,所以要用float型的Mat.

先把nms挑选出来的目标的mask_coeff转为Mat。然后作反量化计算。

//mask_feat里面保存每个目标的mask coeff
cv::Mat mask_feat_qnt(count, 32, CV_32FC1); //float型的Mat, count为nms后目标的个数
for (int i = 0; i < count; i++) {
    float* mask_feat_ptr = mask_feat_qnt.ptr<float>(i);  //指向第i行的指针
    std::memcpy(mask_feat_ptr, proposals[picked[i]].mask_feat.data(), sizeof(float) * proposals[picked[i]].mask_feat.size());
}

//因为mask_feat从outputs[0]中得到,所以用zps[0],scales[0]作反量化
cv::Mat mask_feat = (mask_feat_qnt - out_zps[0]) * out_scales[0];

把proto_type转为cv::Mat. proto_type中的值也要进行反量化计算。

cv::Mat proto = cv::Mat(32, 25600, CV_32FC1, proto_arr);

mask_coeff 和 proto_type 矩阵乘,得到目标的mask.

cv::Mat mulResMask = (mask_feat * proto).t(); //(n,32)*(32,25600)=n*25600,必须加转置,不加的话mask会很奇怪
cv::Mat masks = mulResMask.reshape(count, {160, 160}); //每个目标有一个160*160的mask 

std::vector<cv::Mat> maskChannels;
cv::split(masks, maskChannels); //把count个mask分割开,每个目标的mask保存为vector的一个元素

把每个目标的box, mask保存到结果objects中。

std::vector<Object> objects;
objects.resize(count);  //保存结果的object
 for (int i = 0; i < count; i++)
 {
     objects[i] = proposals[picked[i]];

     // adjust offset to original unpadded
     float x0 = (objects[i].rect.x - (wpad / 2)) / scale;
     float y0 = (objects[i].rect.y - (hpad / 2)) / scale;
     float x1 = (objects[i].rect.x + objects[i].rect.width - (wpad / 2)) / scale;
     float y1 = (objects[i].rect.y + objects[i].rect.height - (hpad / 2)) / scale;

     // clip
     x0 = std::max(std::min(x0, (float)(img_width - 1)), 0.f);
     y0 = std::max(std::min(y0, (float)(img_height - 1)), 0.f);
     x1 = std::max(std::min(x1, (float)(img_width - 1)), 0.f);
     y1 = std::max(std::min(y1, (float)(img_height - 1)), 0.f);

     objects[i].rect.x = x0;
     objects[i].rect.y = y0;
     objects[i].rect.width = x1 - x0;
     objects[i].rect.height = y1 - y0;

     //计算mask并保存到object
     cv::Mat dest, mask;
     cv::exp(-maskChannels[i], dest); //每个mat单独计算sigmoid
     dest = 1.0 / (1.0 + dest);  //160x160的mask
     dest = dest(roi);  //取rect区域

     cv::resize(dest, mask, cv::Size(img_width, img_height), cv::INTER_NEAREST);

     //crop
     cv::Rect tmp_rect = objects[i].rect;
     mask = mask(tmp_rect) > 0.5; //mask阈值设为0.5
     objects[i].mask = mask;
 }

效果如下:

yolov8seg 瑞芯微RKNN部署,DeepLearning,YOLO

完整版代码放在github的semantic。文章来源地址https://www.toymoban.com/news/detail-642764.html

到了这里,关于yolov8seg 瑞芯微RKNN部署的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包赞助服务器费用

相关文章

  • yolov8n 瑞芯微RKNN和地平线Horizon芯片仿真测试部署,部署工程难度小、模型推理速度快

    yolov8n 瑞芯微RKNN和地平线Horizon芯片仿真测试部署,部署工程难度小、模型推理速度快

      特别说明:参考官方开源的yolov8代码、瑞芯微官方文档、地平线的官方文档,如有侵权告知删,谢谢。   模型和完整仿真测试代码,放在github上参考链接 模型和代码。   因为之前写了几篇yolov8模型部署的博文,存在两个问题:部署难度大、模型推理速度慢。该篇解

    2024年02月01日
    浏览(13)
  • 瑞芯微RK3588 C++部署Yolov8检测和分割模型

    瑞芯微RK3588 C++部署Yolov8检测和分割模型

    最近这一个月在研究国产瑞芯微板子上部署yolov8的检测和分割模型,踩了很多坑,记录一下部署的过程和遇到的一些问题: 需要的环境和代码主要包括: (1)rknn-toolkit2-1.5.2:工具链,开发环境 (2)rockchip-yolov8:pt模型转onnx模型 (3)yolov8_onnx2rknn:在(2)的基础上转检测

    2024年04月09日
    浏览(8)
  • 瑞芯微RK3568/RK3588平台YOLOV5实时视频算法的部署小白教程

    瑞芯微RK3568/RK3588平台YOLOV5实时视频算法的部署小白教程

    本文实现整体的部署流程比较小白,首先在PC上分别实现工程中的模型仿真推理、yolov5-pytorch仿真推理、自己训练yolov5模型仿真推理,完成仿真之后再在板端分别实现rk提供模型的板端推理、yolov5-pytorch板端推理、自己训练的yolov5模型板端推理,最后实现自己训练的yolov5模型实

    2024年02月06日
    浏览(95)
  • 【YOLOv8-Seg】实战二:LabVIEW+OpenVINO加速YOLOv8-seg实例分割

    【YOLOv8-Seg】实战二:LabVIEW+OpenVINO加速YOLOv8-seg实例分割

    ‍‍🏡博客主页: virobotics的CSDN博客:LabVIEW深度学习、人工智能博主 🎄所属专栏:『LabVIEW深度学习实战』 🍻上期文章: 【YOLOv8-seg】实战一:手把手教你使用YOLOv8实现实例分割 📰如觉得博主文章写的不错或对你有所帮助的话,还望大家多多支持呀! 欢迎大家✌关注、👍

    2024年02月13日
    浏览(10)
  • YOLOv5-7.0-seg+YOLOv8-seg自定义数据集训练

    下载源码   https://github.com/ultralytics/yolov5.git 参考链接   yolov5-实例分割 1.如何使用yolov5实现实例分割,并训练自己的数据集_哔哩哔哩_bilibili 目录: - datasets     - JPEImages #存放图片和标注后的json文件以及转换后的txt文件     - classes-4 #存放切分好的数据集         - images    

    2024年02月01日
    浏览(14)
  • 舌头分割YOLOV8-SEG

    舌头分割YOLOV8-SEG

    舌头分割,基于YOLOV8-SEG,训练得到PT模型,然后转换成ONNX,OPENCV的DNN调用,从而摆脱YOLO依赖,支持C++,PYTHON,ANDROID开发 舌头分割YOLOV8-SEG

    2024年04月28日
    浏览(12)
  • 轻量级实时跟踪算法NanoTrack在瑞芯微RK3588上的部署以及使用

    轻量级实时跟踪算法NanoTrack在瑞芯微RK3588上的部署以及使用

    文章目录 前言 一、模型转换 1.环境配置 2.模型解构 二、rk3588平台使用 1.模型初始化 2.推理 github: https://github.com/Try2ChangeX/NanoTrack_RK3588_python: python版本基于rk3588的NanoTrack,每秒可达120FPS 主要参考: SiamTrackers/NanoTrack at master · HonglinChu/SiamTrackers · GitHub GitHub - rockchip-linux/rknn-tool

    2024年02月13日
    浏览(14)
  • 手把手教程 | YOLOv8-seg训练自己的分割数据集

    手把手教程 | YOLOv8-seg训练自己的分割数据集

    🚀🚀🚀手把手教程:教会你如何使用自己的数据集开展分割任务 🚀🚀🚀YOLOv8-seg创新专栏: 学姐带你学习YOLOv8,从入门到创新,轻轻松松搞定科研; 1)手把手教你如何训练YOLOv8-seg; 2)模型创新,提升分割性能; 3)独家自研模块助力分割; 番薯破损分割任务,自己手

    2024年02月05日
    浏览(16)
  • YOLOv8改进 | 检测头篇 | 利用DySnakeConv改进检测头专用于分割的检测头(全网独家首发,Seg)

    本文给大家带来的改进机制是一种我进行优化的专用于分割的检测头,在分割的过程中,最困难的无非就是边缘的检测, 动态蛇形卷积 (Dynamic Snake Convolution)通过自适应地聚焦于细长和迂回的局部结构,准确地捕捉管状结构的特征。这种卷积方法的核心思想是, 通过动态形

    2024年02月02日
    浏览(11)
  • 【武汉万象奥科】瑞芯微RK3568芯片

    【武汉万象奥科】瑞芯微RK3568芯片

    ▎产品展示 RK3568核心板是基于Rockchip的RK3568设计的一款高性能核心板。该处理器集成了最新的高性能CPU、GPU,并拥有丰富的接口,非常适用于工业自动化控制、人机界面、中小型医疗分析器、电力等多种行业应用。   ▎RK3568产品特点 ▎ 高性能处理器 ○ 采用四核A55架构CPU,

    2024年02月06日
    浏览(37)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包