getRGB() zu lahm

Status
Nicht offen für weitere Antworten.

MadHatter

Aktives Mitglied
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
Code:
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.
 
B

Beni

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

Bert Brenner

Bekanntes Mitglied
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

Aktives Mitglied
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:
Code:
        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

Top Contributor
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

Top Contributor
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

Bekanntes Mitglied
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

Aktives Mitglied
Leider scheint der eher langsamer statt schneller geworden zu sein :S
Meine Implementierung:
Code:
            Graphics g = lastImg.getGraphics();
            g.setXORMode(Color.WHITE);
            g.drawImage(bImg, 0,0, pnlYourIP);
Code:
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

Top Contributor
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

Top Contributor
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

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

AlArenal

Top Contributor
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

Top Contributor
@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.
 
Status
Nicht offen für weitere Antworten.

Oben