Extrahiere alle Frames eines animierten GIFs

  • Hi

    Wie bereits im Titel erwähnt, suche ich nach einer Möglichkeit, alle Frames (Einzelbilder) aus einem animierten GIF zu extrahieren (als jeweils einzelne Datei abzuspeichern).

    In diesem Link habe ich etwas mit GDI+ gefunden, aber als Laie habe ich große Probleme bei der Übersetzung nach AutoIt.
    Link
    Ich weiss schon nicht, wie ÜBERHAUPT anfangen.

    Klar kann man das alles mit Photoshop oder IrfanView oder sonstigen gratis Konvertern machen. Aber ich sollte das direkt in AutoIt können.

    Vielen Dank für Eure Hilfe!
    Veronesi

    Einmal editiert, zuletzt von veronesi (31. August 2012 um 13:25)

  • Hier bitte:

    Spoiler anzeigen
    [autoit]

    #include <GDIPlus.au3>

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

    _GDIPlus_Startup()
    Global $hImage = _GDIPlus_ImageLoadFromFile(@ScriptDir & "\Test.gif")
    _ExtractFrames($hImage)
    _GDIPlus_ImageDispose($hImage)
    _GDIPlus_Shutdown()

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

    ShellExecute(@ScriptDir & "\AllFrames.bmp")

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

    Func _ExtractFrames($hImage)
    Local $iImgW = _GDIPlus_ImageGetWidth($hImage)
    Local $iImgH = _GDIPlus_ImageGetHeight($hImage)
    Local $iImgF = _GDIPlus_ImageGetPixelFormat($hImage)

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

    Local $aResult = DllCall($ghGDIPDll, "uint", "GdipImageGetFrameDimensionsCount", "hwnd", $hImage, "uint*", 0)
    If @error Then Return SetError(1, 1, False)
    Local $iDim = $aResult[2]

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

    Local $tDim = DllStructCreate("byte[" & $iDim * 16 & "];")
    $aResult = DllCall($ghGDIPDll, "uint", "GdipImageGetFrameDimensionsList", "hwnd", $hImage, "ptr", DllStructGetPtr($tDim), "int", $iDim)
    If @error Then Return SetError(1, 2, False)

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

    Local $iFramesMax = 0
    For $i = 1 To $iDim
    $aResult = DllCall($ghGDIPDll, "uint", "GdipImageGetFrameCount", "hwnd", $hImage, "ptr", DllStructGetPtr($tDim) + ($i - 1) * 16, "uint*", 0)
    If @error Then Return SetError(1, 2, False)
    If $aResult[3] > $iFramesMax Then $iFramesMax = $aResult[3]
    Next

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

    Local $aResult = DllCall($ghGDIPDll, "uint", "GdipCreateBitmapFromScan0", "int", $iImgW * $iFramesMax, "int", $iImgH * $iDim, "int", 0, "int", 0x0026200A, "ptr", 0, "int*", 0)
    If @error Then Return SetError(1, 1, False)
    Local $hBitmap = $aResult[6]
    Local $hContext = _GDIPlus_ImageGetGraphicsContext($hBitmap)

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

    Local $iFrames, $hBmpFrame
    For $i = 1 To $iDim
    $aResult = DllCall($ghGDIPDll, "uint", "GdipImageGetFrameCount", "hwnd", $hImage, "ptr", DllStructGetPtr($tDim) + ($i - 1) * 16, "uint*", 0)
    If @error Then Return SetError(1, 2, False)
    $iFrames = $aResult[3]
    For $j = 1 To $iFrames
    DllCall($ghGDIPDll, "uint", "GdipImageSelectActiveFrame", "hwnd", $hImage, "ptr", DllStructGetPtr($tDim) + ($i - 1) * 16, "uint", $j - 1)
    $hBmpFrame = _GDIPlus_BitmapCloneArea($hImage, 0, 0, $iImgW, $iImgH, $iImgF)
    _GDIPlus_ImageSaveToFile($hBmpFrame, @ScriptDir & "\Frame_" & StringFormat("%03s", $i) & "-" & StringFormat("%03s", $j) & ".bmp")

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

    _GDIPlus_GraphicsDrawImageRect($hContext, $hBmpFrame, ($j - 1) * $iImgW, ($i - 1) * $iImgH, $iImgW, $iImgH)

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

    _GDIPlus_BitmapDispose($hBmpFrame)
    Next
    Next

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

    _GDIPlus_GraphicsDispose($hContext)
    _GDIPlus_ImageSaveToFile($hBitmap, @ScriptDir & "\AllFrames.bmp")
    _GDIPlus_BitmapDispose($hBitmap)
    EndFunc ;==>_ExtractFrames

    [/autoit]

    E

  • Super!
    Geht einwandfrei.

    Für meine Zwecke habe ich es etwas umgebaut und vereinfacht.
    Nicht so professionell wie Deine Lösung, aber für mich funktioniert es so super!

    Ich möchte nämlich entscheiden können, welches Frame extrahiert wird. Und es wird nicht zwingend ein File geschrieben, sondern man kann sich auch das Handle zurückgeben lassen.
    Da mein Programm sowieso mit GDI+ arbeitet, kann ich dann dieses Handle gleiche zum anzeigen verwenden. Ist natürlich schneller, als wenn ich es noch auf der Festplatte zwischenspeichere und danach wieder lade.

    Aber wie gesagt, Dein Programm lief einwandfrei und so wie ich es in der Beschreibung gewünscht habe!
    Danke!

    Spoiler anzeigen
    [autoit]


    #include <GDIPlus.au3>

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

    _GDIPlus_Startup()
    $sFile = @ScriptDir & "\a.gif"
    Local $hFrameHandle
    Local $iNumer = _CountFramesInGif($sFile)
    For $i = 1 To $iNumer
    $hFrameHandle = _ExtractFrame($sFile, "", $i)
    _GDIPlus_ImageSaveToFile($hFrameHandle, @ScriptDir & "\" & $i & ".gif")
    Next
    _GDIPlus_Shutdown()

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

    ;Zählt die Anzahl der vorhandenen Bilder in einem animierten GIF
    ;$sGifFile ist die zu übergebende GIF Datei
    Func _CountFramesInGif($sGifFile)
    Local $hImage = _GDIPlus_ImageLoadFromFile($sGifFile)

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

    Local $tDim = DllStructCreate("byte[" & 16 & "];")
    $aResult = DllCall($ghGDIPDll, "uint", "GdipImageGetFrameDimensionsList", "hwnd", $hImage, "ptr", DllStructGetPtr($tDim), "int", 1)
    If @error Then Return SetError(1, 0, 0)

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

    Local $iFramesMax = 0
    $aResult = DllCall($ghGDIPDll, "uint", "GdipImageGetFrameCount", "hwnd", $hImage, "ptr", DllStructGetPtr($tDim), "uint*", 0)
    If @error Then Return SetError(2, 0, 0)
    $iFramesMax = $aResult[3]

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

    _GDIPlus_ImageDispose($hImage)
    Return $iFramesMax
    EndFunc ;==>_CountFramesInGif

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

    ;Extrahiert ein Bild aus einer animierten GIF Datei
    ;$sGifFile ist das animierte GIF
    ;$sSaveFile ist der Speicherpfad, wo das einzelne Bild gespeichert werden soll.
    ;Wird hier nichts übergeben (""), dann wird von dieser Funktion das Handle auf das extrahierte Bild zurückgegeben. Dieses kann mit GDI+ dann z.B. angezeigt werden.
    ;$iFrame = Nummer des Bildes, welches extrahiert werden soll. (Beginnend mit 1)
    Func _ExtractFrame($sGifFile, $sSaveFile, $iFrame = 1)
    Local $hImage = _GDIPlus_ImageLoadFromFile($sGifFile)

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

    Local $iImgW = _GDIPlus_ImageGetWidth($hImage)
    Local $iImgH = _GDIPlus_ImageGetHeight($hImage)
    Local $iImgF = _GDIPlus_ImageGetPixelFormat($hImage)

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

    Local $tDim = DllStructCreate("byte[" & 16 & "];")
    $aResult = DllCall($ghGDIPDll, "uint", "GdipImageGetFrameDimensionsList", "hwnd", $hImage, "ptr", DllStructGetPtr($tDim), "int", 1)
    If @error Then Return SetError(1, 0, 0)

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

    Local $iFramesMax = 0
    $aResult = DllCall($ghGDIPDll, "uint", "GdipImageGetFrameCount", "hwnd", $hImage, "ptr", DllStructGetPtr($tDim), "uint*", 0)
    If @error Then Return SetError(2, 0, 0)
    $iFramesMax = $aResult[3]

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

    If $iFrame > $iFramesMax Then Return SetError(3, 0, 0)

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

    Local $aResult = DllCall($ghGDIPDll, "uint", "GdipCreateBitmapFromScan0", "int", $iImgW, "int", $iImgH, "int", 0, "int", 0x0026200A, "ptr", 0, "int*", 0)
    If @error Then Return SetError(4, 0, 0)
    Local $hBitmap = $aResult[6]
    Local $hContext = _GDIPlus_ImageGetGraphicsContext($hBitmap)

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

    Local $hFrame
    DllCall($ghGDIPDll, "uint", "GdipImageSelectActiveFrame", "hwnd", $hImage, "ptr", DllStructGetPtr($tDim), "uint", $iFrame - 1)
    $hFrame = _GDIPlus_BitmapCloneArea($hImage, 0, 0, $iImgW, $iImgH, $iImgF)
    If $sSaveFile = "" Then
    _GDIPlus_GraphicsDispose($hContext)
    _GDIPlus_BitmapDispose($hBitmap)
    _GDIPlus_ImageDispose($hImage)
    Return $hFrame
    Else
    _GDIPlus_ImageSaveToFile($hFrame, $sSaveFile)
    _GDIPlus_BitmapDispose($hFrame)
    _GDIPlus_GraphicsDispose($hContext)
    _GDIPlus_BitmapDispose($hBitmap)
    _GDIPlus_ImageDispose($hImage)
    Return True
    EndIf
    EndFunc ;==>_ExtractFrame

    [/autoit]

    Veronesi