Source code for coseda.nexus.images

"""NeXus-friendly NXdata view for image stacks."""

from __future__ import annotations

from typing import Optional, Tuple

import numpy as np
import h5py

from coseda.nexus.paths import IMAGE_DATA_PATH, IMAGE_NXDATA_PATH

_COORDINATE_NOTE = "detector_pixel (origin=center of lower-left pixel, +y up)"


def _set_attr_if_missing(attrs, key: str, value) -> None:
    if key not in attrs:
        attrs[key] = value


def _ensure_axis_dataset(group: h5py.Group, name: str, length: int, units: str, long_name: str) -> None:
    if name in group:
        if group[name].shape != (length,):
            del group[name]
        else:
            group[name][...] = np.arange(length, dtype=group[name].dtype)
            _set_attr_if_missing(group[name].attrs, "units", units)
            _set_attr_if_missing(group[name].attrs, "long_name", long_name)
            return
    ds = group.create_dataset(name, data=np.arange(length, dtype=np.int32))
    ds.attrs["units"] = units
    ds.attrs["long_name"] = long_name


def _link_dataset(group: h5py.Group, name: str, target: h5py.Dataset) -> None:
    if name in group:
        del group[name]
    group[name] = target


def _get_image_shape(h5file: h5py.File) -> Optional[Tuple[int, ...]]:
    if IMAGE_DATA_PATH not in h5file:
        return None
    return tuple(h5file[IMAGE_DATA_PATH].shape)


[docs] def ensure_image_nxdata(h5file: h5py.File) -> None: """ Create an NXdata view for the image dataset without moving the legacy data. """ shape = _get_image_shape(h5file) if not shape: return images_ds = h5file[IMAGE_DATA_PATH] if images_ds.ndim not in (2, 3): return nx_group = h5file.require_group(IMAGE_NXDATA_PATH.lstrip("/")) nx_group.attrs["NX_class"] = "NXdata" nx_group.attrs["signal"] = "images" nx_group.attrs["coordinate_system"] = _COORDINATE_NOTE _link_dataset(nx_group, "images", images_ds) if images_ds.ndim == 3: num_frames, height, width = images_ds.shape nx_group.attrs["axes"] = ["frame", "pixel_y", "pixel_x"] _ensure_axis_dataset(nx_group, "frame", num_frames, "index", "Frame index") else: height, width = images_ds.shape nx_group.attrs["axes"] = ["pixel_y", "pixel_x"] _ensure_axis_dataset(nx_group, "pixel_y", height, "pixel", "Detector pixel Y") _ensure_axis_dataset(nx_group, "pixel_x", width, "pixel", "Detector pixel X")