Skip to main content
 首页 » 编程设计

python之合并磁盘上的大图像

2025年05月04日17over140

主要问题:

我有一个映射步骤,我在其中并行渲染图像的大量扇区:

1 2 
3 4 
 
worker a -> 1 
worker b -> 2 
... 
 
merge 1,2,3,4 to make final image 

如果它能装进内存

对于相对较小且可以放入 RAM 的图像,可以简单地使用 PIL 的功能:

def merge_images(image_files, x, y): 
    images = map(Image.open, image_files) 
    width, height = images[0].size     
    new_im = Image.new('RGB', (width * x, height * y)) 
    for n, im in enumerate(images): 
        new_im.paste(im, ((n%x) * width, (n//y) * height)) 
    return new_im 

不幸的是,我将拥有很多很多大扇区。我想最终将这些图片合并成一张大约 40,000 x 60,000 像素的图像,我估计大约有 20 GB。 (或者甚至更多)

很明显,我们不能在 RAM 上解决这个问题。我知道有一些替代方案,例如 memmap'ing 数组和写入切片,我会尝试这些。但是,我正在寻找尽可能开箱即用的解决方案。

有没有人知道任何更简单的替代方案? 尽管到目前为止我尝试过的所有方法都是在 python 中,但它不需要在 python 中。

请您参考如下方法:

pyvips可以非常快速有效地做你想做的事。例如:

import sys 
import pyvips 
 
images = [pyvips.Image.new_from_file(filename, access="sequential") 
          for filename in sys.argv[2:]] 
final = pyvips.Image.arrayjoin(images, across=10) 
final.write_to_file(sys.argv[1]) 

access="sequential" 选项告诉 pyvips 您想要流式传输图像。它只会在生成输出时按需加载像素,因此您可以仅使用少量内存来合并大量图像。 arrayjoin运算符将图像数组连接到 across 的网格中。它有很多布局选项:您可以指定边框、重叠、背景、居中行为等。

我可以这样运行它:

$ for i in {1..100}; do cp ~/pics/k2.jpg $i.jpg; done 
$ time ../arrayjoin.py x.tif *.jpg  
 
real    0m2.498s 
user    0m3.579s 
sys 0m1.054s 
$ vipsheader x.tif 
x.tif: 14500x20480 uchar, 3 bands, srgb, tiffload 

所以它在这台笔记本电脑上加入 100 张 JPG 图片以在大约 2.5 秒内制作 14,000 x 20,000 像素的马赛克,并且从观看 top 来看,需要大约 300mb 的内存。我用它把 30,000 多张图片合并到一个文件中,而且它会更高。我制作了超过 300,000 x 300,000 像素的图像。

PIL 的 paste 的 pyvips 等价物是 insert .你也可以使用它,尽管它对于大量图像来说效果不佳。

还有一个命令行界面,所以你可以输入:

vips arrayjoin "${echo *.jpg}" x.tif --across 10 

加入大量 JPG 图片。