-
Notifications
You must be signed in to change notification settings - Fork 1
/
phash.go
90 lines (73 loc) · 2.24 KB
/
phash.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
// This program is free software: you can redistribute it and/or modify it
// under the terms of the GNU General Public License as published by the Free
// Software Foundation, either version 3 of the License, or (at your option)
// any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
// Public License for more details.
//
// You should have received a copy of the GNU General Public License along
// with this program. If not, see <http://www.gnu.org/licenses/>.
// Package phash is a simple wrapper around the pHash library.
package phash
/*
#cgo pkg-config: pHash
#include <stdlib.h>
void cimg_exception_mode_quiet();
typedef unsigned long long ulong64;
ulong64 ph_dct_imagehash_wrapper(const char* file);
int ph_hamming_distance(const ulong64 hash1,const ulong64 hash2);
ulong64* ph_dct_videohash_wrapper(const char *filename, int *l);
*/
import "C"
import (
"reflect"
"unsafe"
)
func init() {
C.cimg_exception_mode_quiet()
}
// ImageHashDCT returns the perceptual hash of the image file fn.
func ImageHashDCT(fn string) (uint64, error) {
cfn := C.CString(fn)
defer C.free(unsafe.Pointer(cfn))
hash, err := C.ph_dct_imagehash_wrapper(cfn)
return uint64(hash), err
}
func VideoHash(fn string) ([]C.ulong64, error) {
cfn := C.CString(fn)
var l int
defer C.free(unsafe.Pointer(cfn))
array, err := C.ph_dct_videohash_wrapper(cfn, (*C.int)(unsafe.Pointer(&l)))
if err != nil {
return nil, err
}
length := l
hdr := reflect.SliceHeader{
Data: uintptr(unsafe.Pointer(array)),
Len: length,
Cap: length,
}
goSlice := *(*[]C.ulong64)(unsafe.Pointer(&hdr))
return goSlice, err
}
// HammingDistance returns the Hamming distance between the two perceptual hashes.
func HammingDistance(h1, h2 uint64) int {
const (
m1 = 0x5555555555555555
m2 = 0x3333333333333333
h01 = 0x0101010101010101
m4 = 0x0f0f0f0f0f0f0f0f
)
x := h1 ^ h2
x -= (x >> 1) & m1
x = (x & m2) + ((x >> 2) & m2)
x = (x + (x >> 4)) & m4
return int((x * h01) >> 56)
}
// for benchmarks
func hammingDistanceC(h1, h2 uint64) int {
return int(C.ph_hamming_distance(C.ulong64(h1), C.ulong64(h2)))
}