Sign in to follow this  
Followers 0
Delta

I need some help optimizing a file based pixel search

21 posts in this topic

#1 ·  Posted (edited)

I have a function that searches an image file for a specific pixel color. It works great but it's EXTREMELY slow. Does anyone know of a way to optimize it? I'd even settle for a whole new function. Please ignore the indenting the AutoIt code tags are a bit weird.

; (Image file location, Pixel color, Tolerance)
Func ImageGetPP($image, $pColor, $pTolerance)
    Local $hImage, $iWidth, $iHeight, $hBitmap, $iBitmap, $stride, $Scan0, $v_Buffer, $v_Value, $Pos[2]
    $hImage = _GDIPlus_ImageLoadFromFile($image)
    $iWidth = _GDIPlus_ImageGetWidth($hImage)
    $iHeight = _GDIPlus_ImageGetHeight($hImage)
    $hBitmap = _GDIPlus_BitmapCloneArea($hImage, 0, 0, $iWidth, $iHeight, $GDIP_PXF32ARGB)
    $iBitmap = _GDIPlus_BitmapLockBits($hBitmap, 0, 0, $iWidth, $iHeight, BitOR($GDIP_ILMREAD, $GDIP_ILMWRITE), $GDIP_PXF32ARGB)
    
    $R = _ColorGetRed($pColor)
    $G = _ColorGetGreen($pColor)
    $B = _ColorGetBlue($pColor)

    $stride = DllStructGetData($iBitmap, "stride")
    $Scan0 = DllStructGetData($iBitmap, "Scan0")
    For $i = 0 To $iWidth - 1
    For $j = 0 To $iHeight - 1
    $v_Buffer = DllStructCreate("dword", $Scan0 + ($j * $stride) + ($i * 4))
    $v_Value = DllStructGetData($v_Buffer, 1)
            $CPR = _ColorGetRed($v_Value)
            $CPG = _ColorGetGreen($v_Value)
            $CPB = _ColorGetBlue($v_Value)
            If $CPR + $pTolerance >= $R And $CPG + $pTolerance >= $G And $CPB + $pTolerance >= $B Then
    If $CPR - $pTolerance <= $R And $CPG - $pTolerance <= $G And $CPB - $pTolerance <= $B Then
                    $Pos[0] = $i
                    $Pos[1] = $j
                    _GDIPlus_BitmapUnlockBits($hBitmap, $iBitmap)
                    _GDIPlus_BitmapDispose($hBitmap)
                    _GDIPlus_ImageDispose($hImage)
                    Return $Pos
                EndIf
    EndIf
    Next
    Next
    _GDIPlus_BitmapUnlockBits($hBitmap, $iBitmap)
    _GDIPlus_BitmapDispose($hBitmap)
    _GDIPlus_ImageDispose($hImage)
    Return 0
EndFunc
Edited by Deltaforce229

[size="1"]Please stop confusing "how to" with "how do"[/size]

Share this post


Link to post
Share on other sites



suggestion: with "$v_Buffer = DllStructCreate ..." your buffering only a single pixel. You might speed things up by buffering a single line, or the whole image.


"Straight_and_Crooked_Thinking" : A "classic guide to ferreting out untruths, half-truths, and other distortions of facts in political and social discussions."
"The Secrets of Quantum Physics" : New and excellent 2 part documentary on Quantum Physics by Jim Al-Khalili. (Dec 2014)

"Believing what you know ain't so" ...

Knock Knock ...
 

Share this post


Link to post
Share on other sites

#4 ·  Posted (edited)

maybe this solves your problem....

#include <GUIConstantsEx.au3>
#include <WinAPI.au3>
#include <GDIPlus.au3>
#include <GDIPlusConstants.au3>
#include <StructureConstants.au3>
#include <Array.au3>

$bitmapfile = "test.bmp"  ;works with JPG too...

If FileExists(@ScriptDir & "\prospeed.dll") Then
     global $Prospeedflag = True
     global $prospeed_dll = DllOpen(@ScriptDir & "\ProSpeed.dll")
 Else
     global $prospeedflag= false
endif


$t = TimerInit()
$aCoord = _findpixelinbmpfile($bitmapfile, 0xFFAABB) ;only 24bpp,  color in RGB
$m = TimerDiff($t)
If IsArray($aCoord) Then
 MsgBox(0, "col / row", $aCoord[0] & " / " & $aCoord[1] & @CRLF & $m)
Else
 MsgBox(0, 0, "pixel not found  " & $m)
EndIf

_GDIPlus_Shutdown()


Func _findpixelinbmpfile($bmpfile, $color) ;only 24bpp, returns 0 if miss, returns array with x- and y-koordinates
 $t=timerinit()
 Dim $Coord[2]
 _GDIPlus_Startup()
 $pBitmap = _GDIPlus_BitmapCreateFromFile($bmpfile)
 If @error Then MsgBox(0, "", "Error BitmapCreateFromFile")
 $BitmapData = _GDIPlus_BitmapLockBits($pBitmap, 0, 0, _GDIPlus_ImageGetWidth($pBitmap), _GDIPlus_ImageGetHeight($pBitmap), $GDIP_ILMREAD, $GDIP_PXF24RGB)
 If @error Then MsgBox(0, "", "Error locking region " & @error)
 $stride = DllStructGetData($BitmapData, "Stride");Stride - Offset, in bytes, between consecutive scan lines of the bitmap. If the stride is positive, the bitmap is top-down. If the stride is negative, the bitmap is bottom-up.
 $Width = DllStructGetData($BitmapData, "Width");Image width - Number of pixels in one scan line of the bitmap.
 $Height = DllStructGetData($BitmapData, "Height");Image height - Number of scan lines in the bitmap.
 $pixelFormat = DllStructGetData($BitmapData, "PixelFormat");Pixel format - Integer that specifies the pixel format of the bitmap
 $Scan0 = DllStructGetData($BitmapData, "Scan0");Scan0 - Pointer to the first (index 0) scan line of the bitmap.


 if $prospeedflag=True then
     $struct = DllStructCreate("byte[3]") ;for the color
     DllStructSetData($struct, 1, $color) ;writes the 3-byte colour into the struct
     $ptr = DllStructGetPtr($struct)
     $i = FindBytes( $Scan0, 0, (Abs($stride) * ($Height)), $ptr, 3) + 1
 Else
     $pixeldata = DllStructCreate("byte[" & (Abs($stride) * ($Height)) & "]", $Scan0)
     $BMPData = BinaryToString(DllStructGetData($pixeldata, 1))
     $bincol = StringTrimRight(BinaryToString($color), 1) ;string of the color
     $i = StringInStr($BMPData, $bincol) ;first position of the 3 color-bytes
 EndIf

 While $i <> 0
     $r = Mod($i, $stride) + 2
     If Mod($r, 3) = 0 Then ;only if the 3 color-bytes are in one pixel
         $Coord[0] = $r / 3
         $Coord[1] = Int($i / $stride) + 1
         _GDIPlus_BitmapUnlockBits($pBitmap, $BitmapData)
         _GDIPlus_ImageDispose($pBitmap)
         _GDIPlus_Shutdown()
         Return $Coord
     Else
         If $Prospeedflag Then
             $i = FindBytes( $Scan0, $i + 1, (Abs($stride) * ($Height))-$i, $ptr, 3) + 1
         Else
             $i = StringInStr($BMPData, $bincol, 0, 1, $i + 1) ;start an letzter position
         EndIf
     EndIf
 WEnd

 _GDIPlus_BitmapUnlockBits($pBitmap, $BitmapData)
 _GDIPlus_ImageDispose($pBitmap)
 _GDIPlus_Shutdown()
 Return 0

EndFunc   ;==>_findpixelinbmpfile


Func FindBytes( $b, $O, $a, $S, $l) ;needed if prospeed.dll is used
 Local $S_FindBytes = DllCall($prospeed_dll, "long", "FindBytes", _
         "long", $b, _
         "long", $O, _
         "long", $a, _
         "long", $S, _
         "long", $l)
 Return $S_FindBytes[0]
EndFunc   ;==>FindBytes
If the prospeed.dll is in the scriptdirectory, then the dll is used automatically. If not, AutoIt stringinstr() (which is very fast with not very long strings) finds the pixelposition. 

/EDIT/ opendll outside the function speeds up multiple use of the function

Edited by AndyG

Share this post


Link to post
Share on other sites

#5 ·  Posted (edited)

Thanks guys prospeed did the trick. AndyG thanks for the code it works like a dream. I have one gripe though and that's that I needed the pixel tolerance portion of the code.

Edit: I think I got it figured out though. If it turns out I'm wrong I'll post back. Thanks again.

Edited by Deltaforce229

[size="1"]Please stop confusing "how to" with "how do"[/size]

Share this post


Link to post
Share on other sites

Alright so I didn't figure out how to add tolerance to it and prospeed seems to be causing AutoIt to crash with "autoit3.exe has encountered a problem and needs to close. We are sorry for the inconvenience." when it finds the color. If it helps I'm using the script to track a colored target on my camera.


[size="1"]Please stop confusing "how to" with "how do"[/size]

Share this post


Link to post
Share on other sites

Hi,

because you did´nt post your script i don´t know exactly what you are looking for.

Searching for a "tolerance" is nothing but vary the colors.

The color is 0xRRGGBB so if you are looking for a color with a tolerance of 10 you could vary RR+-10, GG+-10, BB+-10.

$tolerance = 10
$color = 0xAABBCC

$Red     = BitAND(BitShift($color, 16), 0xFF) ;red
$Green     = BitAND(BitShift($color, 8), 0xFF) ;green
$Blue     = BitAND($color, 0xFF) ;blue

For $vary_Red = $Red - $tolerance To $Red + $tolerance   ;red from AA-10 to AA+10
    For $vary_Green = $Green - $tolerance To $Green + $tolerance ;green from BB-10 to BB+10
        For $vary_blue = $Blue To $Blue + $tolerance
            $color = $vary_blue + 256 * $vary_Green + 256 * 256 * $vary_Red
            ConsoleWrite( hex($color,6) & @CRLF )
        Next
    Next
Next

Share this post


Link to post
Share on other sites

The color is 0xRRGGBB so if you are looking for a color with a tolerance of 10 you could vary RR+-10, GG+-10, BB+-10.

That sound like a major speed bump, a tolerance of 10 give a whopping 9261 variations.

I wounder how much faster ProSpeed is compared to a optimized version of the original posted principal when it comes to just scanning for a particular pixel-color. ...


"Straight_and_Crooked_Thinking" : A "classic guide to ferreting out untruths, half-truths, and other distortions of facts in political and social discussions."
"The Secrets of Quantum Physics" : New and excellent 2 part documentary on Quantum Physics by Jim Al-Khalili. (Dec 2014)

"Believing what you know ain't so" ...

Knock Knock ...
 

Share this post


Link to post
Share on other sites

That sound like a major speed bump, a tolerance of 10 give a whopping 9261 variations.

In prospeed.dll,  there is the  "comparebytes" function, which is able to compare two bytes (or bunch of bytes, or files) with a tolerance. This is much more faster if you can use it in your application.

Share this post


Link to post
Share on other sites

#10 ·  Posted (edited)

That sound like a major speed bump, a tolerance of 10 give a whopping 9261 variations.

I wounder how much faster ProSpeed is compared to a optimized version of the original posted principal when it comes to just scanning for a particular pixel-color. ...

In prospeed.dll, there is the "comparebytes" function, which is able to compare two bytes (or bunch of bytes, or files) with a tolerance. This is much more faster if you can use it in your application.

It seems to be faster but when you add a tolerance it's not as fast. Unfortunatly I can't give you a direct answer because as soon as the "While $i <> 0" loop kicks in prospeed causes AutoIt to crash. It was working fine when I first tried it out then I modded the code (I just stripped out the none prospeed stuff AndyG posted.) a bit and it crashed. So I reverted it back and it still crashes.

It's driving me up the wall. ;)

Edit: Since you need it to more accurately help me here's the full of what I'm doing.

;=========================
; Configuration Options
;=========================
$W = 320
$H = 240
$Color = 0xFFFFFF
$Tolerance = 10

;=========================
; Main Script
;=========================
#include <GUIConstants.au3>
#include <WindowsConstants.au3>
#include <StaticConstants.au3>
#include <Color.au3>
#include <GDIPlus.au3>
#include <Webcam.au3>

Opt("GUIOnEventMode", 1)

$CTGui = GUICreate("CamTracker", $W, $H + 25, -1, -1, BitOR($WS_MINIMIZEBOX, $WS_SYSMENU, $WS_CAPTION, $WS_POPUP, $WS_POPUPWINDOW, $WS_GROUP, $WS_BORDER))
$CTS = GUICtrlCreateLabel("Starting CamTracker...", 0, $H + 5, $W, 20, $SS_CENTER)
GUICtrlSetFont(-1, 10, 800)
GUISetState()

$CTGGui = GUICreate("Tracking Grid", $W + 4, $H + 4, -1, -1, BitOR($WS_CAPTION, $WS_POPUP, $WS_BORDER, $WS_CLIPSIBLINGS), 0, $CTGui)
GUISetState()
_Reposition("CamTracker", "Tracking Grid")

GUISetOnEvent($GUI_EVENT_CLOSE, "GuiEvent", $CTGui)
GUISetOnEvent($GUI_EVENT_MINIMIZE, "GuiEvent", $CTGui)
GUISetOnEvent($GUI_EVENT_RESTORE, "GuiEvent", $CTGui)

If FileExists(@ScriptDir & "\prospeed.dll") Then
    Global $prospeed_dll = DllOpen(@ScriptDir & "\ProSpeed.dll")
Else
    MsgBox(48, "Missing DLL", "Missing ProSpeed.dll")
    Exit
EndIf

_GDIPlus_Startup()
$hWnd = WinGetHandle("Tracking Grid")
$hGraphic = _GDIPlus_GraphicsCreateFromHWND($hWnd)
$hBrush = _GDIPlus_BrushCreateSolid(0xFFFF0000)
$hPen = _GDIPlus_PenCreate(0x00000000, 1)
_DrawGrid($hGraphic, $hPen, 1, 1, $W, $H, 10)
$CTCid = _WebcamOpen($CTGui, 0, 0, $W, $H)
Dim $lX, $lY, $lState = True, $Found = False
While 1
    _Reposition("CamTracker", "Tracking Grid")
    $Snap = _WebcamSnap($CTCid, "snap.bmp")
    
    $Red = BitAND(BitShift($Color, 16), 0xFF) ;red
    $Green = BitAND(BitShift($Color, 8), 0xFF) ;green
    $Blue = BitAND($Color, 0xFF) ;blue
    
    $Found = False
    $Count = 0
    $Timer = TimerInit()
    For $vary_Red = $Red - $Tolerance To $Red + $Tolerance ;red from AA-10 to AA+10
        For $vary_Green = $Green - $Tolerance To $Green + $Tolerance ;green from BB-10 to BB+10
            For $vary_blue = $Blue To $Blue + $Tolerance
                $Count = $Count + 1
                GUICtrlSetData($CTS, "Searching("&$Count&")")
                $Color = $vary_blue + 256 * $vary_Green + 256 * 256 * $vary_Red
                $PixelPos = _findpixelinbmpfile("snap.bmp", Hex($Color, 6))
                If IsArray($PixelPos) Then
                    $Found = True
                    ExitLoop(3)
                EndIf
            Next
        Next
    Next
    ConsoleWrite(TimerDiff($Timer))
    If $Found = True Then
        If $PixelPos[0] <> $lX And $PixelPos[1] <> $lY Then
            $lState = True
            GUICtrlSetData($CTS, "Target found at " & $PixelPos[0] & ":" & $PixelPos[1])
            _GDIPlus_GraphicsClear($hGraphic, 0xFFD4D0C8)
            _DrawGrid($hGraphic, $hPen, 1, 1, $W, $H, 10)
            _GDIPlus_GraphicsFillEllipse($hGraphic, $PixelPos[0] + 1, $PixelPos[1] + 1, 5, 5, $hBrush)
        EndIf
    Else
        If $lState = True Then
            $lState = False
            GUICtrlSetData($CTS, "Target not found!")
            _GDIPlus_GraphicsClear($hGraphic, 0xFFD4D0C8)
            _DrawGrid($hGraphic, $hPen, 1, 1, $W, $H, 10)
        EndIf
    EndIf
    Sleep(100)
WEnd

;=========================
; Functions
;=========================

Func GuiEvent()
    Select
        Case @GUI_CtrlId = $GUI_EVENT_CLOSE
            _WebcamClose($CTCid)
            _GDIPlus_PenDispose($hPen)
            _GDIPlus_BrushDispose($hBrush)
            _GDIPlus_GraphicsDispose($hGraphic)
            _GDIPlus_Shutdown()
            DllClose($prospeed_dll)
            Exit

        Case @GUI_CtrlId = $GUI_EVENT_RESTORE
            _DrawGrid($hGraphic, $hPen, 1, 1, $W, $H, 10)

    EndSelect
EndFunc ;==>GuiEvent

Func _findpixelinbmpfile($bmpfile, $Color) ;only 24bpp, returns 0 if miss, returns array with x- and y-koordinates
    Dim $Coord[2]
    $pBitmap = _GDIPlus_BitmapCreateFromFile($bmpfile)
    If @error Then MsgBox(0, "", "Error BitmapCreateFromFile")
    $BitmapData = _GDIPlus_BitmapLockBits($pBitmap, 0, 0, _GDIPlus_ImageGetWidth($pBitmap), _GDIPlus_ImageGetHeight($pBitmap), $GDIP_ILMREAD, $GDIP_PXF24RGB)
    If @error Then MsgBox(0, "", "Error locking region " & @error)
    $stride = DllStructGetData($BitmapData, "Stride");Stride - Offset, in bytes, between consecutive scan lines of the bitmap. If the stride is positive, the bitmap is top-down. If the stride is negative, the bitmap is bottom-up.
    $Width = DllStructGetData($BitmapData, "Width");Image width - Number of pixels in one scan line of the bitmap.
    $Height = DllStructGetData($BitmapData, "Height");Image height - Number of scan lines in the bitmap.
    $pixelFormat = DllStructGetData($BitmapData, "PixelFormat");Pixel format - Integer that specifies the pixel format of the bitmap
    $Scan0 = DllStructGetData($BitmapData, "Scan0");Scan0 - Pointer to the first (index 0) scan line of the bitmap.



    $struct = DllStructCreate("byte[3]") ;for the color
    DllStructSetData($struct, 1, $Color) ;writes the 3-byte colour into the struct
    $ptr = DllStructGetPtr($struct)
    $i = FindBytes($Scan0, 0, (Abs($stride) * ($Height)), $ptr, 3) + 1


    While $i <> 0
        ConsoleWrite("$i <> 0")
        $r = Mod($i, $stride) + 2
        If Mod($r, 3) = 0 Then ;only if the 3 color-bytes are in one pixel
            $Coord[0] = $r / 3
            $Coord[1] = Int($i / $stride) + 1
            _GDIPlus_BitmapUnlockBits($pBitmap, $BitmapData)
            _GDIPlus_ImageDispose($pBitmap)
            Return $Coord
        Else
            $i = FindBytes($Scan0, $i + 1, (Abs($stride) * ($Height)), $ptr, 3) + 1
        EndIf
    WEnd

    _GDIPlus_BitmapUnlockBits($pBitmap, $BitmapData)
    _GDIPlus_ImageDispose($pBitmap)
    Return 0

EndFunc ;==>_findpixelinbmpfile


Func FindBytes($b, $O, $a, $S, $l)
    Local $S_FindBytes = DllCall($prospeed_dll, "long", "FindBytes", _
            "long", $b, _
            "long", $O, _
            "long", $a, _
            "long", $S, _
            "long", $l)
    Return $S_FindBytes[0]
EndFunc ;==>FindBytes

Func _Reposition($Master, $Slave)
    Global $gui_pos_check_left, $gui_pos_check_top, $gui_pos_check_right, $gui_pos_check_bottom
    $Pos = WinGetHandle($Master)
    $Pos = _WinAPI_GetWindowRect($Pos)
    If $gui_pos_check_left <> DllStructGetData($Pos, "Left") Or _
            $gui_pos_check_top <> DllStructGetData($Pos, "Top") Or _
            $gui_pos_check_right <> DllStructGetData($Pos, "Right") Or _
            $gui_pos_check_bottom <> DllStructGetData($Pos, "Bottom") Then
        
        $gui_pos_check_left = DllStructGetData($Pos, "Left")
        $gui_pos_check_top = DllStructGetData($Pos, "Top")
        $gui_pos_check_right = DllStructGetData($Pos, "Right")
        $gui_pos_check_bottom = DllStructGetData($Pos, "Bottom")
        
        $MainWindowPos = WinGetPos($Master)
        WinMove($Slave, "", $MainWindowPos[0] + $MainWindowPos[2], $MainWindowPos[1])
    EndIf
EndFunc ;==>_Reposition

Func _DrawGrid($gHandel, $pHandel, $StartX, $StartY, $EndX, $EndY, $Space)
    $Width = $EndX - $StartX
    $Height = $EndY - $StartY
    $HCount = Round($Width / $Space)
    $VCount = Round($Height / $Space)
    
    ; H Lines
    _GDIPlus_GraphicsDrawLine($gHandel, $StartX, $StartY, $EndX, $StartY)
    For $i = $StartX To $VCount
        _GDIPlus_GraphicsDrawLine($gHandel, $StartY, ($i * $Space) + $StartX, $EndX, ($i * $Space) + $StartX)
    Next
    
    ; V Lines
    _GDIPlus_GraphicsDrawLine($gHandel, $StartY, $StartX, $StartX, $EndY)
    For $i = $StartY To $HCount
        _GDIPlus_GraphicsDrawLine($gHandel, ($i * $Space) + $StartX, $StartY, ($i * $Space) + $StartY, $EndY)
    Next
EndFunc ;==>_DrawGrid
Edited by Deltaforce229

[size="1"]Please stop confusing "how to" with "how do"[/size]

Share this post


Link to post
Share on other sites

$PixelPos = _findpixelinbmpfile("snap.bmp", Hex($Color, 6))
the "color"-Parameter should be an integer, not a string! So try

$PixelPos = _findpixelinbmpfile("snap.bmp", $Color)

Share this post


Link to post
Share on other sites

Profided I did not mesh up somewhere, A general compare between a pure Autoit(scan) versus ProSpeed(find) comes down to about 1/400, so no contest.

When AutoIt crashed on me was because I fed ProSpeed a zero image pointer. Imagine it could do the same with other inconsistent input data.


"Straight_and_Crooked_Thinking" : A "classic guide to ferreting out untruths, half-truths, and other distortions of facts in political and social discussions."
"The Secrets of Quantum Physics" : New and excellent 2 part documentary on Quantum Physics by Jim Al-Khalili. (Dec 2014)

"Believing what you know ain't so" ...

Knock Knock ...
 

Share this post


Link to post
Share on other sites

$PixelPos = _findpixelinbmpfile("snap.bmp", Hex($Color, 6))
the "color"-Parameter should be an integer, not a string! So try

$PixelPos = _findpixelinbmpfile("snap.bmp", $Color)

That hex code was what you gave me and is correct, sort of. The color should be in the format 0xFFFFFF. Hex($Color, 6) gives FFFFFF without the 0x.

[size="1"]Please stop confusing "how to" with "how do"[/size]

Share this post


Link to post
Share on other sites

Hex($Color, 6) gives FFFFFF without the 0x.

...and this is not correct in the function call findpixelinbmpfile() !

0xFFAABB is an other spelling of the number 16755387, HEX(0xFFAABB) is not the same....

Share this post


Link to post
Share on other sites

...and this is not correct in the function call findpixelinbmpfile() !

0xFFAABB is an other spelling of the number 16755387, HEX(0xFFAABB) is not the same....

I'm getting very confused now and unfortunately very angry.

0xFFAABB IS NOT BEING HEXED! The value for $Color after it has gone through "$Color = $vary_blue + 256 * $vary_Green + 256 * 256 * $vary_Red" is.

At that point $Color = 16755387.

Then the Hex part changes it to FFAABB.

Your code as you first posted it requires 0xFFAABB not 16755387 and not FFAABB so why has it suddenly magically changed to an integer value now?


[size="1"]Please stop confusing "how to" with "how do"[/size]

Share this post


Link to post
Share on other sites

#16 ·  Posted (edited)

Your code as you first posted it requires 0xFFAABB not 16755387 and not FFAABB so why has it suddenly magically changed to an integer value now?

nothing changed^^

Run this script, have a look at it and i hope you will see the difference...

$a = 11189196       ;this is a number, right? But if this is a color, no one can "see" the part
                ;which represents the red, green, blue part of the color
$b = 0xAABBCC       ;this is an other spelling of the number 11189196, but its still a number, only difference to $a is,
                ;you can "see" that red is AA, green is BB and blue is CC
$c = "AABBCC"       ;This is a string, a bunch of some letters

;ok, lets play around...
$d = Hex($a,6)      ;This is the same as $c, Hex() converts a NUMBER to a STRING
$e = Dec("AABBCC")  ;DEC() converts a STRING to a NUMBER

;Lets eleminate the whole green part of a colorNUMBER, how would you do this?
;The first solution could be to convert the colorNUMBER to a string, replace the green part
;with 0, and convert the string to a number...
;Second solution: take the number, extract the "red and blue part" with some bitoperations
;and store this without to consider the "green part"
;
;Lets start with our first solution:
msgbox(0,"","First solution....",1)
$colorstring    = hex($a,6)         ;converts a number to a string, you could also take hex($b,6), it´s the same!!!!!
Msgbox(0,"convert the number "& $b &" to a string", _
        "number $a= " &$a & @CRLF & _
        "number $b= " &$b & "  which is 0xAABBCC"& @CRLF & _
        "string $colorstring= "&$colorstring)
$RED_string     = stringmid($colorstring,1,2)   ;part reperesenting the red color as a string
$GREEN_string   = stringmid($colorstring,3,2)   ;part reperesenting the green color as a string
$BLUE_string    = stringmid($colorstring,5,2)   ;part reperesenting the blue color as a string
;lets make numbers
$RED_number     = Dec($RED_string)              ;the number of the red color
$GREEN_number   = Dec($GREEN_string)            ;the number of the green color
$BLUE_number    = Dec($BLUE_string)             ;the number of the blue color
msgbox(0,"show the parts of the color "&$a, _
        "RED     : " & $RED_string  &"   "&$RED_number &  @CRLF  & _
        "GREEN : " & $GREEN_string&"   "&$GREEN_number & @CRLF  & _
        "BLUE    : " & $BLUE_string &"   "&$BLUE_number)

$GREEN_string   = "00"      ;we work with strings, so convert the green part to 0
$colorstring1   = $RED_string & $GREEN_string & $BLUE_string ;put the strings together
$colornumber1   = Dec($colorstring1)  ;convert the string to a number
msgbox(0,"We have eliminated the green part of the color...", _
        "$colorstring = "&$colorstring1 & @CRLF & _
        "$colornumber = "&$colornumber1)

;second solution....
msgbox(0,"","Second solution....",1)
$RED_part       = bitand($a,0xFF0000)   ;extract only the RR-part from the number
$RED_part       = bitand($a,16711680)   ;extract only the RR-part from the number, 16711680=0xFF0000 ,
                                    ;but nobody can "SEE" what will happens with this number...all clear now? ;o)
$GREEN_part     = bitand($a,0x00FF00)   ;we write 0x00FF00 to "SEE" what we will do...
$BLUE_part      = bitand($a,0x0000FF)   ;we write 0x0000FF to "SEE" what we will do...

$colornumber2   = $RED_part + $BLUE_part;add red and blue part, remember that green part is 0, we have finished here...
$colorstring2   = Hex($colornumber2,6)  ;convert the number to a string to show it in the msgbox

msgbox(0,"We have eliminated the green part of the color...", _
        "$colorstring = "&$colorstring2 & @CRLF & _
        "$colornumber = "&$colornumber2)

;we could play around with the parts...converting $RED_part to $RED_number
$RED_number     = bitshift($RED_part,16)    ;transform 0xRR0000 to 0x0000RR,  the same as divide with (256*256=65536)
$GREEN_number   = bitshift($GREEN_part,8)   ;transform 0x00GG00 to 0x0000GG,  the same as divide with 256
$BLUE_number    = $BLUE_part                ;we dont need to transform, because 0x0000BB = 0x0000BB

$RED_string     = Hex($RED_number,2)        ;we want to show it in the msgbox
$GREEN_string   = Hex($GREEN_number,2)      ;we want to show it in the msgbox
$BLUE_string    = Hex($BLUE_number,2)       ;we want to show it in the msgbox

msgbox(0,"show the parts of the color "&$a, _
        "RED     : " & $RED_string  &"   "&$RED_number &  @CRLF  & _
        "GREEN : " & $GREEN_string&"   "&$GREEN_number & @CRLF  & _
        "BLUE    : " & $BLUE_string &"   "&$BLUE_number)

;I hope that I have eliminated all clarity for now...  :o)

/EDIT/ edit copypaste errors...

Edited by AndyG

Share this post


Link to post
Share on other sites

it requires 0xFFAABB not 16755387

what is the difference?

$a=0xFFAABB
$b=16755387
msgbox(0,$a,$b)

Share this post


Link to post
Share on other sites

#18 ·  Posted (edited)

Deltaforce, instead of creating the struct over and over in the loop, reuse it. Try with DllStructGetData and DllStructSetData, use it like you would a variable.

P.S. Are you sure you even need it?

Edited by Manadar

Share this post


Link to post
Share on other sites

#19 ·  Posted (edited)

Hi,

@deltaforce, your script (Post#1) is very fast, the higher the tolerance is! I´ve modified it a little bit so it is 2-3 times faster now

#include <Color.au3>
#include <GDIPlus.au3>
#include <GDIPlusConstants.au3>

_GDIPlus_Startup()

$image = "mona-lisa.jpg"

$pcolor = 0x688950
$ptolerance = 0
$t = TimerInit()
$acoord = Imagegetpp($image, $pcolor, $ptolerance)
$m = TimerDiff($t)
If IsArray($acoord) Then
    MsgBox(0, "col / row", $acoord[0] & " / " & $acoord[1] & @CRLF & $m)
Else
    MsgBox(0, 0, "pixel not found  " & $m)
EndIf


; (Image file location, Pixel color, Tolerance)
Func ImageGetPP($image, $pcolor, $ptolerance)
    Local $hImage, $iWidth, $iHeight, $hBitmap, $iBitmap, $stride, $Scan0, $v_Buffer, $v_Value, $Pos[2]
    $hImage = _GDIPlus_ImageLoadFromFile($image)
    $iWidth = _GDIPlus_ImageGetWidth($hImage)
    $iHeight = _GDIPlus_ImageGetHeight($hImage)
    $hBitmap = _GDIPlus_BitmapCloneArea($hImage, 0, 0, $iWidth, $iHeight, $GDIP_PXF24RGB)
    $iBitmap = _GDIPlus_BitmapLockBits($hBitmap, 0, 0, $iWidth, $iHeight, BitOR($GDIP_ILMREAD, $GDIP_ILMWRITE), $GDIP_PXF24RGB)

    $R = _ColorGetRed($pcolor)
    $G = _ColorGetGreen($pcolor)
    $B = _ColorGetBlue($pcolor)

    $stride = DllStructGetData($iBitmap, "stride")
    $Scan0 = DllStructGetData($iBitmap, "Scan0")

    $v_Buffer = DllStructCreate("byte[" & $stride * $iHeight & "]", $Scan0)

    For $j = 0 To $iHeight - 1
        For $i = 0 To $iWidth - 1
            $cpr = DllStructGetData($v_Buffer, 1, ($j * $stride) + ($i * 3) + 3) ;red
            If $cpr + $ptolerance >= $R And $cpr - $ptolerance <= $R Then
                $cpg = DllStructGetData($v_Buffer, 1, ($j * $stride) + ($i * 3) + 2) ;green
                If $cpg + $ptolerance >= $G And $cpg - $ptolerance <= $G Then
                    $cpb = DllStructGetData($v_Buffer, 1, ($j * $stride) + ($i * 3) + 1)  ;blue
                    If $cpb + $ptolerance >= $B And $cpb - $ptolerance <= $B Then

;~                         ConsoleWrite('@@ Debug(' & @ScriptLineNumber & ') : $CPR = ' & $cpr & @CRLF & '>Error code: ' & @error & @CRLF) ;### Debug Console
;~                         ConsoleWrite('@@ Debug(' & @ScriptLineNumber & ') : $CPG = ' & $cpg & @CRLF & '>Error code: ' & @error & @CRLF) ;### Debug Console
;~                         ConsoleWrite('@@ Debug(' & @ScriptLineNumber & ') : $CPB = ' & $cpb & @CRLF & '>Error code: ' & @error & @CRLF) ;### Debug Console
                        $Pos[0] = $i + 1
                        $Pos[1] = $j + 1
                        _GDIPlus_BitmapUnlockBits($hBitmap, $iBitmap)
                        _GDIPlus_BitmapDispose($hBitmap)
                        _GDIPlus_ImageDispose($hImage)
                        Return $Pos
                    EndIf
                EndIf
            EndIf
        Next
    Next
    _GDIPlus_BitmapUnlockBits($hBitmap, $iBitmap)
    _GDIPlus_BitmapDispose($hBitmap)
    _GDIPlus_ImageDispose($hImage)
    Return 0
EndFunc   ;==>ImageGetPP

I have tested a version with prospeed "comparebytes"-function, but the overhead calling the dll made the script slower than your version in post #1

Here is my version with prospeed/stringinstr. It´s very fast finding the exact color and it will be slower and slower with higher tolerance

#include <GUIConstantsEx.au3>
#include <WinAPI.au3>
#include <GDIPlus.au3>
#include <GDIPlusConstants.au3>
#include <StructureConstants.au3>
#include <Array.au3>

$bitmapfile = "mona-lisa.jpg"
$tolerance = 0
$color = 0x688950


If FileExists(@ScriptDir & "\prospeed.dll") Then
    Global $Prospeedflag = True
    Global $prospeed_dll = DllOpen(@ScriptDir & "\ProSpeed.dll")
Else
    Global $Prospeedflag = False
EndIf


$t = TimerInit()
$aCoord = _findpixelinbmpfile($bitmapfile, $color, $tolerance) ;only 24bpp,  color in RGB
$m = TimerDiff($t)
If IsArray($aCoord) Then
    MsgBox(0, "col / row", $aCoord[0] & " / " & $aCoord[1] & @CRLF & $m)
Else
    MsgBox(0, 0, "pixel not found  " & $m)
EndIf



Func _findpixelinbmpfile($bmpfile, $color, $tolerance = 0) ;only 24bpp, returns 0 if miss, returns array with x- and y-koordinates
    $t = TimerInit()
    Dim $Coord[2]
    _GDIPlus_Startup()
    $pBitmap = _GDIPlus_BitmapCreateFromFile($bmpfile)
    If @error Then MsgBox(0, "", "Error BitmapCreateFromFile")
    $BitmapData = _GDIPlus_BitmapLockBits($pBitmap, 0, 0, _GDIPlus_ImageGetWidth($pBitmap), _GDIPlus_ImageGetHeight($pBitmap), $GDIP_ILMREAD, $GDIP_PXF24RGB)
    If @error Then MsgBox(0, "", "Error locking region " & @error)
    $stride = DllStructGetData($BitmapData, "Stride");Stride - Offset, in bytes, between consecutive scan lines of the bitmap. If the stride is positive, the bitmap is top-down. If the stride is negative, the bitmap is bottom-up.
    $Width = DllStructGetData($BitmapData, "Width");Image width - Number of pixels in one scan line of the bitmap.
    $Height = DllStructGetData($BitmapData, "Height");Image height - Number of scan lines in the bitmap.
    $pixelFormat = DllStructGetData($BitmapData, "PixelFormat");Pixel format - Integer that specifies the pixel format of the bitmap
    $Scan0 = DllStructGetData($BitmapData, "Scan0");Scan0 - Pointer to the first (index 0) scan line of the bitmap.
    $m = TimerDiff($t)
    ConsoleWrite('@@ Debug(' & @ScriptLineNumber & ') : $m = ' & $m & @CRLF & '>Error code: ' & @error & @CRLF) ;### Debug Console

    $Red = BitAND(BitShift($color, 16), 0xFF) ;red
    $Green = BitAND(BitShift($color, 8), 0xFF) ;green
    $Blue = BitAND($color, 0xFF) ;blue

    For $vary_Red = $Red - $tolerance To $Red + $tolerance ;red from AA-10 to AA+10
        For $vary_Green = $Green - $tolerance To $Green + $tolerance ;green from BB-10 to BB+10
            For $vary_blue = $Blue To $Blue + $tolerance
                $color = $vary_blue + 256 * $vary_Green + 256 * 256 * $vary_Red
                ConsoleWrite(Hex($color, 6) & @CRLF)

                If $Prospeedflag = True Then
                    $struct = DllStructCreate("byte[3]") ;for the color
                    DllStructSetData($struct, 1, $color) ;writes the 3-byte colour into the struct
                    $ptr = DllStructGetPtr($struct)
                    $i = FindBytes($Scan0, 0, (Abs($stride) * ($Height)), $ptr, 3) + 1
                Else
                    $pixeldata = DllStructCreate("byte[" & (Abs($stride) * ($Height)) & "]", $Scan0)
                    $BMPData = BinaryToString(DllStructGetData($pixeldata, 1))
                    $bincol = StringTrimRight(BinaryToString($color), 1) ;string of the color
                    $i = StringInStr($BMPData, $bincol) ;first position of the 3 color-bytes
                EndIf

                While $i <> 0
                    $r = Mod($i, $stride) + 2
                    If Mod($r, 3) = 0 Then ;only if the 3 color-bytes are in one pixel
                        $Coord[0] = $r / 3
                        $Coord[1] = Int($i / $stride) + 1
                        _GDIPlus_BitmapUnlockBits($pBitmap, $BitmapData)
                        _GDIPlus_ImageDispose($pBitmap)
                        _GDIPlus_Shutdown()
                        Return $Coord
                    Else
                        If $Prospeedflag Then
                            $i = FindBytes($Scan0, $i + 1, (Abs($stride) * ($Height))-$i, $ptr, 3) + 1
                        Else
                            $i = StringInStr($BMPData, $bincol, 0, 1, $i + 1) ;start an letzter position
                        EndIf
                    EndIf
                WEnd

            Next
        Next
    Next

    _GDIPlus_BitmapUnlockBits($pBitmap, $BitmapData)
    _GDIPlus_ImageDispose($pBitmap)
    _GDIPlus_Shutdown()
    Return 0

EndFunc   ;==>_findpixelinbmpfile


Func FindBytes($b, $O, $a, $S, $l) ;needed if prospeed.dll is used
    Local $S_FindBytes = DllCall($prospeed_dll, "long", "FindBytes", _
            "long", $b, _
            "long", $O, _
            "long", $a, _
            "long", $S, _
            "long", $l)
    Return $S_FindBytes[0]
EndFunc   ;==>FindBytes
Edited by AndyG

Share this post


Link to post
Share on other sites

Alright it works and I thank you for helping me even though I was being a bit of an arse. I used your modification of my first post since I'm still getting weird crashes from prospeed. for some reason AutoIt + DLLs + My computer = Crash. Adding tolerances produces weird effects though nothing that's really a problem. If you have an image with only black and white pixels in it and you add a tolerance greater than 0 the pixel in row 1 column 1 is always going to be selected.

Once again thank's and sorry for being an arse.

@Manadar maybe you could explain structs and such to me on steam sometime. Right now I need to take a step back from all this right now since it was just a hobby project and nothing more.


[size="1"]Please stop confusing "how to" with "how do"[/size]

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