def test_parameter_validator_multiple_errors(): """ Test that multiple errors are correctly reported by the validator """ parameter_validator = ParameterValidator({ "a_number": Parameter( [range_inclusive_validator(1, 2), from_list_validator([1.5, 1.6])]), "b_number": Parameter(list_of_type_validator(str)), }) parameter_validator.validate({ "a_number": 1.5, "b_number": ["foo", "bar"] }) # OK with pytest.raises( ValidationError, match= r"^Parameter a_number with value 0.9 does not meet the following criterion: " r"Value\(s\) must be between 1 and 2 \(inclusive\)\. Parameter a_number with value 0.9 " r"does not meet the following criterion: Value must be in \[1.5, 1.6\]\. " r"Parameter b_number with value \[1, 2\] does not meet the following criterion: " r"Value must be a list of type str$", ): parameter_validator.validate({"a_number": 0.9, "b_number": [1, 2]})
def test_range_inclusive_validator(): """ Test the inclusive validator with some values """ validator = range_inclusive_validator(-1, 1) assert str(validator) == "Value(s) must be between -1 and 1 (inclusive)" assert validator(-0.99) assert validator(0.99) assert validator(-1) assert validator(1) assert not validator(-1.01) assert not validator(1.01) assert not validator("not a number")
def test_parameter_validator_multiple_validators(): """ Test the parameter validator with multiple validators """ parameter_validator = ParameterValidator({ "a_number": Parameter( [range_inclusive_validator(1, 2), from_list_validator([1.5, 1.6])]) }) parameter_validator.validate({"a_number": 1.5}) # OK with pytest.raises( ValidationError, match= r"Parameter a_number with value 1.7 does not meet the following " r"criterion: Value must be in \[1.5, 1.6\]", ): parameter_validator.validate({"a_number": 1.7})
def test_range_inclusive_validator_image_container(): """ Test the inclusive validator with an image container """ validator = range_inclusive_validator(-1, 1) assert str(validator) == "Value(s) must be between -1 and 1 (inclusive)" image_container = NumpyImageContainer( image=np.array([[-0.5, 0.2], [0.1, -0.9]])) assert validator(image_container) image_container = NumpyImageContainer( image=np.array([[-1.0, 0.2], [0.1, -0.9]])) assert validator(image_container) image_container = NumpyImageContainer( image=np.array([[-0.5, 0.2], [1, -0.9]])) assert validator(image_container) image_container = NumpyImageContainer( image=np.array([[-0.5, 0.2], [1.1, -0.9]])) assert not validator(image_container) image_container = NumpyImageContainer( image=np.array([[-1.1, 0.2], [1, -0.9]])) assert not validator(image_container)
def test_range_inclusive_validator_creator(): """Check the inclusive validator creator raises errors when start >end""" with pytest.raises(ValueError): range_inclusive_validator(2, 1)
STRUCTURAL = "structural" SUPPORTED_IMAGE_TYPES = [ASL, GROUND_TRUTH, STRUCTURAL] # Supported asl contexts M0SCAN = "m0scan" CONTROL = "control" LABEL = "label" SUPPORTED_ASL_CONTEXTS = [M0SCAN, CONTROL, LABEL] # Input validator IMAGE_TYPE_VALIDATOR = { GROUND_TRUTH: ParameterValidator( parameters={ ROT_X: Parameter(validators=range_inclusive_validator(-180.0, 180.0), default_value=0.0), ROT_Y: Parameter(validators=range_inclusive_validator(-180.0, 180.0), default_value=0.0), ROT_Z: Parameter(validators=range_inclusive_validator(-180.0, 180.0), default_value=0.0), TRANSL_X: Parameter(validators=range_inclusive_validator(-1000.0, 1000.0), default_value=0.0), TRANSL_Y: Parameter(validators=range_inclusive_validator(-1000.0, 1000.0), default_value=0.0), TRANSL_Z: Parameter(validators=range_inclusive_validator(-1000.0, 1000.0),
def _validate_inputs(self): """ Checks that the inputs meet their validation criteria `'rotation'` (optional) must be a Tuple of floats of length 3, each value -180 to 180 inclusive,default (optional) = (0.0, 0.0, 0.0) `'rotation_origin'` (optional) must be a Tuple of floats of length 3, default = (0.0, 0.0, 0.0) `'translation'` (optional) must be a Tuple of floats of length 3, default = (0.0, 0.0, 0.0) `'scale'` (optional) must be a Tuple of floats of length 3, default = (1.0, 1.0, 1.0) `'affine'` (optional) must be a numpy.ndarray of shape (4,4), default = numpy.eye(4) """ input_validator = ParameterValidator( parameters={ self.KEY_ROTATION: Parameter( validators=[ isinstance_validator(tuple), for_each_validator(isinstance_validator(float)), for_each_validator(range_inclusive_validator( -180, 180)), ], optional=True, default_value=(0.0, 0.0, 0.0), ), self.KEY_ROTATION_ORIGIN: Parameter( validators=[ isinstance_validator(tuple), for_each_validator(isinstance_validator(float)), ], optional=True, default_value=(0.0, 0.0, 0.0), ), self.KEY_TRANSLATION: Parameter( validators=[ isinstance_validator(tuple), for_each_validator(isinstance_validator(float)), ], optional=True, default_value=(0.0, 0.0, 0.0), ), self.KEY_SCALE: Parameter( validators=[ isinstance_validator(tuple), for_each_validator(isinstance_validator(float)), ], optional=True, default_value=(1.0, 1.0, 1.0), ), self.KEY_AFFINE: Parameter( validators=[isinstance_validator(np.ndarray)], optional=True, default_value=np.eye(4), ), self.KEY_AFFINE_LAST: Parameter( validators=[isinstance_validator(np.ndarray)], optional=True, default_value=np.eye(4), ), }) # validate, returning a dictionary which also includes default parameters new_params = input_validator.validate( self.inputs, error_type=FilterInputValidationError) # Further validation that can't be handled by the parameter validator # Check that AffineMatrixFilter.KEY_AFFINE is of size 4x4 if new_params[self.KEY_AFFINE].shape != (4, 4): raise FilterInputValidationError # Check that AffineMatrixFilter.KEY_AFFINE_LAST is of size 4x4 if new_params[self.KEY_AFFINE_LAST].shape != (4, 4): raise FilterInputValidationError # Check that the tuple AffineMatrixFilter.KEY_ROTATION's length is 3 if len(new_params[self.KEY_ROTATION]) != 3: raise FilterInputValidationError # Check that the tuple AffineMatrixFilter.KEY_ROTATION_ORIGIN's length is 3 if len(new_params[self.KEY_ROTATION_ORIGIN]) != 3: raise FilterInputValidationError # Check that the tuple AffineMatrixFilter.KEY_TRANSLATION's length is 3 if len(new_params[self.KEY_TRANSLATION]) != 3: raise FilterInputValidationError # Check that the tuple AffineMatrixFilter.KEY_SCALE's length is 3 if len(new_params[self.KEY_SCALE]) != 3: raise FilterInputValidationError # merge the updated parameters from the output with the input parameters self.inputs = {**self._i, **new_params}
def _validate_inputs(self): """Checks that the inputs meet their validation criteria 'perfusion_rate' must be derived from BaseImageContainer and be >= 0 'transit_time' must be derived from BaseImageContainer and be >= 0 'm0' must be either a float or derived from BaseImageContainer and be >= 0 'label_type' must be a string and equal to "CASL" OR "pCASL" OR "PASL" 'label_duration" must be a float between 0 and 100 'signal_time' must be a float between 0 and 100 'label_efficiency' must be a float between 0 and 1 'lambda_blood_brain' must be a float between 0 and 1 't1_arterial_blood' must be a float between 0 and 100 all BaseImageContainers supplied should be the same dimensions """ input_validator = ParameterValidator( parameters={ self.KEY_PERFUSION_RATE: Parameter(validators=[ greater_than_equal_to_validator(0), isinstance_validator(BaseImageContainer), ]), self.KEY_TRANSIT_TIME: Parameter(validators=[ greater_than_equal_to_validator(0), isinstance_validator(BaseImageContainer), ]), self.KEY_M0: Parameter(validators=[ greater_than_equal_to_validator(0), isinstance_validator((BaseImageContainer, float)), ]), self.KEY_T1_TISSUE: Parameter(validators=[ range_inclusive_validator(0, 100), isinstance_validator(BaseImageContainer), ]), self.KEY_LABEL_TYPE: Parameter(validators=from_list_validator( [self.CASL, self.PCASL, self.PASL], case_insensitive=True)), self.KEY_LABEL_DURATION: Parameter(validators=[ range_inclusive_validator(0, 100), isinstance_validator(float), ]), self.KEY_SIGNAL_TIME: Parameter(validators=[ range_inclusive_validator(0, 100), isinstance_validator(float), ]), self.KEY_LABEL_EFFICIENCY: Parameter(validators=[ range_inclusive_validator(0, 1), isinstance_validator(float), ]), self.KEY_LAMBDA_BLOOD_BRAIN: Parameter(validators=[ range_inclusive_validator(0, 1), isinstance_validator(float), ]), self.KEY_T1_ARTERIAL_BLOOD: Parameter(validators=[ range_inclusive_validator(0, 100), isinstance_validator(float), ]), }) input_validator.validate(self.inputs, error_type=FilterInputValidationError) # Check that all the input images are all the same dimensions input_keys = self.inputs.keys() keys_of_images = [ key for key in input_keys if isinstance(self.inputs[key], BaseImageContainer) ] list_of_image_shapes = [ self.inputs[key].shape for key in keys_of_images ] if list_of_image_shapes.count( list_of_image_shapes[0]) != len(list_of_image_shapes): raise FilterInputValidationError([ "Input image shapes do not match.", [ f"{keys_of_images[i]}: {list_of_image_shapes[i]}, " for i in range(len(list_of_image_shapes)) ], ])
def _validate_inputs(self): """Checks that the inputs meet their validation criteria `'object'` must be derived from BaseImageContainer `'target_shape'` (optional)must be a Tuple of ints of length 3, values > 0 `'rotation'` (optional) must be a Tuple of floats of length 3, each value -180 to 180 inclusive, default (optional) = (0.0, 0.0, 0.0) `'rotation_origin'` (optional) must be a Tuple of floats of length 3, default = (0.0, 0.0, 0.0) `'translation'` (optional) must be a Tuple of floats of length 3, default = (0.0, 0.0, 0.0) """ input_validator = ParameterValidator( parameters={ self.KEY_IMAGE: Parameter(validators=isinstance_validator(BaseImageContainer)), self.KEY_ROTATION: Parameter( validators=[ isinstance_validator(tuple), for_each_validator(isinstance_validator(float)), for_each_validator(range_inclusive_validator( -180, 180)), ], optional=True, default_value=(0.0, 0.0, 0.0), ), self.KEY_ROTATION_ORIGIN: Parameter( validators=[ isinstance_validator(tuple), for_each_validator(isinstance_validator(float)), ], optional=True, default_value=(0.0, 0.0, 0.0), ), self.KEY_TRANSLATION: Parameter( validators=[ isinstance_validator(tuple), for_each_validator(isinstance_validator(float)), ], optional=True, default_value=(0.0, 0.0, 0.0), ), self.KEY_TARGET_SHAPE: Parameter( validators=[ isinstance_validator(tuple), for_each_validator(isinstance_validator(int)), for_each_validator(greater_than_validator(0)), ], optional=True, default_value=(9999, 9999, 9999), ), }) # validate, returning a dictionary which also includes default parameters new_params = input_validator.validate( self.inputs, error_type=FilterInputValidationError) # Further validation that can't be handled by the parameter validator if new_params[self.KEY_TARGET_SHAPE] == (9999, 9999, 9999): new_params[self.KEY_TARGET_SHAPE] = self.inputs[ self.KEY_IMAGE].shape # Check that the tuple self.KEY_ROTATION's length is 3 if len(new_params[self.KEY_ROTATION]) != 3: raise FilterInputValidationError # Check that the tuple self.KEY_ROTATION_ORIGIN's length is 3 if len(new_params[self.KEY_ROTATION_ORIGIN]) != 3: raise FilterInputValidationError # Check that the tuple self.KEY_TRANSLATION's length is 3 if len(new_params[self.KEY_TRANSLATION]) != 3: raise FilterInputValidationError # Check that the tuple self.KEY_SCALE's length is 3 if len(new_params[self.KEY_TARGET_SHAPE]) != 3: raise FilterInputValidationError # merge the updated parameters from the output with the input parameters self.inputs = {**self._i, **new_params}