Search code examples
pythoniopython-imaging-library

Tell() method from io.BytesIO not returning the correct image weight


Whenever I call os.path.getsize(file), it gives me the correct size. When images overshoot a fixed weight, I have to make transformations on it then ask the weight of the new image.

I have a very basic function to return the weight of an image. The reason is that I'd like to avoid going through the file system.

def get_weight(image: Image, img_format: str) -> int:
    out = io.BytesIO()
    image.save(out, format=img_format)
    return out.tell()

From what I understand, it copies the content of the image via a byte stream, then return the position. I'm assuming from its name that the stream buffer size is one byte, thus position == image weight in bytes.

It doesn't work consistently with JPEG format. I have images that gets their weight underestimated by 20% (in the 4 to 8 MB range).

Why is that so ?


Solution

  • The argument quality in Image.save() is set to 75 by default.

    If the image originally has a higher quality than 75, the weight may be underestimated. The opposite is true if the original quality is below 75, its weight may be overestimated.

    From this anwer about JPEG quality in Pillow, it is possible to keep the original quality of the image by setting quality to "keep". Doing that will reproduce the original byte stream of the image, with a more similar length.