GetUniqueColors

  • Warum nicht?

    Nun ja, bei einem 35 mp Bild kann ich mir zwar vorstellen, dass ich vielleicht 50% herausholen kann, aber das ist in dieser Zeit Region nicht so relevant. zumal 35 mp Bilder eher die Ausnahmen sind. Kleinere Bilder z.B. in HD Auflösung dauern ca. 150ms und da lohnt sich vermutlich kein MT.

    Auch am Arsch geht ein Weg vorbei...

    ¯\_(ツ)_/¯

  • Bei meiner Set-Variante ist die MT-Variante beilspielsweise bei 4 logischen Prozessoren bei mir um den Faktor 3,2 schneller.

    Also deutlich schneller als nur die 50% die du erwartest.

    Aber im Grunde stimmts schon - wenn's nicht notwendig warum dann noch einen zusätzlichen Aufwand betreiben und neue Unwägbarkeiten mit reinholen.

    • Offizieller Beitrag

    Also wie gesagt: Was willst du genau zählen bzw. wozu brauchst du es?

    Ja, damit hast Du natürlich Recht!

    Eigentlich will ich damit den Benutzern meines Programms nur eine Zusatzinformation bieten.

    Insofern sollte die PARGB-Methode wohl ausreichend sein.

    Anfangs dachte ich auch, dass ich dafür schnell mal eine Funktion schreibe und alles ist gut.

    Dass sich das Problem quasi als Beispiel für die Schwäche einer Interpretersprache herausstellen sollte, habe ich nicht geahnt.

    Auf jeden Fall hat es sich für mich zu einem sehr interessanten Thema entwickelt, bei dem ich eine Menge dazu gelernt habe. :):thumbup:

    Und ich danke euch allen für die rege Teilnahme und die tollen Lösungen! :klatschen:

  • Eigentlich will ich damit den Benutzern meines Programms nur eine Zusatzinformation bieten.

    WAS?!? Wir schwitzen hier Blut und Tränen, rackern unermüdlich um noch das letzte bisschen Quäntchen rauszukitzeln.

    Und für was?: Für ne popelige Zusatzinformation!

    Wir dachten hier ginge es um Leben und Tod oder zumindest den Weltfrieden!

    ... 8o

  • WAS?!? Wir schwitzen hier Blut und Tränen, rackern unermüdlich um noch das letzte bisschen Quäntchen rauszukitzeln.

    Separates the boys from the men:party:

  • 1334608958_palette.png

    Wie viele Farben hat dieses 32-bit PNG Bild wirklich?

    IrfanView: 18276

    GIMP: 17894

    XnView: 17894

    Oscar's Variante: 18161

    Meine FB Variante: 19520

    GIMP und XnView scheint nur 24- bit zu zählen.

    Auch am Arsch geht ein Weg vorbei...

    ¯\_(ツ)_/¯

  • Also!

    Das hier hat mir irgendwie keine Ruhe gelassen. Wir hatten jetzt Assembler, C, C++... Und C++ sogar multithreaded, allerdings durch die Sets relativ langsam. Ich hab also mal eine Implementation in purem Win32-C mit Threading geschrieben. So langsam muss ich aber auch kapitulieren. Hier mal die Statistiken, jeweils meine C-MT-Implementation gegen AspirinJunkies C++-Sets.

    Folgende Bildmaße wurden verwendet: klein (1024x768), mittel (1920x1280), groß (5590x3731).

    Code
    x       | AJ Single | AJ Multi | CT Multi
    klein   |    120.36 |    51.87 |    25.46
    mittel  |    375.95 |   151.47 |    36.89
    groß    |   2340.58 |   350.63 |   202.09

    Ich weiß zwar nicht, warum meine Implementation bei dem mittleren Bild so extrem heraussticht... Aber prinzipiell kann man so auf jeden Fall noch was rausholen! Ich teile das Bild einfach in mehrere gleiche Abschnitte auf und gehe diese dann in verschiedenen Threads durch. Am Ende, wenn alle Threads fertig sind, zähle ich die einzelnen Farben.


    DLL, vollständiger Source usw. alles im Anhang.

  • Hmm, kannst du bitte mal die FB Version checken? Die FB Version ist auf meiner Kiste ungefähr gleich schnell wie deine MT Version!

    Download: https://www.mediafire.com/file/8qansw4x5…CountColors.zip

    Ich habe auch das 36 MPixel Bild hinzugefügt!

    Danke.

    FB:

    Code
    Loading image
    Image dimension: 7360x4912
    Counting all 24-bit colors
    Unique color count: 402990
    321.7085356591269 ms

    Chesstiger's MT:

    Code
    Zeit: 318.318642564774 ms
    Anzahl der Farben: 402990

    Auch am Arsch geht ein Weg vorbei...

    ¯\_(ツ)_/¯

    2 Mal editiert, zuletzt von UEZ (30. November 2017 um 19:46)

  • chesstiger :

    Klasse!

    Prinzipiell machst du bei deinem Threading das was manuell OpenMP für mich macht.
    Wirklich interessant finde ich aber die Tatsache dass die endgültige Zählung der Farben bei dir in einer Schleife über 16.777.216 Elemente läuft.

    Und das am Ende als Single-Thread!

    Das dies im Endeffekt dennoch meist schneller ist als die Variante per atomaren Inkrement erstaunt mich wirklich.

  • UEZ

    Ah, das hab ich ganz übersehen. Bei mir kommt da raus...

    klein: 18.49 ms

    mittel: 33.51 ms

    groß: 195.28 ms

    riesig: 285.78 ms

    Ist also dennoch ein My schneller als der simple C-Code von oben!

    Selbst, wenn ich noch die Summation der Werte parallelisiere, ist da nicht mehr so viel zu holen.

    ( AspirinJunkie Da war echt noch was zu machen. Atomare Inkrements scheinen nicht ganz so performant zu sein... Aber auch hier macht sich MT nochmal bezahlt.)

    klein: 19.78 ms

    mittel: 32.83 ms

    groß: 182.60 ms

    riesig: 254.05 ms

    Das ist noch etwas, aber nicht so viel, wie ich erwartet hätte. Wobei man fairerweise auch sagen muss, dass jetzt die Grenze des Machbaren erreicht ist. Der eigentliche DllCall dauert bei meiner neuen Multithreaded-Variante selbst für dein Riesen-Löwenzahnbild nur noch knappe 28 ms, also Pi mal Daumen 10% der Zeit. Der Rest geht für LockBits drauf..... Jetzt wäre der Moment gekommen, dort was zu optimieren.

    Edit:

    Ich habe mich jetzt mal etwas informiert. LockBits erstellt eine komplette Kopie der Bitmap. Das muss natürlich Ar...langsam sein. Man könnte evtl. GDIPlus durch was anderes ersetzen.

    Edit²:

    So, ich habe jetzt eine Implementation mit FreeImage gebastelt. Das Bild wird per FreeImage in AutoIt geladen und an meine DLL weitergegeben. Allerdings zeigt sich hier, dass die Werte für JPEGs etwas abweichen. Das Verhalten wurde ja schon zuvor beobachtet. Allerdings ist so nochmal ein MASSIVER Geschwindigkeitsvorteil zu erreichen. Beispiel am Löwenzahn-Bild: GDIPlus + DLL => ~250 ms, FreeImage + DLL => ~191.93 ms.

    • Offizieller Beitrag

    chesstiger : Klasse Version und super schnell! Aber halt auch nicht mit voller Alphakanal-Unterstützung.

    Ich denke, das Problem dabei dürfte klar sein. Für die Array-Varianten kann man nicht 4GB Speicher reservieren.

    AspirinJunkie und UEZ haben zwar gezeigt, dass es möglich ist, auch den Alphakanal mit einzubeziehen und das klappt bei den bisherigen Bildern auch recht gut.

    Ich dachte mir aber, dass die Schwachstelle dieser Umsetzungen bei der Anzahl der Farben liegt. Solange das "nur" eine Million Farben sind, ist noch alles gut, aber was passiert bei 7680 x 4320 Pixel (8k-Bildern)?

    Und um das mal mit möglichst vielen (33.177.600) Farben zu testen, habe ich ein Testbild generiert:

    Das erstellen dauert aber mehrere Minuten. Wer nicht so lange warten will, kann sich das Bild von meiner Homepage herunterladen: http://www.technik-hobby.de/test/index.html

    Die DLL von AspirinJunkie funktioniert auch mit diesem 8k-Bild. Wobei jetzt die Single-Thread-Version (ca. 14 s) schneller ist, als die Multi-Thread-Version (ca. 21 s). :huh::/

    UEZ: Deine FB-Exe stürzt nach kurzer Laufzeit ab (nachdem das 8k-Testbild geladen wurde).

  • Für ein ARGB-Bild benötigt man aber nicht zwangsweise 4 GB Speicher! Man kann den Speicherbedarf erheblich reduzieren, man muss nur bisher ungenutzten Speicher effektiv nutzen.

    Im Endeffekt haben wir für jede mögliche Farbe (das sind 2^32 + 1, von #00000000 bis #FFFFFFFF) nur die Information, ob sie auftritt oder nicht. Zwei mögliche Zustände... Das schreit doch nach der Kodierung in einem Bit!

    Man kann zum Beispiel ein 2^28 + 1 Array of Short (2 Byte) verwenden. (2^28 + 1) * 2 Byte = 536.870.914 Byte = 512 MByte. Das ist zwar auch nicht wenig, aber annehmbarer als 4 GByte.

    Praktisch habe ich das so gelöst, dass die vordersten 4 Bit der Farbe (also die vordere Hälfte des Alpha-Wertes) mit dem jeweiligen Array-Element (indexiert durch die restlichen 28 Bit) verodert werden. Am Ende beim Summieren zähle ich dann einfach die Bits jedes Elementes. Funktioniert prima, und dauert bei Oscars Testbild inklusive LockBits... Ziemlich genau eine Sekunde, wobei davon auch nur knapp 400 ms für den DllCall draufgehen. Alles im Anhang!

  • UEZ: Deine FB-Exe stürzt nach kurzer Laufzeit ab (nachdem das 8k-Testbild geladen wurde).

    Also bei mir läuft die Exe:

    Code
    Loading image
    Image dimension: 7680x4320
    Counting all 32-bit colors
    Sorting color array
    Counting unique colors
    Unique color count: 33177599
    Time: 8275.271474720455 ms

    Wenn ich nur die 24-Bit betrachte (Inline ASM):

    Code
    Loading image
    Image dimension: 7680x4320
    Counting all 24-bit colors
    Unique color count: 16777216
    Time: 701.3580921111782 ms

    Probiere bitte mal diese Exe.

    • Offizieller Beitrag

    Weil es mir keine Ruhe gelassen hat und weil ich noch mit Assembler "rumspielen" wollte, habe ich die "Quicksort- und Zählmethode" mal in Assembler geschrieben.

    Damit kann man jetzt auch volle 32-Bit-Bilder erfassen. Und Quicksort in Assembler schafft die 33 Millionen Pixel von meinem obigen Testbild in etwas über 3 Sekunden (auf meinem Rechner).

    Die FB-Variante von UEZ brauchte auf meinem Rechner über 5 Sekunden.

    Mein Quicksort ist eine rekursive Variante, die den Stack ausgiebig nutzt, aber dafür ansonsten nur den Speicher der Pixelstruct (für das Bild) benutzt.

    Zum testen des Scripts braucht ihr die UDF "assembleit2_64.au3" von Andy und natürlich ein Testbild.

    Hier das Script:

    Andy: Ich bin schon froh, dass ich das überhaupt in Assembler umsetzen konnte (mit einer Quicksort-Vorlage in Pseudocode), aber vielleicht hast Du ja noch ein paar Tips zum optimieren?