Create and run your first Azure Function

Create a function app

Prerequisites

To complete this tutorial, install: the latest version of Visual Studio 2017, including the ASP.NET and web or Azure development workload.

VS Workload

You must also retreive an API key for Azure Cognitive Services. Select Get API Key for the Face API.

Cognitive Services API key and URL home

Save your API keys and the base URL that your account has access to. You’ll need them later.

Create an Azure Functions project in Visual Studio

In Visual Studio:

  • Create a project by selecting File > New > Project.
  • Select Visual C# > Cloud > Azure Functions
    Hint: If you do not see the Azure Functions project type, modify your Visual Studio installation to include the Azure development workload.
  • Click OK.

    New Project dialog selecting Azure Functions

Create the function

Use the project creation dialog to select an Http Trigger.

Http Trigger from Functions dialog

Make changes to the code

First, open up the file called local.settings.json. Replace its contents with this:

{
    "IsEncrypted": false,
    "Values": {
      "AzureWebJobsStorage": "UseDevelopmentStorage=true",
      "AzureWebJobsDashboard": "UseDevelopmentStorage=true",
      "CognitiveServicesUrlBase": "YOUR-URL-BASE-HERE",
      "CognitiveServicesKey": "YOUR-KEY-HERE"
    }
}

The values YOUR-URL-BASE-HERE and YOUR-KEY-HERE need to be the base URL and key that you got from Cognitive Services above.

Next, replace the default template C# code with the following:

using System.IO;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions.Http;
using Microsoft.AspNetCore.Http;
using Microsoft.Azure.WebJobs.Host;
using Newtonsoft.Json;
using System.Net.Http;
using System.Threading.Tasks;
using System;

namespace FunctionApp9
{
    public class FaceRectangle
    {
        public int Height { get; set; }
        public int Width { get; set; }
        public int Top { get; set; }
        public int Left { get; set; }
    }
    public class Emotion
    {
        public float Anger { get; set; }
        public float Contempt { get; set; }
        public float Disgust { get; set; }
        public float Fear { get; set; }
        public float Happiness { get; set; }
        public float Neutral { get; set; }
        public float Sadness { get; set; }
        public float Surprise { get; set; }
    }
    public class FaceAttributes
    {
        public Emotion Emotion { get; set; }
    }
    public class Face
    {
        public FaceRectangle FaceRectangle { get; set; }
        public FaceAttributes FaceAttributes { get; set; }
    }

    public static class Function1
    {
        public static string ProcessFaces(Face[] faces)
        {
            int angerCount = 0;
            int contemptCount = 0;
            int disgustCount = 0;
            int fearCount = 0;
            int happinessCount = 0;
            int neutralCount = 0;
            int sadnessCount = 0;
            int surpriseCount = 0;

            foreach (var face in faces)
            {
                var emotion = face.FaceAttributes.Emotion;

                if (emotion.Anger > 0.1)
                {
                    angerCount += 1;
                }
                if (emotion.Contempt > 0.1)
                {
                    contemptCount += 1;
                }
                if (emotion.Disgust > 0.1)
                {
                    disgustCount += 1;
                }
                if (emotion.Fear > 0.1)
                {
                    fearCount += 1;
                }
                if (emotion.Happiness > 0.1)
                {
                    happinessCount += 1;
                }
                if (emotion.Neutral > 0.1)
                {
                    neutralCount += 1;
                }
                if (emotion.Sadness > 0.1)
                {
                    sadnessCount += 1;
                }
                if (emotion.Surprise > 0.1)
                {
                    surpriseCount += 1;
                }
            }

            return $"For {faces.Length} detected faces:\n\n" +
                $"- {angerCount} were angry\n" +
                $"- {contemptCount} showed contempt\n" +
                $"- {disgustCount} showed disgust\n" +
                $"- {fearCount} showed fear\n" +
                $"- {happinessCount} showed happiness\n" +
                $"- {neutralCount} were neutral\n" +
                $"- {sadnessCount} were sad\n" +
                $"- {surpriseCount} were surprised.";
        }

        public static async Task<Face[]> GetFacialAnalysisResults(string imageUrl)
        {
            string faceUrlBase = Environment.GetEnvironmentVariable("CognitiveServicesUrlBase");
            string key1 = Environment.GetEnvironmentVariable("CognitiveServicesKey1");

            // FaceLandmarks gives us a rectangle containing the face.
            // FaceAttributes=emotion will tell Cognitive Services to read the emotion of a face.
            string reqParams = "?returnFaceLandmarks=true&returnFaceAttributes=emotion";

            var client = new HttpClient();
            client.DefaultRequestHeaders.Add("Ocp-Apim-Subscription-Key", key1);

            var json = "{\"url\":\"" + $"{imageUrl}" + "\"}";

            var resp = await client.PutAsync(faceUrlBase + reqParams, new StringContent(json));
            var jsonResponse = await resp.Content.ReadAsStringAsync();

            var faces = JsonConvert.DeserializeObject<Face[]>(jsonResponse);

            return faces;
        }

        public static string GetImageUrl(HttpRequest req)
        {
            string imageUrl = req.Query["url"];
            string requestBody = new StreamReader(req.Body).ReadToEnd();
            dynamic data = JsonConvert.DeserializeObject(requestBody);

            return imageUrl = imageUrl ?? data?.name;
        }

        [FunctionName("Function1")]
        public static async Task<IActionResult> Run([HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)]HttpRequest req, TraceWriter log)
        {
            log.Info("C# HTTP trigger function processed a request.");
            
            var imageUrl = GetImageUrl(req);
            if (string.IsNullOrWhiteSpace(imageUrl))
            {
                return new BadRequestObjectResult("Please pass an image URL on the query string or in the request body");
            }
            else
            {
                var faces = await GetFacialAnalysisResults(imageUrl);
                var message = ProcessFaces(faces);
                return new OkObjectResult($"{message}");
            }
        }
    }
}

This code will extract an image URL, upload it to Azure Cognitive services, and give a quick report on some of the basic facial analysis capabilities that Azure Cognitive Services has. The base URL and key for using Azure Cognitive Services are now in a configuration file. When you eventually publish this to Azure, the same C# code will read a configuration file hosted there. There’s no need to change that code!

Test the function locally

  • To test your function, press F5. If prompted, accept the request from Visual Studio to download and install Azure Functions Core (CLI) tools. You may also need to enable a firewall exception so that the tools can handle HTTP requests.

  • Copy the URL of your function from the Azure Functions runtime output.

  • Paste the URL for the HTTP request into your browser’s address bar. Append the query string ?url=<image-url>, where <image-url> is a URL to an image on the web, to this URL and execute the request. Here’s what it reports for the infamous “Distracted Boyfriend” stock photo:

Distracted Boyfriend stock photo

(result)

Fun! Now, it’s time to publish the function to Azure.


I ran into an issue I created the project and ran the app