Changing hash size in ImageHash Python library

3.9k Views Asked by At

I'm using ImageHash library to generate the perceptual hash of an image. The library claims to be able to generate hashes of different size (64, 128, 256), but I can't figure how to get a 128 hash.

The hash size is determined by the image size when the library rescales it, for example:

def average_hash(image, hash_size=8):
    image = image.convert("L").resize((hash_size, hash_size), Image.ANTIALIAS)

here the default value is 8 (8x8 image = 64 pixels -> grayscale -> 64 bits).

However, how is a 128 bits hash created?

Second thing, the default size of a pHash is 32, as explained here, but later will be calculated only the DCT of the top-left 8x8 section, so again 64 bit. The DCT is calculated through scipy.fftpack:

def phash(image, hash_size=32):
    image = image.convert("L").resize((hash_size, hash_size), Image.ANTIALIAS)
    pixels = numpy.array(image.getdata(), dtype=numpy.float).reshape((hash_size, hash_size))
    dct = scipy.fftpack.dct(pixels)
    dctlowfreq = dct[:8, 1:9]
    avg = dctlowfreq.mean()
    diff = dctlowfreq > avg
    return ImageHash(diff)

How can the hash size be changed?
Whichever value is used, the calculation will always be based on the top left 8x8, so will always be 64!

Strange thing that happens is that if I start with an 8 size pHash (resizing the image from the beginning), I got a final hash of 56 bit (namely, the calculation of the hash of a 7x8 image: I don't understand why this happens in the DCT computation - but I really know a little about it.

1

There are 1 best solutions below

0
On

It looks like that was a bug in the library that has since been fixed. The current implementation of phash looks like this:

def phash(image, hash_size=8, highfreq_factor=4):
    """
    Perceptual Hash computation.
    Implementation follows http://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
    img_size = hash_size * highfreq_factor
    image = image.convert("L").resize((img_size, img_size), Image.ANTIALIAS)
    pixels = numpy.asarray(image)
    dct = scipy.fftpack.dct(scipy.fftpack.dct(pixels, axis=0), axis=1)
    dctlowfreq = dct[:hash_size, :hash_size]
    med = numpy.median(dctlowfreq)
    diff = dctlowfreq > med
    return ImageHash(diff)

You'll notice this correctly uses the hash_size rather than hardcoded values.