1. Dashboard
  2. Mitglieder
    1. Letzte Aktivitäten
    2. Benutzer online
    3. Team
    4. Mitgliedersuche
  3. Forenregeln
  4. Forum
    1. Unerledigte Themen
  • Anmelden
  • Registrieren
  • Suche
Alles
  • Alles
  • Artikel
  • Seiten
  • Forum
  • Erweiterte Suche
  1. AutoIt.de - Das deutschsprachige Forum.
  2. Mitglieder
  3. Oscar

Beiträge von Oscar

  • GetUniqueColors

    • Oscar
    • 15. Dezember 2017 um 12:28
    Zitat von AspirinJunkie

    Wirklich fix für kleine Größen ist Insertion-Sort.

    Ok, Insertionsort ist tatsächlich schneller als Bubblesort!

    Die Kombination "Quick-/Insertionsort" bringt nochmal ca. 300 ms bei dem 33MP-Bild. Jetzt bin ich bei rund 2700 ms (32-Bit Single-Thread).

    Ich lasse jetzt Insertionsort ran, sobald weniger als 32 Pixel in einer Partition sind. Das hat sich bei Versuchen als schnellste Variante ergeben.

    Hier ist die neue Version:

    AutoIt
    #AutoIt3Wrapper_UseX64=n                          ; 32Bit-Modus
    #include <GDIPlus.au3>
    #include <Memory.au3>
    #include "assembleit2_64.au3"
    
    #Region ASM-Code
    #cs _CountUniqueColors                            ;
        Use32                                         ; 32Bit Modus!
        mov esi,dword[esp+4]                          ; esi = Pixelstruct-Pointer
        mov edx,dword[esp+8]                          ; edx = right & Pixelcounter
    ;~     _ASMDBG_()                                    ; debug-gui anzeigen
        dec edx                                       ; um eins verringern, weil die Pixelstruct bei 0 beginnt
        push edx                                      ; edx fuer die Zaehlschleife sichern
        xor ecx,ecx                                   ; ecx = left (auf 0 setzen)
        push edx ecx                                  ; right und left auf den Stack (fuer Quicksort)
        call quicksort                                ; Quicksort aufrufen (die Pixelstruct sortieren)
        pop edx                                       ; edx wiederherstellen
        mov eax,1                                     ; eax auf 1 setzen (Farbzaehler)
        mov ebx,dword[esi+edx*4]                      ; ebx = Farbwert des letzten Pixels (zum Farbvergleich)
        @count:                                       ; Zaehlschleife fuer die Farben
            dec edx                                   ; Pixelcounter runterzaehlen
            mov ecx,dword[esi+edx*4]                  ; ecx = Farbwert des vorletzten Pixels (zum Farbvergleich)
            cmp ecx,ebx                               ; ecx mit ebx vergleichen
            jae @next                                 ; wenn groesser/gleich, dann mit naechsten Pixel weitermachen
                inc eax                               ; wenn kleiner, dann Farbzaehler um eins erhoehen
                mov ebx,ecx                           ; und die dazugehoerige Farbe merken
            @next:
            cmp edx,0
            jnz @count                                ; wenn noch nicht 0, dann Schleife @count
        mov ecx,eax
        ret                                           ; Pixelcounter an AutoIt zurueckgeben (eax)
    
        quicksort:                                    ; die Quick-/Insertionsort-Funktion
            mov ecx,dword[esp+4]                      ; ecx = left
            mov edx,dword[esp+8]                      ; edx = right
            cmp ecx,edx                               ; left und right vergleichen
            jae @end                                  ; wenn left >= right, dann Funktion beenden
                mov eax,edx
                sub eax,ecx                           ; right - left
                cmp eax,32                            ; mehr als 32 Pixel?
                ja @quick                             ; wenn ja, dann Quicksort
                                                      ; Nein, dann Insertionsort
                    mov edi,ecx
                    inc edi                           ; edi = $i = left + 1
                    @fori:
                        mov ebx,dword[esi+edi*4]      ; ebx = Pixel[Insert]
                        mov eax,edi                   ; eax = $j = Insertpos
                        @forj:                        ; Einfuegeschleife
                            cmp eax,ecx               ; Anfang erreicht?
                            jbe @break1               ; Ja, dann @break1
                            dec eax                   ; j--
                            cmp dword[esi+eax*4],ebx  ; Pixel[j-1] < Pixel[Insert]
                            jbe @break2               ; Ja, dann @break2
                            movd xmm0,dword[esi+eax*4]; Pixel[j-1] holen
                            inc eax                   ; j++
                            movd dword[esi+eax*4],xmm0; als Pixel[j] speichern
                            dec eax                   ; j--
                            jmp @forj                 ; forj fortsetzen
                        @break2:
                            inc eax                   ; j++ (weil oben [j-1])
                        @break1:
                            mov dword[esi+eax*4],ebx  ; Pixel[Insert] nach Pixel[j] speichern
                            inc edi                   ; i++
                            cmp edi,edx               ; i > right
                            jbe @fori                 ; Nein, dann @fori
                    ret 8                             ; Insertionsort beendet, Funktion verlassen
                @quick:                               ; hier beginnt der Quicksort-Bereich
                push edx ecx                          ; edx und ecx auf den Stack (fuer Partition)
                call partition                        ; Partition aufrufen
                pop ecx edx                           ; ecx und edx wiederherstellen
                push ebx edx                          ; Register sichern
                push ebx ecx                          ; ebx und ecx auf den Stack (right und left fuer Quicksort)
                call quicksort                        ; Quicksort aufrufen (rekursiv)
                pop edx ebx                           ; Register wiederherstellen
                inc ebx                               ; ebx++
                push edx ebx                          ; edx und ebx auf den Stack (right und left fuer Quicksort)
                call quicksort                        ; Quicksort aufrufen (rekursiv)
            @end:
            ret 8
    
        partition:
            mov ecx,dword[esp+4]                      ; ecx = left
            mov edx,dword[esp+8]                      ; edx = right
            mov edi,dword[esi+ecx*4]                  ; edi = Pivotwert = Pixel[left]
            mov eax,ecx
            dec eax                                   ; eax = left - 1
            mov ebx,edx
            inc ebx                                   ; ebx = right + 1
            @loop:                                    ; Hauptschleife
                @left:                                ; Schleife fuer die linke Seite
                    inc eax                           ; left++
                    cmp dword[esi+eax*4],edi          ; Vergleich Pixel[left] mit Pivotwert
                    jb @left                          ; wenn kleiner, dann Schleife @left
                @right:                               ; Schleife fuer die rechte Seite
                    dec ebx                           ; right--
                    cmp dword[esi+ebx*4],edi          ; Vergleich Pixel[right] mit Pivotwert
                    ja @right                         ; wenn groesser, dann Schleife @right
                cmp eax,ebx                           ; Vergleich left und right
                jae @return                           ; wenn groesser/gleich, dann @return
                mov ecx,dword[esi+eax*4]              ; Pixel[left] gegen Pixel[right] austauschen
                mov edx,dword[esi+ebx*4]
                mov dword[esi+eax*4],edx
                mov dword[esi+ebx*4],ecx
                jmp @loop                             ; und mit @loop fortfahren
            @return:
            ret                                       ; right zurueckgeben (ebx)
    #ce
    #EndRegion ASM-Code
    
    ;~ $binarycode = _AssembleIt2("retbinary", "_CountUniqueColors") ;gibt nur den assemblierten code zurück
    ;~ ConsoleWrite('@@ Debug(' & @ScriptLineNumber & ') : $binarycode = ' & ($binarycode) & @CRLF & '>Error code: ' & @error & @CRLF) ;### Debug Console
    ;~ Exit
    Global $g_pMem, $g_iMemSize, $bCode, $tCodeBuffer, $hImage, $iW, $iH, $iTimer, $tBitmapData, $pScan0, $tPixel, $pPixel, $tColors, $pColors, $ret
    ;~ $bCode = "0x8B7424048B5424084A5231C95251E81C0000005AB8010000008B1C964A8B0C9639D973034089CB83FA0075F089C1C38B4C24048B54240839D1735189D029C883F820772C89CF478B1CBE89F839C8761548391C86760E660F6E048640660F7E048648EBE840891C864739D776DAC208005251E818000000595A53525351E8ADFFFFFF5A5B435253E8A3FFFFFFC208008B4C24048B5424088B3C8E89C84889D34340393C8672FA4B393C9E77FA39D8730E8B0C868B149E891486890C9EEBE2C3"
    ;~ $tCodeBuffer = _dllstructcreate64_("byte[" & StringLen($bCode) / 2 - 1 & "]") ;reserve Memory for opcodes
    ;~ DllStructSetData($tCodeBuffer, 1, $bCode)
    
    _GDIPlus_Startup()
    
    ;~ $hImage = _GDIPlus_BitmapCreateFromFile(@ScriptDir & '\test_4k.jpg')
    ;~ $hImage = _GDIPlus_BitmapCreateFromFile(@ScriptDir & '\palette.png')
    $hImage = _GDIPlus_BitmapCreateFromFile(@ScriptDir & '\test.png')
    
    $aDim =  _GDIPlus_ImageGetDimension($hImage)
    ConsoleWrite(StringFormat('_GetUniqueColors (ASM)\n%d x %d = %s px\n', $aDim[0], $aDim[1], $aDim[0] * $aDim[1]))
    $iTimer = TimerInit()
    
    $tBitmapData = _GDIPlus_BitmapLockBits($hImage, 0, 0, $aDim[0], $aDim[1], BitOR($GDIP_ILMREAD, $GDIP_ILMWRITE), $GDIP_PXF32ARGB)
    ConsoleWrite('Zeit (BitmapLockBits): ' & Round(TimerDiff($iTimer), 3) & ' ms' & @CR)
    
    $tPixel = DllStructCreate('dword[' & $tBitmapData.Width * $tBitmapData.Height & '];', $tBitmapData.Scan0) ; erstelle Pixelstruct (dword = 32 Bit pro Pixel)
    $pPixel = DllStructGetPtr($tPixel)
    ConsoleWrite('Zeit (Pixelstruct): ' & Round(TimerDiff($iTimer), 3) & ' ms' & @CR)
    
    ;~ $ret = DllCallAddress("uint:cdecl", DllStructGetPtr($tCodeBuffer), "ptr", $pPixel, "dword", $tBitmapData.Width * $tBitmapData.Height)
    ;~ $ret = $ret[0]
    $ret = _AssembleIt2("dword", "_CountUniqueColors", "ptr", $pPixel, "dword", $tBitmapData.Width * $tBitmapData.Height)
    ConsoleWrite('Anzahl der Farben = ' & $ret & @CR)
    ConsoleWrite('Zeit: ' & Round(TimerDiff($iTimer), 3) & ' ms' & @CR)
    
    _GDIPlus_BitmapUnlockBits($hImage, $tBitmapData)
    $tPixel = 0
    $tColors = 0
    $tBitmapData = 0
    _GDIPlus_BitmapDispose($hImage)
    _GDIPlus_Shutdown()
    ;~ _MemVirtualFree($g_pMem, $g_iMemSize, $MEM_DECOMMIT)
    Exit
    
    Func _dllstructcreate64_($struct) ;align auf 16-byte adresse
        Local $temp = DllStructCreate($struct)
        $g_iMemSize = DllStructGetSize($temp) + 64
        Local $ptr = DllStructGetPtr($struct)
        Local $a1 = Mod(Number($ptr), 64)
        Local $temp = 0
        $g_pMem = _MemVirtualAlloc($ptr + $a1, $g_iMemSize, $MEM_COMMIT, $PAGE_EXECUTE_READWRITE)
        Local $a2 = Mod(Number($g_pMem), 64) ;rest div 16 adresse = offset
        $sstruct = DllStructCreate($struct, (Number($g_pMem) - $a2 + 64))
        Return $sstruct ;auf 16 alingned pointer
    EndFunc   ;==>_dllstructcreate64_
    Alles anzeigen
  • GetUniqueColors

    • Oscar
    • 15. Dezember 2017 um 07:35
    Zitat von Andy

    Bringt im vorliegenden Fall nicht viel, aber einmal PUSH/POP ist eingespart. Im Debugger werden diese 128-Bit Register auch angezeigt incl. aller ihrer sowohl als auch integer oder float (4-Byte) Teile.

    Ah, ok!

    Dazu muss der Prozessor SSE unterstützen, oder? Die XMM-Register gehören doch zu der SSE-Unterstützung?

    Merken: Lieber in ein Register moven, als den Stack zu benutzen. :)

    Zitat von Andy

    Du kannst deinen ASM-code übrigens sehr einfach auch in AutoIt multithreadingfähig machen. Für deinen Algorithmus ist das ziemlich ungünstig, aber...

    Danke, für das Beispiel!

    Damit werde ich mich mal beschäftigen. Es ist zwar ungünstig, dass die Threads von AutoIt aus gestartet werden, aber wenn ich eine Interprozesskommunikation hinbekomme, dann sollte sich das sortieren beschleunigen lassen.

    Zitat von Andy

    Selbst für den vollen 32-Bit Farbraum, also incl. Alphakanal wären das gerade mal 500MB Speicher

    Naja, "gerade mal 500MB"? :/

    Auch wenn die meisten mittlerweile mehrere GB Hauptspeicher besitzen, finde ich die 500MB nur dafür ziemlich übertrieben.

    Ich werde mich da eher an der Multi-Thread-Version versuchen. Schon allein, weil mich das Thema mehr anspricht. :)

  • GetUniqueColors

    • Oscar
    • 14. Dezember 2017 um 19:51
    Zitat von AspirinJunkie

    Ergebnis bei mir: Für 32 Bit Bilder scheint die Multi-Thread-Quicksort-Variante die erste Wahl zu sein, für 24-Bit-Bilder die Single-Thread-Array-Variante.

    Kann ich bestätigen! Das ist auch bei mir die schnellste Variante. :thumbup:

    Wobei man bei Deinem Script nur die zu testende Variante auskommentieren darf. Alle hintereinander durchlaufen lassen verfälscht das Ergebnis, weil dann _GDIPlus_BitmapLockBits mit den gecachten Daten arbeitet.

    Meine 32-Bit ASM-Version ist immerhin schneller als Deine 32-Bit-Single-Thread-Quicksort-Version. Deine 64-Bit-Version zieht dann gleich. Von daher bin ich mit meiner ASM-Version schon sehr zufrieden. :)

    Die 64-Bit-Multi-Thread-Quicksort-Version ist aber drei Mal so schnell (auf meinem Quadcore-Rechner), wie meine Version.

    Und außerdem hat die ASM-Version den Vorteil, dass man sie einfacher in das AutoIt-Script packen kann und von der Größe (ASM-Version = 188 Byte) wollen wir erst gar nicht reden. :)

  • GetUniqueColors

    • Oscar
    • 14. Dezember 2017 um 09:16

    Kaum zu glauben, aber ich habe nochmal 300ms bei dem 33MP-Bild rausholen können. Die unten stehende Variante benötigt bei mir jetzt unter 3 Sekunden.

    Mein "Trick" ist der, dass ich eine Quick-/Bubblesort-Kombination einsetze (bei wenigen Elementen ist Bubblesort schneller als Quicksort).

    Wenn sich in den einzelnen Partitionen von Quicksort weniger als 10 Pixel (experimentell ermittelt) befinden, dann wird der Rest mit Bubblesort sortiert.

    Das spart diverse Rekursionen und damit auch Stackzugriffe ein.

    AutoIt
    #AutoIt3Wrapper_UseX64=n                          ; 32Bit-Modus
    #include <GDIPlus.au3>
    #include <Memory.au3>
    #include "assembleit2_64.au3"
    
    #Region ASM-Code
    #cs _CountUniqueColors                            ;
        Use32                                         ; 32Bit Modus!
        mov esi,dword[esp+4]                          ; esi = Pixelstruct-Pointer
        mov edx,dword[esp+8]                          ; edx = right & Pixelcounter
        dec edx                                       ; um eins verringern, weil die Pixelstruct bei 0 beginnt
        push edx                                      ; edx fuer die Zaehlschleife sichern
        xor ecx,ecx                                   ; ecx = left (auf 0 setzen)
        push edx                                      ; right auf den Stack (fuer Quicksort)
        push ecx                                      ; left auf den Stack (fuer Quicksort)
        call quicksort                                ; Quicksort aufrufen (die Pixelstruct sortieren)
        pop edx                                       ; edx wiederherstellen
        mov eax,1                                     ; eax auf 1 setzen (Farbzaehler)
        mov ebx,dword[esi+edx*4]                      ; ebx = Farbwert des letzten Pixels (zum Farbvergleich)
        @count:                                       ; Zaehlschleife fuer die Farben
            dec edx                                   ; Pixelcounter runterzaehlen
            mov ecx,dword[esi+edx*4]                  ; ecx = Farbwert des vorletzten Pixels (zum Farbvergleich)
            cmp ecx,ebx                               ; ecx mit ebx vergleichen
            jae @next                                 ; wenn groesser/gleich, dann mit naechsten Pixel weitermachen
                inc eax                               ; wenn kleiner, dann Farbzaehler um eins erhoehen
                mov ebx,ecx                           ; und die dazugehoerige Farbe merken
            @next:
            cmp edx,0
            jnz @count                                ; wenn noch nicht 0, dann Schleife @count
        mov ecx,eax
        ret                                           ; Pixelcounter an AutoIt zurueckgeben (eax)
    
        quicksort:                                    ; die Quick-/Bubblesort-Funktion
            mov ecx,dword[esp+4]                      ; ecx = left
            mov edx,dword[esp+8]                      ; edx = right
    ;~         _ASMDBG_()                                ; debug-gui anzeigen
            cmp ecx,edx                               ; left und right vergleichen
            jae @end                                  ; wenn left >= right, dann Funktion beenden
                mov eax,edx
                sub eax,ecx                           ; right - left
                cmp eax,10                            ; mehr als 10 Pixel?
                ja @quick                             ; wenn ja, dann Quicksort
                                                      ; Nein, dann Bubblesort
                    mov eax,ecx                       ; eax = loopleft
                    @loop1:                           ; aeussere Schleife vorwaerts
                        mov edi,dword[esi+eax*4]      ; edi = Pixel[loopleft]
                        mov ebx,edx                   ; ebx = loopright
                        @loop2:                       ; innere Schleife rueckwaerts
                            cmp edi,dword[esi+ebx*4]     ; mit Pixel[loopright] vergleichen
                            jbe @f                       ; kleiner/gleich, dann weiter
                                push edi                 ; Pixel[loopleft] sichern
                                mov edi,dword[esi+ebx*4] ; Pixel[loopright] holen
                                mov dword[esi+eax*4],edi ; nach Pixel[loopleft] schreiben
                                pop edi                  ; Pixel[loopleft] wiederherstellen
                                mov dword[esi+ebx*4],edi ; nach Pixel[loopright] schreiben
                                mov edi,dword[esi+eax*4] ; edi = Pixel[loopleft]
                            @@:
                            dec ebx                   ; loopright--
                            cmp ebx,eax               ; loopright mit loopleft vergleichen
                            ja @loop2                 ; wenn loopright > loopleft, dann @loop2
                        inc eax                       ; loopleft++
                        cmp eax,edx                   ; loopleft mit right (edx) vergleichen
                        jb @loop1                     ; wenn loopleft < right (edx), dann @loop1
                    ret 8                             ; Bubblesort beendet, Funktion verlassen
                @quick:                               ; hier beginnt der Quicksort-Bereich
                push edx                              ; edx auf den Stack (fuer Partition)
                push ecx                              ; ecx auf den Stack (fuer Partition)
                call partition                        ; Partition aufrufen
                pop ecx                               ; ecx wiederherstellen
                pop edx                               ; edx wiederherstellen
                push ebx                              ; ebx sichern
                push edx                              ; edx sichern
                push ecx                              ; ecx sichern
                push ebx                              ; ebx auf den Stack (right fuer Quicksort)
                push ecx                              ; ecx auf den Stack (left fuer Quicksort)
                call quicksort                        ; Quicksort aufrufen (rekursiv)
                pop ecx                               ; ecx wiederherstellen
                pop edx                               ; edx wiederherstellen
                pop ebx                               ; ebx wiederherstellen
                inc ebx                               ; ebx++
                push edx                              ; edx auf den Stack (right fuer Quicksort)
                push ebx                              ; ebx auf den Stack (left fuer Quicksort)
                call quicksort                        ; Quicksort aufrufen (rekursiv)
            @end:
            ret 8
    
        partition:
            mov ecx,dword[esp+4]                      ; ecx = left
            mov edx,dword[esp+8]                      ; edx = right
            mov edi,dword[esi+ecx*4]                  ; edi = Pivotwert = Pixel[left]
            mov eax,ecx
            dec eax                                   ; eax = left - 1
            mov ebx,edx
            inc ebx                                   ; ebx = right + 1
            @loop:                                    ; Hauptschleife
                @left:                                ; Schleife fuer die linke Seite
                    inc eax                           ; left++
                    cmp dword[esi+eax*4],edi          ; Vergleich mit Pivotwert
                    jb @left                          ; wenn kleiner, dann Schleife @left
                @right:                               ; Schleife fuer die rechte Seite
                    dec ebx                           ; right--
                    cmp dword[esi+ebx*4],edi          ; Vergleich mit Pivotwert
                    ja @right                         ; wenn groesser, dann Schleife @right
                cmp eax,ebx                           ; Vergleich left und right
                jae @return                           ; wenn groesser/gleich, dann @return
                mov ecx,dword[esi+eax*4]              ; Pixel[left] gegen Pixel[right] austauschen
                mov edx,dword[esi+ebx*4]
                mov dword[esi+eax*4],edx
                mov dword[esi+ebx*4],ecx
                jmp @loop                             ; und mit @loop fortfahren
            @return:
            ret                                       ; right zurueckgeben (ebx)
    #ce
    #EndRegion ASM-Code
    
    ;~ $binarycode = _AssembleIt2("retbinary", "_CountUniqueColors") ;gibt nur den assemblierten code zurück
    ;~ ConsoleWrite('@@ Debug(' & @ScriptLineNumber & ') : $binarycode = ' & ($binarycode) & @CRLF & '>Error code: ' & @error & @CRLF) ;### Debug Console
    ;~ Exit
    Global $g_pMem, $g_iMemSize, $bCode, $tCodeBuffer, $hImage, $iW, $iH, $iTimer, $tBitmapData, $pScan0, $tPixel, $pPixel, $tColors, $pColors, $ret
    ;~ $bCode = "0x8B7424048B5424084A5231C95251E81C0000005AB8010000008B1C964A8B0C9639D973034089CB83FA0075F089C1C38B4C24048B54240839D1734E89D029C883F80A772789C88B3C8689D33B3C9E760E578B3C9E893C865F893C9E8B3C864B39C377E84039D072DEC208005251E81A000000595A5352515351E8B1FFFFFF595A5B435253E8A6FFFFFFC208008B4C24048B5424088B3C8E89C84889D34340393C8672FA4B393C9E77FA39D8730E8B0C868B149E891486890C9EEBE2C3"
    ;~ $tCodeBuffer = _dllstructcreate64_("byte[" & StringLen($bCode) / 2 - 1 & "]") ;reserve Memory for opcodes
    ;~ DllStructSetData($tCodeBuffer, 1, $bCode)
    
    _GDIPlus_Startup()
    
    $hImage = _GDIPlus_BitmapCreateFromFile(@ScriptDir & '\test_g.jpg')
    ;~ $hImage = _GDIPlus_BitmapCreateFromFile(@ScriptDir & '\palette.png')
    ;~ $hImage = _GDIPlus_BitmapCreateFromFile(@ScriptDir & '\test.png')
    
    $aDim =  _GDIPlus_ImageGetDimension($hImage)
    ConsoleWrite(StringFormat('_GetUniqueColors (ASM)\n%d x %d = %s px\n', $aDim[0], $aDim[1], $aDim[0] * $aDim[1]))
    $iTimer = TimerInit()
    
    $tBitmapData = _GDIPlus_BitmapLockBits($hImage, 0, 0, $aDim[0], $aDim[1], BitOR($GDIP_ILMREAD, $GDIP_ILMWRITE), $GDIP_PXF32ARGB)
    ConsoleWrite('Zeit (BitmapLockBits): ' & Round(TimerDiff($iTimer), 3) & ' ms' & @CR)
    
    $tPixel = DllStructCreate('dword[' & $tBitmapData.Width * $tBitmapData.Height & '];', $tBitmapData.Scan0) ; erstelle Pixelstruct (dword = 32 Bit pro Pixel)
    $pPixel = DllStructGetPtr($tPixel)
    ConsoleWrite('Zeit (Pixelstruct): ' & Round(TimerDiff($iTimer), 3) & ' ms' & @CR)
    
    ;~ $ret = DllCallAddress("uint:cdecl", DllStructGetPtr($tCodeBuffer), "ptr", $pPixel, "dword", $tBitmapData.Width * $tBitmapData.Height)
    ;~ $ret = $ret[0]
    $ret = _AssembleIt2("dword", "_CountUniqueColors", "ptr", $pPixel, "dword", $tBitmapData.Width * $tBitmapData.Height)
    ConsoleWrite('Anzahl der Farben = ' & $ret & @CR)
    ConsoleWrite('Zeit: ' & Round(TimerDiff($iTimer), 3) & ' ms' & @CR)
    _GDIPlus_BitmapUnlockBits($hImage, $tBitmapData)
    $tPixel = 0
    $tColors = 0
    $tBitmapData = 0
    _GDIPlus_BitmapDispose($hImage)
    _GDIPlus_Shutdown()
    ;~ _MemVirtualFree($g_pMem, $g_iMemSize, $MEM_DECOMMIT)
    Exit
    
    Func _dllstructcreate64_($struct) ;align auf 16-byte adresse
        Local $temp = DllStructCreate($struct)
        $g_iMemSize = DllStructGetSize($temp) + 64
        Local $ptr = DllStructGetPtr($struct)
        Local $a1 = Mod(Number($ptr), 64)
        Local $temp = 0
        $g_pMem = _MemVirtualAlloc($ptr + $a1, $g_iMemSize, $MEM_COMMIT, $PAGE_EXECUTE_READWRITE)
        Local $a2 = Mod(Number($g_pMem), 64) ;rest div 16 adresse = offset
        $sstruct = DllStructCreate($struct, (Number($g_pMem) - $a2 + 64))
        Return $sstruct ;auf 16 alingned pointer
    EndFunc   ;==>_dllstructcreate64_
    Alles anzeigen
  • GetUniqueColors

    • Oscar
    • 13. Dezember 2017 um 19:54
    Zitat von Andy

    zunächst: TOP(!!) Leistung.

    Danke! Als Assembler-Anfänger hört man das gern. :)

    Ich habe zwar schonmal in Assembler programiert, aber das war auf dem C64. :D

    Zitat von Andy

    Jetzt noch ein ganz privates Statement meinerseits....DANKE! Ich war all die Jahre zu faul, einen Quicksort in ASM zu erstellen, ich werde mich in Zukunft an deinem Code bedienen

    Oh, das hätte ich jetzt nicht gedacht!

    Darfst Dich aber gern bedienen.:)

    Zitat von Andy

    Das ist auch der Hauptgrund für die vergleichbare "Langsamkeit". Stack heißt Speicherzugriffe, und die kosten Zeit. Schlimmstenfalls liegen die rechten/linken Pixeladressen nicht im Cache und dann tritt der Supergau auf: Cachemiss. Das kostet je nach Prozessor massig Takte!

    Daran ist aber nicht deine Umsetzung schuld, sondern der für die landläufige PC-Architektur "langsame" (sprich Speicherzugriffstechnisch aufwendige) Quicksort-Algorithmus.

    Mir würde jetzt auf Anhieb nicht einfallen, wie man vorliegenden Code stark beschleunigen könnte.

    Ok!

    Bei der "Partition-Funktion" habe ich noch zwei Stackzugriffe entfernen können. Ich muss die einfach am Ende nicht vom Stack löschen, dann brauche ich sie nicht doppelt auf den Stack packen.

    Bei der rekursiven Quicksort geht das hingegen nicht, weil ich einerseits die ecx,edx-Werte brauche und zum anderen aber den ebx-Wert als right bzw. left uebergeben muss.

    Aber mir ist da gerade noch etwas eingefallen, wie man evtl. noch Stackzugriffe reduzieren kann.

    Ich werde aber erst morgen dazu kommen, das auszutesten. Mal sehen, ob es was bringt...

  • GetUniqueColors

    • Oscar
    • 13. Dezember 2017 um 18:21

    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:

    AutoIt
    #AutoIt3Wrapper_UseX64=n                          ; 32Bit-Modus
    #include <GDIPlus.au3>
    #include <Memory.au3>
    #include "assembleit2_64.au3"
    
    #Region ASM-Code
    #cs _CountUniqueColors                            ;
        Use32                                         ; 32Bit Modus!
    ;~     org $PTR_SOURCE_ASMCODE                       ; only needed for assembleit debugger
        mov esi,dword[esp+4]                          ; esi = Pixelstruct-Pointer
        mov edx,dword[esp+8]                          ; edx = right & Pixelcounter
        dec edx                                       ; um eins verringern, weil die Pixelstruct bei 0 beginnt
        push edx                                      ; edx fuer die Zaehlschleife sichern
        xor ecx,ecx                                   ; ecx = left (auf 0 setzen)
        push edx                                      ; right auf den Stack (fuer Quicksort)
        push ecx                                      ; left auf den Stack (fuer Quicksort)
        call quicksort                                ; Quicksort aufrufen (die Pixelstruct sortieren)
        pop edx                                       ; edx wiederherstellen
        mov eax,1                                     ; eax auf 1 setzen (Farbzaehler)
        mov ebx,dword[esi+edx*4]                      ; ebx = Farbwert des letzten Pixels (zum Farbvergleich)
        @count:                                       ; Zaehlschleife fuer die Farben
            dec edx                                   ; Pixelcounter runterzaehlen
            mov ecx,dword[esi+edx*4]                  ; ecx = Farbwert des vorletzten Pixels (zum Farbvergleich)
            cmp ecx,ebx                               ; ecx mit ebx vergleichen
            jae @next                                 ; wenn groesser/gleich, dann mit naechsten Pixel weitermachen
                inc eax                               ; wenn kleiner, dann Farbzaehler um eins erhoehen
                mov ebx,ecx                           ; und die dazugehoerige Farbe merken
            @next:
            cmp edx,0
            jnz @count                                ; wenn noch nicht 0, dann Schleife @count
        ret                                           ; Pixelcounter an AutoIt zurueckgeben (eax)
    
        quicksort:                                    ; die Quicksort-Funktion
            mov ecx,dword[esp+4]                      ; ecx = left
            mov edx,dword[esp+8]                      ; edx = right
            cmp ecx,edx                               ; left und right vergleichen
            jae @end                                  ; wenn groesser/gleich, dann Funktion beenden
    ;~             _ASMDBG_()                            ; debug-gui anzeigen
                push edx                              ; edx sichern
                push ecx                              ; ecx sichern
                push edx                              ; edx auf den Stack (fuer Partition)
                push ecx                              ; ecx auf den Stack (fuer Partition)
                call partition                        ; Partition aufrufen
                pop ecx                               ; ecx wiederherstellen
                pop edx                               ; edx wiederherstellen
                push ebx                              ; ebx sichern
                push edx                              ; edx sichern
                push ecx                              ; ecx sichern
                push ebx                              ; eax auf den Stack (right fuer Quicksort)
                push ecx                              ; ecx auf den Stack (left fuer Quicksort)
                call quicksort                        ; Quicksort aufrufen (rekursiv)
                pop ecx                               ; ecx wiederherstellen
                pop edx                               ; edx wiederherstellen
                pop ebx                               ; ebx wiederherstellen
                inc ebx                               ; ebx++
                push edx                              ; edx auf den Stack (right fuer Quicksort)
                push ebx                              ; ebx auf den Stack (left fuer Quicksort)
                call quicksort                        ; Quicksort aufrufen (rekursiv)
            @end:
            ret 8
    
        partition:
            mov ecx,dword[esp+4]                      ; ecx = left
            mov edx,dword[esp+8]                      ; edx = right
            mov edi,dword[esi+ecx*4]                  ; edi = Pivotwert = Pixel[left]
            mov eax,ecx
            dec eax                                   ; eax = left - 1
            mov ebx,edx
            inc ebx                                   ; ebx = right + 1
    ;~         _ASMDBG_()                                ; debug-gui anzeigen
            @loop:                                    ; Hauptschleife
                @left:                                ; Schleife fuer die linke Seite
                    inc eax                           ; left++
                    cmp dword[esi+eax*4],edi          ; Vergleich mit Pivotwert
                    jb @left                          ; wenn kleiner, dann Schleife @left
                @right:                               ; Schleife fuer die rechte Seite
                    dec ebx                           ; right--
                    cmp dword[esi+ebx*4],edi          ; Vergleich mit Pivotwert
                    ja @right                         ; wenn groesser, dann Schleife @right
                cmp eax,ebx                           ; Vergleich left und right
                jae @return                           ; wenn groesser/gleich, dann @return
                mov ecx,dword[esi+eax*4]              ; Pixel[left] gegen Pixel[right] austauschen
                mov edx,dword[esi+ebx*4]
                mov dword[esi+eax*4],edx
                mov dword[esi+ebx*4],ecx
                jmp @loop                             ; und mit @loop fortfahren
            @return:
            ret 8                                     ; right zurueckgeben (ebx)
    #ce
    #EndRegion ASM-Code
    
    ;~ $binarycode = _AssembleIt2("retbinary", "_CountUniqueColors") ;gibt nur den assemblierten code zurück
    ;~ ConsoleWrite('@@ Debug(' & @ScriptLineNumber & ') : $binarycode = ' & ($binarycode) & @CRLF & '>Error code: ' & @error & @CRLF) ;### Debug Console
    ;~ Exit
    Global $g_pMem, $g_iMemSize, $bCode, $tCodeBuffer, $hImage, $iW, $iH, $iTimer, $tBitmapData, $pScan0, $tPixel, $pPixel, $tColors, $pColors, $ret
    ;~ $bCode = "0x8B7424048B5424084A5231C95251E81D0000005A31C08B1C964A8B0C9639D97201408B0C9639D973034089CB4A75F3C38B4C24048B54240839D1732052515251E81A000000595A5352515351E8DFFFFFFF595A5B435253E8D4FFFFFFC208008B4C24048B5424088B3C8E89C84889D34340393C8672FA4B393C9E77FA39D8730E8B0C868B149E891486890C9EEBE2C20800"
    ;~ $tCodeBuffer = _dllstructcreate64_("byte[" & StringLen($bCode) / 2 - 1 & "]") ;reserve Memory for opcodes
    ;~ DllStructSetData($tCodeBuffer, 1, $bCode)
    
    _GDIPlus_Startup()
    
    ;~ $hImage = _GDIPlus_BitmapCreateFromFile(@ScriptDir & '\test_g.jpg')
    $hImage = _GDIPlus_BitmapCreateFromFile(@ScriptDir & '\palette.png')
    ;~ $hImage = _GDIPlus_BitmapCreateFromFile(@ScriptDir & '\test.png')
    
    $aDim =  _GDIPlus_ImageGetDimension($hImage)
    ConsoleWrite(StringFormat('_GetUniqueColors (ASM)\n%d x %d = %s px\n', $aDim[0], $aDim[1], $aDim[0] * $aDim[1]))
    $iTimer = TimerInit()
    
    $tBitmapData = _GDIPlus_BitmapLockBits($hImage, 0, 0, $aDim[0], $aDim[1], BitOR($GDIP_ILMREAD, $GDIP_ILMWRITE), $GDIP_PXF32ARGB)
    ConsoleWrite('Zeit (BitmapLockBits): ' & Round(TimerDiff($iTimer), 3) & ' ms' & @CR)
    
    $tPixel = DllStructCreate('dword[' & $tBitmapData.Width * $tBitmapData.Height & '];', $tBitmapData.Scan0) ; erstelle Pixelstruct (dword = 32 Bit pro Pixel)
    $pPixel = DllStructGetPtr($tPixel)
    ConsoleWrite('Zeit (Pixelstruct): ' & Round(TimerDiff($iTimer), 3) & ' ms' & @CR)
    
    ;~ $ret = DllCallAddress("uint:cdecl", DllStructGetPtr($tCodeBuffer), "ptr", $pPixel, "dword", $tBitmapData.Width * $tBitmapData.Height)
    ;~ $ret = $ret[0]
    $ret = _AssembleIt2("dword", "_CountUniqueColors", "ptr", $pPixel, "dword", $tBitmapData.Width * $tBitmapData.Height)
    ConsoleWrite('Anzahl der Farben = ' & $ret & @CR)
    ConsoleWrite('Zeit: ' & Round(TimerDiff($iTimer), 3) & ' ms' & @CR)
    
    _GDIPlus_BitmapUnlockBits($hImage, $tBitmapData)
    $tPixel = 0
    $tColors = 0
    $tBitmapData = 0
    _GDIPlus_BitmapDispose($hImage)
    _GDIPlus_Shutdown()
    ;~ _MemVirtualFree($g_pMem, $g_iMemSize, $MEM_DECOMMIT)
    Exit
    
    Func _dllstructcreate64_($struct) ;align auf 16-byte adresse
        Local $temp = DllStructCreate($struct)
        $g_iMemSize = DllStructGetSize($temp) + 64
        Local $ptr = DllStructGetPtr($struct)
        Local $a1 = Mod(Number($ptr), 64)
        Local $temp = 0
        $g_pMem = _MemVirtualAlloc($ptr + $a1, $g_iMemSize, $MEM_COMMIT, $PAGE_EXECUTE_READWRITE)
        Local $a2 = Mod(Number($g_pMem), 64) ;rest div 16 adresse = offset
        $sstruct = DllStructCreate($struct, (Number($g_pMem) - $a2 + 64))
        Return $sstruct ;auf 16 alingned pointer
    EndFunc   ;==>_dllstructcreate64_
    Alles anzeigen

    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?

  • Hot Hot Hotfolder?

    • Oscar
    • 9. Dezember 2017 um 09:38
    Zitat von Peter S. Taler

    Da stellt sich eigentlich schon mal die Frage Bug oder Feature? Für mich sollte ein File erst existieren wenn es auch wirklich "vorliegt"

    Ich denke, dass das kein Bug ist, denn die Datei existiert ja bereits. FileExists macht somit genau das, was der Befehl aussagt.

    Bei der Event-Methode werden beim kopieren einer Datei deswegen auch zwei Events ausgelöst. Einmal $SHCNE_CREATE und dann $SHCNE_UPDATEITEM.

  • Geschwindigkeit eines Scripts

    • Oscar
    • 8. Dezember 2017 um 20:06

    [Kopf schüttel] Leute gibt es!

    Eine "Geld-Verdien-Idee" mit PixelSearch?! Ich lach mich tot! :D;(

  • Geschwindigkeit eines Scripts

    • Oscar
    • 8. Dezember 2017 um 17:01

    Wenn Du ein eigenes Spiel programmierst, dann weisst Du, wo sich die Objekte aufhalten und brauchst gar kein Pixelsearch.

    Für den Fall, dass das ein Gamebot werden soll, verweise ich auf unsere Forenregeln!

  • Hot Hot Hotfolder?

    • Oscar
    • 7. Dezember 2017 um 19:11

    Mist, da schreibe ich gerade noch an einem kleinen Beispiel und ihr kloppt hier alles voll mit Lösungen. :)

    Na egal, dann gibt es halt eine Lösung mehr:

    AutoIt
    #include <APIShellExConstants.au3>
    #include <GUIConstantsEx.au3>
    #include <GuiListView.au3>
    #include <MsgBoxConstants.au3>
    #include <WinAPI.au3>
    #include <WinAPIShellEx.au3>
    
    #cs Moegliche Werte bei den Events von _WinAPI_ShellChangeNotify()
        Global Const $SHCNE_ALLEVENTS = 0x7FFFFFFF
        Global Const $SHCNE_ASSOCCHANGED = 0x8000000
        Global Const $SHCNE_ATTRIBUTES = 0x00000800
        Global Const $SHCNE_CREATE = 0x00000002
        Global Const $SHCNE_DELETE = 0x00000004
        Global Const $SHCNE_DRIVEADD = 0x00000100
        Global Const $SHCNE_DRIVEADDGUI = 0x00010000
        Global Const $SHCNE_DRIVEREMOVED = 0x00000080
        Global Const $SHCNE_EXTENDED_EVENT = 0x04000000
        Global Const $SHCNE_FREESPACE = 0x00040000
        Global Const $SHCNE_MEDIAINSERTED = 0x00000020
        Global Const $SHCNE_MEDIAREMOVED = 0x00000040
        Global Const $SHCNE_MKDIR = 0x00000008
        Global Const $SHCNE_NETSHARE = 0x00000200
        Global Const $SHCNE_NETUNSHARE = 0x00000400
        Global Const $SHCNE_RENAMEFOLDER = 0x00020000
        Global Const $SHCNE_RENAMEITEM = 0x00000001
        Global Const $SHCNE_RMDIR = 0x00000010
        Global Const $SHCNE_SERVERDISCONNECT = 0x00004000
        Global Const $SHCNE_UPDATEDIR = 0x00001000
        Global Const $SHCNE_UPDATEIMAGE = 0x00008000
        Global Const $SHCNE_UPDATEITEM = 0x00002000
        Global Const $SHCNE_DISKEVENTS = 0x0002381F
        Global Const $SHCNE_GLOBALEVENTS = 0x0C0581E0
        Global Const $SHCNE_INTERRUPT = 0x80000000
    #ce
    
    OnAutoItExitRegister('OnAutoItExit')
    Opt('GUIOnEventMode', 1)
    
    Global $sNotifyDir = @ScriptDir ; das zu ueberwachende Verzeichnis
    Global $bRecursive = True ; True = mit Unterverzeichnissen
    
    Global $hGui = GUICreate('Test', 800, 400)
    GUISetOnEvent($GUI_EVENT_CLOSE, '_CloseGui') ; Funktion die beim schließen des Fensters aufgerufen wird
    Global $idListview = GUICtrlCreateListView('Event|Pfad', 10, 10, 780, 380)
    
    
    Global $iNotifyMsg = _WinAPI_RegisterWindowMessage('SHELLCHANGENOTIFY') ; die Message bei Windows registrieren
    GUIRegisterMsg($iNotifyMsg, 'WM_SHELLCHANGENOTIFY') ; das Fenster für den Empfang der Message registrieren
    Global $iID = _WinAPI_ShellChangeNotifyRegister($hGui, $iNotifyMsg, $SHCNE_ALLEVENTS, BitOR($SHCNRF_INTERRUPTLEVEL, $SHCNRF_SHELLLEVEL, $SHCNRF_RECURSIVEINTERRUPT), $sNotifyDir, $bRecursive) ; Windows mitteilen welches Verzeichnis überwacht werden soll
    If @error Then ; wenn das fehlschlägt, dann...
        MsgBox(BitOR($MB_ICONERROR, $MB_SYSTEMMODAL), 'Fehler', '"ShellChangeNotifyRegister" ist fehlgeschlagen.')
        Exit ; Programm mit Fehlermeldung beenden
    EndIf
    GUISetState()
    WinWaitClose($hGui) ; warten bis das Fenster geschlossen wird
    
    Func _CloseGui()
        Exit
    EndFunc   ;==>_CloseGui
    
    Func OnAutoItExit()
        If $iID Then _WinAPI_ShellChangeNotifyDeregister($iID)
    EndFunc   ;==>OnAutoItExit
    
    Func WM_SHELLCHANGENOTIFY($hWnd, $iMsg, $wParam, $lParam)
        #forceref $hWnd, $iMsg
        ; diese Funktion wird aufgerufen, wenn sich im überwachten Verzeichnis (@ScriptDir) eine Datei ändert
        Local $sPath = _WinAPI_ShellGetPathFromIDList(DllStructGetData(DllStructCreate('dword Item1; dword Item2', $wParam), 'Item1'))
        GUICtrlCreateListViewItem(StringFormat('0x%s|%s', Hex($lParam, 8), $sPath), $idListview)
        _GUICtrlListView_SetColumnWidth($idListview, 0, $LVSCW_AUTOSIZE)
        _GUICtrlListView_SetColumnWidth($idListview, 1, $LVSCW_AUTOSIZE)
    EndFunc   ;==>WM_SHELLCHANGENOTIFY
    Alles anzeigen
  • Anfrage für Website Parsing

    • Oscar
    • 7. Dezember 2017 um 13:11
    Zitat von elaaar

    Gerne auch in Java...

    In einem AutoIt-Forum wirst Du vielleicht nicht so viele Java-Programmierer erreichen. ;)

  • 1+2+3+4 nun will ich ein Bildchen

    • Oscar
    • 7. Dezember 2017 um 13:09

    Ich hatte gerade mal etwas Zeit über:

    AutoIt
    #include <GDIPlus.au3>
    
    _SaveNumberPic('e-Rechnung', 4711, 0xFF0000DD, 0xFF202020) ; Farben im ARGB-Format
    
    Func _SaveNumberPic($sText, $iNumber, $iTextColor = 0xFF000000, $iNumberColor = 0xFF000000, $iBkColor = 0xFFFFFFFF, $iWidth = 260, $iHeight = 110)
        _GDIPlus_Startup()
        Local $sFilename, $sNumber, $hBitmap, $hGfxCtxt, $hBrushText, $hBrushNumber, $hFormat, $hFamily, $hFont, $tLayout
        $sFilename = StringFormat('%s\%s_%06i.png', @ScriptDir, $sText, $iNumber) ; Dateiname generieren (als png = bessere Qualitaet und kleinere Dateigroesse)
        $sNumber = StringFormat('%06i', $iNumber) ; die Nummer formatieren
        $hBitmap = _GDIPLUS_BitmapCreateFromScan0($iWidth, $iHeight) ; Bitmap erstellen
        $hGfxCtxt = _GDIPlus_ImageGetGraphicsContext($hBitmap) ; Graphic-Context der Bitmap
        _GDIPlus_GraphicsClear($hGfxCtxt, $iBkColor) ; die Graphic mit der Hintergrundfarbe fuellen
        _GDIPlus_GraphicsSetInterpolationMode($hGfxCtxt, 7) ; Interpolationsmodus auf beste Qualitaet setzen
        _GDIPlus_GraphicsSetTextRenderingHint($hGfxCtxt, 3) ; Textrendering auf "antialiased glyph bitmap and hinting"
        $hPen = _GDIPlus_PenCreate($iTextColor, 4) ; die Farbe und Staerke fuer den Rahmen festlegen
        _GDIPlus_GraphicsDrawRect($hGfxCtxt, 2, 2, $iWidth - 4, $iHeight - 4, $hPen) ; den Rahmen auf die Graphic zeichnen
        $hBrushText = _GDIPlus_BrushCreateSolid($iTextColor) ; die Farbe fuer den Text festlegen
        $hBrushNumber = _GDIPlus_BrushCreateSolid($iNumberColor) ; die Farbe fuer die Nummer festlegen
        $hFormat = _GDIPlus_StringFormatCreate() ; Textformat erstellen
        _GDIPlus_StringFormatSetAlign($hFormat, 1) ; Text horizontal zentrieren
        $hFamily = _GDIPlus_FontFamilyCreate('Arial') ; Zeichensatz festlegen
        $hFont = _GDIPlus_FontCreate($hFamily, 24, 1) ; Zeichensatzgroesse und -stil fuer den Text festlegen
        $tLayout = _GDIPlus_RectFCreate(0, 5, $iWidth, 40) ; RECT-Struktur fuer _GDIPlus_GraphicsDrawStringEx erstellen
        _GDIPlus_GraphicsDrawStringEx($hGfxCtxt, $sText, $hFont, $tLayout, $hFormat, $hBrushText) ; den Text in die Graphic zeichnen
        _GDIPlus_FontDispose($hFont) ; die Zeichensatzeigenschaften freigeben
        $hFont = _GDIPlus_FontCreate($hFamily, 42, 1) ; Zeichensatzgroesse und -stil fuer die Nummer festlegen
        $tLayout = _GDIPlus_RectFCreate(0, 45, $iWidth, 60) ; RECT-Struktur fuer _GDIPlus_GraphicsDrawStringEx erstellen
        _GDIPlus_GraphicsDrawStringEx($hGfxCtxt, $sNumber, $hFont, $tLayout, $hFormat, $hBrushNumber) ; die Nummer in die Graphic zeichnen
        _GDIPlus_ImageSaveToFile($hBitmap, $sFilename) ; die Bitmap speichern
        ; die verwendeten Ressourcen wieder freigeben
        $tLayout = 0
        _GDIPlus_FontDispose($hFont)
        _GDIPlus_FontFamilyDispose($hFamily)
        _GDIPlus_StringFormatDispose($hFormat)
        _GDIPlus_BrushDispose($hBrushNumber)
        _GDIPlus_BrushDispose($hBrushText)
        _GDIPlus_PenDispose($hPen)
        _GDIPlus_GraphicsDispose($hGfxCtxt)
        _GDIPlus_BitmapDispose($hBitmap)
        _GDIPlus_Shutdown()
    EndFunc
    Alles anzeigen

    Als PNG wird's kleiner und sieht besser aus.

    Außerdem habe ich mal alles kommentiert, damit Du das evtl. noch anpassen kannst. So nebenbei lernst Du gleich noch einiges über GDI+.

  • Gui show bei WinActiv und Gui hide bei WinClose

    • Oscar
    • 3. Dezember 2017 um 09:09
    Zitat von schandor

    Sobald das Fenster mit dem Ersetzen-Dialog geöffnet ist, öffnet sich mein GUI und schließt sich im Sekundentakt.

    Du fragst ja auch ab, ob das Fenster aktiv ist!

    Wenn Deine GUI aufgeht, ist diese aber das aktive Fenster.

    Statt WinActive benutze lieber WinExists.

    Edit: Falls das Fenster immer existiert (nur nicht sichtbar ist), dann musst Du den Status des Fensters mit WinGetState ermitteln.

  • GetUniqueColors

    • Oscar
    • 30. November 2017 um 05:23
    Zitat von UEZ

    Probiere bitte mal diese Exe.

    Ja, mit der Version funktioniert es, ohne Absturz:

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

    • Oscar
    • 29. November 2017 um 19:43

    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:

    AutoIt
    #include <GDIPlus.au3>
    _GDIPlus_Startup()
    $iW = 7680
    $iH = 4320
    $hImage = _GDIPlus_BitmapCreateFromScan0($iW, $iH)
    Global $iTimer = TimerInit(), $iColor = 0, $iTimerDiff = TimerDiff($iTimer), $iAlpha
    For $iY = 0 To $iH - 1
        For $iX = 0 To $iW - 1
            _GDIPlus_BitmapSetPixel($hImage, $iX, $iY, BitShift(Mod($iAlpha, 256), -24) + $iColor)
            $iColor += 1
            $iAlpha += 1
            If TimerDiff($iTimer) - $iTimerDiff > 250 Then
                $iTimerDiff = TimerDiff($iTimer)
                ToolTip($iColor)
            EndIf
        Next
    Next
    ConsoleWrite('@@ Debug(' & @ScriptLineNumber & ') : TimerDiff($iTimer) = ' & TimerDiff($iTimer) & @CRLF & '>Error code: ' & @error & @CRLF) ;### Debug Console
    _GDIPlus_ImageSaveToFile($hImage, @ScriptDir & '\Test.png')
    _GDIPlus_BitmapDispose($hImage)
    _GDIPlus_Shutdown()
    Alles anzeigen

    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).

  • Prüfen ob Datei schon vorhanden

    • Oscar
    • 26. November 2017 um 11:10

    Mit "_WinAPI_ShellFileOperation" musst Du Dich nicht selbst um die Abfragen (überschreiben, umbenennen, etc.) kümmern. Das übernimmt Windows für Dich.

    Hier mal ein Beispiel:

    AutoIt
    #include <APIShellExConstants.au3>
    #include <File.au3>
    #include <WinAPIShellEx.au3>
    
    Global $sSourceDir = @ScriptDir & '\source\'
    Global $sDestDir = @ScriptDir & '\dest\'
    
    Global $aFilesFolder = _FileListToArray($sSourceDir, '*')
    
    For $i = 1 To $aFilesFolder[0]
        _WinAPI_ShellFileOperation($aFilesFolder[$i], $sDestDir, $FO_COPY, 0, 'Copy-Tool')
    Next
    Alles anzeigen

    Wenn Du das Script zwei Mal aufrufst (vorausgesetzt Du erstellst vorher das Quellverzeichnis und kopierst etwas hinein), wird automatisch nachgefragt, was mit den bereits vorhandene Datein im Zielverzeichnis passieren soll.

  • GetUniqueColors

    • Oscar
    • 26. November 2017 um 09:20
    Zitat von AspirinJunkie

    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:

  • GetUniqueColors

    • Oscar
    • 25. November 2017 um 09:05
    Zitat von AspirinJunkie

    Edit: So nun doch eine Variante gefunden welche zumindest bei großen Bildern bessere Performance liefern kann und dennoch korrekte Werte liefert (über die Windows-API-Funktion InterlockedIncrement16 umgesetzt).

    Wow! Das ist jetzt richtig schnell! :):thumbup:

    Für das große Bild (4608 x 3456 px) braucht die Multi-Thread-Array-Version jetzt nur noch ca. 32 ms.

    Und auch die Single-Thread-Array-Version schlägt jetzt die ASM-Version.

    Bezüglich der "nur 24Bit": Wenn man "_GDIPlus_BitmapLockBits" mit "$GDIP_PXF32PARGB", also PARGB aufruft, dann werden die Alphawerte doch vorher auf die RGB-Werte umgerechnet und somit müsste die Anzahl der Farben wieder stimmen, oder liege ich da falsch?

  • GetUniqueColors

    • Oscar
    • 24. November 2017 um 19:14

    Das hier hat sich als recht schnell erwiesen:

    C
    #include <stdbool.h>
    #include <stdlib.h>
    __declspec(dllexport) unsigned int CountUniqueColors(unsigned int* pixel, unsigned int len) {
        unsigned int i, iColor, colorSum = 0;
        bool* colors = calloc(0xffffff + 1, sizeof(bool));
        for(i = 0; i < len; i++) {
            if (colors[pixel[i] & 0xffffff] == 0){
                colorSum++;
                colors[pixel[i] & 0xffffff] = 1;
            }
        }
        free(colors);
        return colorSum;
    }
    Alles anzeigen
  • GetUniqueColors

    • Oscar
    • 24. November 2017 um 18:29
    Zitat von AspirinJunkie

    Hätte nicht gedacht dass es viel ausmacht aber gerade bei großen Bildern bringt es wirklich einen großen Unterschied.

    Ja, nicht schlecht! :thumbup:

    Bei kleinen (576 x 432 px) Bildern ist die MultiThread-Version allerdings etwas langsamer, als die SingleThread-Version.

    Bei meinen großen Bildern (4608 x 3456 px) sieht das aber schon anders aus:

    Code
    Multi-Thread-Version:
              Zeitbedarf:   927.13 ms
           Anzahl Farben:   727644
    
    Single-Thread-Version:
              Zeitbedarf:  2666.53 ms
           Anzahl Farben:   727644

    Wie würde die Multi-Thread-Version wohl mit der Array-Methode aussehen?

Spenden

Jeder Euro hilft uns, Euch zu helfen.

Download

AutoIt Tutorial
AutoIt Buch
Onlinehilfe
AutoIt Entwickler
  1. Datenschutzerklärung
  2. Impressum
  3. Shoutbox-Archiv
Community-Software: WoltLab Suite™