Using fastai (pytorch) in c# code, how to normalize Bitmap with mean and std?
A few days ago i switched from tensorflow to fastai for my c# Project. But now i am facing a problem with my normalisation. For both i use an onnx pipeline to load the model and the data. 

var onnxPipeline = mLContext.Transforms.ResizeImages(resizing: ImageResizingEstimator.ResizingKind.Fill, outputColumnName: inputName,
                                                                 imageWidth: ImageSettings.imageWidth, imageHeight: ImageSettings.imageHeight,
                                                                 inputColumnName: nameof(ImageInputData.Image)) 
                .Append(mLContext.Transforms.ExtractPixels(outputColumnName: inputName, interleavePixelColors: true, scaleImage: 1 / 255f))
                .Append(mLContext.Transforms.ApplyOnnxModel(outputColumnName: outputName, inputColumnName: inputName, modelFile: onnxModelPath));

var emptyData = mLContext.Data.LoadFromEnumerable(new List&lt;ImageInputData&gt;());
var onnxModel = onnxPipeline.Fit(emptyData);


with 

    class ImageInputData
    {
        [ImageType(ImageSettings.imageHeight, ImageSettings.imageWidth)]
        public Bitmap Image { get; set; }

        public ImageInputData(byte[] image)
        {
            using (var ms = new MemoryStream(image))
            {
                Image = new Bitmap(ms);
            }
        }
        public ImageInputData(Bitmap image)
        {
            Image = image;
        }
    }


After using fastai i learned, that the models get better accuracy if the data is normalized with a specific mean and standard deviation (because i used the resnet34 model it should be means { 0.485, 0.456, 0.406 } stds = { 0.229, 0.224, 0.225 } respectively).
So the pixelvalues (for each color ofc.) have to be transformed with those values to match the trainings images. But how can i achive this in C#? 
What i tried so far is:

int imageSize = 256;
double[] means = new double[] { 0.485, 0.456, 0.406 }; // used in fastai model
double[] stds = new double[] { 0.229, 0.224, 0.225 };
Bitmap bitmapImage = inputBitmap;
Image image = bitmapImage;

Color[] pixels = new Color[imageSize * imageSize];
for (int x = 0; x &lt; bitmapImage.Width; x++)
{
   for (int y = 0; y &lt; bitmapImage.Height; y++)
   {
      Color pixel = bitmapImage.GetPixel(x, y);
      pixels[x + y] = pixel;
      double red = (pixel.R - (means[0] * 255)) / (stds[0] * 255); // *255 to scale the mean and std values to the Bitmap
      double gre = (pixel.G - (means[1] * 255)) / (stds[1] * 255);
      double blu = (pixel.B - (means[2] * 255)) / (stds[2] * 255);
      Color pixel_n = Color.FromArgb(pixel.A, (int)red, (int)gre, (int)blu);
      bitmapImage.SetPixel(x, y, pixel_n);

   }
}


Ofcourse its not working, because the Colorvalues can`t be negative (which i realised only later).
But how can i achive this normalisation between -1 and 1 for my model in C# with the onnx-model?

Is there a different way to feed the model or to handle the normalisation?

Any help would be appreciated!

One way to solve this problem is to switch from an onnx pipeline to an onnx Inferencesession, which is in my view simpler and better to understand:

public List&lt;double&gt; UseOnnxSession(Bitmap image, string onnxModelPath)
{
      double[] means = new double[] { 0.485, 0.456, 0.406 };
      double[] stds = new double[] { 0.229, 0.224, 0.225 };

      using (var session = new InferenceSession(onnxModelPath))
      {
          List&lt;double&gt; scores = new List&lt;double&gt;();
          Tensor&lt;float&gt; t1 = ConvertImageToFloatData(image, means, stds);
          List&lt;float&gt; fl = new List&lt;float&gt;();

          var inputMeta = session.InputMetadata;
          var inputs = new List&lt;NamedOnnxValue&gt;()
          {
             NamedOnnxValue.CreateFromTensor&lt;float&gt;("input_1", t1)
          };
          using (var results = session.Run(inputs))
          {
              foreach (var r in results)
              {
                  var x = r.AsTensor&lt;float&gt;().First();
                  var y = r.AsTensor&lt;float&gt;().Last();
                  var softmaxScore = Softmax(new double[] { x, y });
                  scores.Add(softmaxScore[0]);
                  scores.Add(softmaxScore[1]);
              }
           }
           return scores;
       }
}

// Create your Tensor and add transformations as you need.
public static Tensor&lt;float&gt; ConvertImageToFloatData(Bitmap image, double[] means, double[] std)
{
      Tensor&lt;float&gt; data = new DenseTensor&lt;float&gt;(new[] { 1, 3, image.Width, image.Height });
      for (int x = 0; x &lt; image.Width; x++)
      {
           for (int y = 0; y &lt; image.Height; y++)
           {
               Color color = image.GetPixel(x, y);
               var red = (color.R - (float)means[0] * 255) / ((float)std[0] * 255);
               var gre = (color.G - (float)means[1] * 255) / ((float)std[1] * 255);
               var blu = (color.B - (float)means[2] * 255) / ((float)std[2] * 255);
               data[0, 0, x, y] = red;
               data[0, 1, x, y] = gre;
               data[0, 2, x, y] = blu;
            }
       }
       return data;
}


Also i have to use my own Softmax method on these scores to get the real probabilities out of my model:

        public double[] Softmax(double[] values)
        {
            double[] ret = new double[values.Length];
            double maxExp = values.Select(Math.Exp).Sum();
            for (int i = 0; i &lt; values.Length; i++)
            {
                ret[i] = Math.Round((Math.Exp(values[i]) / maxExp), 4);
            }
            return ret;
        }


Hope this helps someone who has a similar Problem.
