FANN(Fast Artificial Neural Network Library)を使って手書き数字(0~9)をAIに認識させる ~Delphiでお手軽プログラミング
FANNを使用する為のファイルの準備
https://mam-mam.net/delphi/fann.html からfannfloat.dll、fann.pas、MamFann.pasをダウンロードする。トレーニングの為の手書き数字文字(0~9)を大量に作成する。
ひたすら手書き数字(0~9)の画像(16×16ピクセル)を作成しました。![](./img/fann3/0_1.jpg)
![](./img/fann3/1_2.jpg)
![](./img/fann3/2_3.jpg)
![](./img/fann3/3_4.jpg)
![](./img/fann3/4_5.jpg)
![](./img/fann3/5_6.jpg)
![](./img/fann3/6_7.jpg)
![](./img/fann3/7_8.jpg)
![](./img/fann3/8_9.jpg)
![](./img/fann3/9_10.jpg)
![](./img/fann3/0_11.jpg)
![](./img/fann3/1_12.jpg)
![](./img/fann3/2_13.jpg)
![](./img/fann3/3_14.jpg)
![](./img/fann3/4_15.jpg)
![](./img/fann3/5_16.jpg)
![](./img/fann3/6_17.jpg)
![](./img/fann3/7_18.jpg)
![](./img/fann3/8_19.jpg)
![](./img/fann3/9_20.jpg)
![](./img/fann3/0_21.jpg)
![](./img/fann3/1_22.jpg)
![](./img/fann3/2_23.jpg)
![](./img/fann3/3_24.jpg)
![](./img/fann3/4_25.jpg)
![](./img/fann3/5_26.jpg)
![](./img/fann3/6_27.jpg)
![](./img/fann3/7_28.jpg)
![](./img/fann3/8_29.jpg)
![](./img/fann3/9_30.jpg)
![](./img/fann3/0_31.jpg)
![](./img/fann3/1_32.jpg)
![](./img/fann3/2_33.jpg)
![](./img/fann3/3_34.jpg)
![](./img/fann3/4_35.jpg)
![](./img/fann3/5_36.jpg)
![](./img/fann3/6_37.jpg)
![](./img/fann3/7_38.jpg)
![](./img/fann3/8_39.jpg)
![](./img/fann3/9_40.jpg)
![](./img/fann3/0_41.jpg)
![](./img/fann3/1_42.jpg)
![](./img/fann3/2_43.jpg)
![](./img/fann3/3_44.jpg)
![](./img/fann3/4_45.jpg)
![](./img/fann3/5_46.jpg)
![](./img/fann3/6_47.jpg)
![](./img/fann3/7_48.jpg)
![](./img/fann3/8_49.jpg)
![](./img/fann3/9_50.jpg)
![](./img/fann3/0_51.jpg)
![](./img/fann3/1_52.jpg)
![](./img/fann3/2_53.jpg)
![](./img/fann3/3_54.jpg)
![](./img/fann3/4_55.jpg)
![](./img/fann3/5_56.jpg)
![](./img/fann3/6_57.jpg)
![](./img/fann3/7_58.jpg)
![](./img/fann3/8_59.jpg)
![](./img/fann3/9_60.jpg)
![](./img/fann3/0_61.jpg)
![](./img/fann3/1_62.jpg)
![](./img/fann3/2_63.jpg)
![](./img/fann3/3_64.jpg)
![](./img/fann3/4_65.jpg)
![](./img/fann3/5_66.jpg)
![](./img/fann3/6_67.jpg)
![](./img/fann3/7_68.jpg)
![](./img/fann3/8_69.jpg)
![](./img/fann3/9_70.jpg)
![](./img/fann3/0_71.jpg)
![](./img/fann3/1_72.jpg)
![](./img/fann3/2_73.jpg)
![](./img/fann3/3_74.jpg)
![](./img/fann3/4_75.jpg)
![](./img/fann3/5_76.jpg)
![](./img/fann3/6_77.jpg)
![](./img/fann3/7_78.jpg)
![](./img/fann3/8_79.jpg)
![](./img/fann3/9_80.jpg)
![](./img/fann3/0_81.jpg)
![](./img/fann3/1_82.jpg)
![](./img/fann3/2_83.jpg)
![](./img/fann3/3_84.jpg)
![](./img/fann3/4_85.jpg)
![](./img/fann3/5_86.jpg)
![](./img/fann3/6_87.jpg)
![](./img/fann3/7_88.jpg)
![](./img/fann3/8_89.jpg)
![](./img/fann3/9_90.jpg)
![](./img/fann3/0_91.jpg)
![](./img/fann3/1_92.jpg)
![](./img/fann3/2_93.jpg)
![](./img/fann3/3_94.jpg)
![](./img/fann3/4_95.jpg)
![](./img/fann3/5_96.jpg)
![](./img/fann3/6_97.jpg)
![](./img/fann3/7_98.jpg)
![](./img/fann3/8_99.jpg)
![](./img/fann3/9_100.jpg)
![](./img/fann3/0_101.jpg)
![](./img/fann3/1_102.jpg)
![](./img/fann3/2_103.jpg)
![](./img/fann3/3_104.jpg)
![](./img/fann3/4_105.jpg)
![](./img/fann3/5_106.jpg)
![](./img/fann3/6_107.jpg)
![](./img/fann3/7_108.jpg)
![](./img/fann3/8_109.jpg)
![](./img/fann3/9_110.jpg)
![](./img/fann3/0_111.jpg)
![](./img/fann3/1_112.jpg)
![](./img/fann3/2_113.jpg)
![](./img/fann3/3_114.jpg)
![](./img/fann3/4_115.jpg)
![](./img/fann3/5_116.jpg)
![](./img/fann3/6_117.jpg)
![](./img/fann3/7_118.jpg)
![](./img/fann3/8_119.jpg)
![](./img/fann3/9_120.jpg)
![](./img/fann3/0_121.jpg)
![](./img/fann3/1_122.jpg)
![](./img/fann3/2_123.jpg)
![](./img/fann3/3_124.jpg)
![](./img/fann3/4_125.jpg)
![](./img/fann3/5_126.jpg)
![](./img/fann3/6_127.jpg)
![](./img/fann3/7_128.jpg)
![](./img/fann3/8_129.jpg)
![](./img/fann3/9_130.jpg)
![](./img/fann3/0_131.jpg)
![](./img/fann3/1_132.jpg)
![](./img/fann3/2_133.jpg)
![](./img/fann3/3_134.jpg)
![](./img/fann3/4_135.jpg)
![](./img/fann3/5_136.jpg)
![](./img/fann3/6_137.jpg)
![](./img/fann3/7_138.jpg)
![](./img/fann3/8_139.jpg)
![](./img/fann3/9_140.jpg)
![](./img/fann3/0_141.jpg)
![](./img/fann3/1_142.jpg)
![](./img/fann3/2_143.jpg)
![](./img/fann3/3_144.jpg)
![](./img/fann3/4_145.jpg)
![](./img/fann3/5_146.jpg)
![](./img/fann3/6_147.jpg)
![](./img/fann3/7_148.jpg)
![](./img/fann3/8_149.jpg)
![](./img/fann3/9_150.jpg)
![](./img/fann3/0_151.jpg)
![](./img/fann3/1_152.jpg)
![](./img/fann3/2_153.jpg)
![](./img/fann3/3_154.jpg)
![](./img/fann3/4_155.jpg)
![](./img/fann3/5_156.jpg)
![](./img/fann3/6_157.jpg)
![](./img/fann3/7_158.jpg)
![](./img/fann3/8_159.jpg)
![](./img/fann3/9_160.jpg)
![](./img/fann3/0_161.jpg)
![](./img/fann3/1_162.jpg)
![](./img/fann3/2_163.jpg)
![](./img/fann3/3_164.jpg)
![](./img/fann3/4_165.jpg)
![](./img/fann3/5_166.jpg)
![](./img/fann3/6_167.jpg)
![](./img/fann3/7_168.jpg)
![](./img/fann3/8_169.jpg)
![](./img/fann3/9_170.jpg)
![](./img/fann3/0_171.jpg)
![](./img/fann3/1_172.jpg)
![](./img/fann3/2_173.jpg)
![](./img/fann3/3_174.jpg)
![](./img/fann3/4_175.jpg)
![](./img/fann3/5_176.jpg)
![](./img/fann3/6_177.jpg)
![](./img/fann3/7_178.jpg)
![](./img/fann3/8_179.jpg)
![](./img/fann3/9_180.jpg)
![](./img/fann3/0_181.jpg)
![](./img/fann3/1_182.jpg)
![](./img/fann3/2_183.jpg)
![](./img/fann3/3_184.jpg)
![](./img/fann3/4_185.jpg)
![](./img/fann3/5_186.jpg)
![](./img/fann3/6_187.jpg)
![](./img/fann3/7_188.jpg)
![](./img/fann3/8_189.jpg)
![](./img/fann3/9_190.jpg)
![](./img/fann3/0_191.jpg)
![](./img/fann3/1_192.jpg)
![](./img/fann3/2_193.jpg)
![](./img/fann3/3_194.jpg)
![](./img/fann3/4_195.jpg)
![](./img/fann3/5_196.jpg)
![](./img/fann3/6_197.jpg)
![](./img/fann3/7_198.jpg)
![](./img/fann3/8_199.jpg)
![](./img/fann3/9_200.jpg)
各画像(24Bit画像ですが、モノクロなので各1ドットのRGBはすべて同じ値のはず)の 各縦方向のRの値を足して正規化(0~1の間の値にする)したものと、 各横方向のRの値を足して正規化した合計32個のデータをFANNに学習させ、作成(出力)した学習結果ファイルを準備します。
学習結果ファイル number_train.net のダウンロード
Delphiを起動して新規作成を行い、学習結果ファイル number_train.netを使用して手書き数字(0~9)認識を行うプログラムを作成する
Delphi起動⇒ファイル⇒新規作成⇒WindowsVCLアプリケーション を選択します。TImage 1個、TLabel 1個、TButton 2個、 TMemo 1個をフォームへドラッグ&ドロップします。
![](./img/fann3/01.jpg)
ソースコードを記述する
unit Unit1;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, Vcl.ExtCtrls
,fann,mamfann;
type
TForm1 = class(TForm)
Image1: TImage;
Label1: TLabel;
Button1: TButton;
Button2: TButton;
Memo1: TMemo;
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
procedure Image1MouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
procedure Image1MouseMove(Sender: TObject; Shift: TShiftState; X,
Y: Integer);
procedure Image1MouseUp(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
private
{ Private 宣言 }
procedure resetBmp();
procedure createInputs(
bm:TBitmap;
var inp:array of TFann_type);
public
{ Public 宣言 }
md:boolean;
pt:TPoint;
bmp,bmp2:TBitmap;
MamFann: TMamFann;
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.Button1Click(Sender: TObject);
begin
resetBmp;
end;
procedure TForm1.Button2Click(Sender: TObject);
type
trgb=record
b,g,r:byte;
end;
prgb=^trgb;
var mbmp:TBitmap;
x,y:integer;
xmax,ymax,xmin,ymin:integer;
ww,hh:integer;
l,t:integer;
rgb:prgb;
inputs:array[0..31] of TFann_type;
outputs:array[0..9] of TFann_type;
i:integer;
bigbmp:TBitmap;
smax:single;
num:integer;
begin
bigbmp:=TBitmap.Create;
bigbmp.PixelFormat:=pf24bit;
bigbmp.Width:=bmp.Width*3;
bigbmp.Height:=bmp.Height*3;
bigbmp.Canvas.Brush.Color:=$ffffff;
bigbmp.Canvas.Brush.Style:=bsSolid;
bigbmp.Canvas.FillRect(
Rect(0,0,bigbmp.Width,bigbmp.Height)
);
bigbmp.Canvas.CopyMode:=cmSrcCopy;
bigbmp.Canvas.Draw(bmp.Width,bmp.Height,bmp);
xmax:=-1;
ymax:=-1;
xmin:=bigbmp.Width-1;
ymin:=bigbmp.Height-1;
for y := 0 to bigbmp.Height-1 do
begin
rgb:=bigbmp.ScanLine[y];
for x := 0 to bigbmp.Width-1 do
begin
if rgb.b<>255 then
begin
if xmax<x then xmax:=x;
if xmin>x then xmin:=x;
if ymax<y then ymax:=y;
if ymin>y then ymin:=y;
end;
inc(rgb);
end;
end;
if(xmax=-1) then
begin
bigbmp.Free;
exit;//何も描画していない
end;
ww:=xmax-xmin+1;
hh:=ymax-ymin+1;
if ww>hh then hh:=ww else ww:=hh;
l:=(xmin+xmax - ww) div 2;
t:=(ymin+ymax - hh) div 2;
mbmp:=TBitmap.Create;
mbmp.PixelFormat:=pf24bit;
mbmp.Width:=16;
mbmp.Height:=16;
mbmp.Canvas.Brush.Color:=$ffffff;
mbmp.Canvas.FillRect(
Rect(0,0,mbmp.Width,mbmp.Height)
);
SetStretchBltMode(mbmp.Canvas.Handle,HALFTONE);
StretchBlt(
mbmp.Canvas.Handle,0,0,mbmp.Width,mbmp.Height,
bigbmp.Canvas.Handle,l,t,ww,hh,
SRCCOPY
);
bigbmp.Free; //追加(破棄を忘れていました)
createInputs(mbmp,inputs);
mbmp.Free;
MamFann.Run(inputs,outputs);
Memo1.Lines.Clear;
smax:=0;
num:=0;
for i := 0 to length(outputs)-1 do
begin
Memo1.Lines.Add(Format('%1.1d : %5.3f',[i,outputs[i]]));
if outputs[i]>smax then
begin
smax:=outputs[i];
num:=i;
end;
end;
Label1.Caption:=IntToStr(num)+'を描きましたよね?';
end;
procedure TForm1.createInputs(bm: TBitmap; var inp: array of TFann_type);
var x,y:integer;
sxy:integer;
begin
for x := 0 to bm.Width-1 do
begin
sxy:=0;
for y := 0 to bm.Height-1 do
sxy:=sxy+(bm.Canvas.Pixels[x,y] and $ff);
inp[x]:=sxy/(255*bm.Height);
end;
for y := 0 to bm.Height-1 do
begin
sxy:=0;
for x := 0 to bm.Width-1 do
sxy:=sxy+(bm.Canvas.Pixels[x,y] and $ff);
inp[y+16]:=sxy/(255*bm.Width);
end;
end;
procedure TForm1.FormCreate(Sender: TObject);
var NeuronNumInLayer:array of Cardinal;
begin
Image1.Width:=256;
Image1.Height:=256;
md:=False;
bmp:=TBitmap.Create;
bmp2:=TBitmap.Create;
resetBmp;
//TMamFannクラスのインスタンス化
if Assigned(MamFann) then
FreeAndNil(MamFann);
setlength(NeuronNumInLayer,4);//レイヤー(層)の数
NeuronNumInLayer[0]:=32; //入力層のニューロン数
NeuronNumInLayer[1]:=25; //中間層のニューロン数
NeuronNumInLayer[2]:=20; //中間層のニューロン数
NeuronNumInLayer[3]:=10; //出力層のニューロン数
MamFann:=TMamFann.Create(NeuronNumInLayer);
MamFann.LoadFromFile('number_train.net');
end;
procedure TForm1.FormDestroy(Sender: TObject);
begin
bmp.Free;
bmp2.Free;
MamFann.Free;
end;
procedure TForm1.Image1MouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
begin
if md=False then
begin
pt.X:=X;
pt.Y:=Y;
md:=True;
end;
end;
procedure TForm1.Image1MouseMove(Sender: TObject; Shift: TShiftState; X,
Y: Integer);
begin
if md=True then
begin
bmp.Canvas.MoveTo(pt.X,pt.Y);
bmp.Canvas.LineTo(X,Y);
bmp2.Canvas.MoveTo(pt.X,pt.Y);
bmp2.Canvas.LineTo(X,Y);
Image1.Picture.Bitmap.Assign(bmp2);
pt.X:=X;
pt.Y:=Y;
end;
end;
procedure TForm1.Image1MouseUp(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
begin
if md=True then
begin
bmp.Canvas.MoveTo(pt.X,pt.Y);
bmp.Canvas.LineTo(X,Y);
bmp2.Canvas.MoveTo(pt.X,pt.Y);
bmp2.Canvas.LineTo(X,Y);
Image1.Picture.Bitmap.Assign(bmp2);
pt.X:=X;
pt.Y:=Y;
md:=False;
end;
end;
procedure TForm1.resetBmp;
begin
bmp.Width:=Image1.Width;
bmp.Height:=Image1.Height;
bmp.PixelFormat:=pf24bit;
bmp.Canvas.Brush.Style:=bsSolid;
bmp.Canvas.Brush.Color:=$FFFFFF;
bmp.Canvas.FillRect(Rect(0,0,bmp.Width,bmp.Height));
bmp.Canvas.Pen.Width:=16;
bmp.Canvas.Pen.Style:=psSolid;
bmp.Canvas.Pen.Color:=$000000;
bmp2.Width:=Image1.Width;
bmp2.Height:=Image1.Height;
bmp2.PixelFormat:=pf24bit;
bmp2.Canvas.Brush.Style:=bsSolid;
bmp2.Canvas.Brush.Color:=$FFFFFF;
bmp2.Canvas.FillRect(Rect(0,0,bmp2.Width,bmp2.Height));
bmp2.Canvas.Pen.Width:=1;
bmp2.Canvas.Pen.Style:=psSolid;
bmp2.Canvas.Pen.Color:=$777777;
bmp2.Canvas.MoveTo(0,bmp.Height div 2);
bmp2.Canvas.LineTo(bmp.Width,bmp.Height div 2);
bmp2.Canvas.MoveTo(bmp.Width div 2,0);
bmp2.Canvas.LineTo(bmp.Width div 2,bmp.Height);
bmp2.Canvas.Pen.Width:=16;
bmp2.Canvas.Pen.Style:=psSolid;
bmp2.Canvas.Pen.Color:=$000000;
Image1.Picture.Bitmap.Assign(bmp2);
end;
end.
実行する
実行ファイルが生成させるフォルダ内に「fannfloat.dll」と「number_train.net」を配置するのを忘れずに。(又はc:\windosフォルダ等に配置する)実行ボタンを押して実行します。(デバッグ実行でもOK)
Image1に数字を手書き(マウスドラッグでもOK)し、Button2(認識)ボタンをクリックすると、 描いた数字が何か応答します。
![](./img/fann3/02.jpg)