junkew Posted December 14, 2009 Share Posted December 14, 2009 (edited) based on this vb example I rebuild it in AutoIT http://www.vbforums.com/attachment.php?attachmentid=57693&d=1182531646 Somehow I am unable to count the difference in pixels. What am I overlooking after I press button 2. The difference should be 6397 expandcollapse popup#include <GDIPlus.au3> #include <WinAPI.au3> #include <GUIConstants.au3> #include <WindowsConstants.au3> #include <ButtonConstants.au3> #include <GUIConstantsEx.au3> #include <StaticConstants.au3> dim $bmpData, $w, $h, $s #Region ### START Koda GUI section ### Form= $frmCompare = GUICreate("Compare pictures", 385, 300, 193, 125) $Pic1 = GUICtrlCreatePic("earth1.jpg", 32, 24, 100, 100, BitOR($SS_NOTIFY,$WS_GROUP,$WS_CLIPSIBLINGS)) $Pic3 = GUICtrlCreatePic("earth2.jpg", 256, 24, 100, 100, BitOR($SS_NOTIFY,$WS_GROUP,$WS_CLIPSIBLINGS)) $Pic2 = GUICtrlCreatePic("earth1.jpg", 146, 23, 100, 100, BitOR($SS_NOTIFY,$WS_GROUP,$WS_CLIPSIBLINGS)) $Pic4 = GUICtrlCreatePic("", 88, 144, 100, 100, BitOR($SS_NOTIFY,$WS_GROUP,$WS_CLIPSIBLINGS)) $Pic5 = GUICtrlCreatePic("", 224, 144, 100, 100, BitOR($SS_NOTIFY,$WS_GROUP,$WS_CLIPSIBLINGS)) $Button1 = GUICtrlCreateButton("Button1", 104, 256, 75, 25, 0) $Button2 = GUICtrlCreateButton("Button2", 240, 256, 75, 25, 0) GUISetState(@SW_SHOW) #EndRegion ### END Koda GUI section ### $iWidth=100 $iHeight=100 $iX=0 $iY=0 ; Initialize GDI+ library _GDIPlus_Startup() $hGUI1 = ControlGetHandle ( "Compare pictures", "", $Pic1) $hD1 = _WinAPI_GetDC($hGUI1) $hC1 = _WinAPI_CreateCompatibleDC($hD1) $hBMP1 = _WinAPI_CreateCompatibleBitmap($hD1, $iWidth, $iHeight) _WinAPI_SelectObject($hC1, $hBMP1) $hGUI2 = ControlGetHandle ( "Compare pictures", "", $Pic2) $hD2 = _WinAPI_GetDC($hGUI2) $hC2 = _WinAPI_CreateCompatibleDC($hD2) $hBMP2 = _WinAPI_CreateCompatibleBitmap($hD2, $iWidth, $iHeight) _WinAPI_SelectObject($hC2, $hBMP2) $hGUI3 = ControlGetHandle ( "Compare pictures", "", $Pic3) $hD3 = _WinAPI_GetDC($hGUI3) $hC3 = _WinAPI_CreateCompatibleDC($hD3) $hBMP3 = _WinAPI_CreateCompatibleBitmap($hD3, $iWidth, $iHeight) _WinAPI_SelectObject($hC3, $hBMP3) $hGUI4 = ControlGetHandle ( "Compare pictures", "", $Pic4) $hD4 = _WinAPI_GetDC($hGUI4) $hC4 = _WinAPI_CreateCompatibleDC($hD4) $hBMP4 = _WinAPI_CreateCompatibleBitmap($hD4, $iWidth, $iHeight) _WinAPI_SelectObject($hC4, $hBMP4) $hGUI5 = ControlGetHandle ( "Compare pictures", "", $Pic5) $hD5 = _WinAPI_GetDC($hGUI5) $hC5 = _WinAPI_CreateCompatibleDC($hD5) $hBMP5 = _WinAPI_CreateCompatibleBitmap($hD5, $iWidth, $iHeight) _WinAPI_SelectObject($hC5, $hBMP5) While 1 $nMsg = GUIGetMsg() Switch $nMsg Case $GUI_EVENT_CLOSE _GDIPlus_Shutdown() Exit Case $Button1 _WinAPI_BitBlt($hD4, 0, 0, $iWidth, $iHeight, $hd1, $iX, $iY, $SRCCOPY ) _WinAPI_BitBlt($hD4, 0, 0, $iWidth, $iHeight, $hd2, $iX, $iY, $SRCINVERT ) getImageData(_GDIPlus_BitmapCreateFromHBITMAP($hBMP5),$bmpData,$w,$h,$s) Case $Button2 _WinAPI_BitBlt($hD5, 0, 0, $iWidth, $iHeight, $hd1, $iX, $iY, $SRCCOPY ) _WinAPI_BitBlt($hD5, 0, 0, $iWidth, $iHeight, $hd3, $iX, $iY, $SRCINVERT ) getImageData(_GDIPlus_BitmapCreateFromHBITMAP($hBMP5),$bmpData,$w,$h,$s) consolewrite($w & ";" & $s & ";" & ";" &$h) ;~ TODO: ;~ 6397 differences to find $diff=0 $i=0 for $i=0 to binarylen($bmpdata) if binarymid($bmpdata,($i*3)+1,3)<>(chr(0)&chr(0)&chr(0)) Then $diff=$diff+1 endif Next consolewrite($diff & " differences found") EndSwitch WEnd Func GetImageData($pBitmap, ByRef $BMPDataStart, ByRef $Width, ByRef $Height, ByRef $Stride, $imgBytes = 3) Local $Scan0, $pixelData, $hbScreen, $pBitmapCap, $handle If ($imgBytes = 2) Then $BitmapData = _GDIPlus_BitmapLockBits($pBitmap, 0, 0, _GDIPlus_ImageGetWidth($pBitmap), _GDIPlus_ImageGetHeight($pBitmap), $GDIP_ILMREAD, $GDIP_PXF16RGB555) Else $BitmapData = _GDIPlus_BitmapLockBits($pBitmap, 0, 0, _GDIPlus_ImageGetWidth($pBitmap), _GDIPlus_ImageGetHeight($pBitmap), $GDIP_ILMREAD, $GDIP_PXF24RGB) 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 Edited December 14, 2009 by junkew FAQ 31 How to click some elements, FAQ 40 Test automation with AutoIt, Multithreading CLR .NET Powershell CMDLets Link to comment Share on other sites More sharing options...
GEOSoft Posted December 14, 2009 Share Posted December 14, 2009 I think this may have to be done in stages. Start by replacing all those ControlGetHandle() calls to GUICtrlGetHandle() Then we can get on to what is wrong in the loop or in the compare. George Question about decompiling code? Read the decompiling FAQ and don't bother posting the question in the forums.Be sure to read and follow the forum rules. -AKA the AutoIt Reading and Comprehension Skills test.*** The PCRE (Regular Expression) ToolKit for AutoIT - (Updated Oct 20, 2011 ver:3.0.1.13) - Please update your current version before filing any bug reports. The installer now includes both 32 and 64 bit versions. No change in version number. Visit my Blog .. currently not active but it will soon be resplendent with news and views. Also please remove any links you may have to my website. it is soon to be closed and replaced with something else. "Old age and treachery will always overcome youth and skill!" Link to comment Share on other sites More sharing options...
Yashied Posted December 14, 2009 Share Posted December 14, 2009 Perhaps this is due to the fact that you are using JPG images. My UDFs: iKey | FTP Uploader | Battery Checker | Boot Manager | Font Viewer | UDF Keyword Manager | Run Dialog Replacement | USBProtect | 3D Axis | Calculator | Sleep | iSwitcher | TM | NetHelper | File Types Manager | Control Viewer | SynFolders | DLL Helper Animated Tray Icons UDF Library | Hotkeys UDF Library | Hotkeys Input Control UDF Library | Caret Shape UDF Library | Context Help UDF Library | Most Recently Used List UDF Library | Icons UDF Library | FTP UDF Library | Script Communications UDF Library | Color Chooser UDF Library | Color Picker Control UDF Library | IPHelper (Vista/7) UDF Library | WinAPI Extended UDF Library | WinAPIVhd UDF Library | Icon Chooser UDF Library | Copy UDF Library | Restart UDF Library | Event Log UDF Library | NotifyBox UDF Library | Pop-up Windows UDF Library | TVExplorer UDF Library | GuiHotKey UDF Library | GuiSysLink UDF Library | Package UDF Library | Skin UDF Library | AITray UDF Library | RDC UDF Library Appropriate path | Button text color | Gaussian random numbers | Header's styles (Vista/7) | ICON resource enumeration | Menu & INI | Tabbed string size | Tab's skin | Pop-up circular menu | Progress Bar without animation (Vista/7) | Registry export | Registry path jumping | Unique hardware ID | Windows alignment More... Link to comment Share on other sites More sharing options...
Authenticity Posted December 14, 2009 Share Posted December 14, 2009 I'm trying to understand why are you calculating the byte array as: $pixelData = DllStructCreate("ubyte lData[" & (Abs($Stride) * $Height - 1) & "]", $Scan0) If it's 24 bits it should be width * height * 3, if it's 16 bits it should be width * height * 2. Link to comment Share on other sites More sharing options...
Yashied Posted December 14, 2009 Share Posted December 14, 2009 (edited) #Include <WinAPIEx.au3> ... ConsoleWrite($w & ";" & $s & ";" & ";" & $h & @CR) ;~ TODO: ;~ 6397 differences to find $diff = 0 For $i = 0 To 99 For $j = 0 To 99 If _WinAPI_GetPixel($hD5, $i, $j) <> 0 Then $diff += 1 EndIf Next Next ; $i = 0 ; MsgBox(0, '', BinaryLen($bmpData)) ; For $i = 0 To BinaryLen($bmpData) ; If BinaryMid($bmpData, ($i * 3) + 1, 3) <> (Chr(0) & Chr(0) & Chr(0)) Then ; $diff = $diff + 1 ; EndIf ; Next ConsoleWrite($diff & " differences found") ...WinAPIEx.au35297 differences for me. But if you compare two images by using Photoshop then the differences would be much greater. Edited December 14, 2009 by Yashied My UDFs: iKey | FTP Uploader | Battery Checker | Boot Manager | Font Viewer | UDF Keyword Manager | Run Dialog Replacement | USBProtect | 3D Axis | Calculator | Sleep | iSwitcher | TM | NetHelper | File Types Manager | Control Viewer | SynFolders | DLL Helper Animated Tray Icons UDF Library | Hotkeys UDF Library | Hotkeys Input Control UDF Library | Caret Shape UDF Library | Context Help UDF Library | Most Recently Used List UDF Library | Icons UDF Library | FTP UDF Library | Script Communications UDF Library | Color Chooser UDF Library | Color Picker Control UDF Library | IPHelper (Vista/7) UDF Library | WinAPI Extended UDF Library | WinAPIVhd UDF Library | Icon Chooser UDF Library | Copy UDF Library | Restart UDF Library | Event Log UDF Library | NotifyBox UDF Library | Pop-up Windows UDF Library | TVExplorer UDF Library | GuiHotKey UDF Library | GuiSysLink UDF Library | Package UDF Library | Skin UDF Library | AITray UDF Library | RDC UDF Library Appropriate path | Button text color | Gaussian random numbers | Header's styles (Vista/7) | ICON resource enumeration | Menu & INI | Tabbed string size | Tab's skin | Pop-up circular menu | Progress Bar without animation (Vista/7) | Registry export | Registry path jumping | Unique hardware ID | Windows alignment More... Link to comment Share on other sites More sharing options...
junkew Posted December 14, 2009 Author Share Posted December 14, 2009 Start by replacing all those ControlGetHandle() calls to GUICtrlGetHandle()Will do that but I am sure that I am having the right DC as the consolewrite gives the right information and sizes._WinAPI_GetPixelIn general the purpose of my script is to remove the dependency on the "slow" getpixel methods compared to checking the screenpixel(s) directly. Also should work on BMP files or any file format supported by GDIIf it's 24 bits it should be width * height * 3, if it's 16 bits it should be width * height * 2.Retrieving stride is already doing this calculation internally: stride= w * h * 3 or w * h * 2 or w * h * 4.Perhaps this is due to the fact that you are using JPG images.The answer I get is almost 4 times as high as expected. I will retry with BMP files but doubt if that will make a difference.My ultimate goal would bedetermine all x and y locations where there is a difference as soon as possible.do a stringregexp for everything thats not black so I immediately know all locations (x,y) that have a different color. Stringregexp most likely will not work as black is 0x000000 and then the string functions are not to be trusted.Thoughts to workaround that are using stringsplit on 0x00000 or make use of stripws. All those thoughts are there to limit the amount of characters to scan for difference as much as possible before going to a for x = 1 to height for y = 1 to widthloop to determine the exact difference. FAQ 31 How to click some elements, FAQ 40 Test automation with AutoIt, Multithreading CLR .NET Powershell CMDLets Link to comment Share on other sites More sharing options...
Zedna Posted December 14, 2009 Share Posted December 14, 2009 (edited) You can also use Prospeed DLL. There is AU3 UDF wrapper for this DLL so using is quite simple.There is function CompareBytes() where you can specify also tolerance. It returns number of different bytes in two images. Edited December 14, 2009 by Zedna Resources UDF ResourcesEx UDF AutoIt Forum Search Link to comment Share on other sites More sharing options...
junkew Posted December 14, 2009 Author Share Posted December 14, 2009 You can also use Prospeed DLL. There is AU3 UDF wrapper for this DLL so using is quite simple.There is function CompareBytes() where you can specify also tolerance. It returns number of different bytes in two images.Clear. But my goal was to have it in pure AutoIT code. At first sight the problem I have doesn't look complicated but on second sight it just seems not to work. So any help to get it working in pure AutoIT code will help. FAQ 31 How to click some elements, FAQ 40 Test automation with AutoIt, Multithreading CLR .NET Powershell CMDLets Link to comment Share on other sites More sharing options...
junkew Posted December 14, 2009 Author Share Posted December 14, 2009 I at least found a direction where the issue is. Somehow the picturebytes I have are the grey background ones 0x30 0x30 0x30 and not the actual pixels I see in the screen. FAQ 31 How to click some elements, FAQ 40 Test automation with AutoIt, Multithreading CLR .NET Powershell CMDLets Link to comment Share on other sites More sharing options...
AndyG Posted December 14, 2009 Share Posted December 14, 2009 (edited) $Stride=Stride means the number of bytes in one line, including the fillbyte(s) (some say padding) at the end of the line which are there to make the number of bytes to a multiple of four! So stride is only 3*width (24bit) if the width of the image is a multiple of 4 pixels! Because the handling of these fillbytes is a little bit complex in calculation the position of the pixel in the bitmap, some functions (i.e. imagesearch.dll) where manipulating images (or find pixel) allow only a width of the image which is a multiple of four pixel. width=77 pixel (24bit), width*3 = 231 the next multiple of 4 is 232, so stride = 232 , number of "fillbytes" at the end of each line is 1. compare of 2 files, create a 3rd file with differences, gdi+ only used for JPG-files. With BMP-files, gdi+ is not neededexpandcollapse popup#include <GDIPlus.au3> #include <GDIPlusConstants.au3> #include <StructureConstants.au3> #include <WinAPI.au3> ; quick and dirty.... Global $stride, $ptr, $pixeldata global $bitmapfile1, $width1, $height1, $stride1, $pbitmap1 global $bitmapfile2, $width2, $height2, $stride2, $pbitmap2 local $numberdiff = 0 ;number of differences between the 2 pics $pic1 = "earth1.jpg" ;the image....24bit per pixel, could be a JPG $pic2 = "earth2.jpg" $pic3 = "difference.bmp" ;a bitmapfile with the differences _GDIPlus_Startup() $pixelstruct1 = getbmpdata($pic1, $width1, $height1, $stride1, $pbitmap1) ;get all data from the 2 files $pixelstruct2 = getbmpdata($pic2, $width2, $height2, $stride2, $pbitmap2) $lenght = DllStructGetSize($pixelstruct1) ;get lenght of bitmapdata $pixelstruct3 = dllstructcreate("ubyte["&$lenght&"]") ;compare for $i=1 to $lenght ; consolewrite (dllstructgetdata($pixelstruct1,1,$i)&" "&dllstructgetdata($pixelstruct1,1,$i)&@crlf) $diff=dllstructgetdata($pixelstruct1,1,$i)-dllstructgetdata($pixelstruct2,1,$i) ;difference"colour" between the bytes if $diff<>0 Then ;bytes are different $numberdiff+=1 ;count dllstructsetdata($pixelstruct3,1,$diff,$i) ;write the difference into the difference-file, bottom-up isnt funny but we´ll mirror it later :o) endif next $head=DllStructcreate("ubyte[54]") ;lets create a bitmapfile with the given data $Bmpheader = DllStructCreate("align 1;char BM[2];uint Size;uint res;uint Offset;uint BMHI;uint Width;uint Height;short Planes;short BPP;uint BIcomp;int SizeImg;uint reshor;uint resver;uint col;uint colused", dllstructgetptr($head)) ;struct of bytes = header of bitmap #1 ;fill struct(bitmapheader) with data DllStructSetData($bmpheader,"BM","BM") DllStructSetData($bmpheader,"Size",54+$lenght) DllStructSetData($bmpheader,"Offset",54) DllStructSetData($bmpheader,"BMHI",40) DllStructSetData($bmpheader,"Width",$width1) DllStructSetData($bmpheader,"Height",-$height1) ;negativ because "bottom up" DllStructSetData($bmpheader,"Planes",1) DllStructSetData($bmpheader,"BPP",24) DllStructSetData($bmpheader,"BIcomp",0) DllStructSetData($bmpheader,"SizeImg",$lenght) $header = dllstructgetdata($head,1) ;headerdata ConsoleWrite('@@ Debug(' & @ScriptLineNumber & ') : $header = ' & $header & @crlf & '>Error code: ' & @error & @crlf) ;### Debug Console $pixeldata = stringtrimleft(DllStructGetData($pixelstruct3, 1),2) ;pixeldata $bitmap = $header&$pixeldata ;all together is the bitmap-file $filehandle = FileOpen($pic3, 18) ;write file FileWrite($filehandle,BinaryToString($bitmap)) FileClose($filehandle) _GDIPlus_ImageDispose($pBitmap1) ;not in the function, because ImageDispose destroys the struct _GDIPlus_ImageDispose($pBitmap2) ;lets have a look what we have done... GUICreate("Difference pixel",800,500) GUICtrlCreatePic($pic1, 10, 10, 170, 170 / $width1 * $height1) GUICtrlCreatePic($pic2, 200, 10, 170, 170 / $width2* $height2) GUICtrlCreatePic($pic3, 400, 10, 170, 170 / $width2* $height2) GUISetState() Do Until GUIGetMsg() = -3 Func getbmpdata($bmpfile, ByRef $width, ByRef $height, ByRef $stride, Byref $pbitmap) ;returns a struct with the data of the pixel $pbitmap = _GDIPlus_BitmapCreateFromFile($bmpfile) ;_GDIPlus_BitmapLockBits gibt $tagGDIPBITMAPDATA-Struktur zurück $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. $pixeldata = DllStructCreate("ubyte[" & (Abs($stride) * ($height)) & "]", $Scan0) $BMPData = DllStructGetData($pixeldata, 1) _WinAPI_DeleteObject($pbitmap) _GDIPlus_BitmapUnlockBits($pBitmap, $BmpData) ;_GDIPlus_ImageDispose($pBitmap) ;destroys the pixeldatastruct, have to be done at end of the script! return $pixeldata EndFunc ;==>getbmpdata Edited December 15, 2009 by AndyG Link to comment Share on other sites More sharing options...
junkew Posted December 14, 2009 Author Share Posted December 14, 2009 thx. the construct on working directly with the struct definitely helped in improving the speed by 50 times compared to the string and binarystring stuff. Looking at the last sample I see more differences than expected but assume this has to do with bmp and jpg comparison. FAQ 31 How to click some elements, FAQ 40 Test automation with AutoIt, Multithreading CLR .NET Powershell CMDLets Link to comment Share on other sites More sharing options...
AndyG Posted December 15, 2009 Share Posted December 15, 2009 (edited) this has to do with bmp and jpg comparison.oh yes, this is true . I had a lot of trouble when I found out that most of the programs are not able to save a "uncompressed" JPG. Make a 2 colour per pixel (only black&white pixels) Image and save it in Bitmap/JPG-format. Then look with a hex-editor at the data and try to understand why there are some pixel with a colour of 1 and 254, although there are likely to be only 0 and 255.For a detailed explanation of the bitmap-format, here is an example./edit/ change alignment in the struct in the previous posted script Edited December 15, 2009 by AndyG Link to comment Share on other sites More sharing options...
Zedna Posted December 15, 2009 Share Posted December 15, 2009 compare of 2 files, create a 3rd file with differences, gdi+ only used for JPG-files. With BMP-files, gdi+ is not needed[autoit]#include <GDIPlus.au3> VERY nice example AndyG!!! You should make a new topic with this code in Examples forum. Thanks for sharing. Resources UDF ResourcesEx UDF AutoIt Forum Search Link to comment Share on other sites More sharing options...
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