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