From 28b818974e0602d9f38858c9e979d4755550c8e5 Mon Sep 17 00:00:00 2001 From: Leonid Bloch Date: Thu, 16 Feb 2017 02:54:43 +0200 Subject: [PATCH] Allow to save tiff stacks from separate images This is a quick solution that will allow to save tiff stacks from separate images, e.g. from Numpy arrays. Previously, tiff stacks could be saved only from multiframe images. This behavior is similar to what is possible now with GIFs. Note however, that for correct results, all the appended images should have the same encoder{info,config} properties. Example: import numpy as np from PIL import Image a = np.ones((100,100,100), dtype=np.uint8) imlist = [] for m in a: imlist.append(Image.fromarray(m)) imlist[0].save("test.tif", compression="tiff_deflate", save_all=True, append_images=imlist[1:]) (Should result in a 100-frame, 100x100 tiff stack.) Signed-off-by: Leonid Bloch --- PIL/TiffImagePlugin.py | 23 +++++++++++++++++------ Tests/test_file_tiff.py | 12 ++++++++++++ 2 files changed, 29 insertions(+), 6 deletions(-) diff --git a/PIL/TiffImagePlugin.py b/PIL/TiffImagePlugin.py index 505025bb8..ede267c43 100644 --- a/PIL/TiffImagePlugin.py +++ b/PIL/TiffImagePlugin.py @@ -1737,17 +1737,28 @@ class AppendingTiffWriter: self.rewriteLastLong(offset) def _save_all(im, fp, filename): - if not hasattr(im, "n_frames"): + encoderinfo = im.encoderinfo.copy() + encoderconfig = im.encoderconfig + append_images = encoderinfo.get("append_images", []) + if not hasattr(im, "n_frames") and not len(append_images): return _save(im, fp, filename) cur_idx = im.tell() try: with AppendingTiffWriter(fp) as tf: - for idx in range(im.n_frames): - im.seek(idx) - im.load() - _save(im, tf, filename) - tf.newFrame() + for ims in [im]+append_images: + ims.encoderinfo = encoderinfo + ims.encoderconfig = encoderconfig + if not hasattr(ims, "n_frames"): + nfr = 1 + else: + nfr = ims.n_frames + + for idx in range(nfr): + ims.seek(idx) + ims.load() + _save(ims, tf, filename) + tf.newFrame() finally: im.seek(cur_idx) diff --git a/Tests/test_file_tiff.py b/Tests/test_file_tiff.py index 1fe3ad45e..1dfd0620c 100644 --- a/Tests/test_file_tiff.py +++ b/Tests/test_file_tiff.py @@ -473,6 +473,18 @@ class TestFileTiff(PillowTestCase): with Image.open(mp) as im: self.assertEqual(im.n_frames, 3) + # Test appending images + mp = io.BytesIO() + im = Image.new('RGB', (100, 100), '#f00') + ims = [Image.new('RGB', (100, 100), color) for color + in ['#0f0', '#00f']] + im.save(mp, format="TIFF", save_all=True, append_images=ims) + + mp.seek(0, os.SEEK_SET) + reread = Image.open(mp) + self.assertEqual(reread.n_frames, 3) + + def test_saving_icc_profile(self): # Tests saving TIFF with icc_profile set. # At the time of writing this will only work for non-compressed tiffs