使用 python 将多张图片连接成一张图片

Concatenate multiple pieces of an image to a single image using python

我有 15 张图片(从 1 到 15)。我想将这些图像拼接在一起,以便形成一个图像。到目前为止我尝试了什么?

import numpy as np
import PIL
from PIL import Image
import os
filenames = [os.path.abspath(os.path.join(directory, p)) for p in os.listdir(directory) if p.endswith(('jpg', 'png'))]

imgs = [PIL.Image.open(i) for i in filenames]
 
min_shape = sorted( [(np.sum(i.size), i.size ) for i in imgs])[0][1]
imgs_comb = np.hstack( (np.asarray( i.resize(min_shape) ) for i in imgs ) )
 
imgs_comb = PIL.Image.fromarray( imgs_comb)
imgs_comb.save( 'stitched_image.jpg' )

这会水平拼接图像,而不是完美的图像。输出如下所示:

但所需的输出应该是:

我该怎么做?

既然您已经提到它们的尺寸都相同,那么您可以创建一个新图像:

c, h, w = image.shape

new_image = np.zeros((5 * h, 3 * w))

现在我们有一个正确大小的空图像。

下一步是将图像复制到大图像中(请原谅我,因为我没有测试这段代码,但稍作更改/修复它应该可以工作,重要的部分是想法)

row = -1
for i, img in enumerate(list_of_images):
    if i % 3:
        col = 0
        row += 1
    
    new_image[:, row * h: (row + 1) * h, col * w: (col + 1) * w] = img
    col += 1

本质上,您是将图像平铺成大图像,结果应该如您所愿。

这些是水平缝合在一起的,因为当您实际上只想 hstack 一次只 hstack 成行然后 vstack 将它们垂直缝合在一起时,您已经将它们与 np.hstack() 粘在一起。用下面的内容替换这一行应该可以满足您的需要。

img_rows = []
for min_id in range(0, 15, 3):
    img_row = np.hstack( (np.asarray( i.resize(min_shape) ) for i in imgs[min_id: min_id+3] ) )
    img_rows.append(img_row)
imgs_comb = np.vstack( ( i for i in img_rows ) )

我尝试了和Dr. Prof. Patrick一样的方法,做了更通用的功能。在此版本中,图像数量可以少于行数 * 列数。

#!/usr/bin/env python3
import numpy as np
from imageio import imread, imwrite
from pathlib import Path
import math

def tile_images(images, cols=None, bg_val=None, tile_size=None):
    """Tile images to grid with given number of columns.
    
    Args:
        images (list of np.arrays)
        cols (int): 1 = vertical, None = horizontal stitch
        bg_val (int or tuple): color of empty background
    
    Returns:
        np.array: stitched image
    """
    im1 = np.atleast_3d(images[0])  # add 3rd dim to grey image
    h, w, ch = im1.shape
    if tile_size is not None:
        h, w = tile_size
    
    if not cols:
        cols = len(images) 
    
    rows = math.ceil(len(images) / cols)
    
    # make empty array 
    new = np.zeros((h * rows, w * cols, ch), dtype=im1.dtype)

    # change bg color
    if len(images) < rows * cols and bg_val is not None:
        new = np.add(new, bg_val)  
    
    # paste images into array    
    c, r = 0, 0
    for i, im in enumerate(images):
        x, y = r * h, c * w
        new[x : x + im.shape[0], y : y + im.shape[1]] = np.atleast_3d(im)
        c += 1 
        if not (i+1) % cols:
            r += 1  # next row
            c = 0

    return new        

def main():
    paths = sorted(f for f in Path().glob("*.*") if f.suffix in (".jpg", ".png") and f.stem != "new")
    images = [imread(f) for f in paths] 
    new = tile_images(images, cols=3, bg_val=(127,150,127))
    imwrite("new.png", new)


if __name__ == "__main__":
    main()

编辑:我已经删除了使用 np.atleast_3d 函数检查图像是否有 2 或 3 个暗淡的需要。 参数tile_size允许手动设置网格大小,如果省略,将使用第一张图像。

在此版本中,输入图像的大小不必相同。图片可以叠加,但不能溢出。