问题描述
我有 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
本质上,您将图像平铺到大图像中,结果应该如您所愿。
,这些是水平拼接在一起的,因为当您实际上只想一次 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 允许手动设置网格大小,如果省略,将使用第一张图像。
在此版本中,输入图像不必具有相同的大小。图片可以叠加,但不能溢出。