BMP画像から線を読み取るには?


ねこめし  2003-01-13 19:43:37  No: 2581

BMP形式にして取り込んだグラフを座標を新たに設定して、
数値化したいのです。
座標を読み取る前に、色を白、黒にしたいため、以下のコード
には、スマートではありませんが、そのような記述部分があり
ます。もともとのグラフは黒色の背景に白ラインなので、白ラ
インを黒ラインにして、背景を白にすると同時に黒くしたライ
ンの数値を読むよう、工夫してみたのですが、スピードが遅い
ことや、配列として扱う際に、配列の上限を設定するアイデア
が浮かばず、固定値をいれてます。
また、読み込んだ数値から、グラフの極大点を知りたい等、が
目標です。
長くなりますが、以下に試作してみたコードをつけます。
よきアドバイスよろしくお願い致します。

unit Unit1;

interface

uses
  Windows, Messages,SysUtils,Variants, Classes, Graphics, Controls, Forms, Dialogs,
  ExtCtrls, StdCtrls,
  unit2 ,ComCtrls;

type
  TForm1 = class(TForm)
    Button1: TButton;
    OpenDialog1: TOpenDialog;
    SaveDialog1: TSaveDialog;
    Button3: TButton;
    Button4: TButton;
    GroupBox1: TGroupBox;
    Button6: TButton;
    Memo1: TMemo;
    procedure Button1Click(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure Button4Click(Sender: TObject);
    procedure Button3Click(Sender: TObject);
    procedure Button6Click(Sender: TObject);

  private
    { Private 宣言 }
    BitMap     : TBitMap;
    CanselFlag : Boolean;
  public
    { Public 宣言 }       
    Procedure DeskTopSs; 
    Function RoundByte(Data:Double):Byte; 
  end;

var
  Form1: TForm1;

implementation

{$R *.DFM}

  Function TForm1.RoundByte(data:Double):Byte;
   begin
    if Data >= 255 then
       Data := 255;
    if Data <= 0 then
       Data := 0;
    RoundByte := Round(Data);
   end;

 procedure TForm1.DeskTopSs;

 var
  x,y          : SmallInt;  //ビットマップのX軸、Y軸
  CpBMP        : TBitMap;   //取り込まれた画面
  pxlRGB       : LongInt;   //ピクセル値(RGB型)
  R,G,B        : Byte;      //R値、G値、B値
  DC           : Hdc;       //デバイスコンテキストへのハンドルを定義
  rect         :Trect;      //Trect構造体を定義
  hdc          :hwnd;       //ウインドーへのハンドルを定義
  aryDay    : array of  array of  String;
 begin

  CanselFlag := False;

  ShowWindow(form2.Handle,sw_hide);
  sleep(300);

  CpBMP:=Tbitmap.Create;

  hdc:=GetForegroundWindow;

  getwindowrect(hdc,rect);

  CpBMP.width:=rect.Right-rect.Left;
  CpBMP.height:=rect.Bottom-rect.Top;

  dc:=getdc(0);
 bitblt(CpBMP.canvas.handle,0,0,rect.Right-rect.Left,rect.Bottom-rect.Top,dc,rect.Left,rect.top,srccopy);
  Form2.Image1.Picture.BitMap.Assign(CpBMP);

  releasedc(0,dc);

  ShowWindow(Form2.Handle,sw_show);
  SetLength(aryDay,10000 );

  for x := 0 to CpBMP.Width div 2 do

   begin
    SetLength(aryDay[x],10000);
    for y := 0 to CpBMP.Height do

     begin

      pxlRGB  := ColorToRGB( Form2.Image1.Canvas.Pixels[x,y] );

      R := GetRValue(pxlRGB);
      G := GetGValue(pxlRGB);
      B := GetBValue(pxlRGB);

      //色判定(判定色のみ描画)
               if R+G+B=765 then
                begin
                 CpBMP.Canvas.Pixels[x,y] := RGB(0,0,0);
                 aryDay[x][y]  := IntToStr(x)+','+IntToStr(y);
                 Memo1.Lines.Add(aryDay[x][y]);
                end
                 else begin
                 CpBMP.Canvas.Pixels[x,y] := RGB(255,255,255);
                 aryDay[x][y]  := '0';
                 Memo1.Lines.Add(aryDay[x][y]);
                end;
    end;

    Application.ProcessMessages;

    if CanselFlag then
      Break;
    end;

  if CanselFlag = False then

    Form2.ImageView(CpBMP);
 
  CpBMP.Free;

end;

{--TForm1 Event----------------------------------------------------------------}

procedure TForm1.Button1Click(Sender: TObject);
begin
  DeskTopSs;
end;

procedure TForm1.Button2Click(Sender: TObject);
begin
  CanselFlag := True;
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  BitMap := TBitMap.Create;
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
  BitMap.Free;
end;

end.{Unit1 End}


たかみちえ  URL  2003-01-13 20:58:40  No: 2582

遅いのなら、ScanLineプロパティを使ってはどうでしょうか?
このサイトのDelphiのページに、いくつか画像フィルタサンプルがありますけど、
それが参考になると思います。

  あと、XY位置を取るのは、TPointのほうが楽だと思います。
それから、TListを継承して、TPointのリストを作って、
それで各座標を管理するとか。
早くなるかどうかはわかりませんけど。

  あと、GetRValueなどは、ビット演算で一気に抜き出したほうが、
若干早くなるそうです。
  うちの"おんぷむらのうら">おんぷ村倉庫>Delphiユニット>stdGraphicsユニット内に、そういう関数があります。
ScanLineの使い方の参考になる関数(LightScreen関数)も中に入っているので、見てみてください。

  グラフ処理はやったことないので、これが役に立つかどうかわかりませんけど、
一応参考までに。


たかみちえ  URL  2003-01-13 21:09:27  No: 2583

気がついたんですけど、
>  Memo1.Lines.Add(aryDay[x][y]);
関数の最初と最後に、Memo1.beginupdateなどを呼び出すと早くなりますよ。

  あと、ループ内のIntToStrが、遅くなる原因になっているかもしれません。
できるだけループ外でやるようにしたり、どうしてもだめなら、事前に定数配列にしておくとかしておいたほうがいいと思います。


ねこめし  2003-01-14 12:04:06  No: 2584

ありがとうございます。
一つ一つ検討していきます。
時間がかかりそうですが^^;
今Scanlineについて調べています。
お付き合いください。


mam  URL  2003-01-17 02:39:19  No: 2585

こんにちは。はじめまして。
VBでいう、PSET関数、PGET関数を作ってみました。
ScanLineをじかに使うよりはかなり遅いですが、Pixelsをつかうよりは
かなり早いです。

procedure pset(bmp:TBitmap;point:TPoint;R,G,B:byte);
Type  TRGB=record B,G,R:byte; end;
      TRGBArray=array[0..65535] of TRGB;
      PRGBArray=^TRGBArray;
var   Color:TRGB;
      Line:PRGBArray;
begin
  if (bmp.Width<=0) or (bmp.Height<=0) then exit;
  if (point.x<0) or (point.x>(bmp.Width -1)) or
     (point.y<0) or (point.y>(bmp.height-1)) then exit;
  if Bmp.PixelFormat<>pf24bit then Bmp.PixelFormat:=pf24bit;
  Line:=Bmp.ScanLine[point.y];
  Color:=Line^[point.x];
  Color.R:=Byte(r);
  Color.G:=Byte(g);
  Color.B:=Byte(b);
  Line^[point.x]:=Color;
end;

procedure pget(bmp:TBitmap;point:TPoint;var R,G,B:byte);
Type  TRGB=record B,G,R:byte; end;
      TRGBArray=array[0..65535] of TRGB;
      PRGBArray=^TRGBArray;
var   Color:TRGB;
      Line:PRGBArray;
begin
  if (bmp.Width<=0) or (bmp.Height<=0) then exit;
  if (point.x<0) or (point.x>(bmp.Width -1)) or
     (point.y<0) or (point.y>(bmp.height-1)) then exit;
  if Bmp.PixelFormat<>pf24bit then Bmp.PixelFormat:=pf24bit;
  Line:=Bmp.ScanLine[point.y];
  Color:=Line^[point.x];
  r:=Color.R;
  g:=Color.G;
  b:=Color.B;
end;

使い方の例:

procedure TForm1.Button1Click(Sender: TObject);
var x,y:integer;
    point:TPoint;
begin
  Image1.Canvas.FillRect(rect(0,0,Image1.Width-1,Image1.Height-1));
  for x:=0 to Image1.Width-1 do
    for y:=0 to Image1.Height-1 do
    begin
      point.X:=x;
      point.Y:=y;
      Pset(Image1.Picture.Bitmap,point,255,0,0);
    end;
  Image1.Refresh;
end;

procedure TForm1.Button2Click(Sender: TObject);
var point:TPoint;
    r,g,b:byte;
begin
  point.X:=0;
  point.Y:=0;
  pget(Image1.picture.Bitmap,point,r,g,b);
  showmessage('R='+inttostr(r)+' G='+inttostr(g)+' B='+inttostr(b));
end;


※返信する前に利用規約をご確認ください。

※Google reCAPTCHA認証からCloudflare Turnstile認証へ変更しました。






  このエントリーをはてなブックマークに追加