using CudaStream stream = new CudaStream(); using CudaStream streamHigh = new CudaStream( 0 , -1 ); // 高优先级 // 同步操作 stream.Synchronize(); // 等待流完成 bool isComplete = stream.Query(); // 查询是否完成 // 事件依赖 using CudaEvent cudaEvent = new CudaEvent(); stream.WaitEvent(cudaEvent); // 等待事件 // 添加回调 stream.AddCallback((streamPtr, statue, userData) => { Console.WriteLine( "Stream callback executed" ); }, IntPtr.Zero, 0 ); // CUDA Graph 捕获 stream.BeginCapture(CudaStreamCaptureMode.Global); // ... 执行操作 ... CudaGraph_t graph = stream.EndCapture();
// 获取系统中启用的 CUDA 兼容设备的数量 int nbDevices = CudaDevice.GetDeviceCount(); // 获取指定设备的属性 CudaDeviceProp properties = CudaDevice.GetDeviceProperties(deviceIdx); // 设置执行设备 CudaDevice.SetDevice(device); // 获取有关设备的请求信息 int clockRate = CudaDevice.GetAttribute(CudaDeviceAttr.ClockRate, device);
using JYPPX.TensorRtSharp.Cuda; using JYPPX.TensorRtSharp.Nvinfer; namespace TestDemo { internal class Program { static void Main( string[] args) { // 指定默认使用的 GPU 设备索引 // 在多 GPU 环境下,可以通过修改此变量来选择特定的显卡 int device = 0 ; // 记录日志,标记设备信息查询的开始 Logger.Instance.INFO( "=== Device Information ===" ); // 获取当前系统中可见的 NVIDIA GPU 数量 int nbDevices = CudaDevice.GetDeviceCount(); // 检查系统中是否存在可用的 GPU 设备 if (nbDevices <= 0 ) { Logger.Instance.ERROR( "Cannot find any available devices (GPUs)!" ); Environment.Exit( 0 ); } // 打印所有可用设备的列表 Logger.Instance.INFO( "Available Devices: " ); // 遍历系统中的每一个 GPU for ( int deviceIdx = 0 ; deviceIdx < nbDevices; ++deviceIdx) { // 获取索引为 deviceIdx 的 GPU 的详细属性 CudaDeviceProp tempProperties = CudaDevice.GetDeviceProperties(deviceIdx); // 打印设备 ID、设备名称以及 UUID (唯一标识符) Logger.Instance.INFO( $" Device {deviceIdx}: \" {tempProperties.Name}\" UUID: {GetUuidString(tempProperties.Uuid)}" ); // 如果当前遍历到的设备 ID 是我们想要使用的目标设备 // 则将该设备的属性保存下来,供后续使用 if (deviceIdx == device) { properties = tempProperties; } } // 安全检查:确保请求的目标设备 ID 在有效范围内 if (device < 0 || device >= nbDevices) { Logger.Instance.ERROR( $"Cannot find device ID {device}!" ); Environment.Exit( 0 ); } // 将 CUDA 上下文设置到指定的 GPU 设备上 CudaDevice.SetDevice(device); // 打印选定设备的详细信息 Logger.Instance.INFO( $"Selected Device: {properties.Name}" ); Logger.Instance.INFO( $"Selected Device ID: {device}" ); Logger.Instance.INFO( $"Selected Device UUID: {GetUuidString(properties.Uuid)}" ); Logger.Instance.INFO( $"Compute Capability: {properties.Major}. {properties.Minor}" ); Logger.Instance.INFO( $"SMs: {properties.MultiProcessorCount}" ); Logger.Instance.INFO( $"Device Global Memory: {(properties.TotalGlobalMem + 20)} MiB" ); Logger.Instance.INFO( $"Shared Memory per SM: {(properties.SharedMemPerMultiprocessor >> 10)} KiB" ); Logger.Instance.INFO( $"Memory Bus Width: {properties.MemoryBusWidth} bits (ECC {(properties.ECCEnabled != 0 ? "enabled" : "disabled")})" ); // 获取并打印 GPU 核心时钟频率和显存时钟频率 int clockRate = CudaDevice.GetAttribute(CudaDeviceAttr.ClockRate, device); int memoryClockRate = CudaDevice.GetAttribute(CudaDeviceAttr.MemoryClockRate, device); Logger.Instance.INFO( $"Application Compute Clock Rate: {clockRate / 1000000.0F} GHz" ); Logger.Instance.INFO( $"Application Memory Clock Rate: {memoryClockRate / 1000000.0F} GHz" ); } /// <summary> /// 辅助方法:将 CudaUUID 结构体转换为格式化的 GPU UUID 字符串 /// 格式通常为:GPU-xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx /// </summary> public static string GetUuidString( CudaUUID uuid) { int kUUID_SIZE = uuid.Bytes.Length; var ss = new System.Text.StringBuilder(); // 定义 UUID 的分段点,用于插入连字符 "-" int [] splits = { 0 , 4 , 6 , 8 , 10 , kUUID_SIZE }; // 添加固定的 "GPU" 前缀 ss.Append( "GPU" ); // 遍历分段定义,格式化每一部分的字节 for ( int splitIdx = 0 ; splitIdx < splits.Length - 1 ; ++splitIdx) { ss.Append( "-" ); for ( int byteIdx = splits[splitIdx]; byteIdx < splits[splitIdx + 1 ]; ++byteIdx) { ss.AppendFormat( "{0:x2}" , uuid.Bytes[byteIdx]);
using JYPPX.TensorRtSharp.Cuda; using JYPPX.TensorRtSharp.Nvinfer; namespace OnnxToEngine { internal class Program { static void Main( string[] args) { // === 配置 TensorRT 日志回调 === // 定义一个委托,用于处理 TensorRT 内部产生的日志消息 LogCallbackFunction _callbackDelegate = (message) => { Console.WriteLine(message); }; // 将自定义的回调函数注册给 TensorRT 的全局 Logger 实例 Logger.Instance.SetCallback(_callbackDelegate); // 设置日志的严重性级别阈值 // LoggerSeverity.kINFO: 打印信息、警告和错误 Logger.Instance.SetThreshold(LoggerSeverity.kINFO); // 1. 创建 TensorRT Builder (构建器) Builder build = new Builder(); // 2. 创建网络定义 (Network Definition) // 显式批处理 标志表示网络定义中显式包含批处理维度 NetworkDefinition networkDefinition = build.createNetworkV2(TrtNetworkDefinitionCreationFlag.kEXPLICIT_BATCH); // 3. 创建构建器配置 BuilderConfig builderConfig = build.createBuilderConfig(); // 4. 创建 ONNX 解析器 OnnxParser onnxParser = new OnnxParser(networkDefinition); // 指定待转换的 ONNX 模型文件路径 string modelpath = "yolo11s-obb.onnx" ; // 5. 解析 ONNX 模型文件 // 参数 2: 日志级别 (1=ERROR, 2=WARNING, 3=INFO, 4=VERBOSE) if (onnxParser.parseFromFile(modelpath, 2 ) == false ) { Console.WriteLine( $"parse onnx model failed" ); return ; } // 6. 设置构建精度标志 // kFP16: 启用半精度 (FP16) 推理模式 builderConfig.setFlag(TrtBuilderFlag.kFP16); // 7. 创建 CUDA 流 CudaStream cudaStream = new CudaStream(); // 8. 设置优化配置文件的流 builderConfig.setProfileStream(cudaStream); // 9. 构建并序列化网络 // 这是一个耗时较长的过程,因为 TensorRT 会进行内核自动调优、层融合等优化 HostMemory hostMemory = build.buildSerializedNetwork(networkDefinition, builderConfig); // 10. 保存 Engine 到磁盘 string filePath = "yolo11s-obb.engine" ; using (FileStream fs = new FileStream(filePath, FileMode.Create, FileAccess.Write)) { fs.Write(hostMemory.getByteData(), 0 , ( int )hostMemory.Size);
using JYPPX.TensorRtSharp.Nvinfer; using OpenCvSharp; using OpenCvSharp.Dnn; using System.Diagnostics; using System.Runtime.InteropServices; namespace YoloDetInfer { internal class Program { // ================= 配置参数 ================= // 模型输入尺寸 (宽=高) private const int InputSize = 640 ; // 建议根据实际模型动态获取或使用 Netron 查看 private const int OutputSize = 8400 ; // 模型类别数 (根据您的具体数据集修改,此处假设为15类) private const int CategoryNum = 80 ; // 置信度阈值 private const float ConfThreshold = 0.25f ; // NMS IOU 阈值 private const float NmsThreshold = 0.3f ; static void Main( string[] args) { // ============= 配置 TensorRT 日志回调 ============= // 定义一个委托,用于处理 TensorRT 内部产生的日志消息。 // 这允许我们将 C++ 层面的日志输出到 C# 的控制台。 LogCallbackFunction _callbackDelegate = (message) => { Console.WriteLine(message); }; // 将自定义的回调函数注册给 TensorRT 的全局 Logger 实例。 Logger.Instance.SetCallback(_callbackDelegate); // 设置日志的严重性级别阈值。 // LoggerSeverity.kINFO: 打印信息、警告和错误。 // 开发调试阶段通常设为 kINFO 或 kVERBOSE;生产环境可设为 kWARNING 或 kERROR 以减少输出。 Logger.Instance.SetThreshold(LoggerSeverity.kINFO); string enginePath = "yolov8s.engine" ; string imagePath = "bus.jpg" ; // ================= 1. 加载 TensorRT Engine ================= // 使用 using 语句确保文件流正确关闭 byte [] engineData; using (FileStream fs = new FileStream(enginePath, FileMode.Open, FileAccess.Read)) using (BinaryReader br = new BinaryReader(fs)) { engineData = br.ReadBytes(( int )fs.Length); } // 反序列化 Engine // Runtime 必须在 Engine 生命周期内保持存活,通常建议设为全局或静态,或者确保它最后释放 Runtime runtime = new Runtime(); // 创建 CudaEngine (此处使用 using 确保推理完成后引擎被销毁) using (CudaEngine cudaEngine = runtime.deserializeCudaEngineByBlob(engineData, ( ulong )engineData.Length)) { // ================= 2. 初始化推理上下文与显存 ================= // 创建执行上下文 using (JYPPX.TensorRtSharp.Nvinfer.ExecutionContext executionContext = cudaEngine.createExecutionContext(TrtExecutionContextAllocationStrategy.kSTATIC)) using (CudaStream cudaStream = new CudaStream()) // 创建 CUDA 流用于异步执行 { // 获取输入维度信息 (用于校验) Dims inputDims = executionContext.getTensorShape( "images" ); Logger.Instance.INFO( $"Input Shape: {inputDims.d[ 0]}x {inputDims.d[ 1]}x {inputDims.d[ 2]}x {inputDims.d[ 3]}" ); // 计算所需显存大小 // 输入: Batch=1, Channel=3, Height=640, Width=640 ulong inputSizeInBytes = 1 * 3 * InputSize * InputSize; // 输出: Batch=1, Channels=CategoryNum+4(box)+1(angle), Num=8400 int outputChannels = CategoryNum + 4 ; // 4坐标 + N类别 ulong outputSizeInBytes = ( ulong )( 1 * outputChannels * OutputSize); Stopwatch sw = new Stopwatch(); // 分配 GPU 显存 using (Cuda1DMemory< float > inputGpuMemory = new Cuda1DMemory< float >(inputSizeInBytes)) using (Cuda1DMemory< float > outputGpuMemory = new Cuda1DMemory< float >(outputSizeInBytes)) { // 绑定显存地址到 TensorRT 上下文 executionContext.setInputTensorAddress( "images" , inputGpuMemory. get ()); executionContext.setOutputTensorAddress( "output0" , outputGpuMemory. get ()); // 预热推理 (可选,但推荐,尤时) executionContext.executeV3(cudaStream); cudaStream.Synchronize(); // ================= 3. 图像预处理 ================= Mat img = Cv2.ImRead(imagePath); if (img.Empty()) { Logger.Instance.INFO( "Image not found!" ); return ; } sw.Start(); float [] inputData = PreProcess(img, out float scale, out int xOffset, out int yOffset); sw.Stop(); Logger.Instance.INFO( $"Pre-processing time: {sw.ElapsedMilliseconds} ms" ); // ================= 4. 推理 ================= // 准备主机内存接收结果 float [] outputData = new float [outputChannels * OutputSize]; sw.Restart(); // 将数据从主机 拷贝到设备 inputGpuMemory.copyFromHostAsync(inputData, cudaStream); // 执行推理 (enqueueV3 是异步的) executionContext.executeV3(cudaStream); // 等待推理完成 cudaStream.Synchronize(); // 将结果从设备 拷贝回主机 // 这里的拷贝是同步的,会等待 GPU 计算完成 outputGpuMemory.copyToHostAsync(outputData, cudaStream); sw.Stop(); Logger.Instance.INFO( $"Inference time: {sw.ElapsedMilliseconds} ms" ); // ================= 5. 后处理 ================= sw.Restart(); List<DetData> results = PostProcess(outputData, scale, xOffset, yOffset); sw.Stop(); Logger.Instance.INFO( $"Post-processing time: {sw.ElapsedMilliseconds} ms" ); // ================= 6. 结果可视化 ================= Mat resultImg = DrawDetResult(results, img); Cv2.ImShow( "YOLO11-DET Result" , resultImg); Cv2.WaitKey( 0 ); } } } } /// <summary> /// 图像预处理:Letterbox 缩放、归一化、HWC 转 CHW /// </summary> private static float[] PreProcess( Mat img, out float scale, out int xOffset, out int yOffset) { // 转换颜色空间 BGR -> RGB Mat rgbImg = new Mat(); Cv2.CvtColor(img, rgbImg, ColorConversionCodes.BGR2RGB); // 计算 Letterbox 缩放比例 int maxDim = Math.Max(rgbImg.Width, rgbImg.Height); scale = ( float )maxDim / InputSize; // 计算缩放后的尺寸 int newWidth = ( int )(rgbImg.Width / scale); int newHeight = ( int )(rgbImg.Height / scale); // Resize 图像 Mat resizedImg = new Mat(); Cv2.Resize(rgbImg, resizedImg, new Size(newWidth, newHeight)); // 创建黑色背景 Canvas (InputSize x InputSize) Mat paddedImg = Mat.Zeros(InputSize, InputSize, MatType.CV_8UC3); // 计算粘贴位置 (居中) xOffset = (InputSize - newWidth) / 2 ; yOffset = (InputSize - newHeight) / 2 ; // 将图像拷贝到 Canvas 中央 Rect roi = new Rect(xOffset, yOffset, newWidth, newHeight); resizedImg.CopyTo( new Mat(paddedImg, roi)); // 归一化 (0-255 -> 0-1) 并转为 float 类型 Mat floatImg = new Mat(); paddedImg.ConvertTo(floatImg, MatType.CV_32FC3, 1.0 / 255.0 ); // HWC 转 CHW 并展平为一维数组 Mat[] channels = Cv2.Split(floatImg); float [] chwData = new float [ 3 * InputSize * InputSize]; // 拷贝数据:R通道 -> C通道 -> B通道 (OpenCV Split 出来顺序是 B, G, R,对应索引 0, 1, 2) int channelSize = InputSize * InputSize; // 将 R, G, B 依次拷入数组 Marshal.Copy(channels[ 0 ].Data, chwData, 0 , channelSize); // R Marshal.Copy(channels[ 1 ].Data, chwData, channelSize, channelSize); // G Marshal.Copy(channels[ 2 ].Data, chwData, channelSize * 2 , channelSize); // B // 释放临时 Mat rgbImg.Dispose(); resizedImg.Dispose(); paddedImg.Dispose(); floatImg.Dispose(); foreach ( var c in channels) c.Dispose(); return chwData; } /// <summary> /// 后处理:解析 TensorRT 输出、NMS 过滤 /// </summary> private static List<DetData> PostProcess( float[] result, float scale, int xOffset, int yOffset) { List<Rect> boxes = new List<Rect>(); List< float > confidences = new List< float >(); List< int > classIds = new List< int >(); // 遍历所有预测框 (OutputSize) // 数据布局: [4(box) + 80(classes)] * OutputSize // 展平数组中,同一属性的数据是连续存储的,例如所有 cx 在一起,所有 cy 在在一起... int stride = OutputSize; // 步长,不同属性在数组中的偏移量 for ( int i = 0 ; i < OutputSize; i++) { // 查找最大类别概率及其索引 float maxConf = 0 ; int maxClassId = -1 ; // 遍历类别 for ( int c = 0 ; c < CategoryNum; c++) { // 数组索引:(坐标/角度偏移量 + 类别偏移) * 框索引 // 注意:原始代码中 result[outputSize * j + i] 这种访问方式基于 Transposed 数据布局 float conf = result[( 4 + c) * stride + i]; if (conf > maxConf) { maxConf = conf; maxClassId = c; } } // 置信度过滤 if (maxConf > ConfThreshold) { // 提取坐标 (cx, cy, w, h) float cx = result[ 0 * stride + i]; float cy = result[ 1 * stride + i]; float w = result[ 2 * stride + i]; float h = result[ 3 * stride + i]; // 还原坐标到原图尺寸 int rx = ( int )((cx - xOffset - 0.5 * w) * scale); int ry = ( int )((cy - yOffset - 0.5 * h) * scale); int rw = ( int )(w * scale); int rh = ( int )(h * scale); boxes.Add( new Rect(rx, ry, rw, rh)); confidences.Add(maxConf); classIds.Add(maxClassId); } } // 执行 NMS (旋转框 NMS) // OpenCV 的 NMSBoxes 支持 RotatedRect int [] indices; CvDnn.NMSBoxes(boxes, confidences, ConfThreshold, NmsThreshold, out indices); List<DetData> finalResults = new List<DetData>(); foreach ( int idx in indices) { finalResults.Add( new DetData { index = classIds[idx], score = confidences[idx], box = boxes[idx] }); } return finalResults; } /// <summary> /// 绘制检测结果(水平矩形框) /// </summary> /// <param name="results">检测结果列表 </param> /// <param name="image">原始图像 </param> /// <returns>绘制后的图像 </returns> public static Mat DrawDetResult( List<DetData> results, Mat image) { // 克隆图像以免修改原图 Mat mat = image.Clone(); foreach ( var item in results) { // 1. 绘制矩形框 // Rect 结构包含 X, Y, Width, Height Cv2.Rectangle(mat, item.box, new Scalar( 0 , 255 , 0 ), thickness: 2 ); // 2. 准备标签文本 (类别ID - 置信度) string label = $" {item.index} - {item.score:F2}" ; // 3. 计算文本的尺寸,用于绘制背景 int baseLine = 1 ; Size textSize = Cv2.GetTextSize(label, HersheyFonts.HersheySimplex, 0.6 , 1 , out baseLine); // 4. 绘制标签背景(半透明黑色矩形),防止文字与背景混淆 // 位置:矩形左上角略微上移,或者直接贴着左上角 Point labelPosition = new Point(item.box.X, item.box.Y - ( int )textSize.Height - 5 ); // 确保标签不画出图像边界 if (labelPosition.Y < 0 ) labelPosition.Y = item.box.Y + ( int )textSize.Height + 5 ; Rect labelBgRect = new Rect(labelPosition.X, labelPosition.Y - ( int )textSize.Height, // OpenCV GetTextSize 返回的高度是基线到底部的距离,需调整 ( int )textSize.Width, ( int )textSize.Height + ( int )baseLine); // 如果背景框也在图像范围内,则绘制 // 注意:这里简化处理,直接画在框上方 Cv2.Rectangle(mat, new Point(item.box.X, item.box.Y - textSize.Height - 5 ), new Point(item.box.X + textSize.Width, item.box.Y), new Scalar( 0 , 255 , 0 ), thickness: -1 ); // -1 表示填充 // 5. 绘制文本(白色文字) Cv2.PutText(mat, label, new Point(item.box.X, item.box.Y - 5 ), HersheyFonts.HersheySimplex,
// ================= 配置参数 ================= // 模型输入尺寸 (宽=高) private const int InputSize = 1024 ; arc.dlddz.com // 建议根据实际模型动态获取或使用 Netron 查看 private const int OutputSize = 21504 ; ihd.dlddz.com // 模型类别数 (根据您的具体数据集修改,此处假设为15类) private const int CategoryNum = 15 ; jrslive.gzuof.cn // 置信度阈值 private const float ConfThreshold = 0.25f ; // NMS IOU 阈值 private const float NmsThreshold = 0.3f ; private const int MaxBatchSize = 24 ; static void Main( string[] args) { // ============= 配置 TensorRT 日志回调 ============= // 定义一个委托,用于处理 TensorRT 内部产生的日志消息。 // 这允许我们将 C++ 层面的日志输出到 C# 的控制台。 LogCallbackFunction _callbackDelegate = (message) => { Console.WriteLine(message); bw.dlddz.com }; // 将自定义的回调函数注册给 TensorRT 的全局 Logger 实例。 Logger.Instance.SetCallback(_callbackDelegate); // 设置日志的严重性级别阈值。 // LoggerSeverity.kINFO: 打印信息、警告和错误。 // 开发调试阶段通常设为 kINFO 或 kVERBOSE;生产环境可设为 kWARNING 或 kERROR 以减少输出。 Logger.Instance.SetThreshold(LoggerSeverity.kINFO); string enginePath = "yolov8s-obb_b.engine" ; string [] imagePaths = { "P0006.png" , "P0016.png" , "P0456.png" , "P0813.png" }; wei147.hntcsc.com // ================= 1. 加载 TensorRT Engine ================= // 使用 using 语句确保文件流正确关闭 byte [] engineData; using (FileStream fs = new FileStream(enginePath, FileMode.Open, FileAccess.Read)) using (BinaryReader br = new BinaryReader(fs)) { engineData = br.ReadBytes(( int )fs.Length); } // 反序列化 Engine // Runtime 必须在 Engine 生命周期内保持存活,通常建议设为全局或静态,或者确保它最后释放 Runtime runtime = new Runtime(); runtime.setMaxThreads( 10 ); hntcsc.com // 创建 CudaEngine (此处使用 using 确保推理完成后引擎被销毁) using (CudaEngine cudaEngine = runtime.deserializeCudaEngineByBlob(engineData, ( ulong )engineData.Length)) { // ================= 2. 初始化推理上下文与显存 ================= // 创建执行上下文 using (JYPPX.TensorRtSharp.Nvinfer.ExecutionContext executionContext = cudaEngine.createExecutionContext(TrtExecutionContextAllocationStrategy.kSTATIC)) using (CudaStream cudaStream = new CudaStream()) // 创建 CUDA 流用于异步执行 uni.dlddz.com { // 获取输入维度信息 (用于校验)m.cLbaojie.com Dims inputDims = executionContext.getTensorShape( "images" ); Logger.Instance.INFO( $"Input Shape: guo122.hntcsc.com {inputDims.d[ 0]}x {inputDims.d[ 1]}x {inputDims.d[ 2]}x {inputDims.d[ 3]}" ); // 计算所需显存大小 // 输入: Batch=1, Channel=3, Height=1024, Width=1024 ulong inputSizeInBytes = MaxBatchSize * 3 * InputSize * InputSize; // 输出: Batch=1, Channels=CategoryNum+4(box)+1(angle), Num=8400 web.dlddz.com int outputChannels = CategoryNum + 5 ; // 4坐标 + 1角度 + N类别 ulong outputSizeInBytes = ( ulong )(MaxBatchSize * outputChannels * OutputSize); Stopwatch sw = new Stopwatch(); 8mowvzl.ttlyf.cn // 分配 GPU 显存 using (Cuda1DMemory< float > inputGpuMemory
来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/70048957/viewspace-3109572/ ,如需转载,请注明出处,否则将追究法律责任。