extern alias CoreDrawing; using System; using System.Runtime.CompilerServices; using ZeroLevel.ML.DNN.Models; namespace ZeroLevel.ML.DNN { public interface IImageConverter { FastTensorPool ImageToFastTensorsV2(string imagePath); FastTensorPool ImageToFastTensorsV2Inverted(string imagePath); FastTensorPool ImageToFastTensorsV2(CoreDrawing.System.Drawing.Bitmap image); FastTensorPool ImageToFastTensorsV2Inverted(CoreDrawing.System.Drawing.Bitmap image); } public sealed class ImageToTensorConversionOptions { /// /// Размер кропа который хранится в одном срезе тензора /// [batchSize, TensorCropSize, TensorCropSize, 3] /// или /// [batchSize, 3, TensorCropSize, TensorCropSize] /// public int TensorCropSize { get; set; } = 640; /// /// Множитель размера изображения в препроцессинге, например, мы хотим разрезать изображения на части размером 960*960 /// которые затем отресайзить на вход сети к 640 на 640, в этом случае производительнее сначала сделать ресайз изображения /// на множитель side = side * (640 / 960) = side * 0.666, и затем разрезать сразу на части 640*640 /// public float Multiplier { get; set; } = 1.0f; /// /// Размер батча для сетей с фиксированным размером, при -1 батч будет равен количеству кропов /// public int BatchSize { get; set; } = -1; /// /// Использовать BRG порядок пикселей вместо RGB /// public bool UseBRG { get; set; } = false; /// /// Кратность входного размера /// public int SizeMultiplicity { get; set; } = 64; } public sealed class ImageConverter : IImageConverter { static float standart_normalizator_koef = 1.0f / 255.0f; [MethodImpl(MethodImplOptions.AggressiveInlining)] public static float StandartNormalizator(float x) => x * standart_normalizator_koef; [MethodImpl(MethodImplOptions.AggressiveInlining)] public static float MeanStdNormilize(float x, float mean, float std) => (standart_normalizator_koef * x - mean) / std; private readonly ImageToTensorConversionOptions _options; private readonly bool _noPreresize; public ImageConverter(ImageToTensorConversionOptions options) { if (options.Multiplier <= float.Epsilon) { throw new ArgumentException("Multiplier must be positive number"); } _options = options; _noPreresize = Math.Abs(1.0f - options.Multiplier) <= float.Epsilon; } private CoreDrawing.System.Drawing.Bitmap PrepareBitmap(string imagePath) { CoreDrawing.System.Drawing.Bitmap image; image = new CoreDrawing.System.Drawing.Bitmap(imagePath); return PrepareBitmap(image); } private CoreDrawing.System.Drawing.Bitmap PrepareBitmap(CoreDrawing.System.Drawing.Bitmap image) { int sourceWidth = image.Width, sourceHeight = image.Height; if (_noPreresize == false) { sourceWidth = (int)(sourceWidth * _options.Multiplier); sourceHeight = (int)(sourceHeight * _options.Multiplier); } if (_options.SizeMultiplicity > 1) { //Сделать размеры изображения кратным числу sourceWidth = ((int)Math.Ceiling((double)(sourceWidth / _options.SizeMultiplicity))) * _options.SizeMultiplicity; sourceHeight = ((int)Math.Ceiling((double)(sourceHeight / _options.SizeMultiplicity))) * _options.SizeMultiplicity; } if (sourceWidth != image.Width || sourceHeight != image.Height) { using (var buf = image) { image = new CoreDrawing.System.Drawing.Bitmap(buf, new System.Drawing.Size(sourceWidth, sourceHeight)); } } return image; } public FastTensorPool ImageToFastTensorsV2(string imagePath) { var image = PrepareBitmap(imagePath); return Convert(image, image.Width, image.Height, _options.TensorCropSize, _options.BatchSize, _options.UseBRG); } public FastTensorPool ImageToFastTensorsV2Inverted(string imagePath) { var image = PrepareBitmap(imagePath); return ConvertInverted(image, image.Width, image.Height, _options.TensorCropSize, _options.BatchSize, _options.UseBRG); } public FastTensorPool ImageToFastTensorsV2(CoreDrawing.System.Drawing.Bitmap image) { image = PrepareBitmap(image); return Convert(image, image.Width, image.Height, _options.TensorCropSize, _options.BatchSize, _options.UseBRG); } public FastTensorPool ImageToFastTensorsV2Inverted(CoreDrawing.System.Drawing.Bitmap image) { image = PrepareBitmap(image); return ConvertInverted(image, image.Width, image.Height, _options.TensorCropSize, _options.BatchSize, _options.UseBRG); } public static FastTensorPool Convert(CoreDrawing.System.Drawing.Bitmap image, int sourceWidth, int sourceHeight, int cropSize, int batchSize, bool bgr) { var pool = new FastTensorPool(sourceWidth, sourceHeight, image.Width, image.Height, cropSize); pool.BatchSize = batchSize; if (bgr) { pool.FillFromImageBGR(image); } else { pool.FillFromImage(image); } return pool; } public static FastTensorPool ConvertInverted(CoreDrawing.System.Drawing.Bitmap image, int sourceWidth, int sourceHeight, int cropSize, int batchSize, bool bgr) { var pool = new FastTensorPool(sourceWidth, sourceHeight, image.Width, image.Height, cropSize); pool.BatchSize = batchSize; if(bgr) { pool.FillFromImageInvertAxeBGR(image); } else { pool.FillFromImageInvertAxe(image); } return pool; } } }