Problem
Image classification is a common case in many business scenarios. For these cases, you can either use pre-trained models or train your own model to classify images specific to your custom domain.
DataSet
There are two data sources: the tsv file and the image files. The
tsv file contains two columns: the first one is defined as ImagePath and the second one is the Label corresponding to the image. As you can observe, the file does not have a header row, and looks like this:
- broccoli.jpg broccoli
- broccoli.png broccoli
- canoe2.jpg canoe
- canoe3.jpg canoe
- canoe4.jpg canoe
- coffeepot.jpg coffeepot
- coffeepot2.jpg coffeepot
- coffeepot3.jpg coffeepot
- coffeepot4.jpg coffeepot
- pizza.jpg pizza
- pizza2.jpg pizza
- pizza3.jpg pizza
- teddy1.jpg teddy bear
- teddy2.jpg teddy bear
- teddy3.jpg teddy bear
- teddy4.jpg teddy bear
- teddy6.jpg teddy bear
- toaster.jpg toaster
- toaster2.png toaster
- toaster3.jpg toaster
The training and testing images are located in the assets folders. These images belong to Wikimedia Commons.
- Wikimedia Commons, the free media repository. Retrieved 10:48, October 17, 2018 from:
- https://commons.wikimedia.org/wiki/Pizza
- https://commons.wikimedia.org/wiki/Coffee_pot
- https://commons.wikimedia.org/wiki/Toaster
- https://commons.wikimedia.org/wiki/Category:Canoes
- https://commons.wikimedia.org/wiki/Teddy_bear
Pre-trained model
There are multiple models which are pre-trained for classifying images. In this case, we will use a model based on an Inception topology, and trained with images from Image.Net. This model can be downloaded from
here, but it's also available
here.
Solution
The class library project ImageClassification.Score can be used to classify sample images based on the pre-trained Inception-5h TensorFlow model.
Again, note that this sample only uses/consumes a pre-trained TensorFlow model with ML.NET API. Therefore, it does not train any ML.NET model. Currently, TensorFlow is only supported in ML.NET for scoring/predicting with existing TensorFlow trained models.
Code Walkthrough
There are two projects in the solution -ImageClassification.Score is responsible for loading the model in TensorFlow format, and then classify images; hile ImageClassification.Web is responsible for consuming the ImageClassification.Score project to get the image from the user and show prediction results to the user.
ML.NET: Model Scoring
The first step is to load the data using TextLoader,
vardata=mlContext.Data.ReadFromTextFile<ImageNetData>(dataLocation, hasHeader: true);
The image file used to load images has two columns: the first one is defined as ImagePath and the second one is the Labelcorresponding to the image.
It is important to highlight that the label in the ImageNetData class is not really used when scoring with the TensorFlow model. It is used when testing the predictions so you can compare the actual label of each sample data with the predicted label provided by the TensorFlow model.
- broccoli.jpg broccoli
- bucket.png bucket
- canoe.jpg canoe
- snail.jpg snail
- teddy1.jpg teddy bear
As you can observe, the file does not have a header row.
The Inception model has several default parameters you need to pass in.
- public struct ImageNetSettings {
- public const int imageHeight = 224;
- public const int imageWidth = 224;
- public const float mean = 117;
- public const bool channelsLast = true;
- }
The second step is to define the estimator pipeline. Usually, when dealing with deep neural networks, you must adapt the images to the format expected by the network. This is the reason images are resized and then transformed (mainly, pixel values are normalized across all R,G,B channels).
- var pipeline = mlContext.Transforms.LoadImages(outputColumnName: "input", imageFolder: imagesFolder, inputColumnName: nameof(ImageNetData.ImagePath))
- .Append(mlContext.Transforms.ResizeImages(outputColumnName: "input", imageWidth: ImageNetSettings.imageWidth, imageHeight: ImageNetSettings.imageHeight, inputColumnName: "input"))
- .Append(mlContext.Transforms.ExtractPixels(outputColumnName: "input", interleavePixelColors: ImageNetSettings.channelsLast, offsetImage: ImageNetSettings.mean))
- .Append(mlContext.Model.LoadTensorFlowModel(modelLocation)
- .ScoreTensorFlowModel(outputColumnNames: new[] { "softmax2" }, inputColumnNames: new[] { "input" },
- addBatchDimensionInput:true));
You also need to check the neural network and check the names of the input/output nodes. In order to inspect the model, you can use tools like Netron, which is automatically installed with Visual Studio Tools for AI. These names are used later in the definition of the estimation pipe: in the case of the inception network, the input tensor is named 'input' and the output is named 'softmax2'
Finally, we extract the prediction engine after fitting the estimator pipeline. The prediction engine receives as a parameter an object of type ImageNetData (containing 2 properties: ImagePath and Label) and then returns an object of type ImagePrediction.
- ITransformer model = pipeline.Fit(data);
- var predictionEngine = mlContext.Model.CreatePredictionEngine<ImageNetData, ImageNetPrediction>(model);
When obtaining the prediction, we get an array of floats in the property PredictedLabels. Each position in the array is assigned to a label, so for example, if the model has 5 different labels, the array will be length = 5. Each position in the array represents the label's probability in that position; the sum of all array values (probabilities) is equal to one. Then, you need to select the biggest value (probability), and check which is the assigned label to that position.
ImageClassification.Score
Download this from
here and include this project into your Visual Studio solution.
ImageClassification.Web
Let's start to build ASP.NET Core application which will consume the Tensor Flow pre-trained model using ML.NET API.
- Open Visual Studio>New Projects>ASP.NET Core
- Set Project name as ImageClassification.Web
- Click Create button>MVC Web Application
- A new Project will be added to your solution.
- Open Nuget Manager Console and write Install-Package Microsoft.ML
- Right-click on the project and Add refrence>Select ImageClassification.Score>Press Ok
- Download assets from here and include in project
- Here is how your project will look,
- Now we need to consume the model. Create a new Controller or Open HomeController and add the following snippet.
- using System;
- using System.Collections.Generic;
- using System.Diagnostics;
- using System.Linq;
- using System.Threading.Tasks;
- using Microsoft.AspNetCore.Mvc;
- using ImageClassification.Web.Models;
- using System.IO;
- using ImageClassification.ModelScorer;
- using Microsoft.AspNetCore.Http;
- using ImageClassification.ImageDataStructures;
-
- namespace ImageClassification.Web.Controllers
- {
- public class HomeController : Controller
- {
- public IActionResult Index()
- {
- return View();
- }
- [HttpPost]
- public async Task<IActionResult> Index(IFormFile image)
- {
- string assetsRelativePath = @"../../../assets";
- string assetsPath = GetAbsolutePath(assetsRelativePath);
-
- var tagsTsv = Path.Combine(assetsPath, "inputs", "images", "tags.tsv");
- var imagesFolder = Path.Combine(assetsPath, "inputs", "images");
- //Path of Tensor Flow pre trained model
- var inceptionPb = Path.Combine(assetsPath, "inputs", "inception", "tensorflow_inception_graph.pb");
- var labelsTxt = Path.Combine(assetsPath, "inputs", "inception", "imagenet_comp_graph_label_strings.txt");
-
- try
- {
- var filePath = Path.GetTempFileName();
-
- using(var ms=new FileStream(filePath,FileMode.Create))
- {
- await image.CopyToAsync(ms);
- }
- //Create Tensor Flow model for image classification and prediction using ML.NET API
- var modelScorer = new TFModelScorer(tagsTsv, imagesFolder, inceptionPb, labelsTxt);
- //Check image prediction
- var prediction=(ImageNetDataProbability)modelScorer.Score(new ImageNetData() { ImagePath=filePath,Label=""});
- //Store results to ViewBag to show in View
- ViewBag.PredictedLabel = prediction.PredictedLabel;
- ViewBag.Probability = prediction.Probability;
- }
- catch
- {
- return View();
- }
- return View();
- }
- public static string GetAbsolutePath(string relativePath)
- {
- FileInfo _dataRoot = new FileInfo(typeof(Program).Assembly.Location);
- string assemblyFolderPath = _dataRoot.Directory.FullName;
- string fullPath = Path.Combine(assemblyFolderPath, relativePath);
- return fullPath;
- }
- }
- }
- Now we need to create an index view which will be used to upload image to server and show image classification or prediction result from server. Please add following snippet to your view.
- @{
- ViewData["Title"] = "Home Page";
- }
- <h2>Image Classification in ASP.NET Core Using ML.NET with TensorFlow</h2>
- <hr />
- <div class="row">
- <div class="col-md-6">
- <form method="post" enctype="multipart/form-data">
- <input type="file" name="image" />
- <button class="btn btn-sm btn-primary" type="submit">Classify</button>
- </form>
- </div>
- <div class="col-md-6">
- @if (ViewBag.Probability != null)
- {
- <h3>Prediction</h3>
- <h6>Predicted Label: @ViewBag.PredictedLabel</h6>
- <h6>Probability: @ViewBag.Probability</h6>
- }
-
- </div>
- </div>
- Now try to build and run the ImageClassification.Web application
Demo
This sample ASP.NET Core application takes image form user and classify them and show the result to the user.
Conclusion
So, in this article, we tried to build an ASP.NET Core MVC Web Application which consumes Tensor Flow pre-trained model for Image Classification or Image prediction. In this article, we used ML.NET API to consume the Tensor Flow Deep Learning Model.
Note
Please don't forget to add the Tensor Flow pre-trained model
here into asset>inputs>inceptions folder. The source code doesn't include this due to huge size.
For more detailed information please see
here.
You can also download project source code from
here.