如何在保存tiff堆栈时指定颜色映射表

5
我正在使用Python中的“tifffile”保存3通道Tiff堆栈,然后想将其读入ImageJ或FIJI。这些Tiff堆栈在ImageJ中作为复合图像打开,每个通道都分配了(可能是默认的)颜色映射/ LUT。但是,分配的颜色不适用于我的图像。我的问题是我无法弄清楚如何在使用“tifffile”保存图像时为每个通道指定颜色映射。 例如,我想要以下颜色映射分配:
  • ch 0:灰色
  • ch 1:绿色
  • ch 2:红色
下面是我用来保存文件的代码:
# 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'})

必须有保存在tiff中的元数据来保存通道颜色映射信息,因为我可以在ImageJ中手动编辑颜色分配,然后保存、关闭文件,再次打开文件时仍保留我的手动颜色映射分配。所以我猜测必须有一个元数据标签(可能是colormap?)可用于指定通道颜色,但我找不到任何关于要使用哪个标签或语法的信息。

3个回答

8
创建私有的IJMetadata (50839)和IJMetadataByteCounts (50838) TIFF标签,并将它们作为extratags传递给tifffile.imsave。IJMetadata包含应用程序内部以二进制格式存储的元数据。颜色信息在luts元数据中:
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)

1
谢谢!大约四个小时的谷歌搜索和尝试都没有让我接近这样的东西……有没有解释这个的文档? - holastello
你能否解释一下这段代码的哪一部分告诉ImageJ使用绿色伪彩色呢?或者说,我真正想知道的是,如果要为具有多于3个通道的图像添加其他颜色(例如蓝色、粉色、黄色),该如何扩展这段代码? - holastello

5
你可以向tifffile的imsave函数传递许多关键字参数。由于它的文档不是很好,所以我发现最有用的是阅读TiffWriter类中保存函数的docstring:https://github.com/blink1073/tifffile/blob/master/tifffile/tifffile.py#L750
对于ImageJ元数据规范,TiffWriter.save会引用imagej_metadata_tags,在这里您可以看到可以存储在元数据类型变量(第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))

您可以创建LUT(查找表)来使用不同的颜色映射可视化数据。假设您的数据是uint8类型,那么您需要的LUT应该具有形状(3,256),其中3个通道分别表示颜色通道,256个值表示强度值。因此,如果您需要灰色、绿色和红色的LUT,则需要执行以下操作:

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,
) 

谢谢,@Jenny Folkesson。你能解释一下这段代码的哪个部分告诉ImageJ使用绿色伪彩色吗?或者,我真正想知道的是如何扩展它以添加其他颜色(例如蓝色、粉色、黄色)? - holastello
就像您通过填充三列中的第一列和第二列来创建lut_red和lut_green一样,您可以通过lut_blue[2,:] = val_range创建lut_blue,并将强度值范围添加到其他颜色的组合列中。希望这有所帮助。 - Jenny Folkesson

2
我最近在寻找一种解决方案,可以将带有ImageJ元数据的tiff文件保存为多于3个颜色通道以及灰度通道。我发现了这个帖子,并且上面提到的解决方案非常有用,我还根据它们的示例扩展了额外的通道。
在ImageJ中,可以使用最多7种不同的颜色通道,在复合模式下基于RGB颜色方案 - 三种原色红、绿和蓝,混合2种原色得到黄、洋红和青色,以及一个灰度通道。
要添加蓝色LUT,您只需像上面的示例中为红色或绿色LUT定义ndarray,但将强度值范围从0到255分配给第三个数组,而其他两个数组(红色和绿色)则填充为零。
"最初的回答"
lut_blue = np.zeros((3, 256), dtype=np.uint8)
lut_blue[2, :] = val_range

通过混合基本颜色红色和绿色,现在可以生成一个黄色的LUT。

注:LUT是指查找表,是一种数据结构,常用于图像处理中。

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

下面的示例将生成一个具有7个通道的tiff文件。对于tiff堆栈中的图像,颜色分配如下定义: "最初的回答":
以下示例将生成一个包含7个通道的tiff文件。在tiff堆栈中,图像的颜色分配由以下定义:
ijmeta = {'LUTs': [lut_gray, lut_red, lut_green, lut_blue, lut_yellow, lut_magenta, lut_cyan]}

并且可以根据需要进行调整。基于Jenny Folkesson的示例,完整的代码如下:

最初的回答:

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,
) 

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接