2015年5月13日

WPFでの文字認識(InkAnalyzerを使う)

Windowsは手書き認識エンジンを持っている.というわけでそれを使ってみることにした.存在自体は知っていたけど,単にあたえられたものを文字として認識するだけだと思っていた.そうだとノート一ページ丸ごとあたえてもまともに解析してくれるとは思えない.実際にはきちんとレイアウトの解析までしてくれる模様.目的は「手書きノートに文字列を埋め込んだPDFを作る」こと.

実際にやってみた簡単なテスト.わかりやすくするため,認識した場所の上に赤文字で出しています.数式もないし,ということで文字単位では100%正しく認識されています.ただ,英単語が基本的にぶちぎられているので,検索で「This」とやってもひっかからない……意味ないし.適当に調整したらどうにかなるかなぁ.

WPFで作る.Windows.Formsやストアアプリでも作れるけど,方法が各々違うらしい.ストアアプリが一番楽で,というかそれ以外は追加でのインストールが必要.というわけで環境準備から.WDN Engineer's Blogに色々情報があるのでそれを参照.まずはWindows SDK for Vistaをインストール.Vistaって…….その後参照にIAWinFX.dllとIALoader.dllを追加.手元では以下のパスにあった.

  • C:\Program Files\Reference Assemblies\Microsoft\Tablet PC\v1.7\IAWinFx.dll
  • C:\Program Files\Microsoft SDKs\Windows\v6.0\Bin\IALoader.dll

更に,App.configのstartupを次のように書き換える.もともと

<startup> 
    <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5"/>
</startup>
となっていたので,
<startup useLegacyV2RuntimeActivationPolicy="true">
    <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5"/>
</startup>
とした.

これでSystem.Windows.Ink.InkAnalyzerが使えるようになっている.とりあえず解析させる.描かれたストロークの入っているStrokeCollectionクラスStrokesを解析させるには

using System.Windows.Ink;

using(var analyzer = new InkAnalyzer()){
    analyzer.AddStrokes(Strokes);
    analyzer.Analyze();
}
とする.これで解析結果がanalyzer内に格納される.やっぱりではあるけど,それなりに時間がかかる.とりあえず手元の目的が「解析結果をPDFに透明テキストで埋め込む」なので,時間は気にしないことにした.検索とかをしたい場合は考えないとなぁ.解析結果はツリー構造になっているらしい.なんだか色々タイプがあるみたいだが,簡単のためにツリー構造とかは一切無視し,さらに認識された単語のみを取り出すことにする.

using System.Windows.Ink;

using(var analyzer = new InkAnalyzer()){
    analyzer.AddStrokes(Strokes);
    analyzer.Analyze();
    var nodes = analyzer.FindNodesOfType(ContextNodeType.InkWord);
    foreach(var node in nodes) {
        var rect = node.Location.GetBounds();
        // 何か描画しているつもり
        DrawString(((InkWordNode) node).GetRecognizedString(), rect.Left, rect.Bottom);
    }
}

色々適当ですが大体こんな感じ.FindNodesOfTypeで指定したタイプ(この場合はInkWord=認識された単語)を列挙.もしツリー構造を反映したい場合はanalyzer.RootNodeからSubNodesをたどっていけばよいようです.例えば上と等価な処理としてはこんな感じ.

using System.Windows.Ink;
void Func(ContextNode node){
    if(node.Type == ContextNodeType.InkWord){
        var rect = node.Location.GetBounds();
        DrawString(((InkWordNode) node).GetRecognizedString(), rect.Left, rect.Bottom);
    }
    foreach(var n in node.SubNodes) Func(n);
}

using(var analyzer = new InkAnalyzer()){
    analyzer.AddStrokes(Strokes);
    analyzer.Analyze();
    Func(analyzer.Rootnode);
}

0 件のコメント:

コメントを投稿

コメントの追加にはサードパーティーCookieの許可が必要です