こんにちは、太郎と申します。
プログラミング初心者ですが、Delphi6を用いてライントレースみたいなことをしようと考えています。
BitmapやJpeg画像にある物体に対し、ScanLineを用いてその物体の外形の境目付近のRGB値を取得して、その物体の形を判断しようというものです。
普通に2値化したり最小二乗法を用いてやる方法もあるようですが、今回はこの方法を敢えてやってみたいと思った所存です。
コピー機のように縦に何ピクセルかずつ、横にRGB値を取得していく感じです。
その物体の上部が出来たら次は右分、その次は下部、そして左部とやっていくイメージです。
(つまり、上・下部は縦何ピクセルかを横に、左右部は横何ピクセルかを縦にスキャンしていく感じです。)
今のところ、スタート位置の座標だけ自分で打ち、そこから横に読み込んでいくところまではできました。
次は縦に読み込んでいくのですが、ボタン一つで縦も横もすべてやりたいというのが本音で、できれば楕円やピーナッツのようなものを出来るようにしたいです。
今のところのやり方は、以下の通りです。
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls,Jpeg;
type
TForm1 = class(TForm)
Memo1: TMemo;
Button1: TButton;
Button2: TButton;
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
private
{ Private 宣言 }
procedure OutputRGB(Col: TColor);
public
{ Public 宣言 }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.OutputRGB(Col: TColor);
type
TRGB = packed record
case Integer of
0: (Col: TColor;);
1: (R: Byte;
G: Byte;
B: Byte;
I: Byte;);
end;
var
R, G, B : Byte;
begin
// 関数(マクロ扱い)を使った、TColor値 → R,G,Bの取り出し
R:=GetRValue(Col);
G:=GetGValue(Col);
B:=GetBValue(Col);
Memo1.Lines.Add(' (R): '+InttoStr(R)+
' (G): '+InttoStr(G)+
' (B): '+InttoStr(B));
end;
procedure TForm1.Button1Click(Sender: TObject);
const
// Bmpファイルを指定する
BMPFile='C:\Users\User\Pictures\My PictureⅠ\ファイル名.bmp';
var
BMP: TBitmap;
i,j:integer;
begin
BMP:=TBitmap.Create;
try
BMP.LoadFromFile(BMPFile);
Memo1.Lines.Add('--------------------'+ExtractFileName(BMPFile));
for i:=0 to 10 do begin //(x,y)=(0,0)〜(10,10)の場合
for j:=0 to 10 do begin
Memo1.Lines.Add(inttoStr(i)+'---------------------');
Memo1.Lines.Add(inttoStr(j)+'---------------------');
OutputRGB(BMP.Canvas.Pixels[i, j]);
end;
end;
finally
BMP.Free;
end;
end;
procedure TForm1.Button2Click(Sender: TObject);
const
// Jpegファイルを指定する
JPGFile='C:\Users\User\Pictures\My PictureⅠ\ファイル名.jpg';
var
JPG: TJPEGImage;
BMP: TBitmap;
i,j:integer;
begin
JPG:=TJPEGImage.Create;
BMP:=TBitmap.Create;
try
JPG.LoadFromFile(JPGFile);
BMP.Assign(JPG);
Memo1.Lines.Add('----------'+ExtractFileName(JPGFile));
for i:=0 to 10 do begin //(x,y)=(0,0)〜(10,10)の場合
for j:=0 to 10 do begin
Memo1.Lines.Add(inttoStr(i)+'-----------------');
Memo1.Lines.Add(inttoStr(j)+'-----------------');
OutputRGB(BMP.Canvas.Pixels[i, j]);
end;
end;
finally
BMP.Free;
JPG.Free;
end;
end;
end.
初心者故、スマートに出来ていないところ、拙いところはかなりあるかと思いますが、ご助言頂けると幸いです。
また、ここをこうすると良いといった改善策等もございましたら、ご教授頂けると嬉しいです。
質問に関してわからない事等ございましたら、知識はあまりないかもしれませんが、できる限り答えさせて頂きたいと思います!
どうぞよろしくお願いしますm(__)m
申し訳ありません。
注釈のところにある、画像の座標は(i,j)でしたm(_ _)m
質問が不明確すぎて、何を知りたいのかまったく分からないのですが…。
>ScanLineを用いて、ライントレースをしたいです。
現在はCanvas.Pixelsを使ってますよね。これをScanLineに変更したいということでしょうか?
もしそうならScanLineは若干難しいので、今はCanvas.Pixelsを使っていたほうが良いと思います。
アルゴリズムが固まり、望む動作をするコードが出来てからチャレンジしても遅くありません。
気になった点ですが、レコード型の宣言はTRGB型を使用していないなら不要です。
type 〜 end; までを削除してしまった方が良いかと。
あと、for〜end文はインデントさせて見やすくすると良いと思います。
>>Canvas.Pixelsを使っていたほうが良いと思います。
了解しました。このまま、まず自分のやりたい動作が出来る様にしたいと思います。
>>レコード型の宣言はTRGB型を使用していないなら不要です。
type 〜 end; までを削除してしまった方が良いかと。
for〜end文はインデントさせて見やすくすると良いと思います。
ご指摘、ありがとうございます。そのようにして改善したいと思います。
>>質問が不明確すぎて、何を知りたいのかまったく分からないのですが…。
例えば背景を黒、物体を白としたときに、その物体の外形の座標だけを出力するようなプログラムを作りたいと思っています。
最終的にその座標を線で繋ぐことが出来れば万々歳、と思っていますが^^;
説明が不十分で申し訳ありませんでしたm(_ _)m
こんにちは,
>例えば背景を黒、物体を白としたときに、その物体の外形の座標
これはそんなに難しくはないでしょう.
左上からスキャンして,黒でなければ,そのピクセルの座標を保存または,
そのピクセルには何もせず,次のピクセルを黒にするだけです.
この操作を縦方向にもやるだけです.
これなら,輪郭抽出処理のアルゴリズムにしたがって処理するよりはるかに高速です.
>最終的にその座標を線で繋ぐことが出来れば
全く必要ありません.何故なら,
>例えば背景を黒、物体を白としたときに
と仮定していますから,その白い図形は閉じています(たとえ複数あっても,各々は).
つまり,結果のピクセルは連続しています.部分的にはすでに直線です.
ただし,結果の図形,これは輪郭となっています.
これを拡大や縮小あるいは,何かしらの処理をすると問題が発生することがあります.
どんな問題なのかは,経験.... かな ?
そういう場合は,
本来の画像処理のアルゴリズムにしたがって処理するしかありません.
だからこそ,画像処理のアルゴリズムが,いろいろ開発されているわけですね.
Mr.XRAYさん
コメントありがとうございます。
なるほど・・・とてもわかりやすいです!
>>左上からスキャンして,黒でなければ,そのピクセルの座標を保存または,そのピクセルには何もせず,次のピクセルを黒にするだけです
とありますが、そのようなプログラムはどのように作ればいいでしょうか?
誠に恐縮ですが、ソースプログラムを載せて頂けたら幸いです。
拡大・縮小等の処理については、今のところはまだ良いです。
>そのようなプログラムはどのように作ればいいでしょうか?
多分,世の中には,同じようなことを考えている,やっているかも知れない.
と考えて,ネット上を探してみるといいでしょう.
ただ,検索語句を何にするかによって,検索結果が変わってきます.
このあたりも一種のノウハウかも知れません.
Delphi なので,これは入れるといいですね.
自分で調べることには,副次効果もあります.
他の情報を見て,画像のことだけではなく,「あっ,こんなの方法もあるのか.こんなのも」
というように.
Personal 版を使用しているということですが,許されるのであれば,
有料のエデション (Pro 版) 等を購入するいいです.
すると,ソースコードが付いています.
このソースコードが大変参考になります.
では,がんばってください !!
Mr.XRAYさん
ありがとうございます。
もっと調べてやってみたいと思います。
このプログラムが出来た時、この質問は解決としたいと思います。
>そのピクセルには何もせず,次のピクセルを黒にするだけです.
>この操作を縦方向にもやるだけです.
と書いたのですが,これだけでは輪郭抽出はできませんね.
あるピクセルに対して,上下左右方向の検査も必要です.
単なるスキャンだけでは難しいかも知れませんね.
先人達が苦労して開発したアルゴリズムというのは伊達ではないということでしょうか.
>単なるスキャンだけでは難しいかも知れませんね.
単なるスキャンだけではできませんね.
Mr.XRAYさん
それか、for k:=i to i+5 のようにして、i+3に白が来るときの座標を出して、辿っていくというのはどうでしょうか??
もしその方法で出来るのであれば、プログラムをどう組んでいいのかわからないので、ソースプログラムを知りたいです。
エッジ検出もままならない現状なので・・・><。
この辺が参考になるかも。
第3章 画像処理入門1 〜 アルゴリズム入門 〜
http://msdn.microsoft.com/ja-jp/cc998604.aspx
目的がラスターベクター変換なら、テーマが重すぎる気がしますが、
実現の暁には、相当なレベルアップが期待できます。
Novさん
ありがとうございます。
手も足も出なくて困っています・・・(泣
>実現の暁には、相当なレベルアップが期待できます。
御意.
簡単にできるようなら,画像処理の研究は苦労しませんって.
今回は,輪郭抽出が目的ではなく,自分で,その機能をコーディングしたいようですので.
がんばってください,というしかないですね.
Mr.XRAYさん
ありがとうございます。今日も徹夜で頑張って考えてみます。
https://www.petitmonte.com/bbs/answers?question_id=7886
こちらに合流しました。
ツイート | ![]() |