pylibtiff icon indicating copy to clipboard operation
pylibtiff copied to clipboard

Adding ImageDepth support to pylibtiff

Open markemus opened this issue 5 years ago • 5 comments

Hey @pearu, I wrote some code to allow pylibtiff to read 3D TIFF images. It's a modified version of the TIFF.read_tiles() method in libtiff_ctypes.py. Currently it's a monkeypatch that is applied to libtiff at runtime.

If you're interested in it, I'd be happy to refactor it to be part of:

  1. read_tiles(), or
  2. a separate method, TIFF.read_z_tiles()

I'm hopefully also going to be doing this for write_tiles() in the near future.

Thanks for writing this great library!

Here's the code:

"""Adds a method to libtiff.TIFF to read z-stack tiff images (images with an ImageDepth tag).
Import this module anywhere in your code but before instantiating the TIFF class to apply the patch."""
import numpy as np

from libtiff import TIFF


def read_z_tiles(self, dtype=np.uint8):
    num_tcols = int(self.GetField("TileWidth")/2)
    num_trows = int(self.GetField("TileLength")/2)
    num_icols = int(self.GetField("ImageWidth")/2)
    num_irows = int(self.GetField("ImageLength")/2)
    num_depths = self.GetField("ImageDepth")
    # this number includes extra samples
    samples_pp = self.GetField('SamplesPerPixel') * 2

    def read_plane(plane, tmp_tile, plane_index=0, depth_index=0):
        for y in range(0, num_irows, num_trows):
            for x in range(0, num_icols, num_tcols):
                # input data is 2x as many dims per pixel as parameters state.
                r = self.ReadTile(tmp_tile.ctypes.data, x*2, y*2, depth_index, plane_index)
                if not r:
                    raise ValueError(
                        "Could not read tile x:%d,y:%d,z:%d,sample:%d from file" %
                        (x, y, plane_index, depth_index))

                # if the tile is on the edge, it is smaller
                tile_width = min(num_tcols, num_icols - x)
                tile_height = min(num_trows, num_irows - y)

                plane[y:y + tile_height, x:x + tile_width] = \
                    tmp_tile[:tile_height, :tile_width]

    full_image = np.zeros((num_depths, num_irows, num_icols, samples_pp), dtype=dtype, order='C')
    tmp_tile = np.zeros((num_trows, num_tcols, samples_pp), dtype=dtype, order='C')

    for depth_index in range(num_depths):
        read_plane(full_image[depth_index], tmp_tile, depth_index=depth_index)

    return full_image

# Apply monkeypatch
TIFF.read_z_tiles = read_z_tiles```

markemus avatar May 15 '19 19:05 markemus

Just to be clear, this is for color image stacks- the current version already should support 3D grayscale stacks. So I guess these are really "4D" stacks.

markemus avatar May 17 '19 20:05 markemus

Thanks for reporting the issue and sorry for the long delay in responding. Could you submit your changes as a PR?

pearu avatar Feb 03 '20 12:02 pearu

I've developed it more since then, including code for rewriting the data into separate directories. I'll post a PR when I get a chance.

markemus avatar Feb 03 '20 18:02 markemus

@pearu I opened the PR- not sure if you get an alert when that happens.

markemus avatar Feb 03 '20 21:02 markemus

@markemus thanks for the PR. I'll give it a review and I believe github should send any relevant messages to you.

pearu avatar Feb 03 '20 21:02 pearu