0
# Core Classes
1
2
Hash container classes that provide comparison operations, string conversion, and mathematical operations for computing similarity between images. These classes encapsulate hash data and provide the interface for all hash operations.
3
4
## Capabilities
5
6
### ImageHash Class
7
8
Primary hash container for single perceptual hashes with full comparison and serialization support.
9
10
```python { .api }
11
class ImageHash:
12
def __init__(self, binary_array):
13
"""
14
Initialize ImageHash with binary array.
15
16
Args:
17
binary_array (NDArray): Boolean numpy array representing the hash
18
"""
19
20
def __str__(self):
21
"""
22
Convert hash to hexadecimal string representation.
23
24
Returns:
25
str: Hexadecimal string of the hash
26
"""
27
28
def __repr__(self):
29
"""
30
Return string representation of the binary array.
31
32
Returns:
33
str: String representation of hash array
34
"""
35
36
def __sub__(self, other):
37
"""
38
Calculate Hamming distance between two hashes.
39
40
Args:
41
other (ImageHash): Other hash to compare against
42
43
Returns:
44
int: Hamming distance (number of differing bits)
45
46
Raises:
47
TypeError: If other is None or hashes have different shapes
48
"""
49
50
def __eq__(self, other):
51
"""
52
Check if two hashes are equal.
53
54
Args:
55
other (object): Other hash to compare
56
57
Returns:
58
bool: True if hashes are identical
59
"""
60
61
def __ne__(self, other):
62
"""
63
Check if two hashes are not equal.
64
65
Args:
66
other (object): Other hash to compare
67
68
Returns:
69
bool: True if hashes are different
70
"""
71
72
def __hash__(self):
73
"""
74
Return 8-bit integer hash for dictionary keys.
75
76
Returns:
77
int: 8-bit hash value for use as dictionary key
78
"""
79
80
def __len__(self):
81
"""
82
Return bit length of the hash.
83
84
Returns:
85
int: Total number of bits in the hash
86
"""
87
```
88
89
**Usage Example:**
90
91
```python
92
from PIL import Image
93
import imagehash
94
95
# Create hashes
96
image1 = Image.open('photo1.jpg')
97
image2 = Image.open('photo2.jpg')
98
99
hash1 = imagehash.average_hash(image1)
100
hash2 = imagehash.average_hash(image2)
101
102
# Basic operations
103
print(f"Hash 1: {hash1}") # String representation
104
print(f"Hash length: {len(hash1)}") # Bit length (64 for 8x8 hash)
105
106
# Comparison operations
107
distance = hash1 - hash2 # Hamming distance
108
are_equal = hash1 == hash2 # Exact equality
109
are_different = hash1 != hash2 # Inequality
110
111
print(f"Hamming distance: {distance}")
112
print(f"Are equal: {are_equal}")
113
114
# Use as dictionary key
115
hash_dict = {hash1: 'photo1.jpg', hash2: 'photo2.jpg'}
116
filename = hash_dict.get(hash1) # Retrieve using hash as key
117
118
# Hash comparison with tolerance
119
similarity_threshold = 5
120
are_similar = distance < similarity_threshold
121
```
122
123
### ImageMultiHash Class
124
125
Container for multiple hashes used in crop-resistant hashing, with advanced matching capabilities.
126
127
```python { .api }
128
class ImageMultiHash:
129
def __init__(self, hashes):
130
"""
131
Initialize with list of ImageHash objects.
132
133
Args:
134
hashes (list[ImageHash]): List of individual segment hashes
135
"""
136
137
def __eq__(self, other):
138
"""
139
Check equality using matches method.
140
141
Args:
142
other (object): Other multi-hash to compare
143
144
Returns:
145
bool: True if hashes match
146
"""
147
148
def __ne__(self, other):
149
"""
150
Check inequality.
151
152
Args:
153
other (object): Other multi-hash to compare
154
155
Returns:
156
bool: True if hashes don't match
157
"""
158
159
def __sub__(self, other, hamming_cutoff=None, bit_error_rate=None):
160
"""
161
Calculate distance score between multi-hashes.
162
163
Args:
164
other (ImageMultiHash): Other multi-hash to compare
165
hamming_cutoff (float, optional): Maximum hamming distance threshold
166
bit_error_rate (float, optional): Percentage of bits that can differ (default: 0.25)
167
168
Returns:
169
float: Distance score (lower = more similar)
170
"""
171
172
def __hash__(self):
173
"""
174
Return hash of tuple of segment hashes.
175
176
Returns:
177
int: Hash value for dictionary usage
178
"""
179
180
def __str__(self):
181
"""
182
Return comma-separated string of segment hashes.
183
184
Returns:
185
str: Comma-separated hex strings
186
"""
187
188
def __repr__(self):
189
"""
190
Return representation of segment hashes list.
191
192
Returns:
193
str: String representation of hash list
194
"""
195
196
def hash_diff(self, other_hash, hamming_cutoff=None, bit_error_rate=None):
197
"""
198
Get difference metrics between two multi-hashes.
199
200
Args:
201
other_hash (ImageMultiHash): Other multi-hash to compare
202
hamming_cutoff (float, optional): Maximum hamming distance threshold
203
bit_error_rate (float, optional): Bit error rate (default: 0.25)
204
205
Returns:
206
tuple[int, int]: (number_of_matching_segments, sum_of_hamming_distances)
207
"""
208
209
def matches(self, other_hash, region_cutoff=1, hamming_cutoff=None, bit_error_rate=None):
210
"""
211
Check if multi-hash matches another with configurable thresholds.
212
213
Args:
214
other_hash (ImageMultiHash): Other multi-hash to compare
215
region_cutoff (int): Minimum matching regions required (default: 1)
216
hamming_cutoff (float, optional): Maximum hamming distance per region
217
bit_error_rate (float, optional): Bit error rate tolerance (default: 0.25)
218
219
Returns:
220
bool: True if hashes match according to criteria
221
"""
222
223
def best_match(self, other_hashes, hamming_cutoff=None, bit_error_rate=None):
224
"""
225
Find best matching hash from a list of candidates.
226
227
Args:
228
other_hashes (list[ImageMultiHash]): List of candidate hashes
229
hamming_cutoff (float, optional): Maximum hamming distance threshold
230
bit_error_rate (float, optional): Bit error rate tolerance (default: 0.25)
231
232
Returns:
233
ImageMultiHash: Best matching hash from the list
234
"""
235
```
236
237
**Usage Example:**
238
239
```python
240
from PIL import Image
241
import imagehash
242
243
# Create crop-resistant hashes
244
full_image = Image.open('full_photo.jpg')
245
cropped_image = Image.open('cropped_photo.jpg')
246
247
full_hash = imagehash.crop_resistant_hash(full_image)
248
crop_hash = imagehash.crop_resistant_hash(cropped_image)
249
250
# Basic matching
251
matches = full_hash.matches(crop_hash)
252
print(f"Images match: {matches}")
253
254
# Flexible matching with custom thresholds
255
strict_match = full_hash.matches(
256
crop_hash,
257
region_cutoff=2, # Require at least 2 matching regions
258
bit_error_rate=0.15 # Allow 15% bit differences
259
)
260
261
# Get detailed comparison metrics
262
num_matches, total_distance = full_hash.hash_diff(crop_hash)
263
print(f"Matching segments: {num_matches}")
264
print(f"Total distance: {total_distance}")
265
266
# Distance scoring
267
similarity_score = full_hash - crop_hash
268
print(f"Similarity score: {similarity_score}")
269
270
# Find best match from multiple candidates
271
candidates = [
272
imagehash.crop_resistant_hash(Image.open('candidate1.jpg')),
273
imagehash.crop_resistant_hash(Image.open('candidate2.jpg')),
274
imagehash.crop_resistant_hash(Image.open('candidate3.jpg'))
275
]
276
277
best_match = full_hash.best_match(candidates)
278
print(f"Best match: {best_match}")
279
280
# Use as dictionary key
281
multi_hash_dict = {full_hash: 'full_photo.jpg'}
282
```
283
284
## Advanced Usage Patterns
285
286
### Batch Hash Comparison
287
288
```python
289
# Compare one hash against many
290
target_hash = imagehash.average_hash(target_image)
291
image_hashes = [
292
imagehash.average_hash(img) for img in image_list
293
]
294
295
# Find all similar images
296
similar_images = []
297
for i, img_hash in enumerate(image_hashes):
298
distance = target_hash - img_hash
299
if distance < 10: # similarity threshold
300
similar_images.append((i, distance))
301
302
# Sort by similarity
303
similar_images.sort(key=lambda x: x[1])
304
```
305
306
### Hash Tolerance Configuration
307
308
```python
309
# Crop-resistant matching with different tolerance levels
310
strict_tolerance = multi_hash1.matches(
311
multi_hash2,
312
region_cutoff=3, # Need 3+ matching regions
313
bit_error_rate=0.10 # Only 10% bit differences allowed
314
)
315
316
loose_tolerance = multi_hash1.matches(
317
multi_hash2,
318
region_cutoff=1, # Only 1 matching region needed
319
bit_error_rate=0.35 # Allow 35% bit differences
320
)
321
```
322
323
### Custom Hash Functions with Classes
324
325
```python
326
# Create custom hash class wrapper
327
class CustomImageHash(imagehash.ImageHash):
328
def __init__(self, binary_array, metadata=None):
329
super().__init__(binary_array)
330
self.metadata = metadata or {}
331
332
def similarity_percentage(self, other):
333
distance = self - other
334
max_distance = len(self)
335
return (1 - distance / max_distance) * 100
336
337
# Usage
338
custom_hash = CustomImageHash(
339
imagehash.average_hash(image).hash,
340
metadata={'filename': 'image.jpg', 'algorithm': 'average'}
341
)
342
```
343
344
## Error Handling
345
346
```python
347
try:
348
# Hash comparison with error handling
349
distance = hash1 - hash2
350
except TypeError as e:
351
if "must not be None" in str(e):
352
print("One of the hashes is None")
353
elif "same shape" in str(e):
354
print("Hashes have different sizes/shapes")
355
else:
356
raise
357
358
# Safe hash comparison
359
def safe_compare_hashes(hash1, hash2):
360
if hash1 is None or hash2 is None:
361
return None
362
363
try:
364
return hash1 - hash2
365
except TypeError:
366
return None # Incompatible hash types/sizes
367
```