예제 #1
0
def save_stack(name,
               data,
               luts=None,
               display_ranges=None,
               resolution=1.,
               compress=0,
               dimensions=None):
    """
    Saves `data`, an array with 5, 4, 3, or 2 dimensions [TxZxCxYxX]. `resolution` 
    can be specified in microns per pixel. Setting `compress` to 1 saves a lot of 
    space for integer masks. The ImageJ lookup table for each channel can be set
    with `luts` (e.g, ops.io.GRAY, ops.io.GREEN, etc). The display range can be set
    with a list of (min, max) pairs for each channel.

    >>> random_data = np.random.randint(0, 2**16, size=(3, 100, 100), dtype=np.uint16)
    >>> luts = ops.io.GREEN, ops.io.RED, ops.io.BLUE
    >>> display_ranges = (0, 60000), (0, 40000), (0, 20000)
    >>> save('random_image', random_data, luts=luts, display_ranges=display_ranges)

    Compatible array data types are:
        bool (converted to uint8 0, 255)
        uint8 
        uint16 
        float32 
        float64 (converted to float32)
    """

    if name.split('.')[-1] != 'tif':
        name += '.tif'
    name = os.path.abspath(name)

    if isinstance(data, list):
        data = np.array(data)

    if not (2 <= data.ndim <= 5):
        error = 'Input has shape {}, but number of dimensions must be in range [2, 5]'
        raise ValueError(error.format(data.shape))

    if (data.dtype == np.int64):
        if (data >= 0).all() and (data < 2**16).all():
            data = data.astype(np.uint16)
        else:
            data = data.astype(np.float32)
            print('Cast int64 to float32')
    if data.dtype == np.float64:
        data = data.astype(np.float32)
        # print('Cast float64 to float32')

    if data.dtype == np.bool:
        data = 255 * data.astype(np.uint8)

    if data.dtype == np.int32:
        if data.min() >= 0 & data.max() < 2**16:
            data = data.astype(np.uint16)
        else:
            raise ValueError('error casting from np.int32 to np.uint16, '
                             'data out of range')

    if not data.dtype in (np.uint8, np.uint16, np.float32):
        raise ValueError('Cannot save data of type %s' % data.dtype)

    resolution = (1. / resolution, ) * 2

    if not os.path.isdir(os.path.dirname(name)):
        os.makedirs(os.path.dirname(name), exist_ok=True)

    if data.ndim == 2:
        # simple description
        min, max = single_contrast(data, display_ranges)
        description = imagej_description_2D(min, max)
        imsave(name,
               data,
               photometric='minisblack',
               description=description,
               resolution=resolution,
               compress=compress)
    else:
        # hyperstack description
        nchannels = data.shape[-3]
        luts, display_ranges = infer_luts_display_ranges(
            data, luts, display_ranges)

        leading_shape = data.shape[:-2]
        if dimensions is None:
            dimensions = 'TZC'[::-1][:len(leading_shape)][::-1]

        if 'C' not in dimensions:
            # TODO: support lut
            contrast = single_contrast(data, display_ranges)
            description = imagej_description(leading_shape,
                                             dimensions,
                                             contrast=contrast)
            imsave(name,
                   data,
                   photometric='minisblack',
                   description=description,
                   resolution=resolution,
                   compress=compress)
        else:
            # the full monty
            description = imagej_description(leading_shape, dimensions)
            # metadata encoding LUTs and display ranges
            # see http://rsb.info.nih.gov/ij/developer/source/ij/io/TiffEncoder.java.html
            tag_50838 = ij_tag_50838(nchannels)
            tag_50839 = ij_tag_50839(luts, display_ranges)

            imsave(name,
                   data,
                   photometric='minisblack',
                   description=description,
                   resolution=resolution,
                   compress=compress,
                   extratags=[
                       (50838, 'I', len(tag_50838), tag_50838, True),
                       (50839, 'B', len(tag_50839), tag_50839, True),
                   ])
예제 #2
0
def save_stack(name,
               data,
               luts=None,
               display_ranges=None,
               resolution=1.,
               compress=0):
    """
    Saves `data`, an array with 5, 4, 3, or 2 dimensions [TxZxCxYxX]. `resolution` 
    can be specified in microns per pixel. Setting `compress` to 1 saves a lot of 
    space for integer masks. The ImageJ lookup table for each channel can be set
    with `luts` (e.g, ops.io.GRAY, ops.io.GREEN, etc). The display range can be set
    with a list of (min, max) pairs for each channel.

    >>> random_data = np.random.randint(0, 2**16, size=(3, 100, 100), dtype=np.uint16)
    >>> luts = ops.io.GREEN, ops.io.RED, ops.io.BLUE
    >>> display_ranges = (0, 60000), (0, 40000), (0, 20000)
    >>> save('random_image', random_data, luts=luts, display_ranges=display_ranges)

    Compatible array data types are:
        bool (converted to uint8 0, 255)
        uint8 
        uint16 
        float32 
        float64 (converted to float32)
    """

    if name.split('.')[-1] != 'tif':
        name += '.tif'
    name = os.path.abspath(name)

    if isinstance(data, list):
        data = np.array(data)

    if data.ndim == 2:
        data = data[None]

    if (data.dtype == np.int64):
        if (data >= 0).all() and (data < 2**16).all():
            data = data.astype(np.uint16)
        else:
            data = data.astype(np.float32)
            print('Cast int64 to float32')
    if data.dtype == np.float64:
        data = data.astype(np.float32)
        print('Cast float64 to float32')

    if data.dtype == np.bool:
        data = 255 * data.astype(np.uint8)

    if not data.dtype in (np.uint8, np.uint16, np.float32):
        raise ValueError('Cannot save data of type %s' % data.dtype)

    nchannels = data.shape[-3]

    resolution = (1. / resolution, ) * 2

    if luts is None:
        luts = DEFAULT_LUTS + (GRAY, ) * nchannels

    if display_ranges is None:
        display_ranges = [None] * data.shape[-3]
    for i, dr in enumerate(display_ranges):
        if dr is None:
            x = data[..., i, :, :]
            display_ranges[i] = x.min(), x.max()

    try:
        luts = luts[:nchannels]
        display_ranges = display_ranges[:nchannels]
    except IndexError:
        raise IndexError('Must provide at least %d luts and display ranges' %
                         nchannels)

    # metadata encoding LUTs and display ranges
    # see http://rsb.info.nih.gov/ij/developer/source/ij/io/TiffEncoder.java.html
    description = ij_description(data.shape)
    tag_50838 = ij_tag_50838(nchannels)
    tag_50839 = ij_tag_50839(luts, display_ranges)

    if not os.path.isdir(os.path.dirname(name)):
        os.makedirs(os.path.dirname(name))

    imsave(name,
           data,
           photometric='minisblack',
           description=description,
           resolution=resolution,
           compress=compress,
           extratags=[
               (50838, 'I', len(tag_50838), tag_50838, True),
               (50839, 'B', len(tag_50839), tag_50839, True),
           ])