C#でWindows10のOCRを使って文字認識する(WPF)
C#のWPFアプリで、Windows10のOCR機能を使った文字認識をします。
目次
Windows10にはOCR機能がある
Windows10には、UWPアプリ用の機能としてOCRが備わっています。この機能はWindows Runtime APIを通して使用できます。
ということで、ここではWindows Runtimeの機能を利用して、画像から文字を読み取ってみます。
具体的には、UWPアプリ用のAPIであるWindows.Media.OcrをWPFアプリから呼び出して変換を行います。
WPFアプリからWindows Runtime APIを呼び出す方法
Microsoftの「デスクトップ アプリで Windows ランタイム API を呼び出す」という記事に手順が書かれています。
以下で実際に設定をしてみます。
パッケージ管理方法の変更
まず、Visual Studioのパッケージ参照の方法をPackageReferenceに変更します。
Visual Studioのメニューから、「ツール」→「NuGetパッケージマネージャー」→「パッケージマネージャー設定」の順に選択します。
「オプション」ダイアログが開きますので、「NuGetパッケージマネージャー」の「全般」項目を選択して、「規定のパッケージ管理形式」を「PackageReference」に変更します。
パッケージ参照の追加
パッケージ管理形式を変更したら、Visual Studioのメニューから「プロジェクト」→「NuGetパッケージの管理」を選択します。
NuGetパッケージ管理用のウィンドウが開きます。「参照」ページの検索欄にmicrosoft windows sdk contractsと入力して検索すると、Microsoft.Windows.SDK.Contractsというパッケージが見つかると思います。そうしたら「インストール」をクリックしてこのパッケージをインストールします。(下図はインストール済みの状態でのスクリーンショットなので、「インストール」のボタンに「アンインストール」と表示されてます。)
依存関係のある、System.Runtime.WindowsRuntimeとSystem.Runtime.WindowsRuntime.UI.Xamlも一緒にインストールされるはずです。もし自動的にインストールされない場合は、手動でインストールします。
特に今回はSystem.Runtime.WindowsRuntimeの機能を利用しますので、インストールされたかどうかよく確認してください。
以上で、Windows.Media.Ocrライブラリを使用する準備ができました。
OCRの手順
Windows.Media.Ocr.OcrEngineのインスタンスを作成して、そのインスタンスのRecognizeAsyncメソッドにSoftwareBitmap形式のデータを指定して実行すれば、OCRによる文字認識が実行されます。
とても分かり易いですね。
ただし、これをWPFアプリで実行すると面倒なことがあります。
SoftwareBitmapというのはUWPの世界の形式のため、WPFで使用されるBitmapSourceなどの形式からSoftwareBitmap形式に変換しなければいけないのです。
変換方法はいろいろあるのですが、ここではメモリストリームを使うことにします。
手順はこんな感じです。
ファイルを読み込むなどしてBitmapSourceやWritableBitmapの画像データを準備する。
画像データをメモリストリームに書き出す。
メモリストリームをRandomAccessStreamに変換する。
RandomAccessStreamからSoftwareBitmapとして画像を読み込む。
OcrEngineインスタンスを使ってOCRを実行する。
面倒ですが、仕方ありません。
実施例
下図の画像(当サイトのスクリーンショットの切り抜き)をPNGファイルとして保存したデータをOCRします。
アプリで読み込んだ画像を上部に表示して、その下部のTextBoxにOCRの結果を出力します。
XAML
<Window x:Class="ocr_test.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:ocr_test"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="400" Loaded="Window_Loaded">
<Grid>
<StackPanel>
<Image Name="imgSrc" Height="300" />
<TextBox Name="txtResult" Height="100" Margin="10" TextWrapping="Wrap" />
</StackPanel>
</Grid>
</Window>
StackPanelにImageとTextBlockを並べただけですね。
コード
using System;
using System.IO;
using System.Windows;
using System.Windows.Media.Imaging;
using Windows.Graphics.Imaging;
using Windows.Media.Ocr;
namespace ocr_test
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private async void Window_Loaded(object sender, RoutedEventArgs e)
{
// 画像ファイルの読み込みと表示
Stream input_stream = new FileStream("test.png", FileMode.Open, FileAccess.Read, FileShare.Read);
var source = new WriteableBitmap(System.Windows.Media.Imaging.BitmapFrame.Create(input_stream));
imgSrc.Source = source;
input_stream.Close();
// 画像データをメモリストリームへ書き出し
PngBitmapEncoder encoder = new PngBitmapEncoder();
encoder.Frames.Add(System.Windows.Media.Imaging.BitmapFrame.Create(source));
MemoryStream temp_stream = new MemoryStream();
encoder.Save(temp_stream);
// メモリストリームを変換
var converted_stream = WindowsRuntimeStreamExtensions.AsRandomAccessStream(temp_stream);
// メモリストリームからOCR用画像データの生成
var decorder = await Windows.Graphics.Imaging.BitmapDecoder.CreateAsync(converted_stream);
SoftwareBitmap bitmap = await decorder.GetSoftwareBitmapAsync();
converted_stream.Dispose();
temp_stream.Close();
// OCRの実行
OcrEngine engine = OcrEngine.TryCreateFromLanguage(new Windows.Globalization.Language("ja-JP"));
var result = await engine.RecognizeAsync(bitmap);
txtResult.Text = result.Text;
}
}
}
System.IO.MemoryStreamからWindows.Storage.Streams.IRandomAccessStreamへの変換に、WindowsRuntimeStreamExtensions.AsRandomAccessStreamというメソッドを使用しています。これを使うためには、System.Runtime.WindowsRuntimeを参照に追加する必要があります。NuGetでインストールし忘れていないか確認してください。
上記のコードでは、OCRエンジンに日本語を指定しています。英語にしたい場合などには、Windows10に言語パックのインストールが必要のようです。
文字認識の例
実際に実行すると、このような結果になります。
文章の順番のずれについてはプログラム側で工夫できると思います。
「ル」が「ノレ」になってしまっているのがちょっと残念ですね。
まあでも、こんなに簡単にOCRができてしまうのはありがたいことです。
主なメソッド
WindowsRuntimeStreamExtensions.AsRandomAccessStream
dst = WindowsRuntimeStreamExtensions.AsRandomAccessStream(src);
変数 |
型 |
内容 |
---|---|---|
src |
System.IO.Stream |
変換元のストリーム |
dst |
IRandomAccessStream |
変換後のストリーム |
System.IO.Streamクラスのオブジェクトを、Windows.Storage.Streams.IRondomAccessStreamクラスのオブジェクトに変換します。
BitmapDecoder.CreateAsync
decoder = Windows.Graphics.Imaging.BitmapDecoder.CreateAsync(stream);
変数 |
型 |
内容 |
---|---|---|
stream |
IRandomAccessStream |
入力用のストリーム |
decoder |
BitampDecoder |
デコーダーインスタンス |
非同期のメソッドです。ストリームからBitmapDecoderインスタンスを生成します。
System.Windows.Media.ImagingにもBitmapDecoderがありますので、上記のように明示が必要になる場合があります。
BitmapDecoder.GetSoftwareBitmapAsync
bitmap = decoder.GetSoftwareBitmapAsync();
変数 |
型 |
内容 |
---|---|---|
decoder |
BitampDecoder |
デコーダーインスタンス |
bitmap |
SoftwareBitmap |
Bitmapオブジェクト |
非同期のメソッドです。実行が終わると、BitmapDecoderインスタンスから生成したSoftwareBitmapオブジェクトを返します。
OcrEngine.TryCreateFromLanguage
engine = OcrEngine.TryCreateFromLanguage(language);
変数 |
型 |
内容 |
---|---|---|
language |
Language |
OCR言語の指定 |
engine |
OcrEngine |
OCRエンジンのインスタンス |
OcrEngineクラスのインスタンスを生成します。
language引数に言語を指定します。この引数には、Windows.Globalization.Languageクラスのインスタンスを指定します。Languageクラスのコンストラクタ引数にja-JPを指定すると日本語になります。
Windows10の規定の言語に設定したい場合は、TryCreateFromUserProfileLanguagesというメソッドもあります。
OcrEngine.RecognizeAsync
result = engine.RecognizeAsync(bitmap);
変数 |
型 |
内容 |
---|---|---|
engine |
OcrEngine |
OCRエンジンのインスタンス |
bitmap |
SoftwareBitmap |
入力画像 |
result |
OcrResult |
OCRの結果 |
非同期のメソッドです。実行が終わると、OcrResultクラスのオブジェクトを返します。
OcrResultクラスには、Lines、Text、TextAngleというプロパティがあります。Textプロパティを参照すると、OCRの結果の文字列が得られます。
公開日
広告
C#カテゴリの投稿
- C#でMVVMって何でしょう
- C#でPDFを表示する(WPF)
- C#でWindows10のOCRを使って文字認識する(WPF)
- C#でアプリのログを記録してみる
- C#でアプリの設定を保存する
- C#でインスタンスをプログラムで作ってみた(Activator.CreateInstance編)
- C#でインスタンスをプログラムで作ってみた(Type.InvokeMember編)
- C#でインスタンス間のデータの受け渡しをしてみた
- C#でウェブサイトのソースを取得してみた
- C#でエラーの処理をする
- C#でクラスのフィールド宣言とコンストラクターでの初期化はどっちが優先する?
- C#でスタックを使って逆ポーランド記法の計算をしてみた
- C#で数式を中置記法から後置記法(逆ポーランド記法)に変換してみた(三角関数編)
- C#で選択(switch-case編)
- C#のWPFでデータバインディング
- C#のXAMLでメニューとステータスバーのレイアウトをしてみた
- C#のアプリの情報を表示してみた
- C#のクラスとインスタンスとオブジェクト
- C#の反復処理(foreach編)
- C#の命名規則
- C#へのMicrosoft.TeamFoundation.Controlsの参照の追加について