1)实验平台:alientek 阿波罗 STM32F767 开发板

2)摘自《STM32F7 开发指南(HAL 库版)》关注官方微信号公众号,获取更多资料:正点原子

第五十三章 手写识别实验

现在几乎所有带触摸屏的手机都能实现手写识别。本章,我们将利用 ALIENTEK 提供的手

写识别库,在 ALIENTEK 探索者 STM32F4 开发板上实现一个简单的数字字母手写识别。本章

分为如下几个部:

53.1 手写识别简介

53.2 硬件设计

53.3 软件设计

53.4 下载验证

53.1 手写识别简介

手写识别,是指对在手写设备上书写时产生的有序轨迹信息进行识别的过程,是人际交互

最自然、最方便的手段之一。随着智能手机和平板电脑等移动设备的普及,手写识别的应用也

被越来越多的设备采用。

手写识别能够使用户按照最自然、最方便的输入方式进行文字输入,易学易用,可取代键

盘或者鼠标。用于手写输入的设备有许多种,比如电磁感应手写板、压感式手写板、触摸屏、

触控板、超声波笔等。ALIENTEK 探索者 STM32F4 开发板自带的 TFTLCD 触摸屏(2.8/3.5/4.3

寸),可以用来作为手写识别的输入设备。接下来,我们将给大家简单介绍下手写识别的实现过

程。

手写识别与其他识别系统如语音识别图像识别一样分为两个过程:训练学习过程;识别过

程。如图 53.1.1 所示:

图 53.1.1 字母数字识别系统示意图。

上图中虚线部分为训练学习过程,该过程首先需要使用设备采集大量数据样本,样本类别

数目为 0~9,a~z,A~Z 总共 62 类,每个类别 5~10 个样本不等(样本越多识别率就越高)。对

这些样本进行传统的把方向特征提取,提取后特征维数为 512 维,这对 STM32 来讲,计算量

和模板库的存储量来说都难以接受,所以需要运行一些方法进行降维,这里采用 LDA 线性判

决分析的方法进行降维,所谓线性判决分析,即是假设所有样本服从高斯分布(正态分布)对

样本进行低维投影,以达到各个样本间的距离最大化。关于 LDA 的更多知识可以阅读

(http://wenku.baidu.com/view/f05c731452d380eb62946d39.html)等参考文档。这里将维度降到

64 维,然后针对各个样本类别进行平均计算得到该类别的样本模板。

而对于识别过程,首先得到触屏输入的有序轨迹,然后进行一些预处理,预处理主要包括

重采样,归一化处理。重采样主要是因为不同的输入设备不同的输入处理方式产生的有序轨迹

序列有所不同,为了达到更好的识别结果我们需要对训练样本和识别输入的样本进行重采样处

理,这里主要应用隔点重采样的方法对输入的序列进行重采样;而归一化就是因为不同的书写

风格采样分辨率的差异会导致字体太小不同,因此需要对输入轨迹进行归一化。这里把样本进

行线性缩放的方法归一化为 64*64 像素。

接下来进行同样的八方向特征提取操作。所谓八方向特征就是首相将经过预处理后的

64*64 输入进行切分成 8*8 的小方格,每个方格 8*8 个像素;然后对每个 8*8 个小格进行各个

方向的点数统计。如某个方格内一共有 10 个点,其中八个方向的点分别为:1、3、5、2、3、4、

3、2 那么这个格子得到的八个特征向量为[0.1, 0.3, 0.5,0.2, 0.3, 0.4, 0.3, 0.2]。总共有 64

个格子于是一个样本最终能得到 64*8=512 维特征,更多八方向特征提取可以参考一下两个文档:

1,http://wenku.baidu.com/view/d37e5a49e518964bcf847ca5.html;

2,http://wenku.baidu.com/view/3e7506254b35eefdc8d333a1.html;

由于训练过程进行了 LDA 降维计算,所以识别过程同样需要对应的 LDA 降维过程得到最

终的 64 维特征。这个计算过程就是在训练模板的过程中可以运算得到一个 512*64 维的矩阵,

那么我们通过矩阵乘运算可以得到 64 维的最终特征值。

最后将这 64 维特征分别与模板中的特征进行求距离运算。得到最小的距离为该输入的最佳

识别结果输出。

关于手写识别原理,我们就介绍到这里。如果想自己实现手写识别,那得花很多时间学习

和研究,但是如果只是应用的话,那么就只需要知道怎么用就 OK 了,相对来说,简单的多。

ALIENTEK 提供了一个数字字母识别库,这样我们不需要关心手写识别是如何实现的,只

需要知道这个库怎么用,就能实现手写识别。ALIENTEK 提供的手写识别库由 4 个文件组成:

ATKNCR_M_V2.0.lib、ATKNCR_N_V2.0.lib、atk_ncr.c 和 atk_ncr.h。

ATKNCR_M_V2.0.lib 和 ATKNCR_N_V2.0.lib 是两个识别用的库文件(两个版本),使用

的时候,选择其中之一即可。ATKNCR_M_V2.0.lib 用于使用内存管理的情况,用户必须自己

实现 alientek_ncr_malloc 和 alientek_ncr_free 两个函数。而 ATKNCR_N_V2.0.lib 用于不使用内

存管理的情况,通过全局变量来定义缓存区,缓存区需要提供至少 3K 左右的 RAM。大家根据

自己的需要,选择不同的版本即可。ALIENTEK 手写识别库资源需求:FLASH:52K 左右,RAM:

6K 左右。

atk_ncr.c 代码如下:

#include "atk_ncr.h"#include "malloc.h"//内存设置函数void alientek_ncr_memset(char *p,char c,unsigned long len){mymemset((u8*)p,(u8)c,(u32)len);}//内存申请函数void *alientek_ncr_malloc(unsigned int size){return mymalloc(SRAMIN,size);}//内存清空函数void alientek_ncr_free(void *ptr){myfree(SRAMIN,ptr);}

这里,主要实现了 alientek_ncr_malloc、alientek_ncr_free 和 alientek_ncr_memset 等三个函

数。

atk_ncr.h 则是识别库文件同外部函数的接口函数声明

#ifndef __ATK_NCR_H#define __ATK_NCR_H//当使用 ATKNCR_M_Vx.x.lib 的时候,不需要理会 ATK_NCR_TRACEBUF1_SIZE 和//ATK_NCR_TRACEBUF2_SIZE//当使用 ATKNCR_N_Vx.x.lib 的时候,如果出现识别死机,请适当增加//ATK_NCR_TRACEBUF1_SIZE 和 ATK_NCR_TRACEBUF2_SIZE 的值#define ATK_NCR_TRACEBUF1_SIZE500*4//定义第一个 tracebuf 大小(单位为字节),如果出现死机,请把该数组适当改大#define ATK_NCR_TRACEBUF2_SIZE250*4//定义第二个 tracebuf 大小(单位为字节),如果出现死机,请把该数组适当改大//输入轨迹坐标类型__packed typedef struct _atk_ncr_point{short x; //x 轴坐标short y; //y 轴坐标}atk_ncr_point;//外部调用函数//初始化识别器//返回值:0,初始化成功 1,初始化失败unsigned char alientek_ncr_init(void);void alientek_ncr_stop(void); //停止识别器//识别器识别//track:输入点阵集合 potnum:输入点阵的点数,就是 track 的大小//charnum:期望输出的结果数,就是你希望输出多少个匹配结果//mode:识别模式//1,仅识别数字 2,进识别大写字母//3,仅识别小写字母 4,混合识别(全部识别)//result:结果缓存区(至少为:charnum+1 个字节)void alientek_ncr(atk_ncr_point * track,int potnum,int charnum,unsigned char mode,char*result);void alientek_ncr_memset(char *p,char c,unsigned long len); //内存设置函数//动态申请内存,当使用 ATKNCR_M_Vx.x.lib 时,必须实现.void *alientek_ncr_malloc(unsigned int size);//动态释放内存,当使用 ATKNCR_M_Vx.x.lib 时,必须实现.void alientek_ncr_free(void *ptr);#endif

此段代码中,我们定义了一些外部接口函数以及一个轨迹结构体等。

alientek_ncr_init,该函数用与初始化识别器,该函数在.lib 文件实现,在识别开始之前,我

们应该调用该函数。

alientek_ncr_stop,该函数用于停止识别器,在识别完成之后(不需要再识别),我们调用

该函数,如果一直处于识别状态,则没必要调用。该函数也是在.lib 文件实现。

alientek_ncr,该函数就是识别函数了。它有 5 个参数,第一个参数 track,为输入轨迹点的

坐标集(最好 200 以内);第二个参数 potnum,为坐标集点坐标的个数;第三个参数 charnum,

为期望输出的结果数,即希望输出多少个匹配结果,识别器按匹配程度排序输出(最佳匹配排

第一);第四个参数 mode,该函数用于设置模式,识别器总共支持 4 中模式:

1,仅识别数字

2,进识别大写字母

3,仅识别小写字母

4,混合识别(全部识别)

最后一个参数是 result,用来输出结果,注意这个结果是 ASCII 码格式的。

alientek_ncr_memset、alientek_ncr_free 和 alientek_ncr_free 这 3 个函数在 atk_ncr.c 里面实现,

这里就不多说了。

最后,我们看看通过 ALIENTEK 提供的手写数字字母识别库实现数字字母识别的步骤:

1) 调用 alientek_ncr_init 函数,初始化识别程序

该函数用来初始化识别器,在手写识别进行之前,必须调用该函数。

2) 获取输入的点阵数据

此步,我们通过触摸屏获取输入轨迹点阵坐标,然后存放到一个缓存区里面,注意至

少要输入 2 个不同坐标的点阵数据,才能正常识别。注意输入点数不要太多,太多的话,

需要更多的内存,我们推荐的输入点数范围:100~200 点。

3) 调用 alientek_ncr 函数,得到识别结果.

通过调用 alientek_ncr 函数,我们可以得到输入点阵的识别结果,结果将保存在 result

参数里面,采用 ASCII 码格式存储

4) 调用 alientek_ncr_stop 函数,终止识别.

如果不需要继续识别,则调用 alientek_ncr_stop 函数,终止识别器。如果还需要继续识

别,重复步骤 2 和步骤 3 即可。

以上 4 个步骤,就是使用 ALIENTEK 手写识别库的方法,十分简单。

53.2 硬件设计

本章实验功能简介:开机的时候先初始化手写识别器,然后检测字库,之后进入等待输入

状态。此时,我们在手写区写数字/字符,在每次写入结束后,自动进入识别状态,进行识别,

然后将识别结果输出在 LCD 模块上面(同时打印到串口)。通过按 KEY0 可以进行模式切换

(4 种模式都可以测试),通过按 KEY2,可以进入触摸屏校准(如果发现触摸屏不准,请执

行此操作)。DS0 用于指示程序运行状态。

本实验用到的资源如下:

1) 指示灯 DS0

2) KEY0 和 KEY2 两个按键

3) 串口

4) TFTLCD 模块(含触摸屏)

5) SPI FLASH

这些用到的硬件,我们在之前都已经介绍过,这里就不再介绍了。

53.3 软件设计

打开本章实验工程目录可以看到,我们在工程根目录文件夹下新建一个 ATKNCR 的文件夹。

将 ALIETENK 提供的手写识别库文件(ATKNCR_M_V2.0.lib、ATKNCR_N_V2.0.lib、atk_ncr.c

和 atk_ncr.h 这四个个文件,在光盘→ 4,程序源码→5,ATKNCR(数字字母手写识别库) 文件

夹里面)拷贝到该文件夹下,然后在工程里面新建一个 ATKNCR 的组,将 atk_ncr.c 和

ATKNCR_M_V2.0.lib 加入到该组下面(这里我们使用内存管理版本的识别库)。最后,将

ATKNCR 文件夹加入头文件包含路径。

关于 ATKNCR_M_V2.0.lib 和 atk_ncr.c 前面已有介绍,我们这里就不再多说,我们在 main.c

里面修改代码如下:

//最大记录的轨迹点数atk_ncr_point READ_BUF[200];//画水平线//x0,y0:坐标 len:线长度 color:颜色void gui_draw_hline(u16 x0,u16 y0,u16 len,u16 color){if(len==0)return;LCD_Fill(x0,y0,x0+len-1,y0,color);}//画实心圆//x0,y0:坐标 r:半径 color:颜色void gui_fill_circle(u16 x0,u16 y0,u16 r,u16 color){u32 i;u32 imax = ((u32)r*707)/1000+1;u32 sqmax = (u32)r*(u32)r+(u32)r/2;u32 x=r;gui_draw_hline(x0-r,y0,2*r,color);for (i=1;i<=imax;i++){if ((i*i+x*x)>sqmax)// draw lines from outside{if (x>imax){gui_draw_hline (x0-i+1,y0+x,2*(i-1),color);gui_draw_hline (x0-i+1,y0-x,2*(i-1),color);}x--;}// draw lines from inside (center)gui_draw_hline(x0-x,y0+i,2*x,color);gui_draw_hline(x0-x,y0-i,2*x,color);}}//两个数之差的绝对值//x1,x2:需取差值的两个数//返回值:|x1-x2|u16 my_abs(u16 x1,u16 x2){if(x1>x2)return x1-x2;else return x2-x1;}//画一条粗线//(x1,y1),(x2,y2):线条的起始坐标//size:线条的粗细程度//color:线条的颜色void lcd_draw_bline(u16 x1, u16 y1, u16 x2, u16 y2,u8 size,u16 color){u16 t;int xerr=0,yerr=0,delta_x,delta_y,distance;int incx,incy,uRow,uCol;if(x10)incx=1; //设置单步方向else if(delta_x==0)incx=0;//垂直线else {incx=-1;delta_x=-delta_x;}if(delta_y>0)incy=1;else if(delta_y==0)incy=0;//水平线else{incy=-1;delta_y=-delta_y;}if( delta_x>delta_y)distance=delta_x; //选取基本增量坐标轴else distance=delta_y;for(t=0;t<=distance+1;t++ )//画线输出{gui_fill_circle(uRow,uCol,size,color);//画点xerr+=delta_x ; yerr+=delta_y ;if(xerr>distance){ xerr-=distance; uRow+=incx;}if(yerr>distance){ yerr-=distance;uCol+=incy;}}}int main(void){u8 i=0; u8 tcnt=0; u8 key;u8 res[10];u16 pcnt=0; u8 mode=4;//默认是混合模式u16 lastpos[2];//最后一次的数据HAL_Init();//初始化 HAL 库 Stm32_Clock_Init(336,8,2,7);//设置时钟,168Mhzdelay_init(168);//初始化延时函数uart_init(115200);//初始化 USARTusmart_dev.init(84);//初始化 USMARTLED_Init();//初始化 LEDKEY_Init();//初始化 KEYLCD_Init(); //初始化 LCDSRAM_Init();//初始化外部 SRAMW25QXX_Init(); //初始化 W25Q256tp_dev.init();//初始化触摸屏my_mem_init(SRAMIN);//初始化内部内存池my_mem_init(SRAMEX);//初始化外部内存池my_mem_init(SRAMCCM);//初始化 CCM 内存池 alientek_ncr_init();//初始化手写识别 while(font_init())//检查字库{LCD_ShowString(60,50,200,16,16,"Font Error!");delay_ms(200);LCD_Fill(60,50,240,66,WHITE);//清除显示}RESTART: POINT_COLOR=RED;Show_Str(60,10,200,16,"探索者 STM32F407 开发板",16,0);Show_Str(60,30,200,16,"手写识别实验",16,0);Show_Str(60,50,200,16,"正点原子@ALIENTEK",16,0);Show_Str(60,70,200,16,"KEY0:MODE KEY2:Adjust",16,0);Show_Str(60,90,200,16,"识别结果:",16,0);LCD_DrawRectangle(19,114,lcddev.width-20,lcddev.height-5);POINT_COLOR=BLUE;Show_Str(96,207,200,16,"手写区",16,0);tcnt=100; tcnt=100;while(1){key=KEY_Scan(0);if(key==KEY2_PRES&&(tp_dev.touchtype&0X80)==0){TP_Adjust();//屏幕校准LCD_Clear(WHITE);goto RESTART;//重新加载界面}if(key==KEY0_PRES){LCD_Fill(20,115,219,314,WHITE);//清除当前显示mode++;if(mode>4)mode=1;switch(mode){case 1:Show_Str(80,207,200,16,"仅识别数字",16,0);break;case 2:Show_Str(64,207,200,16,"仅识别大写字母",16,0);break;case 3:Show_Str(64,207,200,16,"仅识别小写字母",16,0);break;case 4:Show_Str(88,207,200,16,"全部识别",16,0);break;}tcnt=100;}tp_dev.scan(0);//扫描if(tp_dev.sta&TP_PRES_DOWN)//有按键被按下{delay_ms(1);//必要的延时,否则老认为有按键按下.tcnt=0;//松开时的计数器清空if((tp_dev.x[0]=(20+2))&&(tp_dev.y[0]=(115+2))){if(lastpos[0]==0XFFFF) { lastpos[0]=tp_dev.x[0]; lastpos[1]=tp_dev.y[0];}lcd_draw_bline(lastpos[0],lastpos[1],tp_dev.x[0],tp_dev.y[0],2,BLUE);/画线lastpos[0]=tp_dev.x[0]; lastpos[1]=tp_dev.y[0];if(pcnt<200)//总点数少于 200{if(pcnt){if((READ_BUF[pcnt-1].y!=tp_dev.y[0])&&(READ_BUF[pcnt-1].x!=tp_dev.x[0]))//x,y 不相等{READ_BUF[pcnt].x=tp_dev.x[0];READ_BUF[pcnt].y=tp_dev.y[0];pcnt++;}}else{READ_BUF[pcnt].x=tp_dev.x[0];READ_BUF[pcnt].y=tp_dev.y[0];pcnt++;}}}}else //按键松开了{lastpos[0]=0XFFFF;tcnt++;delay_ms(10);i++;if(tcnt==40){if(pcnt)//有有效的输入{printf("总点数:%d",pcnt);alientek_ncr(READ_BUF,pcnt,6,mode,(char*)res);printf("识别结果:%s",res);pcnt=0;POINT_COLOR=BLUE;//设置画笔蓝色LCD_ShowString(60+72,90,200,16,16,res);}LCD_Fill(20,115,lcddev.width-20-1,lcddev.height-5-1,WHITE);}}if(i==30) {i=0; LED0=!LED0;}}}

这里代码看上去比较多,其实很多都是为 lcd_draw_bline 函数服务的,lcd_draw_bline 函数用

于实现画指定粗细的直线,以得到较好的画线效果。而 main 函数,则实现 53.1.2 节提到的功能。

其中,READ_BUF 用来存储输入轨迹点阵,大小为 200,即最大输入不能超过 200 点,注意:

这里我们采集的都是不重复的点阵(即相邻的坐标不相等)。这样可以避免重复数据,而重复的

点阵数据对识别是没有帮助的。

至此,本实验的软件设计部分结束。

53.4 下载验证

在代码编译成功之后,我们下载代码到 ALIENTEK 探索者 STM32F4 开发板上,得到,如

图 53.4.1 所示:

图 53.4.1 手写识别界面

此时,我们在手写区写数字/字母,即可得到识别结果,如图 53.4.2 所示:

图 53.4.2 手写识别结果

按下 KEY0 可以切换识别模式,同时在识别区提示当前模式。按下 KEY2 可以进行屏幕校

准。每次识别结束,会在串口打印本次识别的输入点数和识别结果,大家可以通过串口助手查

看。

实验5-9 使用函数输出水仙花数_正点原子STM32F407探索者开发板资料连载第五十三章 手写识别实验相关推荐

  1. stm32 ucosii消息队列 串口_正点原子STM32F407探索者开发板资料连载第六十三章 UCOSII 实验...

    1)实验平台:alientek 阿波罗 STM32F767 开发板 2)摘自<STM32F7 开发指南(HAL 库版)>关注官方微信号公众号,获取更多资料:正点原子 http://weix ...

  2. application.properties 不识别_阿波罗 STM32F767 开发板资料连载第五十八章 手写识别实验

    1)实验平台:alientek 阿波罗 STM32F767 开发板2)摘自<STM32F7 开发指南(HAL 库版)>关注官方微信号公众号,获取更多资料:正点原子 第五十八章 手写识别实验 ...

  3. stm32f407 spi3 mosi没有输出_正点原子STM32F407探索者开发板资料连载第56章 USB 读卡器实验

    1)实验平台:alientek 阿波罗 STM32F767 开发板 2)摘自<STM32F7 开发指南(HAL 库版)>关注官方微信号公众号,获取更多资料:正点原子 第五十六章 USB 读 ...

  4. fpga驱动rgb液晶屏_正点原子开拓者FPGA开发板资料连载第五十四章基于的数字识别实验...

    1)实验平台:正点原子开拓者FPGA 开发板 2)摘自<开拓者FPGA开发指南>关注官方微信号公众号,获取更多资料:正点原子 3)全套实验源码+手册+视频下载地址:http://www.o ...

  5. stm32看门狗_「正点原子NANO STM32开发板资料连载」第十一章 独立看门狗实验

    1)实验平台:ALIENTEK NANO STM32F411 V1开发板2)摘自<正点原子STM32F4 开发指南(HAL 库版>关注官方微信号公众号,获取更多资料:正点原子 第十一章 独 ...

  6. 基于stm32f429的手写识别_正点原子【STM32-F407探索者】第五十三章 手写识别实验...

    1)资料下载:点击资料即可下载 2)对正点原子Linux感兴趣的同学可以加群讨论:935446741 3)关注正点原子公众号,获取最新资料更新 现在几乎所有带触摸屏的手机都能实现手写识别.本章,我们将 ...

  7. 共阳数码管段码表_正点原子开拓者FPGA开发板资料连载第十一章 静态数码管显示实验...

    1)实验平台:正点原子开拓者FPGA 开发板 2)摘自<开拓者FPGA开发指南>关注官方微信号公众号,获取更多资料:正点原子 3)全套实验源码+手册+视频下载地址:http://www.o ...

  8. 看门狗寄存器c语言代码_「正点原子NANO STM32F103开发板资料连载」第十一章 看门狗实验...

    1)实验平台:[正点原子] NANO STM32F103 开发板 2)摘自<正点原子STM32 F1 开发指南(NANO 板-HAL 库版)>关注官方微信号公众号,获取更多资料:正点原子 ...

  9. 基于stm32f429的手写识别_【连载】STM32开发指南--第五十一章 手写识别实验

    最后将这64维特征分别与模板中的特征进行求距离运算.得到最小的距离为该输入的最佳识别结果输出. 关于手写识别原理,我们就介绍到这里.如果想自己实现手写识别,那得花很多时间学习和研究,但是如果只是应用的 ...

最新文章

  1. 如何检查对象的类型[iOS/Android/Windows Phone]
  2. 【Java规划】DOM XML Parser分解、遍历、创XML
  3. javascript --- 防抖与节流
  4. 工作275:表单验证重置
  5. 再谈设计模式之-1.单例模式
  6. HDU4035 Maze 【树形DP】【期望DP】
  7. MATLAB切比雪夫带通滤波器
  8. Java互联网医院源码,以互联网方式整合优质医生资源,为患者提供MDT多学科会诊、专家咨询、复诊配药等服务。
  9. 洛谷入门-- P3717
  10. html5 实心圆点,html5如何使用canvas画空心圆与实心圆
  11. codeforces beta round 1
  12. 攻防世界 web高手进阶区 9分题 favorite_number
  13. 机器学习算法之聚类算法拓展:Mini Batch K-Means算法
  14. 转型OMO的背后:考虑机构的资金和实力-线上线下融合式教学
  15. 请教 ANDROID 通信信号、网络信号图标的颜色问题
  16. 拨号服务器应用场景有哪些?
  17. twig  之基本语法
  18. sql server left join 优化_网站优化推广
  19. 未来5年做好随时失业的准备
  20. 在PictureBox上显示gif动态图(winform)

热门文章

  1. phpcms 专题功能
  2. outlook导入pst文件
  3. 软件工程大作业之停车场管理系统
  4. 汇编码和机器码相互转化
  5. Android JSON原生解析的几种思路,以号码归属地,笑话大全,天气预报为例演示
  6. 开发Unity3D空战类插件 战机飞行模拟模板Pro版本
  7. html自动补全pspad,Emmet:HTML/CSS 代码快速编写神器
  8. (17)三维图形几何变换
  9. 易语言取c盘文件夹中的文件被占用,易语言检测文件被哪个进程占用的代码
  10. python自动写作ai_python自动写作ai_ai自动写作python python编程100例