Windows10のOCR利用 画像より文字抽出 WPF C#
Microsoft.Windows.SDK.Contractsを導入。パッケージ管理システムがPackage.configだとエラーが出る場合があります。パッケージ管理がPackage.configの場合は、PackageReferenceに移行してください。
OCR機能は、画像全体と選択範囲を指定してテキストを出力
下記のURLよりContractsのバージョンに合うWindows10SDKをインストールします。 過去バージョンのSDK https://developer.microsoft.com/ja-jp/windows/downloads/sdk-archive/ 最新バージョンのSDK https://developer.microsoft.com/ja-jp/windows/downloads/windows-10-sdk/
参照の追加
◇System.Runtime.WindowsRuntime
C:\Windows\Microsoft.NET\Framework\v4.0.30319
◇System.Runtime.WindowsRuntime.UI.Xaml
C:\Windows\Microsoft.NET\Framework\v4.0.30319
◇System.Runtime.InteropServices.WindowsRuntime
C:\Windows\Microsoft.NET\Framework\v4.0.30319
〇windows.winmd
C:\Program Files (x86)\Windows Kits\10\UnionMetadata\<sdk version>\Facade
〇Windows.Foundation.UniversalApiContract.winmd
C:\Program Files (x86)\Windows Kits\10\References\<sdk version>\Windows.Foundation.UniversalApiContract
〇Windows.Foundation.FoundationContract.winmd
C:\Program Files (x86)\Windows Kits\10\References\<sdk version>\Windows.Foundation.FoundationContract
画面設計
<Window x:Class="WpfApp1.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:WpfApp1"
mc:Ignorable="d"
Title="WpfOcr" Height="840" Width="1103" ResizeMode="NoResize">
<Grid Margin="0,25,0,-6">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="43*"/>
<ColumnDefinition Width="1060*"/>
</Grid.ColumnDefinitions>
<Grid.Background>
<SolidColorBrush Color="{DynamicResource {x:Static SystemColors.ActiveCaptionColorKey}}"/>
</Grid.Background>
<Button x:Name="button1" Content="画像参照" HorizontalAlignment="Left" Margin="41,13,0,0" VerticalAlignment="Top" Height="25" Width="80" Click="Button1_Click" Grid.ColumnSpan="2">
<Button.Background>
<SolidColorBrush Color="#FFFFF1E1"/>
</Button.Background>
</Button>
<Image x:Name="ImageBox1" HorizontalAlignment="Left" Height="691" Margin="41,68,0,0" VerticalAlignment="Top" Width="444" Stretch="Fill" MouseDown="ImageBox1_MouseDown" Grid.ColumnSpan="2" UseLayoutRounding="False" />
<TextBox x:Name="textBox3" HorizontalAlignment="Right" Margin="0,68,9,21" TextWrapping="Wrap" Width="574" FontFamily="Yu Mincho Demibold" FontSize="14" VerticalScrollBarVisibility="Visible" Grid.Column="1" Background="#FFFBFAFA" AcceptsReturn="True"/>
<TextBox x:Name="textBox1" HorizontalAlignment="Left" Margin="41,43,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="1035" Height="20" Background="#FFFBFBD8" Grid.ColumnSpan="2"/>
<Button x:Name="button2" Content="全体処理" HorizontalAlignment="Left" Margin="575,14,0,0" VerticalAlignment="Top" Height="25" Width="80" Click="Button2_Click" Grid.Column="1">
<Button.Background>
<SolidColorBrush Color="#FFFFF1E1"/>
</Button.Background>
</Button>
<Button x:Name="button3" Content="選択処理" HorizontalAlignment="Left" Margin="671,14,0,0" VerticalAlignment="Top" Height="25" Width="80" Click="Button3_Click" Background="#FFFFF1E1" RenderTransformOrigin="0.725,0.642" Grid.Column="1"/>
<Button x:Name="button4" Content="消 去" HorizontalAlignment="Left" Margin="763,14,0,0" VerticalAlignment="Top" Height="25" Width="80" Click="Button4_Click" Background="#FFFFF1E1" RenderTransformOrigin="1.404,0.562" Grid.Column="1"/>
<Button x:Name="button5" Content="保 存" HorizontalAlignment="Left" Margin="857,14,0,0" VerticalAlignment="Top" Height="25" Width="80" Click="Button5_Click" Background="#FFFFF1E1" RenderTransformOrigin="1.404,0.562" Grid.Column="1"/>
<Button x:Name="button6" Content="終 了" HorizontalAlignment="Left" Margin="953,14,0,0" VerticalAlignment="Top" Height="25" Width="80" Click="Button6_Click" Background="#FFFFF1E1" RenderTransformOrigin="1.404,0.562" Grid.Column="1"/>
<TextBox x:Name="textBox2" HorizontalAlignment="Left" Margin="537,14,0,0" TextWrapping="Wrap" Text="ja" VerticalAlignment="Top" Width="24" Height="25" RenderTransformOrigin="-0.156,0.602" HorizontalContentAlignment="Center" Grid.Column="1"/>
<Button x:Name="button7" Content="画像座標" HorizontalAlignment="Left" Margin="83,13,0,0" VerticalAlignment="Top" Height="25" Width="62" Click="Button7_Click" Background="#FFFFF1E1" Grid.Column="1"/>
<TextBox x:Name="textBox4" HorizontalAlignment="Left" Margin="150,18,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="136" HorizontalContentAlignment="Center" Height="18" Grid.Column="1"/>
<TextBox x:Name="textBox5" HorizontalAlignment="Left" Margin="293,18,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="41" HorizontalContentAlignment="Center" Height="18" Grid.Column="1"/>
<TextBox x:Name="textBox6" HorizontalAlignment="Left" Margin="339,18,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="40" HorizontalContentAlignment="Center" Height="18" Grid.Column="1"/>
<TextBox x:Name="textBox7" HorizontalAlignment="Left" Margin="385,18,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="40" RenderTransformOrigin="1.463,0.448" HorizontalContentAlignment="Right" Height="18" Grid.Column="1"/>
<TextBox x:Name="textBox8" HorizontalAlignment="Left" Margin="431,18,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="40" HorizontalContentAlignment="Center" Height="18" Grid.Column="1"/>
<Label x:Name="Label1" Content="言語" HorizontalAlignment="Left" Margin="491,13,0,0" VerticalAlignment="Top" Background="#FFE7D8F7" HorizontalContentAlignment="Center" BorderBrush="Black" Height="26" Width="34" Grid.Column="1"/>
<Rectangle HorizontalAlignment="Left" Height="693" Margin="41,68,0,0" Stroke="Black" VerticalAlignment="Top" Width="446" Grid.ColumnSpan="2"/>
<StackPanel Orientation="Vertical" Margin="2,-21,995,21" Grid.ColumnSpan="2" >
<!--Menu-->
<Menu Width="1100">
<MenuItem Header="処 理">
<MenuItem Header="画像参照" Click="Button1_Click" />
<MenuItem Header="画像座標" Click="Button7_Click" />
<MenuItem Header="全体処理" Click="Button2_Click" />
<MenuItem Header="選択処理" Click="Button3_Click" />
<MenuItem Header="消 去" Click="Button4_Click" />
<MenuItem Header="保 存" Click="Button5_Click" />
<MenuItem Header="終 了" Click="Button6_Click" />
</MenuItem>
</Menu>
</StackPanel>
</Grid>
</Window>
<Window x:Class="WpfApp1.Window1"
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:WpfApp1"
mc:Ignorable="d"
Title="Zoom" Height="572" Width="831" ResizeMode="NoResize">
<Grid Margin="0,0,-29,-27">
<Grid.ColumnDefinitions>
</Grid.ColumnDefinitions>
<Image x:Name="ImageBox2" HorizontalAlignment="Left" Margin="20,10,0,111" Width="788" Stretch="Fill"/>
<Button Content="閉じる" HorizontalAlignment="Left" Margin="708,504,0,0" VerticalAlignment="Top" Height="25" Width="100" Click="Button_Click" RenderTransformOrigin="0.344,1.626"/>
</Grid>
</Window>
MainWindow.xaml.css
using Microsoft.SqlServer.Server;
using Microsoft.Win32;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using Image = System.Drawing.Image;
using Point = System.Windows.Point;
using Rectangle = System.Drawing.Rectangle;
using Windows.Media.Ocr;
using Windows.Graphics.Imaging;
using Windows.Storage;
using Windows.Storage.Streams;
using System.Runtime.InteropServices.WindowsRuntime;
namespace WpfApp1
{
/// <summary>
/// MainWindow.xaml の相互作用ロジック
/// </summary>
public partial class MainWindow : Window
{
//Form1オブジェクトを保持するためのフィールド
private static MainWindow _form1Instance;
public MainWindow()
{
InitializeComponent();
MainWindow._form1Instance = this;
}
private void Button1_Click(object sender, RoutedEventArgs e)
{
open();
}
private void open()
{
//[1]OpenFileDialogのオブジェクトを作成します。
var ofd = new OpenFileDialog();
//[2]ファイルの拡張子指定を行います。
ofd.Filter = "画像ファイル(*.jpg,*.png)|*.jpg;*.png|すべて(*.*)|*.*";
//[3]ダイアログの処理です。
if (ofd.ShowDialog() == true)
{
RenderOptions.SetBitmapScalingMode(ImageBox1, BitmapScalingMode.HighQuality);
ImageBox1.Source = new BitmapImage(new Uri(ofd.FileName));
textBox1.Text = ofd.FileName;
}
}
//全体処理
private void Button2_Click(object sender, RoutedEventArgs e)
{
OcrAll();
}
//選択処理
private void Button3_Click(object sender, RoutedEventArgs e)
{
OcrPart();
}
//消去
private void Button4_Click(object sender, RoutedEventArgs e)
{
textBox3.Text = "";
}
//保存
private void Button5_Click(object sender, RoutedEventArgs e)
{
save();
}
//テキスト保存
private void save()
{
//SaveFileDialogクラスのインスタンスを作成
SaveFileDialog sfd = new SaveFileDialog();
//はじめのファイル名を指定する
//はじめに「ファイル名」で表示される文字列を指定する
sfd.FileName = "新しいファイル.txt";
//はじめに表示されるフォルダを指定する
sfd.InitialDirectory = System.Environment.CurrentDirectory + @"\";
//[ファイルの種類]に表示される選択肢を指定する
//指定しない(空の文字列)の時は、現在のディレクトリが表示される
sfd.Filter = "Textファイル(*.txt)|*.txt|すべてのファイル(*.*)|*.*";
//[ファイルの種類]ではじめに選択されるものを指定する
//2番目の「すべてのファイル」が選択されているようにする
sfd.FilterIndex = 1;
//タイトルを設定する
sfd.Title = "保存先のファイルを選択してください";
//ダイアログボックスを閉じる前に現在のディレクトリを復元するようにする
sfd.RestoreDirectory = true;
//既に存在するファイル名を指定したとき警告する
//デフォルトでTrueなので指定する必要はない
sfd.OverwritePrompt = true;
//存在しないパスが指定されたとき警告を表示する
//デフォルトでTrueなので指定する必要はない
sfd.CheckPathExists = true;
//ダイアログを表示する
if (sfd.ShowDialog() == true)
{
//OKボタンがクリックされたとき、選択されたファイル名を表示する
//Console.WriteLine(sfd.FileName);
//UTF-8で書き込む
//書き込むファイルが既に存在している場合は、上書きする
System.IO.Stream stream;
stream = sfd.OpenFile();
if (stream != null)
{
//ファイルに書き込む
System.IO.StreamWriter sw = new System.IO.StreamWriter(stream);
sw.Write(textBox3.Text);
//閉じる
sw.Close();
stream.Close();
}
}
}
//終了
private void Button6_Click(object sender, RoutedEventArgs e)
{
Close();
}
//選択画像表示
private void Button7_Click(object sender, RoutedEventArgs e)
{
var win = new Window1();
win.ShowDialog();
}
//選択画像表示
private void ImageBox1_MouseDown(object sender,MouseEventArgs e)
{
try
{
Point pos = e.GetPosition(ImageBox1);
Image img = new Bitmap(textBox1.Text);
Int32 X = (int)(pos.X * img.Width / ImageBox1.Width);
Int32 Y = (int)(pos.Y * img.Height / ImageBox1.Height);
e.Handled = true;
//MessageBox.Show("x座標:" + pos.X.ToString() + "\r\ny座標:" + pos.Y.ToString());
textBox4.Text += textBox4.Text + X.ToString() + "," + Y.ToString() + ",";
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString());
}
string[] xy = textBox4.Text.Split(',');
if (xy.Length > 4)
{
textBox5.Text = xy[xy.Length - 5];
textBox6.Text = xy[xy.Length - 4];
textBox7.Text = xy[xy.Length - 3];
textBox8.Text = xy[xy.Length - 2];
textBox4.Text = xy[xy.Length - 5] + ",";
textBox4.Text += xy[xy.Length - 4] + ",";
textBox4.Text += xy[xy.Length - 3] + ",";
textBox4.Text += xy[xy.Length - 2] + ",";
}
}
//Form1オブジェクトを取得、設定するためのプロパティ
public static MainWindow Form1Instance
{
get
{
return _form1Instance;
}
set
{
_form1Instance = value;
}
}
//TextBox1.Textを取得、設定するためのプロパティ
public string TextBox1
{
get
{
return textBox1.Text;
}
set
{
textBox1.Text = value;
}
}
//TextBox5.Textを取得、設定するためのプロパティ
public string TextBox5
{
get
{
return textBox5.Text;
}
set
{
textBox5.Text = value;
}
}
//TextBox6.Textを取得、設定するためのプロパティ
public string TextBox6
{
get
{
return textBox6.Text;
}
set
{
textBox6.Text = value;
}
}
//TextBox7.Textを取得、設定するためのプロパティ
public string TextBox7
{
get
{
return textBox7.Text;
}
set
{
textBox7.Text = value;
}
}
//TextBox5.Textを取得、設定するためのプロパティ
public string TextBox8
{
get
{
return textBox8.Text;
}
set
{
textBox8.Text = value;
}
}
//全体処理
public async void OcrAll()
{
string ImagePath = textBox1.Text;
//画像ファイルをOCRを実行
string ocrResult = await PerformOCR(ImagePath);
//結果を表示
textBox3.Text = textBox3.Text + "\r\n" + ocrResult.Replace(" ", "");
}
//選択処理
public async void OcrPart()
{
//元画像
Bitmap source;
source = new Bitmap(textBox1.Text);
//切り取るサイズ
int x1 = Int32.Parse(textBox5.Text);
int y1 = Int32.Parse(textBox6.Text);
int x2 = Int32.Parse(textBox7.Text);
int y2 = Int32.Parse(textBox8.Text);
Console.WriteLine(x1 + "," + y1 + "," + x2 + "," + y2);
System.Drawing.Rectangle rect;
rect = new System.Drawing.Rectangle(x1, y1, x2 - x1 + 5, y2 - y1 + 5);
//切り取り後の画像
Bitmap trimed;
trimed = source.Clone(rect, source.PixelFormat);
//保存
trimed.Save("trimed.jpg");
source.Dispose();
trimed.Dispose();
string ImagePath = System.Environment.CurrentDirectory + @"\trimed.jpg";
//画像ファイルをOCRを実行
string ocrResult = await PerformOCR(ImagePath);
//結果を表示
textBox3.Text = textBox3.Text + "\r\n" + ocrResult.Replace(" ", "");
}
//OCR処理
private async Task<string> PerformOCR(string imagePath)
{
try
{
//画像ファイルをパイト配列として読み込む
byte[] imageBytes = System.IO.File.ReadAllBytes(imagePath);
//パイト配列をIBufferに変換
IBuffer buffer = imageBytes.AsBuffer();
//IBufferからSoftwareBitmapに変換
SoftwareBitmap softwareBitmap;
using (var stream = new InMemoryRandomAccessStream())
{
await stream.WriteAsync(buffer);
stream.Seek(0);
var decoder = await Windows.Graphics.Imaging.BitmapDecoder.CreateAsync(stream);
softwareBitmap = await decoder.GetSoftwareBitmapAsync();
}
//OCR工ンジンを初期化 言語指定 ja or en
var language = new Windows.Globalization.Language(textBox2.Text);
OcrEngine ocrEngine = OcrEngine.TryCreateFromLanguage(language); //OcrEngine.TryCreateFromUserProfileLanguages();
//画像をOCRにかけ、結果を取碍
OcrResult ocrResult = await ocrEngine.RecognizeAsync(softwareBitmap);
//認識結果をテキストに変換
//string recognizedText = ocrResult.Text;
string recognizedText = ocrResult.Lines.Select(line => line.Text).Aggregate((current, next) => current + Environment.NewLine + next);
return recognizedText;
}
catch (Exception ex)
{
return "OCRエラー" + ex.Message;
}
}
}
}
Window1.xaml.css
using Microsoft.SqlServer.Server;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
using static System.Net.Mime.MediaTypeNames;
using Rectangle = System.Drawing.Rectangle;
namespace WpfApp1
{
/// <summary>
/// Window1.xaml の相互作用ロジック
/// </summary>
public partial class Window1 : Window
{
public Window1()
{
InitializeComponent();
//元画像
Bitmap source;
source = new Bitmap(MainWindow.Form1Instance.TextBox1);
//切り取るサイズ
int x1 = Int32.Parse(MainWindow.Form1Instance.TextBox5);
int y1 = Int32.Parse(MainWindow.Form1Instance.TextBox6);
int x2 = Int32.Parse(MainWindow.Form1Instance.TextBox7);
int y2 = Int32.Parse(MainWindow.Form1Instance.TextBox8);
Rectangle rect;
rect = new Rectangle(x1, y1, x2 - x1 + 5, y2 - y1 + 5);
//切り取り後の画像
Bitmap trimed;
trimed = source.Clone(rect, source.PixelFormat);
//画像を表示する
using (Stream st = new MemoryStream())
{
trimed.Save(st, ImageFormat.Bmp);
st.Seek(0, SeekOrigin.Begin);
RenderOptions.SetBitmapScalingMode(ImageBox2, BitmapScalingMode.HighQuality);
ImageBox2.Source = BitmapFrame.Create(st, BitmapCreateOptions.None, BitmapCacheOption.OnLoad);
}
}
private void Button_Click(object sender, RoutedEventArgs e)
{
Close();
}
}
}
実行画面
画像の左上と右下のクリックで選択し、選択座標のクリックで選択範囲を表示します(確認が必要な時)。OCR選択処理ボタン押下でその範囲を読み取ります。