Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow integer or boolean mask to be passed to Masker #1532

Merged
merged 17 commits into from
Feb 27, 2024
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
* x.x.x
- Check that mask input to Masker is of type 'bool'

* 23.1.0
- Fix bug in IndicatorBox proximal_conjugate
Expand Down Expand Up @@ -113,7 +115,7 @@
- Recon.FBP allows 'astra' backend
- Fixed PowerMethod for square/non-square, complex/float matrices with stopping criterion.
- CofR image_sharpness improved for large datasets
- Geometry alignmentment fix for 2D datasets
- Geometry alignment fix for 2D datasets
- CGLS update for sapyb to enable complex data, bugfix in use of initial
- added sapyb and deprecated axpby. All algorithm updated to use sapyb.
- Allow use of square brackets in file paths to TIFF and Nikon datasets
Expand Down Expand Up @@ -156,7 +158,7 @@
- Fixed bug in Zeiss reader geometry direction of rotation

* 21.0.0
- Show2D now takes 4D datasets and slice infomation as input
- Show2D now takes 4D datasets and slice information as input
- TIGRE reconstruction package wrapped for cone-beam tomography
- Datacontainers have get_slice method which returns a dataset with a single slice of the data
- Datacontainers have reorder method which reorders the data in memory as requested, or for use with 'astra' or 'tigre'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ def xcorrelation(slice_index='centre', projection_index=0, ang_tol=0.1):

>>> processor = CentreOfRotationCorrector.xcorrelation(slice_index=120)
>>> processor.set_input(data)
>>> data_centred = processor.get_ouput()
>>> data_centred = processor.get_output()


Note
Expand Down
53 changes: 29 additions & 24 deletions Wrappers/Python/cil/processors/Masker.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,28 @@

class Masker(DataProcessor):
r'''
Processor to fill missing values provided by mask. Please use the desiried method to configure a processor for your needs.
Processor to fill missing values provided by mask.
paskino marked this conversation as resolved.
Show resolved Hide resolved
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.
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
lauramurgatroyd marked this conversation as resolved.
Show resolved Hide resolved
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.

Returns
-------
DataContainer or it's subclass with masked outliers

'''

@staticmethod
Expand Down Expand Up @@ -71,13 +92,13 @@ def median(mask=None, axis=None):

@staticmethod
def interpolate(mask=None, axis=None, method='linear'):
r'''This operates over the specified axis and uses 1D interpolation over remaining flattened array to fill in missing vaues.
r'''This operates over the specified axis and uses 1D interpolation over remaining flattened array to fill in missing values.
lauramurgatroyd marked this conversation as resolved.
Show resolved Hide resolved

:param mask: A boolean array with the same dimensions as input, where 'False' represents masked values. Mask can be generated using 'MaskGenerator' processor to identify outliers.
:type mask: DataContainer, ImageData, AcquisitionData, numpy.ndarray
:param axis: specify axis as int or from 'dimension_labels' to loop over and perform 1D interpolation.
:type axis: str, int
:param method: One of the following interpoaltion methods: linear, nearest, zeros, linear, quadratic, cubic, previous, next
:param method: One of the following interpolation methods: linear, nearest, zeros, linear, quadratic, cubic, previous, next
:param method: str, default='linear'
'''

Expand All @@ -91,22 +112,6 @@ def __init__(self,
value = 0,
axis = None,
method = 'linear'):

r'''Processor to fill missing values provided by mask.

:param mask: A boolean array with the same dimensions as input, where 'False' represents masked values. Mask can be generated using 'MaskGenerator' processor to identify outliers.
:type mask: DataContainer, ImageData, AcquisitionData, numpy.ndarray
:param mode: a method to fill in missing values (value, mean, median, interpolate)
:type mode: str, default=value
:param value: substitute all outliers with a specific value
:type value: float, default=0
:param axis: specify axis as int or from 'dimension_labels' to calculate mean or median in respective modes
:type axis: str or int
:param method: One of the following interpoaltion methods: linear, nearest, zeros, linear, quadratic, cubic, previous, next
:param method: str, default='linear'
:return: DataContainer or it's subclass with masked outliers
:rtype: DataContainer or it's subclass
'''

kwargs = {'mask': mask,
'mode': mode,
Expand Down Expand Up @@ -153,11 +158,11 @@ def process(self, out=None):
except:
mask_arr = self.mask

try:
mask_invert = ~mask_arr
except TypeError:
if mask_arr.dtype != bool:
raise TypeError("Mask expected to be a boolean array got {}".format(mask_arr.dtype))
lauramurgatroyd marked this conversation as resolved.
Show resolved Hide resolved
lauramurgatroyd marked this conversation as resolved.
Show resolved Hide resolved


mask_invert = ~mask_arr

try:
axis_index = data.dimension_labels.index(self.axis)
except:
Expand Down Expand Up @@ -199,7 +204,7 @@ def process(self, out=None):
elif self.mode == 'interpolate':
if self.method not in ['linear', 'nearest', 'zeros', 'linear', \
'quadratic', 'cubic', 'previous', 'next']:
raise TypeError("Wrong interpolation method, one of the follwoing is expected:\n" +
raise TypeError("Wrong interpolation method, one of the following is expected:\n" +
"linear, nearest, zeros, linear, quadratic, cubic, previous, next")

ndim = data.number_of_dimensions
Expand Down
2 changes: 1 addition & 1 deletion Wrappers/Python/cil/processors/Normaliser.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ class Normaliser(Processor):
Input: AcquisitionData
Parameter: 2D projection with flat field (or stack)
2D projection with dark field (or stack)
Output: AcquisitionDataSetn
Output: AcquisitionDataSet
'''

def __init__(self, flat_field = None, dark_field = None, tolerance = 1e-5):
Expand Down
9 changes: 9 additions & 0 deletions Wrappers/Python/test/test_DataProcessor.py
Original file line number Diff line number Diff line change
Expand Up @@ -2521,12 +2521,21 @@ def setUp(self):
self.mask_manual = DataContainer(mask_manual, dimension_labels=self.data.dimension_labels)
self.mask_generated = MaskGenerator.special_values()(self.data)

# make a copy of mask_manual with 1s and 0s instead of bools:
mask_int_manual = mask_manual.astype(numpy.int32)
self.mask_int_manual = DataContainer(mask_int_manual, dimension_labels=self.data.dimension_labels)


def test_Masker_Manual(self):
self.Masker_check(self.mask_manual, self.data, self.data_init)

def test_Masker_generated(self):
self.Masker_check(self.mask_generated, self.data, self.data_init)

def test_Masker_doesnt_accept_integer_masks(self):
with self.assertRaises(TypeError):
self.Masker_check(mask=self.mask_int_manual)

def Masker_check(self, mask, data, data_init):

# test vaue mode
Expand Down