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)
Exemple #6
0
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),
Exemple #7
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}