【CV学习笔记】tensorrt篇之HRnet

1、前言

在计算机视觉领域,除了对常见的检测、分割网络进行了学习,最近又看到了关注的up主发布了有关关键点检测的网络,正是本文的所学习的网络:HRnet,本文在源代码的基础上,进行了python版本的后处理优化、并使用cupy和torch分别来进行后处理,并对比了两者在后处理的时间,同时在cpp代码里面实现了cuda后处理加速。

原文地址为:https://blog.csdn.net/qq_37541097/article/details/124346626?spm=1001.2014.3001.5501

个人学习地址为:https://github.com/Rex-LK/tensorrt_learning
欢迎正在学习或者想学的CV的同学进群一起讨论与学习,v:Rex1586662742,q群:468713665

2、HRnet简介

在原hrnet博文里面up主绘制了相应的网络结构图,这里就直接拿来用了。
在这里插入图片描述

从图中可看出,图像经过一系列的特征提取,尤其是stage1~3里面的将不同尺度的特征信息进行融合,最终得到了一个64×48×17的特征图,其中64×48表示将图片划分为64×48个热力点,网络分别去预测每个点所属每一类的概率,17代表一个人体的关键点的个数。最后是进行后处理,发现诸如此类的热力图模型的后处理都比较简单,可以参考之前的centernet_tensorrt加速。

3、run-demo

在cpp中运行demo

int main(){
    // cmake使用绝对路径,makefile使用相对路径(包括对应加载图片和模型的.cu文件)
    if(!BaiscTools::build_model("/home/rex/Desktop/tensorrt_learning/trt_cpp/workspace/centernet",1)){
        return -1;
    }
    demoInfer demo;
    //string demo_name = "hrnet"; 
    string demo_name = "hrnet_gpu"; 
    demo.do_infer(demo_name);
    return 0;
}

在python中运行demo

#可能需要调整文件夹的路径
python trtpy/centernet/centernet_inferencv.py

3.1、build_engine

首先将pytorch模型转化为onnx,再将onnx模型幻化为engien,关于转化模型的文章可以参考:https://blog.csdn.net/weixin_42108183/article/details/124969680,拿到onnx模型之后,可以用netron 打开,确认输出没有问题之后,这里的输出的维度为1×17×64×48。下面就可以进行onnx转engine了,依然使用build_engine/batch_1/build_engine_b1.py (此时多batch推理依然没有实现)

3.2、inference

这里只简单展示后处理代码,python

   if use_cupy:
        print("use_cupy")
        t_start = int(time.time() * 1000)
        pred = cp.asarray(pred)
        t_process_start = int(time.time() * 1000)
        print(f"time in numpy2cupy = {t_process_start - t_start} ms")
        pred = pred.reshape(17, 64 * 48)
        indx = cp.argmax(pred, 1)
        scores = cp.max(pred, axis=1)
        keep = scores > 0.2
        indx = indx[keep]
        scores = scores[keep]
        t_end = int(time.time() * 1000)
        print(f'use_cupy postprocess time : {t_end - t_process_start} ms')

    elif use_torch:
        print("use_torch")
        t_start = int(time.time() * 1000)
        pred = torch.from_numpy(pred)
        t_process_start = int(time.time() * 1000)
        print(f"time in numpy2cupy = {t_process_start - t_start} ms")
        pred = pred.reshape(1, 17, 64 * 48).squeeze(0)
        #找到每一行满足条件的点
        keep = pred.max(-1).values > 0.2
        pred = pred[keep]
        scores, indx = torch.max(pred, 1)
        t_end = int(time.time() * 1000)
        print(f'use_torch postprocess time : {t_end - t_process_start} ms')
	#计算坐标
    x = indx % 48
    y = indx / 48
    #恢复至原图大小
    x_os = (x * inv_M[0][0]) + inv_M[0][2]
    y_os = (y * inv_M[1][1]) + inv_M[1][2]
    for x_o, y_o in zip(x_os, y_os):
        cv2.circle(image_o, (int(x_o), int(y_o)), 1, (255, 0, 0), 2)
    cv2.imshow('image', image_o)
    cv2.waitKey(0)

由于最近发现了cupy库,于是尝试用cupy库来进行后处理,结果发现,从numpy中复制数据到cupy中花的时间较长,如果从图像的预处理到推理和后处理都使用cupy的话,应该是能节省一部分时间的,这部分还未实现,总的来说,在这个后处理中,torch花的时间更少,或许cupy还 有其他方法进行加速,毕竟也是刚接触,后续会持续跟进。后处理的方法可以看成从17*3072这个二维矩阵中(每一行代表该类图上出现的以及位置概率)

cpp后处理部分代码

    //预测值
    float* prob = output->cpu<float>();
    float* start = prob;
   	//类别
    int nums = 17;
	//64*48
    int pic_region = 3072;
    for(int i=0;i<output->count();i+=pic_region){
        start = prob + i;
        //位置
        int label = (max_element(start,start+pic_region) - start);
        float score = start[label];
        if(score<0.2)
            continue;
        float x = label % 48 ;
        float y = label / 48 ;
        //这里计算的warpaffine的变换矩阵中的缩放系数 与 pyhton中image_transformer差四倍
        int x_o = (x * d2i[0] * 4) + d2i[2];
        int y_o = (y * d2i[4] * 4) + d2i[5]; 
        cv::circle(img_o, cv::Point((int)x_o,(int)y_o), 1, (0, 0, 255), 2);
        cv::imwrite("hrnet-pred.jpg", img_o);
    }

cpp-cuda后处理代码

static __global__ void hrnet_decode_kernel(
    float* predict,int num_bboxes, int num_classes, float confidence_threshold, 
    float* invert_affine_matrix, float* parray, int max_objects, int NUM_BOX_ELEMENT
){  
    int position = blockDim.x * blockIdx.x + threadIdx.x;
    if (position >= num_bboxes) return;
    // //每隔64*48个位置为 跳到下一行
    float* pitem     = predict + (64*48) * position;
    //pitem 这个类别在图中所有点的概率
    int label = 0;
    //第一个位置的socre
    float* class_confidence = pitem;
    float confidence  = *class_confidence;
    
    for(int i = 1; i < num_classes; ++i, ++class_confidence){
        if(*class_confidence > confidence){
            confidence = *class_confidence;
            label      = i;
        }
    }
    
    if(confidence < confidence_threshold)
        return;
    // printf("%f\n",confidence);
    // printf("%d\n",label-1);
    // printf("%d\n",position);

    int index = atomicAdd(parray, 1);
    if(index >= max_objects)
        return;
    float* pwhxy = pitem;

    float cx  = ((label-1) % 48);
    float cy  = ((label-1) / 48);

    // 第一个位置用来计数
    // left, top, right, bottom, confidence, class, keepflag
    float* pout_item = parray + 1 + index * NUM_BOX_ELEMENT;
    *pout_item++ = cx;
    *pout_item++ = cy;
    *pout_item++ = position;
    *pout_item++ = confidence;
}

其解码方式与cneternet如出一辙,只是减少了nms的操作,最后用一张图来看看hrnet来预测人体关键点的的效果把。
在这里插入图片描述

4、总结

本次学习内容主要复习了HRnet的网络结构,其网络的主要特征是多次将不同尺度的特征相融合之后,可以得到比较好的预测效果,同时通过对热力图预测网络的学习,发现其后处理的方式基本一致,而且能够快速的在cuda上进行部署,同时个熟悉了cupy的一些基本的操作。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Rex久居

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

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

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

打赏作者

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

抵扣说明:

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

余额充值