From 4213492c4ff0997548170a52dc40ae7466816a7a Mon Sep 17 00:00:00 2001 From: Jon Wood Date: Thu, 11 Nov 2021 13:43:17 -0500 Subject: [PATCH 1/2] Update bike rental sample to .NET 6 --- .../BikeDemandForecasting.csproj | 3 +- .../Data/DailyDemand.mdf | Bin 8388608 -> 8388608 bytes .../BikeDemandForecasting/Program.cs | 233 ++++++++---------- 3 files changed, 111 insertions(+), 125 deletions(-) diff --git a/samples/csharp/getting-started/Forecasting_BikeSharingDemand/BikeDemandForecasting/BikeDemandForecasting.csproj b/samples/csharp/getting-started/Forecasting_BikeSharingDemand/BikeDemandForecasting/BikeDemandForecasting.csproj index edf1bc2cc..555000c13 100644 --- a/samples/csharp/getting-started/Forecasting_BikeSharingDemand/BikeDemandForecasting/BikeDemandForecasting.csproj +++ b/samples/csharp/getting-started/Forecasting_BikeSharingDemand/BikeDemandForecasting/BikeDemandForecasting.csproj @@ -2,7 +2,8 @@ Exe - net5 + net6.0 + enable diff --git a/samples/csharp/getting-started/Forecasting_BikeSharingDemand/BikeDemandForecasting/Data/DailyDemand.mdf b/samples/csharp/getting-started/Forecasting_BikeSharingDemand/BikeDemandForecasting/Data/DailyDemand.mdf index 440fa51c6af96ba47256b59897b1fe38e5272d22..c314e3d660968404480eaefbbc1364cd9bb840f5 100644 GIT binary patch delta 1541 zcmb`GTWnNS6o&UXolZ|%&}mQatuQUc&XiJnzlhquH$kjz(SnLqrxz;F8V3)|mK$XY%c}_FDhm z>+E&*1cO0WP?%b0)~8N;b6maGUt4%%{&Vka;3Lm;VAxY|GO6Tz`xs75y*+d3%dbPT z*FJ~|g@Xwf63mpOnojU{bqOO(0}DZ9eKv(KO+b7%JO(&`ML7PxkRTip@7$1s9TZ5 zE$79azWU3#B}u?slVEs4gZs@-QdgwbDyiK!Oh>8%;UxD&U(gKo1!F!t7SU#lE_Ri6 zG0-&VXg?0PJVyOabx`e5BQkcX9<^7zVKpRvlh_`0NDWGMNR3AFjS}mXd8gW{_DO7; z`Je4R4Lc&9ZES$c8O%GL+^-Af65$!A$2 z|LLa4Z(WIv%AF77kgJpYR;`h%X9vu5T|8x_M7GmM{gG9XX!upjrDy!sIwM1OjhnVE zIqbId)uq61&hiXBU2Uc7?S=AYQU8Sa`t+$nkDe>CEcu{`EuV;Vv zO|*M70@cb|0;HjzT45@|#_kwIh< zSwuFGL)=BIBi0jMBA3V`HW2y5M&fRwfbbE1qLA1`6cL+=V&Wd6geWD-h;pKWs3fY0 zYNCdyCGI7*5cd)H6Aut|1c-W~foLR}h-TtJVk@zYc!=0eJWR9@j}Way8_`beARZ+i zBRYtk#4h4-;tAqOVmI*=(Mdc_JVQK71c)x8o9H2Wi9JLg@f@+2*hlOq`iTKzkQgF{ li37y*#0YVa7$pu7W5i+N1!9~yLcB;EC0-(4eh3VGr`w^eb?cON>q1$!MY`=$pg?~>?b{XvtJVTlK&xzl7VSSR|E+W- z<|U~&-SBht#u!74K`(3qEQv8u<3%qB7ix%+n{8qMy-`C=>V?E_wp-c|qceHtocX@@ zeDj`j=0u}WD=Lf^nR&Ufz!Iz2xH5gV{@cJrD_z^JTSuWHp6)u7g^Dq)SdY?x)@#>?KiSsw+0y^Y z#an~M4}bgSDyGeUMBN;|l;zGj9toI}bw0PsdqTzn$+?d%U7eLg%1h2<5JREPxvr3K7ZP} zdw$%?iw~w*`9@sgIYug$eP+yK3F(4I$cT@5><n`CDpFV>`3F?COkb3Hy%?35ag zQf;bNbqnokNF7##s!g}w$Z-lE4p*oFN%pHQeIwy5&?7-7N1qHknXxECkSHcfh!w;W#7d%+2oYsOIT0pS5f#L0VhypDs3g`A>xm6S6;Vyp5Vgcc zViWNs@f7hiQAdENCmM)GVl%OYc!t z#4chtv4?ntc$H`)UL#&7_7V}Io!Ce0Ck_xDL?>~Oc!PM8=pwp_9-^1%Bl?K};t(-N e3=xNkBg9eS7;&69L7XIpi4o#0;?&0&{puf-m69d^ diff --git a/samples/csharp/getting-started/Forecasting_BikeSharingDemand/BikeDemandForecasting/Program.cs b/samples/csharp/getting-started/Forecasting_BikeSharingDemand/BikeDemandForecasting/Program.cs index 9dbb8a401..11e6fa369 100644 --- a/samples/csharp/getting-started/Forecasting_BikeSharingDemand/BikeDemandForecasting/Program.cs +++ b/samples/csharp/getting-started/Forecasting_BikeSharingDemand/BikeDemandForecasting/Program.cs @@ -1,137 +1,122 @@ -using System; -using System.Collections.Generic; -using System.Data.SqlClient; -using System.IO; -using System.Linq; -using Microsoft.ML; +using Microsoft.ML; using Microsoft.ML.Data; using Microsoft.ML.Transforms.TimeSeries; +using System.Data.SqlClient; + +string rootDir = Path.GetFullPath(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "../../../")); +string dbFilePath = Path.Combine(rootDir, "Data", "DailyDemand.mdf"); +string modelPath = Path.Combine(rootDir, "MLModel.zip"); +var connectionString = $"Data Source=(LocalDB)\\MSSQLLocalDB;AttachDbFilename={dbFilePath};Integrated Security=True;Connect Timeout=30;"; + +MLContext mlContext = new MLContext(); + +DatabaseLoader loader = mlContext.Data.CreateDatabaseLoader(); + +string query = "SELECT RentalDate, CAST(Year as REAL) as Year, CAST(TotalRentals as REAL) as TotalRentals FROM Rentals"; + +DatabaseSource dbSource = new DatabaseSource(SqlClientFactory.Instance, + connectionString, + query); + +IDataView dataView = loader.Load(dbSource); -namespace BikeDemandForecasting +IDataView firstYearData = mlContext.Data.FilterRowsByColumn(dataView, "Year", upperBound: 1); +IDataView secondYearData = mlContext.Data.FilterRowsByColumn(dataView, "Year", lowerBound: 1); + +var forecastingPipeline = mlContext.Forecasting.ForecastBySsa( + outputColumnName: "ForecastedRentals", + inputColumnName: "TotalRentals", + windowSize: 7, + seriesLength: 30, + trainSize: 365, + horizon: 7, + confidenceLevel: 0.95f, + confidenceLowerBoundColumn: "LowerBoundRentals", + confidenceUpperBoundColumn: "UpperBoundRentals"); + +SsaForecastingTransformer forecaster = forecastingPipeline.Fit(firstYearData); + +Evaluate(secondYearData, forecaster, mlContext); + +var forecastEngine = forecaster.CreateTimeSeriesEngine(mlContext); +forecastEngine.CheckPoint(mlContext, modelPath); + +Forecast(secondYearData, 7, forecastEngine, mlContext); + +void Evaluate(IDataView testData, ITransformer model, MLContext mlContext) { - class Program - { - static void Main(string[] args) - { - string rootDir = Path.GetFullPath(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "../../../")); - string dbFilePath = Path.Combine(rootDir, "Data", "DailyDemand.mdf"); - string modelPath = Path.Combine(rootDir, "MLModel.zip"); - var connectionString = $"Data Source=(LocalDB)\\MSSQLLocalDB;AttachDbFilename={dbFilePath};Integrated Security=True;Connect Timeout=30;"; - - MLContext mlContext = new MLContext(); - - DatabaseLoader loader = mlContext.Data.CreateDatabaseLoader(); - - string query = "SELECT RentalDate, CAST(Year as REAL) as Year, CAST(TotalRentals as REAL) as TotalRentals FROM Rentals"; - - DatabaseSource dbSource = new DatabaseSource(SqlClientFactory.Instance, - connectionString, - query); - - IDataView dataView = loader.Load(dbSource); - - IDataView firstYearData = mlContext.Data.FilterRowsByColumn(dataView, "Year", upperBound: 1); - IDataView secondYearData = mlContext.Data.FilterRowsByColumn(dataView, "Year", lowerBound: 1); - - var forecastingPipeline = mlContext.Forecasting.ForecastBySsa( - outputColumnName: "ForecastedRentals", - inputColumnName: "TotalRentals", - windowSize: 7, - seriesLength: 30, - trainSize: 365, - horizon: 7, - confidenceLevel: 0.95f, - confidenceLowerBoundColumn: "LowerBoundRentals", - confidenceUpperBoundColumn: "UpperBoundRentals"); - - SsaForecastingTransformer forecaster = forecastingPipeline.Fit(firstYearData); - - Evaluate(secondYearData, forecaster, mlContext); - - var forecastEngine = forecaster.CreateTimeSeriesEngine(mlContext); - forecastEngine.CheckPoint(mlContext, modelPath); - - Forecast(secondYearData, 7, forecastEngine, mlContext); - - Console.ReadKey(); - } - - static void Evaluate(IDataView testData, ITransformer model, MLContext mlContext) - { - // Make predictions - IDataView predictions = model.Transform(testData); - - // Actual values - IEnumerable actual = - mlContext.Data.CreateEnumerable(testData, true) - .Select(observed => observed.TotalRentals); - - // Predicted values - IEnumerable forecast = - mlContext.Data.CreateEnumerable(predictions, true) - .Select(prediction => prediction.ForecastedRentals[0]); - - // Calculate error (actual - forecast) - var metrics = actual.Zip(forecast, (actualValue, forecastValue) => actualValue - forecastValue); - - // Get metric averages - var MAE = metrics.Average(error => Math.Abs(error)); // Mean Absolute Error - var RMSE = Math.Sqrt(metrics.Average(error => Math.Pow(error, 2))); // Root Mean Squared Error - - // Output metrics - Console.WriteLine("Evaluation Metrics"); - Console.WriteLine("---------------------"); - Console.WriteLine($"Mean Absolute Error: {MAE:F3}"); - Console.WriteLine($"Root Mean Squared Error: {RMSE:F3}\n"); - } - - static void Forecast(IDataView testData, int horizon, TimeSeriesPredictionEngine forecaster, MLContext mlContext) - { - - ModelOutput forecast = forecaster.Predict(); - - IEnumerable forecastOutput = - mlContext.Data.CreateEnumerable(testData, reuseRowObject: false) - .Take(horizon) - .Select((ModelInput rental, int index) => - { - string rentalDate = rental.RentalDate.ToShortDateString(); - float actualRentals = rental.TotalRentals; - float lowerEstimate = Math.Max(0, forecast.LowerBoundRentals[index]); - float estimate = forecast.ForecastedRentals[index]; - float upperEstimate = forecast.UpperBoundRentals[index]; - return $"Date: {rentalDate}\n" + - $"Actual Rentals: {actualRentals}\n" + - $"Lower Estimate: {lowerEstimate}\n" + - $"Forecast: {estimate}\n" + - $"Upper Estimate: {upperEstimate}\n"; - }); - - // Output predictions - Console.WriteLine("Rental Forecast"); - Console.WriteLine("---------------------"); - foreach (var prediction in forecastOutput) + // Make predictions + IDataView predictions = model.Transform(testData); + + // Actual values + IEnumerable actual = + mlContext.Data.CreateEnumerable(testData, true) + .Select(observed => observed.TotalRentals); + + // Predicted values + IEnumerable forecast = + mlContext.Data.CreateEnumerable(predictions, true) + .Select(prediction => prediction.ForecastedRentals[0]); + + // Calculate error (actual - forecast) + var metrics = actual.Zip(forecast, (actualValue, forecastValue) => actualValue - forecastValue); + + // Get metric averages + var MAE = metrics.Average(error => Math.Abs(error)); // Mean Absolute Error + var RMSE = Math.Sqrt(metrics.Average(error => Math.Pow(error, 2))); // Root Mean Squared Error + + // Output metrics + Console.WriteLine("Evaluation Metrics"); + Console.WriteLine("---------------------"); + Console.WriteLine($"Mean Absolute Error: {MAE:F3}"); + Console.WriteLine($"Root Mean Squared Error: {RMSE:F3}\n"); +} + +void Forecast(IDataView testData, int horizon, TimeSeriesPredictionEngine forecaster, MLContext mlContext) +{ + + ModelOutput forecast = forecaster.Predict(); + + IEnumerable forecastOutput = + mlContext.Data.CreateEnumerable(testData, reuseRowObject: false) + .Take(horizon) + .Select((ModelInput rental, int index) => { - Console.WriteLine(prediction); - } - } + string rentalDate = rental.RentalDate.ToShortDateString(); + float actualRentals = rental.TotalRentals; + float lowerEstimate = Math.Max(0, forecast.LowerBoundRentals[index]); + float estimate = forecast.ForecastedRentals[index]; + float upperEstimate = forecast.UpperBoundRentals[index]; + return $"Date: {rentalDate}\n" + + $"Actual Rentals: {actualRentals}\n" + + $"Lower Estimate: {lowerEstimate}\n" + + $"Forecast: {estimate}\n" + + $"Upper Estimate: {upperEstimate}\n"; + }); + + // Output predictions + Console.WriteLine("Rental Forecast"); + Console.WriteLine("---------------------"); + foreach (var prediction in forecastOutput) + { + Console.WriteLine(prediction); } +} - public class ModelInput - { - public DateTime RentalDate { get; set; } +public class ModelInput +{ + public DateTime RentalDate { get; set; } - public float Year { get; set; } + public float Year { get; set; } - public float TotalRentals { get; set; } - } + public float TotalRentals { get; set; } +} - public class ModelOutput - { - public float[] ForecastedRentals { get; set; } +public class ModelOutput +{ + public float[] ForecastedRentals { get; set; } - public float[] LowerBoundRentals { get; set; } + public float[] LowerBoundRentals { get; set; } - public float[] UpperBoundRentals { get; set; } - } + public float[] UpperBoundRentals { get; set; } } From c23ee775ac0e3cf298e129023acb73c235dd173b Mon Sep 17 00:00:00 2001 From: Jon Wood Date: Thu, 11 Nov 2021 13:55:24 -0500 Subject: [PATCH 2/2] Update object detection sample to .NET 6 --- .../ObjectDetection.csproj | 3 +- .../ObjectDetectionConsoleApp/Program.cs | 242 ++++++++---------- 2 files changed, 115 insertions(+), 130 deletions(-) diff --git a/samples/csharp/getting-started/DeepLearning_ObjectDetection_Onnx/ObjectDetectionConsoleApp/ObjectDetection.csproj b/samples/csharp/getting-started/DeepLearning_ObjectDetection_Onnx/ObjectDetectionConsoleApp/ObjectDetection.csproj index e717d3ff1..5118bfaa7 100644 --- a/samples/csharp/getting-started/DeepLearning_ObjectDetection_Onnx/ObjectDetectionConsoleApp/ObjectDetection.csproj +++ b/samples/csharp/getting-started/DeepLearning_ObjectDetection_Onnx/ObjectDetectionConsoleApp/ObjectDetection.csproj @@ -2,7 +2,8 @@ Exe - net5 + net6.0 + enable diff --git a/samples/csharp/getting-started/DeepLearning_ObjectDetection_Onnx/ObjectDetectionConsoleApp/Program.cs b/samples/csharp/getting-started/DeepLearning_ObjectDetection_Onnx/ObjectDetectionConsoleApp/Program.cs index 5b758ef09..cf637ae79 100644 --- a/samples/csharp/getting-started/DeepLearning_ObjectDetection_Onnx/ObjectDetectionConsoleApp/Program.cs +++ b/samples/csharp/getting-started/DeepLearning_ObjectDetection_Onnx/ObjectDetectionConsoleApp/Program.cs @@ -1,148 +1,132 @@ -using System; -using System.IO; -using System.Collections.Generic; -using System.Drawing; +using System.Drawing; using System.Drawing.Drawing2D; -using System.Linq; -using Microsoft.ML; using ObjectDetection.YoloParser; using ObjectDetection.DataStructures; +using ObjectDetection; +using Microsoft.ML; + +var assetsRelativePath = @"../../../assets"; +string assetsPath = GetAbsolutePath(assetsRelativePath); +var modelFilePath = Path.Combine(assetsPath, "Model", "TinyYolo2_model.onnx"); +var imagesFolder = Path.Combine(assetsPath, "images"); +var outputFolder = Path.Combine(assetsPath, "images", "output"); -namespace ObjectDetection +// Initialize MLContext +MLContext mlContext = new MLContext(); + +try { - class Program + // Load Data + IEnumerable images = ImageNetData.ReadFromFile(imagesFolder); + IDataView imageDataView = mlContext.Data.LoadFromEnumerable(images); + + // Create instance of model scorer + var modelScorer = new OnnxModelScorer(imagesFolder, modelFilePath, mlContext); + + // Use model to score data + IEnumerable probabilities = modelScorer.Score(imageDataView); + + // Post-process model output + YoloOutputParser parser = new YoloOutputParser(); + + var boundingBoxes = + probabilities + .Select(probability => parser.ParseOutputs(probability)) + .Select(boxes => parser.FilterBoundingBoxes(boxes, 5, .5F)); + + // Draw bounding boxes for detected objects in each of the images + for (var i = 0; i < images.Count(); i++) { - public static void Main() - { - var assetsRelativePath = @"../../../assets"; - string assetsPath = GetAbsolutePath(assetsRelativePath); - var modelFilePath = Path.Combine(assetsPath, "Model", "TinyYolo2_model.onnx"); - var imagesFolder = Path.Combine(assetsPath, "images"); - var outputFolder = Path.Combine(assetsPath, "images", "output"); - - // Initialize MLContext - MLContext mlContext = new MLContext(); - - try - { - // Load Data - IEnumerable images = ImageNetData.ReadFromFile(imagesFolder); - IDataView imageDataView = mlContext.Data.LoadFromEnumerable(images); - - // Create instance of model scorer - var modelScorer = new OnnxModelScorer(imagesFolder, modelFilePath, mlContext); - - // Use model to score data - IEnumerable probabilities = modelScorer.Score(imageDataView); - - // Post-process model output - YoloOutputParser parser = new YoloOutputParser(); - - var boundingBoxes = - probabilities - .Select(probability => parser.ParseOutputs(probability)) - .Select(boxes => parser.FilterBoundingBoxes(boxes, 5, .5F)); - - // Draw bounding boxes for detected objects in each of the images - for (var i = 0; i < images.Count(); i++) - { - string imageFileName = images.ElementAt(i).Label; - IList detectedObjects = boundingBoxes.ElementAt(i); - - DrawBoundingBox(imagesFolder, outputFolder, imageFileName, detectedObjects); - - LogDetectedObjects(imageFileName, detectedObjects); - } - } - catch (Exception ex) - { - Console.WriteLine(ex.ToString()); - } - - Console.WriteLine("========= End of Process..Hit any Key ========"); - Console.ReadLine(); - } + string imageFileName = images.ElementAt(i).Label; + IList detectedObjects = boundingBoxes.ElementAt(i); - public static string GetAbsolutePath(string relativePath) - { - FileInfo _dataRoot = new FileInfo(typeof(Program).Assembly.Location); - string assemblyFolderPath = _dataRoot.Directory.FullName; + DrawBoundingBox(imagesFolder, outputFolder, imageFileName, detectedObjects); - string fullPath = Path.Combine(assemblyFolderPath, relativePath); + LogDetectedObjects(imageFileName, detectedObjects); + } +} +catch (Exception ex) +{ + Console.WriteLine(ex.ToString()); +} - return fullPath; - } +Console.WriteLine("========= End of Process..Hit any Key ========"); - private static void DrawBoundingBox(string inputImageLocation, string outputImageLocation, string imageName, IList filteredBoundingBoxes) - { - Image image = Image.FromFile(Path.Combine(inputImageLocation, imageName)); - - var originalImageHeight = image.Height; - var originalImageWidth = image.Width; - - foreach (var box in filteredBoundingBoxes) - { - // Get Bounding Box Dimensions - var x = (uint)Math.Max(box.Dimensions.X, 0); - var y = (uint)Math.Max(box.Dimensions.Y, 0); - var width = (uint)Math.Min(originalImageWidth - x, box.Dimensions.Width); - var height = (uint)Math.Min(originalImageHeight - y, box.Dimensions.Height); - - // Resize To Image - x = (uint)originalImageWidth * x / OnnxModelScorer.ImageNetSettings.imageWidth; - y = (uint)originalImageHeight * y / OnnxModelScorer.ImageNetSettings.imageHeight; - width = (uint)originalImageWidth * width / OnnxModelScorer.ImageNetSettings.imageWidth; - height = (uint)originalImageHeight * height / OnnxModelScorer.ImageNetSettings.imageHeight; - - // Bounding Box Text - string text = $"{box.Label} ({(box.Confidence * 100).ToString("0")}%)"; - - using (Graphics thumbnailGraphic = Graphics.FromImage(image)) - { - thumbnailGraphic.CompositingQuality = CompositingQuality.HighQuality; - thumbnailGraphic.SmoothingMode = SmoothingMode.HighQuality; - thumbnailGraphic.InterpolationMode = InterpolationMode.HighQualityBicubic; - - // Define Text Options - Font drawFont = new Font("Arial", 12, FontStyle.Bold); - SizeF size = thumbnailGraphic.MeasureString(text, drawFont); - SolidBrush fontBrush = new SolidBrush(Color.Black); - Point atPoint = new Point((int)x, (int)y - (int)size.Height - 1); - - // Define BoundingBox options - Pen pen = new Pen(box.BoxColor, 3.2f); - SolidBrush colorBrush = new SolidBrush(box.BoxColor); - - // Draw text on image - thumbnailGraphic.FillRectangle(colorBrush, (int)x, (int)(y - size.Height - 1), (int)size.Width, (int)size.Height); - thumbnailGraphic.DrawString(text, drawFont, fontBrush, atPoint); - - // Draw bounding box on image - thumbnailGraphic.DrawRectangle(pen, x, y, width, height); - } - } - - if (!Directory.Exists(outputImageLocation)) - { - Directory.CreateDirectory(outputImageLocation); - } - - image.Save(Path.Combine(outputImageLocation, imageName)); - } +string GetAbsolutePath(string relativePath) +{ + FileInfo _dataRoot = new FileInfo(typeof(Program).Assembly.Location); + string assemblyFolderPath = _dataRoot.Directory.FullName; - private static void LogDetectedObjects(string imageName, IList boundingBoxes) - { - Console.WriteLine($".....The objects in the image {imageName} are detected as below...."); + string fullPath = Path.Combine(assemblyFolderPath, relativePath); - foreach (var box in boundingBoxes) - { - Console.WriteLine($"{box.Label} and its Confidence score: {box.Confidence}"); - } + return fullPath; +} - Console.WriteLine(""); +void DrawBoundingBox(string inputImageLocation, string outputImageLocation, string imageName, IList filteredBoundingBoxes) +{ + Image image = Image.FromFile(Path.Combine(inputImageLocation, imageName)); + + var originalImageHeight = image.Height; + var originalImageWidth = image.Width; + + foreach (var box in filteredBoundingBoxes) + { + // Get Bounding Box Dimensions + var x = (uint)Math.Max(box.Dimensions.X, 0); + var y = (uint)Math.Max(box.Dimensions.Y, 0); + var width = (uint)Math.Min(originalImageWidth - x, box.Dimensions.Width); + var height = (uint)Math.Min(originalImageHeight - y, box.Dimensions.Height); + + // Resize To Image + x = (uint)originalImageWidth * x / OnnxModelScorer.ImageNetSettings.imageWidth; + y = (uint)originalImageHeight * y / OnnxModelScorer.ImageNetSettings.imageHeight; + width = (uint)originalImageWidth * width / OnnxModelScorer.ImageNetSettings.imageWidth; + height = (uint)originalImageHeight * height / OnnxModelScorer.ImageNetSettings.imageHeight; + + // Bounding Box Text + string text = $"{box.Label} ({(box.Confidence * 100).ToString("0")}%)"; + + using (Graphics thumbnailGraphic = Graphics.FromImage(image)) + { + thumbnailGraphic.CompositingQuality = CompositingQuality.HighQuality; + thumbnailGraphic.SmoothingMode = SmoothingMode.HighQuality; + thumbnailGraphic.InterpolationMode = InterpolationMode.HighQualityBicubic; + + // Define Text Options + Font drawFont = new Font("Arial", 12, FontStyle.Bold); + SizeF size = thumbnailGraphic.MeasureString(text, drawFont); + SolidBrush fontBrush = new SolidBrush(Color.Black); + Point atPoint = new Point((int)x, (int)y - (int)size.Height - 1); + + // Define BoundingBox options + Pen pen = new Pen(box.BoxColor, 3.2f); + SolidBrush colorBrush = new SolidBrush(box.BoxColor); + + // Draw text on image + thumbnailGraphic.FillRectangle(colorBrush, (int)x, (int)(y - size.Height - 1), (int)size.Width, (int)size.Height); + thumbnailGraphic.DrawString(text, drawFont, fontBrush, atPoint); + + // Draw bounding box on image + thumbnailGraphic.DrawRectangle(pen, x, y, width, height); } } + + if (!Directory.Exists(outputImageLocation)) + { + Directory.CreateDirectory(outputImageLocation); + } + + image.Save(Path.Combine(outputImageLocation, imageName)); } +void LogDetectedObjects(string imageName, IList boundingBoxes) +{ + Console.WriteLine($".....The objects in the image {imageName} are detected as below...."); + foreach (var box in boundingBoxes) + { + Console.WriteLine($"{box.Label} and its Confidence score: {box.Confidence}"); + } + Console.WriteLine(""); +}