Zinnia库及其实现方法研究 (转)
zinnia是一个开源的手写识别库。采用C++实现。具有手写识别,学习以及文字模型数据制作转换等功能。
- 项目地址 [http://zinnia.sourceforge.net ]
- License: NewBSD
- 作者对SVM很有研究. 比同类程序的效率要高效.(同类项目如tegaki)
- 我的目的是通过这个研究简单的手写输入实现方法
- SVM机实现
- 轻量级,可移植
- 线程安全,可供C,C++,Perl,Python,Ruby调用
- 每秒50-100 char的认识速度
- 快速学习
以下为通过源代码研究和debug得出的结论。
可能不是完全准确
接口定义了Character,Recognizer,Result,Trainer等4个接口类。然后分别使用CharacterImpl, RecognizerImpl, ResultImpl, TrainerImpl实现。
公用方法- 定义了一个模板类 read_static 用来从一个大数据集合中读取模板类大小的数据,源数据指针根据读取长度自加。和read_ptr的区别是这个是使用memcpy,读取内容到新内存里,内存的大小即模板类的大小。而read_ptr只是返回指针,并没有指针外的内存占用。
- 定义了一个指针读取类read_ptr用来读取数据指针,源数据指针根据读取长度自加。
- 存储数据普遍使用vector,在每次使用前首先通过resize方法对vector的大小进行重定义。
- 文字模型数据采用如下数据结构。
- 使用一个Vector<Model>将所有的模型数据载入。
- 可以自由设置手写框的大小。在内部处理中所有笔迹都被转化为1*1的手写框内的坐标来处理。
- 通过Character类的add方法增加坐标。add的第一个参数为当前笔画,第二个参数为坐标点。通过重复使用add方法可以加入多笔输入笔迹。其中每笔包含多个坐标点数据。
由于作者没有提供界面程序。所以我使用MFC做了一个界面。包括
- 笔迹输入区域用于接受笔迹输入。300X300pixel
- 文字显示区域。显示笔迹识别结果
- 辞书切换radiobutton。用来切换日语输入模式和汉语输入模式
- 识别按钮。当前笔迹识别并消除当前笔迹。
- OK按钮。退出。
- 首先进行坐标转换。即将用户设置的a*b大小的输入框输入的坐标点转化为1*1大小输入框下的坐标点。即坐标x坐标均缩小至1/a,y坐标均缩小至1/b。存储至node链表中。node包含x和y坐标。结构如下
- 然后使用显著点寻找算法。首先从第一笔的笔记数据开始。以起点first和终点last作为pair[0]的初始值。然后在起点到终点之间的其他点里面寻找一个最显著的点。(作者设定的显著点特征值为0.001。即当dist^2>0.001的时候此点是显著的)。找到最显著的点best之后,再使用递归的方式,在first和best之间(此时best为当前轮回的last)以及best和last之间寻找最显著点。最终寻找到所有的最显著点。以“张”字的第一笔为例。起点终点之间的最显著点就是折点。而起点和折点之间,折点和终点之间没有其他显著点。这样这一笔可以查找出一个显著点。并且生成3个node_pair即 起点-终点 起点-折点 折点-终点
- 然后针对第一笔的3个node_pair添加feature数据。其中每一对node_pair会被添加12个feature。分别是
- 起点终点的距离
- 起点终点所成直线的角度
- 起点距离x轴中心线的距离
- 起点距离y轴中心线的距离
- 终点距离x轴中心线的距离
- 终点距离y轴中心线的距离
- 起点和输入框中心点所成直线的角度
- 终点和输入框中心点所成直线的角度
- 起点距离中心点距离
- 终点距离中心点距离
- 起点终点x轴上的投影距离
- 起点终点y轴上的投影距离
- 以【串】字为实例。node_pair为17个。如图
- 其中这些node_pair可以根据feature结构的index属性分类。包括2个类别。
- 实体笔迹有7笔 (1,345,6,9,111213,15,17)。其中实体笔迹是基于1000*n来定义index的。7笔即在0 – 6*1000 这个范围。
- 非实体笔迹有6笔(2,6,8,10,14,16)就是这些不是实际笔迹输入。只是画完一笔之后的终点和下一笔的起点之间的连线。这类笔迹使用100000+(n+1)*1000来定义index。6笔即在101000-106000这个范围。
- 这样会有17*12 = 204 个feature。然后加上每个字最初的一个和最后的2个feature。一共是207个feature。正是通过这207个feature进行文字的识别,并在文字模型库里面进行匹配。得到前十个最相似的文字。
- 匹配将对汉字库里面所有文字进行匹配。匹配过程是将【串】字204个feature与汉字库当前汉字的所有feature进行匹配。当【串】字某一feature的index与汉字库当前汉字的某一feature的index相同时。即取两个feature值的乘积。如果index不一致则继续下一个链表的结点来比对index。
- 将取得所有乘积相加。最后再加上汉字库当前汉字的固有属性bias。最终形成汉字库当前汉字的最终值。得到所有汉字的最终值之后按从小到大排出前十位即为识别的最终结果。
学习功能依靠SVM机实现。还没有来得及分析这部分代码。
打算专门对SVM做一个研究。