SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

UWPアプリ開発の最前線

日本語OCRによる文字認識
~WPFなどの.NET FrameworkアプリやUWPアプリからWindows 10のOCRエンジンを使う

UWPアプリ開発の最前線 第4回


  • X ポスト
  • このエントリーをはてなブックマークに追加

認識結果を使う

 認識結果はOcrResultクラス(Windows.Media.Ocr名前空間)のオブジェクトとして返ってきます(次の図)。

OcrResultクラスのプロパティ
文字認識結果を表すOcrResultクラス

 OCRエンジンが認識した最小の単位が、緑色で示したOcrWordオブジェクト(Windows.Media.Ocr名前空間)です。英語では1単語、日本語では1文字になります。OcrWordオブジェクトは、認識した文字列を示すTextプロパティ(string型)と認識した領域を示すBoundingRectプロパティ(Windows.Foundation.Rect型)を持っています。なお、BoundingRectプロパティの示す座標は、OcrResultオブジェクトのTextAngleプロパティが示す角度だけ回転させて解釈しなければなりません(後述)。

 このOcrWordオブジェクトを1行分だけ集めたコレクションが、紫色で示したOcrLineオブジェクト(Windows.Media.Ocr名前空間)のWordsプロパティ(IReadOnlyList<OcrWord>型)に格納されています。1行分のデータを表しているから「OcrLine」クラスというわけですね。OcrLineオブジェクトには、1行分の文字列を連結したTextプロパティ(string型)もあります。半角スペースを挟んで連結されています。

 そして、OcrLineオブジェクトを集めたコレクションが、青色で示したOcrResultオブジェクトLinesプロパティ(IReadOnlyList<OcrLine>型)に格納されています。OcrResultオブジェクトは、半角スペースを挟んですべての行を連結したTextプロパティ(string型)と、認識した領域の回転角度を示すTextAngleプロパティ(double?型)も持っています。TextAngleプロパティが示しているのは、認識した画像の中心を原点として、認識したすべての領域(BoundingRectプロパティのRectオブジェクト)を回転させる角度です(⇒MainPage.Ocr.csのDisplayOcrResultメソッド参照)。

 OcrWordオブジェクトのTextプロパティを連結するときに半角スペースが挿入されるという仕様には、注意が必要です。アルファベット圏ではそれで良いのでしょうけれど、日本語ではカナ・漢字の間にすべて半角スペースが入ってしまいます。例として、「OCRエンジンの使い方(UWP編)」という文字列をOCRエンジンに認識させた場合を、次の画像に示します。

カナ・漢字の間には必ず半角スペースが入る
カナ・漢字の間には必ず半角スペースが入る(UWPアプリ)
クリックして拡大すると、「OCR」と「エ」も別の領域として認識しているのが分かります

 「OCR」と「(UWP」が単語として認識されています。残りはすべて1文字ずつの「単語」として、「工」・「ン」・「ジ」・「ン」・「の」・「使」・「い」・「方」・「編」・「)」の間には半角スペースが挿入されています。実際にOCRエンジンを利用するときには、OcrWordのTextを自前で結合するコードを書く必要がありそうです。例えば、前のOcrWordの末尾と後のOcrWordの先頭がともに英数字のときだけ半角空白を挟む、といったように。なお、この例ではカタカナの「エ」を漢字の「工」として認識してしまっています。

 また、OcrLineの順序が入れ替わって認識される場合があります。各OcrLineの先頭のBoundingRectの位置関係を調べれば正しいOcrLineの順序関係を推定できると思いますが、これも実際に使うにあたっては注意が必要な点です。

OCRエンジンの使い方(WPF編)

 WPFなどの.NET FrameworkアプリからOCRエンジンを使う場合も、基本的にはここまで説明してきたUWPアプリの場合と同じです。大きく異なる点として、UWPのAPIを使えるようにすることと、画像データをSoftwareBitmap型に変換しなければならないことがあります。

UWPのAPIを使えるようにする

 OCRエンジンはWindows 10の最初(ビルド10240)から提供されているAPIなので、.NET Frameworkアプリ(WPF/Windows Forms/コンソールプログラムなど)のプロジェクトを作ったら、UwpDesktopを導入するだけです。

 詳しくは、連載第2回「超簡単! WPFなどの.NETのアプリからUWPのAPIを使う ~日本語の読み仮名を取得するAPIを題材に」をご覧ください。

NuGetパッケージマネージャーでUwpDesktopを検索したところ
NuGetからUwpDesktopを導入する(連載第2回より)

画像データをSoftwareBitmap型に変換する

 OCRエンジンは画像データとしてUWPのSoftwareBitmap型しか受け付けません。そこで、.NETのアプリで扱っている画像データをどうやってSoftwareBitmap型に変換するかが問題になります。ここでは、WPFの場合を考えてみます。

 WPFで画像データというとまずBitmapImageクラスが思い浮かぶかもしれませんが、画像フォーマットの変換を考えるとBitmapFrameクラスを中心にしたほうが便利そうです。BitmapFrameは、画像のエンコーダー/デコーダーが扱える型です。なお、BitmapFrameオブジェクトも、BitmapImageオブジェクトと同様に、ImageコントロールのSourceプロパティに直接セットして画像を表示できます。

 WPFのBitmapFrameオブジェクトをUWPのSoftwareBitmapオブジェクトに変換するには、次のコードのようにします(⇒UwpSoftwareBitmapHelper.cs)。まずWPFの世界で、BitmapFrameオブジェクトをバイト配列に変換します。次に、UWPの世界に移って、バイト配列をUWPのストリームに変換した後、UWPのデコーダーを使ってSoftwareBitmapオブジェクトに変換します。

WPFのBitmapFrameオブジェクトをUWPのSoftwareBitmapオブジェクトに変換するコード
using System;
using System.IO;
using System.Threading.Tasks;
using System.Windows.Media.Imaging;

// UWPのSoftwareBitmap関連のエイリアス(UWP APIの使用箇所を明確にするため)
using UwpSoftwareBitmap = Windows.Graphics.Imaging.SoftwareBitmap;
using UwpBitmapDecoder = Windows.Graphics.Imaging.BitmapDecoder;
using UwpBitmapPixelFormat = Windows.Graphics.Imaging.BitmapPixelFormat;
using UwpBitmapAlphaMode = Windows.Graphics.Imaging.BitmapAlphaMode;
using UwpInMemoryRandomAccessStream = Windows.Storage.Streams.InMemoryRandomAccessStream;
using UwpDataWriter = Windows.Storage.Streams.DataWriter;

namespace WpfApp
{
  public static class UwpSoftwareBitmapHelper
  {
    public static async Task<UwpSoftwareBitmap> ConvertFrom(BitmapFrame sourceBitmap)
    {
      // BitmapFrameをBMP形式のバイト配列に変換
      byte[] bitmapBytes;
      var encoder = new BmpBitmapEncoder(); // ここは.NET用のエンコーダーを使う
      encoder.Frames.Add(sourceBitmap);
      using (var memoryStream = new MemoryStream())
      {
        encoder.Save(memoryStream);
        bitmapBytes = memoryStream.ToArray();
      }

      // バイト配列をUWPのIRandomAccessStreamに変換
      using (var randomAccessStream = new UwpInMemoryRandomAccessStream())
      {
        using (var outputStream = randomAccessStream.GetOutputStreamAt(0))
        using (var writer = new UwpDataWriter(outputStream))
        {
          writer.WriteBytes(bitmapBytes);
          await writer.StoreAsync();
          await outputStream.FlushAsync();
        }

        // IRandomAccessStreamをSoftwareBitmapに変換
        // (ここはUWP APIのデコーダーを使う)
        var decoder = await UwpBitmapDecoder.CreateAsync(randomAccessStream);
        var softwareBitmap 
          = await decoder.GetSoftwareBitmapAsync(UwpBitmapPixelFormat.Bgra8,
                                                 UwpBitmapAlphaMode.Premultiplied);
        return softwareBitmap;
      }
    }
  }
}

 このようにしてSoftwareBitmapオブジェクトが手に入ったら、前述したように、あとはOCRエンジンのインスタンスを作ってRecognizeAsyncメソッドを呼び出すだけです(⇒MainWindow.Ocr.csのRecognizeBitmapAsyncメソッド)。

まとめ

 Windows 10に標準で搭載されているOCRエンジンは、日本語もそこそこ認識してくれます。入力の補助としてOCRを使うアプリがいろいろ考えられると思います。

 OCRエンジンはUWPのAPIとして提供されているので、もちろんUWPアプリから使えます。 WPFなどの.NETアプリからはSoftwareBitmap型の扱いがちょっと面倒になりますが、そこさえクリアできれば.NETアプリからもOCRエンジンは簡単に使えます。

 このOCRエンジンのように、アプリから利用できるのにあまり知られていないWindows 10の機能がたくさんあります。「Windows 10のこの機能ってプログラムから使えるのかな?」と気になったら、UWP APIの一覧を眺めてみてください。連載第2回で説明したように、UI以外の多くのUWP APIは.NET Frameworkのアプリからも使えます。ちなみに、このUWP APIの一覧には開発中のAPIも載っています。名前空間に「Preview」と付いているのがそれで、本稿執筆時点ではWindows.AI.MachineLearning.Preview名前空間(マシンラーニングを利用するためのAPIですね!)やWindows.Devices.Input.Preview名前空間(キーボードやマウスなどの入力を扱う名前空間ですがPreviewでは視線入力を開発中)などが公開されています。

UWPアプリに興味が湧いてきた人へ

 Windowsデスクトップ用アプリの開発はWindows Formsなら経験があるけど、ちょっとUWPアプリ開発も気になってきたという方へ。ぴったりの電子書籍を書きました!

UWP アプリ開発 101 第2版: Windows Forms 開発者のための C# / XAML による UWP アプリ開発入門』 (BluewaterSoft 2017/7/1)
Kindle版UWPアプリ版があります。
・どちらも無料で最初の方を読めるので、とりあえず試し読みをしてみてください。

 また、Microsoft公式のチュートリアルやたくさんのサンプルコードなどもあります。UWPアプリの開発環境を整えてUWPアプリのプロジェクトを作れるようになったら、これらのドキュメントやサンプルも参考にしてみてください。

この記事は参考になりましたか?

  • X ポスト
  • このエントリーをはてなブックマークに追加
UWPアプリ開発の最前線連載記事一覧

もっと読む

この記事の著者

biac(ばいあっく)

HONDA R&Dで自動車の設計をやっていた機械屋さんが、技術の進化スピードに魅かれてプログラマーに。以来30年ほど、より良いコードをどうやったら作れるか、模索の人生。わんくま同盟の勉強会(名古屋)で、よく喋ってたりする。2014/10~2019/6 Microsoft MVP (Windows Devel...

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

  • X ポスト
  • このエントリーをはてなブックマークに追加
CodeZine(コードジン)
https://codezine.jp/article/detail/10748 2019/11/10 20:59

おすすめ

アクセスランキング

アクセスランキング

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー

アクセスランキング

アクセスランキング