Face API Similar Face Searching Using ASP.Net MVC, AngularJS

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:

 

Author:

Since March 2011, have 4 years of extensive hands on experience of software development.

2 thoughts on “Face API Similar Face Searching Using ASP.Net MVC, AngularJS”

Leave a Reply

Your email address will not be published. Required fields are marked *