Autor |
Beitrag |
Clemens L.
      
Beiträge: 158
Win Xp SP3
D6 Enterprise, Turbo Delphi Explorer
|
Verfasst: Fr 01.08.08 17:13
Hallöchen mal wieder,
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:
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)
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! 
_________________ Gruß, Clemens
Vor dem 27.04.08 als "hackerTreff" bekannt!
|
|
JJ
      
Beiträge: 100
Windows XP 64 bit!
delphi 5
|
Verfasst: 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)
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 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 ... |
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. 
      
Beiträge: 158
Win Xp SP3
D6 Enterprise, Turbo Delphi Explorer
|
Verfasst: 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
      
Beiträge: 3747
Erhaltene Danke: 123
Windows Vista, Ubuntu
Delphi 7 PE "Codename: Aurora", Eclipse Ganymede
|
Verfasst: 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. 
      
Beiträge: 158
Win Xp SP3
D6 Enterprise, Turbo Delphi Explorer
|
Verfasst: 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 Narses: Beiträge zusammengefasst---
So, habe jetzt eine Funktion geschrieben, die in einer annehmbaren Zeit eine Grafik findet:
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) - 1] of 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 
_________________ Gruß, Clemens
Vor dem 27.04.08 als "hackerTreff" bekannt!
|
|
littlejo
Hält's aus hier
Beiträge: 1
|
Verfasst: So 19.10.08 20:51
Clemens L. hat folgendes geschrieben : |
Bei Verbesserungsvorschlägen / Fragen einfach melden. Ich geh Essen  |
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
      
Beiträge: 19315
Erhaltene Danke: 1747
W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
|
Verfasst: So 19.10.08 21:19
Hallo und  im Forum!
littlejo hat folgendes geschrieben : | 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.
Die Frage ist eigentlich was du dabei nicht verstehst, werde mal etwas genauer.
littlejo hat folgendes geschrieben : | 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
      
Beiträge: 2598
Erhaltene Danke: 156
Ubuntu 13.04, Win 7
C# (VS 2013)
|
Verfasst: So 19.10.08 21:59
littlejo hat folgendes geschrieben : | Clemens L. hat folgendes geschrieben : |
Bei Verbesserungsvorschlägen / Fragen einfach melden. Ich geh Essen  | 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:
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 Result := False;
if (Pic1.Width <> Pic2.Height) or (Pic1.Height <> Pic2.Height) then Exit;
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;
Result := True; end; | (Ungetestet)
Mit ScanLine kann man das ganze noch beschleunigen.
|
|
jaenicke
      
Beiträge: 19315
Erhaltene Danke: 1747
W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
|
Verfasst: So 19.10.08 22:11
Ich habe mir den Quelltext von Clemens 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: 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; |
|
|
|