HeyCHのブログ

慢性疲労のへいちゃんです

【C#】キャンパスに書いた文字を文字列として認識してみよう

音声認識があれば文字認識もあるよなってことで、今回は文字列を認識してみたいと思います。
まだやってないのでわかりませんが、僕のように文字が下手くそな場合、認識されないかもしれません。

DLLの参照

文字の認識にはInkAnalyzerというものを使う事になるのですが、これはDLLを別途インストールする必要があるみたいです。
インストールするには「Windows SDK for Vista」をインストールすればそれに入っているらしいのですが、見当たりません。
いろいろ探していたら、それっぽいものがありました。
Download Microsoft® Windows® Software Development Kit Update for Windows Vista™ from Official Microsoft Download Center
しかし、これにもDLLは入っておらず、違うものだろうという結論に達しました。


そんなこんなでいろいろありまして、
結局のところWindows10に標準搭載されているOCRを使う事にしたんですけども。
手書きレベルじゃ何も反応しませんでした。ちゃんちゃん。
一応コードの方を記載しておきますが、全部どこかからのコピペですw
あと、重要なのは参照設定なので、OCR使いたいって方はまず参照設定をきちんとしてからやったほうが良いです。
僕の場合は最後まできれいにエラーが取れることはありませんでした。(一応ビルドはできてるけども)

using System;
using System.Windows;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Collections.ObjectModel;
using Windows.Media.Ocr;
using System.IO;
using Windows.Graphics.Imaging;
using Windows.Storage.Streams;
using System.Threading.Tasks;
using Windows.Storage;
using Windows.Foundation;
using System.Runtime.CompilerServices;

namespace InkCanvas {
    /// <summary>
    /// MainWindow.xaml の相互作用ロジック
    /// </summary>
    public partial class MainWindow : Window {
        ViewModel vm = new ViewModel();
        public MainWindow() {
            InitializeComponent();
            this.DataContext = vm;
        }

        private async void Button_Click(object sender, RoutedEventArgs e) {
            OcrEngine orc = OcrEngine.TryCreateFromUserProfileLanguages();
            var sbmp = await GetSoftwareBitmap();
            var ocrResult = await orc.RecognizeAsync(sbmp);
            MessageBox.Show(ocrResult.Text);
        }
        private async Task<SoftwareBitmap> GetSoftwareBitmap() {
            var rect = inkCanvas.Strokes.GetBounds();
            DrawingVisual dv = new DrawingVisual();
            DrawingContext dc = dv.RenderOpen();

            // 描画エリアの位置補正(補正しないと黒い部分ができてしまう)
            dc.PushTransform(new TranslateTransform(-rect.X, -rect.Y));

            // 描画エリア(dc)に四角形を作成
            // 四角形の大きさはストロークが描画されている枠サイズとし、
            // 背景色はInkCanvasコントロールと同じにする
            dc.DrawRectangle(inkCanvas.Background, null, rect);

            // 上記で作成した描画エリア(dc)にInkCanvasのストロークを描画
            inkCanvas.Strokes.Draw(dc);
            dc.Close();

            // ビジュアルオブジェクトをビットマップに変換する
            RenderTargetBitmap rtb = new RenderTargetBitmap(
                (int)rect.Width, (int)rect.Height,
                96, 96,
                PixelFormats.Default);
            rtb.Render(dv);

            // ビットマップエンコーダー変数の宣言
            var enc = new PngBitmapEncoder();

            if (enc != null) {
                // ビットマップフレームを作成してエンコーダーにフレームを追加する
                enc.Frames.Add(System.Windows.Media.Imaging.BitmapFrame.Create(rtb));
                // ファイルに書き込む
                System.IO.Stream stream = System.IO.File.Create("tmp.png");
                enc.Save(stream);
                stream.Close();
            }

            SoftwareBitmap softwareBitmap;
            var folder = Directory.GetCurrentDirectory();
            var appFolder = await StorageFolder.GetFolderFromPathAsync(folder);
            var bmpFile = await appFolder.GetFileAsync(@"tmp.png");
            using (IRandomAccessStream stream = await bmpFile.OpenAsync(FileAccessMode.Read)) {
                var decoder = await Windows.Graphics.Imaging.BitmapDecoder.CreateAsync(stream);
                softwareBitmap = await decoder.GetSoftwareBitmapAsync();
            }
            return softwareBitmap;
        }
    }
    public class ViewModel {
        ObservableCollection<string> _AnalList = new ObservableCollection<string>();
        public ObservableCollection<string> AnalList {
            get { return _AnalList; }
            set { _AnalList = value; }
        }
    }
    public static class TaskEx {
        public static Task<T> AsTask<T>(this IAsyncOperation<T> operation) {
            var tcs = new TaskCompletionSource<T>();
            operation.Completed = delegate  //--- コールバックを設定
            {
                switch (operation.Status)   //--- 状態に合わせて完了通知
                {
                    case AsyncStatus.Completed: tcs.SetResult(operation.GetResults()); break;
                    case AsyncStatus.Error: tcs.SetException(operation.ErrorCode); break;
                    case AsyncStatus.Canceled: tcs.SetCanceled(); break;
                }
            };
            return tcs.Task;  //--- 完了が通知されるTaskを返す
        }
        public static TaskAwaiter<T> GetAwaiter<T>(this IAsyncOperation<T> operation) {
            return operation.AsTask().GetAwaiter();
        }
    }
}