Используя fastai (pytorch) в c# коде, как нормализовать растровое изображение со средним и стандартным значением? - PullRequest
1 голос
/ 17 марта 2020

Несколько дней go я переключился с тензорного потока на фастай для моего c# проекта. Но сейчас у меня проблема с нормализацией. Для обоих я использую конвейер onnx для загрузки модели и данных.

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<ImageInputData>());
var onnxModel = onnxPipeline.Fit(emptyData);

с

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

После использования fastai я узнал, что модели получают лучшую точность, если данные нормализованы с указанным средним значением c и стандартным отклонением (потому что я использовал для модели resnet34 это должно быть {0,485, 0,456, 0,406} стандартное значение = {0,229, 0,224, 0,225} соответственно). Таким образом, значения пикселей (для каждого цвета c.) Должны быть преобразованы с этими значениями, чтобы соответствовать изображениям тренировок. Но как я могу достичь этого в C#? До сих пор я пробовал:

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 < bitmapImage.Width; x++)
{
   for (int y = 0; y < 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);

   }
}

Конечно, он не работает, потому что значения цветов не могут быть отрицательными (что я понял только позже). Но как мне добиться этой нормализации между -1 и 1 для моей модели в C# с помощью onnx-модели?

Есть ли другой способ подачи модели или обработки нормализации?

Любая помощь будет оценена!

1 Ответ

0 голосов
/ 19 марта 2020

Один из способов решения этой проблемы - перейти от конвейера onnx к Inferencesession onnx, что, на мой взгляд, проще и лучше для понимания:

public List<double> 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<double> scores = new List<double>();
          Tensor<float> t1 = ConvertImageToFloatData(image, means, stds);
          List<float> fl = new List<float>();

          var inputMeta = session.InputMetadata;
          var inputs = new List<NamedOnnxValue>()
          {
             NamedOnnxValue.CreateFromTensor<float>("input_1", t1)
          };
          using (var results = session.Run(inputs))
          {
              foreach (var r in results)
              {
                  var x = r.AsTensor<float>().First();
                  var y = r.AsTensor<float>().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<float> ConvertImageToFloatData(Bitmap image, double[] means, double[] std)
{
      Tensor<float> data = new DenseTensor<float>(new[] { 1, 3, image.Width, image.Height });
      for (int x = 0; x < image.Width; x++)
      {
           for (int y = 0; y < 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;
}

Также мне нужно использовать мой собственный метод Softmax на эти оценки, чтобы получить реальные вероятности из моей модели:

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

Надеюсь, это поможет кому-то, кто имеет аналогичные проблемы.

...