Opencv_CUDA实现推理图像前处理与后处理

news2025/3/17 14:12:53

Opencv_CUDA实现推理图像前处理与后处理

  • 通过trt 或者 openvino部署深度学习算法时,往往会通过opencv的Mat及算法将图像转换为固定的格式作为输入
  • openvino图像的前后处理后边将在单独的文章中写出
  • 今晚空闲搜了一些opencv_cuda的使用方法,在此总结一下
  • 前提是已经通过CMake将cuda和opencv重新编译好了C++库

1.前处理

  • 参考:【基于opencv-cuda的常见图像预处理】
 
// -------------- opencv ----------------------- # 
#include <opencv2/opencv.hpp>
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
// ---------------- opencv-cuda ---------------- #
#include <opencv2/cudawarping.hpp>
#include <opencv2/cudaarithm.hpp>
#include <opencv2/cudaimgproc.hpp>
 
// ------------ cuda ------------------------- #
#include <cuda_runtime_api.h>
// ------------------- nvinfer1 ------------------ # 
#include "NvInfer.h"
 
// ------------ standard libraries  --------------- # 
#include <iostream>
#include <assert.h>
#include <string>
#include <vector>
 
// ---------------------------------------------- #
 
void preprocessImage(const std::string& image_path, float* gpu_input,
                    nvinfer1::Dims3& dims)
{
    // read image
    cv::Mat frame = cv::imread(image_path);
    if(frame.empty())
    {
        std::cerr << "failed to load image: " << image_path << "!" << std::endl;
        return;
    }
    // upload
    cv::cuda::GpuMat gpu_frame;
    gpu_frame.upload(frame);
 
    // resize
    // CHW order
    auto input_width = dims.d[2];
    auto input_height = dims.d[1];
    auto channels = dims.d[0];
    
    auto input_size = cv::Size(input_width, input_height);
    cv::cuda::GpuMat resized;
    cv::cuda::resize(gpu_frame, resized, input_size, 0, 0, cv::INTER_LINEAR);
 
    //*  ------------------------ Pytorch ToTensor and Normalize ------------------- */
    cv::cuda::GpuMat flt_image;
    resized.convertTo(flt_image, CV_32FC3, 1.f/255.f);
 
    cv::cuda::subtract(flt_image, cv::Scalar(0.485f, 0.346f, 0.406f), flt_image,
                        cv::noArray(), -1);
    
    cv::cuda::divide(flt_image, cv::Scalar(0.229f, 0.224f, 0.225f), flt_image, 1, -1);
    //* ----------------------------------------------------------------------------------- /
    // BGR To RGB
    cv::cuda::GpuMat rgb;
    cv::cuda::cvtColor(flt_image, rgb, cv::COLOR_BGR2RGB);
 
    // toTensor(copy data to input float pointer channel by channel)
    std::vector<cv::cuda::GpuMat> rgb_out;
    for(size_t i=0; i<channels; ++i)
    {
        rgb_out.emplace_back(cv::cuda::GpuMat(cv::Size(input_width, input_height), CV_32FC1, gpu_input + i * input_width * input_height));
    }
 
    cv::cuda::split(flt_image, rgb_out); // opencv HWC order -> CHW order
}
 
// calculate size of tensor
size_t getSizeByDim(const nvinfer1::Dims& dims)
{
    size_t size = 1;
    for (size_t i = 0; i < dims.nbDims; ++i)
    {
        size *= dims.d[i];
    }
    return size;
}
 
int main()
{
    std::string image_path = "./turkish_coffee.jpg";
    // CHW order
    nvinfer1::Dims3 input_dim(3, 640, 640);
 
    auto input_size = getSizeByDim(input_dim) * sizeof(float);
    // allocate gpu memory for network inference
    // 此处的buffer可以认为是TensorRT engine推理时在GPU上分配的输入显存
    std::vector<void*> buffers(1);
    cudaMalloc(&buffers[0], input_size);
 
    // preprocess
    preprocessImage(image_path, (float*)buffers[0], input_dim);
 
    // download
    cv::cuda::GpuMat gpu_output;
    std::vector<cv::cuda::GpuMat> resized;
    for (size_t i = 0; i < 3; ++i)
    {
        resized.emplace_back(cv::cuda::GpuMat(cv::Size(input_dim.d[2], input_dim.d[1]), CV_32FC1, (float*)buffers[0] + i * input_dim.d[2] * input_dim.d[1]));
    }
    cv::cuda::merge(resized, gpu_output);
 
    cv::cuda::GpuMat image_out;
    // normalize
    gpu_output.convertTo(image_out, CV_32FC3, 1.f * 255.f);
    // download
    cv::Mat dst;
    image_out.download(dst);
 
    cv::imwrite("../01_test_demo.jpg", dst);
 
    for(void* buf:buffers)
    {
        cudaFree(buf);
    }
 
    return 0;
}
  • 原图与结果图:
    在这里插入图片描述

2. 输出后处理

  • 下边通过一个trt demo展示一下后处理操作
  • 源码实现如下:
#include <iostream>
#include <fstream>
#include <NvInfer.h>
#include <memory>
#include <NvOnnxParser.h>
#include <vector>
#include <cuda_runtime_api.h>
#include <opencv2/imgcodecs.hpp>
#include <opencv2/core/cuda.hpp>
#include <opencv2/cudawarping.hpp>
#include <opencv2/core.hpp>
#include <opencv2/cudaarithm.hpp>
#include <algorithm>
#include <numeric>

// destroy TensorRT objects if something goes wrong
struct TRTDestroy
{
    template <class T>
    void operator()(T* obj) const
    {
        if (obj)
        {
            obj->destroy();
        }
    }
};

template <class T>
using TRTUniquePtr = std::unique_ptr<T, TRTDestroy>;

// calculate size of tensor
size_t getSizeByDim(const nvinfer1::Dims& dims)
{
    size_t size = 1;
    for (size_t i = 0; i < dims.nbDims; ++i)
    {
        size *= dims.d[i];
    }
    return size;
}

// get classes names
std::vector<std::string> getClassNames(const std::string& imagenet_classes)
{
    std::ifstream classes_file(imagenet_classes);
    std::vector<std::string> classes;
    if (!classes_file.good())
    {
        std::cerr << "ERROR: can't read file with classes names.\n";
        return classes;
    }
    std::string class_name;
    while (std::getline(classes_file, class_name))
    {
        classes.push_back(class_name);
    }
    return classes;
}

// preprocessing stage ------------------------------------------------------------------------------------------------
void preprocessImage(const std::string& image_path, float* gpu_input, const nvinfer1::Dims& dims)
{
    // read input image
    cv::Mat frame = cv::imread(image_path);
    if (frame.empty())
    {
        std::cerr << "Input image " << image_path << " load failed\n";
        return;
    }
    cv::cuda::GpuMat gpu_frame;
    // upload image to GPU
    gpu_frame.upload(frame);

    auto input_width = dims.d[2];
    auto input_height = dims.d[1];
    auto channels = dims.d[0];
    auto input_size = cv::Size(input_width, input_height);
    // resize
    cv::cuda::GpuMat resized;
    cv::cuda::resize(gpu_frame, resized, input_size, 0, 0, cv::INTER_NEAREST);
    // normalize
    cv::cuda::GpuMat flt_image;
    resized.convertTo(flt_image, CV_32FC3, 1.f / 255.f);
    cv::cuda::subtract(flt_image, cv::Scalar(0.485f, 0.456f, 0.406f), flt_image, cv::noArray(), -1);
    cv::cuda::divide(flt_image, cv::Scalar(0.229f, 0.224f, 0.225f), flt_image, 1, -1);
    // to tensor
    std::vector<cv::cuda::GpuMat> chw;
    for (size_t i = 0; i < channels; ++i)
    {
        chw.emplace_back(cv::cuda::GpuMat(input_size, CV_32FC1, gpu_input + i * input_width * input_height));
    }
    cv::cuda::split(flt_image, chw);
}

// post-processing stage ----------------------------------------------------------------------------------------------
void postprocessResults(float *gpu_output, const nvinfer1::Dims &dims, int batch_size)
{
    // get class names
    auto classes = getClassNames("imagenet_classes.txt");

    // copy results from GPU to CPU
    std::vector<float> cpu_output(getSizeByDim(dims) * batch_size);
    cudaMemcpy(cpu_output.data(), gpu_output, cpu_output.size() * sizeof(float), cudaMemcpyDeviceToHost);

    // calculate softmax
    std::transform(cpu_output.begin(), cpu_output.end(), cpu_output.begin(), [](float val) {return std::exp(val);});
    auto sum = std::accumulate(cpu_output.begin(), cpu_output.end(), 0.0);
    // find top classes predicted by the model
    std::vector<int> indices(getSizeByDim(dims) * batch_size);
    std::iota(indices.begin(), indices.end(), 0); // generate sequence 0, 1, 2, 3, ..., 999
    std::sort(indices.begin(), indices.end(), [&cpu_output](int i1, int i2) {return cpu_output[i1] > cpu_output[i2];});
    // print results
    int i = 0;
    while (cpu_output[indices[i]] / sum > 0.005)
    {
        if (classes.size() > indices[i])
        {
            std::cout << "class: " << classes[indices[i]] << " | ";
        }
        std::cout << "confidence: " << 100 * cpu_output[indices[i]] / sum << "% | index: " << indices[i] << "\n";
        ++i;
    }
}

// main pipeline ------------------------------------------------------------------------------------------------------
int main(int argc, char* argv[])
{
    if (argc < 3)
    {
        std::cerr << "usage: " << argv[0] << " model.onnx image.jpg\n";
        return -1;
    }
    std::string model_path(argv[1]);
    std::string image_path(argv[2]);
    int batch_size = 1;

    // initialize TensorRT engine and parse ONNX model
    TRTUniquePtr<nvinfer1::ICudaEngine> engine{nullptr};
   
    //初始化engine.........省略


    // get sizes of input and output and allocate memory required for input data and for output data
    std::vector<nvinfer1::Dims> input_dims; // we expect only one input
    std::vector<nvinfer1::Dims> output_dims; // and one output
    std::vector<void*> buffers(engine->getNbBindings()); // buffers for input and output data
    for (size_t i = 0; i < engine->getNbBindings(); ++i)
    {
        auto binding_size = getSizeByDim(engine->getBindingDimensions(i)) * batch_size * sizeof(float);
        cudaMalloc(&buffers[i], binding_size);
        if (engine->bindingIsInput(i))
        {
            input_dims.emplace_back(engine->getBindingDimensions(i));
        }
        else
        {
            output_dims.emplace_back(engine->getBindingDimensions(i));
        }
    }
    if (input_dims.empty() || output_dims.empty())
    {
        std::cerr << "Expect at least one input and one output for network\n";
        return -1;
    }

    // preprocess input data
    preprocessImage(image_path, (float *) buffers[0], input_dims[0]);
    // inference
    context->enqueue(batch_size, buffers.data(), 0, nullptr);
    // postprocess results
    postprocessResults((float *) buffers[1], output_dims[0], batch_size);

    for (void* buf : buffers)
    {
        cudaFree(buf);
    }
    return 0;
}

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【模式识别】探秘分类奥秘:K-近邻算法解密与实战

​&#x1f308;个人主页&#xff1a;Sarapines Programmer&#x1f525; 系列专栏&#xff1a;《模式之谜 | 数据奇迹解码》⏰诗赋清音&#xff1a;云生高巅梦远游&#xff0c; 星光点缀碧海愁。 山川深邃情难晤&#xff0c; 剑气凌云志自修。 目录 &#x1f30c;1 初识模式识…

怎么制作电子邀请函_1分钟即可完成h5邀请函

电子邀请函&#xff1a;重塑传统&#xff0c;引领潮流的商务邀请新体验 在数字时代的浪潮中&#xff0c;我们的生活逐渐与数字世界深度融合。不论是购物、办公还是社交&#xff0c;都离不开电子化的操作。而在商务邀请函这一领域&#xff0c;电子邀请函以其独特的魅力&#xf…

Quartz持久化(springboot整合mybatis版本实现调度任务持久化)--提供源码下载

1、Quartz持久化功能概述 1、实现使用quartz提供的默认11张持久化表存储quartz相关信息。 2、实现定时任务的编辑、启动、关闭、删除。 3、实现自定义持久化表存储quartz定时任务信息。 4、本案例使用springboot整合mybatis框架和MySQL数据库实现持久化 5、提供源码下载 …

小白的实验室服务器深度学习环境配置指南

安装nvidia 本文在ubuntu server 22.04上实验成功&#xff0c;其他版本仅供参考 注意&#xff0c;本文仅适用于ubuntu server&#xff0c;不需要图形界面&#xff0c;没有对图形界面进行特殊考虑和验证&#xff01;依赖图形操作界面的读者慎用 查看是否安装了gcc gcc -v若没…

HTTP content-type内容类型的常见格式

本专栏是汇集了一些HTML常常被遗忘的知识&#xff0c;这里算是温故而知新&#xff0c;往往这些零碎的知识点&#xff0c;在你开发中能起到炸惊效果。我们每个人都没有过目不忘&#xff0c;过久不忘的本事&#xff0c;就让这一点点知识慢慢渗透你的脑海。 本专栏的风格是力求简洁…

因吹斯汀!只需上传照片,GPT-4V精准识别食物的卡路里和摄入热量

健身和减肥的朋友有福啦&#xff01; 最近一篇文章探索了GPT-4V在膳食评估领域的强大能力&#xff0c;可以根据饮食图片精准判断食物的种类与重量&#xff0c;并给出营养成分的分析&#xff0c;包括碳水化合物、蛋白质、脂肪占比。 最最重要的是&#xff0c;它还能告诉我们这…

Linux操作系统——进程(四)进程切换与命令行参数

进程切换 概念引入 下面我们先了解几个概念&#xff1a; 竞争性: 系统进程数目众多&#xff0c;而CPU资源只有少量&#xff0c;甚至1个&#xff0c;所以进程之间是具有竞争属性的。为了高效完成任务&#xff0c;更合理竞争相关资源&#xff0c;便具有了优先级 独立性: 多进程…

解决国内大模型痛点的最佳实践方案

1.前言 自AI热潮掀起以来&#xff0c;国内互联网大厂躬身入局&#xff0c;各类机构奋起追赶&#xff0c;创业型企业纷至沓来。业内戏称&#xff0c;一场大模型的“百模大战”已经扩展到“千模大战”。 根据近期中国科学技术信息研究所发布的《中国人工智能大模型地图研究报告…

关于linux mv指令机制

最近在mv文件的时候&#xff0c;操作失误将生产服务器一个1TB的文件夹mv到了/opt/test目录&#xff0c;因为最后/opt/目录被沾满所以1TB的文件夹没有迁移过来&#xff0c;写入了30GB数据到了/opt/test目录&#xff0c;因为系统分区被沾满&#xff0c;所以把test目录给删除了。 …

交换机端口镜像技术原理与配置

在网络维护的过程中会遇到需要对报文进行获取和分析的情况&#xff0c;比如怀疑有攻击报文&#xff0c;此时需要在不影响报文转发的情况下&#xff0c;对报文进行获取和分析。镜像技术可以在不影响报文正常处理流程的情况下&#xff0c;将镜像端口的报文复制一份到观察端口&…

OpenAI大模型DecryptPrompt

what is prompt 综述1.Pre-train, Prompt, and Predict: A Systematic Survey of Prompting Methods in Natural Language Processing(五星好评)综述2. Paradigm Shift in Natural Language Processing(四星推荐)综述3. Pre-Trained Models: Past, Present and Future Prompt即…

LeetCode刷题--- 优美的排列

个人主页&#xff1a;元清加油_【C】,【C语言】,【数据结构与算法】-CSDN博客元清加油_【C】,【C语言】,【数据结构与算法】-CSDN博客 个人专栏 力扣递归算法题 http://t.csdnimg.cn/yUl2I 【C】 http://t.csdnimg.cn/6AbpV 数据结构与算法 ​​​​​​http://t.cs…

SQL手工注入漏洞测试(MySQL数据库)

一、实验平台 https://www.mozhe.cn/bug/detail/elRHc1BCd2VIckQxbjduMG9BVCtkZz09bW96aGUmozhe 二、实验目标 获取到网站的KEY&#xff0c;并提交完成靶场。 三、实验步骤 ①、启动靶机&#xff0c;进行访问查找可能存在注入的页面 ②、通过测试判断注入点的位置(id) (1)…

Unity VR Pico apk安装失败:INSTALL_FAILED_UPDATE_INCOMPATIBLE

我的报错&#xff1a; PICO4企业版。安装apk&#xff0c;报错“安装失败。&#xff08;所属的Unity项目打包的apk&#xff0c;被我在同一台pico4安装了20次&#xff09; 调试方法&#xff1a; PIco4发布使用UNITY开发的Vr应用&#xff0c;格式为apk&#xff0c;安装的时候发生…

Java多线程技术六——线程的状态(备份)

1 概述 线程在不同的运行时期存在不同的状态&#xff0c;状态信息在存在于State枚举类中&#xff0c;如下图。 每个状态的解释如下图 调用于线程有关的方法是造成线程状态改变的主要原因&#xff0c;因果关系如下图 从上图可知&#xff0c;在调用与线程有关的方法后&#xff0…

Scikit-Learn线性回归(一)

Scikit-Learn线性回归一 1、线性回归概述1.1、回归1.2、线性1.3、线性回归1.4、线性回归的优缺点1.5、线性回归与逻辑回归2、线性回归的原理2.1、线性回归的定义与原理2.2、线性回归的损失函数3、Scikit-Learn线性回归3.1、Scikit-Learn线性回归API3.2、Scikit-Learn线性回归初…

mac终端自定义登录欢迎语

shigen坚持更新文章的博客写手&#xff0c;擅长Java、python、vue、shell等编程语言和各种应用程序、脚本的开发。记录成长&#xff0c;分享认知&#xff0c;留住感动。 shigen看着单调的终端&#xff0c;突然有了一丝丝的念头&#xff0c;我要搞的炫酷一点。让我想到的一个场景…

VMvare虚拟机之文件夹共享与防火墙设置

共享文件夹 什么是共享文件夹 共享文件夹是一种在网络上共享文件和文件夹的方法。它允许多个用户通过网络连接到共享文件夹&#xff0c;并可以访问其中的文件和文件夹&#xff0c;进行文件的读取、修改、删除等操作。共享文件夹可以用于方便地共享文件和协作工作&#xff0c;…

STL:std::array 和 基本数组类型array 浅谈一二三

一、优缺点比较 在C中&#xff0c;std::array是标准库提供的数组容器&#xff0c;相比于基础数据类型的数组&#xff0c;它具有以下优点和缺点&#xff1a; 优点&#xff1a; 安全性&#xff1a;std::array提供了边界检查&#xff0c;可以避免数组越界访问的问题。 可以作为…

在Centos7中利用Shell脚本:实现MySQL的数据备份

目录 自动化备份MySQL 一.备份数据库脚本 1.创建备份目录 2.创建脚本文件 3.新建配置文件&#xff08;连接数据库的配置文件&#xff09; 4.给文件权限(mysql_backup.sh) ​编辑 5.执行命令 (mysql_backup.sh) ​编辑 二.数据库通过备份恢复 1.创建脚…