Jump to content

How to read pixel data using _GDIPlus_BitmapLockBits?


Xibalba
 Share

Recommended Posts

I'm experimenting with GetPixel functions (in order to find the fastest) and have this simple part in my script:

Local $iWidth = 150
Local $iHeight = 300
Local $array[$iHeight][$iWidth]

For $i = 1 To $iHeight Step 1
    For $j = 1 To $iWidth Step 1
        $array[$i-1][$j-1] = PixelGetColor($iHeight-1, $iWidth-1, $Handle)
    Next
Next

Trying to get the equivalent using the GDI+ library (more specifically, using the _GDIPlus_BitmapLockBits function) I've got this code:

Global $Handle = WinGetHandle("Paint")

Local $sWow64 = ""
If @AutoItX64 Then $sWow64 = "\Wow6432Node" ; X64 running support

; Initialize GDI+ library
_GDIPlus_Startup()

; Capture 32 bit bitmap to a HBITMAP
WinActivate($Handle)
Local $hBMP = _ScreenCapture_CaptureWnd("", $Handle) ; _ScreenCapture_CaptureWnd returns a handle to an HBITMAP if $sFileName is empty
Local $pBitmap = _GDIPlus_BitmapCreateFromHBITMAP($hBMP)

Local $iWidth = 150
Local $iHeight = 300
Local $array[$iHeight][$iWidth]

Local $BitmapData = _GDIPlus_BitmapLockBits($pBitmap, 0, 0, $iWidth, $iHeight, $GDIP_ILMREAD, $GDIP_PXF32RGB)

Local $Scan0 = DllStructGetData($BitmapData, "Scan0")
Local $Stride = DllStructGetData($BitmapData, "Stride")
Local $pixel = $Scan0 + $Stride + 4

Local $dPixel = DllStructCreate("dword", $pixel)

MsgBox(0, "mBox", "$Scan0=" & $Scan0 & "      $Stride=" & $Stride & "     pixel=" & $pixel)
For $i = 1 To $iHeight Step 1
    For $j = 1 To $iWidth Step 1
        ; <?>
    Next
Next

; Clean up resources
_GDIPlus_BitmapUnlockBits($pBitmap, $BitmapData)
_GDIPlus_BitmapDispose($pBitmap)
_WinAPI_DeleteObject($hBMP)
_GDIPlus_Shutdown()

And yeah, there's where I'm struggling at the moment (see ; <?> line).

I don't get any return value from the DllStructCreate() call (to the $dPixel variable)

The $pixel variable contains at this point 0x026D7A84.

I need help to proceed, and I don't fully understand the $Scan0 and $Stride usage. And how to convert pixel data to x and y coordinates for use in the loop (i and j).

My goal is to get the decimal value of each pixel into the array (exactly the same way as when using the PixelGetColor() method, see above)

hAlp :

Link to comment
Share on other sites

Look in the help file for _GDIPlus_BitmapLockBits() example. There is an example how to read the color values of an image.

 

Br,

UEZ

Please don't send me any personal message and ask for support! I will not reply!

Selection of finest graphical examples at Codepen.io

The own fart smells best!
Her 'sikim hıyar' diyene bir avuç tuz alıp koşma!
¯\_(ツ)_/¯  ٩(●̮̮̃•̃)۶ ٩(-̮̮̃-̃)۶ૐ

Link to comment
Share on other sites

UEZ,

I've of course looked at that example. I've tried again and again to recode that (and other examples in the forum) to work in my script.

Every time I'm reaching a dead end.

I need help to get my specific code to work (which by the way has a few fundamental differences to the example).

Br

Link to comment
Share on other sites

This will display the color values as decimal numbers. It is the modified code from the help file:

#include <Array.au3>
#include <Constants.au3>
#include <GUIConstantsEx.au3>
#include <GDIPlus.au3>

Example()

Func Example()
    ; X64 running support
    Local $sWow64 = ""
    If @AutoItX64 Then $sWow64 = "\Wow6432Node"

    ;get AutoIt install dir
    Local $sRegPath = "HKLM\SOFTWARE" & $sWow64 & "\AutoIt v3\AutoIt"

    Local $sFile = RegRead($sRegPath, "InstallDir") & "\Examples\GUI\logo4.gif"
    If Not FileExists($sFile) Then
        MsgBox(BitOR($MB_SYSTEMMODAL, $MB_ICONHAND), "", $sFile & " not found!", 30)
        Return False
    EndIf

    _GDIPlus_Startup()
    Local $hImage = _GDIPlus_ImageLoadFromFile($sFile) ;create an image object based on a file
    If @error Then
        _GDIPlus_Shutdown()
        MsgBox(BitOR($MB_SYSTEMMODAL, $MB_ICONHAND), "", "An error has occured - unable to load image!", 30)
        Return False
    EndIf

    Local $iW = _GDIPlus_ImageGetWidth($hImage), $iH = _GDIPlus_ImageGetHeight($hImage) ;get width and height of the image
    Local $hBitmap = _GDIPlus_BitmapCreateFromScan0($iW, $iH)
    Local $hContext = _GDIPlus_ImageGetGraphicsContext($hBitmap)
    _GDIPlus_GraphicsDrawImageRect($hContext, $hImage, 0, 0, $iW, $iH)

    Local $tBitmapData = _GDIPlus_BitmapLockBits($hBitmap, 0, 0, $iW, $iH, BitOR($GDIP_ILMWRITE, $GDIP_ILMREAD), $GDIP_PXF32ARGB) ;locks a portion of a bitmap for reading and writing. More infor at http://msdn.microsoft.com/en-us/library/windows/desktop/ms536298(v=vs.85).aspx
    Local $iScan0 = DllStructGetData($tBitmapData, "Scan0") ;get scan0 (pixel data) from locked bitmap
    Local $iSearchPixel = Int(0xFF000080), $iReplaceColor = 0x00000000 ;color format 0xAARRGGBB
    Local $tPixel = DllStructCreate("int[" & $iW * $iH & "];", $iScan0)
    Local $iRowOffset, $array[$iH][$iW]

    For $iY = 0 To $iH - 1
        $iRowOffset = $iY * $iW + 1
        For $iX = 0 To $iW - 1 ;get each pixel in each line and row
            $array[$iY][$iX] = DllStructGetData($tPixel, 1, $iRowOffset + $iX) ;get pixel color
        Next
    Next
    _GDIPlus_BitmapUnlockBits($hBitmap, $tBitmapData) ;unlocks a portion of a bitmap that was locked by _GDIPlus_BitmapLockBits
    
    _ArrayDisplay($array)

    ;cleanup resources
    _GDIPlus_ImageDispose($hImage)
    _GDIPlus_GraphicsDispose($hContext)
    _GDIPlus_BitmapDispose($hBitmap)
    _GDIPlus_Shutdown()
EndFunc   ;==>Example

Br,

UEZ

Please don't send me any personal message and ask for support! I will not reply!

Selection of finest graphical examples at Codepen.io

The own fart smells best!
Her 'sikim hıyar' diyene bir avuç tuz alıp koşma!
¯\_(ツ)_/¯  ٩(●̮̮̃•̃)۶ ٩(-̮̮̃-̃)۶ૐ

Link to comment
Share on other sites

UEZ,

I've tried again with your modified code.

I had to change this:

Local $BitmapData = _GDIPlus_BitmapLockBits($pBitmap, 0, 0, $iWidth, $iHeight, $GDIP_ILMREAD, $GDIP_PXF32RGB)

To this:

Local $tBitmapData = _GDIPlus_BitmapLockBits($hBitmap, 0, 0, $iW, $iH, BitOR($GDIP_ILMWRITE, $GDIP_ILMREAD), $GDIP_PXF32ARGB)

Else it would crash with exception.

However, with ARGB I do get values, but they are all negative - how to fix that in the fastest way?

If possible I'd like to use 32RGB because it (should?) be slightly faster.

Thanks so far =)

Link to comment
Share on other sites

Use Hex instead.

$array[$iY][$iX] = "0x" & Hex(DllStructGetData($tPixel, 1, $iRowOffset + $iX)) ;get pixel color

Br,

UEZ

Please don't send me any personal message and ask for support! I will not reply!

Selection of finest graphical examples at Codepen.io

The own fart smells best!
Her 'sikim hıyar' diyene bir avuç tuz alıp koşma!
¯\_(ツ)_/¯  ٩(●̮̮̃•̃)۶ ٩(-̮̮̃-̃)۶ૐ

Link to comment
Share on other sites

Ok so I went on and ended up with this code:

WinActivate($Handle)
Local $hBMP = _ScreenCapture_CaptureWnd("", $Handle) ; _ScreenCapture_CaptureWnd returns a handle to an HBITMAP if $sFileName is empty
Local $pBitmap = _GDIPlus_BitmapCreateFromHBITMAP($hBMP)

Local $iWidth = 150
Local $iHeight = 300
Local $array[$iHeight][$iWidth]

Local $BitmapData = _GDIPlus_BitmapLockBits($pBitmap, 0, 0, $iWidth, $iHeight, BitOR($GDIP_ILMWRITE, $GDIP_ILMREAD), $GDIP_PXF32ARGB)

Local $Scan0 = DllStructGetData($BitmapData, "Scan0")
Local $tPixel = DllStructCreate("int[" & $iWidth * $iHeight & "];", $Scan0)
Local $iRowOffset

For $iY = 0 To $iHeight - 1
    $iRowOffset = $iY * $iWidth + 1
    For $iX = 0 To $iWidth - 1 ;get each pixel in each line and row
        $array[$iY][$iX] = DllStructGetData($tPixel, 1, $iRowOffset + $iX) ;get pixel color
        $array[$iY][$iX] = Abs($array[$iY][$iX])
    Next
Next

_GDIPlus_BitmapUnlockBits($pBitmap, $BitmapData)

; Clean up resources

_GDIPlus_BitmapDispose($pBitmap)
_WinAPI_DeleteObject($hBMP)
_GDIPlus_Shutdown()

_ArrayDisplay($array)

But now I'm getting strange results.

For example, I get the value 1 at [1][1] to [299][1], the value 2830136 at [0][0] etc.

I got no idea where those colors come from, they certainly aren't at the top-left on my handle at least. Perhaps X and Y is mixed up somehow? I played around changing those some but got even more unexpected values. And even so, what kind of value is 1? - to my knowledge, black=0 and white=16777215 in AutoIt?

As said earlier, perhaps its easier to use 32RGB instead of 32ARGB - but how do I do that without crashing the script?

Edit: sfdsdfsdasdafsdf How do I mark my code as AutoIt code (colors) and not just general code (no colors)?

Edited by Xibalba
Link to comment
Share on other sites

What you are doing with Abs($array[$iY][$iX]) makes no sense because you are change the color values!

What is the problem to have hex color values?

It is much easier to see what color values you have when it is encoded in hex.

 

Br,

UEZ

Please don't send me any personal message and ask for support! I will not reply!

Selection of finest graphical examples at Codepen.io

The own fart smells best!
Her 'sikim hıyar' diyene bir avuç tuz alıp koşma!
¯\_(ツ)_/¯  ٩(●̮̮̃•̃)۶ ٩(-̮̮̃-̃)۶ૐ

Link to comment
Share on other sites

Based on your code it would probably be something like this:

Local $aArray[$iHeight][$iWidth]
Local $tBitmapData = _GDIPlus_BitmapLockBits($pBitmap, 0, 0, $iWidth, $iHeight, BitOR($GDIP_ILMWRITE, $GDIP_ILMREAD), $GDIP_PXF32ARGB)
Local $tPixel = DllStructCreate("dword[" & $iWidth * $iHeight & "];", DllStructGetData($tBitmapData, "Scan0"))
For $iX = 0 To $iWidth - 1
    For $iY = 0 To $iHeight - 1
        $aArray[$iY][$iX] = BitAND(DllStructGetData($tPixel, 1, $iY * $iWidth + $iX + 1), 0xFFFFFF)
    Next
Next
_GDIPlus_BitmapUnlockBits($pBitmap, $tBitmapData)
edit: cleaned a bit Edited by trancexx

♡♡♡

.

eMyvnE

Link to comment
Share on other sites

trancexx, absolutely great, thanks! :thumbsup:

For a slight performance boost, consider this:

Local $BitmapData = _GDIPlus_BitmapLockBits($pBitmap, 0, 0, $iWidth, $iHeight, $GDIP_ILMREAD, $GDIP_PXF32RGB)

Notice! the last 2 parameters.

How would I go about defining my DLLStructCreate then? (Hex or decimal return values doesn't matter - I'm looking for the fastest!)

Local $tPixel = DllStructCreate("dword[" & $iWidth * $iHeight & "];", $Scan0)

The following code then breaks (in the loop):

$array[$iY][$iX] = DllStructGetData($tPixel, 1, $iY * $iWidth + $iX + 1)

Probably this happens because I'm trying to get data from a place that doesn't exist(?).

I still can't completely picture the data in my head - how does the $tPixel "look" after it's DllStructCreate? -  Is it some sort of array? _ArrayDisplay() or something equivalent would make it much easier understand those data types.

Link to comment
Share on other sites

Sorry, but can you provide the name of the application you are using? What is your endgame or is it a secret?
LOL, I'm kidding. Pay no attention to those trolls in colors :).

$tPixel is array of integers representing pixel colors. That array isn't organized the way you would expect (the way picture looks), it's actually all backward because rendering engines expect it that way. It's following BMP format specification. That's why I used:

$iY * $iWidth + $iX + 1

...to get right pixel from the array at coordinates you expect them to be. If you would fill your array reading it flat (from the struct) then you would see your picture flipped and rotated. You could even do that and get correct data but before locking bitmap you would have to flip-rotate it (_GDIPlus_ImageRotateFlip).

♡♡♡

.

eMyvnE

Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
 Share

×
×
  • Create New...