C#で数字だけを認識する、手書き文字認識システムを作りました。
非常に簡易的なものです。
以下の説明は、VisualStudioを使用して作成する場合の説明になります。
using System;
using System.Drawing;
using System.Windows.Forms;
namespace TegakiLib
{
public class Tegaki : PictureBox
{
private Bitmap canvas;
private Graphics g;
private Pen p;
private bool writing = false;
private int pointCount = 0;
private bool isInit = true;
private int initPositionX = 0;
private int initPositionY = 0;
/// <summary>
/// up=1,down=2
/// left=1,right=2
/// </summary>
private int[][,] hanbetu = new int[10][,];
private int[] refIndex = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
public Action<int> exec;
public Tegaki()
{
exec = (int i) => { };
InitTegaki();
}
public Tegaki(Action<int> func)
{
exec = func;
InitTegaki();
}
private void InitTegaki()
{
canvas = new Bitmap(500, 500);
g = Graphics.FromImage(canvas);
p = new Pen(Color.Cyan, 5);
hanbetu[0] = new int[,] { { 2, 0, 0, 0 }, { 0, 2, 0, 0 }, { 1, 0, 0, 0 }, { 0, 1, 0, 0 }, { 9, 9, 0, 0 } };
hanbetu[1] = new int[,] { { 2, 0, 0, 0 }, { 2, 0, 0, 0 }, { 2, 0, 0, 0 }, { 9, 9, 0, 0 } };
hanbetu[2] = new int[,] { { 0, 2, 0, 0 }, { 2, 0, 0, 0 }, { 0, 2, 0, 0 }, { 9, 9, 0, 0 } };
hanbetu[3] = new int[,] { { 0, 2, 0, 0 }, { 0, 1, 0, 0 }, { 0, 2, 0, 0 }, { 0, 1, 0, 0 }, { 9, 9, 0, 0 } };
hanbetu[4] = new int[,] { { 0, 1, 0, 0 }, { 1, 0, 0, 0 }, { 2, 0, 0, 0 }, { 9, 9, 0, 0 } };
hanbetu[5] = new int[,] { { 0, 1, 0, 0 }, { 2, 0, 0, 0 }, { 0, 2, 0, 0 }, { 2, 0, 0, 0 }, { 0, 1, 0, 0 }, { 9, 9, 0, 0 } };
hanbetu[6] = new int[,] { { 0, 1, 0, 0 }, { 2, 0, 0, 0 }, { 0, 2, 0, 0 }, { 1, 0, 0, 0 }, { 0, 1, 0, 0 }, { 9, 9, 0, 0 } };
hanbetu[7] = new int[,] { { 1, 0, 0, 0 }, { 0, 2, 0, 0 }, { 2, 0, 0, 0 }, { 9, 9, 0, 0 } };
hanbetu[8] = new int[,] { { 0, 1, 0, 0 }, { 0, 2, 0, 0 }, { 0, 1, 0, 0 }, { 0, 2, 0, 0 }, { 1, 0, 0, 0 }, { 9, 9, 0, 0 } };
hanbetu[9] = new int[,] { { 0, 1, 0, 0 }, { 2, 0, 0, 0 }, { 0, 2, 0, 0 }, { 1, 0, 0, 0 }, { 2, 0, 0, 0 }, { 9, 9, 0, 0 } };
}
protected override void OnMouseDown(MouseEventArgs e)
{
writing = true;
base.OnMouseDown(e);
}
protected override void OnMouseLeave(EventArgs e)
{
drawFin();
base.OnMouseLeave(e);
}
protected override void OnMouseMove(MouseEventArgs e)
{
mouseMoving();
base.OnMouseMove(e);
}
protected override void OnMouseUp(MouseEventArgs e)
{
drawFin();
base.OnMouseUp(e);
}
private void drawFin()
{
writing = false;
isInit = true;
int j;
g.Clear(Color.FromArgb(64, 0, 64));
this.Image = canvas;
for (int i = 0; i <= 9; i++)
{
refIndex[i] = 0;
for (j = 0; j < hanbetu[i].Length / 4; j++)
{
hanbetu[i][j, 2] = 0;
hanbetu[i][j, 3] = 0;
}
}
}
private void mouseMoving()
{
if (!writing) return;
int up = 1, down = 2, left = 1, right = 2;
int ud = 10, lr = 10;
int writtenNumber = 0;
bool m = false;
Point point = this.PointToClient(Control.MousePosition);
g.DrawRectangle(p, point.X, point.Y, 5, 5);
this.Image = canvas;
if (isInit)
{
initPositionX = point.X;
initPositionY = point.Y;
isInit = false;
}
pointCount++;
if (pointCount >= 5)
{
if ((point.X - initPositionX) >= 30)
{
lr = right;
isInit = true;
}
if ((point.X - initPositionX) <= -30)
{
lr = left;
isInit = true;
}
if ((point.Y - initPositionY) >= 30)
{
ud = down;
isInit = true;
}
if ((point.Y - initPositionY) <= -30)
{
ud = up;
isInit = true;
}
pointCount = 0;
//isInit = true;
for (int i = 0; i <= 9; i++)
{
if (hanbetu[i][refIndex[i], 0] == ud || hanbetu[i][refIndex[i], 1] == lr)
{
hanbetu[i][refIndex[i], 2] = point.X;
hanbetu[i][refIndex[i], 3] = point.Y;
refIndex[i]++;
if (hanbetu[i][refIndex[i], 0] == 9 || hanbetu[i][refIndex[i], 1] == 9)
{
switch (i)
{
case 0:
if ((hanbetu[0][0, 2] < hanbetu[0][2, 2]) && (hanbetu[0][1, 3] > hanbetu[0][3, 3]) &&
(hanbetu[0][0, 3] - 10 > hanbetu[0][3, 3]))
{
writtenNumber = 0;
m = !m;
}
break;
case 1:
if (((hanbetu[1][0, 2] + 50 >= hanbetu[1][2, 2]) && (hanbetu[1][0, 2] - 50 <= hanbetu[1][2, 2])) &&
(hanbetu[4][2, 2] == 0) && (hanbetu[4][2, 3] == 0) &&
(hanbetu[9][4, 2] == 0) && (hanbetu[9][4, 3] == 0) &&
(hanbetu[7][0, 2] == 0) && (hanbetu[7][0, 3] == 0))
{
writtenNumber = 1;
m = !m;
}
break;
case 2:
writtenNumber = 2;
m = !m;
break;
case 3:
if ((hanbetu[3][0, 3] < hanbetu[3][1, 3]) && (hanbetu[3][2, 3] < hanbetu[3][3, 3]))
{
writtenNumber = 3;
m = !m;
}
break;
case 4:
if ((hanbetu[4][1, 3] < hanbetu[4][0, 3]) && (hanbetu[4][1, 2] < hanbetu[4][2, 2]))
{
writtenNumber = 4;
m = !m;
}
break;
case 5:
writtenNumber = 5;
m = !m;
break;
case 6:
if ((hanbetu[6][1, 2] < hanbetu[6][0, 2]) &&
(hanbetu[6][1, 2] < hanbetu[6][2, 2]) &&
(hanbetu[6][1, 2] < hanbetu[6][3, 2]) &&
(hanbetu[6][1, 2] < hanbetu[6][4, 2]) &&
(hanbetu[6][0, 3] + 30 < hanbetu[6][4, 3]))
{
writtenNumber = 6;
m = !m;
}
break;
case 7:
if ((hanbetu[9][3, 2] == 0) && (hanbetu[9][3, 3] == 0))
{
writtenNumber = 7;
m = !m;
}
break;
case 8:
if ((hanbetu[8][0, 3] < hanbetu[8][1, 3]) && (hanbetu[8][2, 3] > hanbetu[8][3, 3]))
{
writtenNumber = 8;
m = !m;
}
break;
case 9:
if (hanbetu[9][0, 3] < hanbetu[9][2, 3])
{
writtenNumber = 9;
m = !m;
}
break;
}
if(m) exec(writtenNumber);
}
}
}
}
}
}
}
PictureBox
クラスの派生Tegaki
クラスです。
上のソースコードをそのままコピーして、DLLにもできますし、プロジェクトの一部にしてもかまいません。
フォームのデザイン画面を開いてみると、ツールボックスにTegaki
が追加されます。
##使い方(プログラム例)
Tegaki
のサイズは500✕500のみ対応しています。プロパティでサイズを500✕500にしてください。
背景色は、何でもいいですが、勝手にRGB(64,0,64)に変わってしまいます。(手抜きですが・・・)
他にもラベルを2つ追加しました。
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace TegakiNumber
{
public partial class Form2 : Form
{
int j = 0;
public Form2()
{
InitializeComponent();
tegaki1.exec = exec;
}
private void exec(int i)
{
label1.Text = "現在の判別値" + i.ToString();
j = i;
}
private void tegaki1_MouseUp(object sender, MouseEventArgs e)
{
label2.Text = "確定した判別値" + j.ToString();
}
}
}
フォームのソースコードです。
コンストラクターで、
tegaki1.exec = exec;
とありますが、Tegaki
クラスの中でpublic Action<int> exec;
と定義されています。
これはint型を1つ引数にとって、戻り値を返さない関数を意味しています。
つまりTegaki.exec
にForm2.exec(int i)
を渡しています。
この関数は、手書き入力中に、今現在判別している数字が変化したら実行されます。
その数字が引数i
に渡されます。
label1
は、入力中の現在の判別値を表示します。
この簡易文字認識システムは、マウスクリックからマウスが離されるまでの軌跡で判断します。
よって、label2
は、tegaki1
のMouseUp
イベントで、確定した判別値を返します。
##使い方(数字の書き方)
先ほど説明したように、マウスクリックからマウスが離されるまでの軌跡で判断します。
つまり、一筆書きが基本です。
「4」は、
の順に書きます。
「5」は、
と書きます。
「7」は、
最初の縦棒は必須です。
##実際の実行中の画面
「3」を書いてる途中の画面です。まだ途中なので、現在の判別値が「2」と判定されています。
「6」を書いたときの画面です。(マウスクリックを離すと手書き文字が消えるので、確定した判別値が前回のままになっています。)
以上、数字だけに対応した簡易的な手書き文字認識システムでした。