Jump to content

Recommended Posts

Posted

Hello all,

I have this code found somewhere, it compares 2 images:

Functions:

Func _CompareImages($ciImageOne, $ciImageTwo)
    _GDIPlus_Startup()

    $fname1 = $ciImageOne
    If $fname1 = "" Then Exit

    $fname2 = $ciImageTwo
    If $fname2="" Then Exit

    $bm1 = _GDIPlus_ImageLoadFromFile($fname1)
    $bm2 = _GDIPlus_ImageLoadFromFile($fname2)

    ; MsgBox(0, "bm1==bm2", CompareBitmaps($bm1, $bm2))
    Return CompareBitmaps($bm1, $bm2)

    _GDIPlus_ImageDispose($bm1)
    _GDIPlus_ImageDispose($bm2)
    _GDIPlus_Shutdown()
EndFunc

Func CompareBitmaps($bm1, $bm2)
    $Bm1W        = _GDIPlus_ImageGetWidth($bm1)
    $Bm1H        = _GDIPlus_ImageGetHeight($bm1)
    $BitmapData1 = _GDIPlus_BitmapLockBits($bm1, 0, 0, $Bm1W, $Bm1H, $GDIP_ILMREAD, $GDIP_PXF32RGB)

    $Stride      = DllStructGetData($BitmapData1, "Stride")
    $Scan0       = DllStructGetData($BitmapData1, "Scan0")

    $ptr1        = $Scan0
    $size1       = ($Bm1H - 1) * $Stride + ($Bm1W - 1) * 4

    $Bm2W        = _GDIPlus_ImageGetWidth($bm2)
    $Bm2H        = _GDIPlus_ImageGetHeight($bm2)
    $BitmapData2 = _GDIPlus_BitmapLockBits($bm2, 0, 0, $Bm2W, $Bm2H, $GDIP_ILMREAD, $GDIP_PXF32RGB)

    $Stride      = DllStructGetData($BitmapData2, "Stride")
    $Scan0       = DllStructGetData($BitmapData2, "Scan0")

    $ptr2        = $Scan0
    $size2       = ($Bm2H - 1) * $Stride + ($Bm2W - 1) * 4
    $smallest    = $size1

    If $size2 < $smallest Then $smallest = $size2

    $call = DllCall("msvcrt.dll", "int:cdecl", "memcmp", "ptr", $ptr1, "ptr", $ptr2, "int", $smallest)

    _GDIPlus_BitmapUnlockBits($bm1, $BitmapData1)
    _GDIPlus_BitmapUnlockBits($bm2, $BitmapData2)

    Return $call[0]=0
EndFunc

Call the function example:

; Define the two images (They can be different file formats)
$img1 = "Image1.jpg"
$img2 = "Image2.jpg"

; Compare the two images
$duplicateCheck = _CompareImages($img1, $img2)
MsgBox(0,"Is Duplicate?", $duplicateCheck)

 

It works like charm, the only problem is that once images are loaded to compare, they cannot be deleted even if you don't need to call the function no more, the only way to delete them is exiting the script. So my question is, is there any way to unload the images from the compare function so the same script can delete them after compare?

 

Thank you in advance

Posted

As far as I can see, the function _CompareImages calls Return before a dispose has taken place.

Try :

Func _CompareImages($ciImageOne, $ciImageTwo)
    Local $bReturn
    _GDIPlus_Startup()

    $fname1 = $ciImageOne
    If $fname1 = "" Then Exit

    $fname2 = $ciImageTwo
    If $fname2="" Then Exit

    $bm1 = _GDIPlus_ImageLoadFromFile($fname1)
    $bm2 = _GDIPlus_ImageLoadFromFile($fname2)

    ; MsgBox(0, "bm1==bm2", CompareBitmaps($bm1, $bm2))
    $bReturn = CompareBitmaps($bm1, $bm2)

    _GDIPlus_ImageDispose($bm1)
    _GDIPlus_ImageDispose($bm2)
    _GDIPlus_Shutdown()

    Return $bReturn
EndFunc

 

Musashi-C64.png

"In the beginning the Universe was created. This has made a lot of people very angry and been widely regarded as a bad move."

Posted (edited)
On 3/2/2020 at 2:29 PM, Nine said:

Why do you (Zavier) call "memcmp" if the files are not equal in size ? 

Good point ;).

@Zavier : Here a variant (Original from @UEZ - shortened by me), which checks before the actual comparison with "memcmp", if height and width match.

#include <GDIPlus.au3>

Global $g_hImg1, $g_hImg2, $g_bResult

_GDIPlus_Startup()
$g_hImg1   = _GDIPlus_ImageLoadFromFile(@ScriptDir & "\Pic1.jpg")
$g_hImg2   = _GDIPlus_ImageLoadFromFile(@ScriptDir & "\Pic2.jpg") ; or Pic3.jpg (different size)
$g_bResult = _GDIPlus_ImageCompare($g_hImg1, $g_hImg2)
ConsoleWrite("+ Result=" & $g_bResult & "   Error=" & @error & "   Time (Sec.) =" & @extended & @CRLF)

_GDIPlus_ImageDispose($g_hImg1)
_GDIPlus_ImageDispose($g_hImg2)
_GDIPlus_Shutdown()

Func _GDIPlus_ImageCompare($hImage1, $hImage2)
    Local Const $iW = _GDIPlus_ImageGetWidth($hImage1), $iH = _GDIPlus_ImageGetHeight($hImage1)
    If ($iW <> _GDIPlus_ImageGetWidth($hImage2)) Then Return SetError(1, 0, False)
    If ($iH <> _GDIPlus_ImageGetHeight($hImage2)) Then Return SetError(2, 0, False)

    Local $t = TimerInit() ; <== for comparison time in seconds (not mandatory)
    Local $tBitmapData1 = _GDIPlus_BitmapLockBits($hImage1, 0, 0, $iW, $iH, $GDIP_ILMREAD, $GDIP_PXF32ARGB)
    Local $tBitmapData2 = _GDIPlus_BitmapLockBits($hImage2, 0, 0, $iW, $iH, $GDIP_ILMREAD, $GDIP_PXF32ARGB)

    Local $iScan1 = DllStructGetData($tBitmapData1, "Scan0")
    Local $tPixel1 = DllStructCreate("int[" & $iW * $iH & "];", $iScan1)
    Local $iStride = Abs(DllStructGetData($tBitmapData1, "Stride"))

    Local $iScan2 = DllStructGetData($tBitmapData2, "Scan0")
    Local $tPixel2 = DllStructCreate("int[" & $iW * $iH & "];", $iScan2)

    $iResult = DllCall("msvcrt.dll", "int:cdecl", "memcmp", "ptr", $iScan1, "ptr", $iScan2, "int", DllStructGetSize($tPixel1))[0]

    _GDIPlus_BitmapUnlockBits($hImage1, $tBitmapData1)
    _GDIPlus_BitmapUnlockBits($hImage2, $tBitmapData2)

    Return SetError(0, Int(TimerDiff($t)), $iResult = 0)
EndFunc

Test images : Pic1 and Pic2 -> same size, but different content, Pic3->different size

(Error 1 = Different width, Error 2 = Different height)

EDITED

Pic1.jpg

Pic2.jpg

Pic3.jpg

Edited by Musashi
with correct DllCall

Musashi-C64.png

"In the beginning the Universe was created. This has made a lot of people very angry and been widely regarded as a bad move."

Posted

@Musashi  Why are you leaving out the last four bytes of the structure ?  It should be :

DllCall("msvcrt.dll", "int:cdecl", "memcmp", "ptr", $iScan1, "ptr", $iScan2, "int", $iH * $iW * 4)

; or another way 

DllCall("msvcrt.dll", "int:cdecl", "memcmp", "ptr", $iScan1, "ptr", $iScan2, "int", DllStructGetSize($tPixel1))

 

Posted
14 minutes ago, Nine said:

Why are you leaving out the last four bytes of the structure ? 

To be honest with you, I simply took this line from the function of UEZ. My tests showed plausible results, so I didn't pay closer attention to it (that was probably a bit sloppy) ;). You know more about this topic anyway, so you might want to take a look for yourself.

https://www.autoitscript.com/forum/topic/182010-problems-comparing-two-in-memory-bitmaps-using-memcmp/?do=findComment&comment=1308390

I have only implemented the case $bFastCmp. If you still think it is necessary afterwards, I will replace the DLL call, of course.

Musashi-C64.png

"In the beginning the Universe was created. This has made a lot of people very angry and been widely regarded as a bad move."

Posted

Thank you for the answers!

The first script works pretty good, now my script is able to delete the images after the function ends.

I did not code it so I really don't know why they used memcmp.

 

Checking the width or height in my case is maybe not needed since all images are the same, just changes the content.

I will try both scripts!

 

Thank you both ! 😀

 

Posted

Nine is right, to compare to full range of bitmap blocks you have to use $iW * $iH * $4 or DllStructGetSize($tPixel1) instead not to miss the last memory blocks in the compare functions.

My formular doesn't cover the full range!

Please don't send me any personal message and ask for support! I will not reply!

Selection of finest graphical examples at Codepen.io

The own fart smells best!
Her 'sikim hıyar' diyene bir avuç tuz alıp koşma!
¯\_(ツ)_/¯  ٩(●̮̮̃•̃)۶ ٩(-̮̮̃-̃)۶ૐ

Posted (edited)
2 hours ago, UEZ said:

Nine is right, to compare to full range of bitmap blocks you have to use $iW * $iH * $4 or DllStructGetSize($tPixel1) instead not to miss the last memory blocks in the compare functions. My formular doesn't cover the full range!

First of all, thanks to @Nine and @UEZ for the clarification. On a closer look it is just logical, that the calculation with (... -1) does not capture the whole bitmap.

20 hours ago, Zavier said:

The first script works pretty good, now my script is able to delete the images after the function ends. [...] Checking the width or height in my case is maybe not needed since all images are the same, just changes the content. I will try both scripts!

@Zavier :
Your original script also contains this bug. So you might want to use my modified version, even if your comparison images are always the same size ;).

Edited by Musashi
typo

Musashi-C64.png

"In the beginning the Universe was created. This has made a lot of people very angry and been widely regarded as a bad move."

Posted

I would suggest a couple of things to streamline the code.  You do not need to create the 2 $tPixel structures.  Also $Stride is not required anymore.  So use $iW*$iH*4 for the count.  Save you a few lines of code.  Not that I am finicky, but...well maybe I am ;)

Posted
1 hour ago, Nine said:

I would suggest a couple of things to streamline the code ...

Here is a cleaned up version. I have altered the function name from _GDIPlus_ImageCompare to _ImageCompare_GDIPlus to better distinguish it from the standard _GDIPlus_* functions. I have also removed the time measuring. Who wants to know the duration of the comparison can set TimerInit etc. itself.

I hope this meets your demands :lol:.

; This directive is not mandatory :
#AutoIt3Wrapper_Au3Check_Parameters=-q -d -w 1 -w 2 -w 3 -w 4 -w 6 -w 7

#include <GDIPlus.au3>
Global $g_hImg1, $g_hImg2, $g_bCompareResult, $g_iSizeMismatch

_GDIPlus_Startup()
$g_hImg1          = _GDIPlus_ImageLoadFromFile(@ScriptDir & "\Pic1.jpg")
$g_hImg2          = _GDIPlus_ImageLoadFromFile(@ScriptDir & "\Pic2.jpg")
$g_bCompareResult = _ImageCompare_GDIPlus($g_hImg1, $g_hImg2)
$g_iSizeMismatch  = @error
MsgBox(BitOR(4096,64), "Report", "Compare Images = " & $g_bCompareResult & @CRLF & "Size mismatch  = " & $g_iSizeMismatch)
_GDIPlus_ImageDispose($g_hImg1)
_GDIPlus_ImageDispose($g_hImg2)
_GDIPlus_Shutdown()

Func _ImageCompare_GDIPlus($hImage1, $hImage2)
    Local Const $iWidth = _GDIPlus_ImageGetWidth($hImage1), $iHeight = _GDIPlus_ImageGetHeight($hImage1)
    If ($iWidth  <> _GDIPlus_ImageGetWidth($hImage2)) Then Return SetError(1, 0, False)
    If ($iHeight <> _GDIPlus_ImageGetHeight($hImage2)) Then Return SetError(2, 0, False)
    Local $tBitmapData1 = _GDIPlus_BitmapLockBits($hImage1, 0, 0, $iWidth, $iHeight, $GDIP_ILMREAD, $GDIP_PXF32ARGB)
    Local $tBitmapData2 = _GDIPlus_BitmapLockBits($hImage2, 0, 0, $iWidth, $iHeight, $GDIP_ILMREAD, $GDIP_PXF32ARGB)
    Local $iScan1       = DllStructGetData($tBitmapData1, "Scan0")
    Local $iScan2       = DllStructGetData($tBitmapData2, "Scan0")
    Local $iResult      = DllCall("msvcrt.dll", "int:cdecl", "memcmp", "ptr", $iScan1, "ptr", $iScan2, "int", $iHeight * $iWidth * 4)[0]
    _GDIPlus_BitmapUnlockBits($hImage1, $tBitmapData1)
    _GDIPlus_BitmapUnlockBits($hImage2, $tBitmapData2)
    Return SetError(0, 0, $iResult = 0)
EndFunc    ;==>_ImageCompare_GDIPlus

 

Musashi-C64.png

"In the beginning the Universe was created. This has made a lot of people very angry and been widely regarded as a bad move."

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
  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...