当前位置: 首页 > news >正文

yolov8(yolov11)目标识别使用Tensorrt推理-2.0(C++Api)

        这段代码是在之前的代码上做了一些修改整理。将所有的模型参数从engine模型中获取,减少了参数的设置,同时将函数写入类内,方便程序进行多对象、多线程的使用。接下来直接展示代码,至于一些基础设置可以去前一篇查看yolov8目标识别使用Tensorrt推理(C++Api)。

        yolov11是在yolov8上继续升级的,模型的输入输出没有改变,所以这段代码同样适用于yolov11。

1、推理程序: 

        推理主要使用Tensorrt的C++的Api。程序里主要有Logging.h和Utils.h两个基础的头文件,Utils.h头文件做了一些修改。主要是含推理程序有一些修改,下面是具体程序:

Logging.h

#pragma once
/** Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved.** Licensed under the Apache License, Version 2.0 (the "License");* you may not use this file except in compliance with the License.* You may obtain a copy of the License at**     http://www.apache.org/licenses/LICENSE-2.0** Unless required by applicable law or agreed to in writing, software* distributed under the License is distributed on an "AS IS" BASIS,* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.* See the License for the specific language governing permissions and* limitations under the License.*/#ifndef TENSORRT_LOGGING_H
#define TENSORRT_LOGGING_H#include "NvInferRuntimeCommon.h"
#include <cassert>
#include <ctime>
#include <iomanip>
#include <iostream>
#include <ostream>
#include <sstream>
#include <string>using Severity = nvinfer1::ILogger::Severity;class LogStreamConsumerBuffer : public std::stringbuf
{
public:LogStreamConsumerBuffer(std::ostream& stream, const std::string& prefix, bool shouldLog): mOutput(stream), mPrefix(prefix), mShouldLog(shouldLog){}LogStreamConsumerBuffer(LogStreamConsumerBuffer&& other): mOutput(other.mOutput){}~LogStreamConsumerBuffer(){// std::streambuf::pbase() gives a pointer to the beginning of the buffered part of the output sequence// std::streambuf::pptr() gives a pointer to the current position of the output sequence// if the pointer to the beginning is not equal to the pointer to the current position,// call putOutput() to log the output to the streamif (pbase() != pptr()){putOutput();}}// synchronizes the stream buffer and returns 0 on success// synchronizing the stream buffer consists of inserting the buffer contents into the stream,// resetting the buffer and flushing the streamvirtual int sync(){putOutput();return 0;}void putOutput(){if (mShouldLog){// prepend timestampstd::time_t timestamp = std::time(nullptr);tm* tm_local = new tm();localtime_s(tm_local, &timestamp);std::cout << "[";std::cout << std::setw(2) << std::setfill('0') << 1 + tm_local->tm_mon << "/";std::cout << std::setw(2) << std::setfill('0') << tm_local->tm_mday << "/";std::cout << std::setw(4) << std::setfill('0') << 1900 + tm_local->tm_year << "-";std::cout << std::setw(2) << std::setfill('0') << tm_local->tm_hour << ":";std::cout << std::setw(2) << std::setfill('0') << tm_local->tm_min << ":";std::cout << std::setw(2) << std::setfill('0') << tm_local->tm_sec << "] ";// std::stringbuf::str() gets the string contents of the buffer// insert the buffer contents pre-appended by the appropriate prefix into the streammOutput << mPrefix << str();// set the buffer to emptystr("");// flush the streammOutput.flush();}}void setShouldLog(bool shouldLog){mShouldLog = shouldLog;}private:std::ostream& mOutput;std::string mPrefix;bool mShouldLog;
};//!
//! \class LogStreamConsumerBase
//! \brief Convenience object used to initialize LogStreamConsumerBuffer before std::ostream in LogStreamConsumer
//!
class LogStreamConsumerBase
{
public:LogStreamConsumerBase(std::ostream& stream, const std::string& prefix, bool shouldLog): mBuffer(stream, prefix, shouldLog){}protected:LogStreamConsumerBuffer mBuffer;
};//!
//! \class LogStreamConsumer
//! \brief Convenience object used to facilitate use of C++ stream syntax when logging messages.
//!  Order of base classes is LogStreamConsumerBase and then std::ostream.
//!  This is because the LogStreamConsumerBase class is used to initialize the LogStreamConsumerBuffer member field
//!  in LogStreamConsumer and then the address of the buffer is passed to std::ostream.
//!  This is necessary to prevent the address of an uninitialized buffer from being passed to std::ostream.
//!  Please do not change the order of the parent classes.
//!
class LogStreamConsumer : protected LogStreamConsumerBase, public std::ostream
{
public://! \brief Creates a LogStreamConsumer which logs messages with level severity.//!  Reportable severity determines if the messages are severe enough to be logged.LogStreamConsumer(Severity reportableSeverity, Severity severity): LogStreamConsumerBase(severityOstream(severity), severityPrefix(severity), severity <= reportableSeverity), std::ostream(&mBuffer) // links the stream buffer with the stream, mShouldLog(severity <= reportableSeverity), mSeverity(severity){}LogStreamConsumer(LogStreamConsumer&& other): LogStreamConsumerBase(severityOstream(other.mSeverity), severityPrefix(other.mSeverity), other.mShouldLog), std::ostream(&mBuffer) // links the stream buffer with the stream, mShouldLog(other.mShouldLog), mSeverity(other.mSeverity){}void setReportableSeverity(Severity reportableSeverity){mShouldLog = mSeverity <= reportableSeverity;mBuffer.setShouldLog(mShouldLog);}private:static std::ostream& severityOstream(Severity severity){return severity >= Severity::kINFO ? std::cout : std::cerr;}static std::string severityPrefix(Severity severity){switch (severity){case Severity::kINTERNAL_ERROR: return "[F] ";case Severity::kERROR: return "[E] ";case Severity::kWARNING: return "[W] ";case Severity::kINFO: return "[I] ";case Severity::kVERBOSE: return "[V] ";default: assert(0); return "";}}bool mShouldLog;Severity mSeverity;
};//! \class Logger
//!
//! \brief Class which manages logging of TensorRT tools and samples
//!
//! \details This class provides a common interface for TensorRT tools and samples to log information to the console,
//! and supports logging two types of messages:
//!
//! - Debugging messages with an associated severity (info, warning, error, or internal error/fatal)
//! - Test pass/fail messages
//!
//! The advantage of having all samples use this class for logging as opposed to emitting directly to stdout/stderr is
//! that the logic for controlling the verbosity and formatting of sample output is centralized in one location.
//!
//! In the future, this class could be extended to support dumping test results to a file in some standard format
//! (for example, JUnit XML), and providing additional metadata (e.g. timing the duration of a test run).
//!
//! TODO: For backwards compatibility with existing samples, this class inherits directly from the nvinfer1::ILogger
//! interface, which is problematic since there isn't a clean separation between messages coming from the TensorRT
//! library and messages coming from the sample.
//!
//! In the future (once all samples are updated to use Logger::getTRTLogger() to access the ILogger) we can refactor the
//! class to eliminate the inheritance and instead make the nvinfer1::ILogger implementation a member of the Logger
//! object.class Logger : public nvinfer1::ILogger
{
public:Logger(Severity severity = Severity::kWARNING): mReportableSeverity(severity){}//!//! \enum TestResult//! \brief Represents the state of a given test//!enum class TestResult{kRUNNING, //!< The test is runningkPASSED,  //!< The test passedkFAILED,  //!< The test failedkWAIVED   //!< The test was waived};//!//! \brief Forward-compatible method for retrieving the nvinfer::ILogger associated with this Logger//! \return The nvinfer1::ILogger associated with this Logger//!//! TODO Once all samples are updated to use this method to register the logger with TensorRT,//! we can eliminate the inheritance of Logger from ILogger//!nvinfer1::ILogger& getTRTLogger(){return *this;}//!//! \brief Implementation of the nvinfer1::ILogger::log() virtual method//!//! Note samples should not be calling this function directly; it will eventually go away once we eliminate the//! inheritance from nvinfer1::ILogger//!void log(Severity severity, const char* msg) noexcept override{LogStreamConsumer(mReportableSeverity, severity) << "[TRT] " << std::string(msg) << std::endl;}//!//! \brief Method for controlling the verbosity of logging output//!//! \param severity The logger will only emit messages that have severity of this level or higher.//!void setReportableSeverity(Severity severity){mReportableSeverity = severity;}//!//! \brief Opaque handle that holds logging information for a particular test//!//! This object is an opaque handle to information used by the Logger to print test results.//! The sample must call Logger::defineTest() in order to obtain a TestAtom that can be used//! with Logger::reportTest{Start,End}().//!class TestAtom{public:TestAtom(TestAtom&&) = default;private:friend class Logger;TestAtom(bool started, const std::string& name, const std::string& cmdline): mStarted(started), mName(name), mCmdline(cmdline){}bool mStarted;std::string mName;std::string mCmdline;};//!//! \brief Define a test for logging//!//! \param[in] name The name of the test.  This should be a string starting with//!                  "TensorRT" and containing dot-separated strings containing//!                  the characters [A-Za-z0-9_].//!                  For example, "TensorRT.sample_googlenet"//! \param[in] cmdline The command line used to reproduce the test////! \return a TestAtom that can be used in Logger::reportTest{Start,End}().//!static TestAtom defineTest(const std::string& name, const std::string& cmdline){return TestAtom(false, name, cmdline);}//!//! \brief A convenience overloaded version of defineTest() that accepts an array of command-line arguments//!        as input//!//! \param[in] name The name of the test//! \param[in] argc The number of command-line arguments//! \param[in] argv The array of command-line arguments (given as C strings)//!//! \return a TestAtom that can be used in Logger::reportTest{Start,End}().static TestAtom defineTest(const std::string& name, int argc, char const* const* argv){auto cmdline = genCmdlineString(argc, argv);return defineTest(name, cmdline);}//!//! \brief Report that a test has started.//!//! \pre reportTestStart() has not been called yet for the given testAtom//!//! \param[in] testAtom The handle to the test that has started//!static void reportTestStart(TestAtom& testAtom){reportTestResult(testAtom, TestResult::kRUNNING);assert(!testAtom.mStarted);testAtom.mStarted = true;}//!//! \brief Report that a test has ended.//!//! \pre reportTestStart() has been called for the given testAtom//!//! \param[in] testAtom The handle to the test that has ended//! \param[in] result The result of the test. Should be one of TestResult::kPASSED,//!                   TestResult::kFAILED, TestResult::kWAIVED//!static void reportTestEnd(const TestAtom& testAtom, TestResult result){assert(result != TestResult::kRUNNING);assert(testAtom.mStarted);reportTestResult(testAtom, result);}static int reportPass(const TestAtom& testAtom){reportTestEnd(testAtom, TestResult::kPASSED);return EXIT_SUCCESS;}static int reportFail(const TestAtom& testAtom){reportTestEnd(testAtom, TestResult::kFAILED);return EXIT_FAILURE;}static int reportWaive(const TestAtom& testAtom){reportTestEnd(testAtom, TestResult::kWAIVED);return EXIT_SUCCESS;}static int reportTest(const TestAtom& testAtom, bool pass){return pass ? reportPass(testAtom) : reportFail(testAtom);}Severity getReportableSeverity() const{return mReportableSeverity;}private://!//! \brief returns an appropriate string for prefixing a log message with the given severity//!static const char* severityPrefix(Severity severity){switch (severity){case Severity::kINTERNAL_ERROR: return "[F] ";case Severity::kERROR: return "[E] ";case Severity::kWARNING: return "[W] ";case Severity::kINFO: return "[I] ";case Severity::kVERBOSE: return "[V] ";default: assert(0); return "";}}//!//! \brief returns an appropriate string for prefixing a test result message with the given result//!static const char* testResultString(TestResult result){switch (result){case TestResult::kRUNNING: return "RUNNING";case TestResult::kPASSED: return "PASSED";case TestResult::kFAILED: return "FAILED";case TestResult::kWAIVED: return "WAIVED";default: assert(0); return "";}}//!//! \brief returns an appropriate output stream (cout or cerr) to use with the given severity//!static std::ostream& severityOstream(Severity severity){return severity >= Severity::kINFO ? std::cout : std::cerr;}//!//! \brief method that implements logging test results//!static void reportTestResult(const TestAtom& testAtom, TestResult result){severityOstream(Severity::kINFO) << "&&&& " << testResultString(result) << " " << testAtom.mName << " # "<< testAtom.mCmdline << std::endl;}//!//! \brief generate a command line string from the given (argc, argv) values//!static std::string genCmdlineString(int argc, char const* const* argv){std::stringstream ss;for (int i = 0; i < argc; i++){if (i > 0)ss << " ";ss << argv[i];}return ss.str();}Severity mReportableSeverity;
};namespace
{//!//! \brief produces a LogStreamConsumer object that can be used to log messages of severity kVERBOSE//!//! Example usage://!//!     LOG_VERBOSE(logger) << "hello world" << std::endl;//!inline LogStreamConsumer LOG_VERBOSE(const Logger& logger){return LogStreamConsumer(logger.getReportableSeverity(), Severity::kVERBOSE);}//!//! \brief produces a LogStreamConsumer object that can be used to log messages of severity kINFO//!//! Example usage://!//!     LOG_INFO(logger) << "hello world" << std::endl;//!inline LogStreamConsumer LOG_INFO(const Logger& logger){return LogStreamConsumer(logger.getReportableSeverity(), Severity::kINFO);}//!//! \brief produces a LogStreamConsumer object that can be used to log messages of severity kWARNING//!//! Example usage://!//!     LOG_WARN(logger) << "hello world" << std::endl;//!inline LogStreamConsumer LOG_WARN(const Logger& logger){return LogStreamConsumer(logger.getReportableSeverity(), Severity::kWARNING);}//!//! \brief produces a LogStreamConsumer object that can be used to log messages of severity kERROR//!//! Example usage://!//!     LOG_ERROR(logger) << "hello world" << std::endl;//!inline LogStreamConsumer LOG_ERROR(const Logger& logger){return LogStreamConsumer(logger.getReportableSeverity(), Severity::kERROR);}//!//! \brief produces a LogStreamConsumer object that can be used to log messages of severity kINTERNAL_ERROR//         ("fatal" severity)//!//! Example usage://!//!     LOG_FATAL(logger) << "hello world" << std::endl;//!inline LogStreamConsumer LOG_FATAL(const Logger& logger){return LogStreamConsumer(logger.getReportableSeverity(), Severity::kINTERNAL_ERROR);}} // anonymous namespace#endif // TENSORRT_LOGGING_H

Utils.h

#pragma once
#include <algorithm> 
#include <fstream>
#include <iostream>
#include <opencv2/opencv.hpp>
#include <vector>
#include <chrono>
#include <cmath>
#include <numeric> // std::iota using  namespace cv;#define CHECK(status) \do\{\auto ret = (status);\if (ret != 0)\{\std::cerr << "Cuda failure: " << ret << std::endl;\abort();\}\} while (0)v

DetectInferSystem.h

#pragma once
#include "NvInfer.h"
#include "cuda_runtime_api.h"
#include "NvInferPlugin.h"
#include "logging.h"
#include <opencv2/opencv.hpp>
#include "utils.h"
#include <string>using namespace nvinfer1;
using namespace std;
using namespace cv;static Logger gLogger;struct Output {int id;             //结果类别idfloat confidence;   //结果置信度cv::Rect box;       //矩形框	
};struct OutputVector
{std::vector<int> classIds;    //结果id数组std::vector<float> confidences;    //结果每个id对应置信度数组std::vector<cv::Rect> boxes;    //每个id矩形框
};const float color_list[80][3] =
{{0.000, 0.447, 0.741},{0.850, 0.325, 0.098},{0.929, 0.694, 0.125},{0.494, 0.184, 0.556},{0.466, 0.674, 0.188},{0.301, 0.745, 0.933},{0.635, 0.078, 0.184},{0.300, 0.300, 0.300},{0.600, 0.600, 0.600},{1.000, 0.000, 0.000},{1.000, 0.500, 0.000},{0.749, 0.749, 0.000},{0.000, 1.000, 0.000},{0.000, 0.000, 1.000},{0.667, 0.000, 1.000},{0.333, 0.333, 0.000},{0.333, 0.667, 0.000},{0.333, 1.000, 0.000},{0.667, 0.333, 0.000},{0.667, 0.667, 0.000},{0.667, 1.000, 0.000},{1.000, 0.333, 0.000},{1.000, 0.667, 0.000},{1.000, 1.000, 0.000},{0.000, 0.333, 0.500},{0.000, 0.667, 0.500},{0.000, 1.000, 0.500},{0.333, 0.000, 0.500},{0.333, 0.333, 0.500},{0.333, 0.667, 0.500},{0.333, 1.000, 0.500},{0.667, 0.000, 0.500},{0.667, 0.333, 0.500},{0.667, 0.667, 0.500},{0.667, 1.000, 0.500},{1.000, 0.000, 0.500},{1.000, 0.333, 0.500},{1.000, 0.667, 0.500},{1.000, 1.000, 0.500},{0.000, 0.333, 1.000},{0.000, 0.667, 1.000},{0.000, 1.000, 1.000},{0.333, 0.000, 1.000},{0.333, 0.333, 1.000},{0.333, 0.667, 1.000},{0.333, 1.000, 1.000},{0.667, 0.000, 1.000},{0.667, 0.333, 1.000},{0.667, 0.667, 1.000},{0.667, 1.000, 1.000},{1.000, 0.000, 1.000},{1.000, 0.333, 1.000},{1.000, 0.667, 1.000},{0.333, 0.000, 0.000},{0.500, 0.000, 0.000},{0.667, 0.000, 0.000},{0.833, 0.000, 0.000},{1.000, 0.000, 0.000},{0.000, 0.167, 0.000},{0.000, 0.333, 0.000},{0.000, 0.500, 0.000},{0.000, 0.667, 0.000},{0.000, 0.833, 0.000},{0.000, 1.000, 0.000},{0.000, 0.000, 0.167},{0.000, 0.000, 0.333},{0.000, 0.000, 0.500},{0.000, 0.000, 0.667},{0.000, 0.000, 0.833},{0.000, 0.000, 1.000},{0.000, 0.000, 0.000},{0.143, 0.143, 0.143},{0.286, 0.286, 0.286},{0.429, 0.429, 0.429},{0.571, 0.571, 0.571},{0.714, 0.714, 0.714},{0.857, 0.857, 0.857},{0.000, 0.447, 0.741},{0.314, 0.717, 0.741},{0.50, 0.5, 0}
};class YoloDetectEngineInfer
{
private:ICudaEngine* engine;IRuntime* runtime;IExecutionContext* context;private://模型输入输出名称const char* inputName;const char* outputName;//输入图片的宽高  [1, 3, 640, 640]int batchSize;int channels;int inputWidth;int inputHeight;//模型输出参数[1, 84, 8400]int classes;         //类别数 84int anchors;         //锚点框数 8400//定义静态浮点,用于保存输出头的输出结果float* outputArray;//置信度阈值float CONF_THRESHOLD = 0.5;//nms阈值float NMS_THRESHOLD = 0.5;public://初始化引擎void InitEngine(char* enginePath);//初始化获取模型参数void InitGetConfig();//设置分数阈值void SetConfThreshold(float confThreshold, float nmsThreshold);//销毁引擎void Destroy();//图像预处理cv::Mat PreprocessImg(cv::Mat image, std::vector<int>& paddedSize);//图像进行归一化处理void blobFromImage(cv::Mat image, float* imageArray);//Tensorrt推理void EngineInference(IExecutionContext& context, float* imageArray, int batchSize);//数据后处理vector<Output> Postprocess(cv::Mat image, std::vector<int> paddedSize);//对检测结果应用非极大值抑制vector<Output> ApplyNms(OutputVector outputVector);//绘制结果void DrawOutputResult(cv::Mat image, vector<Output> result);//推理图片void DetectImage(cv::Mat image);
};

DetectInferSystem.cpp

#include "DetectInferSystem.h"//初始化引擎
void YoloDetectEngineInfer::InitEngine(char* enginePath)
{//无符号整型类型,通常用于表示对象的大小或计数//{ 0 }: 这是初始化列表,用于初始化 size 变量。在这种情况下,size 被初始化为 0。size_t size{ 0 };//定义一个指针变量,通过trtModelStream = new char[size];分配size个字符的空间//nullptr表示指针针在开始时不指向任何有效的内存地址,空指针char* trtModelStream{ nullptr };//打开文件,即engine模型std::ifstream file(enginePath, std::ios::binary);if (file.good()){//指向文件的最后地址file.seekg(0, file.end);//计算文件的长度size = file.tellg();//指回文件的起始地址file.seekg(0, file.beg);//为trtModelStream指针分配内存,内存大小为sizetrtModelStream = new char[size]; //开辟一个char 长度是文件的长度assert(trtModelStream);//把file内容传递给trtModelStream,传递大小为size,即engine模型内容传递file.read(trtModelStream, size);//关闭文件file.close();}std::cout << "engine init finished" << std::endl;//创建了一个Inference运行时环境,返回一个指向新创建的运行时环境的指针runtime = createInferRuntime(gLogger);assert(runtime != nullptr);//反序列化一个CUDA引擎。这个引擎将用于执行模型的前向传播engine = runtime->deserializeCudaEngine(trtModelStream, size);assert(engine != nullptr);//使用上一步中创建的引擎创建一个执行上下文。这个上下文将在模型的前向传播期间使用context = engine->createExecutionContext();assert(context != nullptr);//释放了用于存储模型序列化的内存delete[] trtModelStream;}//初始化获取模型参数
void YoloDetectEngineInfer::InitGetConfig()
{//获取输入张量的数量Dims inputDims, outputDims;// 1. 获取输入信息inputName = engine->getBindingName(0);inputDims = engine->getBindingDimensions(0);         //输入形状[1, 3, 640, 640]batchSize = inputDims.d[0];channels = inputDims.d[1];inputWidth = inputDims.d[2];inputHeight = inputDims.d[3];// 2. 获取输出信息0outputName = engine->getBindingName(1);outputDims = engine->getBindingDimensions(1);        //输出形状[1, 84, 8400]  84 = 80 + 4(矩形参数)classes = outputDims.d[1] - 4;anchors = outputDims.d[2];// 3. 申请输出内存outputArray = new float[1 * (classes + 4) * anchors];    //输出结果数组1 * 84 * 8400
}//设置分数阈值
void YoloDetectEngineInfer::SetConfThreshold(float confThreshold, float nmsThreshold)
{this->CONF_THRESHOLD = confThreshold;this->NMS_THRESHOLD = nmsThreshold;
}//销毁引擎
void YoloDetectEngineInfer::Destroy()
{// 1. 销毁执行上下文context->destroy();// 2. 销毁引擎engine->destroy();// 3. 销毁运行时runtime->destroy();delete[] outputArray;
}//图像预处理
cv::Mat YoloDetectEngineInfer::PreprocessImg(cv::Mat image, std::vector<int>& paddedSize)
{int newWidth, newHeight, leftTopX, leftTopY;float rateW = inputWidth / (image.cols * 1.0);float rateH = inputHeight / (image.rows * 1.0);//判断图片的宽高比if (rateH > rateW) {//宽大于高newWidth = inputWidth;newHeight = rateW * image.rows;leftTopX = 0;leftTopY = (inputHeight - newHeight) / 2;}else {newWidth = rateH * image.cols;newHeight = inputHeight;leftTopX = (inputWidth - newWidth) / 2;leftTopY = 0;}//压缩图片cv::Mat resizedImage(newHeight, newWidth, CV_8UC3);cv::resize(image, resizedImage, resizedImage.size(), 0, 0, cv::INTER_LINEAR);//填充图片至640*640,先生成一张灰色底图,再填充cv::Mat paddedImage(inputHeight, inputWidth, CV_8UC3, cv::Scalar(128, 128, 128));resizedImage.copyTo(paddedImage(cv::Rect(leftTopX, leftTopY, resizedImage.cols, resizedImage.rows)));//压缩图片paddedSize.push_back(newHeight);paddedSize.push_back(newWidth);paddedSize.push_back(leftTopY);paddedSize.push_back(leftTopX);return paddedImage;
}//图像进行归一化处理
void YoloDetectEngineInfer::blobFromImage(cv::Mat image, float* imageArray)
{// [1,3,INPUT_H,INPUT_W]	int i = 0;for (int row = 0; row < inputHeight; ++row){//逐行对象素值和图像通道进行处理, pr_img.step=widthx3 就是每一行有width个3通道的值//第row行uchar* uc_pixel = image.data + row * image.step;for (int col = 0; col < inputWidth; ++col){//第col列imageArray[i] = (float)uc_pixel[2] / 255.0;         //R//通道变换imageArray[i + inputHeight * inputWidth] = (float)uc_pixel[1] / 255.0;          //GimageArray[i + 2 * inputHeight * inputWidth] = (float)uc_pixel[0] / 255.0;              //Buc_pixel += 3;//表示进行下一列++i;//表示在3个通道中的第i个位置,rgb三个通道的值是分开的,如r123456g123456b123456}}
}//Tensorrt推理
void YoloDetectEngineInfer::EngineInference(IExecutionContext& context, float* imageArray, int batchSize)
{//从上下文中获取一个CUDA引擎。这个引擎加载了一个深度学习模型const ICudaEngine& engine = context.getEngine();//判断该引擎是否有二个绑定,intput, output0assert(engine.getNbBindings() == 2);//定义了一个指向void的指针数组,用于存储GPU缓冲区的地址void* buffers[2];//获取输入和输出blob的索引,这些索引用于之后的缓冲区操作const int inputIndex = engine.getBindingIndex(inputName);const int outputIndex = engine.getBindingIndex(outputName);// 使用cudaMalloc分配了GPU内存。这些内存将用于存储模型的输入和输出CHECK(cudaMalloc(&buffers[inputIndex], batchSize * channels * inputHeight * inputWidth * sizeof(float)));CHECK(cudaMalloc(&buffers[outputIndex], batchSize * (classes + 4) * anchors * sizeof(float)));// cudaMalloc分配内存 cudaFree释放内存 cudaMemcpy或 cudaMemcpyAsync 在主机和设备之间传输数据// cudaMemcpy cudaMemcpyAsync 显式地阻塞传输 显式地非阻塞传输 //创建一个CUDA流。CUDA流是一种特殊的并发执行环境,可以在其中安排任务以并发执行。流使得任务可以并行执行,从而提高了GPU的利用率。cudaStream_t stream;//判断是否创建成功CHECK(cudaStreamCreate(&stream));// 使用cudaMemcpyAsync将输入数据异步地复制到GPU缓冲区。这个操作是非阻塞的,意味着它不会立即完成。CHECK(cudaMemcpyAsync(buffers[inputIndex], imageArray, batchSize * channels * inputHeight * inputWidth * sizeof(float), cudaMemcpyHostToDevice, stream));//将输入和输出缓冲区以及流添加到上下文的执行队列中。这将触发模型的推理。context.enqueueV2(buffers, stream, nullptr);//使用cudaMemcpyAsync函数将GPU上的数据复制到主内存中。这是异步的,意味着该函数立即返回,而数据传输可以在后台进行。CHECK(cudaMemcpyAsync(outputArray, buffers[outputIndex], batchSize * (classes + 4) * anchors * sizeof(float), cudaMemcpyDeviceToHost, stream));//等待所有在给定流上的操作都完成。这可以确保在释放流和缓冲区之前,所有的数据都已经被复制完毕。//这对于保证内存操作的正确性和防止数据竞争非常重要。cudaStreamSynchronize(stream);//释放内存cudaStreamDestroy(stream);CHECK(cudaFree(buffers[inputIndex]));CHECK(cudaFree(buffers[outputIndex]));}//数据后处理
vector<Output> YoloDetectEngineInfer::Postprocess(cv::Mat image, std::vector<int> paddedSize)
{//储存结果OutputVector outputVector;int newH = paddedSize[0], newW = paddedSize[1], leftY = paddedSize[2], leftX = paddedSize[3];float rateH = (float)image.rows / newH;float rateW = (float)image.cols / newW;// 处理boxint numClasses = classes + 4;//将输出的数据转为84 * 8400的矩阵cv::Mat outputMat = cv::Mat(numClasses, anchors, CV_32F, outputArray);//遍历8400个锚点数据for (int i = 0; i < anchors; i++){//将每一行80个类别的分数单出取出,放在height为80(80个是多少个类别),width为1的mat中(一共8400个锚点)cv::Mat scores = outputMat(Rect(i, 4, 1, classes)).clone();//找出80个类别里最大分数Point classIdPoint;  //定位1*80mat中的最大分数位置,y对应80个类double maxClassSocre;minMaxLoc(scores, 0, &maxClassSocre, 0, &classIdPoint);maxClassSocre = (float)maxClassSocre;//过滤置信度低的检测结果if (maxClassSocre >= CONF_THRESHOLD){// 提取边界框参数 (位置0-3)float cx = (outputMat.at<float>(0, i) - leftX) * rateW;  //cxfloat cy = (outputMat.at<float>(1, i) - leftY) * rateH;  //cyfloat w = outputMat.at<float>(2, i) * rateW;  //wfloat h = outputMat.at<float>(3, i) * rateH;  //hint left = MAX((cx - 0.5 * w), 0);int top = MAX((cy - 0.5 * h), 0);int width = (int)w;int height = (int)h;if (width <= 0 || height <= 0){continue;}//收集结果outputVector.classIds.push_back(classIdPoint.y);outputVector.confidences.push_back(maxClassSocre);outputVector.boxes.push_back(Rect(left, top, width, height));}}//进行nms抑制return ApplyNms(outputVector);
}//对检测结果应用非极大值抑制
vector<Output> YoloDetectEngineInfer::ApplyNms(OutputVector outputVector)
{vector<Output> outputResult;//执行非最大抑制以消除具有较低置信度的冗余重叠框(NMS)std::vector<int> nmsResult;//通过opencv自带的nms函数进行,矩阵box、置信度大小,置信度阈值,nms阈值,结果cv::dnn::NMSBoxes(outputVector.boxes, outputVector.confidences, CONF_THRESHOLD, NMS_THRESHOLD, nmsResult);//提取经过非极大值抑制后的结果for (int i = 0; i < nmsResult.size(); ++i){Output result;result.id = outputVector.classIds[nmsResult[i]];result.confidence = outputVector.confidences[nmsResult[i]];result.box = outputVector.boxes[nmsResult[i]];outputResult.push_back(result);}return outputResult;
}//绘制结果
void YoloDetectEngineInfer::DrawOutputResult(cv::Mat image, vector<Output> result)
{//生成随机颜色std::vector<Scalar> color;//根据类别数,生成不同的颜色for (int i = 0; i < classes; i++) {int b = color_list[i][0] * 255;int g = color_list[i][1] * 255;int r = color_list[i][2] * 255;color.push_back(Vec3b(b, g, r));}//往图片上绘画方框for (int i = 0; i < result.size(); i++){int left, top;left = result[i].box.x;top = result[i].box.y;//画矩形框,颜色是上面选的rectangle(image, result[i].box, color[result[i].id], 2, 8);std::string label = std::to_string(result[i].id) + ":" + std::to_string(result[i].confidence);int baseLine;//获取标签文本的尺寸Size labelSize = getTextSize(label, FONT_HERSHEY_SIMPLEX, 0.5, 1, &baseLine);//确定一个最大的高top = max(top, labelSize.height);//把文本信息加到图像上putText(image, label, Point(left, top), FONT_HERSHEY_SIMPLEX, 1, color[result[i].id], 2);}}//推理图片
void YoloDetectEngineInfer::DetectImage(cv::Mat image)
{vector<int> paddedSize;auto start = std::chrono::system_clock::now();//前处理Mat src = PreprocessImg(image, paddedSize);//图像进行归一化处理float* imageArray = new float[inputHeight * inputWidth * 3];blobFromImage(src, imageArray);EngineInference(*context, imageArray, batchSize);//后处理vector<Output> result = Postprocess(image, paddedSize);DrawOutputResult(image, result);auto end = std::chrono::system_clock::now();std::cout << std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count() << "ms" << std::endl;//imshow("mask", image);imwrite("detect.jpg", image);delete[] imageArray;return;
}

2、测试使用

        测试使用的话直接new一个YoloDetectEngineInfer,使用里面函数就可以。下面给出例子:

 YoloDetectEngineInfer infer;string enginePath = "E:\\yolov8_Tensorrt_Infer\\engine_infer\\best.engine";infer.InitEngine(const_cast<char*>(enginePath.c_str()));infer.InitGetConfig();Mat img = cv::imread("E:\\yolov8_Tensorrt_Infer\\engine_infer\\bus.jpg");infer.DetectImage(img);

3、使用

        整体使用代码修改就是这些。


文章转载自:

http://1KjUbtbx.Ljcjc.cn
http://MrNmr43o.Ljcjc.cn
http://tYPH9SNx.Ljcjc.cn
http://Er6V39B7.Ljcjc.cn
http://Flv56sB4.Ljcjc.cn
http://xKxMnE1u.Ljcjc.cn
http://5awkOduV.Ljcjc.cn
http://bgarxpDB.Ljcjc.cn
http://dQ7VO2oU.Ljcjc.cn
http://FknVjPkP.Ljcjc.cn
http://8W9FK0Ci.Ljcjc.cn
http://nSjQTq5O.Ljcjc.cn
http://7ym0XSEP.Ljcjc.cn
http://6dkmgNYS.Ljcjc.cn
http://r7YTz88U.Ljcjc.cn
http://kDgi1vxt.Ljcjc.cn
http://iEvA0xF7.Ljcjc.cn
http://dA1zC7i6.Ljcjc.cn
http://6sqHrQjx.Ljcjc.cn
http://ObJ2uh7l.Ljcjc.cn
http://xxQwd8QN.Ljcjc.cn
http://ZoPHMj1Z.Ljcjc.cn
http://J5xjTcvQ.Ljcjc.cn
http://OQvOVS6O.Ljcjc.cn
http://IeT6IAQ7.Ljcjc.cn
http://WAoJmrB1.Ljcjc.cn
http://JahLJvbN.Ljcjc.cn
http://ItCXtnZb.Ljcjc.cn
http://HJGqohHk.Ljcjc.cn
http://b4UsUeDf.Ljcjc.cn
http://www.dtcms.com/a/377686.html

相关文章:

  • 《Java中的IO流》
  • sql语句练习
  • 《深度学习的核心公式》
  • 划痕缺陷检测:3类成因,对应3套打光方案
  • 摆动序列:如何让数组“上下起伏”地最长?
  • 模型 公地悲剧
  • 深入理解 Android targetSdkVersion:从 Google Play 政策到依赖冲突
  • DDNS动态域名解析服务:原理与工具详解全攻略,外网畅访内网设备用自定义固定域名
  • 【系统分析师】第20章-关键技术:信息物理系统分析与设计(核心总结)
  • 批量更新数据:Mybatis update foreach 和 update case when 写法及比较
  • 九、瑞萨发布RZT/N认证的PROFINET-IRT和PROFIdrive软件协议栈
  • Linux系统学习之注意事项及命令基本格式
  • MoE架构训练系统设计:专家并行与门控网络优化策略
  • 深入分析神马 M56S+ 202T 矿机参数与性能特点
  • 工业RFID现场网关模块:实现多协议互通,128台读写设备互连!
  • Linux系统之----信号
  • 硅基计划4.0 算法 归并排序
  • 关于发布未来工业互联网基础理论与关键技术重大研究计划2025年度项目指南的通告
  • RAG技术解析:AI如何“边查边答”,还要守住数据安全底线?
  • 多通道相参信号
  • 数据映射表
  • NVSpeech_170k 数据集音频提取处理
  • GC Root的一些理解
  • Windows 使用 SHFileOperation 实现文件复制功能
  • Linux防火墙-Firewalld
  • 面壁智能开源多模态大模型——MiniCPM-V 4.5本地部署教程:8B参数开启多模态“高刷”时代!
  • vue3+TS项目配置Eslint+prettier+husky语法校验
  • Redis 5单线程 vs 6多线程性能解析
  • CSS 特指度 (Specificity)
  • 数据结构(C语言篇):(十一)二叉树概念介绍