In the following article, we'll see how to develop a program that uses the powerful Azure Cognitive Services to get a list of tags from an image, and stores them to further create an application capable of searching images through keywords. The presented source code will be written in C#, and split in two applications: The first one will process a directory, searching for JPEG files and analyzing them through Azure. Cognitive Services results will be saved to an XML file. The second program will simply allow the user to enter a keyword, and scan the XML file to find matches. When a match is found, the images will be shown in a WPF DataGrid.
Before we begin, we must create a Cognitive Service resource on Azure. The following section will cover how to do that.
Create an Azure Computer Vision API resourceLog in to the Azure portal, then click the "Create a resource" link. In the search bar, enter "Computer Vision API", and confirm. Now our browser will show something like the following screenshot. Click "Create" to proceed.
Now we can assign a name, a geographic location (select the nearest for less latency), and a pricing tier. For the purposes of the article, the free F0 tier will be a good choice.
In creation confirming, Azure will assign two API keys, which applications need to know and use to access the services functions.
To get the keys, click on the newly created resource, then "Keys" link under "Resource Management"
Save one of those keys (a single key will be required) for the following steps.
Write a C# class to use Azure resourceAs stated in the Introduction, our source code will be composed of two parts. Here we'll see how to write a simple console app to query Azure Cognitive Services, feeding them with single images we wish to be "seen". The Azure AI will return a set of tags, that will be saved in an XML file for future reference, like a catalog of images along with their descriptive texts.
To achieve that feature, we need first of all a class to communicate with Azure, and some prerequisites.
Let's open Visual Studio, and create a new solution. Add a first project, which will be a Console App. Then, save it and go to Nuget Manager, where two additional packages need to be installed, being them Microsoft.ProjectOxford.Visionand Newtonsoft.Json
The following class is used to send a Stream to Azure Computer Vision, and to read the returned values:
- using Microsoft.ProjectOxford.Vision;
- using Microsoft.ProjectOxford.Vision.Contract;
- using System.Collections.Generic;
- using System.IO;
- using System.Linq;
- using System.Threading.Tasks;
- namespace ivDataGather {
- class MicrosoftCognitiveCaptionService {
- private static readonly string ApiKey = "<INSERT YOUR API KEY HERE>";
- private static readonly string ApiEndpoint = "https://westus.api.cognitive.microsoft.com/vision/v1.0";
- private static readonly VisualFeature[] VisualFeatures = {
- VisualFeature.Description,
- VisualFeature.Tags
- };
- public async Task < string > GetTagsAsync(Stream stream) {
- var client = new VisionServiceClient(ApiKey, ApiEndpoint);
- var result = await client.AnalyzeImageAsync(stream, VisualFeatures);
- return ProcessAnalysisResult(result);
- }
- private static string ProcessAnalysisResult(AnalysisResult result) {
- string message = result ? .Description ? .Captions.Count() > 0 ? string.Join(", ", result ? .Description ? .Captions.Select(x => x.Text)) : "";
- string tags = result ? .Tags ? .Count() > 0 ? string.Join(", ", result ? .Tags ? .Select(x => x.Name)) : "";
- var list = new List < string > ();
- message.Split(' ').ToList().ForEach(x => {
- list.Add(x.Trim());
- });
- tags.Split(',').ToList().ForEach(x => {
- list.Add(x.Trim());
- });
- return string.Join(",", list.Distinct().ToList());
- }
- }
- }
The first async method, GetTagsAsync, will create a new VisionServiceClient object, given the service API key and the endpoint to use (which is the URL of the service, which could be extracted from Azure, and its based on the chosen geographical location). Here we will query Azure on two features: VisualFeature.Description and VisualFeature.Tags. Those enum values will inform the service we are interested in, reading back only that information.
Once we have received a result, GetTagsAsync will call on a second method, ProcessAnalysisResult. The method will return a string object, based on the Description and Tags properties, which will be parsed and taken as a distinct list, comma separated, transformed in a one-line response.
Develop a console app to process an images folderWith those premises, developing the further steps is trivial. Here follows the console part, which simply parse a given directory for JPEG files, sending them to Azure and awaiting results. When they come back to us, those results will be stored in a TaggedImage list (a custom class we'll see in a moment), to be saved in a serialized XML at the end of the procedure.
- using System;
- using System.Collections.Generic;
- using System.IO;
- using System.Linq;
- using System.Xml.Serialization;
- namespace ivDataGather {
- class Program {
- static async void ParseDirectory(string directory, string catalogOutp) {
- var itemList = new List < TaggedImage > ();
- var cc = new MicrosoftCognitiveCaptionService();
- foreach(var f in Directory.GetFiles(directory, "*.jpg")) {
- var r = "";
- var ms = new FileStream(f, FileMode.Open);
- r = await cc.GetTagsAsync(ms);
- itemList.Add(new TaggedImage() {
- FilePath = f,
- Tags = r.Split(',').ToList()
- });
- Console.WriteLine(f + "\n" + r + "\n");
- ms.Dispose();
- }
- cc = null;
- XmlSerializer serialiser = new XmlSerializer(typeof(List < TaggedImage > ));
- TextWriter fileStream = new StreamWriter(catalogOutp);
- serialiser.Serialize(fileStream, itemList);
- fileStream.Close();
- }
- static void Main(string[] args) {
- ParseDirectory(@ "C:\<input_folder>\images", @ "C:\<any_output_folder>\ivcatalog.xml");
- Console.ReadLine();
- }
- }
- }
- using System.Collections.Generic;
- using System.IO;
- using System.Windows.Media.Imaging;
- namespace ivDataGather {
- public class TaggedImage {
- public string FilePath {
- get;
- set;
- }
- public string FileName {
- get {
- return GetFileAttributes() ? .Name;
- }
- }
- public string FileExtension {
- get {
- return GetFileAttributes() ? .Extension;
- }
- }
- public string FileDirectory {
- get {
- return GetFileAttributes() ? .DirectoryName;
- }
- }
- public List < string > Tags {
- get;
- set;
- }
- public BitmapImage Image {
- get {
- return new BitmapImage(new System.Uri(this.FilePath));
- }
- }
- private FileInfo GetFileAttributes() {
- return new FileInfo(this.FilePath);
- }
- }
- }


Join the conversation! Your thoughts help the community grow.