问题
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:
- ch 0: grays
- ch 1: green
- ch 2: red
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.
回答1:
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)
回答2:
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,
)
回答3:
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,
)
来源:https://stackoverflow.com/questions/50258287/how-to-specify-colormap-when-saving-tiff-stack