In this tutorial, we will learn about Face API and create a simple ASP.Net MVC application to search similar face from collection of face by using Face API.
Contents already Discussed:
- What is Cognitive Services?
- What is Face API?
- Sign Up for Face API
- Create ASP.Net MVC Sample Application
- Add AngularJS
- Install & Configure the Face API
Please follow the below link to get all those questions in my previous post: http://shashangka.com/2017/01/08/face-api-using-asp-net-mvc
Contents to Focused:
- Upload Multiple Candidate Face
- List Detected Faces
- Query Face
- Summary
Open existing sample application> Right Click > Controller > Add Controller > Controller > FaceSimilarController
MVC Controller: This is where we are performing our main operation. First of all get FaceServiceKey Value from web.config by ConfigurationManager.AppSettings.
private static string ServiceKey = ConfigurationManager.AppSettings[“FaceServiceKey”];
Here in MVC Controller we have two main method to performing the face detection operation. One is HttpPost method, which is using for uploading the image file to folder and the other one is HttpGet method is using to get uploaded image and detecting faces by calling API Service.
Both methods are getting called from client script while uploading image to detect faces. Let’s get explained in steps.
Multiple Face Upload: This method is responsible for uploading multiple face image.
[HttpPost] public JsonResult SaveCandidateFiles() { //Get Requested File Collection //Create New Folder if not Exist //Clear Existing File in Folder //Create Instance of Service Client by passing Servicekey as parameter in constructor //Create & Save Cropped Detected Faces }
Face Detection: This method is responsible for detecting the faces from uploaded images.
[HttpGet] public async Task<dynamic> GetDetectedFaces() { //Get Files in Directory //Create FaceList //Add Face to FaceList }
Find Similar Faces: This method is responsible for find similar faces from facelist.
[HttpPost] public async Task<dynamic> FindSimilar() { //Upload Face to Search //Find similar faces for each face //Update find similar results collection for rendering }
Finally Full MVC Controller:
public class FaceSimilarController : Controller { private static string ServiceKey = ConfigurationManager.AppSettings["FaceServiceKey"]; private static string directory = "../UploadedFiles"; private static string _faceListName = string.Empty; private static ObservableCollection<vmFace> _facesCollection = new ObservableCollection<vmFace>(); private static ObservableCollection<vmFindSimilarResult> _findSimilarCollection = new ObservableCollection<vmFindSimilarResult>(); public ObservableCollection<vmFace> FacesCollection { get { return _facesCollection; } } public ObservableCollection<vmFindSimilarResult> FindSimilarCollection { get { return _findSimilarCollection; } } // GET: FaceSimilar public ActionResult Index() { return View(); } [HttpPost] public async Task<JsonResult> SaveCandidateFiles() { string message = string.Empty, fileName = string.Empty, actualFileName = string.Empty; bool flag = false; //Requested File Collection HttpFileCollection fileRequested = System.Web.HttpContext.Current.Request.Files; if (fileRequested != null) { //Create New Folder CreateDirectory(); //Clear Existing File in Folder ClearDirectory(); for (int i = 0; i < fileRequested.Count; i++) { var file = Request.Files[i]; actualFileName = file.FileName; fileName = Guid.NewGuid() + Path.GetExtension(file.FileName); int size = file.ContentLength; string FullImgPath = Path.Combine(Server.MapPath(directory), fileName); try { file.SaveAs(FullImgPath); message = "File uploaded successfully"; flag = true; if (FullImgPath != "") { using (var fStream = System.IO.File.OpenRead(FullImgPath)) { // User picked one image var imageInfo = UIHelper.GetImageInfoForRendering(FullImgPath); // Create Instance of Service Client by passing Servicekey as parameter in constructor var faceServiceClient = new FaceServiceClient(ServiceKey); Face[] faces = await faceServiceClient.DetectAsync(fStream, true, true, new FaceAttributeType[] { FaceAttributeType.Gender, FaceAttributeType.Age, FaceAttributeType.Smile, FaceAttributeType.Glasses }); if (faces.Count() > 0) { Bitmap CroppedFace = null; foreach (var face in faces) { //Create & Save Cropped Images var croppedImg = Convert.ToString(Guid.NewGuid()) + ".jpeg" as string; var croppedImgPath = directory + '\\' + croppedImg as string; var croppedImgFullPath = Server.MapPath(directory) + '\\' + croppedImg as string; CroppedFace = CropBitmap( (Bitmap)Image.FromFile(FullImgPath), face.FaceRectangle.Left, face.FaceRectangle.Top, face.FaceRectangle.Width, face.FaceRectangle.Height); CroppedFace.Save(croppedImgFullPath, ImageFormat.Jpeg); if (CroppedFace != null) ((IDisposable)CroppedFace).Dispose(); } //Clear Query File DeleteFile(FullImgPath); } } } } catch (Exception) { message = "File upload failed! Please try again"; } } } return new JsonResult { Data = new { Message = message, Status = flag } }; } [HttpGet] public async Task<dynamic> GetCandidateFiles() { string message = string.Empty; var faceServiceClient = new FaceServiceClient(ServiceKey); FacesCollection.Clear(); DirectoryInfo dir = new DirectoryInfo(Path.Combine(Server.MapPath(directory))); FileInfo[] files = null; files = dir.GetFiles().OrderBy(p => p.CreationTime).ToArray(); if (files.Count() > 0) { _faceListName = Guid.NewGuid().ToString(); await faceServiceClient.CreateFaceListAsync(_faceListName, _faceListName, "face list for sample"); foreach (var item in files) { var imgPath = Server.MapPath(directory) + '\\' + item.Name as string; try { using (var fStream = System.IO.File.OpenRead(imgPath)) { var faces = await faceServiceClient.AddFaceToFaceListAsync(_faceListName, fStream); FacesCollection.Add(new vmFace { ImagePath = imgPath, FileName = item.Name, FilePath = directory + '\\' + item.Name, FaceId = Convert.ToString(faces.PersistedFaceId) }); } } catch (FaceAPIException fe) { //do exception work message = fe.ToString(); } } } else { message = "No files to Detect!! Please Upload Files"; } return new JsonResult { Data = new { Message = message, FacesCollection = FacesCollection }, JsonRequestBehavior = JsonRequestBehavior.AllowGet }; } [HttpPost] public async Task<dynamic> FindSimilar() { string message = string.Empty, fileName = string.Empty, actualFileName = string.Empty; bool flag = false; var faceServiceClient = new FaceServiceClient(ServiceKey); FindSimilarCollection.Clear(); //Requested File Collection HttpFileCollection fileRequested = System.Web.HttpContext.Current.Request.Files; if (fileRequested != null) { for (int i = 0; i < fileRequested.Count; i++) { var file = Request.Files[i]; actualFileName = file.FileName; fileName = Guid.NewGuid() + Path.GetExtension(file.FileName); int size = file.ContentLength; try { file.SaveAs(Path.Combine(Server.MapPath(directory), fileName)); var imgPath = Server.MapPath(directory) + '/' + fileName as string; using (var fStream = System.IO.File.OpenRead(imgPath)) { var faces = await faceServiceClient.DetectAsync(fStream); //Find similar faces for each face foreach (var f in faces) { var faceId = f.FaceId; try { //Call find similar REST API, the result contains all the face ids which similar to the query face const int requestCandidatesCount = 10; var result = await faceServiceClient.FindSimilarAsync(faceId, _faceListName, requestCandidatesCount); var findResult = new vmFindSimilarResult(); findResult.Faces = new ObservableCollection<vmFace>(); findResult.QueryFace = new vmFace() { ImagePath = imgPath, FileName = fileName, FilePath = directory + '/' + fileName, Top = f.FaceRectangle.Top, Left = f.FaceRectangle.Left, Width = f.FaceRectangle.Width, Height = f.FaceRectangle.Height, FaceId = faceId.ToString(), }; //Update find similar results collection for rendering foreach (var fr in result) { findResult.Faces.Add(FacesCollection.First(ff => ff.FaceId == fr.PersistedFaceId.ToString())); } //Update UI FindSimilarCollection.Add(findResult); message = Convert.ToString("Total " + findResult.Faces.Count() + " faces are detected."); flag = true; } catch (FaceAPIException fex) { message = fex.ErrorMessage; } } } } catch (Exception ex) { ex.ToString(); } } } return new JsonResult { Data = new { Message = message, SimilarFace = FindSimilarCollection, Status = flag } }; } public Bitmap CropBitmap(Bitmap bitmap, int cropX, int cropY, int cropWidth, int cropHeight) { Rectangle rect = new Rectangle(cropX, cropY, cropWidth, cropHeight); Bitmap cropped = bitmap.Clone(rect, bitmap.PixelFormat); return cropped; } public void CreateDirectory() { bool exists = System.IO.Directory.Exists(Server.MapPath(directory)); if (!exists) { try { Directory.CreateDirectory(Server.MapPath(directory)); } catch (Exception ex) { ex.ToString(); } } } public void ClearDirectory() { DirectoryInfo dir = new DirectoryInfo(Path.Combine(Server.MapPath(directory))); var files = dir.GetFiles(); if (files.Length > 0) { try { foreach (FileInfo fi in dir.GetFiles()) { GC.Collect(); GC.WaitForPendingFinalizers(); fi.Delete(); } } catch (Exception ex) { ex.ToString(); } } } public void DeleteFile(string FullImgPath) { if (FullImgPath != "") { try { //Clear Query File if ((System.IO.File.Exists(FullImgPath))) { GC.Collect(); GC.WaitForPendingFinalizers(); System.IO.File.Delete(FullImgPath); } } catch (Exception ex) { ex.ToString(); } } } }
MVC View:
@{ ViewBag.Title = "Find Face Similar"; } <div ng-controller="faceSimilarCtrl"> <h3>{{Title}}</h3> <div class="loadmore"> <div ng-show="loaderMoreupl" ng-class="result"> <img src="~/Content/ng-loader.gif" /> {{uplMessage}} </div> </div> <div class="clearfix"></div> <table style="width:100%"> <tr> <th><h4>Select Multiple Candidate Face</h4></th> <th><h4>Select Query Face</h4></th> </tr> <tr> <td style="width:60%" valign="top"> <form novalidate name="f1"> <input type="file" name="file" accept="image/*" multiple onchange="angular.element(this).scope().selectCandidateFileforUpload(this.files)" required /> </form> <div class="clearfix"></div> <hr /> <h4>Face Collection {{resultFaceMessage}}</h4> <input type="submit" value="Reload Faces" ng-click="GetCandidateFile()" class="btn btn-success"/> <div class="clearfix"></div> <div class="loadmore"> <div ng-show="loaderMore" ng-class="result"> <img src="~/Content/ng-loader.gif" /> {{faceMessage}} </div> </div> <hr /> <div ng-repeat="item in UploadedFiles" class="pull-left"> <img ng-src="{{item.FilePath}}" width="100" class="media-list" /> </div> <div ng-hide="UploadedFiles.length">No face found!!</div> </td> <td style="width:40%" valign="top"> <form novalidate name="f2"> <input type="file" name="file" accept="image/*" onchange="angular.element(this).scope().selectFileforFindSimilar(this.files)" required /> </form> <div class="clearfix"></div> <div class="loadmore"> <div ng-show="loaderMorefacefinder" ng-class="result"> <img src="~/Content/ng-loader.gif" /> {{facefinderMessage}} </div> </div> <hr /> <h4>Result</h4> <p>{{resultMessage}}</p> <div class="clearfix"></div> <div class="facePreview_thumb_big"> <img ng-src="{{QueryFace}}" width="200" /> </div> <hr /> <div ng-repeat="item in SimilarFace" class="pull-left"> <img ng-src="{{item.FilePath}}" width="100" class="media-list" /> </div> <div ng-hide="SimilarFace.length">No similar face found!!</div> </td> </tr> </table> </div> @section NgScript{ <script src="~/ScriptsNg/FaceSimilarCtrl.js"></script> }
Angular Controller:
angular.module('myFaceApp', []) .controller('faceSimilarCtrl', function ($scope, FileUploadService) { $scope.Title = 'Microsoft FaceAPI - Find Face Similar'; $scope.resultMessage = 'No result found!!'; $scope.SelectedFileForUpload = null; $scope.UploadedFiles = []; $scope.SimilarFace = []; //File Select & Save $scope.selectCandidateFileforUpload = function (file) { $scope.SelectedFileForUpload = file; $scope.loaderMoreupl = true; $scope.uplMessage = 'Uploading, please wait....!'; $scope.result = "color-red"; //Save File var uploaderUrl = "/FaceSimilar/SaveCandidateFiles"; var fileSave = FileUploadService.UploadFile($scope.SelectedFileForUpload, uploaderUrl); fileSave.then(function (response) { if (response.data.Status) { $scope.GetCandidateFile(); angular.forEach(angular.element("input[type='file']"), function (inputElem) { angular.element(inputElem).val(null); }); $scope.f1.$setPristine(); //$scope.uplMessage = response.data.Message; $scope.loaderMoreupl = false; } }, function (error) { console.warn("Error: " + error); }); } $scope.GetCandidateFile = function () { $scope.loaderMore = true; $scope.faceMessage = 'Preparing, please wait....!'; $scope.result = "color-red"; var fileUrl = "/FaceSimilar/GetCandidateFiles"; var fileView = FileUploadService.GetUploadedFile(fileUrl); fileView.then(function (response) { $scope.UploadedFiles = response.data.FacesCollection; $scope.resultFaceMessage = response.data.Message; $scope.loaderMore = false; }, function (error) { console.warn("Error: " + error); }); }; $scope.selectFileforFindSimilar = function (file) { $scope.SelectedFileForUpload = file; $scope.loaderMorefacefinder = true; $scope.facefinderMessage = 'Preparing, detecting faces, please wait....!'; $scope.result = "color-red"; //Find Similar Face var uploaderUrl = "/FaceSimilar/FindSimilar"; var fileSave = FileUploadService.UploadFile($scope.SelectedFileForUpload, uploaderUrl); fileSave.then(function (response) { if (response.data.Status) { $scope.QueryFace = response.data.SimilarFace[0].QueryFace.FilePath; $scope.SimilarFace = response.data.SimilarFace[0].Faces; angular.forEach(angular.element("input[type='file']"), function (inputElem) { angular.element(inputElem).val(null); }); $scope.f2.$setPristine(); $scope.resultMessage = response.data.Message; $scope.loaderMoreupl = false; } }, function (error) { console.warn("Error: " + error); }); } }) .factory('FileUploadService', function ($http, $q) { var fact = {}; fact.UploadFile = function (files, uploaderUrl) { var formData = new FormData(); angular.forEach(files, function (f, i) { formData.append("file", files[i]); }); var request = $http({ method: "post", url: uploaderUrl, data: formData, withCredentials: true, headers: { 'Content-Type': undefined }, transformRequest: angular.identity }); return request; } fact.GetUploadedFile = function (fileUrl) { return $http.get(fileUrl); } return fact; })
Summary: You have just seen how to call Face API to detect & find faces in FaceList. Hope this will help to make application more smart and intelligent.
References:
- https://www.microsoft.com/cognitive-services/en-us/face-api/documentation/get-started-with-face-api/GettingStartedwithFaceAPIinCSharp
- https://www.microsoft.com/cognitive-services/en-us/face-api
- https://staging.www.projectoxford.ai/
- https://github.com/ShashangkaShekhar/ProjectOxford-ClientSDK
kristan rae arcino says:
You didn’t discuss the changes you should have for the models?
Where did you get vmFindSimilarResult?
Ali Hassan says:
vmFindSimilarResult() sir i cannot manage the defination of this class plz u provide the code of this class ???????
i am so confuse ..!!!!