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

C# OpenVinoSharp部署DEIMv2

关于DEIMv2的介绍,大家可以看这篇文章,据说是对小目标检测效果比较好(没有大量实测)
https://zhuanlan.zhihu.com/p/1954932639887791489
目标检测效果:

模型信息:

测试模型名称 deimv2_hgnetv2_n_coco.onnx,可在Hugging Face获取

模型输入:
name: images
tensor: float32[N,3,640,640]
name: orig_target_sizes
tensor: int64[unk__446,2]
模型输出:
name: labels
tensor: int64[Sublabels_dim_0,Sublabels_dim_1]
name: boxes
tensor: float32[GatherElementsboxes_dim_0,GatherElementsboxes_dim_1,4]
name: scores
tensor: float32[GatherElementsboxes_dim_0,GatherElementsboxes_dim_1]

接下来,就是模型部署推理相关代码了,直接复制就可以用
///检测类 DEIMv2Detector.cs

using OpenCvSharp;
using OpenVinoSharp;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace yolo_world_opencvsharp_net4._8
{
    public class DEIMv2Detector : IDisposable
    {
        private Core _core;
        private CompiledModel _compiledModel;
        private InferRequest _inferRequest;
        private string _modelSize;
        private int _inputSize;
        private bool _shouldNormalize;

        // ImageNet归一化参数
        private readonly float[] _imagenetMean = new float[] { 0.485f, 0.456f, 0.406f };
        private readonly float[] _imagenetStd = new float[] { 0.229f, 0.224f, 0.225f };

        // 颜色列表用于不同类别的绘制
        private readonly Scalar[] _colors = new Scalar[]
        {
                new Scalar(255, 0, 0),     // 红色
                new Scalar(0, 255, 0),     // 绿色
                new Scalar(0, 0, 255),     // 蓝色
                new Scalar(255, 255, 0),   // 青色
                new Scalar(255, 0, 255),   // 洋红色
                new Scalar(0, 255, 255),   // 黄色
                new Scalar(128, 0, 0),     // 深红色
                new Scalar(0, 128, 0),     // 深绿色
                new Scalar(0, 0, 128),     // 深蓝色
                new Scalar(128, 128, 0)    // 橄榄色
        };

        public DEIMv2Detector(string modelPath, string modelSize)
        {
            _core = new Core();
            _compiledModel = _core.compile_model(modelPath, "CPU");
            _inferRequest = _compiledModel.create_infer_request();
            _modelSize = modelSize.ToLower();

            // 根据模型大小决定是否使用归一化
            _shouldNormalize = !new[] { "atto", "femto", "pico", "n" }.Contains(_modelSize);

            // 获取输入尺寸
            var inputShape = _inferRequest.get_tensor("images").get_shape();
            _inputSize = (int)inputShape[2]; // 形状为 [N,3,640,640]

            Console.WriteLine($"Model loaded: {modelPath}");
            Console.WriteLine($"Input size: {_inputSize}");
            Console.WriteLine($"Normalization: {_shouldNormalize}");
        }

        public Mat ProcessImage(string imagePath)
        {
            // 使用OpenCvSharp加载图像
            var originalImage = Cv2.ImRead(imagePath);
            if (originalImage.Empty())
            {
                Console.WriteLine($"Failed to load image: {imagePath}");
                return null;
            }

            // 预处理
            var (processedImage, ratio, padW, padH) = ResizeWithAspectRatio(originalImage, _inputSize);
            var inputTensor = PreprocessImage(processedImage, _shouldNormalize);
            var sizeTensor = PrepareTargetSizes(_inputSize, _inputSize);

            // 设置输入
            _inferRequest.set_input_tensor(0, inputTensor);
            _inferRequest.set_input_tensor(1, sizeTensor);

            // 推理
            _inferRequest.infer();

            // 获取输出
            var labels = _inferRequest.get_output_tensor(0);
            var boxes = _inferRequest.get_output_tensor(1);
            var scores = _inferRequest.get_output_tensor(2);

            // 处理结果
            var resultImage = DrawDetections(originalImage, labels, boxes, scores, ratio, padW, padH);

            // 保存结果
            //string outputPath = "onnx_result_csharp.jpg";
            //Cv2.ImWrite(outputPath, resultImage);
            //Console.WriteLine($"Result saved as: {outputPath}");

            // 清理资源
            inputTensor.Dispose();
            sizeTensor.Dispose();
            labels.Dispose();
            boxes.Dispose();
            scores.Dispose();
            processedImage.Dispose();
            return resultImage;
        }

        private (Mat resizedImage, float ratio, int padW, int padH) ResizeWithAspectRatio(Mat image, int size)
        {
            int originalWidth = image.Width;
            int originalHeight = image.Height;

            float ratio = Math.Min((float)size / originalWidth, (float)size / originalHeight);
            int newWidth = (int)(originalWidth * ratio);
            int newHeight = (int)(originalHeight * ratio);

            // 调整尺寸
            var resizedImage = new Mat();
            Cv2.Resize(image, resizedImage, new Size(newWidth, newHeight));

            // 创建填充后的图像
            var paddedImage = new Mat(size, size, image.Type(), new Scalar(0, 0, 0)); // 用黑色填充
            int padW = (size - newWidth) / 2;
            int padH = (size - newHeight) / 2;

            // 将调整大小后的图像复制到填充图像的中央
            var roi = new Rect(padW, padH, newWidth, newHeight);
            resizedImage.CopyTo(paddedImage[roi]);

            resizedImage.Dispose();
            return (paddedImage, ratio, padW, padH);
        }

        private Tensor PreprocessImage(Mat image, bool normalize)
        {
            int width = image.Width;
            int height = image.Height;

            // 转换为浮点数并归一化
            var floatImage = new Mat();
            image.ConvertTo(floatImage, MatType.CV_32FC3, 1.0 / 255.0);

            // 分离通道
            Mat[] channels = new Mat[3];
            Cv2.Split(floatImage, out channels);

            float[] result = new float[3 * height * width];

            if (normalize)
            {
                // 应用ImageNet归一化
                for (int c = 0; c < 3; c++)
                {
                    var channelData = new float[height * width];
                    channels[c].GetArray(out channelData);

                    for (int i = 0; i < channelData.Length; i++)
                    {
                        channelData[i] = (channelData[i] - _imagenetMean[c]) / _imagenetStd[c];
                    }

                    Array.Copy(channelData, 0, result, c * height * width, channelData.Length);
                }
            }
            else
            {
                // 只使用0-1范围的数值
                for (int c = 0; c < 3; c++)
                {
                    var channelData = new float[height * width];
                    channels[c].GetArray(out channelData);
                    Array.Copy(channelData, 0, result, c * height * width, channelData.Length);
                }
            }

            // 清理资源
            floatImage.Dispose();
            foreach (var channel in channels)
            {
                channel.Dispose();
            }

            // 创建Tensor [1, 3, height, width]
            Tensor tensor = new Tensor(new OpenVinoSharp.element.Type(ElementType.F32), new Shape(1, 3, height, width));
            tensor.set_data<float>(result);
            return tensor;
        }

        private Tensor PrepareTargetSizes(int height, int width)
        {
            long[] sizeData = new long[] { height, width };
            Tensor tensor = new Tensor(new OpenVinoSharp.element.Type(ElementType.I64), new Shape(1, 2));
            tensor.set_data<long>(sizeData);
            return tensor;
        }

        private Mat DrawDetections(Mat originalImage, Tensor labels, Tensor boxes, Tensor scores,
                                    float ratio, int padW, int padH, float threshold = 0.3f)
        {
            var resultImage = originalImage.Clone();

            // 获取输出数据
            long[] labelsData = labels.get_data<long>((int)labels.get_size());
            float[] boxesData = boxes.get_data<float>((int)boxes.get_size());
            float[] scoresData = scores.get_data<float>((int)scores.get_size());

            // 获取输出形状
            long[] labelsShape = labels.get_shape().ToArray();
            long[] boxesShape = boxes.get_shape().ToArray();

            int numDetections = (int)labelsShape[1];//[1,100]

            for (int i = 0; i < numDetections; i++)
            {
                float score = scoresData[i];

                // 过滤低置信度检测
                if (score < threshold)
                    continue;

                long label = labelsData[i];

                // 获取边界框坐标 (x1, y1, x2, y2)
                int baseIdx = i * 4;
                float x1 = boxesData[baseIdx];
                float y1 = boxesData[baseIdx + 1];
                float x2 = boxesData[baseIdx + 2];
                float y2 = boxesData[baseIdx + 3];

                // 调整边界框到原始图像坐标
                float adjustedX1 = (x1 - padW) / ratio;
                float adjustedY1 = (y1 - padH) / ratio;
                float adjustedX2 = (x2 - padW) / ratio;
                float adjustedY2 = (y2 - padH) / ratio;

                // 确保坐标在图像范围内
                adjustedX1 = Math.Max(0, Math.Min(adjustedX1, originalImage.Width));
                adjustedY1 = Math.Max(0, Math.Min(adjustedY1, originalImage.Height));
                adjustedX2 = Math.Max(0, Math.Min(adjustedX2, originalImage.Width));
                adjustedY2 = Math.Max(0, Math.Min(adjustedY2, originalImage.Height));

                // 选择颜色
                Scalar color = _colors[label % _colors.Length];

                // 绘制边界框
                Cv2.Rectangle(resultImage,
                    new Point((int)adjustedX1, (int)adjustedY1),
                    new Point((int)adjustedX2, (int)adjustedY2),
                    color, 2);

                // 绘制标签文本
                string labelText = $"Class: {label} ({score:F2})";
                var textSize = Cv2.GetTextSize(labelText, HersheyFonts.HersheySimplex, 0.5, 1, out int baseline);

                // 绘制文本背景
                Cv2.Rectangle(resultImage,
                    new Point((int)adjustedX1, (int)adjustedY1 - textSize.Height - 5),
                    new Point((int)adjustedX1 + textSize.Width, (int)adjustedY1),
                    color, -1);

                // 绘制文本
                Cv2.PutText(resultImage, labelText,
                    new Point((int)adjustedX1, (int)adjustedY1 - 5),
                    HersheyFonts.HersheySimplex, 0.5, Scalar.White, 1);
            }

            return resultImage;
        }

        public void Dispose()
        {
            _inferRequest?.Dispose();
            _compiledModel?.Dispose();
            _core?.Dispose();
        }

    }
}
 

使用方法示例:

using OpenVinoSharp;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Drawing.Imaging;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using OpenCvSharp;
using Point = OpenCvSharp.Point;
using Size = OpenCvSharp.Size;
using System.Diagnostics;

namespace yolo_world_opencvsharp_net4._8
{
    public partial class DEIMv2Form : Form
    {
        public DEIMv2Form()
        {
            InitializeComponent();
        }
        DEIMv2Detector detector;
        private void button1_Click(object sender, EventArgs e)
        {
            Infer();
        }

        private void Infer()
        {
            try
            {
                // 运行推理
                string inputPath = "D:\\ultralytics-main\\test_det_01.jpg";
                if (IsImageFile(inputPath))
                {
                    Stopwatch sw = Stopwatch.StartNew();
                    var resultImage=detector.ProcessImage(inputPath);
                    sw.Stop();
                    Console.WriteLine(sw.ElapsedMilliseconds+"ms"); 
                    if (resultImage != null)
                    {
                        Cv2.NamedWindow("result",WindowFlags.Normal);
                        Cv2.ImShow("result", resultImage);
                        resultImage.Dispose();
                    }
                }
                else
                {
                   return;
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine($"Error: {ex.Message}");
            }
        }

        static bool IsImageFile(string path)
        {
            string[] imageExtensions = { ".jpg", ".jpeg", ".png", ".bmp", ".tiff", ".tif" };
            return imageExtensions.Any(ext => path.ToLower().EndsWith(ext));
        }

        private void DEIMv2Form_Load(object sender, EventArgs e)
        {
            //"Model sizes: atto, femto, pico, n, s, m, l, x";
            string modelPath = "D:\\deimv2_hgnetv2_n_coco.onnx";
            string modelSize = "n";
            //初始化
            detector = new DEIMv2Detector(modelPath, modelSize);
        }
    }
}
 

http://www.dtcms.com/a/590533.html

相关文章:

  • 给一个网站做需求分析动漫做的游戏 迅雷下载网站
  • 海南响应式网站建设哪里好当面付 wordpress插件
  • 【multi-model】moco系列SimCLRBEiT
  • VLA模型和世界模型知识总结
  • 找人做网站属于了解些什么呢大连外协机械加工网
  • 邯郸网站建设哪儿好做同城相亲网站
  • 长沙免费建站模板软件开发的收官之战是什么
  • 假冒建设厅网站wordpress同步微信素材
  • 高校网站建设情况报告范文免费建站网站大全
  • 网站开发税收分类长沙有名的公司
  • 帮企网站建设简洁大气企业网站源码
  • 佛山知名网站建设公司承德网站制作报价
  • 电力设备高盐雾环境腐蚀状态智能监测与防护策略优化
  • 网站建设公司信科网络南京做网站seo的
  • 瓯海住房与城乡建设局网站dw网页设计案例
  • Dev-C++的Compiler Options在哪里?
  • 地板网站建设国外网站建设接单
  • Java Lambda表达式完全指南:从面向对象到函数式编程的优雅转变
  • 网站安全狗钓鱼网站制作的报告
  • Atcoder(ABC431)A-D
  • 专业网专业网站建设新会网页制作公司
  • 荆州哪里做网站哪个网站可以做翻译赚钱
  • 网站推广常用方法宁乡市住房和城乡建设局网站
  • M3和M4内核的区别
  • 长春免费做网站建筑型专业网站有哪些
  • 如何评价一个企业网站做的好wordpress 华哥
  • 网站建设与运营答案建设自动弹出qq对话框的网站
  • 杭州网站程序开发公司现在ps做网站的尺寸
  • Launch4j打包将jar包生成exe执行文件全流程
  • 荣誉章标志做网站广州 网站建设公司