Source code for ewokstomo.tasks.online.nabu_pipeline
import numpy as np
from collections import namedtuple
from nabu.pipeline.fullfield.processconfig import ProcessConfig
from nabu.pipeline.fullfield.reconstruction import compute_reconstruction_margin
from nabu.cuda.utils import __has_cupy__
if __has_cupy__:
from nabu.pipeline.fullfield.accumulated_cuda import CudaAccumulatedPipeline
default_pipeline_cls = CudaAccumulatedPipeline
else:
from nabu.pipeline.fullfield.accumulated import AccumulatedPipeline
default_pipeline_cls = AccumulatedPipeline
DataPlaceHolder = namedtuple("DataPlaceHolder", ["shape"])
[docs]
def get_nabu_pipeline(
config_dict, dataset_info, projs_batch_size, pipeline_backend="cuda"
):
"""
Create a reconstruction pipeline object, based on configuration and scan info.
Parameters
----------
config_dict: dict
dict of dict, see 'default_nabu_config' for structure and keys.
dataset_info: nabu.resources.dataset.Dataset
Dataset structure, InMemoryDataset in this case
projs_batch_size: int
Number of projections processed at once ; i.e we trigger the pipeline as soon as we have 'projs_batch_size' in memory
"""
dataset_info.rotation_angles = np.zeros(projs_batch_size, dtype="f")
# Build the ProcessConfig data structure - this does extra checks and pre-calculates things for the pipeline
process_config = ProcessConfig(conf_dict=config_dict, dataset_info=dataset_info)
# Sub-region to be reconstructed.
# The full data volume has shape (n_z, n_y, n_x), but we might want to reconstruct a region of interest
# this is defined by start_{x|y|z} and end_{x|y|z} in the config file
# This holds for slice reconstruction. In this case we also need to compute the processing margin.
(start_z, end_z), margin_ud = get_reconstruction_z_region_and_margin(process_config)
sub_region = (
(0, projs_batch_size),
(start_z, end_z),
(0, dataset_info.radio_dims[0]),
)
margin = None if margin_ud is None else (margin_ud, (0, 0))
# Shape of the data volume that will be ingested by a pipeline instance,
# in the form (n_projections, n_vertic, n_horiz)
chunk_shape = (projs_batch_size,) + tuple(sr[1] - sr[0] for sr in sub_region[1:])
# Add place-holder for some fields, otherwise pipeline will not instantiate
dataset_info.data = DataPlaceHolder(chunk_shape)
dataset_info.current_projections_indices = np.zeros(
projs_batch_size, dtype=np.uint64
)
pipeline_cls = default_pipeline_cls
if pipeline_backend == "cuda" and __has_cupy__:
pipeline_cls = CudaAccumulatedPipeline
pipeline = pipeline_cls(process_config, chunk_shape, margin=margin)
pipeline.target_sub_region = sub_region
return pipeline
[docs]
def get_reconstruction_z_region_and_margin(process_config):
"""
Get parameters relevant for processing pipeline: sub-region to reconstruct, and processing margin.
Parameters
----------
process_config: nabu.pipeline.process_config.ProcessConfig object
Data structure with the processing configuration
Returns
-------
start_end_z: tuple of int
Tuple in the form (start_z, end_z), defining the start and stop of slices indices to be reconstructed
margin_ud: tuple of int
Tuple in the form (margin_up, margin_down): "processing margin" in the vertical direction.
Data will be cropped as: reconstruction[margin_up:-margin_down, ...]
"""
rec_region = process_config.rec_region
start_z = rec_region["start_z"]
end_z = (
rec_region["end_z"] + 1
) # cf difference between nabu cfg interface and Python
n_z, n_x = process_config.dataset_info.radio_dims[::-1]
is_reconstructing_full_volume = True
if start_z > 0 or end_z < n_z - 1:
is_reconstructing_full_volume = False
margin_up_down = None
if not (is_reconstructing_full_volume):
margin_v, margin_h = compute_reconstruction_margin(
process_config,
user_margin=process_config.nabu_config["pipeline"].get(
"processing_margin", None
),
)
new_start_z = max(start_z - margin_v, 0)
new_end_z = min(end_z + margin_v, n_z - 1)
margin_up_down = (start_z - new_start_z), (new_end_z - end_z)
start_z = new_start_z
end_z = new_end_z
return (start_z, end_z), margin_up_down