You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C#部署YOLOv4时如何为EmguCV.Mat/Onnx张量扩展维度

C# OnnxRuntime部署YOLOv4预处理维度适配方案

问题描述

  • 部署栈:C# + OnnxRuntime,用EmguCV做图像加载和预处理,适配YOLOv4的416x416输入要求
  • 第一版预处理通过双层循环逐像素做归一化、通道转换,功能正常但运行速度慢
  • 第二版预处理调用EmguCV内置API做缩放、归一化、通道转换,移除了逐像素循环,速度提升明显,但输出张量形状为(416,416,3),缺少长度为1的batch维度,不符合YOLOv4要求的输入形状(1,416,416,3),会触发维度不匹配报错。

初版功能正常但性能差的代码

static List<NamedOnnxValue> preprocess_CV(Mat im)
{
    CvInvoke.Resize(im, im, new Size(416, 416));
    var imData = im.ToImage<Bgr, Byte>().Data;

    Tensor<float> input = new DenseTensor<float>(new[] {1, im.Height, im.Width, 3});
    for (int x = 0; x < im.Width; x++)
        for (int y = 0; y < im.Height; y++)
        {
            input[0, x, y, 0] = imData[x, y, 2] / (float)255.0;
            input[0, x, y, 1] = imData[x, y, 1] / (float)255.0;
            input[0, x, y, 2] = imData[x, y, 0] / (float)255.0;
        }
    List<NamedOnnxValue> inputs = new List<NamedOnnxValue> { NamedOnnxValue.CreateFromTensor("input_1:0", input) };
    return inputs;
}

优化后存在维度问题的v2版代码

static List<NamedOnnxValue> preprocess_CV_v2(Mat im)
{
    CvInvoke.Resize(im, im, new Size(416, 416));
    im.ConvertTo(im, DepthType.Cv32F, 1 / 255.0);
    CvInvoke.CvtColor(im, im, ColorConversion.Bgr2Rgb);            
    var imData = im.ToImage<Bgr, Byte>().Data;
    var input = imData.ToTensor<float>();
    List<NamedOnnxValue> inputs = new List<NamedOnnxValue> { NamedOnnxValue.CreateFromTensor("input_1:0", input) };
    return inputs;
}

注意:该版本代码除了维度缺失问题外,还存在隐藏的类型错误:调用ConvertTo将Mat转为Cv32F32位浮点类型后,仍然调用ToImage<Bgr, Byte>()读取8位无符号字节数据,会导致读取到的像素值完全异常,后续修复方案已同步修正该问题。

修复方案

两种方案都可以保留v2版本的高性能,不需要回退到逐像素循环写法:

  • 方案1:张量维度扩展+内存拷贝
    先创建形状符合要求的4维DenseTensor,将预处理完成的3维图像数据通过内存块拷贝直接写入目标张量,全程无逐像素遍历,性能和v2版本基本持平:

    static List<NamedOnnxValue> preprocess_CV_fixed(Mat im)
    {
        CvInvoke.Resize(im, im, new Size(416, 416));
        im.ConvertTo(im, DepthType.Cv32F, 1 / 255.0);
        CvInvoke.CvtColor(im, im, ColorConversion.Bgr2Rgb);
        // 直接读取浮点类型的图像数据
        var rawData = im.GetData() as float[,,];
        // 创建带batch维度的目标张量
        var input = new DenseTensor<float>(new[] {1, im.Height, im.Width, 3});
        // 内存块直接拷贝,性能远高于逐像素循环
        Buffer.BlockCopy(rawData, 0, input.Buffer.Array, input.Buffer.Offset, rawData.Length * sizeof(float));
        
        return new List<NamedOnnxValue> { NamedOnnxValue.CreateFromTensor("input_1:0", input) };
    }
    
  • 方案2:先给Mat插入batch维度再转张量
    直接调用EmguCV内置的ExpandDims接口,在Mat的第0轴插入长度为1的维度,处理后Mat形状直接变为(1,416,416,3),再转Onnx张量即可直接匹配输入要求:

    static List<NamedOnnxValue> preprocess_CV_fixed_v2(Mat im)
    {
        CvInvoke.Resize(im, im, new Size(416, 416));
        im.ConvertTo(im, DepthType.Cv32F, 1 / 255.0);
        CvInvoke.CvtColor(im, im, ColorConversion.Bgr2Rgb);
        // 第0轴插入batch维度
        Mat expandedMat = new Mat();
        CvInvoke.ExpandDims(im, expandedMat, 0);
        // 转换后张量形状直接符合要求
        var input = expandedMat.ToTensor<float>();
        
        return new List<NamedOnnxValue> { NamedOnnxValue.CreateFromTensor("input_1:0", input) };
    }
    

内容的提问来源于stack exchange,提问作者mary

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.28 03:48:09