# Möglichkeiten, ein Bild schnell auszuwerten



## ankmanu (29. Mrz 2010)

Hi@all,

ich bin gerade dabei, ein webcambild auszuwerten, indem ich auf diesem Bild nach gelben Bereichen suche, dieser Bereich aber zwischen 4 grünen Eckpunkten sein muss!

Bisheriger Ablauf:
-Webcambild wird gespeichert und dann in ein BufferedImage umgewandelt
-Es wird mit der Funktion 
	
	
	
	





```
xyz.getRGB(a,b);
```
die Farbe des Pixels mit den Koordinaten a und b ausgelesen.
-Es werden ALLE Pixel ausgelesen. 
-Wenn ein Pixel zwischen einem bestimmten Bereich von Farbwerten liegt, dann wird es in eine Arraylist gespeichert.
Der Bereich ist zum Beispiel so definiert:

```
Color c = new Color(rgb);
        
        if(c.getRed()>0 && c.getRed()<=20 && c.getGreen()>200 && c.getGreen()<=255 && c.getBlue()>=0 && c.getBlue()<=20){
...
}
```

-Da es auf dem Bild vier grüne "Häufungen" (Bereiche, die grün sind) gibt, werden von den gefundenen grünen Pixeln alle gelöscht, die ähnliche x-Koordinaten/y-Koordinaten haben (x+-20, y+-20).
-Es ergeben sich vier grüne Pixel.
-Nun wird die Auswertung wiederholt, nur das NICHT ALLE Pixel ausgelesen werden, sondern NUR die, die zwischen den 4 Eckpunkten liegen (und es wird NICHT nach GRÜNEN, sondern nach GELBEN Pixeln gesucht).

-Schließlich wird aus den gelben Pixeln ein Schwerpunkt errechnet, sodass sich ein gelbes Pixel mit x und y-Koordinaten ergibt.
-Es wird von dem Wert der x-Koordinate des gelben Pixels der Wert des linken (ob oberen oder unteren ist egal)Eckpunkts abgezogen.
-Bei der y-Koordinate geschieht genau das selbe.

Durch diese Auswertung habe ich immer den richtigen x-Wert(und y-Wert) des Schwerpunkts, und eben NUR zwischen den 4 grünen Punkten.

______________________________________________________________________
Das ganze klappt schon ganz gut, allerdings sind in meinem Code einige Fehler enthalten(das Programm hängt sich auf, weil es immer keine Pixel mit den entsprechenden Farbwerten findet*!).

*auch wenn:
	
	
	
	





```
c.getRed()>0 && c.getRed()<=255 && c.getGreen()>0 && c.getGreen()<=255 && c.getBlue()>=0 && c.getBlue()<=255
```
, was eigentlich heißt, das ALLE Pixel erscheinen müssten!


Bevor ich euch den ganzen Code poste, möchte ich fragen, ob das ganze nicht viel einfacher zu realisieren wäre??
Denn so ist das SEHR zeitintensiv, allein eine Auswertung nur aller 30-ten Pixel dauert eine halbe Minute!!!

Wäre es einfacher(und schneller), wenn ich das Bild in ein binäres wandle, und dann 0 für irgendeine Farbe steht, und 1 in der ersten Auswertung für grün, in der zweiten für gelb??

oder gibt es noch andere Möglichkeiten?

Und wenn, WIE geht das dann?? (das mit dem Binären Bild kann ich auch nicht )

Vielen Dank im Vorraus für eure Antworten
manu


----------



## Marco13 (29. Mrz 2010)

"Ich weiß, dass ich nichts weiß".

(Und jetzt noch ein Zitat von Dieter Nuhr, dann dürfte ich eigentlich nichts mehr schreiben  )

Aber zumindest weiß ich, dass sowas beliebig aufwändig sein kann. In Computer Vision, Feature Detection und Markerless Tracking wurden schon etliche Millionen Euro und etliche Jahre Forschung von sehr cleveren Leuten gesteckt, und trotzdem kann ein Computer noch keinen Autoreifen von einem verbrannten Donut unterscheiden, weil eben beides schwarz und rund ist und in der Mitte ein Loch hat 

Was du mit getRGB und Pixeln usw. beschreibst klingt SEHR low-level. Es wäre gut, zu wissen, ob man irgendwelche Annahmen treffen kann. Wenn es um eine Webcam geht, dann kann wahrscheinlich mal die Beleuchtung sch...lecht sein, vielleicht das Bild verschwommen, und, was nicht ganz unwichtig ist: Das zu erkennende Objekt kann größer oder kleiner oder beliebig gedreht sein. Allerdings scheinst du das auch nicht zu berücksichtigen: Wenn die "Grünen Häufungen" einen Radius von 20 Pixeln haben, kann man schonmal davon ausgehen, dass das gesamte Objekt nicht nur 40 Pixel groß ist. Außerdem ist es wohl nicht gedreht, sonst wäre es eine Raute, und es gäbe keine "linke obere" Ecke....

Ohne dich beunruhigen zu wollen: Ich gehe (mit meinem Halbwissen) davon aus, dass es nicht möglich ist, allein durch "Pi-mal-Daumen"-Pixelgeschätze eine zuverlässigen Erkennung hinzubekommen. Wenn ich jetzt vor diesem Problem stehen würde, würde ich (mich fragen, wie ich dazu komme :shock:  aber) vermutlich erstmal eine Kantenerkennung machen (Stichwort "Sobel-Filter"), daraus dann Linien erstellen (Stichwort "Hough-Transform"), dann schauen, ob ich 4 Linien finde, bei denen jeweils EINE mindestens(!) ZWEI andere schneidet, und dann vielleicht noch prüfen, ob bei den Schnittpunkten im Bild "viele" Grüne Pixel liegen oder auf jeweils einer Seite dieser Linie zwischen zwei Schnittpunkte "viele" gelbe Pixel liegen... Wissend, dass das nur ein hemdsärmeliger, auf Halbwissen aufbauender Ansatz wäre, und es da sicher noch viel robustere (aber eben auch vieeel kompliziertere) Ansätze gibt...

Wenn es wirklich auf Pixelzählen und Abstandschätzen basieren soll, wäre ein bißchen Code und vielleicht ein Beispielbild sicher nicht verkehrt, aber wie viel Zeit man dann in die Lösung investieren kann, kann man so kaum sagen...


----------



## ankmanu (29. Mrz 2010)

danke schon mal für die interessante Antwort,

kannst du mir mal ein Beispiel geben zu dem Sobel-Filter etc., dass ich weiß, wie das geht?


Aber grundsätzlich müsste es doch möglich sein, industrieroboter können das auch schon, mom vielleicht find ich gleich ein Video....

Viele Grüße
manu


----------



## ankmanu (29. Mrz 2010)

Hier ist zum Beispiel ein Video, wo die Roboter die Teile erkennen und danach erfassen...

Chocolate Robots on Flickr - Photo Sharing!

für mich würde es reichen, das das Programm die Position des gelben Gegenstands(bei mir ist es eine runde Tonne, Durchmesser ca. 2cm) erkennt! 

Gruß
manu


----------



## ice-breaker (29. Mrz 2010)

Also das ist bei weitem keine fertige Lösung, aber eine Idee:
In der Uni haben wir mal versimpelt die Erkennung eines Fußballs auf einem Bild (robocup ) als Hausaufgabe machen müssen, da wurde dann mit dem HSV-Farbraum das Bild ausgewertet und es blieben nur Farben in dem Bild, die in etwa mit der des Balles übereinstimmten, übrig.

Wenn man dies einmal auf das Bild anwenden würde, um die gelben Bereiche zu suchen, und einmal um die grünen Bereiche zu finden und dann sich beide Bilder anschaut, kann man vllt den Bereich der Gelbauswertung begrenzen.

Edit: Industrieroboter müssen aber nicht mit Bilderkennung arbeiten, da wird wahrscheinlich eher auf Lichtschranken gesetzt.


----------



## ankmanu (29. Mrz 2010)

danke für die schnelle Antwort, das ist sogar eine sehr gute Idee, aber wie kann ich das mit Java realisieren??

Ich hab noch keine Idee, wie das gehen soll...

Gruß
manu


----------



## Marco13 (29. Mrz 2010)

OK, mal mögliche (grundsätzlich) andere Ansätze außen vor gelassen - wenn ich das richtig verstanden habe, willst/wolltest du ursprünglich sowas machen wie
- Finde 4 Punkte, jeweils irgendeinen Punkt in den grünen Bereichen
- Berechne den Schwerpunkt des eingeschlossenen Bereiches

Die Zwischenschritte (und z.B. an welcher Stelle das "gelb" denn nun eine Rolle spielt) müßtest du ggf. noch erläutern. Und vielleicht auch, welche Annahmen man über das Bild treffen kann...


----------



## ankmanu (30. Mrz 2010)

Also:

Auf dem Bild ist eine Fläche zu sehen, die an den vier Ecken grün gekennzeichnet ist.
Auf dieser Fläche ist irgendwo nun eine gelbe Tonne (also auf dem Bild ein gelber Kreis...).

Es soll die Position der Tonne (gelber Kreis) bestimmt werden, aber abhängig von der Fläche...

Da die vier grünen Ecken auf dem Bild größer als 1 Pixel sind, müssten eben alle Pixel von dem Eckpunkt zusammengefasst werden, sodass vier "Eckpixel" entstehen.

Nun soll in der Fläche (zwischen den 4 punkten)eben der gelbe Kreis gesucht werden und dessen Position gefunden werden.

Die Koordinaten des Kreises (bzw. des Kreisschwerpunktes) sollen schließlich ausgegeben werden.

Vielen Dank im Vorraus 

manu


----------



## gerdgerdgerd (30. Mrz 2010)

hilft dir das wenn du das colormodel der buffered image ausliest und entsprechend iterativ pixel für pixel die gelbwerte  suchst?

Getting the Color Model of an Image | Example Depot


----------



## Marco13 (30. Mrz 2010)

Ist der Hintergrund auch gelb? (Annahmen, Annahmen, Annahmen...)


----------



## ankmanu (30. Mrz 2010)

Also, noch einmal allgemein wie das Bild aussieht:

-Grundsätzlich schwarz(allerdings nicht durchgängig, es sind schwarze Platten, die allerdings Löcher haben).
-Der Großteil des Bildes ist mit einer Fläche bedeckt(diese ist zur Zeit auch schwarz, kann aber auch rot gemacht werden, wenn es einen Nutzen hat)(aber nicht gelb  , denn sonst würde man die gelbe Tonne/Kreis nicht erkennen)
-Die Ecken der Fläche sind grün gekennzeichnet (jeder Eckpunkt 1,5 x 1,5 cm grün)
-auf der Fläche steht die gelbe Tonne. Da die Webcam darüber befestigt ist, sieht Sie diese als gelben Kreis(Radius des Kreises = 1,5cm --> Durchmesser 3cm )

Ich weiß nicht ob die cm Angaben was bringen, allerdings hab ich sie mal angegeben, um so ein Verhältnis klarzumachen. Die Fläche ist ca. 1m x 25cm groß. Sie ist ganz von der Webcam sichtbar(die Webcam befindet sich ca. 1,5meter über der Fläche)

@gerdgerdgerd:
ich verstehe das mit dem Colormodel nicht ganz, kannst du mir das nochmal erklären?

Hauptsache ist, das die ganze Auswertung relativ schnell geht!(unter 20 sekunden wär nett )
Natürlich sollte Sie auch relativ genau sein ... (aber auf 1cm genau würde reichen.)

Viele Grüße
manu


----------



## gerdgerdgerd (30. Mrz 2010)

```
ColorModel model = image.getColorModel();

WritableRaster raster = image.getRaster();

for(int i=0; i<image.getWidth(); i++){
  for(int j=0; j<image.getHeight(); j++){
    Object dataElement= raster.getDataElements(i, j, null);
    int rgbValue = model.getRGB(dataElement); 
  }
}
```

theoretisch kannst du jetzt den erhaltenen rgb wert mit deinen gelbwerten vergleichen. besser wäre natürlich noch ein epsilon um einen farbbereich um den epsilon wert abzudecken, d.h. alle gelblichen farbe:
int gelb = 16776960;
hier kannst du die farbwerte genauer berechnen:
RGB Int Calculator


----------



## ankmanu (30. Mrz 2010)

danke, doch ich verstehe die ganze Konstruktion noch nicht.
Meinst du mit vergleichen sowas:

```
ColorModel model = image.getColorModel();
int rgbgelb = 16776960;
int zähler = 1;  
int sumx = 0;
int sumy = 0;
int xspunkt = 0;
int yspunkt = 0;
 
WritableRaster raster = image.getRaster();
 
for(int i=0; i<image.getWidth(); i++){
  for(int j=0; j<image.getHeight(); j++){
    Object dataElement= raster.getDataElements(i, j, null);
    int rgbValue = model.getRGB(dataElement); 
if(rgbValue <= rgbgelb+1000 && rgbValue >= rgbgelb-1000){
/**i und j wert speichern... */
zähler++;
sumx = sumx+x;
sumy = sumy+y;
}}}
int zählermain = zähler - 1;
//Berechnung des Schwerpunktes...
xspunkt = sumx/zählermain;
yspunkt = sumy/zählermain;
System.out.println("Der Schwerpunkt P hat folgende Koordinaten: P("+xspunkt+"/"+yspunkt+")\n");
```
Gruß
manu


----------



## Marco13 (30. Mrz 2010)

Deine Antwort wird sein: "So funktioniert das bei mir nicht, weil ... [hier irgendwelche Gründe, warum es nicht funktioniert, die auf Annahmen oder Randbedingungen zurückzuführen sind, die du nicht genannt hast]"

Jedenfalls dauert das bisher ein paar Millisekunden.


```
import java.util.*;
import java.util.List;
import java.awt.*;
import java.awt.image.*;
import javax.swing.*;
import javax.imageio.*;
import java.io.*;


class CircleFinder
{
    public static void main(String args[]) throws Exception
    {
        BufferedImage image = ImageIO.read(new File("circleImage.png"));
        findCircle(image);
    }

    private static void findCircle(BufferedImage image)
    {
        float cornerHue = 0.33f;
        float circleHue = 0.16f;
        float threshold = 0.05f;

        int w = image.getWidth();
        int h = image.getHeight();

        BufferedImage cornersImage = new BufferedImage(w,h,BufferedImage.TYPE_INT_ARGB);
        findAreas(image, cornerHue, threshold, cornersImage);

        List<Point> cornerCenters = computeAreaCenters(cornersImage);
        System.out.println("Corner centers "+cornerCenters);



        BufferedImage circleImage = new BufferedImage(w,h,BufferedImage.TYPE_INT_ARGB);
        findAreas(image, circleHue, threshold, circleImage);
        List<Point> circleCenters = computeAreaCenters(circleImage);
        System.out.println("Circle centers "+circleCenters);


        JFrame f = null;
        f = new JFrame("Corners");
        f.getContentPane().add(new JLabel(new ImageIcon(cornersImage)));
        f.pack();
        f.setVisible(true);

        f = new JFrame("Circle");
        f.getContentPane().add(new JLabel(new ImageIcon(circleImage)));
        f.pack();
        f.setVisible(true);


    }

    private static void findAreas(BufferedImage image, float hue, float threshold, BufferedImage resultImage)
    {
        float hsb[] = new float[3];
        int w = image.getWidth();
        int h = image.getHeight();
        for (int x=0; x<w; x++)
        {
            for (int y=0; y<h; y++)
            {
                int rgb = image.getRGB(x,y);
                int r = (rgb >> 16) & 0xFF;
                int g = (rgb >>  8) & 0xFF;
                int b = (rgb >>  0) & 0xFF;
                hsb = Color.RGBtoHSB(r,g,b,hsb);

                if (Math.abs(hsb[0]-hue) < threshold)
                {
                    resultImage.setRGB(x,y,Color.WHITE.getRGB());
                }
                else
                {
                    resultImage.setRGB(x,y,Color.BLACK.getRGB());
                }
            }
        }
    }

    private static List<Point> computeAreaCenters(BufferedImage image)
    {
        List<Set<Point>> areas = new ArrayList<Set<Point>>();
        int w = image.getWidth();
        int h = image.getHeight();
        for (int x=0; x<w; x++)
        {
            for (int y=0; y<h; y++)
            {
                Point p = new Point(x,y);
                int rgb = image.getRGB(x,y);
                //System.out.println("rgb at "+x+" "+y+" is "+rgb);

                if (rgb == Color.WHITE.getRGB())
                {
                    Set<Point> currentFilled = new HashSet<Point>();
                    floodFill(image, x, y, currentFilled);
                    //System.out.println("floodFill at "+x+" "+y+" brought "+currentFilled);
                    areas.add(currentFilled);
                }
            }
        }

        List<Point> centers = new ArrayList<Point>();
        for (Set<Point> area : areas)
        {
            centers.add(computeCenter(area));
        }
        return centers;
    }

    private static Point computeCenter(Set<Point> points)
    {
        long cx = 0;
        long cy = 0;
        for (Point p : points)
        {
            cx += p.x;
            cy += p.y;
        }
        int x = (int)(cx/points.size());
        int y = (int)(cy/points.size());
        return new Point(x, y);
    }



    private static void floodFill(BufferedImage image, int x, int y, Set<Point> currentFilled)
    {
        List<Point> stack = new ArrayList<Point>();
        Point point = new Point(x,y);
        stack.add(point);

        int w = image.getWidth();
        int h = image.getHeight();
        while(stack.size() != 0)
        {
            Point p = stack.get(stack.size()-1);
            stack.remove(stack.size()-1);

            x = p.x;
            y = p.y;
            int rgb = image.getRGB(x, y);

            if (rgb == Color.WHITE.getRGB())
            {
                //System.out.println("rgb at "+p+" is "+rgb+" stack "+stack);

                currentFilled.add(p);
                image.setRGB(p.x, p.y, Color.BLUE.getRGB());

                Point p0 = new Point(x-1,y);
                Point p1 = new Point(x,  y-1);
                Point p2 = new Point(x+1,y);
                Point p3 = new Point(x,  y+1);
                Point p4 = new Point(x-1,y-1);
                Point p5 = new Point(x+1,y-1);
                Point p6 = new Point(x-1,y+1);
                Point p7 = new Point(x+1,y+1);

                if (x>0            && !currentFilled.contains(p0)) stack.add(p0);
                if (y>0            && !currentFilled.contains(p1)) stack.add(p1);
                if (x<w-1          && !currentFilled.contains(p2)) stack.add(p2);
                if (y<h-1          && !currentFilled.contains(p3)) stack.add(p3);
                if (x>0   && y>0   && !currentFilled.contains(p4)) stack.add(p4);
                if (x<w-1 && y>0   && !currentFilled.contains(p5)) stack.add(p5);
                if (x>0   && y<h-1 && !currentFilled.contains(p6)) stack.add(p6);
                if (x<w-1 && y<h-1 && !currentFilled.contains(p7)) stack.add(p7);
            }
        }
    }

}
```


----------



## Gast2 (30. Mrz 2010)

die Lösung hast Du hier schon erhalten ... http://www.java-forum.org/java-basics-anfaenger-themen/94954-viereck-erstellen-webcamtracking.html


----------



## Marco13 (30. Mrz 2010)

Nein, dort nur Lösungs_hinweise_ (bei denen man noch was hätte selbst machen müssen  )


----------



## Gast2 (30. Mrz 2010)

Marco13 hat gesagt.:


> Nein, dort nur Lösungs_hinweise_


wwenn von OP-Seite mögliche Implementierungen gekommen wären, hätte man ihm weiter helfen können ... aber so hat er seine Hausaufgaben gemacht bekommen ... wie man aber Programmieren lernt weist Du


----------



## ankmanu (30. Mrz 2010)

dank eurer Hilfe klappt es jetzt höchstwahrscheinlich 

(ich weiß das ich faul bin...)

Gruß
manu


----------



## ankmanu (5. Apr 2010)

Sorry, das ich das Thema noch mal aufgemacht hab, aber ich bin noch ein blutiger Java-Anfänger und verstehe vieles noch nicht.

Ich verstehe noch nicht mals mehr das obige Programm komplett... 
Kann man die float Zahlen (z.B. 0.33f) auch als rgb angeben(z.B: r = 255, g = 255, b = 70)??
Und was ist bei dir eigentlich das threshold (die schwelle)??

Kannst du mir das bitte noch einmal erklären?

Ich weiß desweiteren nicht, wie ich das anstellen soll:
es soll nur EINE Mitte des Gegenstands geben, in dem bisherigen Programm kommen jetzt oft mehrere Punkte raus 
Kann man das irgendwie zusammenfassen? (also alle gelben Punkte >> einem Schwerpunkt) (Ich glaube das ist bei dir auch drin, aber das klappt noch nicht so richtig.. )

Und der gelbe Punkt soll dann als einzelner Wert gespeichert werden, bisher kommt ja sowas raus : "java.awt.point[x=123, y=123]"
Geht das auch anders??(Das da steht: "x=123, y=123")

Und der x- und y-wert des gegenstands (des gelben oder circles) soll in abhängigheit der grünen angegeben werden!
(man muss also folgendes machen: x_gegenstand_echt = x_gegenstand_bisher - linker_Eckpunkt der Fläche)
Beim y-Wert genauso...

Sorry für die vielen(wahrscheinlich sehr einfach zu beantwortenden und idiotischen) Fragen.

Viele Grüße und schon mal vielen Dank
manu


----------



## Marco13 (5. Apr 2010)

_Ich verstehe noch nicht mals mehr das obige Programm komplett... _
Das Risiko besteht immer, bei Programmen, die man nicht selbst geschrieben hat. Und manchmal sogar bei eigenen 

_Kann man die float Zahlen (z.B. 0.33f) auch als rgb angeben(z.B: r = 255, g = 255, b = 70)??
Und was ist bei dir eigentlich das threshold (die schwelle)??

Kannst du mir das bitte noch einmal erklären?
_

Das zeigt zumindest schonmal, dass du den Link von ice-breaker nicht berücksichtigt hast. Diese Zahlen sind der/die/das "Hue" - der Farbton. Wenn man sich mal ein Bild von einer Kugel ansieht 





dann sieht man, dass die an der glänzenden Stelle "fast weiß" (zumindest sehr hell) ist, und in anderen bereichen praktisch schwarz. Man kommt mit RGB-Farben nicht weit, wenn
Diese Farbe und
Diese Farbe und
Diese Farbe und
Diese Farbe und
Diese Farbe und
Diese Farbe und
Diese Farbe und
alle als "grün" (und damit Teil der grünen Kugel) angesehen werden sollen. In RGB unterscheiden sich diese Farben teilweise dramatisch. In HSV haben sie alle ungefähr(!) ein Hue von 0.33 - vielleicht ein bißchen mehr, vielleicht ein bißchen weniger. Dieses "mehr oder weniger" ist genau der Threshold  Also: Wenn eine Farbe einen Hue von 0.33 (+/- 0.05) hat, dann ist sie "Grünlich". 


Meine Kristallkugel ist zwar gut genug, dass ich vorher schon sagen konnte, dass du sagen wirst, dass das Programm bei dir nicht funktioniert, aber nicht so gut, dass ich genau wüßte, was du eigentlich willst (abgesehen von einer Lösung, mit wenig Arbeit). Du hast dein Problem nicht genau beschrieben, und selbst wenn du das getan hättest (also die Aufgabenstellung und Beispielbilder hier gepostet hättest) könntest du nicht erwarten, dass jemand die perfekte Universallösung hinschreibt. 

Das Programm gibt die Punkte raus, die es als mögliche Mittelpunkte der grünen "Ecken" findet (Corner Centers), und ggf. einen Punkt, der die Mitte des gelben Gegenstandes sein könnte (Circle Center). Das Programm sollte nur zeigen, dass man, wenn man ein bißchen mit HSV und Binärbildern rumrechnet, drumrumkommt irgendwelche ArrayLists mit Color-Objekten anzulegen, in denen man dann mit irgendwelchen Radien und RGB-Vergleichen Positionen rät. Das Programm war nur in der Mittagspause schnell hingeschrieben, und sicher alles andere als "gut". Du kannst es als Basis für eigene Lösungen verwenden, dich davon "inspirieren" lassen, oder weiter deine eigenen Ansätze verfolgen - das steht dir alles frei (einschließlich der Option, Code, Beispielbilder und Rahmenbedingungen zu posten, und präzsiere Fragen zu stellen...)

Wenn du nicht weißt (und keine Möglichkeit siehst, herauszufinden) wie man einen Point als "x=12, y=23" ausgeben kann, oder wie man zwei Punkte voneinander abzieht, solltest du weiter unten anfangen.
System.out.println("x="+point.x+", y="+point.y);
Point difference = new Point(p0.x-p1.x, p0.y-p1.y);


----------



## ankmanu (5. Apr 2010)

Ok, danke, ich werde versuchen mich selbst durchzuschlagen(allerdings fehlen mir eben sehr viele Kenntnisse...)

Noch mal ne Erklärung:
Das Programm greift auf ne Webcam zu und macht ein Bild(das klappt schon)
Als Beispiel folgendes Bild(ist keins von der Webcam, allerdings wird es da anschaulich):



 

Nun soll das Programm die vier grünen Eckflächen erkennen und diese dann zu vier Eckpunkten zusammenfassen.
(Es werden zum Beispiel alle grünen Punkt in ein Array gespeichert und dann alle gelöscht, die eine den x und y wert (+- 40) des ersten erkannten haben.) Dann bleiben 4 Punkte übrig.

Danach wird wieder das Bild durchsucht, diesmal nach gelben Punkten. Alle gelben Punkte werden nun auch wieder zu einem gelben "Schwerpunkt" zusammengefasst. (z.B. xschwerpunkt= 123, yschwerpunkt= 123; )
Nun muss der schwerpunkt abhängig von den grünen Eckpunkten berechnet werden(also 
	
	
	
	





```
x_gelb_echt = xschwerpunkt - x_linker_grüner_eckpunkt;
y_gelb_echt = yschwerpunkt - y_linker_grüner_eckpunkt;
```

Dieser Punkt soll dann ausgegeben werden. (So wie in dem Beispielprogramm ist das natürlich schön, dass ein Bild erzeugt wird, wo man die erkannte Fläche sieht)

_________________________________________________
Ich weiß nur leider nicht wo ich anfangen soll, denn das obige Programm ist für mich so unübersichtlich(das heißt nur das ich es nicht verstehe) und ich weiß nicht wie und wo ich es verändern muss 

Wäre schön wenn mir nochmal jemand eine Anregung geben könnte

Viele Grüße
manu


----------



## Marco13 (5. Apr 2010)

Falls das "schwer Verständlich" sich auf das gepostete bezog, ein paar Kommentare

```
// Das "hue" gibt den Farbton vor (z.B. 0.33 für grün oder 0.15 für gelb). Der Threshold
    // besagt, wie stark der Farbton im Bild vom gegebenen abweichen darf, um noch als
    // dazugehörig erkannt zu werden. Das 'image' ist das Eingabebild. In das 'resultImage'
    // werden mit WHITE die Pixel gemalt, die zu den jeweiligen Farbbereichen gehören
    private static void findAreas(BufferedImage image, float hue, float threshold, BufferedImage resultImage)

    // Berechnet die Schwerpunkte aller WEISSEN Bereiche im gegebenen Bild
    private static List<Point> computeAreaCenters(BufferedImage image)

    // Berechnet den Schwerpunkt einer gegebenen Menge von Punkten
    private static Point computeCenter(Set<Point> points)

    // Füllt im gegebenen Bild einen WEISSEN Bereich, angefangen bei x,y. 
    // Der Bereich wird mit blauen Pixeln gefüllt, und die Menge aller
    // Pixel, die vorher weiß waren, werden in 'currentFilled' gespeichert
    private static void floodFill(BufferedImage image, int x, int y, Set<Point> currentFilled)
```

Das sind die "Bausteine", die man braucht, um auf EINE (SEHR einfache!) Art ungefähr das zu machen, was du angeduetet hast. Im Eingabebild werden erst alle grünen Bereiche gefunden, und dann die Schwerpunkte dieser Bereiche berechnet. Das gleiche dann mit dem gelben Bereich. Und wie man diese Punkte jetzt miteinander verrechnen soll, musst du wissen. 

Was kommt denn raus, wenn du das Programm auf das gepostete Bild losläßt?


----------



## ankmanu (6. Apr 2010)

Hi, danke schon mal für deine erklärungen 
So wirds mir schon viel verständlicher

Das Programm gibt dann folgendes aus:


 


Ich will z.B. noch folgendes einbauen:
-die grünen(und gelben) Flächen sollen nur ausgegeben werden, wenn sie größer als z.B.20x20 Pixel ist. (Damit eben nicht so kleine "Fitzelchen" als Fläche erkannt werden)

Wo muss das dann in den Code??
Bei 
	
	
	
	





```
List<Point> centers = new ArrayList<Point>();
        for (Set<Point> area : areas)
        {
            /** Hier?? */
            centers.add(computeCenter(area));
        }
        return centers;
```
Und wenn ja, wie? Mit
	
	
	
	





```
if(area.getlength() >= 20 && area.getWidth() >= 20){centers.add(computeCenter(area));}
```
??
Viele Grüße
manu


----------



## Marco13 (6. Apr 2010)

Hast du, um diese "Fitzelchen" wegzukriegen, mal versucht, den "Threshold" kleiner zu machen? (Könnte helfen, muss es aber nicht...). Man könnte ja jetzt noch ein k-Means oder so drauf loslassen, aber das wäre eben aufwändiger. Die Erfahrung zeigt, dass es für jedes Problem eine Lösung gibt, die einfach, elegant ... und falsch ist. Oder anders formuliert: Wenn man für ein kompliziertes Problem (Objekterkennung) glaubt, eine einfache Lösung (Pixelzählen) gefunden zu haben, ist es nur eine Frage der Zeit (und der Rahmendbedingungen) bis eine Probleminstanz auftritt, die mit dem einfachen Lösungsansatz nicht funktioniert. 

Wie sehen diese "Fitzelchen" denn im Originalbild aus?


----------



## ankmanu (6. Apr 2010)

naja die "fitzelchen " siehst du ja auf dem obigen Bild... 
Eigentlich sind es einfach 4 grüne flächen(mit paint gemalt) und trotzdem findet das Programm daneben noch andere "grüne " flächen.

Mit kleinerem threshold wird es etwas weniger, aber nicht wirklich "gut".
Man müsste eben eine Bedingung einbauen, dass die Koordinaten nur verwendet werden, wenn die erkannte Fläche z.B. größer als 20x20 pixel ist.
Aber wie geht das??

Viele Grüße
manu


----------



## Marco13 (6. Apr 2010)

Aus dem Bild, das du weiter oben gepostet hast, kann eigentlich nicht das entstehen, was du weiter darunter gepostet hast. Das sieht wenn überhaupt dann nach übertrieben starker JPG-Kompression aus, aber wenn du nicht weißt, wo die "Fitzelchen" herkommen, weißt du auch nicht, ob du eine Größenbeschränkung von 20x20 oder 21x21 verwenden must. Trotzdem steht dir das frei: Diese Liste mit den "currentFilled" Pixeln beschreibt eine Zusammenhängende Fläche, deren Größe man ausrechnen kann.

```
private boolean isLargerThan(List<Point> area, int sizeX, sizeY)
{
    // Trivial
}
```


----------



## ankmanu (6. Apr 2010)

Das stimmt, das Bild zum letzten Post war dieses:


 
Allerdings wundere ich mich, waum neben den grünen und gelben Flächen des Bildes so "Fitzelchen" entstehen, denn dort ist das Bild doch eindeutig schwarz (oder ist das wegen dem float??)

Entschuldigung das ich dauernd frage, aber wie kann ich denn verwirklichen, dass wirklich NUR 4 grüne und NUR 1 gelbe Flächen gefunden werden, damit es am Ende wirklich NUR 4 grüne Schwerpunkte und NUR 1 gelben Schwerpunkt gibt??

Muss man das mit nem Array machen, also so:

```
int[] greencorners = new int[3];   //Array der WIRKLICHEN 4 Eckpunkte 
// In dem Array allppoints sind alle gefundenen grünen Punkte gespeichert
...
int counter = 0;
int i = 0;
for(i=0; i<=allpoints.getLengh(); i++){
//Wenn der aktuelle Punkt der erste gefundene Punkt +- 40 ist, dann zum array hinzufügen
if(allpoints[i] > allpoints[1] - 40 || allpoints[i] < allpoints[1] + 40) {
greencorners[counter] = allppoints[i];
counter++;
}}
```
Allerdings stimmt das mit dem allpoints[1] ja nicht, da der erste gefundene grüne Punkt ja nicht unbedingt der von einem Eckpunkt sein muss. Und außerdem gibt es ja 4 Eckpunkte!

Aber wie kann ich das (wenn das überhaupt ansatzweise stimmt) zum Richtigen verändern?
Und wo müsste ich diesen Code dann einfügen?

Viele Grüße
manu


----------



## Marco13 (6. Apr 2010)

> Allerdings wundere ich mich, waum neben den grünen und gelben Flächen des Bildes so "Fitzelchen" entstehen, denn dort ist das Bild doch eindeutig schwarz (oder ist das wegen dem float??)



Im Anhang siehst du das, was rauskommt, wenn man bei dem Bild, das du gepostet hast, mit GIMP die Helligkeit und den Kontrast auf's Maximum stellt. Wie ich schon sagte
_Das sieht wenn überhaupt dann nach übertrieben starker JPG-Kompression aus_

Ob die nun "übertrieben" ist oder nicht, sei mal dahingestellt. Jedenfalls wäre ein erster Ansatz, das Kriterium bei "findAreas" entsprechend anzupassen. Vorher war das

```
if (Math.abs(hsb[0]-hue) < threshold)
```
Dort könnte man stattdessen sowas machen wie

```
if (Math.abs(hsb[0]-hue) < threshold && hsb[1] > 0.5f && hsb[2] > 0.25f)
```
Das Programm sollte eine grundsätzliche Möglichkeit zeigen, den Prozess in Teilschritte zu zerlegen, die man getrennt anpassen kann. Es hängt von der Kamera und dem genauen Bild ab, wann ein Pixel denn nun "Grün" ist und wann nicht. Beim Testbild funktioniert das so jedenfalls. Ob es beim Webcambild funktioniert, weiß ich nicht. Ich weiß ja nicht, wie das aussieht *räusper*.





> Entschuldigung das ich dauernd frage, aber wie kann ich denn verwirklichen, dass wirklich NUR 4 grüne und NUR 1 gelbe Flächen gefunden werden, damit es am Ende wirklich NUR 4 grüne Schwerpunkte und NUR 1 gelben Schwerpunkt gibt??



Für Fragen ist so ein Forum da. Eine Möglichkeit, das (bei dem Testbild!) zu erreichen, steht oben. Falls ich genervt wirke, dann liegt das daran, dass du anscheinend zu wenig... involviert und engagiert bist, sowas selbst rauszufinden. 

Was du immer mit der Größe der Bereiche hast, leuchtet mit nicht ein. Wann wird aus einem "Fitzel" ein "Eckpunktsbereich"? Das einzige, was mir spontan einfallen würde, um dort einen Hauch von Robustheit reinzubringen, wäre ein k-Means-Clustering. Zum Glück weiß man in diesem Fall zumindest schon, dass k=4 bzw. k=1 sein müßte.


----------



## Guest2 (6. Apr 2010)

Moin,

um mich dem hier auch mal anzuschließen:



Marco13 hat gesagt.:


> Ob es beim Webcambild funktioniert, weiß ich nicht. Ich weiß ja nicht, wie das aussieht *räusper*



ankmanu, könntest Du nicht eines der original Bilder aus der Webcam posten? Oder besser noch, einen ganzen Satz an Beispielbildern? Du kannst den Algorithmus monatelang an synthetischen Bildern optimieren, aber sobald das erste reale Bild aus der Webcam kommt, wirst Du wieder ganz von vorne anfangen.

Computer Vision ist zwar nicht meine Hausdisziplin, aber ich hätte Lust zu spielen. 

Gruß,
Fancy


----------



## ankmanu (6. Apr 2010)

Ja, doch das dauert noch ein paar Tage (  )

Der Grund:
Ich habe eine Webcam, die auf meinem alten PC funktioniert hat, und mit der ich gearbeitet habe.
Doch jetzt habe ich einen neuen PC, der mit dem Betriebssystem Windows 7 ausgestattet ist. Leider war das eine Webcam für 10 € bei mediamarkt, und für diese ist kein Treiber für Windows 7 vorhanden 

Deshalb muss ich mir eine neue bestellen(hab ich noch nicht gemacht). 
(Empfehlungen werden gerne angenommen ...)

Viele Grüße
manu


----------



## agentone (7. Apr 2010)

Vielleicht solltest du dein mit Paint gemachtes Bild mal als Bitmap (.bmp) speichern.
Dann siehst du keine "Fitzelchen" mehr!



> dort ist das Bild doch eindeutig schwarz


Windows XP: 
- Geh auf Destop
- rechtsklick
- "Eigenschaften"
- "Einstellungen"
Unter "Farbqualität" steht da bei mir "32 Bit", das heißt:
8 Bit Rot, 8 Bit Grün, 8 Bit Blau, 8 Bit Alpha.
Und wenn man dann Alpha mal außen vor lässt, hast du dann genau 2^24=16.777.216 verschiedene Farben. Erzähl mir also nicht, du kannst RGB(0,0,0) von RGB(0,0,1) mit bloßem Auge unterscheiden.

Aber sorry, war nicht böße gemeint. 
Ich fand das Wort "eindeutig" nur unpassend gewählt.


----------



## Guest2 (7. Apr 2010)

Besser wäre es aber vermutlich sein synthetisches Bild weiter zu verschlechtern. Das was aus der Webcam rauskommt wird ja auch nie und nimmer schwarz sein, genauso wenig wie das grün grün und das gelb gelb sein wird. Außerdem noch Verzerrungen durch Linse, Licht und Schatten, Artefakte und andere Störobjekte.

Mit ein bissel Glück sieht der Spaß dann doch schon so ähnlich aus:






(Und das ist dann imho sogar noch fernab jeglichen worst cases )

Gruß,
Fancy


----------



## agentone (7. Apr 2010)

Also mit dem folgenden Programm und dem Bild vom letzten Post konnte ich das im Anhang angehängte Bild berechnen:


```
import javax.imageio.*;
import java.awt.*;
import java.awt.image.*;
import java.io.File;
import javax.swing.*;

public class ImageTest
{
  public static final String FILE="paint2.jpg";
  
  public static final int SCHWELLE=20;

  public static void main(String args[])
  {
    try
    {
      BufferedImage image=ImageIO.read(new File(FILE));

      int width=image.getWidth();
      int height=image.getHeight();
      
      BufferedImage output=new BufferedImage(width,height,BufferedImage.TYPE_INT_RGB);
      Graphics g=output.getGraphics();
      g.setColor(Color.WHITE);
      g.fillRect(0,0,width,height);
      g.setColor(Color.BLACK);
      
      for(int x=0; x<width; x++)
      {
        for(int y=0; y<height; y++)
        {
          Color c=new Color(image.getRGB(x,y));

          for(int i=x-1; i<=x+1; i++)
          {
            for(int j=y-1; j<=y+1; j++)
            {
              if(!(i==0 && j==0))
              {
                Color n=getColor(image,i,j);
                if(n!=null)
                {
                  if(difference(c,n)>=SCHWELLE)
                  {
                    g.drawRect(x,y,1,1);
                  }
                }
              }
            }
          }
        }
      }
      
      JFrame frame=new JFrame("Image Output");
      JLabel label=new JLabel(new ImageIcon(output));
      frame.getContentPane().add(label);
      frame.pack();
      frame.setVisible(true);
      
      ImageIO.write(output,"jpg",new File("output.jpg"));
      
      System.out.println("fertig");
    }
    catch(Exception e)
    {
      System.out.println(e);
    }
  }
  
  public static Color getColor(BufferedImage image, int x, int y)
  {
    if(x>0 && x<image.getWidth() && y>0 && y<image.getHeight())
    {
      return new Color(image.getRGB(x,y));
    }
    else
    {
      return null;
    }
  }
  
  public static int difference(Color ca, Color cb)
  {
    int r=Math.abs(ca.getRed()-cb.getRed());
    int g=Math.abs(ca.getGreen()-cb.getGreen());
    int b=Math.abs(ca.getBlue()-cb.getBlue());
    return (r+g+b)/3;
  }
}
```

Zugegeben, das Programm hat nicht wirklich was mit dem Thread hier zu tun, bringt aber auch für die anderen Bilder in diesem Thread ganz vernünftige Ergebnisse.
Es berechnet nämlich eigentlich die "markanten" Ränder von Objekten in Bildern.
Einfach FILE anpassen und ein bisschen an der SCHWELLE rumdrehen, dann kann z. B. auch das 2. Bild im Anhang entstehen, welches im Original auch in diesem Thread irgendwo ist.


----------



## Marco13 (7. Apr 2010)

Wie schon angedeutet: Diese SCHWELLE und der RGB-Vergleich sind ziemlich heikel. Angenommen SCHWELLE ist 20, dann würden die Farben im Bild im Anhang als "gleich" (zur ersten) angesehen werden, obwohl einige davon "eindeutig rötlich" und andere "eindeutig grünlich" (und eine GANZ eindeutig grau  ) sind...

Für verrauschte Bilder könnte man sich dann sowieso noch überlegen, ob man vorher einen Weichzeichner drüberlaufen läßt, aber das wäre ja im Idealfall nur ein weiterer Schritt in der (ansonsten gleichen) Verarbeitungspipeline....


----------



## ankmanu (7. Apr 2010)

Das Programm von agentone sieht ja auch schon super aus. (Hätt ich nie hingekriegt  )
Mal sehn ob ich da was draus machen kann( schwerpunkt berechnung hinzu, ...).

Ich versuch euch so bald wie möglich ein paar "echte" Webcambilder zu posten, dauert allerdings leider noch ein paar Tage.

Und hat jemand eine Idee wie man bei agentones Programm zwischen den rechteckigen Flächen und dem Kreis unterscheiden kann?

Viele Grüße und und vielen Dank dass ihr mir so super helft 

manu


----------



## Janus (7. Apr 2010)

Zur Unterscheidung von Rechtecken und Kreisen ist Kompaktheit ein recht gutes Maß, wobei Kompaktheit hier als Verhältnis von Fläche zu Umfang definiert ist. Je kompakter, desto Kreis.


----------



## ankmanu (9. Apr 2010)

Hi @ all
So: 
Bilder sind jetzt da (eine ganze Bildserie, mit unterschiedlichen Positionen des Gegenstandes und unterschiedlicher Belichtung)





































Allerdings ist die Qualität der Bilder meines Erachtens sehr schlecht 
Außerdem ist der Gegenstand gelb gekennzeichnet, aber die Holzplatte auf der die ganze Fläche befestigt ist hat fast die selbe Farbe, kann man das dann noch gut unterscheiden?

Oder sollte ich den Gegenstand lieber blau oder auch grün markieren?

Viele Grüße
manu


----------



## Gast2 (9. Apr 2010)

*muhaha*

das Semester hast Du wohl in den Sand gesetzt ... da sind ja noch nicht mal die Lichtverhältnisse konstant ... das siehst Du schön wie sich die Farbe des Vierecks in den letzten beiden Bilder *deutlich* von den anderen unterscheidet



ankmanu hat gesagt.:


> ich weiß das ich faul bin...



dann wird es Zeit das Du das änderst

*Nachtrag*



ankmanu hat gesagt.:


> Allerdings ist die Qualität der Bilder meines Erachtens sehr schlecht


nein - eher real (wenn man mal von der JPG-Kompression absieht) ... mit solchen Bilder darf ich arbeiten



ankmanu hat gesagt.:


> Außerdem ist der Gegenstand gelb gekennzeichnet, aber die Holzplatte auf der die ganze Fläche befestigt ist hat fast die selbe Farbe, kann man das dann noch gut unterscheiden?


irgendwie sieht die Tischplatte auf dem Bild deutlich dunkler aus



ankmanu hat gesagt.:


> Oder sollte ich den Gegenstand lieber blau oder auch grün markieren?


wozu ?! ... solange Du alles mit den Augen trennen kannst, kannst Du es auch im Bild trennen ... wenn Du den Punkt auch grün machst wie die Vierecke wirst Du sie im Programm schwerer unterscheiden können


----------



## noobadix (9. Apr 2010)

Hallo,

wenn ich mich da mal einmischen darf, ob meiner völligen Erfahrungslosigkeit auf dem Gebiet und diesem Problem im Besonderen. Hab da Ideen:

1. Farbfolie vor die Kamera setzen, eine neongelbe beispielsweise.
2. Neonfarbe Gelb für die Tonne verwenden
3. Kontraste der Bilder hoch-/runterfahren
4. Mit geringerer Auflösung aufnehmen
5. LED-Belichtung benutzen und Reflektionen (also Lacke etc.) minimieren

Könnte/Darf das helfen?


----------



## ankmanu (9. Apr 2010)

Das ist sogar eine sehr gute Idee !!

Einziges Problem: Es kann nur eine Farbe "erkannt" werden, dass heißt z.B. nur neongelb für den Gegenstand.
Allerdings müssen die Eckpunkte ja auch noch erkannt werden, da die Koordinaten des Gegenstands in Abhängigkeit der Fläche zwischen den 4 Eckpunkten angegeben werden soll.



> > Zitat: ankmanu
> >
> > Beitrag anzeigen
> >
> ...


naja es gibt mehrere schwarze Platten(da sind kleine Löcher drin). Aber diese sind auf einer Holzplatte montiert. Zwischen den schwarzen Platten ist ein kleiner Abstand, und da die Holzplatte auch "fast gelb" ist, sieht sie der Farbe des Gegenstands sehr ähnlich.

Viele Grüße
manu


----------



## noobadix (9. Apr 2010)

Wenn die Kamera fixiert ist, wozu brauchst du die Eckpunkte überhaupt? Ansonsten mach doch die Eckdinger auch gelb, dann brauchste wie weiter oben schon gesagt nur noch die Formen unterscheiden.


----------



## ankmanu (9. Apr 2010)

Also die Kamera ist zwar fixiert, allerdings soll das ganze Konstrukt einmal mitgenommen werden und dazu muss ich die Kamera wieder abmontieren.
Ich gehe davon aus, dass die Kamera beim Wiederanbringen nicht 100-%ig am selben Ort über der Fläche ist (also Pixel 1/1 nicht wieder 1/1)und deshalb wäre das mit den Eckpunkten sinnvoll. Diese müssten dann ja nur alle auf dem Bild zu sehen sein und schon wird der richtige Wert ausgegeben. 

Aber vielleicht ist es wirklich einfacher nur nach gelben Flächen zu suchen. Wäre jedenfalls super wenn das erstmal klappen würde. 

Viele Grüße
manu


----------



## ankmanu (9. Apr 2010)

nochmal als nachtrag:

Wäre es einfacher, ein solches invertiertes/neoneffekt Bild auszuwerten:







Logischerweise nicht oder?
(nur mal als versuch)

Viele Grüße
manu


----------



## noobadix (9. Apr 2010)

Die Farbe der relevanten Bereiche muss auf dem Bild einmalig sein, am besten so, dass du beim Iterieren über die Pixel nur eine if-Abfrage pro Durchlauf hast, so sparst du Zeit. Die if-Prüfung, die du anfangs gepostet hast, ist ja ziemlich zeitintensiv.


----------



## Marco13 (9. Apr 2010)

@mogel: Du Motivationskünstler! 

@Topic: Aber ja, wie schon gesagt wurde: Solange man nichts über das Bild weiß, und die Lichtverhältnisse so unterschiedlich sein können usw. wird das schwierig. Das gelbe Objekt liegt manchmal auf diesen Gitterlinien, und die haben (im Bild) praktisch die gleiche Farbe wie das Objekt. (Weiß - bei zu hoher Belichtung  ).

Ich wollte hier im Forum ja schon immer mal ein Spiel veröffentlichen. Hier ist also mein Spiel. Es trägt den Titel "Bilderkennungsschwellwertsuche":

```
import java.util.*;
import java.util.List;
import java.awt.*;
import java.awt.image.*;
import javax.swing.*;
import javax.swing.event.*;
import javax.imageio.*;
import java.io.*;


class CircleFinder extends JFrame
{
    public static void main(String args[]) throws Exception
    {
        SwingUtilities.invokeLater(new Runnable()
        {
            public void run()
            {
                try
                {
                    BufferedImage image = ImageIO.read(new File("circleImage04.jpg"));
                    new CircleFinder(image).setVisible(true);
                }
                catch (Exception e)
                {
                    e.printStackTrace();
                }
            }
        });
    }

    private BufferedImage image;
    private BufferedImage resultImage;
    private float hsbMin[] = new float[]{ 0.0f, 0.0f, 0.0f };
    private float hsbMax[] = new float[]{ 1.0f, 1.0f, 1.0f };

    public CircleFinder(BufferedImage image)
    {
        this.image = image;
        int w = image.getWidth();
        int h = image.getHeight();

        resultImage = new BufferedImage(w,h,BufferedImage.TYPE_INT_ARGB);

        getContentPane().setLayout(new BorderLayout());
        getContentPane().add(new JLabel(new ImageIcon(resultImage)), BorderLayout.CENTER);

        JPanel p = new JPanel(new GridLayout(0,2));

        p.add(new JLabel("Hue"));
        p.add(new JLabel(""));
        p.add(new JLabel("min"));
        p.add(createSlider(hsbMin, 0, 0));
        p.add(new JLabel("max"));
        p.add(createSlider(hsbMax, 0, 255));

        p.add(new JLabel("Sat"));
        p.add(new JLabel(""));
        p.add(new JLabel("min"));
        p.add(createSlider(hsbMin, 1, 0));
        p.add(new JLabel("max"));
        p.add(createSlider(hsbMax, 1, 255));

        p.add(new JLabel("Br"));
        p.add(new JLabel(""));
        p.add(new JLabel("min"));
        p.add(createSlider(hsbMin, 2, 0));
        p.add(new JLabel("max"));
        p.add(createSlider(hsbMax, 2, 255));

        getContentPane().add(p, BorderLayout.EAST);

        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        pack();
    }

    private JSlider createSlider(final float array[], final int index, int value)
    {
        final JSlider s = new JSlider(0,255,value);
        s.addChangeListener(new ChangeListener()
        {
            public void stateChanged(ChangeEvent e)
            {
                array[index] = s.getValue() / 255.0f;
                System.out.println("min "+Arrays.toString(hsbMin));
                System.out.println("max "+Arrays.toString(hsbMax));
                findAreas();
                repaint();
            }
        });
        return s;
    }



    private void findAreas()
    {
        float hsb[] = new float[3];
        int w = image.getWidth();
        int h = image.getHeight();
        for (int x=0; x<w; x++)
        {
            for (int y=0; y<h; y++)
            {
                int rgb = image.getRGB(x,y);
                int r = (rgb >> 16) & 0xFF;
                int g = (rgb >>  8) & 0xFF;
                int b = (rgb >>  0) & 0xFF;
                hsb = Color.RGBtoHSB(r,g,b,hsb);

                if (hsb[0]>hsbMin[0] && hsb[0]<hsbMax[0] &&
                    hsb[1]>hsbMin[1] && hsb[1]<hsbMax[1] &&
                    hsb[2]>hsbMin[2] && hsb[2]<hsbMax[2]
                    )
                {
                    resultImage.setRGB(x,y,Color.WHITE.getRGB());
                }
                else
                {
                    resultImage.setRGB(x,y,Color.BLACK.getRGB());
                }
            }
        }
    }

}
```
(Das kannst du dir auch gerne nach "RGB" umschreiben, und schauen, ob du damit weiterkommst )

Mal im ernst: Bei einigen Bildern kann man die Slider so einstellen, dass man die Eck-Ecke erkennen kann, aber mit den gleichen Einstellungen kriegt man bei einem der überbelichteten Bilder halt nur eine Weiße Fäche :autsch:

Zusammenfassend würde ich sagen, dass man in diese Aufgabe in dieser Form RICHTIG (RICHTIG) viel Arbeit stecken könnte. Nichts für jemanden der (im unproduktiven Sinne) faul ist  Ich bin kein Bildverarbeitungsexperte, aber bezweifle, dass man diese Aufgabe rein Pixelbasiert (d.h. ohne Kantenerkennung, Hough-Tansform & Co) überhaupt lösen kann. Vielleicht würde man mit genauerer Kameraklibrierung, weniger veränderlichen Lichtverhältnissen, deutlichen Farben, weniger Störungen (Glanzeffekte usw), und mit geschickter Vorverarbeitung (Histogrammabgleich oder was es da nicht alles gibt) irgendwie auf einen grünen Zweig kommen, aber... sicher nicht, indem man schaut ob in irgendeiner Arraylist erst 19 oder schon 20 Pixel liegen...
(So viel zum Thema "Motivationskünstler"  aber ist nur meine subjektiv-unfundierte Einschätzung...)


----------



## ankmanu (9. Apr 2010)

Ja das stimmt, die gepostete if-abfrage(von mir) war sehr zeitintensiv  (bis zu 30 sekunden...)

Ich hab die Fläche jetzt komplett schwarz, das heißt es ist wirklich grün und gelb zu unterscheiden.
Bilder kann ich grade leider nicht posten (morgen müsst ichs schaffen)

Wenn noch jemand Ideen hat: Bin froh wenn ihr sie postet 

Viele Grüße
manu


----------



## Marco13 (9. Apr 2010)

Eine komplett einfarbige (und vielleicht auch weniger glänzende) Fläche wäre schonmal was. Wenn das Objekt selbst dann noch eine "eindeutigere" ("grellere") Farbe hätte (sowas wie dieses Verkehrshütchen-Neonorange oder so...), könnte es vielleicht sogar reichen nur nach dieser spezifischen Farbe zu suchen... Evtl. braucht man dann nichtmal die Ecken und die ganzen anderen aufwändigen Sachen...


----------



## noobadix (9. Apr 2010)

Genial wäre es, wenn die Farbe der relev. Bereiche die einzige im Bild wäre, deren r,g oder b wert höher als 250 oder so ist, kenne mich da im Speziellen nicht so aus, denn dann bräucht's pro Durchlauf nur noch n kompaktes if( farbe>wert). Die immergleichen Lichtverhältnisse ließen sich doch erreichen, wenn das Ding in nem Kasten wär und mit LED's beleuchtet würde, irgendwo hab ich mal aufgeschnappt, dass LED's die wahre Farben verraten. Evtl. sollte noch irgendwas streuendes, Milchglas z.B., vor die LED's gesetzt werden.


----------



## ankmanu (9. Apr 2010)

jo dann versuche ich eben alles mit farben auszufüllen, bis auf den Gegenstand, den markiere ich dann weiß (r = 255, g= 255, b=255).

Morgen post ich dann des Bild....

Gruß
manu


----------



## ankmanu (10. Apr 2010)

So, Bilder sind jetzt da:


 


 


 

Dieses Bild ist absichtlich mit überhöhter Belichtung aufgenommen: (als Test, aber ich denke, dass man dort den Gegenstand nicht erkennen kann  )


 

Die Bilder sind jetzt hauptsächlich schwarz, und nur die Eckpunkte und der Gegenstand sind "farbig" (also nicht schwarz, obwohl schwarz auch eine Farbe ist  )

EDIT: So, ich hab jetzt mal das erste der obigen Bilder mit den Werten:

```
float cornerHue = 0.50f;
        float circleHue = 0.13f;
        float threshold = 0.03f;
```
ausgewertet, und es kommt folgendes raus:


 

Man sieht, dass die Eckpunkte relativ gut erkannt werden! (Nur den Wert für den Gegenstand müsste man noch anpassen... (oder man markiert den gegenstand z.B.ROT, um das mit der Belichtung zu vermeiden))

Doch wie kann man noch folgendes einbauen:
Es sollen nur Flächen größer als x mal y Pixel als Eckpunkte erkannt werden? Dann könnte man nämlich die kleinen Fitzelchen komplett vermeiden(denn zurzeit hat mir das Programm z.B 6 Ecken ausgegeben :-D )
Viele Grüße
manu


----------



## Guest2 (10. Apr 2010)

Moin,

schon besser, aber warum jpeg-vergewaltigst Du die Bilder so?

Wenn man sich den Hue Kanal ansieht, sieht das extrem verblockt aus.
Die kommen doch hoffentlich nicht so aus der Webcam oder?
Würdest Du die in PNG oder wenn es sein muss, jpeg mit ner hohen Bitrate speichern, wäre das viel einfacher!

Gruß,
Fancy


----------



## ankmanu (10. Apr 2010)

Die sind als bmp gespeichert(von der webcam), nur hier als jpg, weil des beim bilderhochladen nicht anders geht...

gruß
manu


----------



## Guest2 (10. Apr 2010)

Dem wage ich zu widersprechen. 

Um bei Deinem bilder-hochladen.net zu bleiben:





PNG geht also durchaus!
Und macht es den anderen viel einfachen.

Gruß,
Fancy


----------



## ankmanu (10. Apr 2010)

ok kann sein, ich habe nur .bmp ausprobiert, und das ging nicht!

Aber wie kann ich das mit der mindestFläche realisieren?

Viele Grüße
manu


----------



## Marco13 (11. Apr 2010)

Mit den Bildern (außer dem letzten) sollte man es auch mit den "einfachen" Methoden hinkriegen.



> Doch wie kann man noch folgendes einbauen:
> Es sollen nur Flächen größer als x mal y Pixel als Eckpunkte erkannt werden?



Falls du immernoch mit dem CircleFinder von der ersten Seite rumhantierst: Was hast du denn bisher (selbst) dafür versucht?


----------



## ankmanu (11. Apr 2010)

Also bei mir sieht das Programm jetzt so aus:
(Ausschnitt)

```
private static Point computeCenter(Set<Point> points)
    {
        long cx = 0;
        long cy = 0;
        int counter = 0;
        for (Point p : points)
        {
            cx += p.x;
            cy += p.y;
            counter++;
        }
        if(counter >= 20){
    
         
        int x = (int)(cx/points.size());
        int y = (int)(cy/points.size());
        //System.out.println("x= "+x+"\n"+"y= "+y+"\n");
    return new Point(x, y);
    
    }
    else{System.out.println("Fläche zu klein");}
 return null;
}
```

Dadurch werden nur Flächen größer als 20 Pixel ausgegeben 
Allerdings sind "kleine" Flächen auf dem resultImage immernoch sichtbar....

EDIT: Allerdings ist das mit dem return null noch nicht perfekt, denn dann (wenn die Fläche zu klein ist) wird auch im Terminal "Circle centers [null, null, null,.... " ausgegeben.


Gruß
manu


----------



## Marco13 (11. Apr 2010)

Erstmal kann man das an sich hinterfragen. Eine 20 pixel lange Linie würde damit auch erkannt. Das macht nicht viel Sinn. Wenn es darum geht, zu überprüfen, ob eine Fläche eine bestimmte Größe hat, könnte man das mit einer Methode machen wie in http://www.java-forum.org/allgemeine-java-themen/98209-moeglichkeiten-bild-schnell-auszuwerten-2.html#post626179 angedeutet:

```
private static boolean isLargerThan(List<Point> area, int sizeX, sizeY)
{
    int minX = Integer.MAX_VALUE;
    int maxX = Integer.MIN_VALUE;
    int minY = Integer.MAX_VALUE;
    int maxY = Integer.MIN_VALUE;

    for (Point p : area)
    {
        minX = Math.min(minX, p.x);
        maxX = Math.max(maxX, p.x);
        minY = Math.min(minY, p.y);
        maxY = Math.max(maxY, p.y);
    }
    int dx = maxX-minX;
    int dy = maxY-minY;
    return dx > sizeX && dy > sizeY;
}
```
(ungetestet - aber trivial, wie schon gesagt).

Damit könnte man dann in der ComputeAreaCenters Methode sowas machen wie

```
List<Point> centers = new ArrayList<Point>();
        for (Set<Point> area : areas)
        {
            if (isLargerThan(area, 20, 20))
            {
                centers.add(computeCenter(area));
            }
        }
        return centers;
```


Aber dass man bei deinem bisherigen Ansatz ganz schlicht und einfach eine *simple if-abfrage* einbauen könnte

```
List<Point> centers = new ArrayList<Point>();
        for (Set<Point> area : areas)
        {
            Point point = computeCenter(area);
            [b]if (point != null)[/b]
            {
                centers.add(point);
            }
        }
        return centers;
```
und du da nicht selbst drauf kommst, zeigt schon einiges. In welchem Zusammenhang auch immer diese Aufgabe erledigt werden sollte: Du solltest dir überlegen, ob das das richtige für dich ist. 

Und denk' daran die Klasse bei dir umzubenennen, sonst könntest du Probleme kriegen, wenn dein Betreuer sowas macht wie "class circlefinder" - Google Search :autsch:


----------

