zoukankan      html  css  js  c++  java
  • 人工智能实战2019

    项目 内容
    课程 人工智能实战2019
    作业要求 第4次作业
    课程目标 学习人工智能基础知识
    本次作业对我的帮助 STM32进阶,LDA 算法
    参考文献 正点原子LDA

    手写识别简介


    不再赘述手写识别的相关背景。。。

    目前用于手写识别的设备有许多种,比如电磁感应手写板、压感式手写板、触摸屏、触摸屏、超声波笔等。ALIENTEK MiniSTM32 开发板自带 2.8 寸电阻型触摸屏,可以用来作为手写识别的输入设备。
    手写数字识别系统如下图所示:
    手写数字识别系统
    虚线部分为训练学习过程,对数据样本进行传统的方向特征提取,提取后特征维数为 512 维。对于单片机来说,如此合成的模板库的存储量过大,需要降维处理。常用方法为线性判别分析(Linear Discriminant Analysis, LDA)。
    LDA 原理是,将带上标签的数据,通过投影的方法,投影到维度更低的空间中,使得投影后的点,会形成按类别区分,一簇一簇的情况;相同类别的点,将会在投影后的空间中更接近。

    对于识别过程,首先得到触屏输入的有序轨迹,然后进行预处理,包括重采样、归一化处理。重采样主要是因为不同的输入设备、不同的输入方式所产生的有序序列不同,为了达到更好的识别结果,我们需要对训练样本和识别输入进行重采样处理。归一化是因为不同的书写风格、采样分辨率的差异会导致字体大小不同,需要对输入轨迹进行归一化。
    预处理后的输入为 (64 imes 64),切分成 (8 imes 8) 的小方格,每个方格 (8 imes 8) 个像素。总共 64 个格子,于是一个样本最终能得到 (64 imes 8=512) 维特征。
    通过 LDA 降维计算,得到 (512 imes 64) 维的矩阵,通过矩阵运算得到 64 维的最终特征值。

    [left[d_{1}, d_{2}, cdots, d_{512} ight] imes left[ egin{array}{ccc}{l} & {cdots} & {l} \ {vdots} & {ddots} & {vdots} \ {l} & {cdots} & {l}end{array} ight]=left[f_{1}, f_{2}, cdots, f_{64} ight] ]

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

    [output=underset{i in[1,62]}{arg min }left{left(f_{1}-f_{1}^{i} ight)^{2}+left(f_{2}-f_{2}^{i} ight)^{2}+cdots+left(f_{64}-f_{64}^{i} ight)^{2} ight} ]

    实现步骤:
    1.调用初始化函数

    • 初始化识别器

    2.获取输入的点阵数据

    • 至少输入2个不同坐标的点阵数据
    • 输入的点不要太多,太多的点阵数据需要更多的内存,推荐的输入点数范围为100-200

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

    • 有5个参数,分别为输入轨迹的坐标集、坐标集点坐标的个数、期望输出的结果(按匹配程度排序输出)、模式设置
    • 结果采用 ASCII 码格式存储

    4.调用终止函数

    • 终止识别器
    • 如果继续识别,重复步骤 2 和步骤 3

    硬件连接


    硬件比较简单,开发板+液晶模块即可。
    我们在手写区写数字或字符,每次写入结束后,自动进入识别状态,然后将识别结果输出到LCD模块,同时打印到串口。通过按KEY1,可以进行模式切换;通过按KEY0,可以进入触摸屏校准。DSO用于表示程序运行状态。

    软件实现


    正点原子提供手写识别的lib供大家使用,但不提供源码。。。
    各种具体辅助函数的实现方法不去赘述,主函数如下:

    //最大记录的轨迹点数
    atk_ncr_point READ_BUF[200];
    int main(void)
    {
    	u8 i = 0;
    	u8 tcnt = 0;
    	u8 res[10];
    	u8 key;
    	u16 pcnt = 0;
    	u8 mode = 4; //默认是混合模式
    	Stm32_Clock_Init(9); //系统时钟设置
    	delay_init(72); //延时初始化
    	uart_init(72, 9600); //串口 1 初始化
    	LCD_Init(); //初始化液晶
    	LED_Init(); //LED 初始化
    	KEY_Init(); //按键初始化
    	TP_Init(); //触摸屏初始化
    	mem_init(); //初始化内部内存池
    	alientek_ncr_init();//初始化手写识别
    	POINT_COLOR = RED;
    	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(40, 10, 200, 16, "MiniSTM32 开发板", 16, 0);
    	Show_Str(40, 30, 200, 16, "手写识别实验", 16, 0);
    	Show_Str(40, 50, 200, 16, "就起这个名字吧@AI实战2019", 16, 0);
    	Show_Str(40, 70, 200, 16, "KEY1:MODE KEY0:Adjust", 16, 0);
    	Show_Str(40, 90, 200, 16, "识别结果:", 16, 0);
    	LCD_DrawRectangle(19, 114, 220, 315);
    	POINT_COLOR = BLUE;
    	Show_Str(96, 207, 200, 16, "手写区", 16, 0);
    	tcnt = 100;
    	while (1)
    	{
    		key = KEY_Scan(0);
    		if (key == KEY0_PRES)
    		{
    			TP_Adjust(); //屏幕校准
    			LCD_Clear(WHITE);
    			goto RESTART; //重新加载界面
    		}
    		if (key == KEY1_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] < 220 && tp_dev.x[0] >= 20) && (tp_dev.y[0] < 315 && tp_dev.y[0] >= 115))
    			{
    				TP_Draw_Big_Point(tp_dev.x[0], tp_dev.y[0], BLUE);//画图
    				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 //按键松开了
    		{
    			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, 219, 314, WHITE);
    			}
    		}
    		if (i == 30) { i = 0; LED0 = !LED0; }
    	}
    }
    

    验证


    在代码编译成功后,我们烧录程序到 ALIENTEK MiniSTM32 开发板上:
    VK9Y2F.jpg
    在手写区手写数字/字母,即可得到识别结果:
    VK9ar9.jpg
    每次识别结束,会在串口打印概率最高的识别结果,可通过串口调试助手查看:
    VJBNKe.png

    总结


    • 可作为小型应用,以识别结果为反馈,通过触摸屏搜集高质量的手写图片
  • 相关阅读:
    汉诺塔
    协变和逆变随笔
    NetCore开发第一步 Log4Net日志引入
    插入排序算法
    选择排序算法
    冒泡排序算法
    排序算法
    线性链表
    SAN
    SAM -- Chap 8 提升方法 自我梳理
  • 原文地址:https://www.cnblogs.com/wangshihong/p/10867431.html
Copyright © 2011-2022 走看看