JohannesBuchner / imagehash

A Python Perceptual Image Hashing Module
BSD 2-Clause "Simplified" License
3.28k stars 331 forks source link

OverflowError: cannot convert float infinity to integer #205

Open dkbarn opened 10 months ago

dkbarn commented 10 months ago

The attached image throws an error when run through the crop_resistant_hash function using the whash hashing algorithm. input

Steps to reproduce:

from PIL import Image
import imagehash

image = Image.open("input.jpg")
imagehash.crop_resistant_hash(image, imagehash.whash)

Traceback:

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/share/venv/lib/python3.10/site-packages/imagehash/__init__.py", line 695, in crop_resistant_hash
    hashes.append(hash_func(bounding_box))
  File "/share/venv/lib/python3.10/site-packages/imagehash/__init__.py", line 364, in whash
    image_natural_scale = 2**int(numpy.log2(min(image.size)))
OverflowError: cannot convert float infinity to integer

The OverflowError is caused by the fact that on this line of code, image.size is (150, 0). In other words, the image segmentation has produced a bounding box with a 0-pixel dimension. The state of the variables at this point are:

orig_w, orig_h = orig_image.size # (150, 150)
scale_w = float(orig_w) / segmentation_image_size # 0.5
scale_h = float(orig_h) / segmentation_image_size # 0.5
min_y = min(coord[0] for coord in segment) * scale_h # 37.5
min_x = min(coord[1] for coord in segment) * scale_w # 0.0
max_y = (max(coord[0] for coord in segment) + 1) * scale_h # 38.5
max_x = (max(coord[1] for coord in segment) + 1) * scale_w # 150.0
bounding_box = orig_image.crop((min_x, min_y, max_x, max_y)) # <PIL.Image.Image image mode=RGB size=150x0>

So the fact that min_y is 37.5 and max_y is 38.5, when passed into orig_image.crop this seems to generate a 0-pixel height image.

Note that Pillow's Image.crop method is not documented as supporting floating-point x,y coordinates: https://pillow.readthedocs.io/en/stable/reference/Image.html#PIL.Image.Image.crop

And we can see from the source that is calling int(round(x)) on each coordinate: https://github.com/python-pillow/Pillow/blob/main/src/PIL/Image.py#L1234

Bizarrely, both 37.5 and 38.5 are getting rounded to 38 on my system 😳

>>> int(round(37.5))
38
>>> int(round(38.5))
38

In case it matters, I'm on Ubuntu 22.04.3 with Python 3.10.12

Rotzbua commented 9 months ago

Bizarrely, both 37.5 and 38.5 are getting rounded to 38 on my system 😳

Works correct. There are different round rules. Python uses round to even. https://docs.python.org/3/library/functions.html#round

JohannesBuchner commented 9 months ago

probably the line hashes.append(...) should be skipped if the bounding box is empty.