Processors#

This module allows the user to manipulate or pre-process their data.

Data Manipulation#

These processors can be used on ImageData or AcquisitionData objects.

Data Slicer#

class cil.processors.Slicer(roi=None)[source]#

This creates a Slicer processor.

The processor will crop the data, and then return every n input pixels along a dimension from the starting index.

The output will be a data container with the data, and geometry updated to reflect the operation.

Parameters:

roi (dict) –

The region-of-interest to slice {‘axis_name1’:(start,stop,step), ‘axis_name2’:(start,stop,step)} The key being the axis name to apply the processor to, the value holding a tuple containing the ROI description

Start: Starting index of input data. Must be an integer, or None defaults to index 0. Stop: Stopping index of input data. Must be an integer, or None defaults to index N. Step: Number of pixels to average together. Must be an integer or None defaults to 1.

Example

>>> from cil.processors import Slicer
>>> roi = {'horizontal':(10,-10,2),'vertical':(10,-10,2)}
>>> processor = Slicer(roi)
>>> processor.set_input(data)
>>> data_sliced= processor.get_output()

Example

>>> from cil.processors import Slicer
>>> roi = {'horizontal':(None,None,2),'vertical':(None,None,2)}
>>> processor = Slicer(roi)
>>> processor.set_input(data.geometry)
>>> geometry_sliced = processor.get_output()

Note

The indices provided are start inclusive, stop exclusive.

All elements along a dimension will be included if the axis does not appear in the roi dictionary, or if passed as {‘axis_name’,-1}

If only one number is provided, then it is interpreted as Stop. i.e. {‘axis_name1’:(stop)} If two numbers are provided, then they are interpreted as Start and Stop i.e. {‘axis_name1’:(start, stop)}

Negative indexing can be used to specify the index. i.e. {‘axis_name1’:(10, -10)} will crop the dimension symmetrically

If Stop - Start is not multiple of Step, then the resulted dimension will have (Stop - Start) // Step elements, i.e. (Stop - Start) % Step elements will be ignored

set_input(dataset)[source]#

Set the input data or geometry to the processor

Parameters:

dataset (DataContainer, Geometry) – The input DataContainer or Geometry

process(out=None)[source]#

Processes the input data

Parameters:

out (ImageData, AcquisitionData, DataContainer, optional) – Fills the referenced DataContainer with the processed output and suppresses the return

Returns:

The downsampled output is returned. Depending on the input type this may be: ImageData, AcquisitionData, DataContainer, ImageGeometry, AcquisitionGeometry

Return type:

DataContainer

get_output(out=None)#

Runs the configured processor and returns the processed data

Parameters:

out (DataContainer, optional) – Fills the referenced DataContainer with the processed data

Returns:

The processed data

Return type:

DataContainer

Data Binner#

class cil.processors.Binner(roi=None, accelerated=True)[source]#

This creates a Binner processor.

The processor will crop the data, and then average together n input pixels along a dimension from the starting index.

The output will be a data container with the data, and geometry updated to reflect the operation.

Parameters:
  • roi (dict) –

    The region-of-interest to bin {‘axis_name1’:(start,stop,step), ‘axis_name2’:(start,stop,step)} The key being the axis name to apply the processor to, the value holding a tuple containing the ROI description

    Start: Starting index of input data. Must be an integer, or None defaults to index 0. Stop: Stopping index of input data. Must be an integer, or None defaults to index N. Step: Number of pixels to average together. Must be an integer or None defaults to 1.

  • accelerated (boolean, default=True) – Uses the CIL accelerated backend if True, numpy if False.

Example

>>> from cil.processors import Binner
>>> roi = {'horizontal':(10,-10,2),'vertical':(10,-10,2)}
>>> processor = Binner(roi)
>>> processor.set_input(data)
>>> data_binned = processor.get_output()

Example

>>> from cil.processors import Binner
>>> roi = {'horizontal':(None,None,2),'vertical':(None,None,2)}
>>> processor = Binner(roi)
>>> processor.set_input(data.geometry)
>>> geometry_binned = processor.get_output()

Note

The indices provided are start inclusive, stop exclusive.

All elements along a dimension will be included if the axis does not appear in the roi dictionary, or if passed as {‘axis_name’,-1}

If only one number is provided, then it is interpreted as Stop. i.e. {‘axis_name1’:(stop)} If two numbers are provided, then they are interpreted as Start and Stop i.e. {‘axis_name1’:(start, stop)}

Negative indexing can be used to specify the index. i.e. {‘axis_name1’:(10, -10)} will crop the dimension symmetrically

If Stop - Start is not multiple of Step, then the resulted dimension will have (Stop - Start) // Step elements, i.e. (Stop - Start) % Step elements will be ignored

get_output(out=None)#

Runs the configured processor and returns the processed data

Parameters:

out (DataContainer, optional) – Fills the referenced DataContainer with the processed data

Returns:

The processed data

Return type:

DataContainer

process(out=None)#

Processes the input data

Parameters:

out (ImageData, AcquisitionData, DataContainer, optional) – Fills the referenced DataContainer with the processed output and suppresses the return

Returns:

The downsampled output is returned. Depending on the input type this may be: ImageData, AcquisitionData, DataContainer, ImageGeometry, AcquisitionGeometry

Return type:

DataContainer

set_input(dataset)#

Set the input data or geometry to the processor

Parameters:

dataset (DataContainer, Geometry) – The input DataContainer or Geometry

Data Padder#

class cil.processors.Padder(mode='constant', pad_width=None, pad_values=0)[source]#

Processor to pad an array with a border, wrapping numpy.pad. See https://numpy.org/doc/stable/reference/generated/numpy.pad.html

It is recommended to use the static methods to configure your Padder object rather than initialising this class directly. See examples for details.

Parameters:
  • mode (str) – The method used to populate the border data. Accepts: ‘constant’, ‘edge’, ‘linear_ramp’, ‘reflect’, ‘symmetric’, ‘wrap’

  • pad_width (int, tuple, dict) – The size of the border along each axis, see usage notes

  • pad_values (float, tuple, dict, default=0.0) – The additional values needed by some of the modes

Notes

pad_width behaviour (number of pixels):
  • int: Each axis will be padded with a border of this size

  • tuple(int, int): Each axis will be padded with an asymmetric border i.e. (before, after)

  • dict: Specified axes will be padded: e.g. {‘horizontal’:(8, 23), ‘vertical’: 10}

pad_values behaviour:
  • float: Each border will use this value

  • tuple(float, float): Each value will be used asymmetrically for each axis i.e. (before, after)

  • dict: Specified axes and values: e.g. {‘horizontal’:(8, 23), ‘channel’:5}

If padding angles the angular values assigned to the padded axis will be extrapolated from the first two, and the last two angles in geometry.angles. The user should ensure the output is as expected.

Example

>>> processor = Padder.edge(pad_width=1)
>>> processor.set_input(data)
>>> data_padded = processor.get_output()
>>> print(data.array)
[[0. 1. 2.]
[3. 4. 5.]
[6. 7. 8.]]
>>> print(data_padded.array)
[[0. 0. 1. 2. 2.]
[0. 0. 1. 2. 2.]
[3. 3. 4. 5. 5.]
[6. 6. 7. 8. 8.]
[6. 6. 7. 8. 8.]]

Example

>>> processor = Padder.constant(pad_width={'horizontal_y':(1,1),'horizontal_x':(1,2)}, constant_values=(-1.0, 1.0))
>>> processor.set_input(data)
>>> data_padded = processor.get_output()
>>> print(data.array)
[[0. 1. 2.]
[3. 4. 5.]
[6. 7. 8.]]
>>> print(data_padded.array)
[[-1. -1. -1. -1.  1.  1.]
[-1.  0.  1.  2.  1.  1.]
[-1.  3.  4.  5.  1.  1.]
[-1.  6.  7.  8.  1.  1.]
[-1.  1.  1.  1.  1.  1.]
static constant(pad_width=None, constant_values=0.0)[source]#

Padder processor wrapping numpy.pad with mode constant.

Pads the data with a constant value border. Pads in all spatial dimensions unless a dictionary is passed to either pad_width or constant_values

Parameters:
  • pad_width (int, tuple, dict) – The size of the border along each axis, see usage notes

  • constant_values (float, tuple, dict, default=0.0) – The value of the border, see usage notes

Notes

pad_width behaviour (number of pixels):
  • int: Each axis will be padded with a border of this size

  • tuple(int, int): Each axis will be padded with an asymmetric border i.e. (before, after)

  • dict: Specified axes will be padded: e.g. {‘horizontal’:(8, 23), ‘vertical’: 10}

constant_values behaviour (value of pixels):
  • float: Each border will be set to this value

  • tuple(float, float): Each border value will be used asymmetrically for each axis i.e. (before, after)

  • dict: Specified axes and values: e.g. {‘horizontal’:(8, 23), ‘channel’:5}

If padding angles the angular values assigned to the padded axis will be extrapolated from the first two, and the last two angles in geometry.angles. The user should ensure the output is as expected.

Example

>>> processor = Padder.constant(pad_width=1, constant_values=0.0)
>>> processor.set_input(data)
>>> data_padded = processor.get_output()
>>> print(data.array)
[[0. 1. 2.]
[3. 4. 5.]
[6. 7. 8.]]
>>> print(data_padded.array)
[[0. 0. 0. 0. 0.]
[0. 0. 1. 2. 0.]
[0. 3. 4. 5. 0.]
[0. 6. 7. 8. 0.]
[0. 0. 0. 0. 0.]]
static edge(pad_width=None)[source]#

Padder processor wrapping numpy.pad with mode edge.

Pads the data by extending the edge values in to the border. Pads in all spatial dimensions unless a dictionary is passed to pad_width.

pad_width: int, tuple, dict

The size of the border along each axis, see usage notes

Notes

pad_width behaviour (number of pixels):
  • int: Each axis will be padded with a border of this size

  • tuple(int, int): Each axis will be padded with an asymmetric border i.e. (before, after)

  • dict: Specified axes will be padded: e.g. {‘horizontal’:(8, 23), ‘vertical’: 10}

If padding angles the angular values assigned to the padded axis will be extrapolated from the first two, and the last two angles in geometry.angles. The user should ensure the output is as expected.

Example

>>> processor = Padder.edge(pad_width=1)
>>> processor.set_input(data)
>>> data_padded = processor.get_output()
>>> print(data.array)
[[0. 1. 2.]
[3. 4. 5.]
[6. 7. 8.]]
>>> print(data_padded.array)
[[0. 0. 1. 2. 2.]
[0. 0. 1. 2. 2.]
[3. 3. 4. 5. 5.]
[6. 6. 7. 8. 8.]
[6. 6. 7. 8. 8.]]
static linear_ramp(pad_width=None, end_values=0.0)[source]#

Padder processor wrapping numpy.pad with mode linear_ramp

Pads the data with values calculated from a linear ramp between the array edge value and the set end_value. Pads in all spatial dimensions unless a dictionary is passed to either pad_width or constant_values

pad_width: int, tuple, dict

The size of the border along each axis, see usage notes

end_values: float, tuple, dict, default=0.0

The target value of the linear_ramp, see usage notes

Notes

pad_width behaviour (number of pixels):
  • int: Each axis will be padded with a border of this size

  • tuple(int, int): Each axis will be padded with an asymmetric border i.e. (before, after)

  • dict: Specified axes will be padded: e.g. {‘horizontal’:(8, 23), ‘vertical’: 10}

end_values behaviour:
  • float: Each border will use this end value

  • tuple(float, float): Each border end value will be used asymmetrically for each axis i.e. (before, after)

  • dict: Specified axes and end values: e.g. {‘horizontal’:(8, 23), ‘channel’:5}

If padding angles the angular values assigned to the padded axis will be extrapolated from the first two, and the last two angles in geometry.angles. The user should ensure the output is as expected.

Example

>>> processor = Padder.linear_ramp(pad_width=2, end_values=0.0)
>>> processor.set_input(data)
>>> data_padded = processor.get_output()
>>> print(data.array)
[[0. 1. 2.]
[3. 4. 5.]
[6. 7. 8.]]
>>> print(data_padded.array)
[[0.  0.  0.  0.  0.  0.  0. ]
[0.  0.  0.  0.5 1.  0.5 0. ]
[0.  0.  0.  1.  2.  1.  0. ]
[0.  1.5 3.  4.  5.  2.5 0. ]
[0.  3.  6.  7.  8.  4.  0. ]
[0.  1.5 3.  3.5 4.  2.  0. ]
[0.  0.  0.  0.  0.  0.  0. ]]
static reflect(pad_width=None)[source]#

Padder processor wrapping numpy.pad with mode reflect.

Pads with the reflection of the data mirrored along first and last values each axis. Pads in all spatial dimensions unless a dictionary is passed to pad_width.

pad_width: int, tuple, dict

The size of the border along each axis, see usage notes

Notes

pad_width behaviour (number of pixels):
  • int: Each axis will be padded with a border of this size

  • tuple(int, int): Each axis will be padded with an asymmetric border i.e. (before, after)

  • dict: Specified axes will be padded: e.g. {‘horizontal’:(8, 23), ‘vertical’: 10}

If padding angles the angular values assigned to the padded axis will be extrapolated from the first two, and the last two angles in geometry.angles. The user should ensure the output is as expected.

Example

>>> processor = Padder.reflect(pad_width=1)
>>> processor.set_input(data)
>>> data_padded = processor.get_output()
>>> print(data.array)
[[0. 1. 2.]
[3. 4. 5.]
[6. 7. 8.]]
>>> print(data_padded.array)
[[4. 3. 4. 5. 4.]
[1. 0. 1. 2. 1.]
[4. 3. 4. 5. 4.]
[7. 6. 7. 8. 7.]
[4. 3. 4. 5. 4.]]
static symmetric(pad_width=None)[source]#

Padder processor wrapping numpy.pad with mode symmetric.

Pads with the reflection of the data mirrored along the edge of the array. Pads in all spatial dimensions unless a dictionary is passed to pad_width.

Parameters:

pad_width (int, tuple, dict) – The size of the border along each axis

Notes

pad_width behaviour (number of pixels):
  • int: Each axis will be padded with a border of this size

  • tuple(int, int): Each axis will be padded with an asymmetric border i.e. (before, after)

  • dict: Specified axes will be padded: e.g. {‘horizontal’:(8, 23), ‘vertical’: 10}

If padding angles the angular values assigned to the padded axis will be extrapolated from the first two, and the last two angles in geometry.angles. The user should ensure the output is as expected.

Example

>>> processor = Padder.symmetric(pad_width=1)
>>> processor.set_input(data)
>>> data_padded = processor.get_output()
>>> print(data.array)
[[0. 1. 2.]
[3. 4. 5.]
[6. 7. 8.]]
>>> print(data_padded.array)
[[0. 0. 1. 2. 2.]
[0. 0. 1. 2. 2.]
[3. 3. 4. 5. 5.]
[6. 6. 7. 8. 8.]
[6. 6. 7. 8. 8.]]
static wrap(pad_width=None)[source]#

Padder processor wrapping numpy.pad with mode wrap.

Pads with the wrap of the vector along the axis. The first values are used to pad the end and the end values are used to pad the beginning. Pads in all spatial dimensions unless a dictionary is passed to pad_width.

Parameters:

pad_width (int, tuple, dict) – The size of the border along each axis

Notes

pad_width behaviour (number of pixels):
  • int: Each axis will be padded with a border of this size

  • tuple(int, int): Each axis will be padded with an asymmetric border i.e. (before, after)

  • dict: Specified axes will be padded: e.g. {‘horizontal’:(8, 23), ‘vertical’: 10}

If padding angles the angular values assigned to the padded axis will be extrapolated from the first two, and the last two angles in geometry.angles. The user should ensure the output is as expected.

Example

>>> processor = Padder.wrap(pad_width=1)
>>> processor.set_input(data)
>>> data_padded = processor.get_output()
>>> print(data.array)
[[0. 1. 2.]
[3. 4. 5.]
[6. 7. 8.]]
>>> print(data_padded.array)
[[8. 6. 7. 8. 6.]
[2. 0. 1. 2. 0.]
[5. 3. 4. 5. 3.]
[8. 6. 7. 8. 6.]
[2. 0. 1. 2. 0.]]
set_input(dataset)[source]#

Set the input data to the processor

Parameters:

dataset (DataContainer, Geometry) – The input DataContainer

get_output(out=None)#

Runs the configured processor and returns the processed data

Parameters:

out (DataContainer, optional) – Fills the referenced DataContainer with the processed data

Returns:

The processed data

Return type:

DataContainer

Mask Generator from Data#

class cil.processors.MaskGenerator(mode='special_values', threshold_value=(None, None), quantiles=(None, None), threshold_factor=3, window=5, axis=None)[source]#

Processor to detect outliers and return a mask with 0 where outliers were detected, and 1 for other pixels. Please use the desiried method to configure a processor for your needs.

static special_values(nan=True, inf=True)[source]#

This creates a MaskGenerator processor which generates a mask for inf and/or nan values.

Parameters:
  • nan (bool, default=True) – mask NaN values

  • inf (bool, default=True) – mask INF values

static threshold(min_val=None, max_val=None)[source]#

This creates a MaskGenerator processor which generates a mask for values outside boundaries

Parameters:
  • min_val (float, default=None) – lower boundary

  • max_val (float, default=None) – upper boundary

static quantile(min_quantile=None, max_quantile=None)[source]#

This creates a MaskGenerator processor which generates a mask for values outside boundaries

Parameters:
  • min_quantile (float, default=None) – lower quantile, 0-1

  • max_quantile (float, default=None) – upper quantile, 0-1

static mean(axis=None, threshold_factor=3, window=None)[source]#

This creates a MaskGenerator processor which generates a mask for values outside a multiple of standard-devaiations from the mean.

abs(A - mean(A)) < threshold_factor * std(A).

Parameters:
  • threshold_factor (float, default=3) – scale factor of standard-deviations to use as threshold

  • axis (int, string) – specify axis as int or from ‘dimension_labels’ to calculate mean. If no axis is specified then operates over flattened array.

  • window (int, default=None) – specify number of pixels to use in calculation of a rolling mean

static median(axis=None, threshold_factor=3, window=None)[source]#

This creates a MaskGenerator processor which generates a mask for values outside a multiple of median absolute deviation (MAD) from the mean.

abs(A - median(A)) < threshold_factor * MAD(A), MAD = c*median(abs(A-median(A))) where c=-1/(sqrt(2)*erfcinv(3/2))

Parameters:
  • threshold_factor (float, default=3) – scale factor of MAD to use as threshold

  • axis (int, string) – specify axis as int or from ‘dimension_labels’ to calculate mean. If no axis is specified then operates over flattened array.

  • window (int, default=None) – specify number of pixels to use in calculation of a rolling median

get_output(out=None)#

Runs the configured processor and returns the processed data

Parameters:

out (DataContainer, optional) – Fills the referenced DataContainer with the processed data

Returns:

The processed data

Return type:

DataContainer

set_input(dataset)#

Set the input data to the processor

Parameters:

input (DataContainer) – The input DataContainer

Data Masking#

class cil.processors.Masker(mask=None, mode='value', value=0, axis=None, method='linear')[source]#

Processor to fill missing values provided by mask. Please use the desired method to configure a processor for your needs.

Parameters:
  • mask (DataContainer, ImageData, AcquisitionData, numpy.ndarray) – A boolean array with the same dimensions as input, where ‘False’ represents masked values. Alternatively an integer array where 0 represents masked values, and any other value represents unmasked values. Mask can be generated using ‘MaskGenerator’ processor to identify outliers.

  • mode ({'value', 'mean', 'median', 'interpolate'}, default='value') – The method to fill in missing values

  • value (float, default=0) – Substitute all outliers with a specific value if method=’value’, otherwise discarded.

  • axis (str or int) – Specify axis as int or from ‘dimension_labels’ to calculate mean, median or interpolation (depending on mode) along that axis

  • method ({'linear', 'nearest', 'zeros', 'linear', 'quadratic', 'cubic', 'previous', 'next'}, default='linear') – Interpolation method to use.

static value(mask=None, value=0)[source]#

Returns a Masker that sets the masked values of the input data to the requested value.

Parameters:
  • mask (DataContainer, ImageData, AcquisitionData, numpy.ndarray) – A boolean array with the same dimensions as input, where ‘False’ represents masked values. Alternatively an integer array where 0 represents masked values, and any other value represents unmasked values. Mask can be generated using ‘MaskGenerator’ processor to identify outliers.

  • value (float, default=0) – Values to be assigned to missing elements

Return type:

Masker processor

static mean(mask=None, axis=None)[source]#

Returns a Masker that sets the masked values of the input data to the mean of the unmasked values across the array or axis.

Parameters:
  • mask (DataContainer, ImageData, AcquisitionData, numpy.ndarray) – A boolean array with the same dimensions as input, where ‘False’ represents masked values. Alternatively an integer array where 0 represents masked values, and any other value represents unmasked values. Mask can be generated using ‘MaskGenerator’ processor to identify outliers.

  • axis (str, int) – Specify axis as int or from ‘dimension_labels’ to calculate mean.

Return type:

Masker processor

static median(mask=None, axis=None)[source]#

Returns a Masker that sets the masked values of the input data to the median of the unmasked values across the array or axis.

Parameters:
  • mask (DataContainer, ImageData, AcquisitionData, numpy.ndarray) – A boolean array with the same dimensions as input, where ‘False’ represents masked values. Alternatively an integer array where 0 represents masked values, and any other value represents unmasked values. Mask can be generated using ‘MaskGenerator’ processor to identify outliers.

  • axis (str, int) – Specify axis as int or from ‘dimension_labels’ to calculate median.

Return type:

Masker processor

static interpolate(mask=None, axis=None, method='linear')[source]#

Returns a Masker that operates over the specified axis and uses 1D interpolation over remaining flattened array to fill in missing values.

Parameters:
  • mask (DataContainer, ImageData, AcquisitionData, numpy.ndarray) – A boolean array with the same dimensions as input, where ‘False’ represents masked values. Alternatively an integer array where 0 represents masked values, and any other value represents unmasked values. Mask can be generated using ‘MaskGenerator’ processor to identify outliers.

  • axis (str, int) – Specify axis as int or from ‘dimension_labels’ to loop over and perform 1D interpolation.

  • method ({'linear', 'nearest', 'zeros', 'linear', 'quadratic', 'cubic', 'previous', 'next'}, default='linear') – Interpolation method to use.

Return type:

Masker processor

get_output(out=None)#

Runs the configured processor and returns the processed data

Parameters:

out (DataContainer, optional) – Fills the referenced DataContainer with the processed data

Returns:

The processed data

Return type:

DataContainer

set_input(dataset)#

Set the input data to the processor

Parameters:

input (DataContainer) – The input DataContainer

Pre-processors#

These processors can be used with AcquisitionData objects

Centre Of Rotation Corrector#

In the ideal alignment of a CT instrument, the projection of the axis of rotation onto the detector coincides with the vertical midline of the detector. In practice this is hard to achieve due to misalignments and/or kinematic errors in positioning of CT instrument components. A slight offset of the center of rotation with respect to the theoretical position will contribute to the loss of resolution; in more severe cases, it will cause severe artifacts in the reconstructed volume (double-borders).

CentreOfRotationCorrector can be used to estimate the offset of center of rotation from the data.

CentreOfRotationCorrector supports both parallel and cone-beam geometry with 2 different algorithms:

  • Cross-correlation, is suitable for single slice parallel-beam geometry. It requires two projections 180 degree apart.

  • Image sharpness method, which maximising the sharpness of a reconstructed slice. It can be used on single

slice parallel-beam, and centre-slice of cone-beam geometry. For use only with datasets that can be reconstructed with FBP/FDK.

class cil.processors.CentreOfRotationCorrector(**attributes)[source]#

This class contains methods to create a CentreOfRotationCorrector processor using the desired algorithm.

static xcorrelation(slice_index='centre', projection_index=0, ang_tol=0.1)[source]#

This creates a CentreOfRotationCorrector processor using the cross-correlation algorithm.

For use on parallel-beam geometry it requires two projections 180 degree apart.

Parameters:
  • slice_index (int or str, optional) – An integer defining the vertical slice to run the algorithm on or string=’centre’ specifying the central slice should be used (default is ‘centre’)

  • projection_index (int or list/tuple of ints, optional) – An integer defining the index of the first projection the cross correlation algorithm will use, where the second projection is chosen as the projection closest to 180 degrees from this. Or a list/tuple of ints specifying the two indices to be used for cross correlation (default is 0)

  • ang_tol (float, optional) – The angular tolerance in degrees between the two input projections 180 degree gap (default is 0.1)

Example

>>> from cil.processors import CentreOfRotationCorrector
>>> processor = CentreOfRotationCorrector.xcorrelation('centre')
>>> processor.set_input(data)
>>> data_centred = processor.get_ouput()

Example

>>> from cil.processors import CentreOfRotationCorrector
>>> processor = CentreOfRotationCorrector.xcorrelation(slice_index=120)
>>> processor.set_input(data)
>>> processor.get_ouput(out=data)

Example

>>> from cil.processors import CentreOfRotationCorrector
>>> import logging
>>> logging.basicConfig(level=logging.WARNING)
>>> cil_log_level = logging.getLogger('cil.processors')
>>> cil_log_level.setLevel(logging.DEBUG)
>>> processor = CentreOfRotationCorrector.xcorrelation(slice_index=120)
>>> processor.set_input(data)
>>> data_centred = processor.get_output()

Note

setting logging to ‘debug’ will give you more information about the algorithm progress

static image_sharpness(slice_index='centre', backend='tigre', tolerance=0.005, search_range=None, initial_binning=None)[source]#

This creates a CentreOfRotationCorrector processor.

The processor will find the centre offset by maximising the sharpness of a reconstructed slice.

Can be used on single slice parallel-beam, and centre slice cone beam geometry. For use only with datasets that can be reconstructed with FBP/FDK.

Parameters:
  • slice_index (int, str, default='centre') – An integer defining the vertical slice to run the algorithm on. The special case slice ‘centre’ is the default.

  • backend ({'tigre', 'astra'}) – The backend to use for the reconstruction

  • tolerance (float, default=0.005) – The tolerance of the fit in pixels, the default is 1/200 of a pixel. This is a stopping criteria, not a statement of accuracy of the algorithm.

  • search_range (int) – The range in pixels to search either side of the panel centre. If None a quarter of the width of the panel is used.

  • initial_binning (int) – The size of the bins for the initial search. If None will bin the image to a step corresponding to <128 pixels. The fine search will be on unbinned data.

Example

from cil.processors import CentreOfRotationCorrector

processor = CentreOfRotationCorrector.image_sharpness(‘centre’, ‘tigre’) processor.set_input(data) data_centred = processor.get_output()

Example

from cil.processors import CentreOfRotationCorrector

processor = CentreOfRotationCorrector.image_sharpness(slice_index=120, ‘astra’) processor.set_input(data) processor.get_output(out=data)

Note

For best results data should be 360deg which leads to blurring with incorrect geometry. This method is unreliable on half-scan data with ‘tuning-fork’ style artifacts.

get_output(out=None)#

Runs the configured processor and returns the processed data

Parameters:

out (DataContainer, optional) – Fills the referenced DataContainer with the processed data

Returns:

The processed data

Return type:

DataContainer

set_input(dataset)#

Set the input data to the processor

Parameters:

input (DataContainer) – The input DataContainer

Data Normaliser#

class cil.processors.Normaliser(flat_field=None, dark_field=None, tolerance=1e-05)[source]#

Normalisation based on flat and dark

This processor read in a AcquisitionData and normalises it based on the instrument reading with and without incident photons or neutrons.

Input: AcquisitionData Parameter: 2D projection with flat field (or stack)

2D projection with dark field (or stack)

Output: AcquisitionDataSet

static estimate_normalised_error(projection, flat, dark, delta_flat, delta_dark)[source]#

returns the estimated relative error of the normalised projection

n = (projection - dark) / (flat - dark) Dn/n = (flat-dark + projection-dark)/((flat-dark)*(projection-dark))*(Df/f + Dd/d)

get_output(out=None)#

Runs the configured processor and returns the processed data

Parameters:

out (DataContainer, optional) – Fills the referenced DataContainer with the processed data

Returns:

The processed data

Return type:

DataContainer

set_input(dataset)#

Set the input data to the processor

Parameters:

input (DataContainer) – The input DataContainer

Transmission to Absorption Converter#

class cil.processors.TransmissionAbsorptionConverter(min_intensity=0.0, white_level=1.0)[source]#

Processor to convert from transmission measurements to absorption based on the Beer-Lambert law

Parameters:
  • white_level (float, optional) – A float defining incidence intensity in the Beer-Lambert law.

  • min_intensity (float, optional) – A float defining some threshold to avoid 0 in log, is applied after normalisation by white_level

Returns:

returns AcquisitionData, ImageData or DataContainer depending on input data type, return is suppressed if ‘out’ is passed

Return type:

AcquisitionData, ImageData or DataContainer

Processor first divides by white_level (default=1) and then take negative logarithm. Elements below threshold (after division by white_level) are set to threshold.

get_output(out=None)#

Runs the configured processor and returns the processed data

Parameters:

out (DataContainer, optional) – Fills the referenced DataContainer with the processed data

Returns:

The processed data

Return type:

DataContainer

set_input(dataset)#

Set the input data to the processor

Parameters:

input (DataContainer) – The input DataContainer

Absorption to Transmission Converter#

class cil.processors.AbsorptionTransmissionConverter(white_level=1)[source]#

Processor to convert from absorption measurements to transmission

Parameters:

white_level (float, optional) – A float defining incidence intensity in the Beer-Lambert law.

Returns:

returns AcquisitionData, ImageData or DataContainer depending on input data type

Return type:

AcquisitionData, ImageData or DataContainer

Processor first multiplies data by -1, then calculates exponent and scales result by white_level (default=1)

get_output(out=None)#

Runs the configured processor and returns the processed data

Parameters:

out (DataContainer, optional) – Fills the referenced DataContainer with the processed data

Returns:

The processed data

Return type:

DataContainer

set_input(dataset)#

Set the input data to the processor

Parameters:

input (DataContainer) – The input DataContainer

Ring Remover#

class cil.processors.RingRemover(decNum=4, wname='db10', sigma=1.5, info=True)[source]#

RingRemover Processor: Removes vertical stripes from a DataContainer(ImageData/AcquisitionData) using the algorithm in https://doi.org/10.1364/OE.17.008567

Parameters:
  • decNum (int) – Number of wavelet decompositions - increasing the number of decompositions, increases the strength of the ring removal but can alter the profile of the data

  • wname (str) – Name of wavelet filter from pywt e.g. ‘db1’ – ‘db35’, ‘haar’ - increasing the wavelet filter increases the strength of the ring removal, but also increases the computational effort

  • sigma (float) – Damping parameter in Fourier space - increasing sigma, increases the size of artefacts which can be removed

  • info (boolean) – Flag to enable print of ring remover end message

Returns:

Corrected ImageData/AcquisitionData 2D, 3D, multi-spectral 2D, multi-spectral 3D

Return type:

DataContainer

get_output(out=None)#

Runs the configured processor and returns the processed data

Parameters:

out (DataContainer, optional) – Fills the referenced DataContainer with the processed data

Returns:

The processed data

Return type:

DataContainer

set_input(dataset)#

Set the input data to the processor

Parameters:

input (DataContainer) – The input DataContainer

Paganin Processor#

class cil.processors.PaganinProcessor(delta=1, beta=0.01, energy=40000, energy_units='eV', full_retrieval=True, filter_type='paganin_method', pad=0, return_units='cm')[source]#

Processor to retrieve quantitative information from phase contrast images using the Paganin phase retrieval algorithm described in [1]

Parameters:
  • delta (float (optional)) – Real part of the deviation of the material refractive index from 1, where refractive index \(n = (1 - \delta) + i \beta\) energy- dependent refractive index information for x-ray wavelengths can be found at [2], default is 1

  • beta (float (optional)) – Complex part of the material refractive index, where refractive index \(n = (1 - \delta) + i \beta\) energy-dependent refractive index information for x-ray wavelengths can be found at [2], default is 1e-2

  • energy (float (optional)) – Energy of the incident photon, default is 40000

  • energy_units (string (optional)) – Energy units, default is ‘eV’

  • full_retrieval (bool, optional) – If True, perform the full phase retrieval and return the thickness. If False, return a filtered image, default is True

  • filter_type (string (optional)) – The form of the Paganin filter to use, either ‘paganin_method’ (default) or ‘generalised_paganin_method’ as described in [3]

  • pad (int (optional)) – Number of pixels to pad the image in Fourier space to reduce aliasing, default is 0

  • return_units (string (optional)) – The distance units to return the sample thickness in, must be one of ‘m’, ‘cm’, ‘mm’ or ‘um’. Only applies if full_retrieval=True (default is’cm’)

Returns:

AcquisitionData corrected for phase effects, retrieved sample thickness or (if full_retrieval=False) filtered data

Return type:

AcquisitionData

Example

>>> processor = PaganinProcessor(delta=5, beta=0.05, energy=18000)
>>> processor.set_input(data)
>>> thickness = processor.get_output()

Example

>>> processor = PaganinProcessor(delta=1,beta=10e2, full_retrieval=False)
>>> processor.set_input(data)
>>> filtered_image = processor.get_output()

Example

>>> processor = PaganinProcessor()
>>> processor.set_input(data)
>>> thickness = processor.get_output(override_filter={'alpha':10})
>>> phase_retrieved_image = thickness*processor.mu

Notes

This processor will work most efficiently using the cil data order with data.reorder(‘cil’)

Notes

This processor uses the phase retrieval algorithm described by Paganin et al. [1] to retrieve the sample thickness

\[T(x,y) = - \frac{1}{\mu}\ln\left (\mathcal{F}^{-1}\left (\frac{\mathcal{F}\left ( M^2I_{norm}(x, y,z = \Delta) \right )}{1 + \alpha\left ( k_x^2 + k_y^2 \right )} \right )\right ),\]

where

  • \(T\), is the sample thickness,

  • \(\mu = \frac{4\pi\beta}{\lambda}\) is the material linear

attenuation coefficient where \(\beta\) is the complex part of the material refractive index and \(\lambda=\frac{hc}{E}\) is the probe wavelength, - \(M\) is the magnification at the detector, - \(I_{norm}\) is the input image which is expected to be the normalised transmission data, - \(\Delta\) is the propagation distance, - \(\alpha = \frac{\Delta\delta}{\mu}\) is a parameter determining the strength of the filter to be applied in Fourier space where \(\delta\) is the real part of the deviation of the material refractive index from 1 - \(k_x, k_y = \left ( \frac{2\pi p}{N_xW}, \frac{2\pi q}{N_yW} \right )\) where \(p\) and \(q\) are co-ordinates in a Fourier mesh in the range \(-N_x/2\) to \(N_x/2\) for an image with size \(N_x, N_y\) and pixel size \(W\).

A generalised form of the Paganin phase retrieval method can be called using filter_type='generalised_paganin_method', which uses the form of the algorithm described in [2]

\[T(x,y) = -\frac{1}{\mu}\ln\left (\mathcal{F}^{-1}\left (\frac{ \mathcal{F}\left ( M^2I_{norm}(x, y,z = \Delta) \right )}{1 - \frac{2 \alpha}{W^2}\left ( \cos(Wk_x) + \cos(Wk_y) -2 \right )} \right ) \right )\]

The phase retrieval is valid under the following assumptions

  • used with paraxial propagation-induced phase contrast images which

can be assumed to be single-material locally - using intensity data which has been flat field corrected - and under the assumption that the Fresnel number \(F_N = W^2/(\lambda\Delta) >> 1\)

To apply a filter to images using the Paganin method, call full_retrieval=False. In this case the pre-scaling and conversion to absorption is not applied so the requirement to supply flat field corrected intensity data is relaxed,

\[I_{filt} = \mathcal{F}^{-1}\left (\frac{\mathcal{F}\left ( I(x, y,z = \Delta) \right )} {1 - \alpha\left ( k_x^2 + k_y^2 \right )} \right )\]

References

With thanks to colleagues at DTU for help with the initial implementation of the phase retrieval algorithm

set_input(dataset)[source]#

Set the input data to the processor

Parameters:

dataset (AcquisitionData) – The input AcquisitionData

get_output(out=None, override_geometry=None, override_filter=None)[source]#

Function to get output from the PaganinProcessor

Parameters:
  • out (DataContainer, optional) – Fills the referenced DataContainer with the processed data

  • override_geometry (dict, optional) – Geometry parameters to use in the phase retrieval if you want to over-ride values found in data.geometry. Specify parameters as a dictionary {'parameter':value} where parameter is 'magnification', 'propagation_distance' or 'pixel_size' and value is the new value to use. Specify distance parameters in the same units as return_units (default is cm).

  • override_filter (dict, optional) – Over-ride the filter parameters to use in the phase retrieval. Specify parameters as {'parameter':value} where parameter is 'delta', 'beta' or 'alpha' and value is the new value to use.

Returns:

AcquisitionData corrected for phase effects, retrieved sample thickness or (if full_retrieval=False) filtered data

Return type:

AcquisitionData

Example

>>> processor = PaganinProcessor(delta=5, beta=0.05, energy=18000)
>>> processor.set_input(data)
>>> thickness = processor.get_output()

Example

>>> processor = PaganinProcessor(delta=1,beta=10e2,
full_retrieval=False)
>>> processor.set_input(data)
>>> filtered_image = processor.get_output()

Example

>>> processor = PaganinProcessor()
>>> processor.set_input(data)
>>> thickness = processor.get_output(override_filter={'alpha':10})
>>> phase_retrieved_image = thickness*processor.mu

Notes

If 'alpha' is specified in override_filter the new value will be used and delta will be ignored but beta will still be used to calculate \(\mu = \frac{4\pi\beta}{\lambda}\) which is used for scaling the thickness, therefore it is only recommended to specify alpha when also using get_output(full_retrieval=False), or re-scaling the result by \(\mu\) e.g. thickness*processor.mu If alpha is not specified, it will be calculated \(\frac{\Delta\delta\lambda}{4\pi\beta}\)

Return Home