Bitmap Daten direkt aus Arbeitsspeicher auslesen

  • Tja... Wie die Überschrift schon sagt, möchte ich die Daten einer Bitmap über ein HBTIMAP Handle (wie es z.B. von _ScreenCapture_Capture zurückgegeben wird) direkt aus dem Speicher auslesen. Also ohne zwischenspeichern auf der Festplatte, da das zu lange dauert :S. Bisherige Ansätze oder Versuche gibt es nicht, da ich keine Ahnung habe wie ich das überhaupt angehen soll. Hoffe auf Lösungsvorschläge/Lösungen. ;)

  • Da ich keine Ahnung habe wie ich mit Autoit etwas in den RAM lade würde ich ein kleines C - Programm schreiben welches mir alle benötigten Bitmaps in den ram läd und in einem File die adressen speichert mit welchen man dann unter autoit arbeiten kann.

    Anschließend muss man natürlich wieder unter C diesen Speicher freieben (oder unter AutoIt wenns irgendwie geht)

  • zu einem GDIPlus Bitmap umwandeln und via LockBits auslesen

    Spoiler anzeigen
    [autoit]

    #include <Array.au3>
    #include <GDIPlus.au3>
    #include <GDIPlusConstants.au3>
    #include <ScreenCapture.au3>

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

    _GDIPlus_Startup()
    $hBmp = _ScreenCapture_Capture("", 0, 0, 50, 50)
    $hBitmap = _GDIPlus_BitmapCreateFromHBITMAP($hBmp)
    _WinAPI_DeleteObject($hBmp)

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

    $BitmapData = _GDIPlus_BitmapLockBits($hBitmap, 0, 0, 50, 50, $GDIP_ILMREAD, $GDIP_PXF32ARGB)
    $Stride = DllStructGetData($BitmapData, "Stride")
    $Width = DllStructGetData($BitmapData, "Width")
    $Height = DllStructGetData($BitmapData, "Height")
    $Scan0 = DllStructGetData($BitmapData, "Scan0")
    $PixelData = DllStructCreate("dword[" & $Width * $Height & ']', $Scan0)
    Global $aPixels[$Width][$Height]
    For $row = 0 To $Height - 1
    For $col = 0 To $Width - 1
    $aPixels[$col][$row] = Hex(DllStructGetData($PixelData, 1, ($row * $Width) + $col + 1))
    Next
    Next
    _GDIPlus_BitmapUnlockBits($hBitmap, $BitmapData)
    _GDIPlus_BitmapDispose($hBitmap)
    _GDIPlus_Shutdown()

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

    _ArrayDisplay($aPixels)

    [/autoit]

    E

  • Das kannte ich schon. Aber ich hätte vielleicht auch schreiben sollen, dass Zeit eine Rolle spielt :S. Die Funktion ist prima, aber ich brauche eine extrem schnelle Funktion... Die Art der Daten wäre mir auch egal (Binär, Farbcodes etc...), ich muss die Daten auch nicht interpretieren oder verändern können sondern nur ggf. wieder zu einer Bitmap zusammenfügen.

  • Ich weiß leider nicht, wie man das ohne den Umweg überGDI+ machen kann, aber vielleicht ist das hier schnell genug:

    [autoit]

    #include <GDIPlus.au3>
    #include <GDIPlusConstants.au3>
    #include <Memory.au3>
    #include <ScreenCapture.au3>
    #include <WinAPI.au3>

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

    _GDIPlus_Startup()
    $hBmp = _ScreenCapture_Capture("", 0, 0, 50, 50)
    $hBitmap = _GDIPlus_BitmapCreateFromHBITMAP($hBmp)
    _WinAPI_DeleteObject($hBmp)

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

    $BitmapData = _GDIPlus_BitmapLockBits($hBitmap, 0, 0, 50, 50, $GDIP_ILMREAD, $GDIP_PXF32ARGB)
    $Stride = DllStructGetData($BitmapData, "Stride")
    $Width = DllStructGetData($BitmapData, "Width")
    $Height = DllStructGetData($BitmapData, "Height")
    $Scan0 = DllStructGetData($BitmapData, "Scan0")
    $PixelData = DllStructCreate("dword[" & $Width * $Height & ']', $Scan0)

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

    $tBitmap = DllStructCreate("dword[" & $Width * $Height & ']')
    _MemMoveMemory(DllStructGetPtr($PixelData), DllStructGetPtr($tBitmap), DllStructGetSize($PixelData))

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

    _GDIPlus_BitmapUnlockBits($hBitmap, $BitmapData)
    _GDIPlus_BitmapDispose($hBitmap)

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

    $aResult = DllCall($ghGDIPDll, "uint", "GdipCreateBitmapFromScan0", "int", $Width, "int", $Height, "int", $Stride, "int", $GDIP_PXF32ARGB, "ptr", DllStructGetPtr($tBitmap), "int*", 0)
    $hBitmapNew = $aResult[6]
    $hBmpNew = _GDIPlus_BitmapCreateHBITMAPFromBitmap($hBitmapNew)
    _GDIPlus_BitmapDispose($hBitmapNew)
    _GDIPlus_Shutdown()

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

    _ScreenCapture_SaveImage(@ScriptDir & "\Test.bmp", $hBmpNew)

    [/autoit]
  • Viel schneller wird es nicht gehen, ich weiß nicht genau wie das im RAM gespeichert wird, aber du brauchst auf jeden Fall eine berechtigung auf den Ort zuzugreifen. Das macht UnlockBits (und vielleicht macht es auch noch eine benutzbare Form). Ich schätze da musst du entweder selber in ein Array die Pixeldaten schreiben, z.B. mit ASM wenn man da eine schnelle Funktion um einen Pixel auszulesen findet oderdie mit UnlockBits zufrieden geben

  • Hi zusammen,
    dass der Lock/Unlock ziemlich viel Zeit verbraucht, hatte ich schon bei anderen Aufgaben bemerkt. Leider ist mir bisher keine andere Variante untergekommen, um den Pointer auf die Bitmapdaten zu bekommen. Ich hatte schon daran gedacht, das "Handle" auseinanderzuklabustern. Ein Handle ist ja nichts weiter als ein Zeiger auf einen Speicherbereich mit Informationen. Irgendwo in diesem Speicherbereich muss zwangsläufig auch die Adresse der Bitmap stehen. Wenn da jemand bissl Zeit in reverse engeneering investieren würde, wäre das sehr zu begrüssen :D
    name22
    hast du schon versucht, die Bitmaps zu blitten? Das ist idR ziemlich schnell, mit etwas Glück unterstützt der Treiber der Graka das Blitten.

  • Zitat

    hast du schon versucht, die Bitmaps zu blitten? Das ist idR ziemlich schnell, mit etwas Glück unterstützt der Treiber der Graka das Blitten.


    Ich werd mich mal damit auseinandersetzen... Vielleicht hilft mir das ja weiter. :) Vielen Dank für deinen Beitrag.

  • Hab hier nochwas ohne GDI+
    ist bei mir doppelt so schnell wie LockBits

    Spoiler anzeigen
    [autoit]

    #include <Memory.au3>
    #include <ScreenCapture.au3>
    #include <StructureConstants.au3>
    #include <WinAPI.au3>

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

    $iTimer = TimerInit()
    $hBmp = _ScreenCapture_Capture("", 0, 0, 500, 500)
    $tData = _GetBmpData($hBmp)
    _WinAPI_DeleteObject($hBmp)

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

    $hBmpNew = _CreateBmpFromData($tData)
    ConsoleWrite(TimerDiff($iTimer) & @CRLF)
    _ScreenCapture_SaveImage(@ScriptDir & "\Test.bmp", $hBmpNew)

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

    Func _CreateBmpFromData($tData)
    Local $tBITMAPINFO = DllStructCreate($tagBITMAPINFO, DllStructGetPtr($tData))
    Local $iSize = DllStructGetData($tBITMAPINFO, "SizeImage")
    Local $tStruct = DllStructCreate($tagBITMAPINFO & ";byte Data[" & $iSize & "]", DllStructGetPtr($tData))
    Local $pStruct = DllStructGetPtr($tStruct)
    Local $aResult = DllCall('gdi32.dll', 'ptr', 'CreateDIBSection', 'hwnd', 0, 'ptr', DllStructGetPtr($tBITMAPINFO), 'uint', 0, 'ptr*', 0, 'ptr', 0, 'dword', 0)
    _MemMoveMemory($pStruct + DllStructGetSize($tBITMAPINFO), $aResult[4], $iSize)
    Return $aResult[0]
    EndFunc ;==>_CreateBmpFromData

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

    Func _GetBmpData($hBmp)
    Local $tBITMAPINFO = DllStructCreate($tagBITMAPINFO)
    DllStructSetData($tBITMAPINFO, "Size", DllStructGetSize($tBITMAPINFO))

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

    Local $hWnd = _WinAPI_GetDesktopWindow()
    Local $hDDC = _WinAPI_GetDC($hWnd)
    _WinAPI_GetDIBits($hDDC, $hBmp, 0, 0, 0, DllStructGetPtr($tBITMAPINFO), 0)

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

    Local $iSize = DllStructGetData($tBITMAPINFO, "SizeImage")
    Local $tData = DllStructCreate($tagBITMAPINFO & ";byte Data[" & $iSize & "]")
    DllStructSetData($tData, "Size", DllStructGetSize($tBITMAPINFO))
    Local $pData = DllStructGetPtr($tData)

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

    Local $iLines = DllStructGetData($tBITMAPINFO, "Height")

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

    _WinAPI_GetDIBits($hDDC, $hBmp, 0, 0, 0, $pData, 0)
    _WinAPI_GetDIBits($hDDC, $hBmp, 0, $iLines, $pData + DllStructGetSize($tBITMAPINFO), $pData, 0)

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

    _WinAPI_ReleaseDC($hWnd, $hDDC)
    Return $tData
    EndFunc ;==>_GetBmpData

    [/autoit]

    Was willst du mit den Daten eigentlich genau machen?

    E

    Edit: evtl. ist _WinAPI_SetDIBits noch schneller bzw. besser geeignet als CreateDIBSection - musst du halt ausprobieren:

    Spoiler anzeigen
    [autoit]

    Func _CreateBmpFromData($tData)
    Local $tBITMAPINFO = DllStructCreate($tagBITMAPINFO, DllStructGetPtr($tData))
    Local $iSize = DllStructGetData($tBITMAPINFO, "SizeImage")
    Local $tStruct = DllStructCreate($tagBITMAPINFO & ";byte Data[" & $iSize & "]", DllStructGetPtr($tData))
    Local $pStruct = DllStructGetPtr($tStruct)

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

    Local $iWidth = DllStructGetData($tBITMAPINFO, "Width")
    Local $iHeight = DllStructGetData($tBITMAPINFO, "Height")

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

    Local $hWnd = _WinAPI_GetDesktopWindow()
    Local $hDDC = _WinAPI_GetDC($hWnd)
    Local $hBmp = _WinAPI_CreateCompatibleBitmap($hDDC, $iWidth, $iHeight)
    _WinAPI_SetDIBits($hDDC, $hBmp, 0, $iHeight, $pStruct + DllStructGetSize($tBITMAPINFO), DllStructGetPtr($tBITMAPINFO))

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

    _WinAPI_ReleaseDC($hWnd, $hDDC)
    Return $hBmp
    EndFunc ;==>_CreateBmpFromData

    [/autoit]
  • Zitat

    Was willst du mit den Daten eigentlich genau machen?


    Möglicherweise in einer Datei o.Ä zwischenlagern um eine Überfüllung des Arbeitsspeichers, bei zu vielen Bitmaps, zu vermeiden (Wenn das eine blöde Idee ist, sagt es mir ;) ). Danach sollten die Daten natürlich wieder in Bitmaps umgewandelt werden können, wobei da die Geschwindigkeit egal wäre. Und vielen Dank für deine performante Funktion, ich werde mir jetzt alle Vorschläge mal anschauen und die günstigste Methode verwenden, es sei denn ihr habt noch einen besseren Vorschlag. ;)

  • In diesem Fall würde ich es in Gdi+ umwandeln und dann mit _GdiPlus_ImageSaveToFile speichern.
    Bei Bedarf wieder laden, zu einem HBitmap umwandeln und auf ein DDBitmap blitten...

    trotzdem hier noch meine Version mit Speicherfunktion

    Spoiler anzeigen
    [autoit]

    #include <Memory.au3>
    #include <ScreenCapture.au3>
    #include <StructureConstants.au3>
    #include <WinAPI.au3>

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

    $hBmp = _ScreenCapture_Capture("", 0, 0, 500, 500)
    $tData = _GetBmpData($hBmp)
    _WinAPI_DeleteObject($hBmp)
    _SaveBmpData(@ScriptDir & "\Test.dat", $tData)

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

    $tDataNew = _LoadBmpData(@ScriptDir & "\Test.dat")
    $hBmpNew = _CreateBmpFromData($tDataNew)
    _ScreenCapture_SaveImage(@ScriptDir & "\Test.bmp", $hBmpNew)

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

    Func _SaveBmpData($sFilename, $tData)
    Local $tSave = DllStructCreate("byte[" & DllStructGetSize($tData) & "]", DllStructGetPtr($tData))
    Local $hFile = FileOpen($sFilename, 18)
    FileWrite($hFile, DllStructGetData($tSave, 1))
    FileClose($hFile)
    EndFunc ;==>_SaveBmpData

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

    Func _LoadBmpData($sFilename)
    Local $hFile = FileOpen($sFilename, 16)
    Local $bData = FileRead($hFile)
    FileClose($hFile)

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

    Local $tLoad = DllStructCreate("byte[" & BinaryLen($bData) & "]")
    DllStructSetData($tLoad, 1, $bData)
    Local $tBITMAPINFO = DllStructCreate($tagBITMAPINFO, DllStructGetPtr($tLoad))

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

    Local $iSize = DllStructGetData($tBITMAPINFO, "SizeImage")
    Local $tStruct = DllStructCreate($tagBITMAPINFO & ";byte Data[" & $iSize & "]")
    _MemMoveMemory(DllStructGetPtr($tLoad), DllStructGetPtr($tStruct), DllStructGetSize($tLoad))
    Return $tStruct
    EndFunc ;==>_LoadBmpData

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

    Func _CreateBmpFromData($tData)
    Local $tBITMAPINFO = DllStructCreate($tagBITMAPINFO, DllStructGetPtr($tData))
    Local $iSize = DllStructGetData($tBITMAPINFO, "SizeImage")
    Local $tStruct = DllStructCreate($tagBITMAPINFO & ";byte Data[" & $iSize & "]", DllStructGetPtr($tData))
    Local $pStruct = DllStructGetPtr($tStruct)

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

    Local $iWidth = DllStructGetData($tBITMAPINFO, "Width")
    Local $iHeight = DllStructGetData($tBITMAPINFO, "Height")

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

    Local $hWnd = _WinAPI_GetDesktopWindow()
    Local $hDDC = _WinAPI_GetDC($hWnd)
    Local $hBmp = _WinAPI_CreateCompatibleBitmap($hDDC, $iWidth, $iHeight)
    _WinAPI_SetDIBits($hDDC, $hBmp, 0, $iHeight, $pStruct + DllStructGetSize($tBITMAPINFO), DllStructGetPtr($tBITMAPINFO))

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

    _WinAPI_ReleaseDC($hWnd, $hDDC)
    Return $hBmp
    EndFunc ;==>_CreateBmpFromData

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

    Func _GetBmpData($hBmp)
    Local $tBITMAPINFO = DllStructCreate($tagBITMAPINFO)
    DllStructSetData($tBITMAPINFO, "Size", DllStructGetSize($tBITMAPINFO))

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

    Local $hWnd = _WinAPI_GetDesktopWindow()
    Local $hDDC = _WinAPI_GetDC($hWnd)
    _WinAPI_GetDIBits($hDDC, $hBmp, 0, 0, 0, DllStructGetPtr($tBITMAPINFO), 0)

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

    Local $iSize = DllStructGetData($tBITMAPINFO, "SizeImage")
    Local $tData = DllStructCreate($tagBITMAPINFO & ";byte Data[" & $iSize & "]")
    DllStructSetData($tData, "Size", DllStructGetSize($tBITMAPINFO))
    Local $pData = DllStructGetPtr($tData)

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

    Local $iLines = DllStructGetData($tBITMAPINFO, "Height")

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

    _WinAPI_GetDIBits($hDDC, $hBmp, 0, 0, 0, $pData, 0)
    _WinAPI_GetDIBits($hDDC, $hBmp, 0, $iLines, $pData + DllStructGetSize($tBITMAPINFO), $pData, 0)

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

    _WinAPI_ReleaseDC($hWnd, $hDDC)
    Return $tData
    EndFunc ;==>_GetBmpData

    [/autoit]
  • Soweit ich dich verstanden habe, willst du eine Menge Screenshots vom Desktop erstellen (so eine Art Abfilmen) und diese Screenshot anschließend speichern.

    Hier ein Test:

    Spoiler anzeigen
    [autoit]


    #include <ScreenCapture.au3>

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

    _GDIPlus_Startup()
    $j = 5000 ;Anzahl der Screenshots
    Dim $memHBitmaps[$j], $hBitmaps[$j]
    $w = 50
    $h = 50
    $t = TimerInit()
    For $i = 0 To UBound($memHBitmaps) - 1
    $x = Random(0, @DesktopWidth - $w, 1)
    $y = Random(0, @DesktopHeight - $h, 1)
    $memHBitmaps[$i] = _ScreenCapture_Capture("", $x, $y, $x + $w, $y + $h, 0)
    $hBitmaps[$i] = _GDIPlus_BitmapCreateFromHBITMAP($memHBitmaps[$i])
    Next
    $tm = Round(TimerDiff($t), 2)
    ConsoleWrite("Laufzeit, um den Speicher mit " & $j & " Screenshots zu füllen: " & $tm & " ms" & @LF)
    $t = TimerInit()
    $l = StringLen(UBound($memHBitmaps) - 1)
    For $i = 0 To UBound($memHBitmaps) - 1
    _GDIPlus_ImageSaveToFile($hBitmaps[$i], "Test" & StringFormat("%0" & $l & "s", $i) & ".jpg")
    _WinAPI_DeleteObject($memHBitmaps[$i])
    _GDIPlus_BitmapDispose ($hBitmaps[$i])
    Next
    $ts = Round(TimerDiff($t), 2)
    ConsoleWrite("Laufzeit für das Speichern der " & $j & " Screenshots : " & $ts & " ms" & @LF)
    $tg = $tm + $ts
    ConsoleWrite("Laufzeit gesamt: " & $tg & " ms" & @LF)
    $bps = Round($j / $tg * 1000, 2)
    ConsoleWrite($bps & " Bilder/s, " & Round(1 / $bps * 1000, 2) & " ms/Bild" & @LF)
    _GDIPlus_Shutdown()
    Exit

    [/autoit]

    Das Resultat:
    Laufzeit, um den Speicher mit 5000 Screenshots zu füllen: 93473.26 ms
    Laufzeit für das Speichern der 5000 Screenshots : 19010.06 ms
    Laufzeit gesamt: 112483.32 ms
    44.45 Bilder/s, 22.5 ms/Bild

    Speicherverbrauch: 64MB

    Gruß,
    UEZ

    Auch am Arsch geht ein Weg vorbei...

    ¯\_(ツ)_/¯

  • Ja so ähnlich habe ich das am Anfang auch gemacht... Allerdings war mir das bei größeren Bildern zu langsam ;). Ich werde sehen, ob ich das vielleicht noch schneller machen kann... Aber dein Test ist shcon mal eine gute Vorlage, danke :D.