I'm using tifffile
in python to save out 3-channel tiff stacks, which I then want to read into ImageJ or FIJI. These tiff stacks open as composites in ImageJ with each channel assigned a (presumably default) colormap/LUT. However, the colors that are assigned aren't the colors that make sense for my images. My problem is that I can't figure out how to specify the colormap for each channel when saving the image using tifffile
.
For example, I'd like to have the following colormap assignments:
Here's the code that I'm using to save the files:
# save hyperstack
with tifffile.TiffWriter(filename, bigtiff=False, imagej=True) as tif:
for i in range(t_stack.shape[0]):
tif.save(t_stack[i], metadata={'Composite mode': 'composite'})
There must be metadata that's saved with the tiff that holds the channel colormap info because I can manually edit the color assignment in ImageJ and then save it, close it, and then when I open the file up again it retains my manual colormap assignments. So I'm guessing there must be a metadata tag (maybe colormap?) that can be used to specify channel colors, but I can't find any info on what tag or syntax to use.
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)