Farben Zählen

Status
Nicht offen für weitere Antworten.

*Blue$creEn*

Mitglied
Guten Tag allerseits

Schönes Forum habt ihr hier. Ich hoffe, ihr könnt mir auch bei meinem Problem helfen.

Ich möchte meine Klasse gerne die Farben eines Bildes zählen lassen. Dazu habe ich es mittels ImageIO in ein BufferedImage geladen. Pixels ist das Array aus dem zugehörigen DataBufferByte.

Wie die Experten unter euch sicherlich sehen können, ist diese Schleife irrsinnig lahm. Was kann ich besser machen?
Code:
farben = new ArrayList<Integer>();
for (int i = 0; i < pixels.length; i+=3) {
    int farbe = 0xff000000 | ((pixels[i]&0xff)<<16) | ((pixels[i+1]&0xff)<<8) |(pixels[i+2]&0xff);
    if (!farben.contains(farbe)) farben.add(farbe);
    }
Ich habe auch mal daran gedacht, die byte[]->Integer-Konversion rauszulassen und eine ArrayList von byte[] zu verweden. Allerdings verlängert das Erstellen so vieler Byte-Arrays (für die RGB-Komponenten) die Rechenzeit auf das Doppelte. Vermutlich sind hilfreiche Tipps ganz simpel; verzeiht mir, dass ich sie nicht kenne oder sehe.
 

dieta

Top Contributor
Versuch's vllt. mal mit einer HashMap, die ist glaube ich etwas schneller in solchen Dingen als eine ArrayList.
 

Jockel

Top Contributor
Spontan würde ich erst einmal die Variable farbe außerhalb der Schleife deklarieren, so dass diese nicht bei jedem Durchlauf neu erzeugt wird. Das alleine wird es wohl aber nicht bringen.
Hast du denn eine Einschränkung, von welchem Typ das Bild ist oder wie groß es sein kann?
 

Marco13

Top Contributor
Ein bißchen schneller würde es vmtl. schon werden, wenn der DataBuffer schon die fertigen ints enthalten würde. Falls es für dich egal ist, könntest du ein BufferedImage vom typ INT_RGBA erstellen, aber das Bild vorher extra zu diesem Zweck umzuwandeln lohnt sich vermutlich nicht!

Ansonsten war dieta's Tipp wohl der richtige:

if (!farben.contains(farbe)) farben.add(farbe);

Die "contains"-Methode durchsucht die ganze ArrayList nach dem übergebenen Element. Fastzustellen, dass eine Farbe NICHT in der Liste enthalten ist, ist der "worst case". Dieser worst case hat eine Laufzeit von O(n) (Websuche: Komplexität von Algorithmen, O-Notation).

Das heißt:
Wenn die ArrayList 100 Einträge hat, und es kommt eine neue Farbe dazu, müssen 100 Eingräge durchsucht werden. Wenn die ArrayList 100000 Einträge hat, und es kommt eine neue Farbe dazu, müssen 100000 Eingräge durchsucht werden.

Im allerschlimmsten Fall hast du z.B. eine Million Farben, und die werden nacheinander eingefügt. Dazu müssen ca. 500 Milliarden(!) Elemente durchsucht werden!!!

Mit einer HashMap (bzw. in deinem Fall: Ein HashSet<Integer>) ist es einfacher, festzustellen, dass ein Element NICHT in der Liste enthalten ist. Der worst case hat dann theoreitsch eine Laufzeit von O(1). D.h. um 1 Million Farben einzufügen, müssen nur 1 Million Tests gemacht werden.

Das dürfte etwas schneller sein :)
 

byte

Top Contributor
TreeSet garantiert eine Laufzeit von log(n) für contains, add und remove.



Marco13 hat gesagt.:
Der worst case hat dann theoreitsch eine Laufzeit von O(1). D.h. um 1 Million Farben einzufügen, müssen nur 1 Million Tests gemacht werden.

Es gibt keine Datenstruktur, wo die contains ne konstante Laufzeit hat. Wie sollte das auch gehen?
 

*Blue$creEn*

Mitglied
Ich lasse das Bild jetzt zum TYPE_INT_RGB umwandeln und ein HashSet verwenden. Die Farbzählung dauert jetzt nur noch ein paar Millisekunden. bytos Vorschlag werde ich auch noch ausmessen. Vielen Dank für eure kompetente Hilfe.

Ich frag euch morgen vermutlich, wie ich schneller Pixel einer Farbe durch die einer anderen ersetzen lassen kann, aber jetzt schau ich erstmal selbst, wie weit ich komme.
 

Ark

Top Contributor
Beim Hashen ist O(1) durchaus möglich, und womöglich auch der Grund, warum die Hashverfahren manchmal magisch anmuten. :shock:

Aber dieses pixels-Array finde ich etwas seltsam …

Vielleicht hilft dieser Link weiter: http://www.java-forum.org/de/viewtopic.php?t=41832 Voraussetzung ist natürlich, dass das Bild als BufferedImage im entsprechenden Format vorliegt. So ist in einem einzigen int-Wert die Farbe komplett gespeichert! Ich würde dann eine Baum- oder Streuwertmenge (TreeSet, HashSet ^^) wie schon beschrieben einsetzen. Das sollte schnell genug sein.

MfG
Ark
 

Marco13

Top Contributor
@byto: Wie Ark auch schon gesagt hat: Bei einem HashSet ist die Zeit für add, get und contains jeweils O(1). Voraussetzung dafür ist eine "gute" (d.h. passende) Hashfunktion. Bei einer schlechten HashFunktion KÖNNTE die Zeit THEORETISCH wieder O(n) werden, aber davon braucht man in diesem Fall nicht auszugehen.

Die einfachste Erklärung dafür, warum es eine Datenstruktur gibt, die (besonders in diesem spziellen Fall, aber - durch das Hashing - auch ganz allgemein) diese Operationen in O(1) durchführen kann ist
Code:
    boolean myVeryLageSet[] = new boolean[Integer.MAX_VALUE]; // *knirsch*
    
    boolean contains(int n)
    {
        return myVeryLargeSet[n]; // O(1) *grins*
    }
}
Und sowas wird beim Hashing eben (prinzipiell!) gemacht. Nur wird ein kleinerer Array verwendet, und die "großen" ints (mit der Hashfunktion) auf einen kleineren int umgerechnet.
 

EgonOlsen

Bekanntes Mitglied
Du hast außerdem im Fall, dass die Farbe nicht in der Struktur ist, zwei implizite Instanziierungen von Integer....wunderschön verschleiert vom ach so tollen Autoboxing. Die erste Instanz davon erzeugst du sowieso, weil du das contains in jedem Fall ausführst. Ich würde die Erzeugung explizit machen und vor das contains ziehen und diese Instanz dann auch in die Struktur (was immer du da jetzt für nimmst) einfügen. Mag nicht viel bringen, aber wer weiß.
Wenn das eine absolut zeitkritische Schleife ist, würde ich ein Hashset für echt primitive Werte bauen, damit die Integer-Erzeugungen komplett wegfallen.
Wenn die Bilder farblich nicht zu variabel sind, mag auch ein kleiner "Cache" aus einem int-Wert was bringen, in dem du den letzten Farbwert merkst und nur ein contains usw. ausführst, wenn sich der aktuelle Wert von diesem unterscheidet. Wenn das aber sehr oft passiert, ist es wiederum kontraproduktiv...das hängt also stark von der Struktur des Bildes ab.
 

byte

Top Contributor
Stimmt ihr habt recht. War mir nie bewusst, dass es tatsächlich konstante Laufzeit ist. :)
 

Ark

Top Contributor
Wenn ich garantieren könnte, dass es folgend zu keiner Kollision kommt, würde ich es wie folgt machen:
Code:
int[] farben;
farben=new int[pixels.length];
for(int i=0;i<pixels.length;i++){
    farben[pixels[i]%farben.length]++;
}
Voraussetzung ist eben, dass keine Kollisionen auftreten und in einem pixels-int der Farbwert vollständig gespeichert ist.

MfG
Ark
 

Marco13

Top Contributor
Die Anzahl der Farben ist einfach die Größe der enstehenden HashSet (und umgekehrt :wink: ). Oder was meintest du?
 

EgonOlsen

Bekanntes Mitglied
Der Vorschlag kann so nicht funktionieren, bzw. du müsstest ein 16Mio. Elemente großes Array dafür verwenden. Nicht wirklich praktikabel...aber man könnte es als Basis für eine Implementierung mit primitiven Typen nehmen. Aber wenn deine aktuelle Lösung jetzt schnell genug ist...
 

Marco13

Top Contributor
@*Blue$creEn*:: Ach so - da wäre es einfach die Anzahl der Einträge in dem Array, die NICHT 0 sind :wink: (falls es nicht zu "Hash"-Kollisionen kommt).
@EgonOlsen: Der Vorschlag funktioniert schon (falls es nicht zu "Hash"-Kollisionen kommt). :wink:
 

EgonOlsen

Bekanntes Mitglied
Marco13 hat gesagt.:
@EgonOlsen: Der Vorschlag funktioniert schon (falls es nicht zu "Hash"-Kollisionen kommt). :wink:
Natürlich, aber das ist akademisch und verwirrt hier nur, weil es eben genau dazu kommen wird. Wenn ich das bei 16Mio möglichen Farbwerten sicher vermeiden will, brauche ich eine 16Mio. Einträge fassende Hashtabelle.
 

Ark

Top Contributor
Marco13 hat gesagt.:
@*Blue$creEn*:: Ach so - da wäre es einfach die Anzahl der Einträge in dem Array, die NICHT 0 sind :wink: (falls es nicht zu "Hash"-Kollisionen kommt).
OK, dann eben anders:
Code:
int[] farben=new int[524288];//2 hoch 24 durch 32
int zaehler=0;
int t;
for(int i=0;i<pixels.length;i++){
    t=pixels[i];
    if((farben[t>>>5]&1<<t)==0){
        farben[t>>>5]|=1<<t;
        zaehler++;
    }
}
Das Ding sollte für Farben mit 8 Bit pro Kanal (RGB) funzen, kostet aber auch 16 MB an RAM!

MfG
Ark
 
Status
Nicht offen für weitere Antworten.
Ähnliche Java Themen
  Titel Forum Antworten Datum
L Farbverlauf RGB alle Farben Allgemeine Java-Themen 28
J Farben mit comparing vergleichen Allgemeine Java-Themen 3
B einen color-chooser bauen, ähnliche Farben vermeiden Allgemeine Java-Themen 5
E am häufigsten vorkommenden Farben aus einem Bild Allgemeine Java-Themen 5
D farben auf ähnlichkeit untersuchen Allgemeine Java-Themen 9
O "Rechnen" mit Farben Allgemeine Java-Themen 12
T Überprüfen ob zwei Farben ähnlich sind Allgemeine Java-Themen 14
M Farben ändern ? Allgemeine Java-Themen 11
O Image mit transparenten farben wie bei *.GIF ? Allgemeine Java-Themen 3
Fabiator Variablen Variablen Zählen Allgemeine Java-Themen 3
S Drools: Zählen wie oft ein Wert vorkommt Allgemeine Java-Themen 1
R Methoden Was fehlt mir bzw. muss ich bei der Methode countHarshabNumbers ändern damit ich die Harshad Zahlen im Intervall [51, 79] zählen kann? Allgemeine Java-Themen 19
A Binärer Suchbaum Knoten Zählen Allgemeine Java-Themen 4
L Menge der Buchstaben eines Textes zählen Allgemeine Java-Themen 3
J Rekursive Programmierung-Zählen von Ziffern Allgemeine Java-Themen 5
J Die Menge einer Zahl im Binärbaum zählen Allgemeine Java-Themen 7
N [stream-api] Parameter pro Typ zählen Allgemeine Java-Themen 1
B Counting Sort (Sortieren durch Zählen) Allgemeine Java-Themen 13
K Wörter in Strings zählen Allgemeine Java-Themen 7
D Fehlgeschlagene Logins zählen... Was ist sinnvoll? Allgemeine Java-Themen 2
R Zusammenhängende Werte in 2-dim. Array finden und zählen Allgemeine Java-Themen 3
C Kleinbuchstaben zählen Allgemeine Java-Themen 10
P Werte in Array zählen und Summe der einzelnen Teile ausgeben Allgemeine Java-Themen 10
M Ein bestimmtes Wort in einem Text zählen (String in String) Allgemeine Java-Themen 9
B substring zählen Allgemeine Java-Themen 7
Landei Collections Word-Frequenzen zählen Allgemeine Java-Themen 7
C Mausklicks zählen (extern) Allgemeine Java-Themen 6
S Knoten zählen in einem Binärbaum Allgemeine Java-Themen 2
S erzeugte objekte zählen Allgemeine Java-Themen 3
H Zeitraum: Arbeitstage zählen Allgemeine Java-Themen 5
J String Wörter zählen Allgemeine Java-Themen 4
S Array: Anzahl Elemente mit best. Wert zählen Allgemeine Java-Themen 4
M Anwendung nur einmal starten / Zeichen in String zählen Allgemeine Java-Themen 7
G Dateien und Verzeichnisse in einem Verzeichnis zählen Allgemeine Java-Themen 9
2 Tage zwischen zwei Datumsdaten zählen Allgemeine Java-Themen 2
G Tage zwischen zwei Datumsdaten zählen Allgemeine Java-Themen 3
G arguemente einer Methode zählen? Allgemeine Java-Themen 19
X Strings aus einer ArrayList zählen Allgemeine Java-Themen 11
S Methode zum Zählen von Buchstaben in Strings gesucht Allgemeine Java-Themen 11
I vergleich und zählen von Strings Allgemeine Java-Themen 7
C Objekte einer Klasse zählen Allgemeine Java-Themen 25
T Zeilen eines Projekts zählen lassen Allgemeine Java-Themen 14
M richtiges Ergebnis zählen und übergeben? Allgemeine Java-Themen 7
F Dateien in einem Ordner zählen Allgemeine Java-Themen 15
G ziffern zählen mit rekursiver methode Allgemeine Java-Themen 2
F Zählen wie oft Methode aufgerufen wurde Allgemeine Java-Themen 2
L Häufigkeit der Werte in Datei zählen! Heap Space beschränkt! Allgemeine Java-Themen 31
F Bestimmes zeichen im String zählen Allgemeine Java-Themen 34
G Dateien zählen im Verzeichnis Allgemeine Java-Themen 11
B Integer zählen bzw. speichern Allgemeine Java-Themen 3
S lines of code zählen Allgemeine Java-Themen 9
A Buchstaben zählen Allgemeine Java-Themen 5

Ähnliche Java Themen

Neue Themen


Oben