Autor Beitrag
Clemens L.
ontopic starontopic starontopic starontopic starontopic starofftopic starofftopic starofftopic star
Beiträge: 158

Win Xp SP3
D6 Enterprise, Turbo Delphi Explorer
BeitragVerfasst: Fr 01.08.08 17:13 
Hallöchen mal wieder, :wave:

habe mal wieder ein kleines Problem. Und zwar muss ich auf dem aktuellen Bildschirminhalt ein bestimmtes Bild finden, das ich aus einer Bitmap lade.
Natürlich muss nicht immer der ganze Bildschirm abgesucht werden, sondern es ist immer ein Bereich gegeben. Nun weiss ich aber leider garnicht, wie ich das anstellen soll, denn es muss auch mit annehmbarer Geschwindigkeit laufen.

Den Bildschirminhalt ermittle ich so:

ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
procedure GetScreenShot (ABitmap : TBitmap);  stdcall;
var 
  DC : THandle; 
begin
  if Assigned(ABitmap) then
  begin 
    DC := GetDC(0);
    try
      ABitmap.Width := Screen.Width;
      ABitmap.Height := Screen.Height;
      BitBlt(ABitmap.Canvas.Handle,
             0,0,Screen.Width,Screen.Height,
             DC,
             0,0,
             SrcCopy
        ); 
    finally 
      ReleaseDC(0, DC);                       
    end
  end
end;


Außerdem zum Vergleichen von Farben: (mit angegebener Toleranz)

ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
function CompareColors(Color1, Color2, Tolerance: Integer): Boolean; stdcall;
var
  C1, C2: TColor;
begin
  Result := False;
  C1 := ColorToRGB(Color1);
  C2 := ColorToRGB(Color2);
  if (Abs(Round(GetRValue(C1) - GetRValue(C2))) <= Tolerance) then
    if (Abs(Round(GetGValue(C1) - GetGValue(C2))) <= Tolerance) then
      if (Abs(Round(GetBValue(C1) - GetBValue(C2))) <= Tolerance) then
        Result := True;
end;


Über Vorschläge, wie ich das am besten realisieren könnte, würd ich mich sehr freuen! :D

_________________
Gruß, Clemens
Vor dem 27.04.08 als "hackerTreff" bekannt!
JJ
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 100

Windows XP 64 bit!
delphi 5
BeitragVerfasst: Fr 01.08.08 22:41 
Also ich habe deinen Code nur kurz überflogen aber ich würde es folgendermaßen realisieren:
du suchst dir von der grafik 3 oder 4 pixel aus (oder eben mehr wenn du ganz sicher sein willst)!
Dann suchst du deinen Bildschirm per forschleife nach dem ersten pixel ab und überprüfst, ob die anderen Pixel auf dem bildschirm das selbe verhältnis haben, wie die in deiner Grafik... wenn nicht soll die forschleife eben weiterlaufen!
Wenn die forschleife durchgelaufen ist, aber die Pixelabfolge nicht gefunden wurde, so kannst du davon ausgehen, dass die grafik im bild nicht zu finden ist!

Du kannst die Farbe ja überprüfen und als kriterium nehmen

ich gebe dir hier mal ein bisschen Pseudocode (so wie ichs machen würde)

ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
for Scanhorizontal:=0 to Screen.width do
     begin
     for Scanvertical:=0 to Screen.height do
          begin
          //NUR PSEUDOCODE! Such dir bitte woanders wie du Pixelfarben vom Bildschirm auslesen kannst (3 Pixel im Beispiel)
          //Das ist aber nicht schwer ;-)
          If Pixelfarbe(Scanhorizontal,ScanVertical= Gesuchter 1.Pixel then
                begin
                If Pixelfarbe(Scanhorizontal+3,ScanVertical+3= Gesuchter 2.Pixel then     
                     begin
                     If Pixelfarbe(Scanhorizontal+6,ScanVertical+6= Gesuchter 3.Pixel then 
                          begin
                          //GEFUNDEN!!! Die Pixelanordnung entspricht der in der Grafik
            ...


Je genauer du vergleichen willst, desto mehr Pixel benötigst du, aber man muss ja die bilder nicht gleich komplett vergleichen. So gehts eindeutig schneller und die Laufzeit sieht auch besser aus...
Ist jedenfalls mein Vorschlag :-) Hoffe es hilft dir

_________________
Hallo!
Clemens L. Threadstarter
ontopic starontopic starontopic starontopic starontopic starofftopic starofftopic starofftopic star
Beiträge: 158

Win Xp SP3
D6 Enterprise, Turbo Delphi Explorer
BeitragVerfasst: Sa 02.08.08 12:08 
Hallo JJ, danke für deinen Vorschlag! :)

Das Problem an deinem Vorschlag ist, dass die Bilder ja von Variabler größe sind, du aber nach einem festen Vergleichsmuster suchst. Außerdem kann es in meinem Fall da problematisch werden, da viele Farben mehrmals vorkommen, und man daher besser komplett vergleichen sollte. Aber möglicherweise könnte man nach der ersten If-Abfrage in deinem Code mit zwei weiteren For-Schleifen prüfen, ob das Bild an der Stelle vorhanden ist.

Ich werd mal schaun, ob ich auf die Art und Weise was hinbekomme. Wer einen besseren Vorschlag hat, immer her damit! :)

_________________
Gruß, Clemens
Vor dem 27.04.08 als "hackerTreff" bekannt!
elundril
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 3747
Erhaltene Danke: 123

Windows Vista, Ubuntu
Delphi 7 PE "Codename: Aurora", Eclipse Ganymede
BeitragVerfasst: Sa 02.08.08 12:26 
du könntest vielleicht vom Desktop immer ein stückchen ausschneiden das so groß ist wie das bild, kopieren, nen Hash daraus machen und mit dem Hash von deinem Vorgegebenen Bitmap vergleichen. und dann verschiebst du den ausschnitt um eins nach recht. Wenn sich in der ersten zeile dann kein vollständiges bild ausgeht dann um ein pixel nach runter.

die zeitersparniss? Ganz einfach: den Hash zu errechnen und die werte miteinander zu vergleichen ist denk ich mir schneller aus die pixel einzeln zu berrechnen.

oder du suchst dir einfach die Bytefolge von der Bytefolge des größeren heraus. Das machst du indem du die erste zeile vom gesuchten bild nimmst und als Bytes im großen Bild suchst, wenn die nicht da ist, abbruch. Wenn schon das schaust du noch ob in der Nächsten BILDSCHRIMZEILE (auflösung zum anfang des letzten bruchstück hinzuaddiert) die bytefolge von der zweiten Zeile ist.


Das is mir (und meinem Vater) so spontan eingefallen. Leider kann man dabei aber nur genau das Bild im Bild finden und es muss alles übereinstimmen.

lg elundril

_________________
This Signature-Space is intentionally left blank.
Bei Beschwerden, bitte den Beschwerdebutton (gekennzeichnet mit PN) verwenden.
Clemens L. Threadstarter
ontopic starontopic starontopic starontopic starontopic starofftopic starofftopic starofftopic star
Beiträge: 158

Win Xp SP3
D6 Enterprise, Turbo Delphi Explorer
BeitragVerfasst: Sa 02.08.08 12:48 
Hmm, das wäre leider nicht möglich, da bestimmte abweichungen innerhalb eines Bereiches erlaubt sind. Ich versuch nachher mal mir etwas zu basteln, was dem ähnlich ist was JJ geschrieben hat.

---Moderiert von user profile iconNarses: Beiträge zusammengefasst---

So, habe jetzt eine Funktion geschrieben, die in einer annehmbaren Zeit eine Grafik findet:

ausblenden volle Höhe Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
60:
61:
62:
63:
64:
65:
66:
67:
68:
69:
70:
71:
72:
73:
74:
75:
76:
77:
78:
79:
80:
type
  TRGB32 = packed record
  B, G, R, A: Byte;
  end;
  TRGB32Array = packed array[0..MaxInt div SizeOf(TRGB32) - 1of TRGB32;
  PRGB32Array = ^TRGB32Array;

  TColorArray = array of array of Integer;

{...}

procedure GetColorArray(Bmp: TBitmap; var ColorArray: TColorArray);
var
  cx, cy: Integer;
  Line: PRGB32Array;
begin
  If Assigned(Bmp) then
  begin
    Bmp.PixelFormat := pf32bit;
    SetLength(ColorArray,Bmp.Width,Bmp.Height);
    for cy := 0 to Bmp.Height - 1 do
    begin
      Line := Bmp.ScanLine[cy];
      for cx := 0 to Bmp.Width - 1 do
      begin
        ColorArray[cx,cy] := RGB(Line[cx].R,Line[cx].G,Line[cx].B);
      end;
    end;
    Line := nil;
  end;
end;


function FindImage(Image: PChar; xs, ys, xe, ye, Tolerance: Integer; Pos: PPoint): Boolean; stdcall;
var
  searchImg: TBitmap;
  screenImg: TBitmap;
  searchColors, screenColors: TColorArray;
  ScanH, ScanV: Integer;
  SrchH, SrchV: Integer;
  Found, Finished: Boolean;
begin
  Result := False;
  Finished := false;
  If not FileExists(Image) then Exit;
  try
    searchImg := TBitmap.Create;
    searchImg.LoadFromFile(Image);
    screenImg := TBitmap.Create;
    GetScreenShot(screenImg);
    GetColorArray(searchImg,searchColors);
    GetColorArray(screenImg,screenColors);
    for ScanH := xs to xe do
    begin
    If Finished then Break;
      For ScanV := ys to ye do
      begin
      If Finished then Break;
        Found := true;
        For SrchH := 0 to SearchImg.Width - 1 do
        begin
          For SrchV := 0 to SearchImg.Height - 1 do
          begin
            If not CompareColors(screenColors[ScanH + SrchH,ScanV + SrchV],searchColors[SrchH,SrchV],Tolerance) then
              Found := false;
          end;
        end;
        If found then
        begin
          Pos^.X := ScanH;
          Pos^.Y := ScanV;
          Finished := true;
        end;
      end;
    end;
   finally
    FreeAndNil(searchImg);
    FreeAndNil(screenImg);
  end;
end;


Bei Verbesserungsvorschlägen / Fragen einfach melden. Ich geh Essen :P

_________________
Gruß, Clemens
Vor dem 27.04.08 als "hackerTreff" bekannt!
littlejo
Hält's aus hier
Beiträge: 1



BeitragVerfasst: So 19.10.08 20:51 
user profile iconClemens L. hat folgendes geschrieben Zum zitierten Posting springen:

Bei Verbesserungsvorschlägen / Fragen einfach melden. Ich geh Essen :P


Hallo,

könntest du erläutern, ob es geht Screenshot (selbst gemacht) mit einem Screenshot vom Programm gemacht zu vergleichen? - Oder halt wie es direkt mit den Pixeln geht?

Ich steige da nicht ganz durch und bin auch etwas verunsichert - da du den Code durch [...] gekürzt hast.

Würde mich freuen, wenn du das mal erklären könntest.

Lg

Jo
jaenicke
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 19315
Erhaltene Danke: 1747

W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
BeitragVerfasst: So 19.10.08 21:19 
Hallo und :welcome: im Forum!
user profile iconlittlejo hat folgendes geschrieben Zum zitierten Posting springen:
Ich steige da nicht ganz durch und bin auch etwas verunsichert - da du den Code durch [...] gekürzt hast.
Er hat doch gar nix wesentliches gekürzt, nur den Code für das Erstellen des Screenshots, aber der hat ja nichts damit zu tun. :nixweiss:

Die Frage ist eigentlich was du dabei nicht verstehst, werde mal etwas genauer.

user profile iconlittlejo hat folgendes geschrieben Zum zitierten Posting springen:
könntest du erläutern, ob es geht Screenshot (selbst gemacht) mit einem Screenshot vom Programm gemacht zu vergleichen? - Oder halt wie es direkt mit den Pixeln geht?
Der Quelltext findet ein Bild in einem gerade gemachten Screenshot.
Yogu
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 2598
Erhaltene Danke: 156

Ubuntu 13.04, Win 7
C# (VS 2013)
BeitragVerfasst: So 19.10.08 21:59 
user profile iconlittlejo hat folgendes geschrieben Zum zitierten Posting springen:
user profile iconClemens L. hat folgendes geschrieben Zum zitierten Posting springen:

Bei Verbesserungsvorschlägen / Fragen einfach melden. Ich geh Essen :P
könntest du erläutern, ob es geht Screenshot (selbst gemacht) mit einem Screenshot vom Programm gemacht zu vergleichen? - Oder halt wie es direkt mit den Pixeln geht?

Du gehst einfach alle Pixel durch, und vergleichst die von beiden Bildern.

Das hier ist zwar wahnsinnig langsam, aber dafür leicht zu verstehen:

ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
function ComparePictures(Pic1, Pic2: TBitmap): Boolean;
var x, y: Integer;
begin
  // Das macht es leichter: Wenn irgendetwas nicht stimmt, wird einfach die Methode verlassen
  Result := False;

  // Natürlich müssen Breite und Höhe übereinstimmen
  if (Pic1.Width <> Pic2.Height) or (Pic1.Height <> Pic2.Height) then Exit;

  // Alle Pixel durchgehen und vergleichen; wenn Farbe unterschiedlich, Prozedur verlassen
  for x := 0 to Pic1.Width-1 do
    for y := 0 to Pic1.Height-1 do
      if Pic1.Pixels[x, y] <> Pic2.Pixels[x, y] then Exit;

  // Wenn der Code bis hier richtig ausgeführt wurde, sind die beiden Bilder wohl gleich
  Result := True;
end;
(Ungetestet)


Mit ScanLine kann man das ganze noch beschleunigen.
jaenicke
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 19315
Erhaltene Danke: 1747

W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
BeitragVerfasst: So 19.10.08 22:11 
Ich habe mir den Quelltext von user profile iconClemens L. mal angeschaut, das lässt sich noch beträchtlich beschleunigen, da im oben geposteten Quelltext nicht abgebrochen wird, obwohl schon klar ist, dass ein Pixel nicht übereingestimmt hat.

Zudem wird gar nicht True zurückgegeben, wenn etwas gefunden wird. So ist es besser würde ich sagen:
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
      For ScanV := ys to ye do
      begin
      If Finished then Break;
        Found := true;
        For SrchH := 0 to SearchImg.Width - 1 do
        begin
          if not Found then
            Break;
          For SrchV := 0 to SearchImg.Height - 1 do
          begin
            If not CompareColors(screenColors[ScanH + SrchH,ScanV + SrchV],
              searchColors[SrchH,SrchV],Tolerance) then
            begin
              Found := false;
              Break;
            end;
          end;
        end;
        If found then
        begin
          Pos^.X := ScanH;
          Pos^.Y := ScanV;
          Finished := true;
          Result := True;
        end;
      end;