Datei-Eigenschaften von Bildern performant einlesen

  • hoi zäme

    Ich lese mit dem folgenden Code die Datei-Eigenschaften von Bilder aus. Brauchen tue ich die Höhe und die Breite.

    Code
    $oShellApp = ObjCreate ("shell.application")
    $oDir = $oShellApp.NameSpace($sBildPfad)
    $oFile = $oDir.Parsename ($sBildName)
    $sBildProperty = $oDir.GetDetailsOf($oFile, 31)

    Mein Problem ist das ich sehr viele Bildformate einlese und mir das zu lang dauert.
    Für 60k Bilder benötigt das Skript 36 Minuten.
    Hat jemand eine Idee, wie man das performanter machen könnte?

    herzlichen Dank
    tollpan

    P.S. der Code ist natürlich nur ein Auszug.

    Einmal editiert, zuletzt von tollpan (16. November 2012 um 07:20)

  • Um die Frage zu klären warum das so lange dauert ist es nicht so interessant wie du die Eigenschaften einer einzelnen Datei ausliest sondern wie du konkret für alle machst.
    Also eher die interessanten Fragen wie:

    • Erstellst du bei jedem einzelnen Abfragen ein neues shell.application-Objekt?
    • Wechselst du bei jeder Datei den Ordner per $oShellApp.NameSpace obwohl mehrere Dateien in einem Ordner liegen?
    • Wie iterierst du durch alle Dateien?


    Zwar gibt es noch alternative Methoden um an die Bilddaten zu kommen (>>Beispiel<<) aber ich denke schon in deinem Skript kann man noch einiges verbessern.

  • Guten Tag lieber Nachbar ;)

    Kommt darauf an in welchem Format die Dateien vorliegen aber alle gängigen Bildformate speichern die Dimensionen des Bildes im Header. Du könntest es mal mit FileRead probieren und diese Daten auslesen. Ob das aber viel schneller ist, weiss ich nicht. Wenn das zu langsam ist, könntest du natürlich auch versuchen das Ganze in eine DLL auszulagern. Aber AutoIt ist halt nun mal nicht sehr Performant...

    PS: Es gibt bestimmt noch viel bessere Lösungen, die mir einfach gerade nicht einfallen.

    EDIT: Och Mensch. Das ist schon das gefühlt zehnte mal diese Woche, dass mir einer zuvorkommt :evil:

    Gruss Shadowigor

  • P.S. der Code ist natürlich nur ein Auszug.


    dann kann man das schlecht beurteilen wobei mir 1000 Bilder/min zufriedenstellend vorkommt Ich habe aber ein Uraltsystem am laufen.
    Interessnt wäre für mich wie du die Ergebnisse auswertest.

    mfg autoBert

  • Danke für die schnellen Antworten
    Ich programmiere erst seit einem Monat mit AutoIt und habe aus dem Forum echt viel gelernt :)

    Mit dem Kommentar von AspirinJunkie fällt es mir selbst auf, das ich die Shell nicht jedes Mal neu aufrufen sollte.
    Wie gewünscht der ganze Code:

    Die Bilder (alle Format JPG) liegen in x-beliebigen vorher ausgelesen Unterverzeichnisse. Die Bildnamen selbst lese ich auch pro Verzeichnis aus, so dass Namen auch doppelt vorkommen können.

    Die Zeitmessung führ ich mit $iZeitmessung = TimerInit() vor der Schleife und mit $iZeitDurchlauf = TimerDiff($iZeitMessung) nach der Schleife durch. Anschliessend rechne ich die Milli-Sekunden in Minuten und Sekunden um.

    Ich scanne übrigens damit meine Photos und erstelle anschliessend eine Bildersammlung mit HTML.

  • Mit dem Kommentar von AspirinJunkie fällt es mir selbst auf, das ich die Shell nicht jedes Mal neu aufrufen sollte.
    Wie gewünscht der ganze Code:


    Na dann weist du ja jetzt wo du die Zeit liegen lässt.

    PS.: danke dir für den kompletten Code, hatte/musste mich noch nicht mit diesem Thema beschäftiigen

    mfg autobert

  • Liegen alle Unterverzeichnisse im selben Ordnerpfad wie der Stammordner?
    Weil wenn ja brauchst du die Dateien und Unterordner nicht vorher auszulesen sondern kannst auch gleich mit dem shell.application-Objekt die Verzeichnisse nach Bilddateien absuchen.
    Mal hier ein kleines Beispiel dafür:

    Spoiler anzeigen
    [autoit]

    #include <Misc.au3>
    Global Const $s_Path = "C:\Bilder" ; Pfad anpassen

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    $o_Bilder = CheckPictures($s_Path)
    If @error Then Exit MsgBox(48, "Fehler", "@error: " & @error & @CRLF & "@extended: " & @extended)

    [/autoit] [autoit][/autoit] [autoit]

    For $Bild In $o_Bilder.Keys
    ConsoleWrite(StringFormat("% 180s:\t%4s\n", $Bild, $o_Bilder($Bild)))
    Next

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    ; Sucht alle Bilddateien in einem Ordner / und Unterordnern und gibt ein Dictionary der Form:
    ; "Dateipfad : Bildausrichtung" zurück
    Func CheckPictures(Const $s_Path, Const $s_FileTypes = "jpg|png|bmp|tif|tiff", $b_First = True)
    Local Static $o_Shell = ObjCreate("Shell.Application")
    Local Static $o_Dic = ObjCreate("Scripting.Dictionary")
    Local Static $s_Pattern
    Local $o_Folder, $o_Details, $d_width, $d_height

    [/autoit] [autoit][/autoit] [autoit]

    If $b_First Then
    $o_Dic.RemoveAll
    $s_Pattern = "(?m)\.(?:" & $s_FileTypes & ")$"
    EndIf

    [/autoit] [autoit][/autoit] [autoit]

    $o_Folder = $o_Shell.NameSpace($s_Path)
    If @error Or $o_Folder = "" Then Return SetError(1, @error, 0)

    [/autoit] [autoit][/autoit] [autoit]

    For $i In $o_Folder.Items
    If $i.IsFolder Then CheckPictures($i.Path, "", False)
    If StringRegExp($i.Path, $s_Pattern) Then
    $o_Details = $o_Folder.GetDetailsOf($i, 31)
    $d_width = Int(StringTrimLeft(StringLeft($o_Details, StringInStr($o_Details, " ", 1)), 1))
    $d_height = Int(StringTrimLeft($o_Details, StringInStr($o_Details, " ", 1, -1)))

    [/autoit] [autoit][/autoit] [autoit]

    $o_Dic($i.Path) = _Iif($d_width > $d_height, "quer", "hoch")
    EndIf
    Next

    [/autoit] [autoit][/autoit] [autoit]

    If $b_First Then
    $o_Shell = 0
    Return $o_Dic
    EndIf
    EndFunc

    [/autoit]
  • Ja, alle Unterverzeichnisse liegen im selben Ordnerpfad wie der Stammordner.

    Ich muss die Dateien und Unterverzeichnisse vorher auslesen, weil ich die Daten später noch für die HTML-Ausgabe brauche. Pro Verzeichnis erstelle ich eine eigene HTML-Seite. Die ganze Ausleserei ist zwar nicht so schön programmiert wie die Rekursiv-Funktionen von Autobert, aber reicht für meine Bedürfnisse und dauert nur ein paar Sekunden.

    Dank den Hinweisen habe ich den Code umgestellt, so dass die Shell nur einmal geöffnet und der Verzeichnispfad entsprechend weniger verwendet wird. Durch diese andere Reihenfolge des Codes verkürzt sich die Durchlaufzeit der beiden Schleifen um ungefähr 40%.

    Spoiler anzeigen


    $oShellApp = ObjCreate ("shell.application")
    For $iZ2 = 1 to $iBildVerzAnzahl Step 1
    $oDir = $oShellApp.NameSpace($asBildPfad[$iZ2])
    For $iZ3 = 1 to $aiBildAnzahl[$iZ2] Step 1
    $oFile = $oDir.Parsename ($asBildName[$iZ2][$iZ3])
    $sBildProperty = $oDir.GetDetailsOf($oFile, 31) ; ?834 x 1280? ?968 x 631?
    $sBildProperty = StringTrimRight(StringTrimLeft($sBildProperty, 1), 1) ; entferne Fragezeichen
    $aiBildBreite[$iZ2][$iZ3] = Number(StringLeft($sBildProperty, StringInStr($sBildProperty, " ", 0, 1) - 1)) ; 3stellig oder 4stellig
    $aiBildHoehe[$iZ2][$iZ3] = Number(StringRight($sBildProperty, StringLen($sBildProperty) - StringInStr($sBildProperty, " ", 0, 2))) ; 3stellig oder 4stellig

    ; Bildformat hoch oder quer ermitteln
    If $aiBildBreite[$iZ2][$iZ3] >= $aiBildHoehe[$iZ2][$iZ3] then
    $asBildFormat[$iZ2][$iZ3] = "quer"
    Else
    $asBildFormat[$iZ2][$iZ3] = "hoch"
    EndIf
    Next
    Next

    Ich werde meine Durchlaufzeiten mit ImageGetInfo sowie mit dem Vorschlag von Aspirin Junkie vergleichen und die Ergebnisse hier zeigen.
    Vielleicht kann jemand anders die Infos mal brauchen.

  • Ich muss die Dateien und Unterverzeichnisse vorher auslesen, weil ich die Daten später noch für die HTML-Ausgabe brauche. Pro Verzeichnis erstelle ich eine eigene HTML-Seite.


    Das kann ja simultan zum Auslesen mit gemacht werden.
    Man geht ja eh alle Verzeichnisse durch und kann da ja auch dabei die Html-Datei erstellen.

  • Ich habe die Durchlaufzeiten für die verschiedenen Varianten zum Hoehe & Breite einlesen gemessen.
    Szenario: 60.438 Bildnamen im Format JPG in 447 Verzeichnissen auf einer Ebene

    1. Tollpans Variante - Dauer 15 Min 20 Sekunden

    Spoiler anzeigen
    [autoit]


    $oShellApp = ObjCreate ("shell.application")
    For $iZ2 = 1 to $iBildVerzAnzahl Step 1
    $oDir = $oShellApp.NameSpace($asBildPfad[$iZ2])
    For $iZ3 = 1 to $aiBildAnzahl[$iZ2] Step 1
    $oFile = $oDir.Parsename($asBildName[$iZ2][$iZ3])
    $sBildProperty = $oDir.GetDetailsOf($oFile, 31) ; ?834 x 1280? ?968 x 631?
    $sBildProperty = StringTrimRight(StringTrimLeft($sBildProperty, 1), 1) ; entferne Fragezeichen
    $aiBildBreite[$iZ2][$iZ3] = Number(StringLeft($sBildProperty, StringInStr($sBildProperty, " ", 0, 1) - 1)) ; 3stellig oder 4stellig
    $aiBildHoehe[$iZ2][$iZ3] = Number(StringRight($sBildProperty, StringLen($sBildProperty) - StringInStr($sBildProperty, " ", 0, 2))) ; 3stellig oder 4stellig

    ; Bildformat hoch oder quer ermitteln
    If $aiBildBreite[$iZ2][$iZ3] >= $aiBildHoehe[$iZ2][$iZ3] then
    $asBildFormat[$iZ2][$iZ3] = "quer"
    Else
    $asBildFormat[$iZ2][$iZ3] = "hoch"
    EndIf
    Next
    Next

    [/autoit]

    2. Aspirin Junkies Variante - Dauer 13 Minuten 3 Sekunden

    Spoiler anzeigen
    [autoit]


    #include <Misc.au3>
    Global Const $s_Path = "C:\Bilder" ; Pfad anpassen

    [/autoit] [autoit][/autoit] [autoit]

    $o_Bilder = CheckPictures($s_Path)
    If @error Then Exit MsgBox(48, "Fehler", "@error: " & @error & @CRLF & "@extended: " & @extended)

    [/autoit] [autoit][/autoit] [autoit]

    For $Bild In $o_Bilder.Keys
    ConsoleWrite(StringFormat("% 180s:\t%4s\n", $Bild, $o_Bilder($Bild)))
    Next

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    ; Sucht alle Bilddateien in einem Ordner / und Unterordnern und gibt ein Dictionary der Form:
    ; "Dateipfad : Bildausrichtung" zurück
    Func CheckPictures(Const $s_Path, Const $s_FileTypes = "jpg|png|bmp|tif|tiff", $b_First = True)
    Local Static $o_Shell = ObjCreate("Shell.Application")
    Local Static $o_Dic = ObjCreate("Scripting.Dictionary")
    Local Static $s_Pattern
    Local $o_Folder, $o_Details, $d_width, $d_height

    [/autoit] [autoit][/autoit] [autoit]

    If $b_First Then
    $o_Dic.RemoveAll
    $s_Pattern = "(?m)\.(?:" & $s_FileTypes & ")$"
    EndIf

    [/autoit] [autoit][/autoit] [autoit]

    $o_Folder = $o_Shell.NameSpace($s_Path)
    If @error Or $o_Folder = "" Then Return SetError(1, @error, 0)

    [/autoit] [autoit][/autoit] [autoit]

    For $i In $o_Folder.Items
    If $i.IsFolder Then CheckPictures($i.Path, "", False)
    If StringRegExp($i.Path, $s_Pattern) Then
    $o_Details = $o_Folder.GetDetailsOf($i, 31)
    $d_width = Int(StringTrimLeft(StringLeft($o_Details, StringInStr($o_Details, " ", 1)), 1))
    $d_height = Int(StringTrimLeft($o_Details, StringInStr($o_Details, " ", 1, -1)))

    [/autoit] [autoit][/autoit] [autoit]

    $o_Dic($i.Path) = _Iif($d_width > $d_height, "quer", "hoch")
    EndIf
    Next

    [/autoit] [autoit][/autoit] [autoit]

    If $b_First Then
    $o_Shell = 0
    Return $o_Dic
    EndIf
    EndFunc

    [/autoit]

    3. UDF: ImageGetInfo - Dauer 12 Minuten 59 Sekunden
    http://www.autoitscript.com/forum/topic/13…f-imagegetinfo/

    Spoiler anzeigen
    [autoit]


    #include "image_get_info.au3"

    [/autoit] [autoit][/autoit] [autoit]

    For $iZ2 = 1 to $iBildVerzAnzahl Step 1
    For $iZ3 = 1 to $aiBildAnzahl[$iZ2] Step 1
    $aInfo = _ImageGetInfo($asBildPfad[$iZ2] & $asBildName[$iZ2][$iZ3])
    $aiBildBreite[$iZ2][$iZ3] = _ImageGetParam($aInfo, "Width")
    $aiBildHoehe[$iZ2][$iZ3] = _ImageGetParam($aInfo, "Height")

    ; Bildformat hoch oder quer ermitteln
    If $aiBildBreite[$iZ2][$iZ3] >= $aiBildHoehe[$iZ2][$iZ3] then
    $asBildFormat[$iZ2][$iZ3] = "quer"
    Else
    $asBildFormat[$iZ2][$iZ3] = "hoch"
    EndIf
    Next
    Next

    [/autoit]

    Anfänger Code ist halt am langsamsten :)

    Herzlichen Dank fürs mithelfen
    Tollapn