How to specify colormap when saving tiff stack

南楼画角 提交于 2019-12-06 04:49:28

Create the private IJMetadata (50839) and IJMetadataByteCounts (50838) TIFF tags on your own and pass them to tifffile.imsave as extratags. IJMetadata contains application internal metadata in a binary format. The color information is in the luts metadata:

import struct
import numpy
import tifffile


def imagej_metadata_tags(metadata, byteorder):
    """Return IJMetadata and IJMetadataByteCounts tags from metadata dict.

    The tags can be passed to the TiffWriter.save function as extratags.

    """
    header = [{'>': b'IJIJ', '<': b'JIJI'}[byteorder]]
    bytecounts = [0]
    body = []

    def writestring(data, byteorder):
        return data.encode('utf-16' + {'>': 'be', '<': 'le'}[byteorder])

    def writedoubles(data, byteorder):
        return struct.pack(byteorder+('d' * len(data)), *data)

    def writebytes(data, byteorder):
        return data.tobytes()

    metadata_types = (
        ('Info', b'info', 1, writestring),
        ('Labels', b'labl', None, writestring),
        ('Ranges', b'rang', 1, writedoubles),
        ('LUTs', b'luts', None, writebytes),
        ('Plot', b'plot', 1, writebytes),
        ('ROI', b'roi ', 1, writebytes),
        ('Overlays', b'over', None, writebytes))

    for key, mtype, count, func in metadata_types:
        if key not in metadata:
            continue
        if byteorder == '<':
            mtype = mtype[::-1]
        values = metadata[key]
        if count is None:
            count = len(values)
        else:
            values = [values]
        header.append(mtype + struct.pack(byteorder+'I', count))
        for value in values:
            data = func(value, byteorder)
            body.append(data)
            bytecounts.append(len(data))

    body = b''.join(body)
    header = b''.join(header)
    data = header + body
    bytecounts[0] = len(header)
    bytecounts = struct.pack(byteorder+('I' * len(bytecounts)), *bytecounts)
    return ((50839, 'B', len(data), data, True),
            (50838, 'I', len(bytecounts)//4, bytecounts, True))


filename = 'FluorescentCells.tif'
image = tifffile.imread(filename)

grays = numpy.tile(numpy.arange(256, dtype='uint8'), (3, 1))
red = numpy.zeros((3, 256), dtype='uint8')
red[0] = numpy.arange(256, dtype='uint8')
green = numpy.zeros((3, 256), dtype='uint8')
green[1] = numpy.arange(256, dtype='uint8')
ijtags = imagej_metadata_tags({'LUTs': [grays, green, red]}, '>')

tifffile.imsave('test_ijmetadata.tif', image, byteorder='>', imagej=True,
                metadata={'mode': 'composite'}, extratags=ijtags)

You can pass a number of keyword arguments to tifffile's imsave function. It's not very well documented, so what I found most helpful was to read the docstring for the save function in the TiffWriter class:

https://github.com/blink1073/tifffile/blob/master/tifffile/tifffile.py#L750

For ImageJ metadata specifications, TiffWriter.save then refers to imagej_metadata_tags where you can see what types of data you can store in the variable metadata_types (line 7749):

https://github.com/blink1073/tifffile/blob/master/tifffile/tifffile.py#L7710

metadata_types = (
    ('Info', b'info', 1, _string),
    ('Labels', b'labl', None, _string),
    ('Ranges', b'rang', 1, _doubles),
    ('LUTs', b'luts', None, _ndarray),
    ('Plot', b'plot', 1, _bytes),
    ('ROI', b'roi ', 1, _bytes),
    ('Overlays', b'over', None, _bytes))

You can create LUTs to visualize your data using different colormaps. Presumably your data is uint8, then the LUTs you will need have the shape (3, 256) for the 3 color channels and 256 intensity values. So for gray, green and red LUTs you will need to something along the lines:

import numpy as np
import tifffile

# Create a random test image
im_3frame = np.random.randint(0, 255, size=(3, 150, 250), dtype=np.uint8)
# Intensity value range
val_range = np.arange(256, dtype=np.uint8)
# Gray LUT
lut_gray = np.stack([val_range, val_range, val_range])
# Red LUT
lut_red = np.zeros((3, 256), dtype=np.uint8)
lut_red[0, :] = val_range
# Green LUT
lut_green = np.zeros((3, 256), dtype=np.uint8)
lut_green[1, :] = val_range
# Create ijmetadata kwarg
ijmeta = {'LUTs': [lut_gray, lut_red, lut_green]}
# Save image
tifffile.imsave(
    save_name,
    im_rgb,
    imagej=True,
    metadata={'mode': 'composite'},
    ijmetadata=ijmeta,
) 

I recently came across this thread while looking for a solution to save tiff files with ImageJ metadata for more than 3 color channels in addition to a gray channel. The solutions described above were very helpful and I extended the example for additional channels.

In ImageJ one can use up to 7 different color channels in composite mode based on the RGB color scheme - the three primary colors red, green and blue, mixtures of 2 primary colors resulting in yellow, magenta and cyan as well as a gray channel.

To add a blue LUT you would simply define a ndarray as shown in the example above for the red or green LUT but assign the intensity values ranging from 0 to 255 to the third array while the other two arrays (red and green) are filled with zeros.

lut_blue = np.zeros((3, 256), dtype=np.uint8)
lut_blue[2, :] = val_range

By 'mixing' of e.g. the primary colors red and green one could now generate a yellow LUT.

lut_yellow= np.zeros((3, 256), dtype='uint8')
lut_yellow[[0,1],:] = np.arange(256, dtype='uint8')

The example given below will result in the generation of a tiff file with 7 channels. The color assignment to the images in the tiff stack is defined by:

ijmeta = {'LUTs': [lut_gray, lut_red, lut_green, lut_blue, lut_yellow, lut_magenta, lut_cyan]}

and can be adjusted as required. The complete code based on the example by Jenny Folkesson looks as follows:

import numpy as np
from tifffile import imread, imsave

# Create a random test image
im_3frame = np.random.randint(0, 255, size=(7, 150, 250), dtype=np.uint8)
# Intensity value range
val_range = np.arange(256, dtype=np.uint8)
# Gray LUT
lut_gray = np.stack([val_range, val_range, val_range])
# Red LUT
lut_red = np.zeros((3, 256), dtype=np.uint8)
lut_red[0, :] = val_range
# Green LUT
lut_green = np.zeros((3, 256), dtype=np.uint8)
lut_green[1, :] = val_range
# Blue LUT
lut_blue = np.zeros((3, 256), dtype=np.uint8)
lut_blue[2, :] = val_range
# Yellow LUT
lut_yellow= np.zeros((3, 256), dtype='uint8')
lut_yellow[[0,1],:] = np.arange(256, dtype='uint8')
# Magenta LUT
lut_magenta= np.zeros((3, 256), dtype='uint8')
lut_magenta[[0,2],:] = np.arange(256, dtype='uint8')
# Cyan LUT
lut_cyan= np.zeros((3, 256), dtype='uint8')
lut_cyan[[1,2],:] = np.arange(256, dtype='uint8')


# Create ijmetadata kwarg
ijmeta = {'LUTs': [lut_gray, lut_red, lut_green, lut_blue, lut_yellow, lut_magenta, lut_cyan]}
# Save image
imsave(
    'test.tif',
    im_3frame,
    imagej=True,
    metadata={'mode': 'composite'},
    ijmetadata=ijmeta,
) 
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!