Source code for mantidimaging.core.tools.tomopy_tool
# Copyright (C) 2022 ISIS Rutherford Appleton Laboratory UKRI
# SPDX - License - Identifier: GPL-3.0-or-later
from logging import getLogger
import numpy as np
from mantidimaging import helper as h
from mantidimaging.core.tools.abstract_tool import AbstractTool
from mantidimaging.core.utility.progress_reporting import Progress
from mantidimaging.core.utility import projection_angles
[docs]
def run_reconstruct(sample, config, proj_angles=None, **kwargs):
"""
Module function for running a reconstruction.
It will create the tomopy tool object at runtime.
:param sample: The sample image data as a 3D numpy.ndarray
:param config: A ReconstructionConfig with all the necessary parameters
to run a reconstruction. The Centers of Rotation
must be interpolated independently!
:param proj_angles: The projection angle for each slice.
If not provided equidistant angles will be generated
:param kwargs: Any keyword arguments will be forwarded to the TomoPy
reconstruction function
:return: The reconstructed volume
"""
tool = TomoPyTool()
return tool.run_reconstruct(sample, config, proj_angles, **kwargs)
[docs]
class TomoPyTool(AbstractTool):
[docs]
@staticmethod
def tool_supported_methods():
return [
'art', 'bart', 'fbp', 'gridrec', 'mlem', 'osem', 'ospml_hybrid', 'ospml_quad', 'pml_hybrid', 'pml_quad',
'sirt'
]
[docs]
@staticmethod
def check_algorithm_compatibility(algorithm):
if algorithm not in TomoPyTool.tool_supported_methods():
raise ValueError("The selected algorithm {0} is not supported by TomoPy.".format(algorithm))
def __init__(self):
AbstractTool.__init__(self)
self._tomopy = self.import_self()
import tomopy.prep
import tomopy.recon
import tomopy.misc
# pretend we have the functions
self.find_center = self._tomopy.find_center
self.find_center_vo = self._tomopy.find_center_vo
self.circ_mask = self._tomopy.circ_mask
# make all tomopy methods available
self.misc = tomopy.misc
self.prep = tomopy.prep
self.recon = tomopy.recon
[docs]
def import_self(self):
try:
import tomopy
import tomopy.prep
import tomopy.recon
import tomopy.misc
except ImportError as exc:
raise ImportError("Could not import the tomopy package and its subpackages. " "Details: {0}".format(exc))
return tomopy
[docs]
def run_reconstruct(self, sample, config, proj_angles=None, progress=None, **kwargs):
"""
Run a reconstruction with TomoPy, using the CPU algorithms they
provide.
Information for each reconstruction method is available at
http://tomopy.readthedocs.io/en/latest/api/tomopy.recon.algorithm.html
:param sample: The sample image data as a 3D numpy.ndarray
:param config: A ReconstructionConfig with all the necessary parameters
to run a reconstruction. The Centers of Rotation
must be interpolated independently!
:param proj_angles: The projection angle for each slice.
If not provided equidistant angles will be
generated
:param kwargs: Any keyword arguments will be forwarded to the TomoPy
reconstruction function
:return: The reconstructed volume
"""
progress = Progress.ensure_instance(progress, task_name='TomoPy')
log = getLogger(__name__)
h.check_data_stack(sample)
if proj_angles is None:
num_radiograms = sample.shape[1]
proj_angles = projection_angles.generate(config.func.max_angle, num_radiograms).value
alg = config.func.algorithm
num_iter = config.func.num_iter
cores = config.func.cores
cors = config.func.cors
assert len(cors) == sample.shape[0],\
"The provided number of CORs does not match the slice number! \
A Center of rotation must be provided for each slice. Usually \
that is done via core.utility.cor_interpolate"
iterative_algorithm = False if alg in ['gridrec', 'fbp'] else True
with progress:
if iterative_algorithm: # run the iterative algorithms
progress.update(msg="Iterative method with TomoPy")
log.info("Avg Center of Rotation: {0}, Algorithm: {1}, number "
"of iterations: {2}...".format(np.mean(cors), alg, num_iter))
kwargs = dict(kwargs, num_iter=num_iter)
else: # run the non-iterative algorithms
progress.update(msg="Non-iterative method with TomoPy")
log.info("Mean COR: {0}, Number of CORs provided {1}, "
"Algorithm: {2}...".format(np.mean(cors), len(cors), alg))
# TODO need to expose the operations to CLI
# filter_name='parzen',
# filter_par=[5.],
recon = self._tomopy.recon(tomo=sample,
theta=proj_angles,
center=cors,
ncore=cores,
algorithm=alg,
sinogram_order=True,
**kwargs)
log.info("Reconstructed 3D volume. Shape: {0}, and pixel data type: " "{1}.".format(recon.shape, recon.dtype))
return recon