Azure认知服务之使用墨迹识别功能识别手写汉字
前面我们使用Azure Face实现了人脸识别、使用Azure表格识别器提取了表格里的数据。这次我们试试使用Azure墨迹识别API来对笔迹进行识别。
墨迹识别
墨迹识别器认知服务提供基于云的 REST API 用于分析和识别数字墨迹内容。 与使用光学字符识别 (OCR) 的服务不同,该 API 需要使用数字墨迹笔划数据作为输入。 数字墨迹笔划是 2D 点(X,Y 坐标,表示数字手写笔或手指的动作)的时序集。 然后,墨迹识别器会识别输入中的形状和手写内容,并返回包含所有已识别实体的 JSON 响应。
引用自微软文档
它不是ocr对图像进行识别,而是对墨迹数据进行识别。墨迹数据的原理主要是一些手写输入设备,比如平板,手写板等。
创建墨迹识别资源
跟前面的内容一样,在portal控制台找到墨迹识别功能,点击创建,取一个实例名。墨迹识别也是一个免费服务,定价选F0方案,额度为5次/分,20000事务/月。
获取秘钥和终结点
我们调用墨迹识别API需要秘钥跟终结点信息。点击菜单“密钥和终结点”查看信息。
新建一个WPF项目
我们这次同样实现一个WPF小程序。界面上放置一个InkCanvas用来手写,一个文本框用来显示识别的文本,一个按钮用来触发识别。
MainWindow.xaml
修改MainWindow.xaml为如下代码:
<Window x:Class="InkRec2.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
xmlns:local="clr-namespace:NoteTaker"
xmlns:controls="clr-namespace:Microsoft.Toolkit.Wpf.UI.Controls;assembly=Microsoft.Toolkit.Wpf.UI.Controls"
Title="MainWindow">
<Grid >
<Grid.RowDefinitions>
<RowDefinition Height="4*" />
<RowDefinition Height="1*" />
<RowDefinition Height="50" />
</Grid.RowDefinitions>
<Border Grid.Row ="0" BorderBrush="Black" BorderThickness="1">
<controls:InkCanvas x:Name="inkCanvas" Loaded="inkCanvas_Loaded"/>
</Border>
<Border Grid.Row ="1" BorderBrush="Black" BorderThickness="1">
<ScrollViewer>
<TextBox x:Name="output" FontSize="18" TextWrapping="Wrap"/>
</ScrollViewer>
</Border>
<StackPanel Grid.Row="2" Orientation="Horizontal">
<Button Click="Button_InkRec">开始识别</Button>
</StackPanel>
</Grid>
</Window>
注意:InkCanvas控件需要使用的是Microsoft.Toolkit.Wpf.UI.Controls包下的,如果本地没有使用nuget进行安装
采集墨迹
inkCanvas load事件里设置输入设备的类型:
private void inkCanvas_Loaded(object sender, RoutedEventArgs e)
{
inkCanvas.InkPresenter.InputDeviceTypes = CoreInputDeviceTypes.Mouse | CoreInputDeviceTypes.Pen | CoreInputDeviceTypes.Touch;
}
先定义几个模型用来存储墨迹数据:
public class InkStroke
{
public int id { get; set; }
public string points { get; set; }
}
public class InkData
{
public string language { get; set; }
public List<InkStroke> strokes { get; set; }
}
从InkCanvas获取墨迹数据组装成InkData:
private InkData GetInkData()
{
var data = new InkData();
data.language = "zh-CN";
data.strokes = new List<InkStroke>();
int id = 0;
foreach (var stroke in this.inkCanvas.InkPresenter.StrokeContainer.GetStrokes())
{
var points = stroke.GetInkPoints();
var convertPoints = ConvertPixelsToMillimeters(points);
var inkStorke = new InkStroke();
inkStorke.id = id++;
var sb = new StringBuilder();
foreach (var point in convertPoints)
{
sb.Append(point.X);
sb.Append(",");
sb.Append(point.Y);
sb.Append(",");
}
inkStorke.points = sb.ToString().TrimEnd(',');
data.strokes.Add(inkStorke);
}
return data;
}
private List<System.Windows.Point> ConvertPixelsToMillimeters(IReadOnlyList<InkPoint> pointsInPixels)
{
float dpiX = 96.0f;
float dpiY = 96.0f;
var transformedInkPoints = new List<System.Windows.Point>();
const float inchToMillimeterFactor = 25.4f;
foreach (var point in pointsInPixels)
{
var transformedX = (point.Position.X / dpiX) * inchToMillimeterFactor;
var transformedY = (point.Position.Y / dpiY) * inchToMillimeterFactor;
transformedInkPoints.Add(new System.Windows.Point(transformedX, transformedY));
}
return transformedInkPoints;
}
调用墨迹API
这里需要前面复制好的密钥跟终结点地址。识别其实很简单,就是把墨迹数据转换成json后给服务器发生一个put请求,识别成功后就会返回一个json字符串的结果。
private async Task<string> InkRec(InkData data)
{
string inkRecognitionUrl = "/inkrecognizer/v1.0-preview/recognize";
string endPoint = "x";
string subscriptionKey = "x";
using (HttpClient client = new HttpClient { BaseAddress = new Uri(endPoint) })
{
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 jsonData = JsonConvert.SerializeObject(data);
var content = new StringContent(jsonData, Encoding.UTF8, "application/json");
var res = await client.PutAsync(inkRecognitionUrl, content);
if (res.IsSuccessStatusCode)
{
var result = await res.Content.ReadAsStringAsync();
return result;
}
else
{
var err = $"ErrorCode: {res.StatusCode}";
return err;
}
}
}
解析识别结果
识别成功后,结果会以json字符串的形式进行返回。结果是一个数组,里面存放了每一个笔迹的识别结果,以及最终的识别结果。
结果示例:
{"recognitionUnits":[{"alternates":[{"category":"inkWord","recognizedString":"乖"},{"category":"inkWord","recognizedString":"黍"},{"category":"inkWord","recognizedString":"秉"},{"category":"inkWord","recognizedString":"乗"},{"category":"inkWord","recognizedString":"埀"}],"boundingRectangle":{"height":48.159999847412109,"topX":7.190000057220459,"topY":22.010000228881836,"width":35.639999389648438},"category":"inkWord","class":"leaf","id":4,"parentId":3,"recognizedText":"乘","rotatedBoundingRectangle":[{"x":41.490001678466797,"y":21.25},{"x":43.209999084472656,"y":69.239997863769531},{"x":7.8299999237060547,"y":70.5},{"x":6.1100001335144043,"y":22.520000457763672}],"strokeIds":[0,1,2,3,4,5,6,7,8,9]},{"alternates":[{"category":"inkWord","recognizedString":"風"},{"category":"inkWord","recognizedString":"夙"},{"category":"inkWord","recognizedString":"凤"},{"category":"inkWord","recognizedString":"凡"},{"category":"inkWord","recognizedString":"㶡"}],"boundingRectangle":{"height":32."class":"leaf","id":8,"parent
...
那么我们只要对其进行反序列化取出想要的识别结果就行了。
public class InkRecResponse
{
public List<InkRecResponseUnit> recognitionUnits { get; set; }
}
public class InkRecResponseUnit
{
public string category { get; set; }
public string recognizedText { get; set; }
}
private async void Button_InkRec(object sender, RoutedEventArgs e)
{
var inkData = GetInkData();
var response = await InkRec(inkData);
var jsonObj = JsonConvert.DeserializeObject<InkRecResponse>(response);
var recognizedText = jsonObj.recognitionUnits.First(o => o.category == "line").recognizedText;
this.output.Text = recognizedText;
}
运行一下
我们的程序写好了,运行一下。在canvas上随便写上几个汉字点击识别按钮。字虽然丑了点,但是结果还是完美的。
总结
使用Azure墨迹识别可以轻松的识别手写输入设备的笔迹。墨迹识别功能并不是见到的orc识别,它可以对每一个笔画进行识别,提供候选结果。以上代码虽然多,其实主要是获取墨迹数据比较麻烦,其实真正识别墨迹只是一个http put请求而已,这是非常简单的。有了这个API我们可以实现很多创意,比如稍微改进下上面的代码就可以实现手写文字的连续识别功能,一边写一边不断的识别,封装进平板就是一款可以实时识别手写板啦。
关注我的公众号一起玩转技术
Azure认知服务之使用墨迹识别功能识别手写汉字的更多相关文章
- Azure 认知服务 (5) 计算机视觉API - 使用C#代码实现读取图片中的文字(OCR)功能
<Windows Azure Platform 系列文章目录> 在笔者之前的文章:Azure 认知服务 (4) 计算机视觉API - 读取图片中的文字 (OCR) 介绍了使用用户界面,在海 ...
- Azure 认知服务概述
背景知识 近些年随着机器学习.深度学习等技术的不断发展,人工智能在越来越多的场景得到了应用,如人脸识别.图像识别.语音识别.语音生成.自然语言处理.决策分析等等,让机器拥有了听.说.看和思考的能力,很 ...
- Azure认知服务之表格识别器
认知服务 Azure 认知服务的目标是帮助开发人员创建可以看.听.说.理解甚至开始推理的应用程序. Azure 认知服务中的服务目录可分为五大主要支柱类别:视觉.语音.语言.Web 搜索和决策.开发人 ...
- 技术博客:Azure 认知服务
Azure 认知服务 1.概述 微软认知服务(Microsoft Cognitive Services)集合了多种智能API以及知识API,使每个开发人员无需具备机器学习的专业知识就能接触到 AI ...
- Azure 认知服务 (2) 计算机视觉API - 分析图像
<Windows Azure Platform 系列文章目录> 在上一节内容中,笔者介绍了微软认知服务的概览. 在本节中,笔者将详细介绍微软认知服务中的一种:计算机视觉 (Computer ...
- Azure 认知服务 (4) 计算机视觉API - 读取图片中的文字 (OCR)
<Windows Azure Platform 系列文章目录> 微软Azure认知服务的计算机视觉API,还提供读取图片中的文字功能 在海外的Windows Azure认知服务的读取图片功 ...
- Azure认知服务之Face API上手体验
Azure认知服务:Face API Face API是Azure认知服务之一,Face API有两个主要功能: 人脸检测 Face API可在图像中以高精度人脸位置检测多达64个人脸.图像可以通过文 ...
- Azure 认知服务--计算机视觉 API - 分析图像
在本节中,笔者将详细介绍 Azure 认知服务中的一种:计算机视觉 (Computer Vision) API. 我的一个客户有需求,他们需要消费者与自己的产品合照,然后上传到服务器并转发到朋友圈. ...
- Azure 认知服务 (3) 计算机视觉API - 分析图像,使用C#代码
<Windows Azure Platform 系列文章目录> 在上一节中Azure 认知服务 (2) 计算机视觉API - 分析图像,笔者介绍了如何使用API测试控制台进行调试. 本章将 ...
随机推荐
- ajax快速入门
一.ajax简单入门 1.Ajax的实现步骤 // 1.创建ajax对象var xhr = new XMLHttpRequest();// 2.高数ajax请求地址及请求方式//第一个参数就是请求方式 ...
- Kaggle 入门题-泰坦尼克号灾难存活预测
这个题目的背景概况来讲就是基于泰坦尼克号这个事件,然后大量的人员不幸淹没在这个海难中,也有少部分人员在这次事件之中存活,然后这个问题提供了一些人员的信息如姓名.年龄.性别.票价,所在客舱等等一些信息, ...
- Web压测工具之Webbench和http_load
Webbench简介 是知名的网站压力测试工具,能测试处在相同硬件上,不同服务的性能以及不同硬件上同一个服务的运行状况. webbench的标准测试可以向我们展示服务器的两项内容:每秒钟相应请求数和每 ...
- Shiro @RequiresRoles注解相关参数说明
@RequiresRoles(value={"admin","user"},logical = Logical.OR) @RequiresPermissions ...
- Python 字典(Dictionary) type()方法
Python 字典(Dictionary) type()方法 描述 Python 字典(Dictionary) type() 函数返回输入的变量类型,如果变量是字典就返回字典类型.高佣联盟 www.c ...
- Python Tuple(元组) len()方法
描述 Python 元组 len() 函数计算元组元素个数.高佣联盟 www.cgewang.com 语法 len()方法语法: len(tuple) 参数 tuple -- 要计算的元组. 返回值 ...
- P2489 [SDOI2011]迷宫探险 概率dp
LINK:迷宫探险 题目中要求在最优的策略下的最大概率 而并非期望概率. 一个坑点 题目中虽然没有明说 但是 探险者是知道地图的模样和每个陷阱的概率的. 所以才有最优策略一说. 最优策略尽管不知道可以 ...
- luogu P2462 [SDOI2007]游戏
LINK:SDOI2007游戏 题意:接龙前一个要比后面大1 且后一个单词出现的各自字母的次数>=前一个单词各自的字母的次数 考虑暴力dp sort之后dpY 显然会T. 考虑我们没必要枚举j ...
- php操作mysql关于文件上传、存储
php+前端+mysql实现文件上传并储存 我们都知道很多网站都需要上传文件,最普遍的就是图片上传,即是用户头像等等: 关于mysql+php实现文件查询,存储大致两个方式, 1.直接把文件写入mys ...
- requests入门实践02_下载斗图拉最新表情包
新版本移步:https://www.cnblogs.com/zy7y/p/13376228.html 下载斗图拉最新表情包 要爬取的目标所在网址:http://www.doutula.com/phot ...