Source code for SDF.file_io.lsm

import logging
import os

import numpy as np
from PIL import Image

from SDF.config import CONFIG
from SDF.data_model import Workspace, SourceParameters, Instrument, ArrayDataset1D, Parameter, ImageDataset, \
    ImageData, ParameterSet, ArrayDataset2D, ArrayData2D

if CONFIG.use_system_tifffile:
    import tifffile
else:
    from SDF.extern import tifffile

logger = logging.getLogger(__name__)


# The parameters read from the lsm file lack units.
# The following dictionary assigns a few units to
# parameters manually. We are not 100% sure they
# are always right, but those were the fixed units
# shown by Zeiss' ZEN software for our examples
# lsm files.
MANUALLY_INSERTED_UNITS = {"plane_spacing": "um",
                           "line_spacing": "um",
                           "sample_spacing": "um",
                           "pinhole_diameter": "um"}


[docs]def load_from_lsm(filename: str) -> Workspace: logger.info(f'Reading {filename}') with tifffile.TiffFile(filename) as lsm: if not lsm.is_lsm: raise ValueError("File is no lsm file") logger.info(f'Found {len(lsm.pages)} pages (images)') ws = Workspace(name=os.path.split(filename)[1]) ws.parameters.add(SourceParameters(converter_name="lsm2sdf", source_file_type="lsm-file", source_file=filename)) # the first page contains global information not present in the other pages first_page = lsm.pages[0] instrument = Instrument(name="Carl Zeiss LSM") instrument.add_from_structure(first_page.cz_lsm_scan_info, parse_names_from_dicts=True) ws.instruments.add(instrument) ws.datasets.add(ArrayDataset1D(name="time-stamps", data=first_page.cz_lsm_time_stamps)) # all attributes of the second page are present in all other pages attr_keys = {key for key in dir(lsm.pages[1]) if not key.startswith("_")} attr_keys.remove("is_lsm") # only true for first page, irrelevant for the others attr_keys.remove("asarray") # image content, handled separately # TODO: These are just copied from old converter # I (Niklas) have no clue, so I copied the old comments for future reference attr_keys.remove("tags") # dictionary of tags, needs some special treatment attr_keys.remove("imagej_tags") # there is some problem attr_keys.remove("uic_tags") # there is some problem # handle series data for series_index, series in enumerate(lsm.series): series_ws = Workspace(name=f"series_{series_index}") ws.workspaces.add(series_ws) # find attributes that vary per layer in the series per_layer_attr_keys = set() for key in attr_keys: for layer in series[1:]: # if key has different values in different layers: handle later if getattr(layer, key) != getattr(series[0], key): per_layer_attr_keys.add(key) break else: # if key has the same values for all layers: take value from first page series_ws.parameters.add(Parameter(key, getattr(first_page, key))) # handle layers in series n_layers = series[0].asarray().shape[0] for layer_index in range(n_layers): layer_ws = Workspace(name=f"layer_{layer_index}") series_ws.workspaces.add(layer_ws) # handle images in layer for image_index in range(len(series)): image = Image.fromarray(series[image_index].asarray()[layer_index].astype(np.uint8)) dataset = ImageDataset(name=f"img_{image_index}", data=ImageData(image)) layer_ws.datasets.add(dataset) for key in per_layer_attr_keys: dataset.parameters.add(Parameter(key, getattr(series[image_index], key))) # global lsm attributes lsm_keys = {key for key in dir(lsm) if not key.startswith("_")} lsm_keys.remove("byteorder") # irrelevant since tifffile handled this for us already lsm_keys.remove("pages") lsm_keys.remove("filehandle") lsm_keys.remove("asarray") lsm_keys.remove("fstat") lsm_keys.remove("close") lsm_keys.remove("series") for key in lsm_keys: value = getattr(lsm, key) ws.parameters.add(Parameter(key, value)) # other attributes unique to first page: # 1. cz_lsm_info cz_lsm_info_params = ParameterSet(name="cz_lsm_info") ws.parameters.add(cz_lsm_info_params) for index, value in enumerate(first_page.cz_lsm_info): cz_lsm_info_params.add(Parameter(name=str(index), value=value)) # 2. cz_lsm_channel_colors ws.datasets.add(ArrayDataset2D(name="channel colors", data=ArrayData2D(first_page.cz_lsm_channel_colors))) # 3. cz_lsm_event_list: empty in example, skipped for now # TODO return ws