Skip to content

Commit fc8bfe6

Browse files
committed
Add Hamming Distance functions
1 parent 4501332 commit fc8bfe6

File tree

3 files changed

+134
-13
lines changed

3 files changed

+134
-13
lines changed

README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ package main
1616
import "github.com/kavu/go-phash"
1717

1818
func main() {
19-
hash, _ := phash.ImageHash("~/my_cat.jpg")
19+
hash, _ := phash.ImageHashDCT("~/my_cat.jpg")
2020
println(hash)
2121
}
2222
```

phash.go

+26-9
Original file line numberDiff line numberDiff line change
@@ -12,26 +12,43 @@ package phash
1212
1313
#include <stdlib.h>
1414
15-
#if defined( _MSC_VER) || defined(_BORLANDC_)
16-
typedef unsigned _uint64 ulong64;
17-
typedef signed _int64 long64;
18-
#else
19-
typedef unsigned long long ulong64;
20-
typedef signed long long long64;
21-
#endif
15+
typedef unsigned long long ulong64;
2216
2317
extern ulong64 pc_dct_imagehash_Wrapper(const char *file);
18+
extern int ph_hamming_distance(ulong64 hasha, ulong64 hashb);
2419
*/
2520
import "C"
2621

2722
import "unsafe"
2823

29-
// ImageHash gets a pHash for image with a given path.
30-
func ImageHash(file string) (uint64, error) {
24+
// ImageHash returns a DCT pHash for image with a given path.
25+
func ImageHashDCT(file string) (uint64, error) {
3126
cs := C.CString(file)
3227

3328
h, err := C.pc_dct_imagehash_Wrapper(cs)
3429
C.free(unsafe.Pointer(cs))
3530

3631
return uint64(h), err
3732
}
33+
34+
// HammingDistanceForHashes returns a Hamming Distance between two images' DCT pHashes.
35+
func HammingDistanceForHashes(hasha uint64, hashb uint64) (int, error) {
36+
d, err := C.ph_hamming_distance(C.ulong64(hasha), C.ulong64(hashb))
37+
38+
return int(d), err
39+
}
40+
41+
// HammingDistanceForFiles returns a Hamming Distance between two images with a given paths.
42+
func HammingDistanceForFiles(filea string, fileb string) (interface{}, error) {
43+
hasha, err := ImageHashDCT(filea)
44+
if err != nil {
45+
return nil, err
46+
}
47+
48+
hashb, err := ImageHashDCT(fileb)
49+
if err != nil {
50+
return nil, err
51+
}
52+
53+
return HammingDistanceForHashes(hasha, hashb)
54+
}

phash_test.go

+107-3
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,11 @@ var hashTestData = []struct {
1818
// {"test_data/gif/animated_zebras.gif", 7086864532766789979},
1919
}
2020

21+
// ImageHash
22+
2123
func BenchmarkImageHash(b *testing.B) {
2224
for i := 0; i < b.N; i++ {
23-
_, err := ImageHash(hashTestData[0].file)
25+
_, err := ImageHashDCT(hashTestData[0].file)
2426
if err != nil {
2527
b.Fatal(err)
2628
}
@@ -29,7 +31,7 @@ func BenchmarkImageHash(b *testing.B) {
2931

3032
func TestImageHash(t *testing.T) {
3133
for _, test := range hashTestData {
32-
hash, err := ImageHash(test.file)
34+
hash, err := ImageHashDCT(test.file)
3335
if err != nil {
3436
t.Fatal(err)
3537
}
@@ -41,10 +43,112 @@ func TestImageHash(t *testing.T) {
4143
}
4244

4345
func ExampleImageHash() {
44-
hash, err := ImageHash("test_data/jpg/cat.jpg")
46+
hash, err := ImageHashDCT("test_data/jpg/cat.jpg")
4547
if err != nil {
4648
panic(err)
4749
}
4850
fmt.Println(hash)
4951
// Output: 11220389026139797626
5052
}
53+
54+
// HammingDistanceForHashes
55+
56+
func BenchmarkHammingDistanceForHashesForSameImage(b *testing.B) {
57+
for i := 0; i < b.N; i++ {
58+
_, err := HammingDistanceForHashes(7086864532766789979, 7086864532766789979)
59+
if err != nil {
60+
b.Fatal(err)
61+
}
62+
}
63+
}
64+
65+
func BenchmarkHammingDistanceForHashesForDifferentImages(b *testing.B) {
66+
for i := 0; i < b.N; i++ {
67+
_, err := HammingDistanceForHashes(7086864532766789979, 11220389026139797626)
68+
if err != nil {
69+
b.Fatal(err)
70+
}
71+
}
72+
}
73+
74+
func TestHammingDistanceForHashesForSameImage(t *testing.T) {
75+
d, err := HammingDistanceForHashes(7086864532766789979, 7086864532766789979)
76+
if err != nil {
77+
t.Fatal(err)
78+
}
79+
80+
if d != 0 {
81+
t.Errorf("Distance Mismatch: expected 0, got %d", d)
82+
}
83+
}
84+
85+
func TestHammingDistanceForHashesForDifferentImages(t *testing.T) {
86+
d, err := HammingDistanceForHashes(11220389026139797626, 4855808264951085874)
87+
if err != nil {
88+
t.Fatal(err)
89+
}
90+
91+
if d == 0 {
92+
t.Error("Distance Mismatch: got unexpected 0")
93+
}
94+
}
95+
96+
func ExampleHammingDistanceForHashes() {
97+
d, err := HammingDistanceForHashes(11220389026139797626, 4855808264951085874)
98+
if err != nil {
99+
panic(err)
100+
}
101+
fmt.Println(d)
102+
// Output: 30
103+
}
104+
105+
// HammingDistanceForFiles
106+
107+
func BenchmarkHammingDistanceForFilesForSameImage(b *testing.B) {
108+
for i := 0; i < b.N; i++ {
109+
_, err := HammingDistanceForFiles("test_data/jpg/cat.jpg", "test_data/jpg/cat.jpg")
110+
if err != nil {
111+
b.Fatal(err)
112+
}
113+
}
114+
}
115+
116+
func BenchmarkHammingDistanceForFilesForDifferentImages(b *testing.B) {
117+
for i := 0; i < b.N; i++ {
118+
_, err := HammingDistanceForFiles("test_data/jpg/cat.jpg", "test_data/png/gopher.png")
119+
if err != nil {
120+
b.Fatal(err)
121+
}
122+
}
123+
}
124+
125+
func TestHammingDistanceForFilesForSameImage(t *testing.T) {
126+
d, err := HammingDistanceForFiles("test_data/jpg/cat.jpg", "test_data/jpg/cat.jpg")
127+
if err != nil {
128+
t.Fatal(err)
129+
}
130+
131+
if d != 0 {
132+
t.Errorf("Distance Mismatch: expected 0, got %d", d)
133+
}
134+
}
135+
136+
func TestHammingDistanceForFilesForDifferentImages(t *testing.T) {
137+
d, err := HammingDistanceForFiles("test_data/jpg/cat.jpg", "test_data/png/gopher.png")
138+
if err != nil {
139+
t.Fatal(err)
140+
}
141+
142+
if d == 0 {
143+
t.Error("Distance Mismatch: got unexpected 0")
144+
}
145+
}
146+
147+
func ExampleHammingDistanceForFiles() {
148+
d, err := HammingDistanceForFiles("test_data/jpg/cat.jpg", "test_data/png/gopher.png")
149+
if err != nil {
150+
panic(err)
151+
}
152+
fmt.Println(d)
153+
// Output: 30
154+
}

0 commit comments

Comments
 (0)