Exakter Screenshot

Zeeu

Aktives Mitglied
Hi leute, ich habe ein Problem, ich möchte mit meinem Programm einen Screenshot machen.
Auf diesem Screenshot möchte ich eine Mustererkennung laufen lassen.

Das sieht so aus, dass ich mir in einem festgelegten bereich mit

Java:
public static int getPixel(BufferedImage img,int x, int y){
		int arr = 0;
		for(; y<20; y+=4)
			for(; x<20; x+=4)
				arr += img.getRGB(x, y);		
		return arr;		
	}

durchlaufe.

Mit selbst erstellten Screenshots, die ich ins Programm lade, klappt das super, aber diese mit dem Code unten erstellten Screenshots sind nicht genau, einige Pixel unterscheiden sich einfach bei den selben Mustern, daher findet die Mustererkennung dort nicht :-(

Java:
	public static BufferedImage makeScreen() throws Exception {
		BufferedImage bi = new Robot().createScreenCapture( 
		new Rectangle(Toolkit.getDefaultToolkit().getScreenSize()) ); 	
		ImageIO.write( bi, "png", new File("c:/javaprog/scgreenshot.png") );
  
		return bi;
	}

Kennt jemand eine Methode, mit der man in java exakte Screenshots machen kann ?

MfG Zeeu
 

Volvagia

Top Contributor
Hab da noch nie einen Unterschied bemerkt. Vielleicht nützt es dir zu wissen, dass du einen Component-Inhalt per printAll auf ein Bild zeichnen kannst.
 

madboy

Top Contributor
Ich kann dir zwar mit den Screenshots nicht helfen, aber folgendes sieht sehr seltsam aus.
Java:
public static int getPixel(BufferedImage img,int x, int y){
		int arr = 0;
		for(; y<20; y+=4)
			for(; x<20; x+=4)
				arr += img.getRGB(x, y);		
		return arr;		
	}
Bist du dir sicher dass du die Werte summieren willst? Das gibt Überläufe und vermutlich seltsame Ergebnisse. Siehe auch BufferedImage (Java 2 Platform SE v1.4.2))
 

Zeeu

Aktives Mitglied
@madboy, wie der inhalt der variable nachher haussieht, ist mir im prinzip egal, hauptsache der Wert,
wie wirr er auch immer sein mag, ist bei allen gleichen Mustern der selbe. das ist auch so, zumindest wenn die bilder Exakt gleich sind.

@volvagia, ich weiss nicht was du meinst, also im prinzip habe ich nen haufen Spielkarten.
In meinem Programm Speichere ich nach dem Prinzip wie in meiner oberen Methode den Code.

Jetzt mache ich ein Screenshot von einem Spielfeld, und schaue ob ich einen dieser Codes finde.

Das Problem ist, dass bei den minderwertigen Screenshot die ich mit der zweiten Methode gemacht habe,
die Selben Karten unterschiedliche werte habe.

Wenn ich ganz nah an das Bild ran zoom, erkenne ich, dass bei einem Pik Ass einige Pixel anders sind als bei einem anderen Pik Ass.

Für mich gibts jetzt 3 wege:
1. eine andere Mustererkennung, die nicht auf 100%ig genauigkeit wert legt.
2. Schwarz/weiss Screenshots(keine Graustufen)
3. Genauere Screenshots
 

Cola_Colin

Top Contributor
Eventuell das ganze als bitmap abspeichern und nicht als png ?
png ist zwar eigentlich verlustfrei, aber bitmap komprimiert eben gar nicht. Da sollte dann doch keine Verfälschung auftreten ?
Klingt merkwürdig.
 

Zeeu

Aktives Mitglied
Java:
 ImageIO.write( bi, "png", new File("c:/javaprog/scgreenshot.png") );

 ImageIO.write( bi, "jpg", new File("c:/javaprog/scgreenshot.jpg") );

 ImageIO.write( bi, "bmp", new File("c:/javaprog/scgreenshot.bmp") );

 ImageIO.write( bi, "gif", new File("c:/javaprog/scgreenshot.gif") );

schon alles ausprobiert.

Edit: Hab mir sagen lassen, dass BufferedImage komprimiert ist, und es daher kommt, kennt jemand eine Alternative ?

Edit 2: Schwarz weiss fällt raus, die Bilder haben immernoch kleine unterschiede :-(
 
Zuletzt bearbeitet:

madboy

Top Contributor
Dass BufferedImage komprimiert sein soll wäre mir neu.

Was genau machst du? Screenshot erstellen, in eine Datei schreiben, neuen Screenshot erstellen, in neue Datei schreiben und dann die Dateien vergleichen?
Wie vergleichst du die Dateien?
 
A

anonym

Gast
Du liest da über Screenshots Daten ein, die dein Programm vorher selbst geschrieben hat?!?
 

Zeeu

Aktives Mitglied
Ich habe ein Feld mit karten, von diesem feld will ich wissen, wo welche karten sind, da ich nur Karten auf bestimmten feldern benutzen kann.

damit ich nicht jedesmal ein screenshot von einem Spielfeld machen muss, lade ich mir ein feld statisch ein. da ich mit des Tests durch bin, und alles läuft,
mache ich den screenshot jetzt zur laufzeit, und ersetze den Dateinamen den ich lade, einfach nur mit dem namen des screenshots.

Daher ja, ich mache einen shot, speicher ihn, und lade ihn dann. (habs auch schon probiert, den screenshot gleich weiter zu bearbeiten ohne ihn als Datei wieder einzulesen, hilft auch nicht.


das Programm läuft Folgendermaßen:
Ich mache einen Screenshot von dem Spielfeld,
durchsuche es nach Karten,
speichere alle Karten die benutzbar sind (oben liegen)
berechne alle möglichen kombinationen die aus den verfügbaren karten in summe auf einen festgelegten wert kommen,
berechne aus allen kombinationen die eine, die am meisten punkte gibt.
klicke diese Karten an.

das ganze läuft zu testzwecken noch recht statisch.
ich habe intern für jede Karte einen bestimmten Pixel code gespeichert,
der einfach nur die summe von einem 20x20 pixel feld dieser Karte ist.

Also ist jede Karte mit dieser summe eindeutig zu identifizieren.

Das Problem ist einfach, wie schon erläutert, dass der Robot Screenshots macht,
wo die 2 Karten (z.B. 2x das Pik Ass) nicht den selben code haben.


PS: Ja es ist ein bot, der ein Kartenspiel wie Solitäir Selbstständig lösen soll.
 
Zuletzt bearbeitet:

HalloWelt_

Mitglied
Ich denke da wäre es besser nicht alle Pixel nach der reihe zu addieren, sondern mit einer Methode der es nicht egal ist
ob zu erst weiß und dann schwarz kommt, oder zuerst schwarz und dann weiß.

Ich denke mit MessageDigeset würde dieses Problem nicht auftreten:
Java:
        MessageDigest a = MessageDigest.getInstance("MD5");
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        ImageIO.write(img, "bmp", out);     
        a.digest(out.toByteArray()); //Damit vergleichen
        out.close();

Aber wahrscheinlich ist es zu genau: Wenn nur ein Pixel etwas anders ist klappt es nicht mehr.
 

hansmueller

Bekanntes Mitglied
Hallo,

es gibt in der Klasse Robot noch die Methode getPixelColor(int x, int y).
Wenn du den entspechenden zu untersuchenden Bildschirmbereich mit dieser Methode in ein 2-dimensionales Array abspeicherst, hast du letztendlich auch die Infos, die du brauchst ohne das irgendwas durch irgendein Bild-Komprimierungsverfahren verfälscht wird.

Das png und bmp nicht richtig funktionieren finde ich allerdings schon seltsam ???:L

MfG
hansmueller
 

Crian

Top Contributor
Und wie kommt dein Programm zu diesen Karten? Irgendwie habe ich das Gefühl, dass du einen Irrweg gehst. Beschreibe doch bitte mal die Ausgangssituation und das, was du erreichen möchtest. Vielleicht brauchst du gar keine Screenshots.
 

Zeeu

Aktives Mitglied
ich denke ich hab das oben schon erläutert, ich möchte z.b. solitär spielen.
C:/programme/spiele/solitär.exe (als beispiel)
mein programm soll mir sagen welche züge grad alle möglich sind, also brauch mein Programm informationen über das spiel, welche karten wo liegen.
dazu benötige ich diesen screenshot.

@hallowelt, das ist das problem, wenn ein pixel anders ist, gehts nimmer.

@hans, ich muss ja aber irgendwie an das teil kommen, und mit dem Screenshot den Robot mir macht, gehts nicht

Edit: Gegeben sei ein Computerspiel in form eines Kartenspiels.
mein Programm soll ein Bot sein, das dieses Spiel selbstständig löst.
die informationen, welche karten wo liegen etc, soll es sich über einen Screenshot holen.
 
Zuletzt bearbeitet:

Marco13

Top Contributor
Vielleicht hängt das mit dem Bildtyp zusammen? Hast du irgendein Beispiel, wo man das Verhalten reproduzieren kann? Sinnlos-planlos rum-geraten könnte man mal schauen, was passiert, wenn man

BufferedImage bi = new BufferedImage(w,h,BufferedImage.TYPE_INT_RGB);
Graphics2D g = bi.createGraphics();
g.drawImage(screenshot, 0,0,null);
ImageIO.write(bi, "png", new File("c:/javaprog/scgreenshot.png") );

macht (d.h. den Screenshot erst in ein Bild (ohne Transparenzen) reinmalt und das dann speichert)... ist aber wirklich ins Blaue...
 

Cola_Colin

Top Contributor
Ich hab mal ein wenig herumprobiert und einmal ganz normal den Screenshot gemacht, dann per getPixelColor einen int Array mit den Zahlenwerten erstellt und ein simples Differenzbild erzeugt.

Java:
package junk;

import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.*;
import javax.imageio.ImageIO;

/**
 *
 * @author Colin Clausen
 */
public class ScreenshotDifferenceTest {

    public static Rectangle getScreenRect() {
        return new Rectangle(0, 0, 50, 50);
    }

    public static BufferedImage createNormalScreenshot() throws AWTException {
        Robot robot = new Robot();
        System.out.println("creating normal screenshot");
        return robot.createScreenCapture(getScreenRect());
    }

    public static int[][] createPerPixelScreenshot() throws AWTException {
        Robot robot = new Robot();
        
        System.out.println("creating pixel screenshot");
        
        Rectangle screen = getScreenRect();
        
        int[][] result = new int[screen.width][screen.height];
        
        for (int x = 0; x < screen.width; x++) {
            for (int y = 0; y < screen.height; y++) {
                result[x][y] = robot.getPixelColor(screen.x+x, screen.y+y).getRGB();
            }
        }
        
        return result;
    }
    
    public static BufferedImage createDifferenceImage(int[][] genuineData, BufferedImage possiblyCorruptedData) {
        int width = possiblyCorruptedData.getWidth();
        int height = possiblyCorruptedData.getHeight();
        
        System.out.println("comparing screenshots");
        
        if (width != genuineData.length || height != genuineData[0].length) {
            throw new IllegalArgumentException("array and image need to be of the same size !");
        }
        
        BufferedImage difImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
        
        for (int x = 0 ; x < width; x++) {
            for (int y = 0; y < height; y++) {
                if (possiblyCorruptedData.getRGB(x, y) != genuineData[x][y]) {
                    difImage.setRGB(x, y, Color.BLACK.getRGB());
                } else {
                    difImage.setRGB(x, y, Color.WHITE.getRGB());
                }
            }
        }
        
        return difImage;
    }
    
    public static void main(String[] args) throws AWTException, IOException{
      
        int[][] pixelScreenshot = createPerPixelScreenshot();
        BufferedImage normalScreenshot = createNormalScreenshot();
        BufferedImage difImage = createDifferenceImage(pixelScreenshot, normalScreenshot);
        
        System.out.println("writing result");
        ImageIO.write(normalScreenshot, "png", new File("C:\\Users\\Cola_Colin\\Desktop\\normal.png"));
        
        ImageIO.write(difImage, "png", new File("C:\\Users\\Cola_Colin\\Desktop\\dif.png"));
    }
}

Im Anhang das Differenzbild -welches keinen Unterschied zeigt- und der Screenshot, der beim normalen Verfahren entstanden ist.

Dabei fällt auch noch auf, dass die Methode jedes Pixel einzeln einzulesen schrecklich langsam ist.
Es dauert bei mir ca 45 Sekunden den 50*50 großen Ausschnitt aufzuzeichnen.
Wenn ich in der Zeit das Fenster bewege, dann sieht man das später im Differenzbild, da der per Pixel Screenshot praktisch ein verwischtes Bild aufgezeichnet hat.
Die Methode wäre für größere Flächen also so oder so total unbrauchbar.

Wegen dem Alpha Kanal:

Das ist das Colormodel, welches vom Robot für den Screenshot erzeugt wird:
Java:
        /*
         * Fix for 4285201 
         * Create a DirectColorModel equivalent to the default RGB ColorModel,
         * except with no Alpha component.
         */

        screenCapCM = new DirectColorModel(24,
                         /* red mask */    0x00FF0000,
                         /* green mask */  0x0000FF00,
                         /* blue mask */   0x000000FF);
Kein Alpha also.

Kann aber so oder so keinen Unterschied zwischen dem Screenshot und dem was ich auf dem Bildschirm sehe ausmachen...
 

Anhänge

  • dif - Kopie.png
    dif - Kopie.png
    113 Bytes · Aufrufe: 55
  • normal.png
    normal.png
    3,3 KB · Aufrufe: 52

Zeeu

Aktives Mitglied
Marcos beispiel hatte ich schon ausprobiert, nur mit w,h,BufferedImage.TYPE_INT_BINARY

ich glaube ich hab den Fehler aber grad gefunden, bin noch am testen, ob es nur glück war ^^

Edit: Fehler gefunden, mag garnicht sagen was es war :-(
nennen wir es mal "Dummer Anwender Fehler"...

habs grad auch live im spiel getestet, funktioniert wunderbar, musste nur ein paar konstante verändern.

Danke für eure hilfe, hat mir sehr geholfen. :)

MfG Zeeu
 
Zuletzt bearbeitet:

Ähnliche Java Themen


Oben