I want to assemble 8 images of 9450x75600 px (i.e. 75600x75600 px)
import sys
import PIL
from PIL import Image
    
PIL.Image.MAX_IMAGE_PIXELS = 9331200000
ListeImage=['test1.tif','test2.tif','test3.tif','test4.tif','test5.tif','test6.tif','test7.tif','test8.tif']
images = [Image.open(x) for x in ListeImage]
widths, heights = zip(*(i.size for i in images))
    
total_width = sum(widths)
max_height = max(heights)
    
new_im = Image.new('RGB', (total_width, max_height))
    
y_offset = 0
for im in images:
      new_im.paste(im, (0,y_offset))
      y_offset += im.size[0]
      new_im.save('TOTAL'+str(y_offset)+'.tif')
but I've got this error...
Traceback (most recent call last): File "C:\Python27\MergeImages.py", line 21, in <module> new_im.save('test'+str(bande[0])+'.tif') (...) File "C:\Python27\lib\site-packages\PIL\TiffImagePlugin.py", line 626, in _pack return struct.pack(self._endian + fmt, *values) error: integer out of range for 'L' format code
I think it is a memory problem. How to solve it?
You are getting an exception because you are exceeding the 4GB Tiff format limit.
See: What is the maximum size of TIFF metadata?.
You may use BigTIFF format.
You may try using Tifffile for writing BigTIFF image files.
I prefer using JPEG 2000 image format.
I found this post regarding saving JPEG 2000 with pillow.
The way I know is saving JPEG 2000 using OpenCV.
OpenCV saves JP2 images in lossless format (same way as Tiff is lossless).
Issues saving with OpenCV:
Here is a modified version of your code that saves JP2 using OpenCV:
import sys
import PIL
from PIL import Image
import cv2
import numpy as np
    
PIL.Image.MAX_IMAGE_PIXELS = 9331200000
ListeImage=['test1.tif','test2.tif','test3.tif','test4.tif','test5.tif','test6.tif','test7.tif','test8.tif']
images = [Image.open(x) for x in ListeImage]
widths, heights = zip(*(i.size for i in images))
    
#total_width = sum(widths)
#max_height = max(heights)
    
#new_im = Image.new('RGB', (total_width, max_height))
new_im = Image.new('RGB', (max(widths), sum(heights)))
    
y_offset = 0
for im in images:
    new_im.paste(im, (0,y_offset))
    y_offset += im.size[1] #im.size[0]
    # new_im.save('TOTAL'+str(y_offset)+'.tif')
    cv2.imwrite('TOTAL'+str(y_offset)+'.jp2', cv2.cvtColor(np.array(new_im), cv2.COLOR_RGB2BGR))
Notes:
The above solution consumes way too much RAM (more than 100GB).
Using a large page file (disk space as virtual memory) works, but it's too slow.
The solution (saving as JPEG 2000) is not a practical for most systems.
The solution below uses BigTIFF format.
The implementation is also more efficient in terms of RAM:
import sys
import PIL
from PIL import Image
import tifffile
import numpy as np
import gc
    
PIL.Image.MAX_IMAGE_PIXELS = 9331200000
ListeImage=['test1.tif','test2.tif','test3.tif','test4.tif','test5.tif','test6.tif','test7.tif','test8.tif']
images = [Image.open(x) for x in ListeImage]
widths, heights = zip(*(i.size for i in images))
# Free memory - release memory of all images.
del images
gc.collect()  # Explicitly invoke the Garbage Collector https://stackoverflow.com/questions/1316767/how-can-i-explicitly-free-memory-in-python
    
#new_im = Image.new('RGB', (max(widths), sum(heights)))
new_im = np.zeros((sum(heights), max(widths), 3), np.uint8)  # Use NumPy array instead of pillow image.
    
y_offset = 0
for x in ListeImage:
    im = Image.open(x)  # Read one input image at a time (for saving RAM).
    #new_im.paste(im, (0,y_offset))
    new_im[y_offset:y_offset+im.size[1], :, :] = np.array(im)  # Copy im to NumPy array (instead of pasting to pillow image - saves RAM).
    y_offset += im.size[1]
    tifffile.imwrite('TOTAL'+str(y_offset)+'.tif', new_im, bigtiff=True)  # Write new_im as BigTIFF.
    del im
    gc.collect()  # Explicitly invoke the Garbage Collector
                        If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With