Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

MemoryError with large georef image #7534

Closed
Caio-Giulio-Cesare opened this issue Nov 8, 2023 · 11 comments
Closed

MemoryError with large georef image #7534

Caio-Giulio-Cesare opened this issue Nov 8, 2023 · 11 comments
Labels

Comments

@Caio-Giulio-Cesare
Copy link

Caio-Giulio-Cesare commented Nov 8, 2023

What did you do?

Hi everyone.
I have a large georef image (file .tif 1.6 GB 29898x18312 px. - 72 dpi - 24 bit) and I should be able to create an ImageTk.PhotoImage object for use in a canvas (tk.Canvas) without resizing it

What did you expect to happen?

the object to be used

What actually happened?

the following exception is raised

<class 'MemoryError'>
(<class 'MemoryError'>, MemoryError(), <traceback object at 0x00000233632B8D00>)

What are your OS, Python and Pillow versions?

  • OS: Win 10/11
  • Python: 3.10.4
  • Pillow: 10.0.0
  • No RAM problem (32 GB and very free RAM during the execution)
from PIL import Image, ImageOps, ImageTk, ImageFilter, ImageDraw,ImageGrab
Image.MAX_IMAGE_PIXELS = None
image = Image.open(file_path)
image_for_canvas = ImageTk.PhotoImage(image)

Is there a work-around for solve the error?
What is the maximum usable size?

Thank you for your attention and for the information you provide me.

@radarhere
Copy link
Member

Would you be able to provide the full traceback of the error?

@Caio-Giulio-Cesare
Copy link
Author

here it is the full traceback of the error. (without try/except).

Thanks for the attention

MemoryError

@radarhere
Copy link
Member

I ran

import tkinter
from PIL import Image, ImageTk
im = Image.new("RGB", (29898, 18312))
root = tkinter.Tk()
ImageTk.PhotoImage(im)

and hit

/* overflow check for malloc */
if (im->linesize && im->ysize > INT_MAX / im->linesize) {
return (Imaging)ImagingError_MemoryError();
}

This check was added in #1781. linesize varies depending on the mode, but in my scenario it is the width multiplied by 4. So width * height *4 is greater than INT_MAX.

Because this is part of our C code, and not an arbitrary limit set in Python, there isn't a simple workaround for this, no.

@Caio-Giulio-Cesare
Copy link
Author

Hi @radarhere, thank you so much for the analysis and tests you did.

@radarhere
Copy link
Member

Is that all you wanted, or is there something further to be done here?

@Caio-Giulio-Cesare
Copy link
Author

Unfortunately, if, as understood, there is no solution to the problem, I believe that nothing else can be done.

I'll try to see if I can solve it in another way, using smaller images that are correctly processed (size limit about 1GB max.)

Thank you so much for your help and support, and for the time you have dedicated to the problem.

@Yay295
Copy link
Contributor

Yay295 commented Nov 13, 2023

Pillow does support larger images, but the ImageTk.PhotoImage paste() method deliberately converts the data to a contiguous buffer. Presumably there's a reason for this, but I don't know Tk and wasn't able to follow the code far enough to see what it is. This is the only place in Pillow a contiguous buffer is used though.

Pillow/src/PIL/ImageTk.py

Lines 164 to 182 in 902055f

def paste(self, im):
"""
Paste a PIL image into the photo image. Note that this can
be very slow if the photo image is displayed.
:param im: A PIL image. The size must match the target region. If the
mode does not match, the image is converted to the mode of
the bitmap image.
"""
# convert to blittable
im.load()
image = im.im
if image.isblock() and im.mode == self.__mode:
block = image
else:
block = image.new_block(self.__mode, im.size)
image.convert2(block, image) # convert directly between buffers
_pyimagingtkcall("PyImagingPhoto", self.__photo, block.id)

@radarhere
Copy link
Member

So perhaps a workaround is to simply crop out sections of the image, convert them into multiple PhotoImages, and place them onto the canvas one at a time.

@wiredfool
Copy link
Member

This is super old code -- and it looks like the general idea is that the pointer to the block of memory is passed directly into tk (here:

tk.call(command, photo, id)
).

The only possibility I can see here is if you're actually using a 32 bit build of python. I don't recall if we can actually allocate bigger chunks than 2 gig in the 64 bit builds, but I do think that we avoid doing so in general. This is kind of a special case though, since as noted this is the only place where we explicitly require a single block. That being said, I don't know how TK would react to passing in such a large image.

@radarhere
Copy link
Member

Is there anything further to be done here?

@Caio-Giulio-Cesare
Copy link
Author

Caio-Giulio-Cesare commented Nov 22, 2023

No, thank you very much.
Thank you very much also for the analysis and the time you have dedicated to the problem.

bye...CIAO

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

4 participants