#!/usr/bin/env -S uv run --script # /// script # requires-python = ">=3.12" # dependencies = [ # "imagehash" # ] # /// from PIL import Image import imagehash import numpy def net_phash(image): # type: (Image.Image) -> imagehash.ImageHash """ Modified phash implementation to match the output of https://github.com/coenm/ImageHash -- Perceptual Hash computation. Implementation follows https://www.hackerfactor.com/blog/index.php?/archives/432-Looks-Like-It.html @image must be a PIL instance. """ if hash_size < 2: raise ValueError('Hash size must be greater than or equal to 2') import scipy.fftpack hash_size = 8 img_size = 64 image = image.convert('L').resize((img_size, img_size), Image.Resampling.BICUBIC) pixels = numpy.asarray(image, dtype=numpy.float64) rows_dct = scipy.fftpack.dct(pixels, axis=1, norm='ortho') cols_dct_full = scipy.fftpack.dct(rows_dct[:, :hash_size], axis=0, norm='ortho') dctlowfreq = cols_dct_full[:hash_size, :].T med = numpy.median(dctlowfreq) diff = dctlowfreq > med return imagehash.ImageHash(diff) def ulong_to_hash(value: int): """ Converts a 64-bit unsigned long integer into an ImageHash object. The integer is transformed into an 8x8 binary numpy array, which is the standard representation for many perceptual hashes. :param value: The 64-bit integer hash value. Must be between 0 and 2**64 - 1. :return: An ImageHash object representing the integer. """ if not (0 <= value < 2**64): raise ValueError("Value must be a 64-bit unsigned integer (between 0 and 2**64 - 1).") # 1. Convert the integer to its 64-bit binary string representation. # numpy.binary_repr is perfect for this, as it handles padding. # Example: 1 -> '000...001' binary_string = numpy.binary_repr(value, width=64) # 2. Convert the binary string into a numpy array of booleans. # '1' becomes True, '0' becomes False. binary_array = numpy.array([char == '1' for char in binary_string], dtype=bool) # 3. Reshape the 64-element flat array into an 8x8 matrix. This is a # common convention for image hashes (e.g., ahash, phash). hash_matrix = binary_array.reshape((8, 8)) # 4. Create and return the ImageHash instance. return imagehash.ImageHash(hash_matrix) hash = net_phash(Image.open('peppers.png')) hash2 = ulong_to_hash(15500626565295817037) print(hash - hash2)