kylejustknows Posted June 11, 2019 Posted June 11, 2019 (edited) The code is solid and simple, it can almost explain itself. This is the native autoit way to do the "imagesearch", no 3rd party .dll needed. It gets "your.bmp", and "screenshot.bmp" ----> Convert the .bmp files into 2D-Arrays (Malkey's function) ----> Compare the 2D-arrays, return the matched position. Tested on: Windows 7; Windows server 2008R2; Windows 10 1809. Pros: It is native. No extra .dll needed It is super robust. (I used to have lots of funny results using other imagesearch libs). It gets screenshot the same you get your screenshot crop, so it always gets a solid result, and 100% accurate. The code is very simple and friendly, all level users can understand and use it. Cons: It is slow to convert your.big.screen.bmp into a 2D-array, and may consume 200+MB of memory and may take 5 - 20 seconds to return the result. (the actual search in an array is fast, but the conversion from .bmp to array is slow. The speed depends on your CPU speed and your screen size). Correct: now optimized, it's ~5 seconds and ~ 70MB ram usage. It is a pixel-by-pixel color-code strict comparison in the "array-in-array" search, so you have to use the 24-bit BMP file, no "Tolerance" allowed. 2019-Jun-11: script update: Same day updated: Update example; Optimize the algorithm for performance, now most computers can get the result in ~5 seconds, using ~70MB temporary memory, for the 1920x1080 resolution screen. 2019-Jun-12 script update: It now uses "PrintScreen" hotkey to save the screenshot.bmp (restores the user's old clipboard content after it is done) ~This is the only way to make sure the screenshot matches exactly what the user is seeing, after doing dozens of harsh tests. The reason: The UDF "ScreenCapture" and "ImageSearch.dll" are not reliable for an unknown reason. Some window/dialogue special drawings are "invisible" in their screenshots. But the "PrintScreen" key -> Clipboard -> screenshot.bmp, this method always catches exact things showing on the screen. expandcollapse popup#include <GDIPlus.au3> #include <ClipBoard.au3> ;Sinple Example.================== the 1.bmp is what you want to find on your screen $result = _ScreenSearchBmp("1.bmp") if $result[0] = 0 Then MsgBox(0,"","not found") Else MouseMove($result[0],$result[1],20) ;move mouse to the result EndIf ;Example End.================== You can "include" this file after you remove this "Example" part here. ;=============================================================================== ; ; Description: Main Function. Find the position of an image on the desktop ; Parameter(s): ; $center = 1 - Set where the returned x,y location of the image is. ; default 1 means center, 0 means top-left ; ; Return Value(s): On Success - Returns the array of matched position [x,y] on your screen. ; On Failure - Returns array [0,0] (BTW, there is no position 0,0 on a screen, so it means error) ; ; Note: Warning: The BMP file must be a 24-bit BMP (windows default) ; ;=============================================================================== Func _ScreenSearchBmp($file,$center=1) local $pixelarray,$screenarray ;get both your image.bmp and screenshot.bmp into pixel-by-pixel 2D arrays _FileImageToArray($file, $pixelarray) _Clip_screenshot(@TempDir & "\screenshot.bmp") _FileImageToArray(@TempDir & "\screenshot.bmp",$screenarray) FileDelete(@TempDir & "\screenshot.bmp") ;compare the 2 2D-arrays local $result = _2darray_in_2darray($screenarray,$pixelarray) ;result tidy up, for if $center=1, and for if not found. Local $aresult[2] $aresult[0] = $result[0] $aresult[1] = $result[1] if $aresult[0] = 0 then Return $aresult ;if not found , return 0 0 here if $center = 1 then $aresult[0] = $result[0]+ Round(UBound($pixelarray,1)/2) if $center = 1 then $aresult[1] = $result[1]+ Round(UBound($pixelarray,2)/2) Return $aresult ;if ALL GOOD, and $center=1 then return the center of the image here. EndFunc ;=============================================================================== ; Code by Malkey, converts .bmp into 2D array pixal by pixal. : thanks man! ;=============================================================================== Func _FileImageToArray($filename, ByRef $aArray) Local $Reslt, $stride, $format, $Scan0, $iW, $iH, $hImage Local $v_Buffer, $width, $height Local $i, $j _GDIPlus_Startup() $hImage = _GDIPlus_ImageLoadFromFile($filename) $iW = _GDIPlus_ImageGetWidth($hImage) $iH = _GDIPlus_ImageGetHeight($hImage) $Reslt = _GDIPlus_BitmapLockBits($hImage, 0, 0, $iW, $iH, $GDIP_ILMREAD, $GDIP_PXF32ARGB) ;Get the returned values of _GDIPlus_BitmapLockBits () $width = DllStructGetData($Reslt, "width") $height = DllStructGetData($Reslt, "height") $stride = DllStructGetData($Reslt, "stride") $format = DllStructGetData($Reslt, "format") $Scan0 = DllStructGetData($Reslt, "Scan0") Dim $aArray[$width][$height] For $i = 0 To $iW - 1 For $j = 0 To $iH - 1 $aArray[$i][$j] = DllStructGetData(DllStructCreate("dword", $Scan0 + ($j * $stride) + ($i * 4)), 1) Next Next _GDIPlus_BitmapUnlockBits($hImage, $Reslt) _GDIPlus_ImageDispose($hImage) _GDIPlus_Shutdown() Return EndFunc ;==>_FileImageToArray ;=============================================================================== ; ; Description: ; My code, search a 2D array inside another 2d array ; If found, return the positon of first element ; If error or not found, return array [0,0]. Because the very first match would be [1,1], "0" means something wrong. ; eg. search a 2d array ; [1,2,3,4] ; [5,6,7,8] ; [9,0,1,2] ; for: ; [7,8] ; [1,2] ; You will get result [2,3] (means, matched, first element position is row 2, colunm 3) ; ; Parameter(s): ; ; Return Value(s): On Success - Returns the array of matched [x,y], the top-left element position in the source. ; On Failure - Returns [0,0] ; ; ;=============================================================================== Func _2darray_in_2darray($source,$search) ;get the size of the both arrays local $sourcerow = UBound($source,1) Local $sourcecol = UBound($source,2) local $searchrow = UBound($search,1) Local $searchcol = UBound($search,2) ;error input cheching, if error return position 0,0 if $sourcerow = 0 or $sourcecol = 0 or $searchrow = 0 or $searchcol = 0 then Local $aPeople[2] $aPeople[0] = 0 $aPeople[1] = 0 Return $aPeople EndIf ; A crazy 4-for-loops, compare every x,y of search array in every x,y in source array for $ssr = 1 to $sourcerow - $searchrow +1 for $ssc = 1 to $sourcecol - $searchcol +1 for $sr = 1 to $searchrow for $sc = 1 to $searchcol ;if an element not match, go back, search for next if $search[$sr-1][$sc-1] <> $source[$ssr+$sr-2][$ssc+$sc-2] then ContinueLoop 3 Next Next ;if the loop passed all elements test, made it here, means the result is found! congress! lets return the result: Local $aPeople[2] $aPeople[0] = $ssr $aPeople[1] = $ssc Return $aPeople Next Next ;all the loops finished, no result found. return [0,0] Local $aPeople[2] $aPeople[0] = 0 $aPeople[1] = 0 Return $aPeople EndFunc ; #FUNCTION# ==================================================================================================================== ; Name ..........: _Clip_screenshot ; Description ...: This get a screenshot.bmp using "Print Screen" key, so the image is EXACT same image you use "Print Screen" key, to avoid funny results. ; Syntax ........: _Clip_screenshot($file) ; Parameters ....: $file - The location of the screen shot .bmp file you want it to save ; Return values .: None ; Author ........: Kyle ; =============================================================================================================================== Func _Clip_screenshot($file) local $tempdata = _ClipBoard_GetData() ;save current user's clipboard Send("{PRINTSCREEN}") sleep(200) If _ClipBoard_IsFormatAvailable($CF_BITMAP) Then _ClipBoard_Open(0) $hClipboardImage = _ClipBoard_GetDataEx($CF_BITMAP) _ClipBoard_Close() _GDIPlus_Startup() $hBitmap = _GDIPlus_BitmapCreateFromHBITMAP($hClipboardImage) Local $iX = _GDIPlus_ImageGetWidth($hBitmap) Local $iY = _GDIPlus_ImageGetHeight($hBitmap) Local $hClone = _GDIPlus_BitmapCloneArea($hBitmap, 0, 0, $iX, $iY, $GDIP_PXF24RGB) ;make sure its 24bit bmp _GDIPlus_ImageDispose($hBitmap) $hBitmap = $hClone $sCLSID = _GDIPlus_EncodersGetCLSID("BMP") _GDIPlus_ImageSaveToFileEx($hBitmap, $file, $sCLSID, 0) _GDIPlus_BitmapDispose($hBitmap) _GDIPlus_Shutdown() EndIf _ClipBoard_SetData($tempdata) ; restore user clipboard EndFunc Remove the "example" part then you can include this code as a file. Edited July 6, 2019 by kylejustknows ScrapeYourself, ImStUPid and aa2zz6 1 2
aa2zz6 Posted June 11, 2019 Posted June 11, 2019 Thanks! If I get a chance tonight I would like to try this.
legend Posted December 3, 2019 Posted December 3, 2019 First one i tried that actually works, unfortunately it's super slow finding the image, any way to improve the speed?
kylejustknows Posted March 19, 2020 Author Posted March 19, 2020 On 12/4/2019 at 5:24 AM, legend said: First one i tried that actually works, unfortunately it's super slow finding the image, any way to improve the speed? unfortunately, this is the fastest the AutoIt can do. As an advanced script language, Autoit is awfully slow at native heavy computing (image process). Unless someone can provide me a more "advanced algorithm" to do the faster array-in-array search.
Moderators Melba23 Posted April 21, 2020 Moderators Posted April 21, 2020 Skamleinad, Welcome to the AutoIt forum. Unfortunately you appear to have missed the Forum rules on your way in. Please read them now - particularly the bit about not discussing game automation - and then you will understand why you will get no help here. See you soon with a legitimate question I hope. M23 Any of my own code posted anywhere on the forum is available for use by others without any restriction of any kind Open spoiler to see my UDFs: Spoiler ArrayMultiColSort ---- Sort arrays on multiple columnsChooseFileFolder ---- Single and multiple selections from specified path treeview listingDate_Time_Convert -- Easily convert date/time formats, including the language usedExtMsgBox --------- A highly customisable replacement for MsgBoxGUIExtender -------- Extend and retract multiple sections within a GUIGUIFrame ---------- Subdivide GUIs into many adjustable framesGUIListViewEx ------- Insert, delete, move, drag, sort, edit and colour ListView itemsGUITreeViewEx ------ Check/clear parent and child checkboxes in a TreeViewMarquee ----------- Scrolling tickertape GUIsNoFocusLines ------- Remove the dotted focus lines from buttons, sliders, radios and checkboxesNotify ------------- Small notifications on the edge of the displayScrollbars ----------Automatically sized scrollbars with a single commandStringSize ---------- Automatically size controls to fit textToast -------------- Small GUIs which pop out of the notification area
Cam9649 Posted February 9, 2021 Posted February 9, 2021 Searched for a desktop icon but it selects the icon below and to the right of the icon. Upgrade.bmp
Joaquin Posted December 27, 2022 Posted December 27, 2022 (edited) Excellent tool!!! It's gorgeous. If you are looking for a way to cheat on a videogame. this is not for you. I recommend adding alt + ctrl to the screenshoot in order to have two monitors compatibility. Func _Clip_screenshot($file) local $tempdata = _ClipBoard_GetData() ;save current user's clipboard Send("{ALTDOWN}") Send("{CTRLDOWN}") Send("{PRINTSCREEN}") Send("{ALTUP}") Send("{CTRLUP}") sleep(200) If _ClipBoard_IsFormatAvailable($CF_BITMAP) Then _ClipBoard_Open(0) $hClipboardImage = _ClipBoard_GetDataEx($CF_BITMAP) _ClipBoard_Close() _GDIPlus_Startup() $hBitmap = _GDIPlus_BitmapCreateFromHBITMAP($hClipboardImage) Local $iX = _GDIPlus_ImageGetWidth($hBitmap) Local $iY = _GDIPlus_ImageGetHeight($hBitmap) Local $hClone = _GDIPlus_BitmapCloneArea($hBitmap, 0, 0, $iX, $iY, $GDIP_PXF24RGB) ;make sure its 24bit bmp _GDIPlus_ImageDispose($hBitmap) $hBitmap = $hClone $sCLSID = _GDIPlus_EncodersGetCLSID("BMP") _GDIPlus_ImageSaveToFileEx($hBitmap, $file, $sCLSID, 0) _GDIPlus_BitmapDispose($hBitmap) _GDIPlus_Shutdown() EndIf _ClipBoard_SetData($tempdata) ; restore user clipboard EndFunc Edited December 27, 2022 by Jos added codebox... please use <> next time. :)
Funtime60 Posted January 31, 2023 Posted January 31, 2023 I appreciate the help this file has given me, but I took issue with the error handling. Images DO have a 0,0 and that is what @error is for. It also doesn't account for multiple monitors if the monitor is left or above the primary monitor giving them a position of -x and/or -y. I also didn't like that I had to screen cap manually. I also didn't like that it doesn't try to avoid collisions in the tmp directory. I also didn't like how early it imported the sub-image to search for before it was actually used. I also found it to be slow when searching an entire 3000x5560 desktop, much to not have a progress bar. (The one downside to quint monitors, 1920 + 2560 + 1080 wide and 1080 + 1920 tall). So I fixed all these issues in my personal version. #include <File.au3> - For _TempFile in order to have better temp file usage #include <ScreenCapture.au3> - For _ScreenCapture_Capture in order to screen cap without user interaction #include <WinAPISys.au3> - For _WinAPI_GetSystemMetrics 76-79 in order to get the size of the screen and the number of pixels left and above the origin I edited _ScreenSearchBmp primarily, but added SetError to _2darray_in_2darray and toggle-able progress console to _FileImageToArray and _2darray_in_2darray. I'm not sure whether I should share my copy or not so I've just included a description of my modifications for now.
Recommended Posts
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 accountSign in
Already have an account? Sign in here.
Sign In Now