1. dhash
In Reference 1, an algorithm called DHash which can be used to calculate the hash value of images in similar image search is presented. Using DHash to compute image hash can give consideration to both computational efficiency and accuracy. The good stuff is already available in open source, see Reference 2.
2. Dhash lib
Refer to lib 2 for its readme and get the following usage in Python:
import dhash
from PIL import Image
def get_dhash(img_path) :
image = Image.open(img_path)
row, col = dhash.dhash_row_col(image, size=16)
hh = dhash.format_hex(row, col)
return hh
h1 = get_dhash('xxx.png')
print(h1) # hash value
print(len(h1)*4) # binary bits count, 512
print(len(h1)) # hex bits count, 128
Copy the code
Set different size, the resulting DHash value of the number of bits is different. If size=16, the dhash is 512 bits (binary 0/1).
3. Existing problems
In the above code, the output dhash number may not always be 512 bits for different images.
Some images yielded 496 bits, some 508 bits, some 512 bits.
This is troublesome, if the number is not fixed, then the back to do similarity search, is also unable to achieve.
How to solve this problem?
The intuitive idea is zero-padding, which precedes each hash value with a fixed number of 512 digits. But is it ok?
4. Source code analysis
To understand this, you need to look at the design of the Dhash lib to see if direct zeroing is possible.
- Gets the column and column hash value
- Source dhash_row_col() function
- Source location: github.com/benhoyt/dha…
The key logic in the source code is annotated below
def dhash_row_col(image, size=8) :
width = size + 1
# Change color image to grayscale image
# change the image size to (size+1)x(size+1)
grays = get_grays(image, width, width)
Calculate the row hash and column hash separately
row_hash = 0
col_hash = 0
for y in range(size):
for x in range(size):
offset = y * width + x
# grays[offset] is the current pixel value during iteration (gray value)
If the current point is smaller than the next point, the current bit=1; otherwise, the current bit=0
row_bit = grays[offset] < grays[offset + 1]
# row_hash moves 1 bit left to add the currently calculated bit
row_hash = row_hash << 1 | row_bit
If the current point is smaller than the pixel value of the offset + width point, the current bit=1, otherwise the bit=0
col_bit = grays[offset] < grays[offset + width]
# col_hash moves 1 bit left to add the currently calculated bit
col_hash = col_hash << 1 | col_bit
return (row_hash, col_hash)
Copy the code
From the DHash source, we can see that the size=16 we entered will cause the image to be converted to 17×17 grayscale.
Instead of getting a hash directly, you get a row hash and a column hash.
So each of these hashes has size x size bits, and the row hashes and the column hashes that we’ve got here are 256 bits of binary.
These two hashes need to be transformed to get the final DHash value.
- Convert row and column hashes to DHash values
- Source format_matrix() function
- Source location: github.com/benhoyt/dha…
Here is the key logic in the source code
def format_hex(row_hash, col_hash, size=8) :
hex_length = size * size // 4
Convert the row hash to hex_length, and concatenate the column hashes together
return '{0:0{2}x}{1:0{2}x}'.format(row_hash, col_hash, hex_length)
Copy the code
Size =16
def format_hex(row_hash, col_hash) :
hex_length = 16 * 16 // 4 # 64
return '{0:0{2}x}{1:0{2}x}'.format(row_hash, col_hash, 64)
Copy the code
As you can see, the end result is to convert the row hash and the column hash into 64-bit hexadecimal data and concatenate them together.
So, the final dhash value is 64+64=128 hexadecimal digits, which is exactly 512 binary digits.
5. How to solve the problem of unfixed digits
From source code analysis, we know that in order to get fixed-digit DHash values, size must be specified in the two key functions mentioned above.
To solve this problem, change the code given in 2 to the following.
import dhash
from PIL import Image
def get_dhash(img_path) :
image = Image.open(img_path)
row, col = dhash.dhash_row_col(image, size=16)
hh = dhash.format_hex(row, col, size=16)# plus the size parameter
return hh
h1 = get_dhash('xxx.png')
print(h1) # hash value
print(len(h1)*4) # binary bits count, 512
print(len(h1)) # hex bits count, 128
Copy the code
6. Summary
This article introduces the key code logic for dhASH computing open source projects in Reference 2, and presents a scheme for obtaining fixed-digit hashes when obtaining DHash values using this project.
Reference 7.
-
www.hackerfactor.com/blog/?/arch…
-
Github.com/benhoyt/dha…