-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathdhash.go
138 lines (113 loc) · 4.64 KB
/
dhash.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
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
/*
Implements the dhash algorithm from http://archive.is/NFLVW
dhash is an image hashing algorithm that generates a unique
signature from an image's gradients.
The image is first grayscaled to reduce every RGB pixel set to the same value.
Then, it is resized down to 'hashLen' size, with one of the sides 1px
larger than the other (the width for horizontalGradient(), and the
height for verticalGradient()).
Finally, the gradient difference is calculated. If the current pixel is
less than the next one, a '1' is appended to the BitArray. Otherwise,
a '0' is appended.
TODO Phash? Every new package gets a branch until testing is done
TODO Benchmarks for every algorithm
*/
package imagehash
import (
"github.com/disintegration/imaging"
"image"
)
// Dhash calculates the horizontal and vertical gradient hashes separately, then
// concatenates then to return one result as: <horizontal><vertical>.
// 'img' is an Image object returned by opening an image file using OpenImg().
// 'hashLen' is the size that the image will be shrunk to. It must be a non-zero multiple of 8.
func Dhash(img image.Image, hashLen int) ([]byte, error) {
imgGray := imaging.Grayscale(img) // Grayscale image first for performance
// Calculate both horizontal and vertical gradients
horiz, err1 := horizontalGradient(imgGray, hashLen)
vert, err2 := verticalGradient(imgGray, hashLen)
if err1 != nil {
return nil, err1
}
if err2 != nil {
return nil, err2
}
// Return the concatenated horizontal and vertical hash
return append(horiz, vert...), nil
}
// DhashHorizontal returns the result of a horizontal gradient hash.
// 'img' is an Image object returned by opening an image file using OpenImg().
// 'hashLen' is the size that the image will be shrunk to. It must be a non-zero multiple of 8.
func DhashHorizontal(img image.Image, hashLen int) ([]byte, error) {
imgGray := imaging.Grayscale(img) // Grayscale image first
return horizontalGradient(imgGray, hashLen) // horizontal diff gradient
}
// DhashVertical returns the result of a vertical gradient hash.
// 'img' is an Image object returned by opening an image file using OpenImg().
// 'hashLen' is the size that the image will be shrunk to. It must be a non-zero multiple of 8.
func DhashVertical(img image.Image, hashLen int) ([]byte, error) {
imgGray := imaging.Grayscale(img) // Grayscale image first
return verticalGradient(imgGray, hashLen) // vertical diff gradient
}
// horizontalGradient performs a horizontal gradient diff on a grayscaled image
func horizontalGradient(img image.Image, hashLen int) ([]byte, error) {
// Width and height of the scaled-down image
width, height := hashLen+1, hashLen
// Downscale the image by 'hashLen' amount for a horizonal diff.
res := imaging.Resize(img, width, height, imaging.Lanczos)
// Create a new bitArray
bitArray, err := NewBitArray(hashLen * hashLen)
if err != nil {
return nil, err
}
var prev uint32 // Variable to store the previous pixel value
// Calculate the horizonal gradient difference
for y := 0; y < height; y++ {
for x := 0; x < width; x++ {
// Since the image is grayscaled, r = g = b
r, _, _, _ := res.At(x, y).RGBA() // Get the pixel at (x,y)
// If this is not the first value of the current row, then
// compare the gradient difference from the previous one
if x > 0 {
if prev < r {
bitArray.AppendBit(1) // if it's smaller, append '1'
} else {
bitArray.AppendBit(0) // else append '0'
}
}
prev = r // Set this current pixel value as the previous one
}
}
return bitArray.GetArray(), nil
}
// verticalGradient performs a vertical gradient diff on a grayscaled image
func verticalGradient(img image.Image, hashLen int) ([]byte, error) {
// Width and height of the scaled-down image
width, height := hashLen, hashLen+1
// Downscale the image by 'hashLen' amount for a vertical diff.
res := imaging.Resize(img, width, height, imaging.Lanczos)
// Create a new bitArray
bitArray, err := NewBitArray(hashLen * hashLen)
if err != nil {
return nil, err
}
var prev uint32 // Variable to store the previous pixel value
// Calculate the vertical gradient difference
for x := 0; x < width; x++ {
for y := 0; y < height; y++ {
// Since the image is grayscaled, r = g = b
r, _, _, _ := res.At(x, y).RGBA() // Get the pixel at (x,y)
// If this is not the first value of the current column, then
// compare the gradient difference from the previous one
if y > 0 {
if prev < r {
bitArray.AppendBit(1) // if it's smaller, append '1'
} else {
bitArray.AppendBit(0) // else append '0'
}
}
prev = r // Set this current pixel value as the previous one
}
}
return bitArray.GetArray(), nil
}