使用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' )

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

Output_from_the_script

但是想要的输出应该是:

Desired_output

我该怎么做?

解决方法

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

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

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

,

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

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 ) )
,

我尝试了与 Patrick 教授相同的方法,并制作了更通用的功能。在此版本中,图像数量可以少于行 * cols。

#!/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 允许手动设置网格大小,如果省略,将使用第一张图像。

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