Search code examples
c#onnximage-preprocessingyolov4onnxruntime

Expand dimension of "EmguCV.Mat" or "Onnx Tensor"


I am using Onnxruntime in C# for yolov4. Here is the pretrained yolo model: https://github.com/onnx/models/tree/main/vision/object_detection_segmentation/yolov4/model

EmguCV is used to get an image, and then preprocess it to suit Yolo's input.

This is my preprocessing code:

    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;
    }

It works fine, but it is really slow, definitely because of nested fors.

So I decide to change it to the following code:

    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;
    }

It does not use nested for and runs faster, but...

Output tensor shape of this code is (416,416,3), but yoloV4 need input tensor with shape (1,416,416,3).

How can I add a single dimension to onnx tensor or CV.Mat image, to fit my tensor to yoloV4 input?

It would be nice of you if you would help me with this problem.

Thanks in advance Mary


Solution

  • I found one solution myself :D

    It may help somebody else, with the same problem.

    For me, this code is about 10 times faster than the original code (preprocess_CV function in the question).

    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, float>().Data;
    
            float[] imDataFlat = new float[imData.Length];
            Buffer.BlockCopy(imData, 0, imDataFlat, 0, imData.Length * 4);
    
            var inputTensor = new DenseTensor<float>(imDataFlat, new int[] { 1, im.Height, im.Width, 3 });
            List<NamedOnnxValue> inputs = new List<NamedOnnxValue> { NamedOnnxValue.CreateFromTensor("input_1:0", inputTensor) };
            return inputs;
        }