AI应用开发实战系列之三:手写识别应用入门

AI应用开发实战 - 手写识别应用入门

手写体识别的应用已经非常流行了,如输入法,图片中的文字识别等。但对于大多数开发人员来说,如何实现这样的一个应用,还是会感觉无从下手。本文从简单的MNIST训练出来的模型开始,和大家一起入门手写体识别。

在本教程结束后,会得到一个能用的AI应用,也许是你的第一个AI应用。虽然离实际使用还有较大的距离(具体差距在文章后面会分析),但会让你对AI应用有一个初步的认识,有能力逐步搭建出能够实际应用的模型。

建议和反馈,请发送到
https://github.com/Microsoft/vs-tools-for-ai/issues

联系我们
OpenmindChina@microsoft.com

准备工作

  • 使用win10 64位操作系统的计算机
  • 参考上一篇博客AI应用开发实战 - 从零开始配置环境。在电脑上训练并导出MNIST模型。

一、 思路

通过上一篇文章搭建环境的介绍后,就能得到一个能识别单个手写数字的模型了,并且识别的准确度会在98%,甚至99%以上了。那么我们要怎么使用这个模型来搭建应用呢?

大致的步骤如下:

  1. 实现简单的界面,将用户用鼠标或者触屏的输入变成图片
  2. 将生成的模型包装起来,成为有公开数据接口的类。
  3. 将输入的图片进行规范化,成为数据接口能够使用的格式。
  4. 最后通过模型来推理(inference)出图片应该是哪个数字,并显示出来。

是不是很简单?

二、动手

步骤一:获取手写的数字

提问:那我们要怎么获取手写的数字呢?

回答:我们可以写一个简单的WinForm画图程序,让我们可以用鼠标手写数字,然后把图片保存下来。

首先,我们打开Visual Studio,选择文件->新建->项目

在弹出的窗口里选择Visual C#->Windows窗体应用,项目名称不妨叫做DrawDigit,解决方案名称不妨叫做MnistForm,点击确定。

此时,Visual Studio也自动弹出了一个窗口的设计图。

在DrawDigit项目上点击右键,选择属性,在生成一栏将平台目标从Any CPU改为x64

否则,DrawDigit(首选32位)与它引用的MnistForm(64位)的编译平台不一致会引发System.BadImageFormatException的异常。

然后我们对这个窗口做一些简单的修改:

首先我们打开VS窗口左侧的工具箱,这个窗口程序需要以下三种组件:
1. PictureBox:用来手写数字,并且把数字保存成图片
2. Label:用来显示模型的识别结果
3. Button:用来清理PictureBox的手写结果

那经过一些简单的选择与拖动还有调整大小,这个窗口现在是这样的:

一些注意事项

  1. 这些组件都可以通过右键->查看属性,在属性里修改它们的设置
  2. 为了方便把PictureBox里的图片转化成Mnist能识别的格式,PictureBox的需要是正方形
  3. 可以给这些控件起上有意义的名称。
  4. 可以调整一下label控件大小、字体等,让它更美观。

经过一些简单的调整,这个窗口现在是这样的:

现在来让我们愉快地给这些组件添加事件!

还是在属性窗口,我们选择某个组件,右键->查看属性,点击闪电符号,给组件绑定对应的事件。每次绑定后,会跳到代码部分,生成一个空函数。点回设计视图继续操作即可。

组件类型事件
pictureBox1Mouse下双击MouseDownMouseUpMouseMove来生成对应的响应事件函数。
button1如上,在Action下双击Click
Form1如上,在Behavior下双击Load

然后我们开始补全对应的函数体内容。

注意,如果在上面改变了控件的名称,下面的代码需要做对应的更改。

废话少说上代码!

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Drawing.Drawing2D;//用于优化绘制的结果
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using MnistModel;namespace DrawDigit
{public partial class Form1 : Form{public Form1(){InitializeComponent();}private Bitmap digitImage;//用来保存手写数字private Point startPoint;//用于绘制线段,作为线段的初始端点坐标private Mnist model;//用于识别手写数字private const int MnistImageSize = 28;//Mnist模型所需的输入图片大小private void Form1_Load(object sender, EventArgs e){//当窗口加载时,绘制一个白色方框model = new Mnist();digitImage = new Bitmap(pictureBox1.Width, pictureBox1.Height);Graphics g = Graphics.FromImage(digitImage);g.Clear(Color.White);pictureBox1.Image = digitImage;}private void clean_click(object sender, EventArgs e){//当点击清除时,重新绘制一个白色方框,同时清除label1显示的文本digitImage = new Bitmap(pictureBox1.Width, pictureBox1.Height);Graphics g = Graphics.FromImage(digitImage);g.Clear(Color.White);pictureBox1.Image = digitImage;label1.Text = "";}private void pictureBox1_MouseDown(object sender, MouseEventArgs e){//当鼠标左键被按下时,记录下需要绘制的线段的起始坐标startPoint = (e.Button == MouseButtons.Left) ? e.Location : startPoint;}private void pictureBox1_MouseMove(object sender, MouseEventArgs e){//当鼠标在移动,且当前处于绘制状态时,根据鼠标的实时位置与记录的起始坐标绘制线段,同时更新需要绘制的线段的起始坐标if (e.Button == MouseButtons.Left){Graphics g = Graphics.FromImage(digitImage);Pen myPen = new Pen(Color.Black, 40);myPen.StartCap = LineCap.Round;myPen.EndCap = LineCap.Round;g.DrawLine(myPen,startPoint, e.Location);pictureBox1.Image = digitImage;g.Dispose();startPoint = e.Location;}}private void pictureBox1_MouseUp(object sender, MouseEventArgs e){//当鼠标左键释放时//同时开始处理图片进行推理//暂时不处理这里的代码}}
}

步骤二:把模型包装成一个类

将模型包装成一个C#是整个过程中比较麻烦的一步。所幸的是,Tools for AI对此提供了很好的支持。进一步了解,可以看这里。

首先,我们在解决方案MnistForm下点击鼠标右键,选择添加->新建项目,在弹出的窗口里选择AI Tools->Inference->模型推理类库,名称不妨叫做MnistModel,点击确定,于是我们又多了一个项目,

然后自己配置好这个项目的名称、位置,点击确定

然后弹出一个模型推理类库创建向导,这个时候就需要我们选择自己之前训练好的模型了~

首先在模型路径里选择保存的模型文件的路径。这里我们使用在AI应用开发实战 - 从零开始配置环境博客中训练并导出的模型

note:模型可在/samples-for-ai/examples/tensorflow/MNIST目录下找到,其中output文件夹保存了检查点文件,export文件夹保存了模型文件。

对于TensorFlow,我们可以选择检查点的.meta文件,或者是保存的模型的.pb文件

这里我们选择在AI应用开发实战 - 从零开始配置环境这篇博客最后生成的export目录下的检查点的SavedModel.pb文件,这时程序将自动配置好配置推理接口,见下图:

类名可以自己定义,因为我们用的是MNIST,那么类名就叫Mnist好了,然后点击确定。

这样,在解决方案资源管理器里,在解决方案MnistForm下,就多了一个MnistModel

双击Mnist.cs,我们可以看到项目自动把模型进行了封装,生成了一个公开的infer函数。

然后我们在MnistModel上右击,再选择生成,等待一会,这个项目就可以使用了~

步骤三:连接两个部分

这一步差不多就是这么个感觉:

I have an apple , I have a pen. AH~ , Applepen

首先,我们来给DrawDigit添加引用,让它能使用MnistModel。在DrawDigit项目的引用上点击鼠标右键,点击添加引用,在弹出的窗口中选择MnistModel,点击确定。

然后,由于MNIST的模型的输入是一个28×28的白字黑底的灰度图,因此我们首先要对图片进行一些处理。
首先将图片转为28×28的大小。
然后将RGB图片转化为灰阶图,将灰阶标准化到[-0.5,0.5]区间内,转换为黑底白字。
最后将图片用mnist模型要求的格式包装起来,并传送给它进行推理。
于是,我们在pictureBox1_MouseUp中添加上这些代码,并且在文件最初添加上using MnistModel;

        private void pictureBox1_MouseUp(object sender, MouseEventArgs e){//当鼠标左键释放时//开始处理图片进行推理if (e.Button == MouseButtons.Left){Bitmap digitTmp = (Bitmap)digitImage.Clone();//复制digitImage//调整图片大小为Mnist模型可接收的大小:28×28using (Graphics g = Graphics.FromImage(digitTmp)){g.InterpolationMode = InterpolationMode.HighQualityBicubic;g.DrawImage(digitTmp, 0, 0, MnistImageSize, MnistImageSize);}//将图片转为灰阶图,并将图片的像素信息保存在list中var image = new List<float>(MnistImageSize * MnistImageSize);for (var x = 0; x < MnistImageSize; x++){for (var y = 0; y < MnistImageSize; y++){var color = digitTmp.GetPixel(y, x);var a = (float)(0.5 - (color.R + color.G + color.B) / (3.0 * 255));image.Add(a);}}//将图片信息包装为mnist模型规定的输入格式var batch = new List<IEnumerable<float>>();batch.Add(image);//将图片传送给mnist模型进行推理var result = model.Infer(batch);//将推理结果输出label1.Text = result.First().First().ToString();}}

最后让我们尝试一下运行~

三、效果展示

现在我们就有了一个简单的小程序,可以识别手写的数字了。

赶紧试试效果怎么样~

注意

  1. 路径中不能有中文字符,否则可能找不到模型。

进阶

那么,如果要识别多个连写的数字,或支持字母该怎么做呢?大家多用用也会发现,如果数字写得很小,或者没写到正中,识别起来正确率也会不高。要解决这些问题,做成真正的产品,就不止这一个模型了。比如在多个数字识别中,可能要根据经验来切分图,或者训练另一个模型来检测并分割数字。要支持字母,则需要重新训练一个包含手写字母的模型,并准备更多的字母的数据。要解决字太小的问题,还要检测一下字的大小,做合适的放大等等。

我们可以看到,一个训练出来的模型本身到一个实际的应用之间还有不少的功能要实现。希望我们这一系列的介绍,能够帮助大家将机器学习的概念带入到传统的编程领域中,做出更聪明的产品。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/500341.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

重力加速度换算_中考物理重难点汇总——公式换算大全

初中物理中最重要的部分就是公式了&#xff0c;在这之中公式的换算可以说是一个难点&#xff0c;也是一个重点。力学部分一、速度公式火车过桥(洞)时通过的路程s&#xff1d;L桥&#xff0b;L车声音在空气中的传播速度为340m/s 光在空气中的传播速度为3108m/s二、密度公式(ρ水…

新手一小时就写出人工智能应用 - 看图识熊

来不及了&#xff0c;先上车&#xff1a; 人工智能开发案例 熊的分类 如何安装必要的工具并配置环境呢&#xff0c;请看这个详细的解说 今后会有更详细的文字版在这个专题出现。 如果有对这个教程有疑问&#xff0c;请在这里留言。

c++ 线性回归_模型之母:简单线性回归的代码实现

模型之母&#xff1a;简单线性回归的代码实现关于作者&#xff1a;饼干同学&#xff0c;某人工智能公司交付开发工程师/建模科学家。专注于AI工程化及场景落地&#xff0c;希望和大家分享成长中的专业知识与思考感悟。0x00 前言 在《模型之母&#xff1a;简单线性回归&最小…

AI应用开发实战系列之四 - 定制化视觉服务的使用

AI应用开发实战 - 定制化视觉服务的使用 本篇教程的目标是学会使用定制化视觉服务&#xff0c;并能在UWP应用中集成定制化视觉服务模型。 前一篇&#xff1a;AI应用开发实战 - 手写识别应用入门 建议和反馈&#xff0c;请发送到 https://github.com/Microsoft/vs-tools-for-…

现代软件工程 结对/团队作业 - 汉字的 2048 + 俄罗斯方块

一个很有趣的软件工程/编程作业&#xff0c;如果把汉字构成的规律运用在 2048 俄罗斯方块这样的游戏中&#xff0c;会有什么效果呢? (链接1&#xff0c; 链接2) 既然是软件工程的作业&#xff0c; 那就要体现出一些工程的特性&#xff1a; 作业要求&#xff1a; 1) 学生自行…

机器学习平台建设

本文从机器学习平台的架构开始&#xff0c;再到具体的功能&#xff0c;然后从需求的角度带给读者思考&#xff0c;找到合适的机器学习平台建设之路。最后&#xff0c;推荐了微软开源开放的机器学习平台OpenPAI&#xff0c;是可私有部署的机器学习训练平台。 本文不少要点都可以…

型管件的作用_管道工程基础 - 管件和管道附件的布置规定

概述1.1 管件的用途1.2 管件的种类根据管件的端部连接形式可将管件分为对焊连接管件、承插焊连接管件、螺纹连接管件、法兰连接管件以及其它管件。管件和管道附件的布置2.1管件的布置(1)弯头宜选用曲率半径等于1.5倍公称直径的长半径弯头&#xff1b;输送气固、液固两相流物料的…

java grpc 客户端处理 go 服务端多返回值_grpc基础实践(二)

在此篇中我们将简要介绍关于grpc对java客户端的实现。在开始开发前&#xff0c;我们需要先导入io.grpc grpc-netty 1.11.0io.grpc grpc-protobuf 1.11.0io.grpc grpc-stub 1.11.0如果是Android除了这几个包外&#xff0c;你可能还需要一个javax.annotation:javax.annotation-ap…

asp.net 文本框显示xml格式数据_Excel 办公小技巧,查找和替换数据,您值得拥有...

在数据处理的过程中&#xff0c;有时需要在工作表中查找一些数据&#xff0c;以便查看或修改数据。若工作表的数据能一目了然&#xff0c;则可手动进行查找&#xff0c;但在记录繁多的工作表中查找所需数据并替换&#xff0c;效率比较低&#xff0c;而且极易出现遗漏。这时就可…

微软认知服务应用秘籍 – 与机器人聊知识

在本篇博客中&#xff0c;我们将会学习到零代码情况下&#xff0c;如何利用已有的技术建立自己的知识问答系统&#xff0c;这种系统的可以广泛适用于学校、企业、客服、政府公开信息等领域&#xff0c;代替传统的电话咨询、电子邮件沟通等高人工负荷的方式。 建立知识库 什么…

.net 开发怎么实现前后端分离_ASP.NET Core模块化前后端分离快速开发框架介绍

源码地址GitHub&#xff1a;https://github.com/iamoldli/NetModular演示地址地址&#xff1a;http://118.24.75.170:6220/账户&#xff1a;admin密码&#xff1a;admin前端框架演示地址(临时)地址&#xff1a;http://progqx5cu.bkt.clouddn.com/skins/index.html#/账户&#x…

微软认知服务应用秘籍 – 支持跨平台客户端的视觉服务中间层

不断演进的应用场景 初级应用场景—宅在家里 场景&#xff1a;Bob同学有一天在网上看到了一张建筑物的图片&#xff0c;大发感慨&#xff1a;"好漂亮啊&#xff01;这是哪里&#xff1f;我要去亲眼看看&#xff01;"Bob同学不想问别人&#xff0c;可笑的自尊心让他…

就业技术书文件表格_429页标准指南,教你如何管理工程监理文件资料,丰富图表一看就会...

房屋建筑工程监理文件资料的管理&#xff0c;参差不齐&#xff0c;也给监理工作带来了很大障碍。这份房建工程监理文件资料管理标准指南&#xff0c;能够帮助广大监理和资料员们实现监理文件资料标准化管理&#xff0c;能够有效地补充和支撑现场监理工作&#xff0c;对于工程资…

微软认知服务应用秘籍 – 君子动口不动手

概述 科技的不断发展带动着人们生活质量不断的提升&#xff0c;其中一方面就体现在日常家庭生活中&#xff0c;智能设备层出不穷&#xff0c;给人们的生活带来了很大的便利。 以电视为例&#xff0c;几十年前的电视还是按钮式的&#xff0c;每次换台还要跑到电视跟前&#xf…

AI应用开发实战 - 手写算式计算器

扩展手写数字识别应用 识别并计算简单手写数学表达式 主要知识点 了解MNIST数据集了解如何扩展数据集实现手写算式计算器 简介 本文将介绍一例支持识别手写数学表达式并对其进行计算的人工智能应用的开发案例。本文的应用是基于前文“手写识别应用入门”中的基础应用进行扩…

sed 删除某一行_Linux常用命令三剑客之sed,您真的会用吗?

请关注本头条号&#xff0c;每天坚持更新原创干货技术文章。如需学习视频&#xff0c;请在微信搜索公众号“智传网优”直接开始自助视频学习1. 前言本文主要讲解Linux系统中的sed命令的作用与使用案例。sed用于查找、过滤、文本替换、替换和插入、删除等文本操作。它是Linux/Un…

现代软件工程 期中/期末总结博客作业

(软件工程作业列表) 经历了一个学期的学习&#xff0c; 可以把经历和收获总结一下。 第一部分 耕耘 请说明你付出了多少时间&#xff0c;写了多少代码&#xff0c;多少注释&#xff0c;多少文档(博客)&#xff0c;多少次签入&#xff0c;最好用折线图或其他合适的图形化的方式…

caj转pdf python_PDF怎么转换成Word?2种方法3秒一键转换!建议收藏

Hello&#xff0c;大家好&#xff01;今天给大家带来&#xff1a;PDF一键转Word的干货&#xff01;保证全方位满足你工作和学习的需求~01Word转换法适用&#xff1a;纯文字简单型PDF文件。其实对于简单纯文字的PDF文件&#xff0c;直接使用Word就能转换&#xff0c;操作也非常简…

AI应用开发基础傻瓜书系列1-神经网络的基本工作原理

Copyright © Microsoft Corporation. All rights reserved. 适用于License版权许可 更多微软人工智能学习资源&#xff0c;请见微软人工智能教育与学习共建社区 Content01.0-神经网络的基本工作原理01.1-基本数学导数公式01.2-Python-Numpy库的点滴02.0-反向传播与梯度下…

动力环境监控系统论文_浅谈动力环境监控系统技术标准

动力环境监控系统技术标准规定了监控系统各因素指标、功能&#xff0c;对实现机房设备故障自动检测、无人化值守、降低维护保养成本的动环系统有重要的作用和意义。运用动环监控&#xff0c;能解决成本高、效率低、告警慢、信息落后能问题。一、动环监控系统的技术标准1、可靠性…