Zinnia庫及其實作方法研究 (轉)
zinnia是一個開源的手寫識别庫。采用C++實作。具有手寫識别,學習以及文字模型資料制作轉換等功能。
- 項目位址 [http://zinnia.sourceforge.net ]
- License: NewBSD
- 作者對SVM很有研究. 比同類程式的效率要高效.(同類項目如tegaki)
- 我的目的是通過這個研究簡單的手寫輸入實作方法
Zinnia庫特點
- 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的大小進行重定義。
讀取文字模型檔案
- 文字模型資料采用如下資料結構。
struct Model{ const char *character; // utf-8 character float bias; // const FeatureNode *x; //features};
- 使用一個Vector<Model>将所有的模型資料載入。
讀取使用者輸入的手寫筆迹
- 可以自由設定手寫框的大小。在内部進行中所有筆迹都被轉化為1*1的手寫框内的坐标來處理。
- 通過Character類的add方法增加坐标。add的第一個參數為目前筆畫,第二個參數為坐标點。通過重複使用add方法可以加入多筆輸入筆迹。其中每筆包含多個坐标點資料。
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsIyZwpmLjJTMhJDOihjZ3QmZkVzN2cjM1EjM5IWYvwVblRXavw1YpB3LcxWYpxWZiJzM0ETM2ITMz8CXt92YuUHZpFmYuM3b09GawlGavw1LcpDc0RHaiojIsJye.jpg)
由于作者沒有提供界面程式。是以我使用MFC做了一個界面。包括
- 筆迹輸入區域用于接受筆迹輸入。300X300pixel
- 文字顯示區域。顯示筆迹識别結果
- 辭書切換radiobutton。用來切換日語輸入模式和漢語輸入模式
- 識别按鈕。目前筆迹識别并消除目前筆迹。
- OK按鈕。退出。
筆迹識别feature提取
- 首先進行坐标轉換。即将使用者設定的a*b大小的輸入框輸入的坐标點轉化為1*1大小輸入框下的坐标點。即坐标x坐标均縮小至1/a,y坐标均縮小至1/b。存儲至node連結清單中。node包含x和y坐标。結構如下
struct Node { float x; float 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做一個研究。