Created
October 22, 2021 15:52
-
-
Save JoeGlines/15a9761c0200bb032307ebe547a4069c to your computer and use it in GitHub Desktop.
Jackie Sztuk method for image comparison
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
If !pToken := Gdip_Startup() | |
{ | |
MsgBox, 48, gdiplus error!, Gdiplus failed to start. Please ensure you have gdiplus on your system | |
ExitApp | |
} | |
FileSelectFile, File1, , , Select File 1 | |
FileSelectFolder, folder1, | |
;~ FileSelectFile, File2, , , Select File 2 | |
SplitPath, File1,, outDir | |
Gui, 1:+AlwaysOnTop +ToolWindow +LastFound | |
pBitmap1 := Gdip_CreateBitmapFromFile(File1) | |
;~ pResizedBitmap1 := Gdip_ResizeBitmap(pBitmap1, 16, 16) | |
;~ pResizedGrayBitmap1 := GetGrayScaleVersion(pResizedBitmap1) | |
;~ msgbox % (grayScaleValues1 := GetColorValues(pResizedGrayBitmap1))[4,4] ; get color at 4x4 | |
Loop Files, %folder1%\*.jpg | |
{ | |
pBitmap2 := Gdip_CreateBitmapFromFile(A_LoopFileFullPath) | |
PercentageDifference := PercentageDifference(pBitmap1, pBitmap2) | |
BhattacharyyaDifference := BhattacharyyaDifference(GetColorValues(Gdip_ResizeBitmap(GetGrayScaleVersion(pBitmap1))), GetColorValues(Gdip_ResizeBitmap(GetGrayScaleVersion(pBitmap2)))) | |
if ((PercentageDifference * 100 ) < 10) | |
msgbox % A_LoopFileFullPath | |
} | |
;~ pBitmap2 := Gdip_CreateBitmapFromFile(File2) | |
;~ pResizedBitmap2 := Gdip_ResizeBitmap(pBitmap2, 16, 16) | |
;~ pResizedGrayBitmap2 := GetGrayScaleVersion(pResizedBitmap2) | |
;~ msgbox % (grayScaleValues2 := GetColorValues(pResizedGrayBitmap2))[4,4] ; get color at 4x4 | |
;~ Gui, 1: Add, Pic, x0 y0, %File1% | |
;~ Gui, 1: Add, Pic, y0, %File2% | |
;~ Gui, 1: Add, Button, gCompare, compare | |
;~ Gui, 1: Show,, Differences | |
;~ return | |
;~ Compare: | |
;~ Gui, 1: submit | |
;~ PercentageDifference := PercentageDifference(pBitmap1, pBitmap2) | |
;~ BhattacharyyaDifference := BhattacharyyaDifference(GetColorValues(Gdip_ResizeBitmap(GetGrayScaleVersion(pBitmap1))), GetColorValues(Gdip_ResizeBitmap(GetGrayScaleVersion(pBitmap2)))) | |
;~ msgbox % "PercentageDifference: " PercentageDifference "`nBhattacharyyaDifference: " BhattacharyyaDifference | |
;~ Gdip_SaveBitmapToFile(Gdip_ResizeBitmap(GetGrayScaleVersion(pBitmap1)), outDir "\resizedimage.jpg", 100) | |
;~ return | |
;~ return | |
3GuiClose: | |
2GuiClose: | |
GuiClose: | |
Gdip_Shutdown(pToken) | |
ExitApp | |
#Include <Gdip.ahk> ;Be sure to download this file and put it in your library | |
; ========================================================================================================= | |
; returns = how different two images are as a percentage value based on a threshold | |
PercentageDifference(pBitmap1, pBitmap2, threshold = 3) | |
{ | |
pResizedBitmap1 := Gdip_ResizeBitmap(pBitmap1, 16, 16) | |
pResizedGrayBitmap1 := GetGrayScaleVersion(pResizedBitmap1) | |
pResizedBitmap2 := Gdip_ResizeBitmap(pBitmap2, 16, 16) | |
pResizedGrayBitmap2 := GetGrayScaleVersion(pResizedBitmap2) | |
differences := GetDifferences(GetColorValues(pResizedGrayBitmap1), GetColorValues(pResizedGrayBitmap2)) | |
num = 0 | |
numArray := differences | |
Loop % numArray.length() | |
{ | |
index1 := A_Index | |
Loop % numArray.length() | |
{ | |
index2 := A_Index | |
;~ msgbox % numArray[index2, index1] | |
if (numArray[index2, index1] > threshold) | |
num++ | |
} | |
} | |
return (num / 256) | |
} | |
; returns = array of integer Differences of every pixel in the two images' | |
GetDifferences(firstArray, secondArray) | |
{ | |
numArray := {} | |
Loop % firstArray.length() | |
{ | |
index1 := A_Index | |
Loop % firstArray.length() | |
{ | |
index2 := A_Index | |
numArray[index2, index1] := Abs(firstArray[index2, index1] - secondArray[index2, index1]) | |
} | |
} | |
return numArray | |
} | |
; returns = the amount of red color of each pixel in an array | |
GetColorValues(pBitmap) | |
{ | |
Gdip_GetImageDimensions(pBitmap, origW, origH) | |
numArray := {} | |
Loop % origW | |
{ | |
x := A_Index | |
Loop % origH | |
{ | |
y := A_Index | |
;~ msgbox % Abs(GetColor(pBitmap, x, y).R) | |
numArray[x, y] := Abs(GetColor(pBitmap, x, y).R) | |
} | |
} | |
return numArray | |
} | |
; get a pixel color from coords | |
GetColor(pBitmap, xpos, ypos) | |
{ | |
ret := GDIP_GetPixel(pBitmap, xpos, ypos) | |
ret := ARGBtoRGB(ret) | |
return new CColor(ret) | |
} | |
; Converts RGB with Alpha Channel to RGB | |
ARGBtoRGB( ARGB ){ | |
SetFormat, IntegerFast, hex | |
ARGB := ARGB & 0x00ffffff | |
ARGB .= "" ; Necessary due to the "fast" mode. | |
SetFormat, IntegerFast, d | |
return ARGB | |
} | |
; color class - provides r/g/b values via Dynamic Properties | |
Class CColor { | |
__New(RGB){ | |
this._RGB := RGB | |
} | |
; Implement RGB and R, G, B as Dynamic Properties | |
__Get(aName := ""){ | |
if (aName = "RGB"){ | |
; Return RGB in Hexadecimal (eg 0xFF00AA) format | |
SetFormat, IntegerFast, hex | |
ret := this._RGB | |
ret += 0 | |
ret .= "" | |
SetFormat, IntegerFast, d | |
return ret | |
} else if (aName = "R"){ | |
; Return red in Decimal format | |
return (this._RGB >> 16) & 255 | |
} else if (aName = "G"){ | |
return (this._RGB >> 8) & 255 | |
} else if (aName = "B"){ | |
return this._RGB & 255 | |
} | |
} | |
; Compares this pixel to a provided color, with a tolerance | |
Compare(c2, tol := 20){ | |
return PixelCompare(this, c2, tol) | |
} | |
} | |
; Compares two r/g/b integer objects, with a tolerance | |
; returns true or false | |
; Note! simply compares two rgb Hex colors. | |
PixelCompare(c1, c2, tol := 20) { | |
return (PixelDiff(c1,c2) <= tol) | |
} | |
; Returns the Difference between two r/g/b integer colors objects | |
PixelDiff(c1,c2){ | |
diff := Abs( c1.r - c2.r ) "," Abs( c1.g - c2.g ) "," Abs( c1.b - c2.b ) | |
sort diff,N D, | |
StringSplit, diff, diff, `, | |
return diff%diff0% | |
} | |
; Returns = the gray scale version of an image using a color Matrix | |
GetGrayScaleVersion(pBitmap) | |
{ | |
Gdip_GetImageDimensions(pBitmap, origW, origH) | |
pBitmap2 := Gdip_CreateBitmap(origW, origH) | |
pGraphics := Gdip_GraphicsFromImage(pBitmap2) | |
MatrixGreyScale = 0.299|0.299|0.299|0|0|0.587|0.587|0.587|0|0|0.114|0.114|0.114|0|0|0|0|0|1|0|0|0|0|0|1 | |
Gdip_DrawImage(pGraphics, pBitmap, 0, 0, origW, origH, 0, 0, origW, origH, MatrixGreyScale) | |
return pBitmap2 | |
} | |
; Bhattacharyya histogram algorithm, comparing two images based on their normalized histograms | |
; This tells something about the differences in the brightness of the images as a whole, not so much about where they differ. | |
; param1 = The first image to compare | |
; param2 = The second image to compare | |
; returns = The difference between the images' normalized histograms | |
BhattacharyyaDifference(img1GrayscaleValuesArray, img2GrayscaleValuesArray) | |
{ | |
numArray1 := {} | |
numArray2 := {} | |
num1 := 0.0 | |
num2 := 0.0 | |
numArray3 := img1GrayscaleValuesArray | |
Loop % numArray3.length() | |
{ | |
index1 := A_Index | |
Loop % numArray3.length() | |
{ | |
index2 := A_Index | |
num3 := numArray3[index1, index2] | |
num1 += num3 | |
} | |
} | |
numArray4 := img2GrayscaleValuesArray | |
Loop % numArray4.length() | |
{ | |
index1 := A_Index | |
Loop % numArray4.length() | |
{ | |
index2 := A_Index | |
num3 := numArray4[index1, index2] | |
num2 += num3 | |
} | |
} | |
Loop % img1GrayscaleValuesArray.length() | |
{ | |
index1 := A_Index | |
Loop % img1GrayscaleValuesArray.length() | |
{ | |
index2 := A_Index | |
numArray1[index1, index2] := img1GrayscaleValuesArray[index1, index2] / num1 | |
} | |
} | |
Loop % img2GrayscaleValuesArray.length() | |
{ | |
index1 := A_Index | |
Loop % img2GrayscaleValuesArray.length() | |
{ | |
index2 := A_Index | |
numArray2[index1, index2] := img2GrayscaleValuesArray[index1, index2] / num2 | |
} | |
} | |
num4 := 0 | |
Loop % img2GrayscaleValuesArray.length() | |
{ | |
index1 := A_Index | |
Loop % img2GrayscaleValuesArray.length() | |
{ | |
index2 := A_Index | |
d := numArray1[index1, index2] * numArray2[index1, index2] | |
num4 += Sqrt(d) | |
} | |
} | |
return Round(Sqrt(Round(1.0 - num4, 8)), 8) | |
} | |
; Examples: | |
; pResizedBitmap := Gdip_ResizeBitmap(pBitmap, 50, 50) ; resizes to 50x50pixels | |
Gdip_ResizeBitmap(pBitmap, newWidth=16, newHeight=16) { ; returns resized bitmap. By Learning one. | |
pBitmap2 := Gdip_CreateBitmap(newWidth, newHeight) | |
graphics := Gdip_GraphicsFromImage(pBitmap2) | |
Gdip_SetSmoothingMode(graphics, 2) | |
Gdip_SetInterpolationMode(graphics, 7) | |
Gdip_SetPixelOffsetMode(graphics, 2) | |
Gdip_DrawImage(graphics, pBitmap, 0, 0, NewWidth, NewHeight) | |
Gdip_DeleteGraphics(graphics) | |
return pBitmap2 | |
} ; http://www.autohotkey.com/community/viewtopic.php?p=477333#p477333 | |
; Default = 0 | |
; HighSpeed = 1 | |
; HighQuality = 2 | |
; ModeNone = 3 | |
; ModeHalf = 4 | |
Gdip_SetPixelOffsetMode(pGraphics, PixelOffsetMode) | |
{ | |
return DllCall("gdiplus\GdipSetPixelOffsetMode", A_PtrSize ? "UPtr" : "UInt", pGraphics, "int", PixelOffsetMode) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment