# Bilderkennung Zahl



## Fabse (9. Jul 2012)

Hi,

habe folgenden Screenshot: 
	

	
	
		
		

		
			





Der Wert ändert sich mehrmals in der Sekunde. Hab schon nen java Programm was mir immer diesen Ausschnit als Bild speichert, jetzt möchte ich alle 5-10 Sekunden ein Bild erzeugen und dann den Wert aus dem Bild rausholen. Ich weiß, dass es so ziemlich das schwierigste ist, was man machen kann, aber durch den weißen Hintergrund und der schwarzen Zahl müsste es doch einfach umzusetzen sein?

Also ich will nur den schwarzen Wert auf dem weißen Hintergrund, als Wert in meinem Java Programm.

Wer kann mir da helfen?


----------



## SlaterB (9. Jul 2012)

weiße Zeilen erkennen wirst du doch wohl schaffen, dann könntest du den Rest abscheiden, bleibt nur noch eine Zahl auf einem Bild, fertig,
oder ist die Position gar immer dieselbe? dann muss dich in angepassten Programm der Rest gar nicht erst groß interessieren,
nur einen bestimmten x/y-Bereich anschauen,

sind es immer zwei Nachkommastellen, zwischen 0 und 100%, bleibt es bei dieser Schriftart, Schriftgröße usw?

dann könntest du mit einfachen Mitteln und bisschen Arbeit noch weiter kommen, 
suche nach senkrechten weißen Linien, beginnende erste schwarze Pixel, schneide die Zahlen raus,
so dass sie immer in ein festes Raster einzeln gegeben sind, 

dann suche nach markanten Eigenschaften,
wahrscheinlich reicht es, einfach nur bestimmte Pixel zu prüfen, 

```
000####000
0##000##00
0#00000#00
00000##000
0#00000#00
0##000##00
000####000
```
hier ein Pseudobeispiel für die 3, wenn also an x,y 1,2 ein schwarzes Pixel steht und an fünf weiteren Punkten auch, die bei anderen Zahlen nicht auch alle gleichzeitig gelten, dann ist eine 3 erwiesen,

usw. für alle Zahlen testen, das ganze mit möglichst vielen Testdaten prüfen

--------

wenn so ein einfacher Selbsbauweg nicht geht, ist das große Suchwort fürs Internet, 
falls noch nicht bekannt, Bilderkennung, Mustererkennung, Image analysis,
Thema auch umbenannt


----------



## Fabse (9. Jul 2012)

Erst mal vielen dank für die schnelle Hilfe 

geht das auch alles mit der Robot Klasse oder welche Klassen kann ich dafür nehmen?



SlaterB hat gesagt.:


> weiße Zeilen erkennen wirst du doch wohl schaffen, dann könntest du den Rest abscheiden, bleibt nur noch eine Zahl auf einem Bild, fertig,
> oder ist die Position gar immer dieselbe? Nein dann muss dich in angepassten Programm der Rest gar nicht erst groß interessieren,
> nur einen bestimmten x/y-Bereich anschauen,
> 
> ...


----------



## SlaterB (9. Jul 2012)

ich dachte an BufferedImage, da kann man Pixel einzeln abfragen


----------



## JohannisderKaeufer (9. Jul 2012)

Wenn du tatsächlich für die selben Werte, das selbe Bild erzeugen kannst.

Also, wenn es nur um die zahl schwarz auf weiss geht, dann sollte auch nur diese auf dem Bild sein.

Dann kannst du die Bilder hashen und dir mit den Hashes eine Knowledgebase generieren und die hashes miteinander vergleichen.


```
import java.awt.image.BufferedImage;
import java.io.File;
import java.util.Arrays;
 
import javax.imageio.ImageIO;
 
public class ImageToHash {
 
    public static int hashImage(String filename) throws Exception {
      BufferedImage image = ImageIO.read(new File(filename));
      int[] array = new int[image.getWidth() * image.getHeight()];
      image.getData().getPixel(0,0, array);
      return  Arrays.hashCode(array);
    }

    public static void main(String[] args) throws Exception {
        int hashA = hashImage(args[0]);
 
        int hashB = hashImage(args[1]);
 
        System.out.println(hashA == hashB);
 
	System.out.println(hashA);
	System.out.println(hashB);
    }
}
```

Der Clou dabei ist, das für das gleiche Bild immer der gleiche integerwert erzeugt wird. Folglich kannst du für alle möglichen Bilder diesen int und die dazugehörende Zahl abspeichern bis du alle Zahlen beisammen hast.

Wenn du dann dein Programm startest mußt du nur noch das aktuelle Bild hashen um einen hashwert zu bekommen und nachschauen zu welcher Zahl er gehört.


----------



## SlaterB (9. Jul 2012)

oder aus der Höhe des weißen Balken auch den zugehörigen ungefähren Prozentwert ermitteln


----------



## JohannisderKaeufer (9. Jul 2012)

Ach, der Balken ändert sich auch in der Höhe. Dachte zuerst der bleibt immer an selbiger.

Was manchmal noch einfacher ist, sich die Api anschauen und den Wert irgendwo anders herbekommen.
Logfiles zum Beispiel. Gerade im Unix Bereich sind viele GUI-Frontends nur "aufgeflanscht" um eine darunterliegende Konsolenanwendung mit der Maus bedienen zu können.


----------



## Marco13 (9. Jul 2012)

Ich hätte da jetzt ganz pragmatisch von jeder Ziffer ein Bild erstellt, und dann geschaut, wo und in welcher Reihenfolge die Ziffern-Bilder im aktuellen Bild enthalten sind. SEHR pragmatisch, ja  aber vermutlich auch sehr einfach...


----------



## Fabse (9. Jul 2012)

Hi,

ich habs jetzt so vor:
Ich fange oben links in der Ecke an und hol mir von jedem Pixel den RGB Wert. Dann komm ich irgendwann in den weißen Bereich. Die koordinaten merk ich mir. Dann zähl ich bei Photoshop wie breit und hoch das weiße Feld ist, dann hab ich das weiße Feld "eleminiert". (da weiß ich noch nicht mit welcher Funktion ich das machen könnte?)
Dann geh ich wieder das weiße feld ab und mach mir nen 2d array. Alle RGB werte < 200 bekommen eine 0, alle anderen eine 1. Dann hab ich ja quasi die zahlen eleminiert.

Könnte das so funktionieren oder is das arg kompliziert?


----------



## Fabse (9. Jul 2012)

Hmm...

hatte das jetzt gerade mal so versucht, aber es dauert ewig das Programm läuft und läuft gibt mir aber nix auf der Konsole aus, das kann doch nicht so lange dauern ein 70 x 110 Pixel bild abzusuchen?

Hier mal mein Code:


```
import java.awt.AWTException;
import java.awt.Color;
import java.awt.Rectangle;
import java.awt.Robot;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;

import javax.imageio.ImageIO;


public class alarm {

	
	
	public static void main(String[] args) throws AWTException, IOException {
		
		//Screenshot machen
		
		Robot alarm = new Robot();
		BufferedImage bild = new BufferedImage(1920,1080,2);
		Rectangle rec = new Rectangle(640,435,70,110);
		
		bild = alarm.createScreenCapture(rec);
		
		ImageIO.write(bild, "JPG", new File("C:\\Users\\Fabian\\Desktop\\alarm\\alarm.jpg"));
		
		//Screenshot auswerten
		
		Color weiß = new Color(255,255,255);
		
		int x1 = 0;
		int y1 = 0;
		
		//doppel Forschleife um durch das Bild zu kommen, merke wo das erste mal weiß ist
	
		for (int y9 = 1; y9 < 110;y9++) {
			
			for (int x9 = 1; x9 < 70; x9++) {
				
				if (alarm.getPixelColor(x9, y9) == weiß) {
					
					x1 = x9;
					y1 = y9;
					x9 = 100;
					y9 = 200;
					
				}
			}
	
		}
		
		System.out.println(x1);
		System.out.println(y1);
	}

}
```


----------



## Fabse (9. Jul 2012)

Tante Edit:

Mit der getRGB von Buffered Image gehts schon mal schneller! Aber da bekomm ich nur int Werte! Woher weiß ich welche int Wert welcher RGB wert ist?
Kann es sein das -1 weiß ist?

btw. ich meld mich hier mal geschwind an, dann mach ich keine Doppelposts mehr...


----------



## Xeonkryptos (9. Jul 2012)

Ich weiß es nicht genau, aber kurz in die API geschaut, ist zu erkennen, dass man mit dem ColorModel herausfinden kann, welche Farbe hinter dem RGB-Integer steckt:



> The ColorModel abstract class encapsulates the methods for translating a pixel value to color components (for example, red, green, and blue) and an alpha component.



Getestet habe ich es nicht und wissen tue ich es auch nicht! Es entnehme ich einfach aus der API!


----------



## SlaterB (9. Jul 2012)

testen lautet immer das Zauberwort, erstelle ein weißes Bild mit Paint, schaue dir die Werte an,
erstelle ein schwarzes Bild mit Paint, schaue dir die Werte an

wenn etwas langsam ist, dann finde zumindest erstmal mit weiteren Ausgaben heraus, ob das einzelne Befehle sind wie das Capture,

Objekte, insbesondere Strings, aber auch hier Color immer mit equals, nicht mit == vergleichen,
wenn du gar lokal extra ein Color-Objekt erzeugst, kann das unmöglich == getIrgendwas() sein


----------



## Marco13 (9. Jul 2012)

getPixelColor von Robot war ohnehin nicht das, was du da wolltest  image.getRGB ist schon richtig. Der zurückgegebene Wert ist eben der RGB-Farbwert. Dabei wäre 

```
private static final int WHITE_RGB = 0xFFFFFFFF;

...

if (image.getRGB(x,y) == WHITE_RGB) { ... }
```

Aber spätestens mit Antialiasing wird das mit dem Unterscheiden von Farben ziemlich frickelig (Start->Programme->Zubehör->Eingabehilfen->Bildschirmlupe um das gepostete Bild mal genauer anzusehen - da sind zwar auch noch JGP-Artefakte dabei, aber allgemein...)

Wie willst du denn dann anhand er Pixel die Zahl erkennen? ???:L


----------



## hüteüberhüte (10. Jul 2012)

Erster Schritt ist doch immer, das Bild anhand der RGB-Werte weiß und schwarz zu machen. Anschließend, wenn es immer die gleichen Ziffern sind, Hash erstellen und vergleichen oder irgendwie eine Figur/Shape erkennen


----------



## Marco13 (10. Jul 2012)

Ja, das mit dem hash hatte ich mit so einem leich flauen Bauchgefühl gelesen... 



> Der Clou dabei ist, das für das gleiche Bild immer der gleiche integerwert erzeugt wird.



Wenn's dumm kommt (und ... als Programmierer sollte man damit rechnen, dass es dumm kommt - damit hat man nämlich meistens Recht   ) würde so ein Hash dann für zwei Zahlen-Bilder den gleichen Wert liefern... In diesem Fall könnte ein equals auf den Arrays nicht schaden...


----------



## Fabse (10. Jul 2012)

Also ich bin jetzt soweit, dass ich das ausgangsbild auf den weißen Bereich beschränkt habe und wieder ein neues daraus gemacht habe. Dann hab ich alle einzelnen Zahlen rausgeholt und wieder als eigenständiges Bild gemacht. Jetzt brauch ich nur noch die Einzel-Zahl-Bilder auf schwarz oder weiß machen bzw. einfach nen 2d array machen was dann z.b. 6x19 ist und alles was nicht weiß ist, ist eine 1, der rest eine 0 und dann den arrays die passende Zahl zu ordnen 
Ist das mit hashen gemeint?


```
import java.awt.AWTException;
import java.awt.Color;
import java.awt.Rectangle;
import java.awt.Robot;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;

import javax.imageio.ImageIO;


public class alarm {

	
	
	public static void main(String[] args) throws AWTException, IOException {
		
		//Screenshot machen
		
		Robot alarm = new Robot();
		BufferedImage bild = new BufferedImage(1920,1080,2);
		Rectangle rec = new Rectangle(640,435,70,110);
		
		bild = alarm.createScreenCapture(rec);
		
		ImageIO.write(bild, "JPG", new File("C:\\Users\\Fabian\\Desktop\\alarm\\alarm.jpg"));
		
		//Screenshot auswerten
		
		Color weiß = new Color(255,255,255);
		
		int x1 = 0;
		int y1 = 0;
		
		//doppel Forschleife um durch das Bild zu kommen, merke wo das erste mal weiß ist
	
		for (int y9 = 0; y9 < 110; y9++) {
			
			for (int x9 = 0; x9 < 70; x9++) {
				
				if(bild.getRGB(x9, y9) == -1) {
					
					x1 = x9;
					y1 = y9;
					x9 = 100;
					y9 = 200;

				}
				
//			System.out.println(bild.getRGB(x9, y9));
					
										
				}
			}
		
//		System.out.println(x1);
//		System.out.println(y1);
	
		// Höhe des weißen Feldes 19 Pixel
		// Breite des weißen Feldes 44 Pixel
		
		int x2 = 0;
		int y2 = 0;
		
		x2 = x1 + 19;
		y2 = y1 + 44;
		
		BufferedImage bild2 = new BufferedImage(x2,y2,2);
		
		bild2 = bild.getSubimage(x1, y1, 44, 19);
		
		ImageIO.write(bild2, "JPG", new File("C:\\Users\\Fabian\\Desktop\\alarm\\alarm2.jpg"));
		
	
		//Breite erste Zahl 13
		
		BufferedImage zahl1 = new BufferedImage(13,19,2);
		
		zahl1 = bild2.getSubimage(0, 0, 13, 19);
		
		ImageIO.write(zahl1, "JPG", new File("C:\\Users\\Fabian\\Desktop\\alarm\\zahl1.jpg"));
		
		//Breite zweite Zahl 6
		
		BufferedImage zahl2 = new BufferedImage(6,19,2);
		
		zahl2 = bild2.getSubimage(13, 0, 6, 19);
		
		ImageIO.write(zahl2, "JPG", new File("C:\\Users\\Fabian\\Desktop\\alarm\\zahl2.jpg"));
		
		//Breite dritte Zahl 6
		
		BufferedImage zahl3 = new BufferedImage(6,19,2);
		
		zahl3 = bild2.getSubimage(19, 0, 6, 19);
		
		ImageIO.write(zahl2, "JPG", new File("C:\\Users\\Fabian\\Desktop\\alarm\\zahl3.jpg"));
		
		//Breite vierte Zahl 6
		
		BufferedImage zahl4 = new BufferedImage(6,19,2);
		
		zahl4 = bild2.getSubimage(28, 0, 6, 19);
		
		ImageIO.write(zahl4, "JPG", new File("C:\\Users\\Fabian\\Desktop\\alarm\\zahl4.jpg"));
		

	}

}
```


----------



## SlaterB (10. Jul 2012)

Hash ist hier im Sinne von Prüfsumme gemeint
Prüfsumme ? Wikipedia

wenn man allein schon die schwarzen Bits einfach zählt, kommen am Ende vielleicht verschiedene Anzahlen raus, 
das reicht schon zur Unterscheidung, sofern immer exakt dasselbe Bild vorliegt, was nicht ganz leicht ist,

falls doch zwei Ziffern gleiche Anzahl haben, kann man die Position mit einbeziehen, so dass es unterschiedlich wird

edit:
wobei, eine Prüfsumme hat doch eher eine bisschen andere Aufgabe,
wenn man einfach nur Testfall mit Referenz vergleichen will, dann könnte man direkt beim Wort 'Vergleich' bleiben, 
Pixel für Pixel abgleichen (Referenz im Programm vorhanden) statt zu zählen


----------



## Fabse (10. Jul 2012)

Bin gerade nen bissl weitergekommen, aber jetzt häng ich...

hab in den Array m0-m9 die zahlen drinn mit 0 und 1. Jetzt will ich ne for schleife machen um durch alle array nacheinander durchzugehen und mit der aktuellen zahl vergleichen will, aber ich merke gerade, dass das nicht!?

Kann ich nicht:


```
for(int i = 0; i > 9; i++) {

          for (int k = 0; k < m0.length;i++) {
			
			for (int j = 0; j < m0[0].length; j++) {
				
				if (m4 [k] [j] != a_m01 [k] [j])


....
```

da will ich jetzt das m0.length quasi mit mi.length ersetzen, sodass ich nicht für jedes array ne abfrage machen muss. 

Wie lautet dafür die Syntax?


----------



## SlaterB (10. Jul 2012)

http://www.java-forum.org/top-fragen/62032-fragen-variablennamen.html

es gibt keinen Variablennamen-Zusammenbau, verzichte auf m0 bis mx einzeln, lege die Arrays in eine Liste bzw. ein höheres Array


----------



## Fabse (10. Jul 2012)

ahja stimmt sowas hatte man mal gelernt :rtfm:

danke


----------



## Gonzo17 (10. Jul 2012)

Dumme Frage hierzu:

[JAVA=37]        for (int y9 = 0; y9 < 110; y9++) {

            for (int x9 = 0; x9 < 70; x9++) {

                if(bild.getRGB(x9, y9) == -1) {

                    x1 = x9;
                    y1 = y9;
                    x9 = 100;
                    y9 = 200;

                }

//          System.out.println(bild.getRGB(x9, y9));


                }
            }[/code]

Du verwendest x9 und y9 nur als Zählvariablen in der for-Schleife. Du setzt dann anscheinend die Werte 100 und 200 für diese Variablen nur, damit die Schleife abbricht? Sehr unschön und würde ich auf jeden Fall anders lösen! Stell dir mal vor du änderst irgendwann aus irgendeinem Grund die for-Schleife, sie läuft länger und auf einmal hast du Probleme.

Mein Tipp: Mach eine Methode, in der du diese doppelte for-Schleife auslagerst und mach dann entsprechend ein 
	
	
	
	





```
return
```
 der Werte, die du zurückgeben möchtest.


----------

