Einfache Texterkennung auf Webseite/Screen, Auslösen von klickevent an Screen Koordinaten?

Hallo,
ich sollte vielleicht vorausschicken was mein Ziel ist:
Ich will mir einen nach bestimmter Strategie agierenden Roulette Bot bauen.
Bot hört sich viel an, aber letztlich nur ein Skript dass im Prinzip eine vorgegebene Zeit lang immer die selben 6 Schritte wiederholt.

An 2 dieser Schritte scheitert es mir:

  1. Zum Einen wird nach einem Drehen des Rouletterads eine Zahl einer Farbe gezogen.
    Diese Farbe, die da gedreht wurde, interessiert mich.
    Nun gibt es , wie bei jeder Rouletteseite, oben rechts eine Auflistung der bisher gezogenen Zahlen.
    In der ersten Zeile die zuletzt gezogene Zahl, in der 2 . Zeile die Zahl davor, usw.
    war die gezogene Zahl rot, wird sie in der Zeile links stehen, wen Null dann grün und in der Mitte, und wenn schwarz, steht sie rechts.

Jetzt wäre halt die Frage, gibt es eine einfache Möglichkeit die Farbe der zuletzt gezogenen Zahl zu ermitteln?
Ideal wäre es natürlich wenn auch die Zahl an sich erkannt werden würde.

Ich habe da ein wenig Gegoogelt und bin pber Teseract und sowass gestoßen aber ehrlich gesagt verstehe ich das nicht wie das geht.
Ich komme auch auf Teufel komm raus nicht mit Github klar wo bspw dann dort soas steht „Ja, hier ist ein Easy skript um … zu tun“ und ich gucke oben und da sind 20 ordner mit kryptischen namen und dutzende Dateien aller Art darin verteilt mit ebenso kryptischen namen.
Und ich denke mir nur „Und was davon ist jetzt die Datei, die die unten meinen?
Ach, lassen wirs. Das geht mir ejtzt shcon wieder auf den …, unübersichtliches Github“

Kurzum, wenn es eine super easy Lösung gübe dieauch ein neuling hinkriegt bzw. verwenden kann, wäre das cool.
Die vorkommenden Zahlen sind auch immer alle im gleichen Font, gleiche Shcriftgröße, usw. Also sollte wirklich jede Texterkennung hinkriegen.
Farberkennung weiß ich halt nicht ob oder wie es eine grüne Zahl vor einem shcwarzen hintergrund bspw. erkennen könnte.

  1. Das klicken an sich.
    Ich wollte das Ganze anfangs in einer anderen Sprache versuchen aber die die einfach zu viele verkorkste Eigenheiten hat, wo der Shcöpfer der Sprache offenbar nicht an den Endnutzer gedacht hat, habe ich das aufgegeben.

Gibt es in java 1-2 einfache Befehle oder Möglichkeiten um „Klicke x Mal mit der linken/rechten Maustaste auf die Position (234,1012) auf dem Bildschirm“?
(wobei am Besten einfach wirklich der gesamte Bildschirm als Rahmen für die Koordinaten genommen wird)
Praktishc wäre es gegebenenfalls wnen man es nahc up und down event getrennt behandeln würde.
Also down event und up event separat voneinander an der position auslösen könnte. :slight_smile:

Ich hoffe mir kann da Jemand helfen. Was ich zu den themen fand, sind irgendwelche komishcen Bibliotheken wo ich nicht durchsteige geistig :frowning:

Zu 1. ein Neural Network (im Folgenden nenne ich es NN) kann sowohl die Farbe als auch die Zahl sehr gut erkennen. Es gibt einige Libs in Java für NNs, die sehr umfangreich sind, und noch weit mehr beinhalten als nur NNs. Es ist kurz darüber nachzudenken, ein NN selber zu implementieren. Beim Roulette würde es außerdem schon ausreichen, nur die Zahl zu erkennen, weil aus ihr ja unmittelbar die Farbe ableitbar ist…

Zu 2.: https://docs.oracle.com/javase/7/docs/api/java/awt/Robot.html mit Robot ist das Beschriebene überhaupt kein Problem. Wenn gewünscht, kann ich dir auch ein Beispiel dazu zeigen.

Dann noch zu deiner Strategie: Die einzig sinnvolle Strategie, die nur die Farbe berücksichtigt, ist natürlich denkbar einfach:

In der Theorie ist diese Strategie natürlich bombensicher. Allerdings wird sie in der Praxis nur mit Spielgeld funktionieren, denn es gibt 3 Faktoren, die diese Strategie aushebeln:

  • Begrenzter Einsatz
  • Begrenztes Eigenkapital
  • spezieller Zufallsgenerator

Das ist mit ein Grund, weshalb, soweit ich weiß, Online-Casinos verboten sind. Die sind ja auch nicht blöd…

Wenn der Minimaleinsatz zum Beispiel 5 € ist, müsstest du 163840 € auf eine Farbe setzen können (2^15*5), was dir kein Online-Casino der Welt erlauben wird…

  1. Das stimmt, die Zahl zu kennen würde reichen. Könnte es dann ja abgleichen ob es mit einer der roten oder schwarzen Farben (die in einem final int arra jeweils gespeichert sind) übereinstimmt.

Braucht es dau unbedingt ein neuronales Netzwerk?
Weil mit sowas hatte ich echt noch nie zu tun, davon habe ich gar keine Ahnung.

NN ist am einfachsten.

Ach, ich hab mal eben nachgeschaut, das Thema gibt es tausendfach (sowohl Blogs als auch Videos) im Netz:

https://www.google.com/search?q=neural+network+to+recognize+digits+java

https://itnext.io/building-a-handwritten-digit-recognizer-in-java-4eca4014eb2f:

/*
Construct the neural neural
*/
log.info(“Build model….”);
MultiLayerConfiguration conf = new NeuralNetConfiguration.Builder()
.seed(SEED)
.learningRate(LEARNING_RATE)
.weightInit(WeightInit.XAVIER)
//NESTEROVS is referring to gradient descent with momentum
.updater(Updater.NESTEROVS)
.list()

Das sind jetzt 6 Zeilen, also doch nicht allzu kompliziert…

Links zur Lib:
https://github.com/eclipse/deeplearning4j
https://www.baeldung.com/deeplearning4j

Also, da ein Neuronales Netzwerk auch nur am Ende eine Turing Maschine ist, wird man damit auch irgendwie jedes Problem mit dem Notwendigen Aufwand lösen. Daher kann man nicht sagen, dass die Lösung von CB falsch ist.

Mal davon abgesehen, dass hier eigentlich nur mit BUZZWORDs um sich geworfen wird…

Jedoch ist die Lösung ALLES ANDERE als trivial und hier muss man deutlich niedriger einsteigen.

Zunächst ist mir nicht klar, ob es sich bei dem Spiel um eine Webanwendung handelt oder eine andere Art der Anwendung.

Ist es ein Browsergame, dann stehen die Informationen irgendwo schon da. Der Browser stellt sie ja da, dann müsste man anaysieren, wie die Webseite die Daten verarbeitet. Anschließend kann man mit entsprechenden BrowserPlugins auf diese Daten zugreifen und diese auch manipulieren,.

Alternativ: Man nimmt sich AutoIt. Ich meine das hätte so eine Pipettenfunktion. Damit kann man die aktuelle Farbe unter dem Cursor bestimmen. Die Funktion nennt sich PixelSearch die API findet man recht gut.

Ansonsten wäre es gut, wenn man mehr Informationen zur Ansicht etc. bekäme, um hier genauer helfen zu können.

@bernd1 So sehr es mir widerstrebt, hier einzelne Leute so „an den Pranger zu stellen“: CyborgBeta ist der Forentroll, der ständig Leute nervt, verwirrende und auf technischer Ebene unsinnige Kommentare abgibt, immer Flamewars startet, und trotz regelmäßiger Sperren und Beitragslöschungen immer wieder kommt. Ein bißchen wie Unkraut.


Wie martin123 schon gesagt hat: Man bräuchte viel mehr Informationen über das genaue Umfeld und das Ziel - nicht nur „Da soll Roulette gespielt werden“, sondern auf der Ebene von sinnvollen Zwischenschritten. Wenn das eine Aufgabe aus einer Vorlesung „Bilderkennung mit Neuronalen Netzen“ wäre, dann könnte man was mit Neuronalen Netzen machen. In jedem anderen Fall wäre das mit ziemlicher Sicherheit unsinnig. Wenn es um eine Aufgabe in einer Vorlesung „Netzwerkkommunikation“ geht, dann gibt es vielleicht irgendeine Schnittstelle, auf die man zugreifen kann, um die zuletzt gezogene Zahl zu bekommen. Wenn das nur eine Art „Freizeitprojekt“ ist, und es „egal“ ist, wie das Ziel erreicht wird, bräuchte man trotzdem noch mehr Informationen. Ob man da mit einem Browser-Plugin rangeht (und was die Frage aufwirft, ob Java denn überhaupt der geeignete Tech-Stack ist - das wäre nämlich vermutlich nicht so…), oder ob man da wirklich mit Robot&Co was machen kann, müßte man sich überlegen.

Technisch gesehen, und wenn es um irgendeine Webseite geht: Sowas hab’ ich schonmal gebastelt. Genaugenommen ging es dabei darum, einen „Civilization (1) Bot“ zu schreiben - wobei „Bot“ zu viel gesagt ist. Es ging nur darum, aus dem Titelbild…

Intro01

herauszulesen, welche Technologien man am Anfang hat. Dazu habe ich das Alphabet als einzelne Bilder gespeichert…

Alphabets

und diese Buchstaben dann in einem Screenshot wie dem obigen gesucht, und da die Technologien rausgelesen, und das dann in Textdateien gespeichert:

Caesar you have risen to become leader of the Romans May y ur reign be long and p sperous The Romans ha knowledge of Irrigation Mining Currency Bronze Working and Roads

Die fehlenden Buchstaben kommen da her, dass da der effing Cursor drüber war :neutral_face: . Aber es ging nur darum, mit welcher Nation man wie oft welche Techologien hat. (Kurz: Russen sind am besten :roll_eyes: )

Der Code dafür ist recht überschaubar, aber halt gandenlos „dreckig“ hingeschrieben:

    String extract()
    {
        StringBuilder sb = new StringBuilder();
//        for (int y=0; y<image.getHeight(); y++)
//        {
//            for (int x=0; x<image.getWidth(); x++)
        System.out.println("Using hacky limits in text extraction..."); // XXX
        for (int y=0; y<900; y++)
        {
            for (int x=0; x<1000; x++)
        
            {
                char letter = findLetterAt(x, y);
                if (letter != '?')
                {
                    //System.out.println("At "+x+" "+y+" found "+letter);
                    Point newPoint = readWord(x, y, sb);
                    //System.out.println("sb now "+sb);
                    x = newPoint.x;
                    y = newPoint.y;
                }
            }
        }
        //System.out.println("Found "+sb.toString());
        return sb.toString();
    }

    private Point readWord(int x, int y, StringBuilder sb)
    {
        int currentX = x;
        while (true)
        {
            char letter = findLetterAt(currentX, y);
            if (letter != '?')
            {
                sb.append(letter);
                currentX += letterImages.get(letter).getWidth();
            }
            else
            {
                //System.out.println("No letter at "+currentX);
                //System.out.println(findLetterAt(currentX-1, y));
                //System.out.println(findLetterAt(currentX+1, y));
                sb.append(' ');
                currentX += 1;
                return new Point(currentX, y);
            }
        }
    }
    
    private char findLetterAt(int x, int y)
    {
        for (char c='a'; c<='z'; c++)
        {
            BufferedImage letter = letterImages.get(c);
            boolean found = Images.isSubImageAt(image, x, y, letter, Integer::compare);
            if (found)
            {
                return c;
            }
        }
        for (char c='A'; c<='Z'; c++)
        {
            BufferedImage letter = letterImages.get(c);
            boolean found = Images.isSubImageAt(image, x, y, letter, Integer::compare);
            if (found)
            {
                return c;
            }
        }
        return '?';
    }

Dazu kam noch eine Klasse RobotController, die eine convenience-Schicht um den Robot herum ist, und mit der man sowas machen kann wie

        RobotController robotController = new RobotController();
        robotController
            .delay(5000)
            .keyType(KeyEvent.VK_C)
            .keyType(KeyEvent.VK_I)
            .keyType(KeyEvent.VK_V)
            .keyType(KeyEvent.VK_ENTER) // Start civ
            .delay(500)
            .keyType(KeyEvent.VK_1) // Config
            .delay(500)
            .keyType(KeyEvent.VK_1) // Config
            .delay(500)
            .keyType(KeyEvent.VK_1) // Config
            .delay(5000)
            .keyType(KeyEvent.VK_ESCAPE) // Skip first intro
            .delay(5000)
            .keyType(KeyEvent.VK_DOWN)
            .delay(500)
            .keyType(KeyEvent.VK_DOWN) // Select "Earth"
            .delay(500)
            .keyType(KeyEvent.VK_ENTER) // Start "Earth" game
            .delay(40 * 1000) // The actual intro, while the world is created
            .keyType(KeyEvent.VK_ENTER) // Chieftain
            .delay(500)
            .keyType(KeyEvent.VK_DOWN)
            .delay(500)
            .keyType(KeyEvent.VK_DOWN)
            .delay(500)
            .keyType(KeyEvent.VK_DOWN)
            .delay(500)
            .keyType(KeyEvent.VK_DOWN) // Select 3 Civilizations
            .delay(500)
            .keyType(KeyEvent.VK_ENTER) // Start with 3
            .delay(500);

was im wesentlichen Civilization in der Konsole startet, sich durch’s Menü hangelt bis das oben gepostete Intro-Bild kommt, da den Text rausliest, und danach (mit einer ähnlichen Folge von keyType-aufrufen usw) das Spiel speichert und beendet.

Nun. Das geht alles technisch „relativ einfach“. Aber … wenn man das Ziel nicht genau kennt, hilft die Information, dass das „irgendwie geht“ nicht so viel weiter…


Edit: Noch ein kleines Outtake aus dem, was ich damals geschrieben hatte:

        ...
        catch (IOException e)
        {
            System.out.println("Fuckit");
            e.printStackTrace();
        }

Jaaaa, das war nicht ganz „production quality“ :smiley:

Naja, das ist ein privates Projekt sozusagen.
Muss nichts bestimmtes verwenden, insofern ist das am Interessantesten was einfach und schnell geht (er sollte keine zig Sekunden brauchen um eine Zahl zu erkennen und zuzuordnen)

Ich will damit eine Strategie umsetzen wie bspw.
„Leerdrehen bis 5 mal die gleiche Farbe kommt, dann mit Martingale 4 mal auf rot und anschließend 4 mal auf schwarz wetten. Wenn immer noch nicht gewonnen, starte von vorne“

Und das Skript eben die nötigen Schritte durchgehen.

Natürlich brauche ich Methoden, um Klicks auf die verschiedenen Einsatzbuttons und so zu machen.
Klick auf Drehbutton, klick auf Einsatz löschen Button, etc.

Es werden also erst die Einsätze geklickt (bzw. mit dem Einsatz löschen Button sichergestelltdass Leergedreht wird), dann den Dreh Button geklickt, x Sekunden gewartet, dann
eben geguckt welche Farbe die zuletzt gewürfelte Zahl hat.

Ich speicher mir die zuletzt gedrehten Zahlen bspw. in einem int Array mit Länge 13.
Wenn eine neue Zahl hinzukommt, wird eben Alles nahc hinten geschoben und das letzteZeichen im Array fliegt raus.

Zu Spielbeginn wird es eben mit 13 mal -1 initialisiert bis wir irgendwann mind. 13 Eingaben haben.

Naja, dann wird ausgehend vom geupdateten zahlenarray eben der Einsatz für nächste Runde bestimmt.
Dann beginnt die nächste Runde, es werden die gerade bestimmten Einsätze gesetzt, usw.

So in der Art.

Irgendwie im Hintergrund sollte noch ein Timer laufen der nach Ablauf von bpw. 3 Stunden seit Programmstart Diverses tut und den Browser schließt und das Programm beendet.

So in etwa. Anbei einfach mal ein Bild wie die Casino Seite aussieht (ist das Netent Casino wers genau wissen will)
Im Screenshot ist oben rechts die Auflistung mit den Zahlen, die oberste Zahl ist eigentlich eine weise Zahl, nur (ich hatte das vergessen bzw. die Seite wurde geändert) blinkt die zuletzt gezogene Zahl.
(Also ein Tick ist die weiße Zahl da, im nächsten Tick weg, im 3. Tick wieder da)
Aber das sollte ja nicht das Problem sein, wird eben 2-3 Mal nahc einer Zahl geprüft.
In einem der Versuche werden wir die zahl doch erwischen :slight_smile:

PS: Ich will eigentlich bevorzugt nicht über irgendwelche Dom Strukturen oder so an die Werte kommen, sondern idealerweise wirklich durch optische Erkennung der Zahlen.
Damit könnte ich dann auch bspw. den Guthabenstand ablesen und Co., hätte also durchaus mehrere Vorteile :slight_smile:

Erste Frage die mir gerade auffällt, redest du von Java oder JavaScript?
Und deine UI sollte auf deinen Daten basieren und nicht deine Daten auf deiner UI.
Weil nur mal so als Punkt, 6 oder 9 :wink:

Ich meine durchaus Java.
Das ist die Programmiersprache von der ich noch die meiste Ahnung habe (wenngleich auch trotzdem nicht viel, was über simple Unikurse hinausgeht) :slight_smile:
Habe vorher versucht das Ganze mit Autohotkey zu lösen aber ich hasse diese Sprache offen gestanden wegen ihrer zwiespältigen Syntax mit Legacy/Expression Mode und Co.

Nicht wie bei Java, wo jeder Befehl festgelegte Input, Outputparameter und werte hat und nicht so ein undurchsichtiges Chaos. Bei Autohotkey blickt doch kein Schwein durch -.-

Den Rest deiner Frage habe ich nicht wirklich verstanden, wie im Screenshot sieht jedenfalls die Webseite aus mit diversen dynamischen Elementen die sich halt ändenr je nachdem wo wann geklickt wird (Roulette Software halt). Wie das Seitenintern mit Html, php, Widgets, und wat weiß ich strukturiert ist, weiß ich nicht. Die Netent Software ist ja in dem Casino vermutlich auch nur irgendwie eingebunden.
Mich da durchzuhangeln bis ich bspw. die zuletzt gedrehte Zahl habe, wird schwer; daher würd eich otpische Zeichenerkennung bevorzugen (ich meine, es müssen ja nichth mal Buchstaben entziffert werden; im Prinzip reicht es wenn die Zahlen 0-9 erkannnt werden können. Der Rest lässt sich anhand der Fundposition ja logisch herleiten)

Und oben rechts die ziemlich klein geshcriebenen roten oder weißen (oder grün, fehlt hier drauf weil nur die Zahl Null grün ist und die zu erdrehen mir zu lange dauerte nur für einen Screenshot) Zahlen hätte ich gerne eingelesen.

Unten die zahl hinter Einsatz lesen zu können wäre auch nice :slight_smile:

ok mich hat deine Aussage mit dem Browser und Dom nur irritiert.

ach ok mom sorry hab deine Frage falsch verstanden, hätte sie erst nachm Essen lesen sollen und nicht davor :smiley:

statt mit Bilderkennung oder so, guck dir an wie das Spiel gebaut wurde, also reine Frontend Anwendung oder FE<-> BE, Wenn ein Backend im Spiel ist kannst du gucken ob du dich nicht da irgendwie mit reinhängen kannst und die Daten abgreifst.

Außerdem je nach Frontend Technik, wenn es pures JS + HTML ist guck wie du an die Datenlogik kommst, ist Fummellei aber kann einfacher als alles andere sein.

Da irgendwelche Zahlen zu erkennen könnte, wie gesagt, theoretisch sehr einfach sein. (Bezogen auf das Beispiel mit Civilization: Man macht einen Screenshot, und schaut, wo das vorher als „das Zeichen“ gespeicherte Bild in diesem Screenshot auftaucht).

Aber jede auch noch so kleine Abweichung kann das ganze gleich um Größenordnungen schwieriger machen. Zwei die mir ganz spontan einfallen:

  • Wenn diese Seite auflösungsabhängig skaliert, wird’s schwierig. Wenn d.h. die Schriftgröße oder die Größe der dargstellten Elemente allgemein von der Größe des Browserfensters abhängt, könnte man den „einfachen“ Ansatz nur verwenden, wenn man die Größe auf eine GANZ bestimmte Größe festlegt. („Vollbild“ würde sich anbieten, wenn man nicht vorhat, morgen einen neuen Monitor zu kaufen :wink: )
  • Die Zahl blinkt ?!? :expressionless: Echt jetzt? Das ist halt kagge. Da weiß man nie, welchen Stand man im Screenshot erwischt hat.

Und als Verallgemeinerung davon

Damit könnte ich dann auch bspw. den Guthabenstand ablesen und Co

So einfach ist das eben nicht immer. Da hat man unterschiedliche Schriftgrößen, Schriftarten, unterschiedliche Hintergründe… da muss man dann früher oder später irgendwas anwenden, was allgemeine OCR ist. Da gibt’s vielleicht Ansätze, aber eine konkrete Empfehlung könnte ich nicht aussprechen.

Sagen wirs mal so:
Ich will das Programm jetzt gar nicht so „universell“ bauen dass es von sich aus mit allen möglichen Sachen klarkommt. Ich will es schon so auf die hier gegebene Situation, wie im Screenshot, abstimmen.
Ich will dass Programm schon so bauen dass es auf meine Verhältnisse zugeschnitten ist.

Insofern kann ich da schon hingehen und Bilder der einzelnen Zahlen speichern und bei Bedarf abgleichen.

Falls ich halt mal zu einem anderen Computer, Auflösung und so wehcsle, müsste ich halt im Code die Koordinaten anpassen und die Vergleichsbilder ersetzen.

Naja, ich weiß ja nicht wie lang das Screenshot machen+OCR dauert, aber solange es kurz ist, würe ich einfach 2 oder 3 Screenshots+OCR im Abstand von 0.5 Sekunden oder so machen. Auf einem der 3 wird man ja wohl die Zahl erwischen.
Ich meine, die blinkende Zahl wechselt ihre „Sichtbarkeit“ jede Sekunde oder so, also nicht allzu schwer die während der sichtbaren Phase anzutreffen :slight_smile:

Wie gesagt, auch Guthabenstand und so wird inm selben Font, Schriftgröße und so wie auf dem Screenshot sein, also insofern ich da einem Screenshot eines passend bemessenen Rechtecks um die Zahl mache, dürfte OCR kein großes Problem sein. Hoffe ich :slight_smile:

Ich mache generell das Programm ja speziell für meine Zwecke und für die Benutzung auf meinem einzigen notebook.
Insofern ändert sich da höchstens alle paar Jahre mal was an Auflösung und Co.
Da kann ich die Maße und so schon „hardcoden“ in dem Sinne. :slight_smile:

Ich will die Koordinaten hardcoden dass sie genau auf meinen Computer, Auflösung, Chrome iN Vollbild, etc. passen.
Eben auf eine ganze bestimmte Konfiguration.

Bei der Civilization Sache oben, war das der ganze Code oder hast du da Klassen noch importiert?
Weil du da bspw. „letterImages“ vorkommt, eine Klasse die oben nicht mit dabei steht :slight_smile:

Da ist natürlich noch ein Haufen Kram drumherum. Aber wie das so ist, wenn man etwas nicht „universell“ bauen will, sondern nur mal schnell was macht, was auf die eigenen Verhältnisse zugeschnitten ist: Man hat dann irgendwelchen Code, der vielleicht etwas „nützliches“ macht, aber für sich genommen nicht „nützlich“ ist. Zumindest nicht besonders.

Die Klasse LetterImages war nur eine Klasse, die (hartverdrahtet) eine Map mit den Bildern für die jeweiligen Buchstaben füllt:

class LetterImages 
{
    private final Map<Character, BufferedImage> letters;
    
    public LetterImages()
    {
        letters = new LinkedHashMap<Character, BufferedImage>();
        for (int i=0; i<26; i++)
        {
            char cU = (char)('A'+i);
            BufferedImage imageU = Images.readUnchecked("./data/upper/" + cU + ".png");
            letters.put(cU, imageU);
            
            char cL = (char)('a'+i);
            BufferedImage imageL = Images.readUnchecked("./data/lower/" + cL + ".png");
            letters.put(cL, imageL);
        }
    }
    
    BufferedImage get(String letter)
    {
        return letters.get(letter.charAt(0));
    }
    BufferedImage get(char letter)
    {
        return letters.get(letter);
    }
}

Und da kommt natürlich jetzt die Klasse Images vor, die auch gefehlt hat… das TODO, mal eine Lib zu erstellen, wo ich ein paar Utility-Funktionen für images zusammenwerfe, steht schon seit Jahren auf meiner Liste, und… einige dieser Funktionen hier wären dann Teil davon:

package de.javagl.civ;

import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.RenderingHints;
import java.awt.geom.AffineTransform;
import java.awt.image.AffineTransformOp;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.function.IntBinaryOperator;

import javax.imageio.ImageIO;

public class Images
{
    static BufferedImage readUnchecked(String path)
    {
        try
        {
            return convertToArgb(ImageIO.read(new File(path)));
        }
        catch (IOException e)
        {
            e.printStackTrace();
            return null;
        }
    }
    
    static BufferedImage scale(
        BufferedImage image, int factor)
    {
        int w = image.getWidth();
        int h = image.getHeight();
        RenderingHints renderingHints = new RenderingHints(null);
        renderingHints.put(
            RenderingHints.KEY_INTERPOLATION, 
            RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR);
        return scale(image, w * factor, h * factor, renderingHints);
        
    }
    
    private static BufferedImage scale(
        BufferedImage image, int w, int h,
        RenderingHints renderingHints)
    {
        BufferedImage scaledImage = new BufferedImage(w, h, image.getType());
        double scaleX = (double) w / image.getWidth();
        double scaleY = (double) h / image.getHeight();
        AffineTransform affineTransform = 
            AffineTransform.getScaleInstance(scaleX, scaleY);
        AffineTransformOp affineTransformOp = new AffineTransformOp(
            affineTransform, renderingHints);
        return affineTransformOp.filter(
            image, scaledImage);
    }

    public static BufferedImage convertToArgb(BufferedImage image)
    {
        BufferedImage newImage = new BufferedImage(
            image.getWidth(), image.getHeight(),
            BufferedImage.TYPE_INT_ARGB);
        Graphics2D g = newImage.createGraphics();
        g.drawImage(image, 0, 0, null);
        g.dispose();
        return newImage;
    }    
    
    static Point findImageLocation(
        BufferedImage mainImage, 
        BufferedImage subImage, 
        IntBinaryOperator rgbComparator)
    {
        int w = mainImage.getWidth();
        int h = mainImage.getHeight();
        for (int x=0; x < w; x++)
        {
            for (int y = 0; y < h; y++)
            {
                if (isSubImageAt(mainImage, x, y, subImage, rgbComparator))
                {
                    return new Point(x, y);
                }
            }
        }
        return null;
    }
    
    static boolean isSubImageAt(
        BufferedImage mainImage, int x, int y, 
        BufferedImage subImage, 
        IntBinaryOperator rgbComparator)
    {
        int w = subImage.getWidth(); 
        int h = subImage.getHeight();
        if (x + w > mainImage.getWidth())
        {
            return false;
        }
        if (y + h > mainImage.getHeight())
        {
            return false;
        }
        for (int ix=0; ix < w; ix++)
        {
            for (int iy = 0; iy < h; iy++)
            {
                int mainRgb = mainImage.getRGB(x + ix, y + iy);
                int subRgb = subImage.getRGB(ix, iy);
                
                //System.out.println("At "+x+" "+y+" sub "+ix+ " "+iy+" have "+Integer.toHexString(mainRgb)+" and "+Integer.toHexString(subRgb));
                
                if (rgbComparator.applyAsInt(mainRgb, subRgb) != 0)
                {
                    return false;
                }
            }
        }
        return true;
    }

}