ある画像の指定した範囲からを数字を抽出してEditに表示しようと考えております、検索したらOCRの技術を使えば可能なのでは?思ったのですが
どのようにすればいいのか全くわからず悩んでおります
過去に似たような事をされた方がおりましたら、ご教授お願いいたします
>ある画像の指定した範囲からを数字を抽出してEditに表示しようと考えております
う〜む.こればどんな目的なんでしょう.
文字認識の研究,趣味とか.
スキャナからでも画面でも基本的には考え方は同じだとは思うのですが,
いわゆる「文字認識」という分野ですね.
そう簡単に説明できない,というより私は説明できませんが,おそらくネット
上でサンプルを探し出すのは困難だと思います.
(市販品はけっこうあるんですが...)
たとえ知っていたとしても,処理のアルゴリズムを説明するのは容易ではな
いでしょう.
関係した学協会,研究会などに所属していると,研究目的のためにプログラム
の提供を受けることができることがあります.お互いの研究のためです.
(ここのQ and Aの掲示板も本来はそういう目的なんでしょうけどね)
もし,研究が目的であれば,そのような学会に参加するのも手です.
とりあえず,ネットで「文字認識」あるいは「文字認識 コンピュータ」
などで検索してみて,「文字認識」がいったいどんなものなのか調べてみて
はいかがでしょうか.
ちょっと見た感じでは以下のようなページがありました.
まっ,簡単ではないですね.
(ちなみに,私は文字認識処理関係の参考書をもっています.埃にまみれていますが)
http://pooh.fukushima-nct.ac.jp/nakaolab/sotuken/moji/RIRON.HTML
http://www.geocities.jp/itsuoinada/HOR_index/Horabstract.htm
(高い !!)
http://133.13.40.79/Syllabus/sub%5Cy-FIT.ppt
(ブラウザの設定によってはダウンロードになるかも)
おおざっばな調査でいいと思いますので,もしよろしかったら
http://hpcgi2.nifty.com/Mr_XRAY/DelphiBBS/plcyclamen.cgi?tree=s1732#1732
を使ってやってみてください.
ただしテストで作成したので,動作は不安定です.とんてもないことが
起きるかも知れません(笑).
>http://133.13.40.79/Syllabus/sub%5Cy-FIT.ppt
>(ブラウザの設定によってはダウンロードになるかも)
これは,このURLをブラウザのURL欄にコピペして起動してください.
ダブルクリックでは開らかないと思います.
追記です.
文字認識 DelphiでGoogleを検索してみてください.
解決策がみつかるわけではないのですが,もしかしたら何らかの参考になるかも
知れません.
Mr.XRAYさん返信ありがとうございます
何がやりたいかと言いますと、伝票をスキャンして商品コードを画像から読み取って1日に○○の商品が入荷等の帳票印字をやろうと思ってます
やはり数字認識だけとはいえ、難しそうですね・・・
ほぼ単一の画像から数字を読み取るだけならそれほど難しくはないですよ。
僕なら、二階調化したあと、数字の特徴となりそうなピクセルの白黒判断してみます。
余談ですが、よくあるパスワード入力用の画像とかは本当に難問だと思います。
もにゅさん何か参考になりそうなサイト等はございますか?
森北出版「画像処理とパターン認識入門-基礎からVC#/VC++.NETによるプロジェクト作成まで-」
には色々なパターン認識アルゴリズムが載っていますね。
フーリエ識別子による数字認識 っていうのが最も近いかな。
ソースはここ(C#だけど)
http://www.morikita.co.jp/soft/84591/
でも、見たけどDelphiに移植するのは大変だ(意味無いジャン)
数字だけなら、しかも手書きじゃないなら、単純なテンプレートマッチングアルゴリズムで一から組んでも99.999%ぐらいの正解率は出来そう。
画像を認識させるのでしたら、比較的単純な(印字)数字でも
ニ値化はもちろん(閾値など)、ノイズの除去などは必須で
更にパターン認識は基本となります。
(カスレ、汚れ、傾きなど悪条件が想定できるため)
通常のアプリケーション開発者でしたらその知識・技術の経験
だけでは極めて困難です。(..可能性は無いとは言えませんが)
その類の専門技術屋がいた方がよいかと・・・
(全部私がやった訳ではありませんが体験談です)
..こんな回答で申し訳ないですが、
正直言ってかなり難しいです。
汎用性を求めると画像認知アルゴリズムは極端に難しくなります。
アルゴリズムを簡単にするために、汎用性を捨てる場合、
自分の専用環境に特化したアルゴリズムを詰めていきますので自分の状況に適応したサンプルがネットに落ちていることなど皆無といってよいと思います。
すこし冷たかった気がするので再投稿。
確かに、スコピョン・キングさんがおっしゃっている
「テンプレートマッチング」
が最適かと思います、これをキーワードにググってみてください。
もにゃさん返信ありがとうございます
「テンプレートマッチング」でググってみて頑張ってみます^^;
このあたりで解決とさせていただきますね
もう十分遅い気がしますが、適当に作ったのでお披露目します。
image1 サイズ 370x330
image2 サイズ 30x38
image3 サイズ 370x57
image4 サイズ 370x57
button1 caption: 画像ペースト
button2 caption: 配列作成
button3 caption: 認識
memo1
を置いて(各イベントはアタッチ)、下記ユニットコピペで。
使い方は、「配列作成」ボタンをクリックしテンプレート配列を作成後、
image1で数値を2回りくらい大きめにマウスでドラッグし、「認識」ボタンクリック。
たたき台としてはまあまあの認識率です。きちんと文字の多きさを合わせれば99%以上は出るでしょう。
ほとんどのコードがWEBからのコピペでできています。いい時代だなぁ。
----------------------------------------
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, Clipbrd, StdCtrls, ExtCtrls, math;
type
TRGBTripleArray = array[0..High(Integer) div 3 - 1] of RGBTRIPLE;
PRGBTripleArray = ^TRGBTripleArray; //配列型のポインタ
TForm1 = class(TForm)
Button1: TButton;
Image1: TImage;
Memo1: TMemo;
Button2: TButton;
Image2: TImage;
Button3: TButton;
Image3: TImage;
Image4: TImage;
procedure Button1Click(Sender: TObject);
procedure FormShow(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 Button2Click(Sender: TObject);
procedure Button3Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
FDrag: boolean; //マウスがドラッグ中かを示すフラグ
DownPt: TPoint; //描く矩形の基点となるマウスがおろされた座標
RR: TRect; //現在の選択矩形
MatchSt: array[0..9] of array[0..31] of array[0..23] of Integer;
implementation
{$R *.dfm}
procedure AngleTextOut(CV: TCanvas; const sText: string; x, y, angle: integer; size: Integer);
var
LogFont: TLogFont;
SaveFont: TFont;
begin
SaveFont := TFont.Create;
SaveFont.Assign(CV.Font);
GetObject(SaveFont.Handle, sizeof(TLogFont), @LogFont);
with LogFont do
begin
lfEscapement := angle * 10; //角度は0.1度単位
lfPitchAndFamily := FIXED_PITCH or FF_DONTCARE;
lfQuality := ANTIALIASED_QUALITY; //最高画質
lfHeight := size; //負
lfWidth := 0; //ゼロ
lfWeight := FW_EXTRABOLD; //太さ
{ Font Weights }
{
FW_DONTCARE = 0; FW_EXTRABOLD = 800;
FW_THIN = 100; FW_HEAVY = 900;
FW_EXTRALIGHT = 200; FW_ULTRALIGHT = FW_EXTRALIGHT;
FW_LIGHT = 300; FW_REGULAR = FW_NORMAL;
FW_NORMAL = 400; FW_DEMIBOLD = FW_SEMIBOLD;
FW_MEDIUM = 500; FW_ULTRABOLD = FW_EXTRABOLD;
FW_SEMIBOLD = 600; FW_BLACK = FW_HEAVY;
FW_BOLD = 700;
}
end;
CV.Font.Handle := CreateFontIndirect(LogFont);
SetBkMode(CV.Handle, TRANSPARENT);
CV.TextOut(x, y, sText);
CV.Font.Assign(SaveFont);
SaveFont.Free;
end;
procedure BrendImage(imageA: TImage; imageB: TImage; rate: Integer);
var
n, m: Integer;
PA, PB: PRGBTripleArray;
r, g, b: Integer;
begin
for n := 0 to imageA.Picture.bitmap.height - 1 do
begin
PA := imageA.Picture.bitmap.ScanLine[n];
PB := imageB.Picture.bitmap.ScanLine[n];
for m := 0 to imageA.Picture.bitmap.width - 1 do
begin
r := PA[m].rgbtRed;
g := PA[m].rgbtGreen;
b := PA[m].rgbtBlue;
PB[m].rgbtRed := (r * rate + PB[m].rgbtRed * (100 - rate)) div 100;
PB[m].rgbtGreen := (r * rate + PB[m].rgbtGreen * (100 - rate)) div 100;
PB[m].rgbtBlue := (r * rate + PB[m].rgbtBlue * (100 - rate)) div 100;
end;
end;
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
if Clipboard.HasFormat(CF_BITMAP) then
begin
Image1.Picture.Assign(Clipboard);
end;
end;
procedure TForm1.Button2Click(Sender: TObject);
var
gb: TBitmap;
I, J, K, r, g, b, kido, posx, posy: Integer;
PA, PB: PRGBTripleArray;
sub, minsub, minnum: Integer;
min_by_num: array[0..9] of Integer;
begin
gb := TBitmap.Create;
gb.Width := Image2.Width;
gb.Height := Image2.Height;
//前の矩形を消去
Image1.Canvas.DrawFocusRect(RR);
gb.Canvas.CopyRect(Rect(0, 0, Image2.Width, Image2.Height),
Image1.Canvas,
RR
);
Image2.Picture.Bitmap.Assign(gb);
Image2.Picture.Bitmap.PixelFormat := pf24bit;
gb.Free;
//新たな座標で矩形を描く
Image1.Canvas.DrawFocusRect(RR);
Application.ProcessMessages;
minsub := High(Integer);
minnum := -1;
//マッチング処理開始
for K := 0 to 9 do //10種類(0〜9)を順番に試す
begin
;
min_by_num[K] := High(Integer);
for posy := 0 to 5 do //座標を縦横1dotにずらしながら
begin
for posx := 0 to 5 do
begin
sub := 0;
for I := 0 to 31 do //24x32の矩形領域で比較
begin
PA := image2.Picture.bitmap.ScanLine[I + posy];
for J := 0 to 23 do
begin
r := PA[J + posx].rgbtRed;
g := PA[J + posx].rgbtGreen;
b := PA[J + posx].rgbtBlue;
kido := 256 - ((r + g + b) div 3);
if kido <= 0 then
kido := 0;
if kido >= 256 then
kido := 255;
kido := kido div 16;
sub := sub + abs(MatchSt[K][I][J] - kido);//差分絶対値和を積算している
end;
end;
//算出した輝度の差分絶対値和が今までのものより小さいなら、その時点でのベストを更新
if sub < minsub then
begin
minsub := sub;
minnum := K;
end;
if sub < min_by_num[K] then //数字毎のベストの集計
begin
min_by_num[K] := sub;
end;
end;
end;
Memo1.Lines.Add( Format( '%d:最小値=%d', [K,min_by_num[K]] ) );
end;
Memo1.Lines.Add('--------');
Memo1.Lines.Add( Format( '認識値:%d', [minnum] ) );
end;
procedure TForm1.Button3Click(Sender: TObject);
var
FontName: array[0..8] of AnsiString;
I, J, K, fsize: Integer;
ypos: Integer;
PA, PB: PRGBTripleArray;
r, g, b: Integer;
St: AnsiString;
kido: Integer;
begin
FontName[0] := 'Times New Roman';
FontName[1] := 'Lucida Console';
FontName[2] := 'MS Pゴシック';
FontName[3] := 'MS 明朝';
FontName[4] := 'MS UI Gothic';
FontName[5] := 'Tahoma';
FontName[6] := 'Verdana';
FontName[7] := 'Courier New';
FontName[8] := 'Arial';
image3.Picture.Bitmap.PixelFormat := pf24bit;
image4.Picture.Bitmap.PixelFormat := pf24bit;
image3.Picture.bitmap.Width := image3.Width;
image4.Picture.bitmap.Width := image4.Width;
image3.Picture.bitmap.height := image3.height;
image4.Picture.bitmap.height := image4.height;
for I := 0 to 8 do
begin
fsize := -32;
ypos := 0;
if I = 1 then
ypos := 2;
if I = 5 then //フォント毎に微調整しないとうまくない
fsize := -31
else if I = 1 then
fsize := -30
else if I = 6 then
fsize := -30
else if I = 7 then
fsize := -35
else if I = 8 then
fsize := -33;
image3.Canvas.Font.Name := FontName[i];
image1.Canvas.Font.Name := FontName[i];
image3.Canvas.Brush.Color := clWhite;
image3.Canvas.FillRect(Rect(0, 0, image3.Width, image3.height));
for J := 0 to 9 do
begin
AngleTextOut(image3.canvas, IntToStr(J), 10 + 34 * J, 8 + ypos, 0, fsize);
AngleTextOut(image1.canvas, IntToStr(J), 10 + 34 * J, 8 + 34 * I + ypos, 0, fsize);
end;
BrendImage(image3, image4, 100 div (I + 1));
end;
// 配列の生成。輝度はRGBの平均で代用。
// また、0〜15に正規化している。
for K := 0 to 9 do
begin
//Memo1.Lines.Add( IntToStr(K) + '--------------');
for I := 0 to 31 do
begin
PA := image4.Picture.bitmap.ScanLine[I + 10];
//St :='';
for J := 0 to 23 do
begin
r := PA[K * 34 + J + 8].rgbtRed;
g := PA[K * 34 + J + 8].rgbtGreen;
b := PA[K * 34 + J + 8].rgbtBlue;
kido := 256 - ((r + g + b) div 3);
if kido <= 0 then
kido := 0;
if kido >= 256 then
kido := 255;
kido := kido div 16;
MatchSt[K][I][J] := kido;
//St := St+ ',' + Format( '%x', [kido] );
end;
//Memo1.Lines.Add( St);
end;
end;
end;
procedure TForm1.FormShow(Sender: TObject);
begin
if Clipboard.HasFormat(CF_BITMAP) then
begin
Image1.Picture.Assign(Clipboard);
end;
end;
procedure TForm1.Image1MouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
begin
if Button = mbRight then Exit;
//前の矩形を消去
Image1.Canvas.DrawFocusRect(RR);
DownPt := Point(X, Y);
FDrag := true;
RR := Rect(X, Y, X, Y);
Image1.Canvas.DrawFocusRect(RR);
end;
procedure TForm1.Image1MouseMove(Sender: TObject; Shift: TShiftState; X,
Y: Integer);
begin
if FDrag then begin
//前の矩形を消去
Image1.Canvas.DrawFocusRect(RR);
//現在のマウス位置と基点の座標から新たな矩形をセット
RR := Rect(MIN(Downpt.X, X), MIN(Downpt.Y, Y),
MAX(Downpt.X, X), MAX(Downpt.Y, Y));
//新たな座標で矩形を描く
Image1.Canvas.DrawFocusRect(RR);
end;
end;
procedure TForm1.Image1MouseUp(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
begin
//ドラッグ終了でフラグを Falseに
FDrag := false;
end;
end.
(修正)
すんません。下記が正しいです。
button1 caption: 画像ペースト
button3 caption: 配列作成
button2 caption: 認識
解決してるのにありがとうございます><
参考にさせていただきますね
ツイート | ![]() |