TRT4-trt-integrate - 3 使用onnxruntime进行onnx的模型推理过程

前言:

  • onnx是microsoft开发的一个中间格式,而onnxruntime简称ort是microsoft为onnx开发的推理引擎。
  • 允许使用onnx作为输入进行直接推理得到结果。

py接口的推理过程:

 main函数:


if __name__ == "__main__":

    session = onnxruntime.InferenceSession("workspace/yolov5s.onnx", providers=["CPUExecutionProvider"])#建立一个InferenceSession,塞进去的是onnx的路径实际运算

    image = cv2.imread("workspace/car.jpg")
    image_input, M, IM = preprocess(image)
    pred = session.run(["output"], {"images": image_input})[0]
    boxes = post_process(pred, IM)

    for obj in boxes:
        left, top, right, bottom = map(int, obj[:4])
        confidence = obj[4]
        label = int(obj[6])
        cv2.rectangle(image, (left, top), (right, bottom), (0, 255, 0), 2)
        cv2.putText(image, f"{label}: {confidence:.2f}", (left, top+20), 0, 1, (0, 0, 255), 2, 16)

    cv2.imwrite("workspace/python-ort.jpg", image)
 session = onnxruntime.InferenceSession("workspace/yolov5s.onnx", providers=["CPUExecutionProvider"])

建立一个InferenceSession,塞进去的是onnx的路径,实际运算的后端选用的是CPU

也可以选用cuda等等

    image = cv2.imread("workspace/car.jpg")
    image_input, M, IM = preprocess(image)

之后就是预处理

    pred = session.run(["output"], {"images": image_input})[0]
    boxes = post_process(pred, IM)

session.run就是运行的inference过程

 输入第一个是output的name,决定了哪几个节点作为输出,就将这个名字传递给他

第二个是input的dict,这个意思就是如果有好多个输入,那应该是将名字与输入进行一一对应,比如"input1 ":input1  ,   "input2":input2....

那么在这里output就是一个输出的list,然后我们取第0项

就是这个样子。

预处理:


def preprocess(image, input_w=640, input_h=640):
    scale = min(input_h / image.shape[0], input_w / image.shape[1])
    ox = (-scale * image.shape[1] + input_w + scale  - 1) * 0.5
    oy = (-scale * image.shape[0] + input_h + scale  - 1) * 0.5
    M = np.array([
        [scale, 0, ox],
        [0, scale, oy]
    ], dtype=np.float32)
    IM = cv2.invertAffineTransform(M)

    image_prep = cv2.warpAffine(image, M, (input_w, input_h), flags=cv2.INTER_LINEAR, borderMode=cv2.BORDER_CONSTANT, borderValue=(114, 114, 114))
    image_prep = (image_prep[..., ::-1] / 255.0).astype(np.float32)
    image_prep = image_prep.transpose(2, 0, 1)[None]
    return image_prep, M, IM

 

后处理:

def nms(boxes, threshold=0.5):

    keep = []
    remove_flags = [False] * len(boxes)
    for i in range(len(boxes)):

        if remove_flags[i]:
            continue

        ib = boxes[i]
        keep.append(ib)
        for j in range(len(boxes)):
            if remove_flags[j]:
                continue

            jb = boxes[j]

            # class mismatch or image_id mismatch
            if ib[6] != jb[6] or ib[5] != jb[5]:
                continue

            cleft,  ctop    = max(ib[:2], jb[:2])
            #例子:
            #将 ib 的前两个元素 [2, 3] 与 jb 的前两个元素 [4, 1] 进行比较,并取其中较大的值。所以结果是 [4, 3]。
            cright, cbottom = min(ib[2:4], jb[2:4])
            cross = max(0, cright - cleft) * max(0, cbottom - ctop)
            union = max(0, ib[2] - ib[0]) * max(0, ib[3] - ib[1]) + max(0, jb[2] - jb[0]) * max(0, jb[3] - jb[1]) - cross
            iou = cross / union
            if iou >= threshold:
                remove_flags[j] = True
    return keep

def post_process(pred, IM, threshold=0.25):

    # b, n, 85
    boxes = []
    for image_id, box_id in zip(*np.where(pred[..., 4] >= threshold)):
        item = pred[image_id, box_id]
        cx, cy, w, h, objness = item[:5]
        label = item[5:].argmax()
        confidence = item[5 + label] * objness
        if confidence < threshold:
            continue

        boxes.append([cx - w * 0.5, cy - h * 0.5, cx + w * 0.5, cy + h * 0.5, confidence, image_id, label])

    boxes = np.array(boxes)
    lr = boxes[:, [0, 2]]
    tb = boxes[:, [1, 3]]
    boxes[:, [0, 2]] = lr * IM[0, 0] + IM[0, 2]
    boxes[:, [1, 3]] = tb * IM[1, 1] + IM[1, 2]

    # left, top, right, bottom, confidence, image_id, label
    boxes = sorted(boxes.tolist(), key=lambda x:x[4], reverse=True)
    return nms(boxes)

 我们可以发现,真正的onnxruntime只有两行,一个onnxruntime.InferenceSession,一个run就结束了。其余的都是和之前一样的,这是非常好用便捷的,所以如果有模型需要作测试,是非常推荐用onnxruntime的

CPP接口推理过程:

Inference:

在main函数中只有一个inference


int main(){
    inference();
    return 0;
}

所以我们直接来到inference的解读中

 auto engine_data = load_file("yolov5s.onnx");
//读onnx文件
    Ort::Env env(ORT_LOGGING_LEVEL_INFO, "onnx");
//设置打印的日志级别    
    Ort::SessionOptions session_options;
//定义sessionoptions 类似于python中的    session =     onnxruntime.InferenceSession("workspace/yolov5s.onnx", providers=["CPUExecutionProvider"])

    auto mem = Ort::MemoryInfo::CreateCpu(OrtArenaAllocator, OrtMemTypeDefault);
//设置MemoryInfo
    session_options.SetIntraOpNumThreads(1);
    session_options.SetGraphOptimizationLevel(GraphOptimizationLevel::ORT_ENABLE_EXTENDED);
//启动一些扩展
    Ort::Session session(env, "yolov5s.onnx", session_options);
    //创建session,将选项传进去
    auto output_dims = session.GetOutputTypeInfo(0).GetTensorTypeAndShapeInfo().GetShape();
    //获取output的shape
    const char *input_names[] = {"images"}, *output_names[] = {"output"};
    int input_batch = 1;
    int input_channel = 3;
    int input_height = 640;
    int input_width = 640;
    int64_t input_shape[] = {input_batch, input_channel, input_height, input_width};
    int input_numel = input_batch * input_channel * input_height * input_width;
    float* input_data_host = new float[input_numel];
    auto input_tensor = Ort::Value::CreateTensor(mem, input_data_host, input_numel, input_shape, 4);
    //创建一个Tensor,引用input_data_host中的数据

预处理:


    ///
    // letter box
    auto image = cv::imread("car.jpg");
    float scale_x = input_width / (float)image.cols;
    float scale_y = input_height / (float)image.rows;
    float scale = std::min(scale_x, scale_y);
    float i2d[6], d2i[6];
    i2d[0] = scale;  i2d[1] = 0;  i2d[2] = (-scale * image.cols + input_width + scale  - 1) * 0.5;
    i2d[3] = 0;  i2d[4] = scale;  i2d[5] = (-scale * image.rows + input_height + scale - 1) * 0.5;

    cv::Mat m2x3_i2d(2, 3, CV_32F, i2d);
    cv::Mat m2x3_d2i(2, 3, CV_32F, d2i);
    cv::invertAffineTransform(m2x3_i2d, m2x3_d2i);

    cv::Mat input_image(input_height, input_width, CV_8UC3);
    cv::warpAffine(image, input_image, m2x3_i2d, input_image.size(), cv::INTER_LINEAR, cv::BORDER_CONSTANT, cv::Scalar::all(114));
    cv::imwrite("input-image.jpg", input_image);

    int image_area = input_image.cols * input_image.rows;
    unsigned char* pimage = input_image.data;
    float* phost_b = input_data_host + image_area * 0;
    float* phost_g = input_data_host + image_area * 1;
    float* phost_r = input_data_host + image_area * 2;
    for(int i = 0; i < image_area; ++i, pimage += 3){
        // 注意这里的顺序rgb调换了
        *phost_r++ = pimage[0] / 255.0f;
        *phost_g++ = pimage[1] / 255.0f;
        *phost_b++ = pimage[2] / 255.0f;
    }
    ///

制作输出矩阵并运行:

    // 3x3输入,对应3x3输出
    int output_numbox = output_dims[1];
    int output_numprob = output_dims[2];
    int num_classes = output_numprob - 5;
    int output_numel = input_batch * output_numbox * output_numprob;
    float* output_data_host = new float[output_numel];
    int64_t output_shape[] = {input_batch, output_numbox, output_numprob};
    auto output_tensor = Ort::Value::CreateTensor(mem, output_data_host, output_numel, output_shape, 3);

    Ort::RunOptions options;
    session.Run(options, 
        (const char* const*)input_names, &input_tensor, 1, 
        (const char* const*)output_names, &output_tensor, 1
    );
//指定输入输出的name,tensor和个数,传入tensor进行推理

后处理:

 // decode box
    vector<vector<float>> bboxes;
    float confidence_threshold = 0.25;
    float nms_threshold = 0.5;
    for(int i = 0; i < output_numbox; ++i){
        float* ptr = output_data_host + i * output_numprob;
        float objness = ptr[4];
        if(objness < confidence_threshold)
            continue;

        float* pclass = ptr + 5;
        int label     = std::max_element(pclass, pclass + num_classes) - pclass;
        float prob    = pclass[label];
        float confidence = prob * objness;
        if(confidence < confidence_threshold)
            continue;

        float cx     = ptr[0];
        float cy     = ptr[1];
        float width  = ptr[2];
        float height = ptr[3];
        float left   = cx - width * 0.5;
        float top    = cy - height * 0.5;
        float right  = cx + width * 0.5;
        float bottom = cy + height * 0.5;
        float image_base_left   = d2i[0] * left   + d2i[2];
        float image_base_right  = d2i[0] * right  + d2i[2];
        float image_base_top    = d2i[0] * top    + d2i[5];
        float image_base_bottom = d2i[0] * bottom + d2i[5];
        bboxes.push_back({image_base_left, image_base_top, image_base_right, image_base_bottom, (float)label, confidence});
    }
    printf("decoded bboxes.size = %d\n", bboxes.size());

    // nms
    std::sort(bboxes.begin(), bboxes.end(), [](vector<float>& a, vector<float>& b){return a[5] > b[5];});
    std::vector<bool> remove_flags(bboxes.size());
    std::vector<vector<float>> box_result;
    box_result.reserve(bboxes.size());

    auto iou = [](const vector<float>& a, const vector<float>& b){
        float cross_left   = std::max(a[0], b[0]);
        float cross_top    = std::max(a[1], b[1]);
        float cross_right  = std::min(a[2], b[2]);
        float cross_bottom = std::min(a[3], b[3]);

        float cross_area = std::max(0.0f, cross_right - cross_left) * std::max(0.0f, cross_bottom - cross_top);
        float union_area = std::max(0.0f, a[2] - a[0]) * std::max(0.0f, a[3] - a[1]) 
                         + std::max(0.0f, b[2] - b[0]) * std::max(0.0f, b[3] - b[1]) - cross_area;
        if(cross_area == 0 || union_area == 0) return 0.0f;
        return cross_area / union_area;
    };

    for(int i = 0; i < bboxes.size(); ++i){
        if(remove_flags[i]) continue;

        auto& ibox = bboxes[i];
        box_result.emplace_back(ibox);
        for(int j = i + 1; j < bboxes.size(); ++j){
            if(remove_flags[j]) continue;

            auto& jbox = bboxes[j];
            if(ibox[4] == jbox[4]){
                // class matched
                if(iou(ibox, jbox) >= nms_threshold)
                    remove_flags[j] = true;
            }
        }
    }
    printf("box_result.size = %d\n", box_result.size());

    for(int i = 0; i < box_result.size(); ++i){
        auto& ibox = box_result[i];
        float left = ibox[0];
        float top = ibox[1];
        float right = ibox[2];
        float bottom = ibox[3];
        int class_label = ibox[4];
        float confidence = ibox[5];
        cv::Scalar color;
        tie(color[0], color[1], color[2]) = random_color(class_label);
        cv::rectangle(image, cv::Point(left, top), cv::Point(right, bottom), color, 3);

        auto name      = cocolabels[class_label];
        auto caption   = cv::format("%s %.2f", name, confidence);
        int text_width = cv::getTextSize(caption, 0, 1, 2, nullptr).width + 10;
        cv::rectangle(image, cv::Point(left-3, top-33), cv::Point(left + text_width, top), color, -1);
        cv::putText(image, caption, cv::Point(left, top-5), 0, 1, cv::Scalar::all(0), 2, 16);
    }
    cv::imwrite("image-draw.jpg", image);

    delete[] input_data_host;
    delete[] output_data_host;
}

小结:

可以看到,这个与我们之前yolov5后处理没什么太大的区别,关键只在于对于output_tensor和output作关联,input_tensor和input作关联。

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/46410.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

模拟量采集S_ITR函数(信捷C语言FC)

模拟量采集和转换函数非常简单,这里不再介绍,想了解具体算法的可以查看下面博客文章: PLC模拟量输入 模拟量转换FC S_ITR_博途模拟量转换程序_RXXW_Dor的博客-CSDN博客模拟量采集、工业现场应用特别广泛、大部分传感器的测量值和输出信号都是线型关系,所以我们可以利用线性…

JPA连接达梦数据库导致auto-ddl失效问题解决

现象&#xff1a; 项目使用了JPA&#xff0c;并且auto-ddl设置的为update&#xff0c;在连接达梦数据库的时候&#xff0c;第一次启动没有问题&#xff0c;但是后面重启就会报错&#xff0c;发现错误为重复建表&#xff0c;也就是说已经建好的表没有检测到&#xff0c;…

mysql用户添加

一、连接mysql服务 mysql -u root -p 二、查询用户表 use mysql &#xff1b; SELECT User, Host FROM mysql.user; 三、新增用户并授权 Create USER dev4rw% IDENTIFIED WITH mysql_native_password BY 新密码; GRANT ALL PRIVILEGES ON *.* TO dev4rw% WITH GRANT OP…

【Spring MVC学习】连接 接收请求参数 响应返回参数

目录 前言&#xff1a;认识Spring MVC &#x1f337;1、什么是MVC&#xff1f; 一、建立连接&#xff08;5个注解&#xff09; &#x1f337;1、RequestMapping注解:注册接⼝的路由映射&#xff08;默认返回页面&#xff09; &#x1f337;2、ResponseBody注解&#xff1a…

【C++】开源:Redis数据库配置与使用

&#x1f60f;★,:.☆(&#xffe3;▽&#xffe3;)/$:.★ &#x1f60f; 这篇文章主要介绍Redis数据库配置与使用。 无专精则不能成&#xff0c;无涉猎则不能通。。——梁启超 欢迎来到我的博客&#xff0c;一起学习&#xff0c;共同进步。 喜欢的朋友可以关注一下&#xff0c…

智能驾驶中的数据标注

目前&#xff0c;各大自动驾驶汽车制造商都在通过获取高质量的训练数据最大化其数据资产的投入产出比。在海量的智能驾驶数据面前&#xff0c;如何让每个数据都有存在意义&#xff1f;从《数字商业时代》对澳鹏Appen(中国)高级产品总监张童皓的采访中&#xff0c;你或许能找到一…

MySQL数据库远程访问权限设置

MySQL数据库远程访问权限设置 对于初学者小伙伴来说&#xff0c;我们安装mysql到本地服务&#xff0c;再用一些图形化工具链接。一般情况下我们都能链接成功&#xff1b;但是、在模拟真实的环境中我们的数据库不可能直接安装在本地机器上&#xff0c;大多数是在云服务器上&…

头戴式玩具外贸出口EN71检测报告需要什么资料?

EN71是欧盟市场玩具类产品的规范标准。儿童是全社会最关心和爱护的群体&#xff0c;儿童普遍喜爱的玩具市场发展迅猛&#xff0c;同时各类玩具由于各方面质量问题给儿童带来的伤害也时有发生&#xff0c;因此世界各国对本国市场上的玩具的要求正日益变得严格。许多国家都就这些…

Docker Compose容器的快速编排

Docker Compose容器的快速编排 一、Docker Compose简介1、Docker Compose是什么2、Docker Compose三大概念 二、Docker Compose 安装与操作1、环境安装2、YAML文件格式及编写注意事项3、Docker Compose配置常用字段4、Docker Compose常用命令5、Docker Compose文件结构6、删除创…

Java带符号右移(>>)、不带符号右移(>>>)

Java的右移涉及带符号右移&#xff08;>>&#xff09;、不带符号右移&#xff08;>>>&#xff09;。 对于正数&#xff0c;因为符号位是0&#xff0c;带符号右移和不带符号右移左侧都用0填充&#xff0c;所以结果相同。 对于负数&#xff0c;因为符号位是1&…

四,Eureka 第四章

2.1.3 增加依赖 <!--添加依赖--><dependencies><!--Eureka Server--><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-netflix-eureka-server</artifactId></dependency>&l…

C++笔记之memset分析

C笔记之memset分析 code review! 文章目录 C\笔记之memset分析1.介绍2.误区总结3.代码一&#xff0c;char数组和uint8_t使用memset4.代码三&#xff0c;int数组使用memset 1.介绍 2.误区总结 参考文章&#xff1a;Cmemset踩坑 3.代码一&#xff0c;char数组和uint8_t使用mem…

网络安全进阶学习第七课——文件包含漏洞

文章目录 一、文件包含概念二、文件包含漏洞原理三、文件包含分类文件包含分为两种&#xff1a; 四、与文件包含相关的配置文件&#xff1a;&#xff08;php.ini文件&#xff09;五、与文件包含有关的函数1、include()2、include_once()3、require()4、require_once() 六、文件…

C# WPF项目创建(基于VS 2019介绍)

1.打开VS&#xff0c;选择《创建新项目》 2.选择《WPF应用》&#xff0c;这里设计两个有.NET Framework框架和.NET core 框架&#xff0c;如图所示&#xff1a; 区别&#xff1a; .NET Framework 框架只能在windows下使用 .NET core 框架支持linux 下运行 3. 项目名称根据需…

qsort的使用及模拟实现

qsort函数是C语言库中提供的一种快速排序&#xff0c;头文件是stdlib.h qsort的使用 qsort函数需要四个参数&#xff1a; 1.排序的起始位置的地址&#xff08;数组名&#xff09;: arr 2.排序元素的个数&#xff1a; sizeof&#xff08;arr)/sizeof(arr[0]) 3.排序元素…

Windows如何安装Django及如何创建项目

目录 1、Windows安装Django--pip命令行 2、创建项目 2.1、终端创建项目 2.2、在Pycharm中创建项目 2.3、二者创建的项目有何不同 2.4、项目目录说明 1、Windows安装Django--pip命令行 安装Django有两种方式&#xff1a; pip命令行【推荐--简单】手动安装【稍微复杂一丢丢…

Yunfly 一款高效、性能优异的node.js企业级web框架

介绍 Yunfly 一款高性能 Node.js WEB 框架, 使用 Typescript 构建我们的应用。 使用 Koa2 做为 HTTP 底层框架, 使用 routing-controllers 、 typedi 来高效构建我们的 Node 应用。 Yunfly 在 Koa 框架之上提升了一个抽象级别, 但仍然支持 Koa 中间件。在此基础之上, 提供了一…

063、故障处理之快速恢复数据

数据丢失快速恢复的重要性 目的&#xff1a;尽快修复数据&#xff0c;恢复业务 快速恢复相关技术对比 常用备份恢复技术 数据快速恢复原理 MVCC 是TiDB数据库原生的一项功能&#xff0c;默认使用无需配置&#xff0c;它使用多个历史快照的方式来维护数据在某个时间点对并…

uniapp 微信小程序 navigationBarBackgroundColor 标题栏颜色渐变

大体思路&#xff1a; 第一步&#xff1a;“navigationStyle”:“custom” 第二步&#xff1a; template内 重点&#xff1a;给view添加ref“top” 第三步&#xff1a;添加渐变色样式 1、pages.json {"path" : "pages/user/user","style" : …

java中线程池、Lambda表达式、file类、递归

线程池&#xff1a; 在多线程的使用过程中&#xff0c;会存在一个问题&#xff1a;如果并发的线程数量很多&#xff0c;并且每个线程都执行一个时间很短的任务就结束&#xff0c;这样频繁的创建线程就会大大降低系统的效率&#xff0c;因为线程的创建和销毁都需要时间。 线程…
最新文章