Jump to content

How to unload image from script


Recommended Posts

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

Link to comment
Share on other sites

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

Link to comment
Share on other sites

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

Link to comment
Share on other sites

@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))

 

Link to comment
Share on other sites

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

Link to comment
Share on other sites

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

 

Link to comment
Share on other sites

http://pinvoke.net/default.aspx/msvcrt/memcmp.html?diff=y

Shows really it is a count not a range, so it should be like I said.  IDK why UEZ scripted like that.  I am using a lot of memcpy in my scripts and it's working also with a count.  

Edited by Nine
Link to comment
Share on other sites

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!
¯\_(ツ)_/¯  ٩(●̮̮̃•̃)۶ ٩(-̮̮̃-̃)۶ૐ

Link to comment
Share on other sites

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

Link to comment
Share on other sites

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 ;)

Link to comment
Share on other sites

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

Link to comment
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
 Share

  • Recently Browsing   0 members

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