
Oreiller (PIL) peut être utilisé pour concaténer (combiner) plusieurs images verticalement et horizontalement. Créez un arrière-plan avec Image.new() et collez les images avec Image.paste().
Il existe plusieurs façons de concaténer des images de tailles différentes.
Cet article décrit les cas suivants.
- Concaténer des images de même hauteur ou largeur
 - Concaténer des images avec différentes hauteurs ou largeurs
- Couper la zone supplémentaire et concaténer
 - Créer des marges et concaténer
 - Redimensionner et concaténer
 
 - Concaténer plusieurs images à la fois
 - Concaténer la même image à plusieurs reprises
 
Utilisez les deux images suivantes comme exemple.
from PIL import Image
im1 = Image.open('data/src/lena.jpg')
im2 = Image.open('data/src/rocket.jpg')
L’exemple de code est supposé être une image couleur (mode= »RGB »), mais le flux est le même pour les images monochromes (mode= »L »).
Pour plus d’informations sur la concaténation d’images à l’aide d’OpenCV et de scikit-image, consultez les articles suivants. Pour des images de même taille, scikit-image est facile à utiliser. Vous pouvez également ajouter une bordure entre les images.
Concaténer des images de même hauteur ou largeur
Créez un arrière-plan avec Image.new() et collez les images avec Image.paste().
def get_concat_h(im1, im2):
    dst = Image.new('RGB', (im1.width + im2.width, im1.height))
    dst.paste(im1, (0, 0))
    dst.paste(im2, (im1.width, 0))
    return dst
def get_concat_v(im1, im2):
    dst = Image.new('RGB', (im1.width, im1.height + im2.height))
    dst.paste(im1, (0, 0))
    dst.paste(im2, (0, im1.height))
    return dst
get_concat_h(im1, im1).save('data/dst/pillow_concat_h.jpg')
get_concat_v(im1, im1).save('data/dst/pillow_concat_v.jpg')


Couper la zone supplémentaire et concaténer
Dans Image.paste(), la zone hors de la plage de l’image à coller est ignorée (coupée).
Dans le cas d’un alignement horizontal, générez un arrière-plan avec une hauteur plus petite, et dans le cas d’un alignement vertical, générez une largeur plus petite, la zone en excès est coupée et concaténée.
def get_concat_h_cut(im1, im2):
    dst = Image.new('RGB', (im1.width + im2.width, min(im1.height, im2.height)))
    dst.paste(im1, (0, 0))
    dst.paste(im2, (im1.width, 0))
    return dst
def get_concat_v_cut(im1, im2):
    dst = Image.new('RGB', (min(im1.width, im2.width), im1.height + im2.height))
    dst.paste(im1, (0, 0))
    dst.paste(im2, (0, im1.height))
    return dst
get_concat_h_cut(im1, im2).save('data/dst/pillow_concat_h_cut.jpg')
get_concat_v_cut(im1, im2).save('data/dst/pillow_concat_v_cut.jpg')


Dans l’exemple ci-dessus, le bord supérieur ou gauche de chaque image est aligné. Les centres peuvent également être alignés.
def get_concat_h_cut_center(im1, im2):
    dst = Image.new('RGB', (im1.width + im2.width, min(im1.height, im2.height)))
    dst.paste(im1, (0, 0))
    dst.paste(im2, (im1.width, (im1.height - im2.height) // 2))
    return dst
def get_concat_v_cut_center(im1, im2):
    dst = Image.new('RGB', (min(im1.width, im2.width), im1.height + im2.height))
    dst.paste(im1, (0, 0))
    dst.paste(im2, ((im1.width - im2.width) // 2, im1.height))
    return dst
get_concat_h_cut_center(im1, im2).save('data/dst/pillow_concat_h_cut_center.jpg')
get_concat_v_cut_center(im1, im2).save('data/dst/pillow_concat_v_cut_center.jpg')


Créer des marges et concaténer
Créez des marges et concaténez pour éviter que l’image d’origine ne soit coupée.
Créez d’abord un grand arrière-plan. Vous pouvez faire la marge de n’importe quelle couleur en spécifiant le paramètre color de Image.new().
def get_concat_h_blank(im1, im2, color=(0, 0, 0)):
    dst = Image.new('RGB', (im1.width + im2.width, max(im1.height, im2.height)), color)
    dst.paste(im1, (0, 0))
    dst.paste(im2, (im1.width, 0))
    return dst
def get_concat_v_blank(im1, im2, color=(0, 0, 0)):
    dst = Image.new('RGB', (max(im1.width, im2.width), im1.height + im2.height), color)
    dst.paste(im1, (0, 0))
    dst.paste(im2, (0, im1.height))
    return dst
get_concat_h_blank(im1, im2).save('data/dst/pillow_concat_h_blank.jpg')
get_concat_v_blank(im1, im2, (0, 64, 128)).save('data/dst/pillow_concat_v_blank.jpg')


Redimensionner et concaténer
Redimensionnez l’image pour qu’elle corresponde à la largeur ou à la hauteur et reliez-les.
Dans l’exemple, il est possible de choisir de redimensionner une grande image en petit ou de redimensionner une petite image en grand. Fondamentalement, il est préférable de réduire la taille d’une grande image à une taille plus petite car la qualité de l’image diminue lorsqu’elle est agrandie.
En outre, le filtre de rééchantillonnage peut être spécifié.
def get_concat_h_resize(im1, im2, resample=Image.BICUBIC, resize_big_image=True):
    if im1.height == im2.height:
        _im1 = im1
        _im2 = im2
    elif (((im1.height > im2.height) and resize_big_image) or
          ((im1.height  im2.height) and not resize_big_image)):
        _im1 = im1.resize((int(im1.width * im2.height / im1.height), im2.height), resample=resample)
        _im2 = im2
    else:
        _im1 = im1
        _im2 = im2.resize((int(im2.width * im1.height / im2.height), im1.height), resample=resample)
    dst = Image.new('RGB', (_im1.width + _im2.width, _im1.height))
    dst.paste(_im1, (0, 0))
    dst.paste(_im2, (_im1.width, 0))
    return dst
def get_concat_v_resize(im1, im2, resample=Image.BICUBIC, resize_big_image=True):
    if im1.width == im2.width:
        _im1 = im1
        _im2 = im2
    elif (((im1.width > im2.width) and resize_big_image) or
          ((im1.width  im2.width) and not resize_big_image)):
        _im1 = im1.resize((im2.width, int(im1.height * im2.width / im1.width)), resample=resample)
        _im2 = im2
    else:
        _im1 = im1
        _im2 = im2.resize((im1.width, int(im2.height * im1.width / im2.width)), resample=resample)
    dst = Image.new('RGB', (_im1.width, _im1.height + _im2.height))
    dst.paste(_im1, (0, 0))
    dst.paste(_im2, (0, _im1.height))
    return dst
get_concat_h_resize(im1, im2).save('data/dst/pillow_concat_h_resize.jpg')
get_concat_v_resize(im1, im2, resize_big_image=False).save('data/dst/pillow_concat_v_resize.jpg')


Concaténer plusieurs images à la fois
Vous pouvez obtenir une image concaténée dans laquelle plusieurs images sont disposées en ligne verticalement et horizontalement à l’aide de la fonction définie ci-dessus.
Par exemple, passez une liste de PIL.Image et concaténez-les verticalement et horizontalement.
def get_concat_h_multi_blank(im_list):
    _im = im_list.pop(0)
    for im in im_list:
        _im = get_concat_h_blank(_im, im)
    return _im
get_concat_h_multi_blank([im1, im2, im1]).save('data/dst/pillow_concat_h_multi_blank.jpg')

Dans l’exemple ci-dessus, nous utilisons la fonction pour créer des marges. Si vous souhaitez concaténer avec d’autres méthodes, utilisez la fonction correspondante, le cas échéant.
Lors du redimensionnement et de l’organisation de plusieurs images, il est préférable de redimensionner d’abord toutes les images à la largeur ou à la hauteur finale pour éviter un redimensionnement répété et une détérioration de la qualité de l’image.
Ici, créez d’abord un grand arrière-plan avec Image.new().
def get_concat_h_multi_resize(im_list, resample=Image.BICUBIC):
    min_height = min(im.height for im in im_list)
    im_list_resize = [im.resize((int(im.width * min_height / im.height), min_height),resample=resample)
                      for im in im_list]
    total_width = sum(im.width for im in im_list_resize)
    dst = Image.new('RGB', (total_width, min_height))
    pos_x = 0
    for im in im_list_resize:
        dst.paste(im, (pos_x, 0))
        pos_x += im.width
    return dst
def get_concat_v_multi_resize(im_list, resample=Image.BICUBIC):
    min_width = min(im.width for im in im_list)
    im_list_resize = [im.resize((min_width, int(im.height * min_width / im.width)),resample=resample)
                      for im in im_list]
    total_height = sum(im.height for im in im_list_resize)
    dst = Image.new('RGB', (min_width, total_height))
    pos_y = 0
    for im in im_list_resize:
        dst.paste(im, (0, pos_y))
        pos_y += im.height
    return dst
get_concat_h_multi_resize([im1, im2, im1]).save('data/dst/pillow_concat_h_multi_resize.jpg')
get_concat_v_multi_resize([im1, im2, im1]).save('data/dst/pillow_concat_v_multi_resize.jpg')


La fonction d’arrangement dans une tuile est également facile. Passez une liste 2D de PIL.Image et disposez-les en mosaïques verticalement et horizontalement.
def get_concat_tile_resize(im_list_2d, resample=Image.BICUBIC):
    im_list_v = [get_concat_h_multi_resize(im_list_h, resample=resample) for im_list_h in im_list_2d]
    return get_concat_v_multi_resize(im_list_v, resample=resample)
get_concat_tile_resize([[im1],
                        [im1, im2],
                        [im1, im2, im1]]).save('data/dst/pillow_concat_tile_resize.jpg')

Les compréhensions de liste sont utilisées pour créer une liste d’images redimensionnée et pour calculer la largeur et la hauteur minimales, la largeur et la hauteur totales.
Concaténer la même image à plusieurs reprises
Si vous souhaitez combiner la même image à plusieurs reprises verticalement et horizontalement, il est difficile de le spécifier dans la liste 2D, il est donc pratique de préparer une fonction pour spécifier le nombre de répétitions.
def get_concat_h_repeat(im, column):
    dst = Image.new('RGB', (im.width * column, im.height))
    for x in range(column):
        dst.paste(im, (x * im.width, 0))
    return dst
def get_concat_v_repeat(im, row):
    dst = Image.new('RGB', (im.width, im.height * row))
    for y in range(row):
        dst.paste(im, (0, y * im.height))
    return dst
def get_concat_tile_repeat(im, row, column):
    dst_h = get_concat_h_repeat(im, column)
    return get_concat_v_repeat(dst_h, row)
im_s = im1.resize((im1.width // 2, im1.height // 2))
get_concat_tile_repeat(im_s, 3, 4).save('data/dst/pillow_concat_tile_repeat.jpg')

