OCRエンジンの使い方(UWP編)
おおまかな流れ
Windows 10のOCRエンジンを使う処理のおおまかな流れは、次の図のようになります。
まず最初に、利用できる言語の一覧を取得します。これは、前述したように、読み取りたい文書に適した言語を指定しないと認識精度が落ちるからです。一覧を取得したら、必要な言語が使えることをコードで確認したり、一覧を提示してそこからユーザーに選択してもらったりします。
次に、認識させたい画像を取り込みます。このとき、SoftwareBitmapクラス(Windows.Graphics.Imaging名前空間)のオブジェクトにしなければなりません。UWPアプリでは特に問題ありませんが、WPFなどの.NET Frameworkアプリではちょっと手間です(後述)。
OCRエンジンの呼び出し自体は簡単です。言語を指定してOcrEngineクラス(Windows.Media.Ocr名前空間)のインスタンスを作り、RecognizeAsyncメソッドに画像を渡すだけです。
サポート言語の一覧を取得する
利用できる言語の一覧を取得するには、OcrEngineクラスのAvailableRecognizerLanguages静的プロパティを見ます。これが、利用できる言語を表すLanguageクラス(Windows.Globalization名前空間)のコレクションになっています。
また、現在のユーザー設定に基づく言語を取得することもできます。OcrEngineクラスのTryCreateFromUserProfileLanguages静的メソッドを呼び出すと、ユーザーのプロファイルに基づいたOcrEngineインスタンスが生成されます。そのRecognizerLanguageプロパティ(Language型)が、(OCRエンジンにとっての)現在のユーザーの言語です。これは、ユーザーがWindowsの「設定アプリ」で設定している言語と同じとは限りません。英語以外の言語の場合、OCRエンジンのサポートがあるとは限りませんし、また、サポートされていてもそのランゲージパックがインストールされている必要があるからです。
例として、利用できる言語の一覧を取得してコンボボックスに表示し、さらにユーザーの言語を選択状態にするC#のコードは、次のようになります(MainPage.xaml.csのsetLanguageListローカル関数)。
// サポート言語の一覧を得る IReadOnlyList<Language> langList = OcrEngine.AvailableRecognizerLanguages; // コンボボックス「LangComboBox」にサポート言語の一覧を表示する this.LangComboBox.ItemsSource = langList; // 選択肢として表示するのはDisplayNameプロパティ(「日本語」など) this.LangComboBox.DisplayMemberPath = nameof(Windows.Globalization.Language.DisplayName); // SelectedValueとしてLanguageTagプロパティを使う(「ja」など) this.LangComboBox.SelectedValuePath = nameof(Windows.Globalization.Language.LanguageTag); // ユーザープロファイルに基いてOCRエンジンを作る var ocrEngine = OcrEngine.TryCreateFromUserProfileLanguages(); // その認識言語をコンボボックスで選択状態にする this.LangComboBox.SelectedValue = ocrEngine.RecognizerLanguage.LanguageTag;
言語を1つも追加していない日本語版Windows 10で実行してみると、コンボボックスには日本語と英語(en-us)が表示されます(次の画像)。英語だけは、ランゲージパックの導入に関係なく、常に利用できるようです。
後ろに写っている「設定アプリ」で既定の日本語環境だけと分かりますが、英語も選択できます
そのほかの言語は、ランゲージパックを導入すると利用可能になります。次の画像は、中国語(zh-Hans-CN)のランゲージパックがインストールされている状態です。
後ろに写っている「設定アプリ」で英語と中国語のランゲージパックが導入済みだと分かります
画像を取り込んでSoftwareBitmap型にする
OCRエンジンが認識できる画像データはSoftwareBitmap型だけです。UWPアプリで画像ファイルやクリップボードの画像データを認識させるには、そのストリームから次のコードのようにしてBitmapDecoderクラス(Windows.Graphics.Imaging 名前空間)を使ってSoftwareBitmapオブジェクトを作ります(⇒SoftwareBitmapHelper.cs)。画像をImageコントロールに表示もしたいときは、Imageコントロールが受け付けてくれる画像フォーマットにしておく必要があります(BGRA8形式でアルファチャネルはプリマルチプライ処理済み)。
// 画像データのストリームからSoftwareBitmapを作る
public static async Task<SoftwareBitmap> ConvertToSoftwareBitmap(IRandomAccessStream stream)
{
BitmapDecoder decoder = await BitmapDecoder.CreateAsync(stream);
return await decoder.GetSoftwareBitmapAsync(BitmapPixelFormat.Bgra8,
BitmapAlphaMode.Premultiplied);
}
また、MediaCaptureクラス(Windows.Media.Capture 名前空間)を使ってWebカメラから静止画をキャプチャするときも、それをImageコントロールに表示するなら、次のコードのようにしてSoftwareBitmapオブジェクトの画像フォーマットを修正する必要があります。筆者の手元の環境では、MediaCaptureオブジェクトが返してきたSoftwareBitmapオブジェクトは、BGRA8形式ではありましたがアルファチャネルはストレート(アン・プリマルチプライド)でした。
// SoftwareBitmapの画像フォーマットをImageコントロールに表示できるものに作り直す
public static SoftwareBitmap CorrectFormat(SoftwareBitmap bitmap)
{
if (bitmap.BitmapPixelFormat != BitmapPixelFormat.Bgra8
|| bitmap.BitmapAlphaMode == BitmapAlphaMode.Straight)
{
// Imageコントロールに表示できるのは、BGRA8形式でアルファはプリマルチプライド
var newBitmap = SoftwareBitmap.Convert(bitmap, BitmapPixelFormat.Bgra8,
BitmapAlphaMode.Premultiplied);
bitmap.Dispose();
return newBitmap;
}
return bitmap;
}
OCRエンジンを呼び出す
OCRに認識させる言語を決定し、SoftwareBitmapオブジェクトの準備ができたら、次のコードのようにしてOCRエンジンのインスタンスを作ってRecognizeAsyncメソッドを呼び出すだけです(⇒MainPage.Ocr.csのRecognizeBitmapAsyncメソッド内)。OcrEngineクラスのTryCreateFromLanguageメソッドに渡すLanguageオブジェクトとして、ここでは前述のコンボボックスで選択されたものを使っています。
// 言語を指定してOCRエンジンのインスタンスを作る var ocrEngine = OcrEngine.TryCreateFromLanguage(this.LangComboBox.SelectedItem as Language); // OCRエンジンにSoftwareBitmapオブジェクトを渡して文字認識させる OcrResult ocrResult = await ocrEngine.RecognizeAsync(bitmap);
なお、OCRエンジンが処理できる画像サイズには限度があります。今回のサンプルコードでは割愛しましたが、必要なときはOcrEngineクラスのMaxImageDimension静的プロパティを参照してください(筆者の手元の環境では1万ピクセルでした)。
