Source code for nxtomomill.converter.fscan.fscanconverter

import logging
from posixpath import join
import numpy as np
from tomoscan.esrf.scan.h5utils import get_h5_value
from nxtomo.application.nxtomo import NXtomo
from nxtomo.nxobject.nxdetector import ImageKey
from tomoscan.esrf.scan.fscan import FscanDataset, list_datasets

_logger = logging.getLogger(__name__)


[docs]def from_fscan_to_nx( fname, output_fname, detector_name="pcoedgehs", rotation_motor_name="hrrz", halftomo=False, ignore_last_n_projections=0, energy=None, distance=None, ): entries = list_datasets(fname) if len(entries) < 3: _logger.error( "Error: Expected at least three datasets, got only %d" % (len(entries)) ) return do_360 = False if len(entries) == 6: # 360 degrees scans are done in two parts: [1.1, 2.1, 3.1] and then [4.1, 5.1] do_360 = True projs = FscanDataset(fname, detector_name=detector_name, entry="1.1") flats = FscanDataset(fname, detector_name=detector_name, entry="2.1") darks = FscanDataset(fname, detector_name=detector_name, entry="3.1") if do_360: projs2 = FscanDataset(fname, detector_name=detector_name, entry="4.1") flats2 = FscanDataset(fname, detector_name=detector_name, entry="5.1") # Some datasets don't have the 6.1 entry. # Anyway nabu does not support several series of darks. # For now it's safer to ignore the second series of darks try: darks2 = FscanDataset(fname, detector_name=detector_name, entry="6.1") except ValueError: _logger.error("Could not find entry 6.1, proceeding") darks2 = None if darks2 is not None: _logger.warning( "Discarding entry 6.1 as nabu does not support several series of darks yet" ) my_nxtomo = NXtomo() data_urls = [darks.dataset_hdf5_url, flats.dataset_hdf5_url, projs.dataset_hdf5_url] if do_360: data_urls.extend( [ # darks2.dataset_hdf5_url, flats2.dataset_hdf5_url, projs2.dataset_hdf5_url, ] ) my_nxtomo.instrument.detector.data = data_urls img_keys = [ [ImageKey.DARK_FIELD] * darks.data_shape[0], [ImageKey.FLAT_FIELD] * flats.data_shape[0], [ImageKey.PROJECTION] * (projs.data_shape[0] - ignore_last_n_projections), [ImageKey.ALIGNMENT] * ignore_last_n_projections, ] if do_360: img_keys.extend( [ # [ImageKey.DARK_FIELD] * darks2.data_shape[0], [ImageKey.FLAT_FIELD] * flats2.data_shape[0], [ImageKey.PROJECTION] * (projs2.data_shape[0] - ignore_last_n_projections), [ImageKey.ALIGNMENT] * ignore_last_n_projections, ] ) my_nxtomo.instrument.detector.image_key_control = np.concatenate(img_keys) rotation_angles_path = join(projs.entry, f"instrument/{rotation_motor_name}/data") rotation_angles = get_h5_value(projs.fname, rotation_angles_path) # Sometimes values are in /data, sometimes in /value ... who needs a stable format anyway ? if rotation_angles is None: _logger.error( f"Could not find the rotation angles in {rotation_angles_path}, trying in /values" ) rotation_angles_path = rotation_angles_path.replace("/data", "/value") # --- rotation_angles = get_h5_value(projs.fname, rotation_angles_path) if rotation_angles is not None: last_idx = None if ignore_last_n_projections > 0: last_idx = -ignore_last_n_projections rotation_angles = [ [0] * darks.data_shape[0], [0] * flats.data_shape[0], rotation_angles[:last_idx], [0] * ignore_last_n_projections, ] if do_360: rotation_angles2 = get_h5_value( projs2.fname, join(projs2.entry, f"instrument/{rotation_motor_name}/data"), ) rotation_angles.extend( [ # [0] * darks2.data_shape[0], [0] * flats2.data_shape[0], rotation_angles2[:last_idx], [0] * ignore_last_n_projections, ] ) my_nxtomo.sample.rotation_angle = np.concatenate(rotation_angles) my_nxtomo.instrument.detector.field_of_view = ( "Half" if (halftomo and do_360) else "Full" ) my_nxtomo.instrument.detector.x_pixel_size = ( my_nxtomo.instrument.detector.y_pixel_size ) = (6.5 * 1e-6) if energy is not None: my_nxtomo.energy = energy # in keV by default if distance is not None: my_nxtomo.instrument.detector.distance = distance # in meter my_nxtomo.save( file_path=output_fname, data_path="entry", overwrite=True, nexus_path_version=1.1, )