Sign in to follow this  
Followers 0
junkew

Bitblt picture compare count difference

13 posts in this topic

#1 ·  Posted (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

#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

post-31530-12607491647515_thumb.jpg

post-31530-12607491738634_thumb.jpg

Edited by junkew

Share this post


Link to post
Share on other sites



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!"

Share this post


Link to post
Share on other sites

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.

Share this post


Link to post
Share on other sites

#5 ·  Posted (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.au3

5297 differences for me. But if you compare two images by using Photoshop then the differences would be much greater.

Posted Image

Edited by Yashied

Share this post


Link to post
Share on other sites

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_GetPixel

In 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 GDI

If 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 be

determine 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 width

loop to determine the exact difference.

Share this post


Link to post
Share on other sites

#7 ·  Posted (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 by Zedna

Share this post


Link to post
Share on other sites

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.

Share this post


Link to post
Share on other sites

#10 ·  Posted (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 needed

#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 by AndyG

Share this post


Link to post
Share on other sites

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.

Share this post


Link to post
Share on other sites

#12 ·  Posted (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 by AndyG

Share this post


Link to post
Share on other sites

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.

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