# getRGB() zu lahm



## MadHatter (28. Jul 2005)

Hi,
ich habe 2 BufferedImages (1024x768) die ich miteinander vergleiche. Dafür gehe ich mit 2 For-schleifen durch jedes Pixel. Problem: es verbraucht zuviel CPU. Der Grund (durch Profiler entdeckt) ist die Funktion getRGB() die am meißten verbraucht.
Am Anfang hatte ich zuerst mithilfe von 
	
	
	
	





```
int[] rgb_neu = bImg.getRGB(0, 0, w, h, null, 0, w);
```
erstmal alles in einen array geschrieben und dann vom Array abgelesen. Dann aber habe ich es direkt mit getRGB(x,y) abgefragt, scheint etwas schneller zu sein.
Gibt es dennoch eine bessere/schnellere Möglichkeit die Pixeldaten abzufragen. Bei C++/WinAPI gings durch die Lock() Methoden relativ schnell.
Ich bin dankbar für jede Hilfe.


----------



## Beni (28. Jul 2005)

Frage: was ist mit der "equals"-Methode? Die müsste doch auch für Images implementiert sein...


----------



## Bert Brenner (28. Jul 2005)

Ansonsten, evtl einfach beide Images in ein neues Zeichnen und beim Zeichnen des 2. Graphics.setXORMode(Color.WHITE) benutzen. Wenn das neue Bild dann komplett weiss ist, dann sind sie identisch.


----------



## MadHatter (28. Jul 2005)

Das geht ja nicht, ich muss ja wissen, welche Teile des Bildes anders sind, und mache darum einen Rechteck. (Die Teile des Bildes die durch setXORMode nicht weiß sind, sind sie dann schwarz oder eine ganz andere beliebige farbe?) 
Zurzeit mach ich das so:

```
public void prepareImage(BufferedImage bImg)
        {
            Areas = new Vector<Rectangle>();

            if(lastImg == null)
            {
                Areas.add(new Rectangle(0, 0, bImg.getWidth(), bImg.getHeight()));
                return;
            }

            // erstmal Zugriff auf Pixeldaten verschaffen
            int w = bImg.getWidth(), h = bImg.getHeight();
            //int[] rgb_neu = bImg.getRGB(0, 0, w, h, null, 0, w);
            //int[] rgb_alt = lastImg.getRGB(0, 0, w, h, null, 0, w);

            int pos = 0;
            int[] data1 = bImg.getData().getPixels(0,0,w,h, (int[])null);
            int[] data2 = lastImg.getData().getPixels(0,0,w,h, (int[])null);

            // los gehts!
            final int spacex = 5, spacey = 5;
            for(int y = 0; y < h; ++y)
            {
                // für jede Reihe
                for(int x = 0; x < w; ++x)
                {
                    pos = y * w + x;

                    // aha, hier hat sich was geändert!
                    //if(rgb_neu[pos] != rgb_alt[pos])
                    if(data1[pos] != data2[pos])//bImg.getRGB(x,y) != lastImg.getRGB(x,y))
                    {
                        // I. es gibt noch keine Rectangles
                        if(Areas.isEmpty())
                        {
                            Rectangle nRect = new Rectangle(x, y, 1, 1);
                            Areas.add(nRect);
                            continue;
                        }

                        // II. er findet ein Rectangle, das nützlich erweitert werden könnte
                        // TODO das geigneteste Rectangle finden, ergo: das was am wenigsten an
                        // pixel erweitert
                        // werden würde
                        boolean found = false;
                        for(Rectangle r : Areas)
                        {
                            if(x > (r.x - spacex) && x < (r.x + r.width + spacex) && y > (r.y - spacey)
                                    && y < (r.y + r.height + spacey))
                            {
                                r.x = Math.min(x, r.x);
                                r.y = Math.min(y, r.y);
                                r.width = Math.max(x - r.x, r.width);
                                r.height = Math.max(y - r.y, r.height);
                                found = true;
                                break;
                            }
                        }

                        // III. es müsste ein neues Rectangle erstellt werden
                        if(!found)
                        {
                            Rectangle nRect = new Rectangle(x, y, 1, 1);
                            Areas.add(nRect);
                        }
                    }
                } // for every x
            } // for every y
        } // prepareImage
```


----------



## lin (28. Jul 2005)

musst du feststellen können, ob sich die beiden Bilder z.B. nur duch 10px unterscheiden... 
Weil du könntest sonst einfach nur gewisse Teile oder Pixel in einem bestimmten Abstand vergleichen, wäre deutlich performanter...


----------



## Grizzly (28. Jul 2005)

lin hat gesagt.:
			
		

> musst du feststellen können, ob sich die beiden Bilder z.B. nur duch 10px unterscheiden...
> Weil du könntest sonst einfach nur gewisse Teile oder Pixel in einem bestimmten Abstand vergleichen, wäre deutlich performanter...


Im Worst Case bekommst Du halt genau die Pixel im Abstand von 10px, die sich nicht unterscheiden - dazwischen schon. 

Unterschiedliche Pixel? Rechteck darum rum? Klingt irgendwie nach VNC (TightVNC, WinVNC, u.ä.). 
Dort werden die zu sendenden Bildausschnitte auch so ähnlich festgestellt.


----------



## Bert Brenner (29. Jul 2005)

Wenn das du das so machst wie ich geschrieben habe sind alle Teile die nicht identisch sind mit einer anderen Farbe versehen. Der Vorteil währe halt das du nur noch bei dem resultierenden Bild mir getRGB alles überprüft werden muss welche Bildpunkte nicht weiss sind.


----------



## MadHatter (29. Jul 2005)

Leider scheint der eher langsamer statt schneller geworden zu sein :S
Meine Implementierung:

```
Graphics g = lastImg.getGraphics();
            g.setXORMode(Color.WHITE);
            g.drawImage(bImg, 0,0, pnlYourIP);
```


```
if(lastImg.getRGB(x,y)!=0xFFFFFFFF) { ... }
```
Halt die entsprechenden Teile vom code ersetzen.

Das mit VNC liegt ihr nicht so ganz falsch  : ich arbeite gerade an einem sehr ähnlichem Projekt, java-basiert.


----------



## Grizzly (30. Jul 2005)

MadHatter hat gesagt.:
			
		

> [...]
> Das mit VNC liegt ihr nicht so ganz falsch  : ich arbeite gerade an einem sehr ähnlichem Projekt, java-basiert.


Bei den (meisten) VNC Servern gibt es ja auch die Möglichkeit per HTTP auf den Rechner zu zu greifenen. Dabei wird das über ein Applet gelöst. Nur der Server funktioniert halt über C oder C++ und Systemfunktionen.


----------



## Sanix (26. Aug 2005)

Sonst schreibs halt in c++, wenns um Performance geht. Weil das Problem liegt ja eher am Algorithmus.


/edit
Könnte unklar sein. Wolltedamit sagen:
Der Algorithmus ist zwangsläufig so. Wenn dir die Javafunktion zu langsam ist, musst du halt ausweichen.


----------



## byte (26. Aug 2005)

getRGB() ansich is ja nich langsam, aber du rufst es halt 2 * 786432 mal auf. bezweifel dass das unter c++ schneller geht.


----------



## AlArenal (26. Aug 2005)

Sanix hat gesagt.:
			
		

> Sonst schreibs halt in c++, wenns um Performance geht. Weil das Problem liegt ja eher am Algorithmus.



Was ist das denn für ein Tipp? Wird sein Algo schneller, wenn er ihn in ner anderen Sprache implementiert? Nein, wird er nicht. Wenn du das Gaspedal nicht runterdrückst ist es ziemlich schnurz ob du Trabi oder Ferrari fährst...


----------



## 0xdeadbeef (26. Aug 2005)

@MadHatter:

Mal so 'ne Frage: in obigem Code benutzt Du "getData", um das das Raster des Images zu gelangen. 
Das erzeugt eine Kopie des Rasters, was schon einige Zeit dauern könnte. Warum benutzt Du nicht "getRaster" - das ist einfach das (eh schon vorhandene) Raster des Images und geht sofort ohne Kopieraufwand.

Ansonsten sind die getPixel(s)-Funktionen nicht sonderlich effizient implementiert. Eventuell würde es sich lohnen, sich ein eigenes SampleModel basierend auf SinglePixelPackedSampleModel o.ä. zu schreiben, bei dem man direkt den Int-Wert in einem Zyklus lesen/schreiben kann, ohne aufwendige Maskierungen zu durchlaufen.

Wobei für eine VNC-ähnliche Applikation die Beschränkung auf 16bit bzw. einen palettierten 8bit-Modus aus Gründen der Übertragungsgeschwindigkeit eine gute Idee wäre.


----------

