Sign in to follow this  
Followers 0
DrGamut

Custom pixel scanning function.

16 posts in this topic

I need a function to scan a region of pixels and count instances of a particular color. I wish PixelSearch did this for me but it only locates the first instance of a color and then returns.

I'm attempting to do this with a nested For loop and I have run into a problem. It's wildly inconsistent, I can scan the exact same pixel region multiple times and get different results every time, even though the pixels have not changed (as an md5 check of bitmap screenshots confirm).

For $x = 195 To 350 Step 1
        For $y = 57 To 85 Step 1
            $color = PixelGetColor($x,$y)
            If $color == 16777215 Then
                $whitecount += 1
            EndIf
        Next
    Next

I've even tried editing the script to dump pixel values to a text file so I could compare them manually for debugging purposes. Many of the values are right and many are wrong. A MsgBox command ran after the For loops execute will occasionally report a different pixel color value than was dumped in the debug log.

What's gone wrong?

Share this post


Link to post
Share on other sites



I found that if I screen capture the source of the pixels and set it as my desktop background and run the script against that, the results are consistent.

When I run the script against the program drawing the pixels however, they are not (even though the pixels being counted /do not change/).

This is a mystery to me.

Share this post


Link to post
Share on other sites

#3 ·  Posted (edited)

Probably your coordmode vs. the type of coords you got.

I just made this, seems to be "fairly" (loosely used) fast.

MsgBox(64, 'Pixel Count', _PixelGetCount('Untitled - Notepad', 3, 3, 671, 261, 0x000000))
Func _PixelGetCount($hWnd, $iXStart, $iYStart, $iXEnd, $iYEnd, $nColor, $iPixCMode = 2)
    $OptPCM = Opt('PixelCoordMode', $iPixCMode)
    If IsString($hWnd) Then $hWnd = WinGetHandle($hWnd)
    Local $iAddX = 0, $iAddY = 0, $nCount, $iCC
    While 1
        If Not WinActive($hWnd) Then WinWaitActive($hWnd)
        PixelSearch($iXStart + $iAddX, $iYStart + $iAddY, $iXStart + $iAddX, $iYStart + $iAddY, $nColor)
        If Not @error Then $nCount += 1
        If $iAddX < ($iXEnd - $iXStart) Then 
            $iAddX += 1
        ElseIf $iAddY < ($iYEnd - $iYStart) Then 
            $iAddY += 1
            $iAddX = 0
        ElseIf $iAddX = ($iXEnd - $iXStart) And $iAddY = ($iYEnd - $iYStart) Then 
            ExitLoop
        EndIf
    WEnd
    Opt('PixelCoordMode', $OptPCM)  
    Return $nCount
EndFunc
Edited by SmOke_N

[center]Common sense plays a role in the basics of understanding AutoIt... If you're lacking in that, do us all a favor, and step away from the computer.[/center]

Share this post


Link to post
Share on other sites

I'm going to try your function (thanks for that!).

My CoordMode should be correct (1) as it's a full screen application, and the debug logs I created showed that it was picking up the right coordinates (the pixels at the top of the list were exactly correct).

Share this post


Link to post
Share on other sites

Your script suffers the same fate as mine. :)

Wildly fluctuating results.

The pixel region that I am trying to scan does change, but the pixels of the color I'm looking for (in this case, white) do not. If I take multiple screen captures and run the pixel counting script against those, the results are consistent. But running the script against the drawing application itself has not been successful.

Share this post


Link to post
Share on other sites

Your script suffers the same fate as mine. :P

Wildly fluctuating results.

The pixel region that I am trying to scan does change, but the pixels of the color I'm looking for (in this case, white) do not. If I take multiple screen captures and run the pixel counting script against those, the results are consistent. But running the script against the drawing application itself has not been successful.

Well, I'm not psychic :), what game is this?

[center]Common sense plays a role in the basics of understanding AutoIt... If you're lacking in that, do us all a favor, and step away from the computer.[/center]

Share this post


Link to post
Share on other sites

#7 ·  Posted (edited)

Well, I'm not psychic :), what game is this?

ArchLord, here's a screenshot to give you an idea of what I'm working with.

I want to count the white (or near white) pixels in 425,5 to 575,15. The green bar is translucent but the pixels comprising the text are static.

Posted Image

If only I could draw a bitmap with AutoIt using the pixel data from GetPixelColor and compare to a screenshot to see what is being done wrong.

EDIT: I wrote a function to dump the pixel data into a bitmap file, and sure enough, ArchLord is obscuring pixel captures.

Posted Image

Edited by DrGamut

Share this post


Link to post
Share on other sites

#include <String.au3>
;Example: PixelDumpBitmap('Untitled - Notepad',0,0,200,200,"notepad.bmp")
Func PixelDumpBitmap($hWnd, $left, $top, $right, $bottom, $file, $PCM = 0)
    $OldCM = Opt("ColorMode", 1)
    $OldPCM = Opt('PixelCoordMode', $PCM)
    If IsString($hWnd) Then $hWnd = WinGetHandle($hWnd)
    Local $width = $right - $left + 1,$height = $bottom - $top + 1,$arrayX = 0,$arrayY = 0,$bitmap[$width][$height]
    If Not WinActive($hWnd) Then WinWaitActive($hWnd)
    For $y = $top To $bottom Step 1
        For $x = $left To $right Step 1
            $bitmap[$arrayX][$arrayY] = Hex(PixelGetColor($x,$y),6)
            $arrayX += 1
        Next
        $arrayY += 1
        $arrayX = 0
    Next
    $headerType = "424D"
    $headerFileSize = _BigToLittle(Hex($width * $height * 3 + Mod($width, 4) * $height + 54,8))
    $headerReserved = "00000000"
    $headerOffset = "36000000"
    $headerInfoSize = "28000000"
    $headerWidth = _BigToLittle(Hex($width,8))
    $headerHeight = _BigToLittle(Hex($height,8))
    $headerPlanes = "0100"
    $headerBits = "1800"
    $headerCompression = "00000000"
    $headerImageSize = _BigToLittle(Hex($width * $height * 3 + Mod($width, 4) * $height,8))
    $headerXPelsPerMeter = "00000000"
    $headerYPelsPerMeter = "00000000"
    $headerClrUsed = "00000000"
    $headerClrImportant = "00000000"
    $bmp = FileOpen($file, 2)
    FileWrite($bmp, _HexToString($headerType & $headerFileSize & $headerReserved & $headerOffset & $headerInfoSize & $headerWidth & $headerHeight & $headerPlanes & $headerBits & $headerCompression & $headerImageSize & $headerXPelsPerMeter & $headerYPelsPerMeter & $headerClrUsed & $headerClrImportant))
    For $y = $height - 1 To 0 Step -1
        For $x = 0 To $width - 1 Step 1
            FileWrite($bmp, _HexToString($bitmap[$x][$y]))
        Next
        FileWrite($bmp, _HexToString(_StringRepeat("00",Mod($width, 4))))
    Next
    FileClose($bmp)
    Opt('ColorMode', $OldCM)  
    Opt('PixelCoordMode', $OldPCM)  
    Return 1
EndFunc
Func _BigToLittle($Motorola)
    Local $Intel = ""
    For $i = 1 To StringLen($Motorola) Step 2
        $Intel &= StringMid($Motorola,StringLen($Motorola) - $i,2)
    Next
    Return $Intel
EndFunc

Share this post


Link to post
Share on other sites

#9 ·  Posted (edited)

that would be a great function... except it gives me a sharing violation...

sharing violation occured while accessing *filename and path*

EDIT: Nevermind... I just have to wait a very long time after it is created to open it -.- like more then a minute!

Edited by Kickassjoe

What goes around comes around... Payback's a bitch.

Share this post


Link to post
Share on other sites

EDIT: Nevermind... I just have to wait a very long time after it is created to open it -.- like more then a minute!

If a minute is "such a long time", then my wife should have NO complaints!

[center]Common sense plays a role in the basics of understanding AutoIt... If you're lacking in that, do us all a favor, and step away from the computer.[/center]

Share this post


Link to post
Share on other sites

if you had to do that every time you wanted to see if you were at the right place in your script... and you had to wait a minute each time... and you checked like once every 5 mins (if it was possible...) that would be a lot of time for a loop... and in my scripts i would NOT check less than 2x/min!!! :) thats why a minute is such a long time... :P


What goes around comes around... Payback's a bitch.

Share this post


Link to post
Share on other sites

#13 ·  Posted (edited)

that would be a great function... except it gives me a sharing violation...

sharing violation occured while accessing *filename and path*

EDIT: Nevermind... I just have to wait a very long time after it is created to open it -.- like more then a minute!

As explained in: http://www.autoitscript.com/forum/index.php?showtopic=32409

This function is very slow. You have to wait a good while before the BMP will be fully created depending on how many pixels you scanned and the speed of your computer.

Perhaps this function can be optimized, I do not know how. You should wait until it returns a success value before attempting to open the BMP.

Can anyone guess as to what exactly is interfering with the pixel grabs of ArchLord based on the image I posted above?

Edited by DrGamut

Share this post


Link to post
Share on other sites

maybe... the sky is moving and is going through the label, or the grass / tree thing behind it is moving or something...

I'm not so sure, it might be something completely different, thats just an idea.


What goes around comes around... Payback's a bitch.

Share this post


Link to post
Share on other sites

maybe... the sky is moving and is going through the label, or the grass / tree thing behind it is moving or something...

I'm not so sure, it might be something completely different, thats just an idea.

Nothing is "moving" per say, but the bar the text is on is translucent and does change colors. Though humanly I can only notice it changing colors while moving, so that doesn't explain why it would mess up the pixel grabs that are done without moving.

Share this post


Link to post
Share on other sites

I have had similar problems and did some of my own test. I believe that your problem is that your game is double buffering the display. You may already know this, but the game probably used a ScreenBuffer1 and a ScreenBuffer2 and then displays from one buffer while rendering the next frame in the other buffer. Then it switches the buffers to display the one just rendered and starts work on the next fram in the other one. This is a standard technique from rendering displays and giving the illusion of seamless motion. This effectively doubles the game's framerate.

Also, some games use a blanking field where they erase the current image with black prior to displaying the next frame buffer.

AutoIT does not seem to be able to work around this as it is currently implemented. However, one idea might be to grab a block of pixels from the screen buffer in the AutoIT low level code (the C++) and then use the cached buffer to do pixel calculations. I was working on this, but got sidetracked.

I hope that helps a bit.

Share this post


Link to post
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
Sign in to follow this  
Followers 0