Search code examples
pythonpython-imaging-libraryimage-stitchingpython-imageio

Combine all tiff images into one single image


I have 15 tiles or tiff files a folder and I would like combine it as a single file with all the images as one tiff image. All the tiles should be stitched as a single tiff image. How do I do that?

What I tried so far?

import imageio
import os

path = "path/to/dir"
image_path_list = os.listdir(path)

with imageio.get_writer("new_image.tif") as new_image:
    for image_path in image_path_list:
        image = imageio.imread(path+image_path)
        new_image.append_data(image)

This saves as a separate image in a tiff file. I would like to stitch all the images together and save it like the following:

Desired Output

1,2,3...,15 represent the tiles. Needs to be stitched as a single image.


Solution

  • given one directory with 15 images of same size

    using PIL (pillow), I ended up with:

    from PIL import Image
    
    
    import os
    
    path_to_file ='tiff-files'
    
    
    images = []
    
    
    
    for i in os.listdir(path_to_file):
        with Image.open(path_to_file+'/'+i) as im:
            images.append(im.copy())
    
        
    new_image = Image.new(images[0].mode, (images[0].size[0]*3,images[0].size[1]*5))
    
    
    
    new_image.paste(images[0])
    new_image.paste(images[1],(images[0].size[0]*1,0))
    new_image.paste(images[2],(images[0].size[0]*2,0))
    new_image.paste(images[3],(0,images[0].size[1]*1))
    new_image.paste(images[4],(images[0].size[0]*1,images[0].size[1]*1))
    new_image.paste(images[5],(images[0].size[0]*2,images[0].size[1]*1))
    new_image.paste(images[6],(0,images[0].size[1]*2))
    new_image.paste(images[7],(images[0].size[0]*1,images[0].size[1]*2))
    new_image.paste(images[8],(images[0].size[0]*2,images[0].size[1]*2))
    new_image.paste(images[9],(0,images[0].size[1]*3))
    new_image.paste(images[10],(images[0].size[0]*1,images[0].size[1]*3))
    new_image.paste(images[11],(images[0].size[0]*2,images[0].size[1]*3))
    new_image.paste(images[12],(0,images[0].size[1]*4))
    new_image.paste(images[13],(images[0].size[0]*1,images[0].size[1]*4))
    new_image.paste(images[14],(images[0].size[0]*2,images[0].size[1]*4))
    
    new_image.show()
    

    let me know if it works.....

    After Mark Setchell suggestion here a new version, hope it is better

    from PIL import Image
    import os
    
    path_to_file ='tiff-files'
    
    
    
    def stich_tile(path_to_file, xx , yy):
        images = []
        for i in os.listdir(path_to_file):
                images.append(i)
    
        
        if len(images) >= xx*yy:
            pass
        
        else:
            raise ValueError('not enough images in path_to_file !!!!!!!!!!!')
            
        
        sq_x = xx
        sq_y = yy
        img_x = (Image.open(path_to_file+'/'+images[0]).size[0])
        img_y = (Image.open(path_to_file+'/'+images[0]).size[1])
        img_mode = (Image.open(path_to_file+'/'+images[0]).mode)
        
        new_image = Image.new(img_mode, (img_x*sq_x, img_y*sq_y))
        
        x = 0
        y = 0
        cnt = 0
        for i in images:
            with Image.open(path_to_file+'/'+i) as img:
                new_image.paste(img, (x,y))
                cnt += 1
                x += img_x 
                if cnt == sq_x:
                    x = 0
                    y += img_y
                    cnt = 0
                else:
                    pass
                    
      
        return new_image
     
    
    stich_tile(path_to_file, 3, 5).show()
    

    And thinking more along the lines of https://stackoverflow.com/a/68468658/2836621

    import numpy as np
    from PIL import Image
    import os
    
    # path_to_file ='tiff-files'
    
    path_to_file ='tiff-files2'
    
    # path_to_file ='tiff-files3'
    
    
    
        
    
    image = []
    for i in os.listdir(path_to_file):
        with Image.open(path_to_file+'/'+i) as im:
            image.append(im.copy()) 
            
         
    
    
    w, h = image[0].size
    
    
    
    new_image = np.zeros((4 * h, 3 * w)).astype('uint8')
    
    
    col = 0
    row = -1
    for i, img in enumerate(image):
        if not i % 3 :
            row += 1
            col = 0
        img = np.array(img)
        new_image[row * h: (row + 1) * h, col * w: (col + 1) * w] = img
        col += 1
    
    
    
    
    image_pillow = Image.fromarray(new_image, mode = 'L')
    
    image_pillow.save('prova.tif', mode = 'L')
    
    
    image_pillow.show()
    

    tested with .tif images grayscale 8-bit

    modify adding 3 channel for RGB et similia:

    new_image = np.zeros((3 * h, 3 * w,3)).astype('uint8')

    new_image[row * h: (row + 1) * h,col * w: (col + 1) * w,:] = img

    once more the last example as function for 8 bit grayscale images:

    import numpy as np
    from PIL import Image
    import os
    
    path_to_file ='tiff-files'
    
    # path_to_file ='tiff-files2'
    
    # path_to_file ='tiff-files3'
    
    # path_to_file ='tiff-files5'
    
        
    def stich_img(path_to_file, x , y):
    
        image = []
        for i in os.listdir(path_to_file):
                image.append(path_to_file+'/'+i)
        
        print(image)
             
        if len(image) >= x*y:
            pass
        
        else:
            # raise ValueError('not enough images in path_to_file !!!!!!!!!!!')
            raise ValueError('EXCEPTION not enough images in path_to_file !!!!!!!!!!!', x*y ,'images  needed : ', len(image),'images present !!!')
        
        
        image = image[:x*y] #-----> riduce lista immagini al numero richiesto
        
        
        with Image.open(image[0]) as img0:
            w, h = img0.size
       
        
        
        
        # new_image = np.zeros((4 * h, 3 * w)).astype('uint8')
        new_image = np.zeros((y * h, x * w)).astype('uint8')
        
        
         
        col = 0
        row = -1
        for i, imgs in enumerate(image):
            with Image.open(imgs) as img:
                if not i % x :
                    row += 1
                    col = 0
                img = np.array(img)
                new_image[row * h: (row + 1) * h, col * w: (col + 1) * w] = img
                col += 1
                
        
    
        
        image_pillow = Image.fromarray(new_image, mode = 'L')
        
        return image_pillow
    
    img_stiched = stich_img(path_to_file, 3,5)   
    
    # img_stiched.save('prova.tif', mode = 'L')
    
    
    img_stiched.show()