GetUniqueColors

  • Ist es denn 100% gewährleistet, dass der Speicher für das Bool auch wirklich 0 ist? Ich meine es gab da was, dass man nicht gewährleisten kann, was im Speicher drinnen steht. Würde das dann nicht im Extremfall zum Ausbleiben von gefundenen Pixeln führen?

    Dafür gibt es ja calloc. malloc stellt dir nur den Speicher zur Verfügung, zur Sicherheit sollte man diesen mit Nullen überschreiben.

  • Danke, wieder was gelernt :D

    Ich hab ein kleines Beispielprogramm geschrieben und das zeigt das ganze auch schön:

    Code
    Non-Zeroes Malloc: 18
    Non-Zeroes Calloc: 0
  • Ich hab ein kleines Beispielprogramm geschrieben und das zeigt das ganze auch schön:

    Code
    Non-Zeroes Malloc: 18
    Non-Zeroes Calloc: 0

    Danke, mir war ja bewusst, dass der Effekt existiert, sonst hätte ich nicht gefragt. War mir nur nicht bewusst, dass calloc() uns dieses unsauberen Effekts entledigt.

    Es gibt sehr viele Leute, die glauben. Aber aus Aberglauben.
    - Blaise Pascal

  • Hab eine primitive ASM Version geschrieben. (primitiv im Sinne von "kein SSE und sonstige Extras, sondern wirklich nur extrem einfach"). Da hierbei AutoIt für das calloc zuständig ist (das geht bestimmt auch in asm, aber ich habe das noch nie gemacht und hatte keine Zeit zu googeln :D) gibt es einen größeren Overhead. Daraus folgt, dass die ASM Variante für "kleine" Bilder langsamer als die DLL ist und für große Bilder schneller.

    Edit: Zumindest bei mir lief es für 4K Bilder ca. 30-50% schneller. Aber keine Garantie dass der PC nicht explodiert wenn der Code ausgeführt wird :D

  • Hi zusammen,

    um Farben in einem Bild zu zählen hatte ich mal eine Funktion in ASM erstellt....ist schon SEHR lange her^^ (aus 2010). Die "schöne" Variante habe ich leider nicht mehr gefunden, aber ggf. hilft ja das anliegende Script weiter.

    Vorgehensweise:

    Ich erstelle eine Struct mit der Anzahl möglicher Farben einer Bitmap, also 256^3 Farben.

    Dann erhöhe ich für jedes Pixel die entsprechende Adresse innerhalb der Struct (was ja die Farbe darstellt) um 1 hoch. Fertig.

    Um jetzt ein Array für die Farben zu bekommen, erstelle ich aus der Struct einen kommaseparierten Text, welcher aus der Farbe in Hex-Darstellung, gefolgt von der Anzahl dieser Farbe, besteht.

    Diese Texterstellung (aus der Adresse die HEX-Darstellung der Farbe erzeugen) ist ca. 90% des ASM-Codes, das eigentliche "zählen" wird von einer kurzen Schleife mit 6 Zeilen ( erledigt.

    Aus diesem so erstellten Text fummelt StringSplit() ein Array.....

    Bei meinem Uralt-Laptop dauert die Farbseparierung in den Textstring ca. 30ms.

    Das folgende Stringsplit, um daraus ein Array zu erstellen, habe ich nie ausgestoppt, da ich das Array nie brauchte....

    • Offizieller Beitrag

    Andy: Vielen Dank für Dein Beispiel!

    Mit der Hilfe habe ich mal mein erstes ASM-Programm erstellt und es funktioniert sogar. :)

    Bei den kleinen Bildern ist es nicht viel schneller als die mit TCC erstellte DLL in C, aber bei dem großen Bild (4608 x 3456 px) ist es dann doch noch etwas schneller (185ms).

    Wobei ich dazusagen muss, dass mir die 200ms bei der DLL schon ausreichen würden. Im Gegensatz zu den fast 29 Sekunden beim puren AutoIt ist das schon eine andere Liga.

    Außerdem sollte ich noch erwähnen, dass der größte Teil von den 200 bzw. 185 ms für das "_GDIPlus_BitmapLockBits" unter AutoIt draufgehen (ca. 141 ms).

    Aber vielleicht magst Du ja mal über mein ASM-Programm schauen und mir evtl. Verbesserungsvorschläge aufschreiben:

    Auf jeden Fall schonmal ein großes DANKESCHÖN für "assembleit2_64.au3"! :thumbup::klatschen:

  • Außerdem sollte ich noch erwähnen, dass der größte Teil von den 200 bzw. 185 ms für das "_GDIPlus_BitmapLockBits" unter AutoIt draufgehen (ca. 141 ms).

    Ja, das "sperren" dauert wirklich extrem lange.

    Ich kann mich vage erinnern, dass bei meinen früheren Versuchen unter XP und WIN7, "schnell" Bitmaps zu bearbeiten, sich _GDIPlus_BitmapLockBits() als langsamste Variante herausgestellt hat, um einen Pointer auf die Pixeldaten zu bekommen.

    Jedenfalls, wie auch schon bei dem Beitrag von Mars erwähnt, wenn man das erste Mal im Script das _GDIPlus_BitmapLockBits() aufruft.

    Sämtliche weiteren Aufrufe dieser Funktion erfolgen teilweise bis zu 100x schneller!

    GGf. hat sich das ja unter WIN10 gegenüber XP/WIN7 geändert?! Auf dem aktuellen WIN10 ist (bei mir zumindest) _GDIPlus_BitmapLockBits() SEHR schnell im Vergleich zu allen anderen getesteten Methoden!

    Generell ist die resultierende Geschwindigkeit aber eine Frage der Programmierung einer selbst erstellten Funktion. Natürlich kann man ALLES was zum Aufruf dieser Funktion erforderlich ist, in den Funktions-Code hineinpacken. Das ist dann idiotensicher und Programmierer/Userfreundlich!

    Ich habe mir allerdings angewöhnt, alles was in irgendeiner Weise mit Speicheranforderungen usw. zu tun hat, beim Start des Programms zu erledigen.

    Dann dauert der Start des Programms ggf. eine Sekunde länger (was niemand bemerkt/stört), aber der Ablauf beim teilweise zig-tausendfachen Aufruf einer Funktion wird massiv beschleunigt (DAS merkt man deutlich!).

    Übrigens Mars

    GEILER ASM-Code! Sauschnell und clever umgesetzt!


    Aber vielleicht magst Du ja mal über mein ASM-Programm schauen und mir evtl. Verbesserungsvorschläge aufschreiben:

    Da ist nicht viel zu verbessern:klatschen:

    Sehr einfach, strukturiert und nachvollziehbar, SO muss das sein!:thumbup:

    Hast du den Debugger benutzt? Und ggf. Verbesserungsvorschläge?

    //EDIT

    Insgesamt ist festzustellen, dass sparsamer "Verbrauch" von Speicher für die Farbcodes, also BYTE statt UINT/DWORD in diesem Fall extrem viel an Performance bringt.

    IdR ist das bei "Bildbearbeitung", also Filter u.ä., völlig unerheblich, da Pixel für Pixel nacheinander abgearbeitet wird.

    Der Prozessor liest bei einer Anfrage aus dem Speicher die gesamte Cacheline, ggf. auch mehrere Blöcke. Daher ist das "nächste" Pixel immer im Cache. Es "schwuppt"!

    Beim vorliegenden Script allerdings wird beim Lesen/Schreiben der "Farben" aber relativ wild im Speicher umhergesprungen, der Cache wird somit wesentlich seltener getroffen, Cache-Misses bremsen das Programm extrem aus!

    Ich habe das ASM-Programm daher testweise aufgeteilt:

    Die erste Schleife schreibt, wie gehabt, den Farbcode jedes Pixels als "1" in die BYTE-Struct."Wildes" Schreiben an unterschiedliche Adressen in den Speicher....

    Die nächste Schleife durchläuft nun die BYTE-Struct und liest/addiert nacheinander die gesetzten BYTEs auf, zählt also die verwendeten Farben. Da alle Adressen aufeinander folgen, liegt das nächste zu lesende BYTE immer im Cache!

    In der Summe dauert dieses Programm exakt genau so lange wie die "verschachtelte" Version, allerdings kann man die Zeiten der Schleifen per RDTSC besser ausstoppen.

    Der Unterschied ist gewaltig! Die Schreibschleife benötigt 5-7 Mal so lange wie die Leseschleife.

    Oscar

    Ich habe die von dir erstellte DLL mal mit dem Debugger bzw. IDA untersucht.

    Der Compiler werkelt dabei in der Standard-Einstellung, verwendet also relativ großzügig (Speicher-)Variablen anstelle von Prozessorregistern.

    Schau mal, ob es da einen/mehrere Compilerschalter gibt, die das optimieren, dann käme der Code sicher nahe an den selbstgestrickten ASM-Code heran.

    Btw. wie unterscheiden sich bei dir die Laufzeiten von DLL und ASM?

    ciao
    Andy


    "Schlechtes Benehmen halten die Leute doch nur deswegen für eine Art Vorrecht, weil keiner ihnen aufs Maul haut." Klaus Kinski
    "Hint: Write comments after each line. So you can (better) see what your program does and what it not does. And we can see what you're thinking what your program does and we can point to the missunderstandings." A-Jay

    Wie man Fragen richtig stellt... Tutorial: Wie man Script-Fehler findet und beseitigt...X-Y-Problem

    2 Mal editiert, zuletzt von Andy (22. November 2017 um 19:54)

    • Offizieller Beitrag

    Erstmal Danke für Deine "Bewertung"!

    Ich bin ja schon froh, dass ich da nicht grobe Schnitzer eingebaut habe. Von Mars habe ich mir jetzt auch noch die kürzere Variante abgeguckt. Eigentlich lagen wir ja schon dicht zusammen.

    Und so wie die Funktion jetzt arbeitet, reicht mir das völlig. Für ein 4k-Bild (3840x2160 px) braucht die Funktion ca. 85ms. Der reine ASM-Teil liegt bei 20ms. Ich denke, da hat es wenig Sinn weiter am ASM-Teil zu feilen.

    Die ganz großen Bilder (direkt von der Kamera) liegen mit ca. 185ms auch völlig im grünen Bereich. Und das mit nur 40 Bytes Assemblercode. :)

    Den Debugger habe ich auch benutzt und finde ihn sehr hilfreich. Man kann schnell mal nachsehen, ob in den Registern die vorgesehen Werte stehen. Top! :klatschen:

    Ich denke, solche Inner-Loops werde ich jetzt öfter in Assembler schreiben. :)

    Edit: Muss man sich eigentlich um das "Retten" der Registerinhalte selbst kümmern? Also bei Aufruf des ASM-Codes alle Register auf den Stack und am Ende wiederherstellen? Oder macht DllCallAddress das automatisch?

    Ich habe das "Retten" bisher nicht gemacht und es klappte alles, aber ich weiß nicht, ob das nicht auf Dauer zum Absturz führen kann.

  • Ich hatte auch vor einiger Zeit diese Funktion in AutoIt und FreeBasic geschrieben und dabei bemerkt, dass die Anzahl der Farben sich von Programm zu Programm unterscheidet.

    Dein Amsel Bild:

    Unsere GDI+ Versionen: 160514

    IfranView: 148516

    XnView: 160537

    Keine Ahnung, wie man nun weiß, welcher Wert der richtige ist.

    Ach ja, meine FB Version benötigt für das HD Amsel Bild ca. 400ms, wobei ich den Weg über Arrays genommen hatte (hinzufügen, sortieren und zählen).

    Auch am Arsch geht ein Weg vorbei...

    ¯\_(ツ)_/¯

  • Ach ja, meine FB Version benötigt für das HD Amsel Bild ca. 400ms, wobei ich den Weg über Arrays genommen hatte (hinzufügen, sortieren und zählen).

    Kannst du mal deine Variante umprogrammieren, so dass du sie nicht sortierst? Ich hatte ja ein AutoIt-Script bereits dazu am Anfang gepostet,

    wenn du so viele Indizes wie Farben erzeugst, dann fällt die Suchzeit weg und aus 400ms sollten deutlich weniger werden.

  • Leider kann FB nicht 256^4 als Array adressieren oder ich weiß nicht, wie das gehen soll. Jedenfalls mit 256^3, sprich 24-Bit Support, liege ich jetzt bei ca. 150 ms für das HD Amsel Bild.

    Auch am Arsch geht ein Weg vorbei...

    ¯\_(ツ)_/¯

  • Das liegt vermutlich daran, dass 256^4 * 1 Byte = 4 Gigabyte sind. Versuch mal das Programm mit 64Bit zu kompilieren, oder 256^4 * 1 Bit (also 1 Bit je Pixelfarbe) zu adressieren. (Für die 1 Bit Variante brauchst du dann aber ein paar Bitshifts, damit du die einsen korrekt speichern kannst, da der Zugriff nur auf 8 Bit Blöcke möglich ist. Alernativ kann man auch per OR und 10000000b das erste, mit OR 01000000b das zweite usw Bit setzen und mit AND 10000000b abfragen ob dieses Bit gesetzt wurde)

  • Das Array unter 32-bit funzt nicht, aber als 64-bit. Da werden schlappe 4 GB einfach belegt. Nicht gut, aber wie soll's auch anders sein!

    Die langsamere Variante unterstützt auch 32-bit Bilder und verbraucht nur einen Bruchteil an Speicher.

    Auch am Arsch geht ein Weg vorbei...

    ¯\_(ツ)_/¯

    • Offizieller Beitrag

    Das ist mir auch schon aufgefallen. Ich kann mir aber nicht erklären wieso das so ist.

    Vom Prinzip her dürfte die von uns verwendete Array-Methode keine "falschen" Ergebnisse liefern.

    Kann es sein, dass die Laderoutinen von GDI+ und den von IrfanView bzw. XnView die JPEG-Daten anders "interpretieren"?

    Edit: Zum zählen der echten 32 Bit (ARGB): da wirklich 4 GB RAM zu reservieren, finde ich übertrieben. Ich bin in meiner Funktion ja den Weg über PARGB gegangen. So werden die Alphawerte vor dem Bitlock auf die RGB-Werte addiert.

  • Die unterschiedlichen Farben habe ich auch bemerkt. Wenn ich das .jpg Bild als .png abspeichere sollte sich ja an den Farben nichts ändern.

  • Oscar : stimmt, wo du es jetzt sagst, hatte ich auch schon recherchiert und festgestellt, dass es am JPEG Decoder hing. Hab's wieder vergessen (Altersamnesie). ;)

    Auch am Arsch geht ein Weg vorbei...

    ¯\_(ツ)_/¯

  • Ich bin mal ganz naiv rangegangen und dachte mir: Es gibt ne Set in C++ - warum nicht einfach die nutzen?

    So ist der Code brutal simpel:

    Das Ergebnis in Form zweier DLL liegt im Anhang.

    Bei kleinen Bildern könnte der Code fix sein.
    Bei größeren Bildern womöglich langsamer als die vorgestellten Lösungen.

    Auch wenn es vielleicht nicht die Performance-Bestmarke knackt: Viel einfacher geht es im Grunde nicht mehr.

    Da ich bisschen neidisch auf die hier vorgestellten ASM-Lösungen schiele wollte ich dennoch noch was hier beitragen.

    Edit: Ach ja: 32 Bit-Bilder funktionieren hiermit ebenfalls.