The Ink Recognizer is one of the new Cognitive Services announced and as mentioned in this post, Azure Ink Recognizer is an AI service in the Vision Category that recognizes digital ink content, such as handwriting, shapes, and ink document layout. In this three-part series, we are going to see how to use the Azure Ink Recognizer to transform handwriting to text. In the first part, we learned about Ink stroke data and how to generate them. In the second part, we learned how to create the Azure Recognizer service. In this tutorial, we are going to learn how to use the rest API calls to get our results.
Prerequisites
- Read part 1 of the series here.
- Read part 2 of the series here.
- To run the sample code you must have Visual Studio 2017 and above installed.
Create the application
Open Visual Studio and create a new console application. Add the following constants in the beginning.
static string subscriptionKey = "{REPLACE WITH YOUR KEY}"; static string endpoint = "{REPLACE WITH YOUR ENDPOINT}"; static string inkRecognitionUrl = "/inkrecognizer/v1.0-preview/recognize"; static readonly string dataPath = @"InkData/sample_Ink.json";
Replace the subscription key and endpoint with the values you collected in the previous tutorial when you created the Ink Recognizer service. Then create a folder and add the .json file you exported from the tutorial part 1 or one that you downloaded from GitHub. Add the file path in the dataPath variable.
Then replace the rest of the file with the following code under Program and run it.
static async Task<string> Request(string apiAddress, string endpoint, string subscriptionKey, string requestData) { using (HttpClient client = new HttpClient { BaseAddress = new Uri(apiAddress) }) { System.Net.ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12 | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls; client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); client.DefaultRequestHeaders.Add("Ocp-Apim-Subscription-Key", subscriptionKey); var content = new StringContent(requestData, Encoding.UTF8, "application/json"); var res = await client.PutAsync(endpoint, content); if (res.IsSuccessStatusCode) { return await res.Content.ReadAsStringAsync(); } else { return $"ErrorCode: {res.StatusCode}"; } } } static void recognizeInk(string requestData) { var result = Request( endpoint, inkRecognitionUrl, subscriptionKey, requestData).Result; dynamic jsonObj = Newtonsoft.Json.JsonConvert.DeserializeObject(result); System.Console.WriteLine(jsonObj); } public static JObject LoadJson(string fileLocation) { var jsonObj = new JObject(); using (StreamReader file = File.OpenText(fileLocation)) using (JsonTextReader reader = new JsonTextReader(file)) { jsonObj = (JObject)JToken.ReadFrom(reader); } return jsonObj; } static void Main(string[] args) { var requestData = LoadJson(dataPath); string requestString = requestData.ToString(Newtonsoft.Json.Formatting.None); recognizeInk(requestString); System.Console.WriteLine("\nPress any key to exit "); System.Console.ReadKey(); }
The result will be similar to the following. As you can see although it recognized and compared several versions of Hello from o or zero for the final letter, the final recommendation is the word Hello which is correct!
{ "recognitionUnits": [ { "boundingRectangle": { "height": 0.009999999776482582, "topX": 958, "topY": 3, "width": 0.009999999776482582 }, "category": "inkDrawing", "center": { "x": 958.0700073242188, "y": 3 }, "class": "leaf", "confidence": 0.37562236189842224, "id": 1, "parentId": 0, "points": [ { "x": 958, "y": 3 }, { "x": 958.1500244140625, "y": 3 } ], "recognizedObject": "line", "rotatedBoundingRectangle": [ { "x": 958, "y": 3 }, { "x": 958.1500244140625, "y": 3 }, { "x": 958.1500244140625, "y": 3.1500000953674316 }, { "x": 958, "y": 3.1500000953674316 } ], "rotationAngle": 0, "strokeIds": [ 8 ] }, { "alternates": [ { "category": "inkWord", "recognizedString": "Hell O" }, { "category": "inkWord", "recognizedString": "Hell o" }, { "category": "inkWord", "recognizedString": "Hell 0" }, { "category": "inkWord", "recognizedString": "Help O" }, { "category": "inkWord", "recognizedString": "Help o" }, { "category": "inkWord", "recognizedString": "Help 0" }, { "category": "inkWord", "recognizedString": "Hall O" }, { "category": "inkWord", "recognizedString": "Hall o" }, { "category": "inkWord", "recognizedString": "Hallo" } ], "boundingRectangle": { "height": 241.00999450683594, "topX": 253, "topY": 236, "width": 727.010009765625 }, "category": "inkWord", "class": "leaf", "id": 5, "parentId": 4, "recognizedText": "Hello", "rotatedBoundingRectangle": [ { "x": 276.1000061035156, "y": 209.72000122070312 }, { "x": 994.4600219726562, "y": 277.3900146484375 }, { "x": 970.5499877929688, "y": 531.2100219726562 }, { "x": 252.19000244140625, "y": 463.5400085449219 } ], "strokeIds": [ 3, 2, 1, 4, 5, 6, 7 ] }, { "alternates": [ { "category": "line", "recognizedString": "Hell O" }, { "category": "line", "recognizedString": "Hell o" }, { "category": "line", "recognizedString": "Hell 0" }, { "category": "line", "recognizedString": "Help O" }, { "category": "line", "recognizedString": "Help o" }, { "category": "line", "recognizedString": "Help 0" }, { "category": "line", "recognizedString": "Hall O" }, { "category": "line", "recognizedString": "Hall o" }, { "category": "line", "recognizedString": "Hallo" } ], "boundingRectangle": { "height": 241.00999450683594, "topX": 253, "topY": 236, "width": 727.010009765625 }, "category": "line", "childIds": [ 5 ], "class": "container", "id": 4, "parentId": 3, "recognizedText": "Hello", "rotatedBoundingRectangle": [ { "x": 276.1000061035156, "y": 209.72000122070312 }, { "x": 994.4600219726562, "y": 277.3900146484375 }, { "x": 970.5499877929688, "y": 531.2100219726562 }, { "x": 252.19000244140625, "y": 463.5400085449219 } ], "strokeIds": [ 3, 2, 1, 4, 5, 6, 7 ] }, { "boundingRectangle": { "height": 241.00999450683594, "topX": 253, "topY": 236, "width": 727.010009765625 }, "category": "paragraph", "childIds": [ 4 ], "class": "container", "id": 3, "parentId": 2, "rotatedBoundingRectangle": [ { "x": 276.1000061035156, "y": 209.72000122070312 }, { "x": 994.4600219726562, "y": 277.3900146484375 }, { "x": 970.5499877929688, "y": 531.2100219726562 }, { "x": 252.19000244140625, "y": 463.5400085449219 } ], "strokeIds": [ 3, 2, 1, 4, 5, 6, 7 ] }, { "boundingRectangle": { "height": 241.00999450683594, "topX": 253, "topY": 236, "width": 727.010009765625 }, "category": "writingRegion", "childIds": [ 3 ], "class": "container", "id": 2, "parentId": 0, "rotatedBoundingRectangle": [ { "x": 276.1000061035156, "y": 209.72000122070312 }, { "x": 994.4600219726562, "y": 277.3900146484375 }, { "x": 970.5499877929688, "y": 531.2100219726562 }, { "x": 252.19000244140625, "y": 463.5400085449219 } ], "strokeIds": [ 3, 2, 1, 4, 5, 6, 7 ] } ] }
You can find the complete code for this part of the series in the following Github repository. 🙂