Jump to content

PixelSearch a region for a cluster of pixels.


SmOke_N
 Share

Recommended Posts

  • Moderators

On a topic here: http://www.autoitscript.com/forum/index.php?showtopic=95554

I was trying to understand what the OP was trying to do, I think I may have still missed the boat with him, but this function came out of it (so most of this is a simple copy and paste).

Things to know.

  • You must send an array of colors, these colors will be left to right, top to bottom (and must be the same ubound as the width*height).

    So if I have a 2 pixel width, and a 2 pixel height area I want to search, and for this example, my starting point is 0 for both x and y.

    My color array is [0xFF0000, 0x00FF00, 0x0000FF, 0x00FFFF]

    x-0, y-0 = 0xFF0000

    x-1, y-0 = 0x00FF00

    x-0, y-1 = 0x0000FF

    x-1, y-1 = 0x00FFFF

    In that example you see how you would set your array up (zero based array).

  • Shade variation must also be an array. The ubound must be the same as the ubound of the color array (width of pixel search * height of pixel search).

    You can do different shade variations for each pixel. My example will just show a constant variation of 3 for simplicity.

  • The parameters:

    x-start = the x starting position of the total search area

    y-start = the y starting position of the total search area

    x-end = the x ending position of the total search area

    y-end = the y ending position of the total search area

    width = total number of pixels to search (left to right) once first pixel color is found

    height = total number of pixels to search (top to bottom) once the first pixel color is found

    colors = array of colors to search in specific order (explained above) one the match is found [0] is used for the first search pattern

    shade = array of shade variations for each position found [0] is used for the first search pattern

    step = number of steps to make like pixelsearch main function, this is only used in the main search for patterns, once a match on the first color is found, the rest of the search is step 1

    hwnd = handle to window if you want to use one

    quick match = If you only want the first match, set to true, and the array [n][n] will hold your values, otherwise default is to return all matches

  • The return is a two dimensional array:

    [0][0] = total number of matches found

    [n][0] = x starting position the match was found

    [n][1] = y starting position the match was found

  • Errors:

    1 = the color variable sent was either not an array or the ubound did not match the total value of the width*height

    2 = the shade variable sent was either not an array or the ubound did not match the total value of the width*height

    3 = No match found

Guess I could have written a header, just didn't feel like it.

; Example
#include <array.au3>

; Array of colors UBound must match number of pixels searching
Local $a_clr[9] = [0xFF4AA5, 0xFF4AA5, 0xFF4AA5, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFF4AA5, 0xFF4AA5]
; Array of variations UBound must match number of pixels searching
Local $a_vrn[9] = [3,3,3,3,3,3,3,3,3]

Local $a_found = _Pixel_Region_FindMatch(0, 0, 1678, 945, 3, 3, $a_clr, $a_vrn)
If @error Then
    MsgBox(0, "Error", "Error was: " & @error)
Else
    _ArrayDisplay($a_found)
EndIf

Func _Pixel_Region_FindMatch($i_x, $i_y, $i_x_end, $i_y_end, $i_width, $i_height, $a_colors, $a_shade, $i_step = 1, $h_wnd = 0, $f_quick_match = False)
    Local $i_total = $i_width * $i_height
    Local $a_col = $a_colors, $a_var = $a_shade
    $i_width -= 1
    $i_height -= 1
    
    If (IsArray($a_colors) = 0) Or (UBound($a_colors) <> $i_total) Then Return SetError(1, 0, 0)
    If (IsArray($a_shade) = 0) Or (UBound($a_shade) <> $i_total) Then Return SetError(2, 0, 0)
    
    Local $i_c, $i_c_n, $i_x_c, $i_y_c
    Local $a_found, $i_y_n, $i_x_n, $i_c_n, $i_match
    Local $a_ret[((Abs($i_x_end - $i_x) + 1) * (Abs($i_y_end - $i_y) + 1)) + 1][2]
    
; Simple search
    For $i_y_c = $i_y To $i_y_end Step $i_step
        $i_x_c = $i_x
        While 1
            $a_found = PixelSearch($i_x_c, $i_y_c, $i_x_end, $i_y_c, $a_colors[0], $a_shade[0], $i_step, $h_wnd)
            If @error Then ExitLoop
            
            If ($a_found[0] + $i_width > $i_x_end) Then ExitLoop
            If ($a_found[1] + $i_height > $i_y_end) Then ExitLoop
            
            $i_match = 0
            For $i_y_n = $a_found[1] To $i_height + $a_found[1]
                For $i_x_n = $a_found[0] To $i_width + $a_found[0]
                    PixelSearch($i_x_n, $i_y_n, $i_x_n, $i_y_n, $a_colors[$i_match], $a_shade[$i_match], 1, $h_wnd)
                    If @error Then ExitLoop 2
                    $i_match += 1
                    If $i_match = $i_total Then
                        $i_c += 1
                        $a_ret[$i_c][0] = $a_found[0]
                        $a_ret[$i_c][1] = $a_found[1]
                        If $f_quick_match Then ExitLoop 4
                    EndIf
                Next
            Next
            
            $i_x_c = $a_found[0] + 1
        WEnd
    Next
    
    If $i_c = 0 Then Return SetError(3, 0, 0)
    ReDim $a_ret[$i_c + 1][2]
    $a_ret[0][0] = $i_c
    Return $a_ret
EndFunc
The example above found all the "@" symbols in scite for me (obviously the unique search was only for that symbol).

I expected this to be to slow to use.

I was pleasantly dissapointed there!

Searching a 1678x945 area, the example provided found 7 "@" symbols in scite in 250 ms, 10 in 350 ms. Should give you ideas on how fast/slow it is.

Also, this is a simple helper function if you want to use it, will get all the pixel colors and positions in a 2D-3 index array (Used it to help with the color array).

; Should only be used for small regions, didn't feel like writing out bitblt method
Func _Pixel_GetRegion($i_x_start, $i_y_start, $i_x_end, $i_y_end, $i_step = 1, $h_wnd = 0)
    Local $a_ret[((Abs($i_x_end - $i_x_start) + 1) * (Abs($i_y_end - $i_y_start) + 1)) + 1][3], $i_c = 0
    For $i_y = $i_y_start To $i_y_end Step $i_step
        For $i_x = $i_x_start To $i_x_end Step $i_step
            $i_c += 1
            $a_ret[$i_c][0] = $i_x
            $a_ret[$i_c][1] = $i_y
            $a_ret[$i_c][2] = PixelGetColor($i_x, $i_y, $h_wnd)
        Next
    Next
    ReDim $a_ret[$i_c + 1][3]
    $a_ret[0][0] = $i_c
    Return $a_ret
EndFunc
Edited by SmOke_N

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.

Link to comment
Share on other sites

  • 4 months later...

On a topic here: http://www.autoitscript.com/forum/index.php?showtopic=95554

I was trying to understand what the OP was trying to do, I think I may have still missed the boat with him, but this function came out of it (so most of this is a simple copy and paste).

Things to know.

[*]You must send an array of colors, these colors will be left to right, top to bottom (and must be the same ubound as the width*height).

So if I have a 2 pixel width, and a 2 pixel height area I want to search, and for this example, my starting point is 0 for both x and y.

Smoke,

This is pretty good - I tested this as a replacement for a version I wrote that does basically the same thing, and yours seems faster, although I'm not sure that it correctly handles cases where there are false matches earlier on the same horizontal line?

In any case, there's one major problem with searching like this: if you're searching for a moving object, e.g. a sprite, the search can easily fail if the object was moving up the screen.

i.e. at time t, the pixel pattern is on line 201. You search line 200 and find no match.

At time t+k, the pixel pattern has moved up to line 200. You search 201 (and onwards) and find no match. Search ends having missed the object.

It's basically a concurrency problem... so I guess the solution to this is to save a snapshot of the search area to a buffer and search that, so that searching is atomic. Any tips on how to do this gracefully and still be able to use the nice API like PixelSearch and PixelChecksum?

Maybe you could draw a snapshot of the region to another window somewhere (perhaps even outside visible screen bounds) and search that normally, but it's a bit smelly.

Link to comment
Share on other sites

_ScreenCapture_Capture gets a region of the screen, using Manadar's pixel from memory UDF you could search it, combine Smoke_N's technique with those two, and you've neatly bypassed the concurrency issue. Performance will depend on how you put it together, but I don't see any problems. I'll take a whack at putting it together.

Very cool, Smoke!

Link to comment
Share on other sites

  • 1 month later...

On a topic here: http://www.autoitscript.com/forum/index.php?showtopic=95554

I was trying to understand what the OP was trying to do, I think I may have still missed the boat with him, but this function came out of it (so most of this is a simple copy and paste).

Things to know.

  • You must send an array of colors, these colors will be left to right, top to bottom (and must be the same ubound as the width*height).

    So if I have a 2 pixel width, and a 2 pixel height area I want to search, and for this example, my starting point is 0 for both x and y.

    My color array is [0xFF0000, 0x00FF00, 0x0000FF, 0x00FFFF]

    x-0, y-0 = 0xFF0000

    x-1, y-0 = 0x00FF00

    x-0, y-1 = 0x0000FF

    x-1, y-1 = 0x00FFFF

    In that example you see how you would set your array up (zero based array).

  • Shade variation must also be an array. The ubound must be the same as the ubound of the color array (width of pixel search * height of pixel search).

    You can do different shade variations for each pixel. My example will just show a constant variation of 3 for simplicity.

  • The parameters:

    x-start = the x starting position of the total search area

    y-start = the y starting position of the total search area

    x-end = the x ending position of the total search area

    y-end = the y ending position of the total search area

    width = total number of pixels to search (left to right) once first pixel color is found

    height = total number of pixels to search (top to bottom) once the first pixel color is found

    colors = array of colors to search in specific order (explained above) one the match is found [0] is used for the first search pattern

    shade = array of shade variations for each position found [0] is used for the first search pattern

    step = number of steps to make like pixelsearch main function, this is only used in the main search for patterns, once a match on the first color is found, the rest of the search is step 1

    hwnd = handle to window if you want to use one

    quick match = If you only want the first match, set to true, and the array [n][n] will hold your values, otherwise default is to return all matches

  • The return is a two dimensional array:

    [0][0] = total number of matches found

    [n][0] = x starting position the match was found

    [n][1] = y starting position the match was found

  • Errors:

    1 = the color variable sent was either not an array or the ubound did not match the total value of the width*height

    2 = the shade variable sent was either not an array or the ubound did not match the total value of the width*height

    3 = No match found

Guess I could have written a header, just didn't feel like it.

; Example
#include <array.au3>

; Array of colors UBound must match number of pixels searching
Local $a_clr[9] = [0xFF4AA5, 0xFF4AA5, 0xFF4AA5, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFF4AA5, 0xFF4AA5]
; Array of variations UBound must match number of pixels searching
Local $a_vrn[9] = [3,3,3,3,3,3,3,3,3]

Local $a_found = _Pixel_Region_FindMatch(0, 0, 1678, 945, 3, 3, $a_clr, $a_vrn)
If @error Then
    MsgBox(0, "Error", "Error was: " & @error)
Else
    _ArrayDisplay($a_found)
EndIf

Func _Pixel_Region_FindMatch($i_x, $i_y, $i_x_end, $i_y_end, $i_width, $i_height, $a_colors, $a_shade, $i_step = 1, $h_wnd = 0, $f_quick_match = False)
    Local $i_total = $i_width * $i_height
    Local $a_col = $a_colors, $a_var = $a_shade
    $i_width -= 1
    $i_height -= 1
    
    If (IsArray($a_colors) = 0) Or (UBound($a_colors) <> $i_total) Then Return SetError(1, 0, 0)
    If (IsArray($a_shade) = 0) Or (UBound($a_shade) <> $i_total) Then Return SetError(2, 0, 0)
    
    Local $i_c, $i_c_n, $i_x_c, $i_y_c
    Local $a_found, $i_y_n, $i_x_n, $i_c_n, $i_match
    Local $a_ret[((Abs($i_x_end - $i_x) + 1) * (Abs($i_y_end - $i_y) + 1)) + 1][2]
    
; Simple search
    For $i_y_c = $i_y To $i_y_end Step $i_step
        $i_x_c = $i_x
        While 1
            $a_found = PixelSearch($i_x_c, $i_y_c, $i_x_end, $i_y_c, $a_colors[0], $a_shade[0], $i_step, $h_wnd)
            If @error Then ExitLoop
            
            If ($a_found[0] + $i_width > $i_x_end) Then ExitLoop
            If ($a_found[1] + $i_height > $i_y_end) Then ExitLoop
            
            $i_match = 0
            For $i_y_n = $a_found[1] To $i_height + $a_found[1]
                For $i_x_n = $a_found[0] To $i_width + $a_found[0]
                    PixelSearch($i_x_n, $i_y_n, $i_x_n, $i_y_n, $a_colors[$i_match], $a_shade[$i_match], 1, $h_wnd)
                    If @error Then ExitLoop 2
                    $i_match += 1
                    If $i_match = $i_total Then
                        $i_c += 1
                        $a_ret[$i_c][0] = $a_found[0]
                        $a_ret[$i_c][1] = $a_found[1]
                        If $f_quick_match Then ExitLoop 4
                    EndIf
                Next
            Next
            
            $i_x_c = $a_found[0] + 1
        WEnd
    Next
    
    If $i_c = 0 Then Return SetError(3, 0, 0)
    ReDim $a_ret[$i_c + 1][2]
    $a_ret[0][0] = $i_c
    Return $a_ret
EndFunc
The example above found all the "@" symbols in scite for me (obviously the unique search was only for that symbol).

I expected this to be to slow to use.

I was pleasantly dissapointed there!

Searching a 1678x945 area, the example provided found 7 "@" symbols in scite in 250 ms, 10 in 350 ms. Should give you ideas on how fast/slow it is.

Also, this is a simple helper function if you want to use it, will get all the pixel colors and positions in a 2D-3 index array (Used it to help with the color array).

; Should only be used for small regions, didn't feel like writing out bitblt method
Func _Pixel_GetRegion($i_x_start, $i_y_start, $i_x_end, $i_y_end, $i_step = 1, $h_wnd = 0)
    Local $a_ret[((Abs($i_x_end - $i_x_start) + 1) * (Abs($i_y_end - $i_y_start) + 1)) + 1][3], $i_c = 0
    For $i_y = $i_y_start To $i_y_end Step $i_step
        For $i_x = $i_x_start To $i_x_end Step $i_step
            $i_c += 1
            $a_ret[$i_c][0] = $i_x
            $a_ret[$i_c][1] = $i_y
            $a_ret[$i_c][2] = PixelGetColor($i_x, $i_y, $h_wnd)
        Next
    Next
    ReDim $a_ret[$i_c + 1][3]
    $a_ret[0][0] = $i_c
    Return $a_ret
EndFunc

Noob question:How would you get the actual coords in a variable, like the PixelSearch function?

Thanks in advance.

Link to comment
Share on other sites

  • 1 month later...

how come when i run this script i get a msgbox with Error was:3

Errors:

1 = the color variable sent was either not an array or the ubound did not match the total value of the width*height

2 = the shade variable sent was either not an array or the ubound did not match the total value of the width*height

3 = No match found

:D

This world is crazy

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

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...