如何使用PIL保存大图片?

4
我想拼接8张9450x75600像素的图像(即75600x75600像素)。
import sys
import PIL
from PIL import Image
    
PIL.Image.MAX_IMAGE_PIXELS = 9331200000
ListeImage=['test1.tif','test2.tif','test3.tif','test4.tif','test5.tif','test6.tif','test7.tif','test8.tif']
images = [Image.open(x) for x in ListeImage]
widths, heights = zip(*(i.size for i in images))
    
total_width = sum(widths)
max_height = max(heights)
    
new_im = Image.new('RGB', (total_width, max_height))
    
y_offset = 0
for im in images:
      new_im.paste(im, (0,y_offset))
      y_offset += im.size[0]
      new_im.save('TOTAL'+str(y_offset)+'.tif')

但是我遇到了这个错误...

Traceback (most recent call last):
  File "C:\Python27\MergeImages.py", line 21, in <module>
    new_im.save('test'+str(bande[0])+'.tif')
  (...)
  File "C:\Python27\lib\site-packages\PIL\TiffImagePlugin.py", line 626, in _pack
    return struct.pack(self._endian + fmt, *values)
error: integer out of range for 'L' format code

我认为这是一个内存问题。如何解决?


你已经调整了输出图像的大小以容纳所有的图像并排放置,但是你似乎在垂直方向上堆叠单个图像而不是水平方向上。我认为你需要使用 x 偏移量而不是 y - Mark Setchell
1个回答

3

您之所以会收到异常报错,是因为您超过了4GB的TIFF格式限制。

请参阅:什么是TIFF元数据的最大大小?

您可以使用BigTIFF格式。
您可以尝试使用Tifffile来编写BigTIFF图像文件。

我更喜欢使用JPEG 2000图像格式。
我在这篇文章中发现了有关使用pillow保存JPEG 2000格式的信息。

我知道一种保存JPEG 2000的方法,就是使用OpenCV。
OpenCV将JP2图像以无损格式保存(与Tiff相同)。

使用OpenCV保存时可能会遇到的问题:

  • 我们需要将pillow图像转换为NumPy数组。
  • 我们需要将RGB转换为BGR颜色格式。

以下是修改后的代码版本,用于使用OpenCV保存JP2:

import sys
import PIL
from PIL import Image
import cv2
import numpy as np
    
PIL.Image.MAX_IMAGE_PIXELS = 9331200000
ListeImage=['test1.tif','test2.tif','test3.tif','test4.tif','test5.tif','test6.tif','test7.tif','test8.tif']
images = [Image.open(x) for x in ListeImage]
widths, heights = zip(*(i.size for i in images))
    
#total_width = sum(widths)
#max_height = max(heights)
    
#new_im = Image.new('RGB', (total_width, max_height))
new_im = Image.new('RGB', (max(widths), sum(heights)))
    
y_offset = 0
for im in images:
    new_im.paste(im, (0,y_offset))
    y_offset += im.size[1] #im.size[0]
    # new_im.save('TOTAL'+str(y_offset)+'.tif')
    cv2.imwrite('TOTAL'+str(y_offset)+'.jp2', cv2.cvtColor(np.array(new_im), cv2.COLOR_RGB2BGR))

注意事项:

  • 看起来你混淆了宽度和高度 - 我尝试修复了它。
  • 我的系统内存不足以测试如此大的图像 - 我用较小的图像进行了测试。
  • 我使用Python 3.6测试了代码,不知道它是否适用于Python 2.7。
  • 我实现了没有中间变量的代码,希望它消耗更少的内存。

更新:

上述解决方案消耗的内存太多了(超过100GB)。 使用大页文件(磁盘空间作为虚拟内存)可以解决,但速度太慢。 该解决方案(保存为JPEG 2000)对于大多数系统并不实用。

下面的解决方案使用BigTIFF格式。 在内存方面,实现也更加高效:

import sys
import PIL
from PIL import Image
import tifffile
import numpy as np
import gc
    
PIL.Image.MAX_IMAGE_PIXELS = 9331200000
ListeImage=['test1.tif','test2.tif','test3.tif','test4.tif','test5.tif','test6.tif','test7.tif','test8.tif']
images = [Image.open(x) for x in ListeImage]
widths, heights = zip(*(i.size for i in images))

# Free memory - release memory of all images.
del images
gc.collect()  # Explicitly invoke the Garbage Collector https://dev59.com/BXM_5IYBdhLWcg3wlEPO

    
#new_im = Image.new('RGB', (max(widths), sum(heights)))
new_im = np.zeros((sum(heights), max(widths), 3), np.uint8)  # Use NumPy array instead of pillow image.
    
y_offset = 0
for x in ListeImage:
    im = Image.open(x)  # Read one input image at a time (for saving RAM).
    #new_im.paste(im, (0,y_offset))
    new_im[y_offset:y_offset+im.size[1], :, :] = np.array(im)  # Copy im to NumPy array (instead of pasting to pillow image - saves RAM).
    y_offset += im.size[1]
    tifffile.imwrite('TOTAL'+str(y_offset)+'.tif', new_im, bigtiff=True)  # Write new_im as BigTIFF.

    del im
    gc.collect()  # Explicitly invoke the Garbage Collector

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接