{"id":3251,"date":"2017-07-01T01:56:54","date_gmt":"2017-07-01T08:56:54","guid":{"rendered":"https:\/\/www.microsoft.com\/reallifecode\/?p=3251"},"modified":"2020-03-14T20:22:07","modified_gmt":"2020-03-15T03:22:07","slug":"building-image-classification-pipeline-using-serverless-architecture","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/ise\/building-image-classification-pipeline-using-serverless-architecture\/","title":{"rendered":"Building an Image Classification Pipeline Using Serverless Architecture"},"content":{"rendered":"<h2>Background<\/h2>\n<p>The <a href=\"https:\/\/www.icrc.org\">International Committee of the Red Cross<\/a> (ICRC)\u00a0<a href=\"https:\/\/familylinks.icrc.org\/europe\/en\/Pages\/Home.aspx\">Trace-the-Face<\/a>\u00a0program uses photos to match up missing family members who have been separated due to migration and conflict. We have been working with the ICRC to update and extend Trace-the-Face to perform automatic face detection and matching using machine learning, via Microsoft Cognitive Services <a href=\"https:\/\/azure.microsoft.com\/en-us\/services\/cognitive-services\/face\/\">Face API<\/a>.<\/p>\n<p>As we discussed how Trace-the-Face could use computer vision technologies, the ICRC grew more and more intrigued and developed a long list of their challenges that could potentially be solved by the use of computer vision technologies. Through a series of workshops and discussions, we designed a centralized image classification pipeline that could be integrated across the organization to build up a rich, tagged image set over time \u2013 including faces found in any images from any source \u2013 with the intent of addressing many of their computer vision related challenges.<!--more--><\/p>\n<h2>The Solution<\/h2>\n<p>In February, ICRC and Microsoft started a collaboration on <a href=\"http:\/\/imaginem.io\">Imaginem<\/a>, an <strong>image classification pipeline<\/strong> built on top of <a href=\"https:\/\/docs.microsoft.com\/en-us\/azure\/azure-functions\/\">Azure Functions<\/a>. If you&#8217;re not familiar with Azure Functions, it is a framework for building serverless microservices that can easily be deployed to the cloud. Azure Functions is built on top of <a href=\"https:\/\/azure.microsoft.com\/en-us\/services\/app-service\/\">Azure App Service<\/a>, which means it&#8217;s easily manageable and has built-in scaling. Visual Studio supports developing Azure Functions, but you can easily use your favorite text editor to write either C# or JavaScript files.<\/p>\n<p>The choice to go serverless gave us the flexibility to add or remove functions as the pipeline grows. The initial focus was to do general image classification and face detection and matching. \u00a0We have additional plans to incorporate custom image classification and object detection built with ML frameworks like <a href=\"https:\/\/www.microsoft.com\/en-us\/cognitive-toolkit\/\">CNTK<\/a> and <a href=\"https:\/\/www.tensorflow.org\/\">TensorFlow<\/a>.<\/p>\n<p>Our architecture uses message queuing to move images along the pipeline. Each message contains the information needed to move the image to the next step, \u00a0including a link to the image blob and the collected properties about the image classification.<\/p>\n<h4><img decoding=\"async\" class=\"alignnone wp-image-3792\" src=\"\/developerblog\/wp-content\/uploads\/icrc-hackfest-architecture-1-300x140.jpg\" alt=\"\" width=\"1027\" height=\"479\" \/><\/h4>\n<h3>The Code<\/h3>\n<p>Each step in the pipeline is an <a href=\"https:\/\/docs.microsoft.com\/en-us\/azure\/azure-functions\/\">Azure Function<\/a>;\u00a0building the pipeline involved creating each Azure Function, and then adding its input queue name into the pipeline definition.<\/p>\n<p>Initially, you can <a href=\"https:\/\/docs.microsoft.com\/en-us\/azure\/azure-resource-manager\/resource-group-template-deploy\">set these steps pre-deployment<\/a> in the <a href=\"https:\/\/github.com\/CatalystCode\/Imaginem\/blob\/master\/azuredeploy.parameters.json\">ARM deployment parameters<\/a>.<\/p>\n<pre class=\"lang:default decode:true\" title=\"Sample of the pipeline definition in JSON\">    \"pipelineDefinition\": {\r\n      \"value\": \"generalclassification,ocr,facedetection,facecrop,faceprint,facematch,pipelineoutput\"\r\n    },<\/pre>\n<p>If you set the steps post-deployment, you can simply edit them in your Function App&#8217;s <a href=\"https:\/\/docs.microsoft.com\/en-us\/azure\/app-service-web\/web-sites-configure\">environment variables<\/a>.<\/p>\n<p>Below is a simple function that breaks down a message, retrieves the image from <a href=\"https:\/\/azure.microsoft.com\/en-us\/services\/storage\/blobs\/\">Azure Blob Storage<\/a> and then sends it into Microsoft Cognitive Services <a href=\"https:\/\/azure.microsoft.com\/en-us\/services\/cognitive-services\/computer-vision\/\">Computer Vision API<\/a> for image tagging. This example is written in C#, but you could also write it using <a href=\"https:\/\/docs.microsoft.com\/en-us\/azure\/azure-functions\/functions-reference-node\">JavaScript<\/a>.<\/p>\n<pre class=\"lang:c# decode:true\" title=\"A function run.csx sample\">#load \"..\\Common\\FunctionHelper.csx\"\r\n#load \"..\\Common\\ComputerVisionFunctions.csx\"\r\n\r\nusing System.Net.Http.Headers;\r\nusing System.Text;\r\nusing System.Net.Http;\r\nusing System.Web;\r\nusing System.Runtime;\r\nusing Newtonsoft.Json;\r\n\r\nprivate const string ClassifierName = \"generalclassification\";\r\n\r\npublic static void Run(string inputMsg, TraceWriter log)\r\n{\r\n    log.Info(inputMsg);\r\n    PipelineHelper.Process(GeneralClassificationFunction, ClassifierName, inputMsg, log);\r\n}\r\n\r\npublic static dynamic GeneralClassificationFunction(dynamic inputJson, string imageUrl, TraceWriter log)\r\n{\r\n    var parameters = inputJson.job_definition.image_parameters;\r\n    var response = ComputerVisionFunctions.AnalyzeImageAsync(imageUrl, log).Result;\r\n    return JsonConvert.DeserializeObject(response);\r\n}<\/pre>\n<p>The code includes calls to helper methods\u00a0that allow the function to take part in the pipeline in a well-defined way. They ensure adherence to the message contract and then advance the message to the next defined queue. These methods\u00a0are <a href=\"https:\/\/github.com\/CatalystCode\/Imaginem-Functions\/tree\/master\">defined in the GitHub repo<\/a>, if you want to see what they do in the context of the pipeline.<\/p>\n<p>The first parameter in the function&#8217;s <em>Run<\/em>\u00a0method is defined as <span class=\"lang:c# decode:true crayon-inline \">string inputMsg<\/span>\u00a0and is tied to an Azure Function,\u00a0<a href=\"https:\/\/docs.microsoft.com\/en-us\/azure\/azure-functions\/functions-bindings-storage-queue\">QueueTrigger<\/a>. The framework will watch the Azure Storage Queue that is defined in the function.json file for new messages and trigger the function when a new message is found.<\/p>\n<p>Here is a sample of a function definition for a <em>QueueTrigger<\/em> from the <a href=\"https:\/\/docs.microsoft.com\/en-us\/azure\/azure-functions\/functions-bindings-storage-queue\">documentation<\/a>:<\/p>\n<pre class=\"lang:default decode:true\">{\r\n    \"type\": \"queueTrigger\",\r\n    \"direction\": \"in\",\r\n    \"name\": \"&lt;The name used to identify the trigger data in your code&gt;\",\r\n    \"queueName\": \"&lt;Name of queue to poll&gt;\",\r\n    \"connection\":\"&lt;Name of app setting - see below&gt;\"\r\n}<\/pre>\n<p>To add a custom step to the pipeline:<\/p>\n<ol>\n<li>Create a new Azure Storage Queue in the attached Storage Account for your Function App<\/li>\n<li>Add a new function to the Function App with a <em>QueueTrigger<\/em><\/li>\n<li>Set the <span class=\"lang:default decode:true crayon-inline \">name<\/span>\u00a0\u00a0field to &#8220;inputMsg&#8221; to match our\u00a0<em>Run<\/em>\u00a0method<\/li>\n<li>Set the <span class=\"lang:default decode:true crayon-inline \">queueName<\/span>\u00a0\u00a0field to the name of the input queue that you created in step 1<\/li>\n<li>Set the <span class=\"lang:default decode:true crayon-inline \">connection<\/span>\u00a0\u00a0field to the Storage Account name defined in <em>appsettings.json<\/em><\/li>\n<\/ol>\n<p>Keep in mind that you can perform any action on an image in this step of the pipeline. For example, we&#8217;ve had partners use a step to combine multiple images into a grid for submission to the Cognitive Services Face API in order to save on API calls.<\/p>\n<h2>Conclusion<\/h2>\n<p>Through our collaboration with ICRC, we created a nice, tidy reusable framework for processing images using message queues. It detects faces, searches for similar or matching faces, identifies objects, and extracts text characters from images. The results can be easily extracted from the JSON object or retrieved from the SQL database.<\/p>\n<p>Imaginem could be reused in many scenarios that require facial recognition and matching.\u00a0 We welcome your feedback and PRs!<\/p>\n<p><strong>Note<\/strong>: This code was built using preview tools for Azure Functions in Visual Studio 2015. The Azure Functions team recommends using Visual Studio 2017 (<a href=\"https:\/\/docs.microsoft.com\/en-us\/azure\/azure-functions\/functions-create-your-first-function-visual-studio\">read more<\/a>).<\/p>\n<h4>Resources<\/h4>\n<ul>\n<li><a href=\"http:\/\/imaginem.io\">Imaginem project website<\/a><\/li>\n<li><a href=\"https:\/\/github.com\/CatalystCode\/Imaginem\">GitHub Repo for Imaginem code<\/a><\/li>\n<li><a href=\"https:\/\/docs.microsoft.com\/en-us\/azure\/azure-functions\">Azure Functions<\/a><\/li>\n<li><a href=\"https:\/\/azure.microsoft.com\/en-us\/services\/cognitive-services\">Cognitive Services<\/a><\/li>\n<\/ul>\n","protected":false},"excerpt":{"rendered":"<p>We collaborated on an image classification pipeline to perform automatic face detection and matching using machine learning via Microsoft Cognitive Services Face API. Our project was built with Azure Functions to process images using message queues. <\/p>\n","protected":false},"author":21364,"featured_media":10904,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[10,19],"tags":[60,66,97,239,250],"class_list":["post-3251","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-azure-app-services","category-machine-learning","tag-azure","tag-azure-blob-storage","tag-azure-web-apps","tag-machine-learning-ml","tag-microsoft-cognitive-services"],"acf":[],"blog_post_summary":"<p>We collaborated on an image classification pipeline to perform automatic face detection and matching using machine learning via Microsoft Cognitive Services Face API. Our project was built with Azure Functions to process images using message queues. <\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/ise\/wp-json\/wp\/v2\/posts\/3251","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/ise\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/ise\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/ise\/wp-json\/wp\/v2\/users\/21364"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/ise\/wp-json\/wp\/v2\/comments?post=3251"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/ise\/wp-json\/wp\/v2\/posts\/3251\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/ise\/wp-json\/wp\/v2\/media\/10904"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/ise\/wp-json\/wp\/v2\/media?parent=3251"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/ise\/wp-json\/wp\/v2\/categories?post=3251"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/ise\/wp-json\/wp\/v2\/tags?post=3251"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}