diff --git a/Wrappers/Python/cil/processors/FluxNormaliser.py b/Wrappers/Python/cil/processors/FluxNormaliser.py index 769f530e4..3eb3c7ecf 100644 --- a/Wrappers/Python/cil/processors/FluxNormaliser.py +++ b/Wrappers/Python/cil/processors/FluxNormaliser.py @@ -128,7 +128,6 @@ def check_input(self, dataset): return True - def _calculate_flux(self): ''' Function to calculate flux from a region of interest in the data. If the @@ -216,13 +215,16 @@ def _calculate_flux(self): if 0 in self.flux: raise ValueError('Flux value can\'t be 0, provide a different flux\ or region of interest with non-zero values') - - + def _calculate_target(self): ''' Calculate the target value for the normalisation ''' - if isinstance(self.target, float): + + if self.flux is None: + raise ValueError('Flux not found') + + if isinstance(self.target, (int,float)): self.target_value = self.target elif isinstance(self.target, str): if self.target == 'first': @@ -236,11 +238,9 @@ def _calculate_target(self): raise ValueError("Target string not recognised, found {}, expected 'first' or 'mean'" .format(self.target)) else: - raise TypeError("Target must be string or float, found {}" + raise TypeError("Target must be string or a number, found {}" .format(type(self.target))) - - def preview_configuration(self, angle=None, channel=None, log=False): ''' Preview the FluxNormalisation processor configuration for roi mode. @@ -406,18 +406,4 @@ def process(self, out=None): arr_proj *= self.target_value/f out.array.flat[i*proj_size:(i+1)*proj_size] = arr_proj - - # if 'angle' in data.dimension_labels: - # proj_axis = data.get_dimension_axis('angle') - # slice_proj = [slice(None)]*len(data.shape) - # slice_proj[proj_axis] = 0 - - # for i in range(len(data.geometry.angles)): - # if len(flux_size) > 0: - # f = self.flux[i] - # slice_proj[proj_axis] = i - # out.array[tuple(slice_proj)] = data.array[tuple(slice_proj)]*self.target_value/f - # else: - # out.array = data.array*self.target_value/f - - return out + return out \ No newline at end of file diff --git a/Wrappers/Python/test/test_DataProcessor.py b/Wrappers/Python/test/test_DataProcessor.py index 288b3a482..c75640ac8 100644 --- a/Wrappers/Python/test/test_DataProcessor.py +++ b/Wrappers/Python/test/test_DataProcessor.py @@ -3160,6 +3160,59 @@ def test_calculate_flux(self): with self.assertRaises(ValueError): processor._calculate_flux() + def test_calculate_target(self): + + # check target calculated with default method 'mean' + processor = FluxNormaliser(flux=1) + processor.set_input(self.data_cone) + processor._calculate_flux() + processor._calculate_target() + self.assertAlmostEqual(processor.target_value, 1) + + processor = FluxNormaliser(flux=numpy.linspace(1,3,len(self.data_cone.geometry.angles))) + processor.set_input(self.data_cone) + processor._calculate_flux() + processor._calculate_target() + self.assertAlmostEqual(processor.target_value, 2) + + # check target calculated with method 'first' + processor = FluxNormaliser(flux=1, target='first') + processor.set_input(self.data_cone) + processor._calculate_flux() + processor._calculate_target() + self.assertAlmostEqual(processor.target_value, 1) + + processor = FluxNormaliser(flux=numpy.linspace(1,3,len(self.data_cone.geometry.angles)), + target='first') + processor.set_input(self.data_cone) + processor._calculate_flux() + processor._calculate_target() + self.assertAlmostEqual(processor.target_value, 1) + + # check target calculated with float + processor = FluxNormaliser(flux=1, + target=55.0) + processor.set_input(self.data_cone) + processor._calculate_flux() + processor._calculate_target() + self.assertAlmostEqual(processor.target_value, 55.0) + + # check error if target is an unrecognised string + processor = FluxNormaliser(flux=1, + target='string') + processor.set_input(self.data_cone) + processor._calculate_flux() + with self.assertRaises(ValueError): + processor._calculate_target() + + # check error if target is not a string or floar + processor = FluxNormaliser(flux=1, + target={'string': 10}) + processor.set_input(self.data_cone) + processor._calculate_flux() + with self.assertRaises(TypeError): + processor._calculate_target() + @patch("matplotlib.pyplot.figure") def test_preview_configuration(self, mock_plot): # Test error in preview configuration if there is no roi @@ -3236,67 +3289,69 @@ def test_FluxNormaliser(self): data_norm_test.array[a,:,:]*= norm_value numpy.testing.assert_allclose(data_norm.array, data_norm_test.array, atol=1e-6) - # #Test roi with no norm_value - # roi = {'vertical':(0,10), 'horizontal':(0,10)} - # processor = FluxNormaliser(roi=roi) - # processor.set_input(self.data_cone) - # data_norm = processor.get_output() - # numpy.testing.assert_allclose(data_norm.array, self.data_cone.array) + # #Test roi with no target + roi = {'vertical':(0,10), 'horizontal':(0,10)} + processor = FluxNormaliser(roi=roi) + processor.set_input(self.data_cone) + data_norm = processor.get_output() + numpy.testing.assert_allclose(data_norm.array, self.data_cone.array) # #Test roi with norm_value - # roi = {'vertical':(0,10), 'horizontal':(0,10)} - # processor = FluxNormaliser(roi=roi, norm_value=5) - # processor.set_input(self.data_cone) - # data_norm = processor.get_output() - # numpy.testing.assert_allclose(data_norm.array, 5*self.data_cone.array) + roi = {'vertical':(0,10), 'horizontal':(0,10)} + processor = FluxNormaliser(roi=roi, target=5.0) + processor.set_input(self.data_cone) + data_norm = processor.get_output() + numpy.testing.assert_allclose(data_norm.array, 5*self.data_cone.array) # # Test roi with just one dimension - # roi = {'vertical':(0,2)} - # processor = FluxNormaliser(roi=roi, norm_value=5) - # processor.set_input(self.data_cone) - # data_norm = processor.get_output() - # numpy.testing.assert_allclose(data_norm.array, 5*self.data_cone.array) + roi = {'vertical':(0,2)} + processor = FluxNormaliser(roi=roi, target=5) + processor.set_input(self.data_cone) + data_norm = processor.get_output() + numpy.testing.assert_allclose(data_norm.array, 5*self.data_cone.array) # test roi with different data shapes and different flux values per projection - # for data in [self.data_cone, self.data_parallel, self.data_multichannel, - # self.data_slice, self.data_reorder]: - # roi = {'horizontal':(25,40)} - # processor = FluxNormaliser(roi=roi, norm_value=5) - # processor.set_input(data) - # data_norm = processor.get_output() - - # ax = data.get_dimension_axis('horizontal') - # slc = [slice(None)]*len(data.shape) - # slc[ax] = slice(25,40) - # axes=[ax] - # if 'vertical' in data.dimension_labels: - # axes.append(data.get_dimension_axis('vertical')) - # if 'channel' in data.dimension_labels: - # axes.append(data.get_dimension_axis('channel')) - # flux = numpy.mean(data.array[tuple(slc)], axis=tuple(axes)) - # slice_proj = [slice(None)]*len(data.shape) - # proj_axis = data.get_dimension_axis('angle') - # data_norm_test = data.copy() - # for i in range(len(data.geometry.angles)): - # f = flux[i] - # slice_proj[proj_axis] = i - # with numpy.errstate(divide='ignore', invalid='ignore'): - # data_norm_test.array[tuple(slice_proj)] = 5/f*data.array[tuple(slice_proj)] - # numpy.testing.assert_allclose(data_norm.array, data_norm_test.array, atol=1e-6, - # err_msg='Flux Normaliser roi test failed with data shape: ' + str(data.shape) + ' and configuration:\n' + str(data.geometry.config.system)) - - # data = self.data_single_angle - # processor = FluxNormaliser(roi=roi, norm_value=5) - # processor.set_input(data) - # data_norm = processor.get_output() - # ax = data.get_dimension_axis('horizontal') - # slc = [slice(None)]*len(data.shape) - # slc[ax] = slice(25,40) - # axes=[ax,data.get_dimension_axis('vertical')] - # flux = numpy.mean(data.array[tuple(slc)], axis=tuple(axes)) - - # numpy.testing.assert_allclose(data_norm.array, 5/flux*data.array, atol=1e-6, - # err_msg='Flux Normaliser roi test failed with data shape: ' + str(data.shape) + ' and configuration:\n' + str(data.geometry.config.system)) + for data in [ self.data_cone, self.data_parallel, self.data_multichannel, + self.data_slice, self.data_reorder]: + roi = {'horizontal':(25,40)} + processor = FluxNormaliser(roi=roi, target=5) + processor.set_input(data) + data_norm = processor.get_output() + + ax = data.get_dimension_axis('horizontal') + slc = [slice(None)]*len(data.shape) + slc[ax] = slice(25,40) + axes=[ax] + if 'vertical' in data.dimension_labels: + axes.append(data.get_dimension_axis('vertical')) + flux = numpy.mean(data.array[tuple(slc)], axis=tuple(axes)) + slice_proj = [slice(None)]*len(data.shape) + proj_axis = data.get_dimension_axis('angle') + data_norm_test = data.copy() + h_size = data.get_dimension_size('horizontal') + if 'vertical' in data.dimension_labels: + v_size = data.get_dimension_size('vertical') + else: + v_size = 1 + proj_size = h_size*v_size + for i in range(len(data.geometry.angles)*data.geometry.channels): + data_norm_test.array.flat[i*proj_size:(i+1)*proj_size] /=flux.flat[i] + data_norm_test.array.flat[i*proj_size:(i+1)*proj_size] *=5 + numpy.testing.assert_allclose(data_norm.array, data_norm_test.array, atol=1e-6, + err_msg='Flux Normaliser roi test failed with data shape: ' + str(data.shape) + ' and configuration:\n' + str(data.geometry.config.system)) + + data = self.data_single_angle + processor = FluxNormaliser(roi=roi, target=5) + processor.set_input(data) + data_norm = processor.get_output() + ax = data.get_dimension_axis('horizontal') + slc = [slice(None)]*len(data.shape) + slc[ax] = slice(25,40) + axes=[ax,data.get_dimension_axis('vertical')] + flux = numpy.mean(data.array[tuple(slc)], axis=tuple(axes)) + + numpy.testing.assert_allclose(data_norm.array, 5/flux*data.array, atol=1e-6, + err_msg='Flux Normaliser roi test failed with data shape: ' + str(data.shape) + ' and configuration:\n' + str(data.geometry.config.system)) if __name__ == "__main__":