See also http://www.purebasic.fr/english/viewtop ... 35#p436035
The bigger the Hamming distance between two hashes, the more likely it's a different image.
I tried to implement pHash ( http://www.phash.org ) but am not entirely sure if it is correct.
Hash1.q = ImageHash::pHash(Image1)
Hash2.q = ImageHash::pHash(Image2)
Debug ImageHash::HammingDistance(hash1, hash2)
Code: Select all
DeclareModule ImageHash; v0.1.2
Declare.i HammingDistance(hash1.q, hash2.q)
Declare.q pHash(imagefile.s, useMedian = #True)
Declare.q dHash(imagefile.s)
Declare.q dHash3(imagefile.s, edge = 0)
EndDeclareModule
Module ImageHash
UseJPEGImageDecoder()
UsePNGImageDecoder()
; *** Hamming distance ***
Procedure.i HammingDistance(hash1.q, hash2.q)
!mov ecx, [p.v_hash1]
!xor ecx, [p.v_hash2]
!mov edx, ecx
!shr edx, 1
!and edx, 0x55555555
!sub ecx, edx
!mov edx, ecx
!shr edx, 2
!and edx, 0x33333333
!and ecx, 0x33333333
!add ecx, edx
!mov edx, ecx
!shr edx, 4
!add ecx, edx
!and ecx, 0x0f0f0f0f
!imul eax, ecx, 0x01010101
!shr eax, 24
!mov ecx, [p.v_hash1 + 4]
!xor ecx, [p.v_hash2 + 4]
!mov edx, ecx
!shr edx, 1
!and edx, 0x55555555
!sub ecx, edx
!mov edx, ecx
!shr edx, 2
!and edx, 0x33333333
!and ecx, 0x33333333
!add ecx, edx
!mov edx, ecx
!shr edx, 4
!add ecx, edx
!and ecx, 0x0f0f0f0f
!imul ecx, 0x01010101
!shr ecx, 24
!add eax, ecx
ProcedureReturn
EndProcedure
; *** pHash ***
Structure y32 : y.d[32] : EndStructure
Structure m32 : x.y32[32] : EndStructure
Global mutex = CreateMutex()
Global *m0.m32 = AllocateMemory(24832, #PB_Memory_NoClear) & -256 + 256
Global *m1.m32 = *m0 + 8192, *m2.m32 = *m0 + 16384
Procedure GenerateDCTmatrix()
Protected.i x, y, c.d = 1/Sqr(32)
For x = 0 To 31 : *m0\x[x]\y[0] = c : Next
For y = 1 To 31 : For x = 0 To 31
*m0\x[x]\y[y] = 0.25*Cos(#PI*0.015625*y*(2*x+1))
Next : Next
EndProcedure
GenerateDCTMatrix()
Procedure.q pHash(imagefile.s, useMedian = #True)
Protected.i img, c, x, y, i, sum.d, m.d, one.q, hash.q
img = LoadImage(#PB_Any, imagefile)
If img And ResizeImage(img, 32, 32)
LockMutex(mutex)
; fill image matrix
StartDrawing(ImageOutput(img))
For y = 0 To 31
For x = 0 To 31
c = Point(x, y)
*m1\x[x]\y[y] = 0.299*Red(c) + 0.587*Green(c) + 0.114*Blue(c)
Next
Next
StopDrawing()
FreeImage(img)
; multiply matrices and calculate median/mean value
For y = 1 To 8
For x = 0 To 31
sum = 0
For i = 0 To 31
sum + *m0\x[i]\y[y] * *m1\x[x]\y[i]
Next
*m2\x[x]\y[y] = sum
Next
Next
m = 0
For y = 1 To 8
For x = 1 To 8
sum = 0
For i = 0 To 31
sum + *m2\x[i]\y[y] * *m0\x[i]\y[x]
Next
*m1\x[x]\y[y] = sum
m + sum
Next
Next
If useMedian
m = 0.5 * (*m1\x[8]\y[4] + *m1\x[1]\y[5])
Else
m * 0.015625
EndIf
; build hash
one = 1
For y = 1 To 8
For x = 1 To 8
If *m1\x[x]\y[y] > m : hash | one : EndIf
one << 1
Next
Next
UnlockMutex(mutex)
EndIf
ProcedureReturn hash
EndProcedure
; *** dHash ***
Procedure.q dHash(imagefile.s)
Protected.i img, x, y, c, l0, l1, one.q, hash.q
img = LoadImage(#PB_Any, imagefile)
If img And ResizeImage(img, 9, 8)
StartDrawing(ImageOutput(img))
one = 1
For y = 0 To 7
c = Point(0, y)
l0 = 0.299*Red(c) + 0.587*Green(c) + 0.114*Blue(c)
For x = 1 To 8
c = Point(x, y)
l1 = 0.299*Red(c) + 0.587*Green(c) + 0.114*Blue(c)
If l0 < l1 : hash | one : EndIf
l0 = l1
one << 1
Next
Next
StopDrawing()
FreeImage(img)
EndIf
ProcedureReturn hash
EndProcedure
; *** dHash3 ***
Global Dim sample_x(63)
Global Dim sample_y(63)
Procedure dHash3Init()
Protected.i x, y, i
For y = 0 To 7
For x = 0 To 7
i = (x & 4) >> 2 + (x & 2) << 1 + (x & 1) << 4 + (x!y & 4) >> 1 + (x!y & 2) << 2 + (x!y & 1) << 5
sample_x(i) = x
sample_y(i) = y
Next
Next
EndProcedure
dHash3Init()
Procedure.q dHash3(imagefile.s, edge = 0)
Protected.i img, b, c, r0, g0, b0, r1, g1, b1, hash.q
img = LoadImage(#PB_Any, imagefile)
If img And ResizeImage(img, 8 + edge << 1, 8 + edge << 1)
StartDrawing(ImageOutput(img))
g0 = Point(sample_x(31) + edge, sample_y(31) + edge) & $ff00
b0 = Point(sample_x(47) + edge, sample_y(47) + edge) & $ff0000
r0 = Point(sample_x(63) + edge, sample_y(63) + edge) & $ff
For b = 0 To 63
c = Point(sample_x(b) + edge, sample_y(b) + edge)
If b < 32
g1 = c & $ff00
If g0 < g1 : hash | 1 << b : EndIf
g0 = g1
ElseIf b < 48
b1 = c & $ff0000
If b0 < b1 : hash | 1 << b : EndIf
b0 = b1
Else
r1 = c & $ff
If r0 < r1 : hash | 1 << b : EndIf
r0 = r1
EndIf
Next
StopDrawing()
FreeImage(img)
EndIf
ProcedureReturn hash
EndProcedure
EndModule