Jump to content



Photo

Find BMP in another BMP (imagesearch)


  • Please log in to reply
84 replies to this topic

#1 junkew

junkew

    Universalist

  • Active Members
  • PipPipPipPipPipPip
  • 322 posts

Posted 17 March 2008 - 01:10 AM

Code below searches a certain bitmap (all supported formats of GDI+) picture in another picture or on screen (after saved as bitmap)
not depending on 3rd party dll's
Pentium 4 of 3 GHz takes
* about 4.5 seconds on a 1280*1024 desktop to find a 260*260 bitmap. Partial match 0.5 seconds
* about 1 seconds on a 1280*1024 desktop to find a 20*17 bitmap. Partial match 0.5 seconds

Some references I used to come up with this solution
* code to get a smaller bitmap: http://www.autoitscript.com/forum/index.ph...=pixelchecksum)
* Pixelchecksum seems not to do the full job but in combination with below it can become very fast: http://www.autoitscript.com/forum/index.ph...l=pixelchecksum
* A pixel by pixel searcher http://www.autoitscript.com/forum/index.ph...&hl=compare
* UDF Get or Read Pixel from Memory
* GetDIBits

Not fully finished but all comments/enhancements are appreciated
* Save to file or directly on special window description SCREEN, [ACTIVE] or name of picture file
* Searching is done on a line by line basis instead of pixel by pixel
* Bitmap to search in example program should be fully visible to work for first 3 examples as it are screenshots taken. Same applies for 3 in AUTOIT homepage
* Speed depends on the size of the bitmap you are searching for but is about linear and < 5 seconds
* Some doubts on left and top returned (sometimes seems to be off by 1 or several pixels)
* Transparency (when search letters on a different background) http://www.winprog.org/tutorial/transparency.html
* Match quicker by doing a bitblt with srcinvert when first line is found (instead of checking line by line)
* $BMP1 and $BMP2 to be HBITMAP handle as input instead of filenames (will make searching within partial screen

Sample to work
* Install pics.zip into %temp% / Save BMP attachments for example to @tempdir (%temp%) for example to work

Sample code
AutoIt         
Opt('MustDeclareVars', 1) ;#include <GUIConstants.au3> #include <GDIPlus.au3> #Include <ScreenCapture.au3> #include <string.au3> ;~ If debugging output is wanted to understand and analyse the algorithm put this on true/false Const $bDebug=True ;~ Const $bDebug=False ;~ Const $cMatchLinePercentage=0.97 ;Minimum matching, put on 1 if exact matching is needed on all lines Const $cMatchLinePercentage=0.99 ;Minimum matching, put on 1 if exact matching is needed on all lines ;~ Constants for type of picture matching const $c24RGBFullMatch=1 ;Load as 24 bits and full match const $c24RGBPartialMatch=2 ;Load as 24 bits and partial match const $c16RGBFullMatch=3 ;Load as 16 bits and full match const $c16RGBPartialMatch=4 ;Load as 16 bits and partial match ; ** Example start ** ; Screen samples Global Const $Bitmap1Filename = @scriptDir & "\FULLSCREEN.BMP" Global Const $Bitmap2Filename = @scriptDir & "\CALCULATOR.BMP" Global Const $Bitmap3Filename = @scriptDir & "\BACKSPACE.BMP" ;BMP samples Global Const $Bitmap4Filename = @scriptDir & "\7WDS_BW.BMP" Global Const $Bitmap5Filename = @scriptDir & "\SEVEN_BW.BMP" Global Const $Bitmap6Filename = @scriptDir & "\CAT_BW.BMP" Global Const $Bitmap7Filename = @scriptDir & "\AUTOIT3.bmp";Make sure the homepage of autoit with 3 is visible local $calcHWND, $begin, $pos, $aWinPos, $aWinCSize, $start local $pBitmap, $BitmapData, $pixelFormat Opt("WinTitleMatchMode", 4);Matching windows by advanced options ; Initialize GDI+ library _GDIPlus_Startup () ;Calculator, make sure its started $calcHWND = WinGetHandle("[REGEXPCLASS:.*Calc.*]") If $calcHWND = "" Then Run("calc.exe") Sleep(2000) $calcHWND = WinGetHandle("[REGEXPCLASS:.*Calc.*]") EndIf ;Capture the calculator screen $begin = TimerInit() winactivate($calcHWND) Sleep(500) _ScreenCapture_CaptureWnd($Bitmap2Filename, $calcHWND,0,0,-1,-1,False) ConsoleWrite("Saving calculator window " & TimerDiff($begin) & " milliseconds " & @LF) winmove("[ACTIVE]","",223,341) Sleep(500) ;Capture an area within the calculator area backspace $begin = TimerInit() ;~ $pos=controlgetpos("","","[CLASS:Button; TEXT:BACKSPACE]") $pos=controlgetpos("","","[CLASS:Button; INSTANCE:2]") $aWinPos = WinGetPos($calchWnd) $aWinCSize = WinGetClientSize($calchWnd) ;Q&D calculation of offsets to capture _ScreenCapture_Capture($Bitmap3Filename, ($aWinPos[0]+$pos[0]) + ($aWinPos[2] - $aWinCSize[0])-3, ($awinpos[1]+$pos[1])+ ($aWinPos[3]-$aWinCSize[1])-3, $aWinPos[0]+$pos[0]+$pos[2]+3, ($awinpos[1]+$pos[1])+ ($aWinPos[3]-$aWinCSize[1])+$pos[3]-3,false) ConsoleWrite("Saving backspacebutton " & TimerDiff($begin) & " milliseconds " & @LF) ; Capture full screen $begin = TimerInit() _ScreenCapture_Capture($Bitmap1Filename,0,0,-1,-1,False) ConsoleWrite("Saving full screen took " & TimerDiff($begin) & " milliseconds " & @LF) func filegetname($f) local $i $i=stringinstr($f,"\",false,-1) if $i > 0 Then return stringmid($f,$i+1) Else return $f EndIf EndFunc ;Do the actual find Func FindTester($BMP1, $BMP2, $Bool) local $tResult $start = TimerInit() $tResult=findBMP($BMP1,$BMP2, $Bool) ConsoleWrite($tResult & " " & FileGetName($BMP2) & " in " & FileGetName($BMP1) & " ** matchtype " & $Bool & " time elapsed: " & TimerDiff($start) & " milliseconds" & @LF) EndFunc if not $bDebug=True Then ; Not very usefull to find full screen and tricky as screen is most likely changed (clock, output from application etc.) ; findTester("SCREEN",$Bitmap1Filename,TRUE);Find the full screen itself ; findTester($Bitmap1Filename,$Bitmap1Filename,$c24RGBFullMatch);Find the full screen itself 31 seconds findTester($Bitmap1Filename,$Bitmap1Filename,$c24RGBPartialMatch);Find the full screen itself 2.8529088255245 seconds ; findTester($Bitmap1Filename,$Bitmap1Filename,$c16RGBFullMatch);Find the full screen itself 21 seconds findTester($Bitmap1Filename,$Bitmap1Filename,$c16RGBPartialMatch);Find the full screen itself 1 seconds ; Be aware that overlapping windows, moving things on screen can make it difficult to find on full screen findTester("SCREEN",$Bitmap2Filename,$c24RGBFullMatch);Find the full calculatorscreen findTester("SCREEN",$Bitmap2Filename,$c24RGBPArtialMatch);Find the full calculatorscreen with partial match findTester("SCREEN",$Bitmap2Filename,$c16RGBFullMatch);Find the full calculatorscreen findTester("SCREEN",$Bitmap2Filename,$c16RGBPArtialMatch);Find the full calculatorscreen with partial match findTester($Bitmap1Filename,$Bitmap2Filename,$c16RGBFullMatch);Find the full calculatorscreen findTester($Bitmap1Filename,$Bitmap2Filename,$c16RGBPartialMatch);Find the full calculatorscreen with partial match findTester($Bitmap2Filename,$Bitmap3Filename,$c24RGBFullMatch);Find the backspace button findTester($Bitmap2Filename,$Bitmap3Filename,$c24RGBPartialMatch);Find the backspace button with partial match findTester($Bitmap2Filename,$Bitmap3Filename,$c16RGBFullMatch);Find the backspace button findTester($Bitmap2Filename,$Bitmap3Filename,$c16RGBPartialMatch);Find the backspace button with partial match winactivate($calcHWND);Make sure calculator is active on the screen findTester("[ACTIVE]",$Bitmap3Filename,$c24RGBFullMatch);Find the backspace button findTester("[ACTIVE]",$Bitmap3Filename,$c24RGBPartialMatch);Find the backspace button with partial match findTester("[ACTIVE]",$Bitmap3Filename,$c16RGBFullMatch);Find the backspace button on active screen findTester("[ACTIVE]",$Bitmap3Filename,$c16RGBPartialMatch);Find the backspace button with partial match findTester("SCREEN",$Bitmap3Filename,$c24RGBFullMatch);Find the backspace button findTester("SCREEN",$Bitmap3Filename,$c24RGBPartialMatch);Find the backspace button with partial match findTester("SCREEN",$Bitmap3Filename,$c16RGBFullMatch);Find the backspace button findTester("SCREEN",$Bitmap3Filename,$c16RGBPartialMatch);Find the backspace button with partial match findTester($Bitmap4Filename,$Bitmap5Filename,$c16RGBFullMatch);Find the seven findTester($Bitmap4Filename,$Bitmap5Filename,$c16RGBPartialMatch);Find the seven with partial match findTester($Bitmap4Filename,$Bitmap6Filename,$c16RGBFullMatch);Find the cat findTester($Bitmap4Filename,$Bitmap6Filename,$c16RGBPartialMatch);Find the cat with partial match findTester($Bitmap1Filename,$Bitmap7Filename,$c24RGBFullMatch);Find the 3 of Autoit Homepage findTester($Bitmap1Filename,$Bitmap7Filename,$c24RGBPartialMatch);Find the 3 of Autoit Homepage findTester($Bitmap1Filename,$Bitmap7Filename,$c16RGBFullMatch);Find the 3 of Autoit Homepage findTester($Bitmap1Filename,$Bitmap7Filename,$c16RGBPartialMatch);Find the 3 of Autoit Homepage Else ;~ findTester($Bitmap4Filename,$Bitmap6Filename,$c24RGBFullMatch);Find the cat ;~ findTester($Bitmap1Filename,$Bitmap2Filename,$c24RGBFullMatch);Find the full calculatorscreen findTester("SCREEN",$Bitmap2Filename,$c24RGBFullMatch);Find the full calculatorscreen EndIf _GDIPlus_Shutdown() ;** Example end **


Actual functions

AutoIt         
;=============================================================================== ; Function Name:    findBMP ; Description:   Finds a bitmap (.BMP) in another BMP file (other formats PNG and TIFF work also other formats less usefull GIF, JPEG, Exif, WMF, and EMF should work) ; Syntax:        findBMP($BMP1, $BMP2, $MatchType=TRUE) ; ; Parameter(s):  $BMP1           = Filename of bitmap to search in ;                $BMP2           = Filename of bitmap to search for ;                $MatchType     = c24RGBFullMatch, c24RGBPartialMatch, c16RGBFullMatch, c16RGBPartialMatch ; ; Return Value(s): On Success: = Returns Array List ;                On Failure: = @error 1 (Control was found but there was an error with the DLLCall) ; ; Author(s):        JunkEW ; ; Note(s): ;               * Its never an exact match even with TRUE as last few bits are disposed in algorithm and lines below ;                are not checked under assumption that those are 99.99% of the time correct ;            * locking bits overview http://www.bobpowell.net/lockingbits.htm ; ToDo: ;               * Transparency (when search letters on a different background) http://www.winprog.org/tutorial/transparency.html ;               * Match quicker by doing a bitblt with srcinvert when first line is found (instead of checking line by line) ;               * $BMP1 and $BMP2 to be HBITMAP handle as input instead of filenames (will make searching within partial screen easier) ; Example(s): ; ;=============================================================================== Func findBMP($BMP1, $BMP2, $MatchType=$c24RGBFullMatch)     Dim $fLine[1];Line number of found line(s), redimmed when second picture size is known     Dim $BMP1Data="", $BMP1Width=0, $BMP1Height=0, $BMP1LineWidth=0;     Dim $BMP2Data="", $BMP2Width=0, $BMP2Height=0, $BMP2LineWidth=0     Dim $foundAt = "", $matchPossible=FALSE, $matchedLines=0, $foundAtLeft=-1, $foundAtTop=-1     Dim $bestMatchLine=-1, $HighestMatchingLines=-1; For knowing when no match is found where best area is     Dim $iPos=1;     dim $imgBytes, $avData;     local $iFuzzyDist, $searchFor, $iAbove, $bMatchPossible, $aboveLine     local $j, $imgBits local $tDebug     if ($MatchType=$c24RGBFullMatch) or ($matchtype=$c24RGBPartialMatch) then         $imgBytes=3     Else         $imgBytes=2     endif ; Load the bitmap to search in     getImage($BMP1, $BMP1Data, $BMP1Width, $BMP1Height, $BMP1LineWidth, $imgBytes)     $BMP1Data = BinaryToString($BMP1Data) ; Load the bitmap to find     getImage($BMP2, $BMP2Data, $BMP2Width, $BMP2Height, $BMP2LineWidth, $imgBytes) ;Make it strings to be able to use string functions for searching     $BMP2Data = BinaryToString($BMP2Data) if $bDebug=True Then ;~ START debugging ;Get some debugging information     FileDelete(@ScriptDir & "\BMP1DATA.TXT")     FileDelete(@ScriptDir & "\BMP2DATA.TXT")     FileDelete(@ScriptDir & "\COMPAREDLINES.TXT")     consolewrite($BMP1Width & @crlf)     consolewrite($BMP1Height & @crlf)     consolewrite($BMP1LineWidth & @crlf)     consolewrite(stringlen($BMP1Data) & @crlf)     consolewrite(".{" & $BMP1LineWidth & "}"& @CRLF)     consolewrite(".{" & $BMP2LineWidth & "}"& @CRLF)     $tDebug=StringRegExpReplace(_stringtohex($BMP1Data),"(.{" & $BMP1LineWidth*2 & "})","$1" & @CRLF)     consolewrite(@error & @extended & @CRLF)     FileWrite (@ScriptDir & "\BMP1DATA.TXT" , $tdebug)     $tDebug=StringRegExpReplace(_stringtohex($BMP2Data),"(.{" & $BMP2LineWidth*2 & "})","$1" & @CRLF)     consolewrite(@error & @extended & @CRLF)     FileWrite (@ScriptDir & "\BMP2DATA.TXT" , $tdebug) ;~ END debugging EndIf ;For reference of line where in BMP2FindIn a line of BMP2Find was found     If $BMP2Height = 0 Then         SetError(1,0,0)         Return False     EndIf     ReDim $fline[$BMP2Height] ;If exact match check every 1 line else do it more fuzzy (as most likely other lines are unique)     if ($MatchType=$c24RGBFullMatch) or ($matchtype=$c16RGBFullMatch) Then         $iFuzzyDist = 1     Else ;Check fuzzy every 10% of lines         $iFuzzyDist = ceiling(($bmp2height * 0.1))     endIf     $begin = TimerInit() ;Look for each line of the bitmap if it exists in the bitmap to find in ;~ Split bitmap to search in lines ;~  $avData=stringregexp($BMP2Data, ".{1," & $BMP2lineWidth & "}+", 3)     For $i = 0 To $BMP2Height - 1 ;Minus imgbytes as last bits are padded with unpredictable bytes (24 bits image assumption) or 2 when 16 bits         $searchFor = StringMid($BMP2Data, 1 + ($i * $BMP2lineWidth), ($BMP2lineWidth - $imgBytes)) ;~      $searchfor=stringleft($avData[$i],$BMP2lineWidth - $imgBytes)         $iPos = StringInStr($BMP1Data, $searchFor, 2, 1, $iPos) ;       $iPos = StringInStr($BMP1Data, $searchFor) ;Look for all lines above if there is also a match ;Not doing it for the lines below as speed is more important and risk of mismatch on lines below is small         $iAbove=1         if $iPos > 0 then             $bMatchPossible=True             $matchedLines=1;As first found line is matched we start counting ;Location of the match             $foundAtTop = Int($iPos / $BMP1lineWidth) -$i             $foundAtLeft = int(mod($iPos,$bmp1linewidth) / $imgBytes)         Else ;~          No 1st line found so far nothing to start matching             $bMatchPossible=false             exitloop         endif         while (($i+$iAbove) <= ($BMP2Height -1)) and ($bMatchPossible=True)             $searchFor = StringMid($BMP2Data, 1 + (($i + $iAbove) * $BMP2lineWidth), ($BMP2lineWidth - $imgBytes)) ;~          $searchfor = stringleft($avData[$i+$iAbove],$BMP2lineWidth - $imgBytes)             $aboveLine = stringmid($BMP1Data,$iPos + ($iAbove * $BMP1LineWidth), ($BMP2LineWidth - $imgBytes))             if $bDebug=True Then             ;~ START debugging                 $tDebug=StringRegExpReplace(_stringtohex($searchfor),"(.{8})","$1_")                 FileWrite (@ScriptDir & "\COMPAREDLINES.TXT" , $tDebug & @crlf)                 $tDebug=StringRegExpReplace(_stringtohex($aboveline),"(.{8})","$1_")                 FileWrite (@ScriptDir & "\COMPAREDLINES.TXT" , $tDebug & @crlf)                 If $aboveLine <> $searchFor Then                     FileWrite (@ScriptDir & "\COMPAREDLINES.TXT" , "** MISMATCH ** of line " & (1+$iAbove) & " in 2nd bitmap at line " & ($foundAtTop + $i) & @crlf)                 endIf             ;~ END debugging             EndIf             if comparePicLine($aboveline,$searchfor) = false then                 $bMatchPossible=False ;To remember the area with the best match                 if $matchedLines >= $HighestMatchingLines Then                     $HighestMatchingLines = $matchedLines     ;Best guess of location ;~                  $foundAtTop = $fline[$i] + $i - $BMP2Height                     $foundAtTop = Int($iPos / $BMP1lineWidth);+ $i - $BMP2Height                     $bestMatchLine = Int($iPos / $BMP1lineWidth)                 EndIf                 ExitLoop             EndIf             $matchedLines=$matchedLines + 1             $iAbove=$iAbove+$iFuzzyDist         WEnd ;If bMatchPossible is still true most likely we have found the bitmap         if $bmatchPossible = True then ;~          ConsoleWrite("Could match top: " & $foundAtTop & " left: " & $foundAtLeft & " in " & TimerDiff($begin) / 1000 & " seconds" & @LF) ;           MouseMove($foundatleft,$foundatTop)             exitloop         else ;~          consolewrite("i not matched " & $ipos & " " & $matchedlines & @crlf )         EndIf     Next ;For some debugging of time ;   if $bMatchPossible = True Then ;       ConsoleWrite("Searching took " & TimerDiff($begin) / 1000 & " seconds " & @LF) ;   Else ;       ConsoleWrite("NOT FOUND Searching took " & TimerDiff($begin) / 1000 & " seconds" & @LF) ;   endif ;Return an error if not found else return an array with all information     if $bMatchPossible = False Then         SetError(1, 0, 0)     endif ;   return stringsplit($bMatchPossible & ";" & $matchedLines & ";" & $foundAtLeft & ";" & $foundAtTop & ";" & $bmp2width & ";" & $BMP2Height & ";" & $HighestMatchingLines & ";" & $bestMatchLine,";")     return $bMatchPossible & ";" & $matchedLines & ";" & $foundAtLeft & ";" & $foundAtTop & ";" & $bmp2width & ";" & $BMP2Height & ";" & $HighestMatchingLines & ";" & $bestMatchLine EndFunc;==>findBMP func comparePicLine($s1,$s2)     local $sLen, $iMatched     if $s1=$s2 Then         return True     Else         $iMatched=0         $sLen=stringlen($s1)         for $tJ=1 to $slen             if stringmid($s1,$tJ,1)=stringmid($s2,$tJ,1) Then                 $iMatched=$imatched+1             Else                 if $bDebug=True Then                 ;~ START debugging                     FileWrite (@ScriptDir & "\COMPAREDLINES.TXT" , "Debug mismatch pixel " & $tj & "=" & int($tj/4) & @crlf)                 endif             endif         Next         if  ($iMatched / $sLen ) > $cMatchLinePercentage then             return true         Else             return false         endif     EndIf endfunc Func GetImage($BMPFile, byref $BMPDataStart, byref $Width, byRef $Height, byref $Stride, $imgBytes=3) local $Scan0, $pixelData, $hbScreen, $pBitmap, $pBitmapCap, $handle ; Load the bitmap to search in     If $BMPFile="SCREEN" Then         $hbScreen=_ScreenCapture_Capture("",0,0,-1,-1,False)         $pBitmap = _GDIPlus_BitmapCreateFromHBITMAP($hbScreen); returns memory bitmap     Else ;try to get a handle         $handle = WinGetHandle($BMPFile)         If @error Then ;Assume its an unknown handle so correct filename should be given             $pBitmap = _GDIPlus_BitmapCreateFromFile($BMPFile)         Else             $hbScreen=_ScreenCapture_CaptureWnd("",$handle,0,0,-1,-1,False)             $pBitmap = _GDIPlus_BitmapCreateFromHBITMAP($hbScreen); returns memory bitmap         EndIf     EndIf ;Get $tagGDIPBITMAPDATA structure ;~  ConsoleWrite("Bitmap Width:  " & _GDIPlus_ImageGetWidth($pBitmap) & @CRLF ) ;~  ConsoleWrite("Bitmap Height:     " & _GDIPlus_ImageGetHeight($pBitmap) & @CRLF) ;~  24 bits (3 bytes) or 16 bits (2 bytes) comparison     if ($imgBytes=2) then         $BitmapData= _GDIPlus_BitmapLockBits($pBitmap, 0, 0, _GDIPlus_ImageGetWidth($pBitmap), _GDIPlus_ImageGetHeight($pBitmap), $GDIP_ILMREAD, $GDIP_PXF16RGB555) ;~      $BitmapData= _GDIPlus_BitmapLockBits($pBitmap, 0, 0, _GDIPlus_ImageGetWidth($pBitmap), _GDIPlus_ImageGetHeight($pBitmap), $GDIP_ILMREAD, $GDIP_PXF32ARGB)     Else         $BitmapData= _GDIPlus_BitmapLockBits($pBitmap, 0, 0, _GDIPlus_ImageGetWidth($pBitmap), _GDIPlus_ImageGetHeight($pBitmap), $GDIP_ILMREAD, $GDIP_PXF24RGB) ;~      $BitmapData= _GDIPlus_BitmapLockBits($pBitmap, 0, 0, _GDIPlus_ImageGetWidth($pBitmap), _GDIPlus_ImageGetHeight($pBitmap), $GDIP_ILMREAD, $GDIP_PXF32ARGB)     endIf     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.     $pixelData = DllStructCreate("ubyte lData[" & (abs($Stride) * $Height-1) & "]", $Scan0)     $BMPDataStart = $BMPDataStart & DllStructGetData($pixeldata,"lData")     _GDIPlus_BitmapUnlockBits($pBitmap, $BitmapData)     _GDIPlus_ImageDispose ($pBitmap)     _WinAPI_DeleteObject ($pBitmap) EndFunc;==>GetImage


Edit: Speed and partial match enhancements. Search is within a split second especially when partial match is turned on
Edit2: Cleaned up code in a nicer way, returns best match results even if find is false
Edit 24 march 2008: Becomes dependent on beta v3.2.11.5 (beta)
;$iPos = StringInStr($BMP1Data, $searchFor)
$iPos = StringInStr($BMP1Data, $searchFor, 2, 1, $iPos+1)
Edit 13th june 2008 updated for new autoit version
Edit 31st august 2008 added some reference links
Edit 04 sept 2008 completely rewritten using GDI plus functions and speedenhancements by using different 24 bits and 16 bits pictures
Edit 23 nov 2008 small additional check on bmp2height beeing 0
Edit 1st dec 2008 fixed top/left, added [active] find on active screen, work with window special description
Edit 20st feb 2009 fixed Stringinstr to use additional parameters, now matchin b/w pictures better
$iPos = StringInStr($BMP1Data, $searchFor, 2, 1, $iPos)

Edit jan 5th 2013 full updated all working under Windows 7 / 64 bits (not tested under windows XP but no reason to be broken)
see also http://www.autoitscript.com/forum/topic/146996-find-image-in-image-loop/
works with BMP, PNG and TIFF
added threshold for small bits off on a single line (unclear why GDI writes those pixels/shade variation)
speed I assume is no issue for nowadays CPU power

Attached Files

  • Attached File  pics.zip   282.91KB   325 downloads

Edited by junkew, 10 February 2013 - 04:01 PM.








#2 sbgwb0769

sbgwb0769

    Seeker

  • Banned (NOT IN USE)
  • 33 posts

Posted 18 March 2008 - 02:58 AM

very very thank you!hehe~~~~~~~~~~~

#3 Armand

Armand

    Universalist

  • Active Members
  • PipPipPipPipPipPip
  • 541 posts

Posted 20 March 2008 - 10:19 AM

ummzz....
not quit accurate working.... try finding pic A inside pic B in these:

A.
Posted Image

B.
Posted Image


Could be very useful if it could work on such pictures... :)

#4 junkew

junkew

    Universalist

  • Active Members
  • PipPipPipPipPipPip
  • 322 posts

Posted 20 March 2008 - 06:01 PM

Searching the picture as suggested just works on my machine. Remove the 2 right vertical whitespaces of your picture to search.
It seems that the 2 pictures are slightly different.

Finding the other characters I would suggest to only match on the bottom 8 lines as it looks like the cats are not there and its unique enough to recognize what you want.

Other suggestion I can give take a look at the hexadecimal values of the picture and make the line matching a little more advanced.
As your pictures are black and white it should not be very hard. Study the code and you can make it work to not only match a rectangle but search for boundary.

Offcourse its hard to get the cats out of it and if it becomes coloured it will become even harder to match.

Edit:
* Based on the seven sample I modified the UDF in such a way that if result is false you still get the best top/left location on a best guess basis.
* Works only with BMP files not with PNG to see it working you should save the PNG as seven.BMP
Something like
Global Const $Bitmap4Filename = @TempDir & "\SEVEN.bmp" $start = TimerInit() $tResult=findBMP($Bitmap1Filename,$Bitmap4Filename,TRUE) ConsoleWrite($tResult &  "** Full seven totalmatch time elapsed: " & TimerDiff($start) / 1000 & "  seconds" & @LF)

Edited by junkew, 21 March 2008 - 10:20 PM.


#5 Armand

Armand

    Universalist

  • Active Members
  • PipPipPipPipPipPip
  • 541 posts

Posted 20 March 2008 - 07:57 PM

@junkew
how could there be any difference if picture A is in fact a slice of B ?!
i am almost definite that there is a mistake in the comparing algorithm...

anyhow, just trying to help you improving you UDF :)
thanks in advance!

#6 Impulse08

Impulse08

    Seeker

  • Active Members
  • 30 posts

Posted 20 March 2008 - 09:46 PM

@junkew
how could there be any difference if picture A is in fact a slice of B ?!
i am almost definite that there is a mistake in the comparing algorithm...

anyhow, just trying to help you improving you UDF :)
thanks in advance!

I can see what he's saying. Just by do a copy image and pasting it into MSPaint, I see that your first 7 cuts off part of the cat on the right side. So the algorithm to find the whole image A is failing because there is some extra whitespace in image A on the right versus image B.

Try it out for yourself from your post. Right-click on the image and copy image. Then paste into MSPaint. Then do the same for image A. You'll see that image A is slightly larger than the 7 in B
-- If the apocalypse comes... beep me.

#7 Oldschool

Oldschool

    Universalist

  • Active Members
  • PipPipPipPipPip
  • 254 posts

Posted 21 March 2008 - 07:49 AM

Well done!! This is awesome work!!

I've been trying to come up with a way, but you beat me to it.

#8 Armand

Armand

    Universalist

  • Active Members
  • PipPipPipPipPipPip
  • 541 posts

Posted 24 March 2008 - 09:15 AM

does anyone has anyone have any example with pre'made .bmp files ?!

#9 junkew

junkew

    Universalist

  • Active Members
  • PipPipPipPipPipPip
  • 322 posts

Posted 24 March 2008 - 10:29 AM

does anyone has anyone have any example with pre'made .bmp files ?!


What do you mean?

* http://www.autoitscript.com/forum/index.ph...=pixelchecksum) gives an idea how to store an BMP.

If you remove all code between ; ** Example start ** and ; ** Example end ** and put in something like

$tResult=findBMP("C:\TEMP\BITMAP1.BMP", "C:\TEMP\BITMAP2.BMP",TRUE)

It should work although not thoroughly tested with BMP not created by AutoIt.

Known Issues
* Rounding rectangles on window can cause mismatch as backgroundcolor on corner can be different compared to moment when screenshot was taken.

#10 Armand

Armand

    Universalist

  • Active Members
  • PipPipPipPipPipPip
  • 541 posts

Posted 24 March 2008 - 11:48 AM

well.. i meant that you'll try and do something more like what i've done...

post pic A.
[PICTURE]

post pic B.
[PICTURE]

and the script that handles both of these.

Cause i'd really really really like to use this UDF for non-autoit pictures...

#11 junkew

junkew

    Universalist

  • Active Members
  • PipPipPipPipPipPip
  • 322 posts

Posted 24 March 2008 - 07:19 PM

I tried and actually the algorithm seems to be misbehaving on black/white pictures. (fixed)
The less color in the picture(s) the more risk it will not properly recognize (Even if saved as 24 bits BMP)

I will rewrite the algoritm as soon as I have new beta installed as to fix/enhance the algorithm I am in need of stringinstr function where I can give startposition or I have to put the bitmaps in an array for each line of data which will slow down performance (Not sure how long it takes to load a desktop of 1280*1024 into an array of 1280 lines and with an array I need to do more comparisons as done in current algorithm).

* http://www.autoitscript.com/forum/index.ph...mp;#entry496554

Edit 24 march 2008: Modified first posting and added attachments and samples on how to handle

Edited by junkew, 24 March 2008 - 10:13 PM.


#12 junkone

junkone

    Seeker

  • Active Members
  • 9 posts

Posted 26 March 2008 - 03:01 PM

i get error AVector: [] Out of bounds. in C:\Program Files\AutoIt3\Include\GDIPlus.au3 (2688) : ==> Subscript used with non-Array variable.:
Return $aResult[0] <> 0
Return $aResult^ ERROR
>Exit code: 1 Time: 275.393

I am not sure what this means. appreiate any help. I am running it on windows 2000 prof

#13 junkew

junkew

    Universalist

  • Active Members
  • PipPipPipPipPipPip
  • 322 posts

Posted 26 March 2008 - 10:03 PM

First try if a script like below works

#include <ScreenCapture.au3> _ScreenCapture_Capture(@tempdir & "\screen.bmp",0,0,-1,-1,false)


If this gives the same problem you most likely have not GDIPLUS.DLL on the right path location

#14 junkone

junkone

    Seeker

  • Active Members
  • 9 posts

Posted 28 March 2008 - 07:04 PM

bingo. gdiplus is not installed on win2k by default. i installed it and it works beautifully. however i havea smaill issue. i used the app to take a screenshot of my app. then i am using the same to take subsequent screenshots to compare and see if it is the same. however this is what i find.
$tResult=findBMP($BitmapOriginalFile,$BitmapCurrentFile,False)
it always returns "False;1;0;0;808;627;2;22"

i would have imagined that it would return true as both the images are supposed to be taken the same way without any change. strange is the way it works.

#15 Toady

Toady

    Easy there turbo...

  • Active Members
  • PipPipPipPipPipPip
  • 698 posts

Posted 28 March 2008 - 07:46 PM

this is neat, good work!
www.itoady.com (Go here to download the MacroGamer installer)

#16 junkew

junkew

    Universalist

  • Active Members
  • PipPipPipPipPipPip
  • 322 posts

Posted 29 March 2008 - 10:07 AM

bingo. gdiplus is not installed on win2k by default. i installed it and it works beautifully. however i havea smaill issue. i used the app to take a screenshot of my app. then i am using the same to take subsequent screenshots to compare and see if it is the same. however this is what i find.
$tResult=findBMP($BitmapOriginalFile,$BitmapCurrentFile,False)
it always returns "False;1;0;0;808;627;2;22"

i would have imagined that it would return true as both the images are supposed to be taken the same way without any change. strange is the way it works.

Try code like below. What happens if you compare the same screen bitmap to each other. On my system it works and returns True (takes about 30 seconds to match a full screen in all details).
Remember that when anything changes on the screen you have a mismatch. Moving a mouse can already change highlighted pixels of a button,
taskbar at the bottom, animated gifs, characters typed somewhere.

Global Const $Bitmap1Filename = @TempDir & "\FULLSCREEN.bmp" ; Capture full screen $begin = TimerInit() _ScreenCapture_Capture($Bitmap1Filename,0,0,-1,-1,false) ConsoleWrite("Saving full screen took " & TimerDiff($begin) & " milliseconds " & @LF) ;Do the actual find ;Find the full screen itself $start = TimerInit() $tResult=findBMP($Bitmap1Filename,$Bitmap1Filename,TRUE) ConsoleWrite($tResult &  "** Full screen totalmatch time elapsed: " & TimerDiff($start) / 1000 & "  seconds" & @LF)


#17 junkone

junkone

    Seeker

  • Active Members
  • 9 posts

Posted 31 March 2008 - 06:40 PM

i do not capture the full screen. i onlyh capture the specific window. obviously there maybe changes if i did fullscreen because of the clock at the bottom of my window.
$targetHWND = WinGetHandle("Presentation Server")


$begin = TimerInit()
winactivate($targetHWND)
_ScreenCapture_CaptureWnd($BitmapCurrentFile, $targetHWND)
ConsoleWrite("Saving NR window " & TimerDiff($begin) & " milliseconds " & @LF)
$WshShell.LogEvent ($INFORMATION, "Saved NR L window ")

then i compare it with a copy of the window captured using teh same method previously
$tResult=findBMP($BitmapOriginalFile,$BitmapCurrentFile,False)
it alwys returns false with the same difference False;1;0;0;808;627;2;22"

#18 junkew

junkew

    Universalist

  • Active Members
  • PipPipPipPipPipPip
  • 322 posts

Posted 01 April 2008 - 11:07 AM

i do not capture the full screen
...
it alwys returns false with the same difference False;1;0;0;808;627;2;22"


1. Try to do the same for calculator screen take twice a calculator snapshot. On my system it returns correct result
2. Attach your 2 bitmapfiles to the post I can check then if the algorithm has an issue.

Actually the result you get back means that there actually is only 1 matching line. To me it suggest(s) that your picture is off by one line or one pixel column and the second screenshot is not fully equal to the previous one taken.
Is the title on your window exactly the same (no spaces added etc.)?

#19 Armand

Armand

    Universalist

  • Active Members
  • PipPipPipPipPipPip
  • 541 posts

Posted 01 April 2008 - 12:09 PM

@junkew
any luck there with the external monochrome bitmaps algorithm ?

#20 junkew

junkew

    Universalist

  • Active Members
  • PipPipPipPipPipPip
  • 322 posts

Posted 01 April 2008 - 04:22 PM

@junkew
any luck there with the external monochrome bitmaps algorithm ?


I modified the first post and included the samples you gave to work with.
You can work with black and white pictures as long as you store them as 24 bits bitmap.

With monochrome bitmaps the algorithm will only work if you align on 8 bits (which then becomes a byte and then it will work as algorithm is based on byte boundaries) but still its tricky as the picture you are searching in should also be on an 8 bits boundary.

The "problem" with monochrome is that each pixel is bit encoded and as such 8 pixels are in a byte whereas in a 24 color bitmap you have 3 bytes for 1 pixel.

If you want to find a monochrome bmp on a 24 bmp colored screen I would suggest to first convert it to a 24 bits color bitmap.

To be able to do 2 color bitmap searching I have to shift each string each time 8 times and compare it 8 times with a shifted string of the picture to search in.

Imagine that you want to find 00010001 on a screen of 32 pixels width which is 4 bytes like 00000001 00010000 00000000 10010000
I then would have to shift like below
00010001
shift 1: 00000010 00100000 00000001 00100000 would not match as the matching string is over the first 2 bytes
shift 2: 00000100 01000000 00000010 01000000 would not match as the matching string is over the first 2 bytes
shift 3: 00001000 10000000 00000100 10000000 would not match as the matching string is over the first 2 bytes
shift 4: 00010001 00000000 00001001 00000000 would match


Incorporating that kind of logic is making it much more complicated then to switch to a getPixel algorithm

Edited by junkew, 01 April 2008 - 04:22 PM.





0 user(s) are reading this topic

0 members, 0 guests, 0 anonymous users