Windows10のOCR利用 画像より文字抽出 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
画面設計
Form1.css
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Drawing;
using System.Linq;
using System.Reflection.Emit;
using System.Runtime.InteropServices;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using static System.Windows.Forms.VisualStyles.VisualStyleElement;
using Windows.Graphics.Imaging;
using Windows.Media.Ocr;
using System.Drawing.Imaging;
using System.IO;
using static System.Net.Mime.MediaTypeNames;
using Shell32;
using Windows.Storage.Streams;
using Windows.Storage;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Controls.Primitives;
using static System.Net.WebRequestMethods;
using System.Runtime.InteropServices.ComTypes;
using Windows.Foundation;
using Windows.UI.Xaml.Shapes;
using Point = System.Drawing.Point;
using System.Runtime.InteropServices.WindowsRuntime;
namespace WindowsFormsApp1
{
public partial class Form1 : Form
{
//control clsResize
clsResize _form_resize;
//Form1オブジェクトを保持するためのフィールド
private static Form1 _form1Instance;
public Form1()
{
InitializeComponent();
Form1.Form1Instance = this;
_form_resize = new clsResize(this); //I put this after the initialize event to be sure that all controls are initialized properly
this.Load += new EventHandler(_Load); //This will be called after the initialization // form_load
this.Resize += new EventHandler(_Resize); //form_resize
}
//clsResize _Load
private void _Load(object sender, EventArgs e)
{
_form_resize._get_initial_size();
}
//clsResize _Resize
private void _Resize(object sender, EventArgs e)
{
_form_resize._resize();
}
//画像選択
private void Open()
{
//OpenFileDialogクラスのインスタンスを作成
OpenFileDialog ofd = new OpenFileDialog();
//はじめのファイル名を指定する
//はじめに「ファイル名」で表示される文字列を指定する
ofd.FileName = "default.html";
//はじめに表示されるフォルダを指定する
//指定しない(空の文字列)の時は、現在のディレクトリが表示される
ofd.InitialDirectory = @"C:\";
//[ファイルの種類]に表示される選択肢を指定する
//指定しないとすべてのファイルが表示される
ofd.Filter = "画像ファイル(*.jpg;*.png;*.bmp)|*.jpg;*.png;*.bmp|すべてのファイル(*.*)|*.*";
//[ファイルの種類]ではじめに選択されるものを指定する
//2番目の「すべてのファイル」が選択されているようにする
ofd.FilterIndex = 1;
//タイトルを設定する
ofd.Title = "開くファイルを選択してください";
//ダイアログボックスを閉じる前に現在のディレクトリを復元するようにする
ofd.RestoreDirectory = true;
//存在しないファイルの名前が指定されたとき警告を表示する
//デフォルトでTrueなので指定する必要はない
ofd.CheckFileExists = true;
//存在しないパスが指定されたとき警告を表示する
//デフォルトでTrueなので指定する必要はない
ofd.CheckPathExists = true;
//ダイアログを表示する
if (ofd.ShowDialog() == DialogResult.OK)
{
//OKボタンがクリックされたとき、選択されたファイル名を表示する
Console.WriteLine(ofd.FileName);
pictureBox1.ImageLocation = ofd.FileName;
textBox8.Text = ofd.FileName;
}
}
//画像選択
private void 画像選択ToolStripMenuItem_Click(object sender, EventArgs e)
{
Open();
}
private void oCR全体処理ToolStripMenuItem_Click(object sender, EventArgs e)
{
OcrAll();
}
private void oCR選択処理ToolStripMenuItem_Click(object sender, EventArgs e)
{
OcrPart();
}
private void 消去ToolStripMenuItem_Click(object sender, EventArgs e)
{
textBox1.Text = "";
}
private void 保存ToolStripMenuItem_Click(object sender, EventArgs e)
{
save();
}
private void 終了ToolStripMenuItem_Click(object sender, EventArgs e)
{
Environment.Exit(0);
}
private void label1_Click(object sender, EventArgs e)
{
Form2 form2 = new Form2();
form2.ShowDialog(this);
form2.Dispose();
}
//画像選択
private void button1_Click(object sender, EventArgs e)
{
Open();
}
//ocr全体処理
private void button2_Click(object sender, EventArgs e)
{
OcrAll();
}
//ocr選択処理
private void button3_Click(object sender, EventArgs e)
{
OcrPart();
}
//消去
private void button4_Click(object sender, EventArgs e)
{
textBox1.Text = "";
}
//保存
private void button5_Click(object sender, EventArgs e)
{
save();
}
//終了
private void button6_Click(object sender, EventArgs e)
{
Environment.Exit(0);
}
//テキスト保存
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() == DialogResult.OK)
{
//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(textBox1.Text);
//閉じる
sw.Close();
stream.Close();
}
}
}
//画像クリック
private void pictureBox1_MouseDown(object sender, MouseEventArgs e)
{
System.Drawing.Image img = pictureBox1.Image;
Point pos = new Point(0, 0);
pos.X = e.Location.X * img.Width / pictureBox1.Width;
pos.Y = e.Location.Y * img.Height / pictureBox1.Height;
textBox2.Text += textBox2.Text + pos.X + "," + pos.Y + ",";
string[] xy = textBox2.Text.Split(',');
if (xy.Length > 4)
{
textBox3.Text = xy[xy.Length - 5];
textBox4.Text = xy[xy.Length - 4];
textBox5.Text = xy[xy.Length - 3];
textBox6.Text = xy[xy.Length - 2];
textBox2.Text = xy[xy.Length - 5] + ",";
textBox2.Text += xy[xy.Length - 4] + ",";
textBox2.Text += xy[xy.Length - 3] + ",";
textBox2.Text += xy[xy.Length - 2] + ",";
}
}
//Form1オブジェクトを取得、設定するためのプロパティ
public static Form1 Form1Instance
{
get
{
return _form1Instance;
}
set
{
_form1Instance = value;
}
}
//TextBox8.Textを取得、設定するためのプロパティ
public string TextBox8
{
get
{
return textBox8.Text;
}
set
{
textBox8.Text = value;
}
}
//TextBox3.Textを取得、設定するためのプロパティ
public string TextBox3
{
get
{
return textBox3.Text;
}
set
{
textBox3.Text = value;
}
}
//TextBox4.Textを取得、設定するためのプロパティ
public string TextBox4
{
get
{
return textBox4.Text;
}
set
{
textBox4.Text = value;
}
}
//TextBox5.Textを取得、設定するためのプロパティ
public string TextBox5
{
get
{
return textBox5.Text;
}
set
{
textBox5.Text = value;
}
}
//TextBox5.Textを取得、設定するためのプロパティ
public string TextBox6
{
get
{
return textBox6.Text;
}
set
{
textBox6.Text = value;
}
}
public async void OcrAll()
{
string ImagePath = textBox8.Text;
//画像ファイルをOCRを実行
string ocrResult = await PerformOCR(ImagePath);
//結果を表示
textBox1.Text = textBox1.Text + "\r\n" + ocrResult.Replace(" ", "");
}
public async void OcrPart()
{
//元画像
Bitmap source;
source = new Bitmap(textBox8.Text);
//切り取るサイズ
int x1 = Int32.Parse(textBox3.Text);
int y1 = Int32.Parse(textBox4.Text);
int x2 = Int32.Parse(textBox5.Text);
int y2 = Int32.Parse(textBox6.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);
//結果を表示
textBox1.Text = textBox1.Text + "\r\n" + ocrResult.Replace(" ", "");
}
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工ンジンを初期化
var language = new Windows.Globalization.Language(textBox7.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;
}
}
}
}
Form2.css
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using static System.Windows.Forms.VisualStyles.VisualStyleElement;
namespace WindowsFormsApp1
{
public partial class Form2 : Form
{
public Form2()
{
InitializeComponent();
//元画像
Bitmap source;
source = new Bitmap(Form1.Form1Instance.TextBox8);
//切り取るサイズ
int x1 = Int32.Parse(Form1.Form1Instance.TextBox3);
int y1 = Int32.Parse(Form1.Form1Instance.TextBox4);
int x2 = Int32.Parse(Form1.Form1Instance.TextBox5);
int y2 = Int32.Parse(Form1.Form1Instance.TextBox6);
Rectangle rect;
rect = new Rectangle(x1, y1, x2 - x1 + 5, y2 - y1 + 5);
//切り取り後の画像
Bitmap trimed;
trimed = source.Clone(rect, source.PixelFormat);
//画像を表示する
pictureBox1.Image = trimed;
}
//閉じる
private void button1_Click(object sender, EventArgs e)
{
this.Close();
}
}
}
clsResize
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;
public class clsResize
{
List<System.Drawing.Rectangle> _arr_control_storage = new List<System.Drawing.Rectangle>();
private bool showRowHeader = false;
public clsResize(Form _form_)
{
form = _form_; //the calling form
_formSize = _form_.ClientSize; //Save initial form size
_fontsize = _form_.Font.Size; //Font size
//ADD
var _controls = _get_all_controls(form);//call the enumerator
FontTable = new Dictionary<string, float>();
ControlTable = new Dictionary<string, System.Drawing.Rectangle>();
foreach (Control control in _controls) //Loop through the controls
{
FontTable.Add(control.Name, control.Font.Size);
ControlTable.Add(control.Name, control.Bounds);
}
//ADD
}
//ADD
Dictionary<string, float> FontTable;
Dictionary<string, System.Drawing.Rectangle> ControlTable;
//ADD
private float _fontsize { get; set; }
private System.Drawing.SizeF _formSize { get; set; }
private Form form { get; set; }
public void _get_initial_size() //get initial size//
{
var _controls = _get_all_controls(form);//call the enumerator
foreach (Control control in _controls) //Loop through the controls
{
_arr_control_storage.Add(control.Bounds); //saves control bounds/dimension
//If you have datagridview
if (control.GetType() == typeof(DataGridView))
_dgv_Column_Adjust(((DataGridView)control), showRowHeader);
}
}
public void _resize() //Set the resize
{
double _form_ratio_width = (double)form.ClientSize.Width / (double)_formSize.Width; //ratio could be greater or less than 1
double _form_ratio_height = (double)form.ClientSize.Height / (double)_formSize.Height; // this one too
var _controls = _get_all_controls(form); //reenumerate the control collection
int _pos = -1;//do not change this value unless you know what you are doing
foreach (Control control in _controls)
{
//ADD
this._fontsize = FontTable[control.Name]; //<-取得したコントロールのフォントサイズ値で上書きするためにこれを追加
//ADD
// do some math calc
_pos += 1;//increment by 1;
System.Drawing.Size _controlSize = new System.Drawing.Size((int)(_arr_control_storage[_pos].Width * _form_ratio_width),
(int)(_arr_control_storage[_pos].Height * _form_ratio_height)); //use for sizing
System.Drawing.Point _controlposition = new System.Drawing.Point((int)
(_arr_control_storage[_pos].X * _form_ratio_width), (int)(_arr_control_storage[_pos].Y * _form_ratio_height));//use for location
//set bounds
control.Bounds = new System.Drawing.Rectangle(_controlposition, _controlSize); //Put together
//Assuming you have a datagridview inside a form()
//if you want to show the row header, replace the false statement of
//showRowHeader on top/public declaration to true;
if (control.GetType() == typeof(DataGridView))
_dgv_Column_Adjust(((DataGridView)control), showRowHeader);
//Font AutoSize
control.Font = new System.Drawing.Font(form.Font.FontFamily,
(float)(((Convert.ToDouble(_fontsize) * _form_ratio_width) / 2) +
((Convert.ToDouble(_fontsize) * _form_ratio_height) / 2)));
}
}
private void _dgv_Column_Adjust(DataGridView dgv, bool _showRowHeader) //if you have Datagridview
//and want to resize the column base on its dimension.
{
int intRowHeader = 0;
const int Hscrollbarwidth = 5;
if (_showRowHeader)
intRowHeader = dgv.RowHeadersWidth;
else
dgv.RowHeadersVisible = false;
for (int i = 0; i < dgv.ColumnCount; i++)
{
if (dgv.Dock == DockStyle.Fill) //in case the datagridview is docked
dgv.Columns[i].Width = ((dgv.Width - intRowHeader) / dgv.ColumnCount);
else
dgv.Columns[i].Width = ((dgv.Width - intRowHeader - Hscrollbarwidth) / dgv.ColumnCount);
}
}
private static IEnumerable<Control> _get_all_controls(Control c)
{
return c.Controls.Cast<Control>().SelectMany(item =>
_get_all_controls(item)).Concat(c.Controls.Cast<Control>()).Where(control =>
control.Name != string.Empty);
}
}
実行画面
画像の左上と右下のクリックで選択し、選択座標のクリックで選択範囲を表示します(確認が必要な時)。OCR選択処理ボタン押下でその範囲を読み取ります。