From ea96ba676d94bc6fd5f582f7c1bf17c8cef4dc26 Mon Sep 17 00:00:00 2001 From: TanguyJeanneau Date: Tue, 4 Jun 2024 13:31:37 +0000 Subject: [PATCH 01/32] Add an iris_code_version attribute to IrisFilterResponse and IrisTemplate --- src/iris/io/dataclasses.py | 6 +++++- src/iris/io/validators.py | 11 +++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/src/iris/io/dataclasses.py b/src/iris/io/dataclasses.py index f9beac2..7adf650 100644 --- a/src/iris/io/dataclasses.py +++ b/src/iris/io/dataclasses.py @@ -539,10 +539,12 @@ class IrisFilterResponse(ImmutableModel): iris_responses: List[np.ndarray] mask_responses: List[np.ndarray] + iris_code_version: str _responses_mask_shape_match = root_validator(pre=True, allow_reuse=True)( v.are_all_shapes_equal("iris_responses", "mask_responses") ) + _iris_code_version_check = validator("iris_code_version", allow_reuse=True)(v.iris_code_version_check) def serialize(self) -> Dict[str, List[np.ndarray]]: """Serialize IrisFilterResponse object. @@ -570,11 +572,13 @@ class IrisTemplate(ImmutableModel): iris_codes: List[np.ndarray] mask_codes: List[np.ndarray] + iris_code_version: str _responses_mask_shape_match = root_validator(pre=True, allow_reuse=True)( v.are_all_shapes_equal("iris_codes", "mask_codes") ) - _is_binary = validator("*", allow_reuse=True, each_item=True)(v.is_binary) + _is_binary = validator("iris_codes", "mask_codes", allow_reuse=True, each_item=True)(v.is_binary) + _iris_code_version_check = validator("iris_code_version", allow_reuse=True)(v.iris_code_version_check) def serialize(self) -> Dict[str, np.ndarray]: """Serialize IrisTemplate object. diff --git a/src/iris/io/validators.py b/src/iris/io/validators.py index 9eca17c..eadf2be 100644 --- a/src/iris/io/validators.py +++ b/src/iris/io/validators.py @@ -1,8 +1,12 @@ +import re from typing import Any, Callable, Dict, Iterable, List import numpy as np from pydantic import fields +from iris.io.errors import IRISPipelineError + + # ----- validators ----- @@ -137,6 +141,13 @@ def are_all_positive(cls: type, v: Any, field: fields.ModelField) -> Any: return v +def iris_code_version_check(cls: type, v: str, field: fields.ModelField) -> str: + """Check if the version provided in the input config matches the current iris.__version__.""" + if not re.match("v[\d]+\.[\d]+$", v): + raise IRISPipelineError(f"Wrong iris code version. Expected standard version nuber, received {v}") + return v + + def to_dtype_float32(cls: type, v: np.ndarray, field: fields.ModelField) -> np.ndarray: """Convert input np.ndarray to dtype np.float32. From 50db406a7e3428e0329bb7d198b96cb75c6a293f Mon Sep 17 00:00:00 2001 From: TanguyJeanneau Date: Tue, 4 Jun 2024 13:36:10 +0000 Subject: [PATCH 02/32] Adapt ConvFilterBank to produce iris_code_version + unit tests --- src/iris/nodes/iris_response/conv_filter_bank.py | 15 +++++++++++---- .../iris_response/test_e2e_conv_filter_bank.py | 2 ++ 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/src/iris/nodes/iris_response/conv_filter_bank.py b/src/iris/nodes/iris_response/conv_filter_bank.py index e645568..78ef39c 100644 --- a/src/iris/nodes/iris_response/conv_filter_bank.py +++ b/src/iris/nodes/iris_response/conv_filter_bank.py @@ -5,7 +5,7 @@ from iris.io.class_configs import Algorithm from iris.io.dataclasses import IrisFilterResponse, NormalizedIris -from iris.io.validators import are_lengths_equal, is_not_empty +from iris.io.validators import are_lengths_equal, iris_code_version_check, is_not_empty from iris.nodes.iris_response.image_filters.gabor_filters import GaborFilter from iris.nodes.iris_response.image_filters.image_filter_interface import ImageFilter from iris.nodes.iris_response.probe_schemas.probe_schema_interface import ProbeSchema @@ -47,15 +47,18 @@ class Parameters(Algorithm.Parameters): filters: List[ImageFilter] probe_schemas: List[ProbeSchema] + iris_code_version: str # Validators _are_lengths_equal = root_validator(pre=True, allow_reuse=True)(are_lengths_equal("probe_schemas", "filters")) - _is_not_empty = validator("*", allow_reuse=True)(is_not_empty) + _is_not_empty = validator("filters", "probe_schemas", allow_reuse=True)(is_not_empty) + _iris_code_version_check = validator("iris_code_version", allow_reuse=True)(iris_code_version_check) __parameters_type__ = Parameters def __init__( self, + iris_code_version: str = "v0.1", filters: List[ImageFilter] = [ GaborFilter( kernel_size=(41, 21), @@ -87,7 +90,7 @@ def __init__( filters (List[ImageFilter]): List of image filters. probe_schemas (List[ProbeSchema]): List of corresponding probe schemas. """ - super().__init__(filters=filters, probe_schemas=probe_schemas) + super().__init__(filters=filters, probe_schemas=probe_schemas, iris_code_version=iris_code_version) def run(self, normalization_output: NormalizedIris) -> IrisFilterResponse: """Apply filters to a normalized iris image. @@ -106,7 +109,11 @@ def run(self, normalization_output: NormalizedIris) -> IrisFilterResponse: iris_responses.append(iris_response) mask_responses.append(mask_response) - return IrisFilterResponse(iris_responses=iris_responses, mask_responses=mask_responses) + return IrisFilterResponse( + iris_responses=iris_responses, + mask_responses=mask_responses, + iris_code_version=self.params.iris_code_version, + ) def _convolve( self, img_filter: ImageFilter, probe_schema: ProbeSchema, normalization_output: NormalizedIris diff --git a/tests/e2e_tests/nodes/iris_response/test_e2e_conv_filter_bank.py b/tests/e2e_tests/nodes/iris_response/test_e2e_conv_filter_bank.py index 0993828..352fd7f 100644 --- a/tests/e2e_tests/nodes/iris_response/test_e2e_conv_filter_bank.py +++ b/tests/e2e_tests/nodes/iris_response/test_e2e_conv_filter_bank.py @@ -79,6 +79,7 @@ def test_computed_responses( assert np.allclose(expected_result.mask_responses[0], result.mask_responses[0], rtol=1e-05, atol=1e-07) assert np.allclose(expected_result.mask_responses[1], result.mask_responses[1], rtol=1e-05, atol=1e-07) assert np.allclose(expected_result.mask_responses[2], result.mask_responses[2], rtol=1e-05, atol=1e-07) + assert result.iris_code_version == 'v0.1' @pytest.mark.parametrize( @@ -121,3 +122,4 @@ def test_convfilterbank_constructor( assert np.max(i_mask_response) > np.min(i_mask_response) assert np.max(i_iris_response.real) > np.min(i_iris_response.real) assert np.max(i_iris_response.imag) > np.min(i_iris_response.imag) + assert filter_responses.iris_code_version == 'v0.1' From 7effc946a2b2ba2dbeb84f0d5652d15a7089310a Mon Sep 17 00:00:00 2001 From: TanguyJeanneau Date: Tue, 4 Jun 2024 13:36:29 +0000 Subject: [PATCH 03/32] Adapt IrisEncoder to produce iris_code_version --- src/iris/nodes/encoder/iris_encoder.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/iris/nodes/encoder/iris_encoder.py b/src/iris/nodes/encoder/iris_encoder.py index bef9227..8817d97 100644 --- a/src/iris/nodes/encoder/iris_encoder.py +++ b/src/iris/nodes/encoder/iris_encoder.py @@ -56,4 +56,4 @@ def run(self, response: IrisFilterResponse) -> IrisTemplate: iris_codes.append(iris_code) mask_codes.append(mask_code) - return IrisTemplate(iris_codes=iris_codes, mask_codes=mask_codes) + return IrisTemplate(iris_codes=iris_codes, mask_codes=mask_codes, iris_code_version=response.iris_code_version) From 65b9ad9d8d769cc3f22557ffbef7c406053d45cd Mon Sep 17 00:00:00 2001 From: TanguyJeanneau Date: Tue, 4 Jun 2024 13:37:02 +0000 Subject: [PATCH 04/32] Adapt IrisEncoder unit tests --- .../iris_encoder/e2e_expected_result.pickle | Bin 160513 -> 160542 bytes .../mocks/iris_encoder/iris_response.pickle | Bin 640558 -> 640587 bytes .../nodes/encoder/test_e2e_iris_encoder.py | 1 + 3 files changed, 1 insertion(+) diff --git a/tests/e2e_tests/nodes/encoder/mocks/iris_encoder/e2e_expected_result.pickle b/tests/e2e_tests/nodes/encoder/mocks/iris_encoder/e2e_expected_result.pickle index e8cf2f94d2503cb1daeb7a7ff1e09dcf6f121870..d597b85edc5811f15f6b0c57f511d1709c207c24 100644 GIT binary patch delta 90 zcmZpC#yRg9XTui8jh2jz?VBtaw{Nm!;;^)x8qEL!J%X7lR5x_5g)<; delta 44 wcmbRDjI;3>XTui8jh2i|?VBtaw{Nm!;;`f@oodSf0n_gnF^NzAQN*MM09w)w^8f$< diff --git a/tests/e2e_tests/nodes/encoder/mocks/iris_encoder/iris_response.pickle b/tests/e2e_tests/nodes/encoder/mocks/iris_encoder/iris_response.pickle index 2157c22d9333709048c41a703586bbe8e4cc7bb2..18e236c927d709815201a3cdc2fd563425acb34c 100644 GIT binary patch delta 73 zcmV-P0Ji_Gj3>*CCxC98;lWYa%pp4V{c?- fUv_13b7^mGl#B#+GA=Qcm+-|27y&_-2gV5}CrcbR delta 62 zcmX@zqrR?3y`hD%g{g(Pg{6hHg{_5s3rE6j#$($PZ*%m&5-;uHi;qvsOwCCtjxSCv SiI1PsKSd*heY(mqPIUnKEf@^| diff --git a/tests/e2e_tests/nodes/encoder/test_e2e_iris_encoder.py b/tests/e2e_tests/nodes/encoder/test_e2e_iris_encoder.py index 2524ee1..3e9197d 100644 --- a/tests/e2e_tests/nodes/encoder/test_e2e_iris_encoder.py +++ b/tests/e2e_tests/nodes/encoder/test_e2e_iris_encoder.py @@ -24,6 +24,7 @@ def test_iris_encoder_constructor() -> None: assert len(result.iris_codes) == len(expected_result.iris_codes) assert len(result.mask_codes) == len(expected_result.mask_codes) + assert len(result.iris_code_version) == len(expected_result.iris_code_version) for i, (i_iris_code, i_mask_code) in enumerate(zip(result.iris_codes, result.mask_codes)): assert np.allclose(expected_result.iris_codes[i], i_iris_code, rtol=1e-05, atol=1e-07) From 04fdbd3a7078fa800e0276415a9d80ba31deeeb7 Mon Sep 17 00:00:00 2001 From: TanguyJeanneau Date: Tue, 4 Jun 2024 13:37:26 +0000 Subject: [PATCH 05/32] Adapt FragileBitsRefinement --- .../iris_response_refinement/fragile_bits_refinement.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/iris/nodes/iris_response_refinement/fragile_bits_refinement.py b/src/iris/nodes/iris_response_refinement/fragile_bits_refinement.py index 0cae1f2..6d20522 100644 --- a/src/iris/nodes/iris_response_refinement/fragile_bits_refinement.py +++ b/src/iris/nodes/iris_response_refinement/fragile_bits_refinement.py @@ -68,4 +68,8 @@ def run(self, iris_filter_response: IrisFilterResponse) -> IrisFilterResponse: mask_value = mask_value * iris_mask fragile_masks.append(mask_value) - return IrisFilterResponse(iris_responses=iris_filter_response.iris_responses, mask_responses=fragile_masks) + return IrisFilterResponse( + iris_responses=iris_filter_response.iris_responses, + mask_responses=fragile_masks, + iris_code_version=iris_filter_response.iris_code_version + ) From aebd2e0ae3ad0dcab88ce91cea46a3aa190b0623 Mon Sep 17 00:00:00 2001 From: TanguyJeanneau Date: Tue, 4 Jun 2024 13:37:35 +0000 Subject: [PATCH 06/32] Adapt FragileBitsRefinement unit tests --- ...artificial_iris_responses_cartesian.pickle | Bin 38867 -> 38896 bytes .../artificial_iris_responses_polar.pickle | Bin 10659 -> 10688 bytes 2 files changed, 0 insertions(+), 0 deletions(-) diff --git a/tests/e2e_tests/nodes/iris_response_refinement/mocks/fragile_bits/artificial_iris_responses_cartesian.pickle b/tests/e2e_tests/nodes/iris_response_refinement/mocks/fragile_bits/artificial_iris_responses_cartesian.pickle index bb437179cd3bd31ccec1133e9a9a2e7a52f5ffce..31c9cd91dc4e1e7639adf55d2235c19fb47e33d5 100644 GIT binary patch delta 74 zcmV-Q0JZkmF_d+T g4qsnpX=Q9=b6<01bYEYTkCZ5AFK8oZ2a~RsC4kKx#{d8T delta 27 lcmV+$0ObGhuL9Gr0tA2rmB_IKk(ZOOmlgvDXd{!$mnEv-3x@yz diff --git a/tests/e2e_tests/nodes/iris_response_refinement/mocks/fragile_bits/artificial_iris_responses_polar.pickle b/tests/e2e_tests/nodes/iris_response_refinement/mocks/fragile_bits/artificial_iris_responses_polar.pickle index 5e8b5dc236c61729eaddb724f463c9e0b53188f8..045b1922cd1b419ae0495f30a8c78c57040e99a9 100644 GIT binary patch delta 72 zcmZ1+d?1*mfo1B}jVy_pW<7$LMVZC%$@wX%@nxw+#hLkeQ+im+jPwkrl=kq&$ERhc c=A;zI7pIoQ$4}{>qLE>cp`5`!xlU6R0Q+Qyf~Ppfo1B9jVy_plN&U}nAtOwC(qVY1ps*o2w?yK From 3a92f0034212a753df218120661fcf5a04ff89ff Mon Sep 17 00:00:00 2001 From: TanguyJeanneau Date: Tue, 4 Jun 2024 13:38:10 +0000 Subject: [PATCH 07/32] Adapt dataclasses and validators unit tests --- tests/unit_tests/io/test_dataclasses.py | 83 ++++++++++++++++--- .../validators/test_object_validators.py | 2 + 2 files changed, 73 insertions(+), 12 deletions(-) diff --git a/tests/unit_tests/io/test_dataclasses.py b/tests/unit_tests/io/test_dataclasses.py index 6a84688..ad74baa 100644 --- a/tests/unit_tests/io/test_dataclasses.py +++ b/tests/unit_tests/io/test_dataclasses.py @@ -5,7 +5,7 @@ from pydantic import ValidationError import iris.io.dataclasses as dc - +from iris.io.errors import IRISPipelineError def test_irimage_constructor() -> None: mock_image = np.ones(shape=(10, 10)) @@ -304,77 +304,133 @@ def test_normalized_iris_serialize_deserialize() -> None: def test_iris_filter_response_constructor() -> None: mock_responses = [np.random.randint(5, size=(4, 6)) for _ in range(3)] mock_masks = [np.random.randint(2, size=(4, 6)).astype(bool) for _ in range(3)] + mock_iris_code_version = 'v14.28' - _ = dc.IrisFilterResponse(iris_responses=mock_responses, mask_responses=mock_masks) + _ = dc.IrisFilterResponse(iris_responses=mock_responses, mask_responses=mock_masks, iris_code_version=mock_iris_code_version) @pytest.mark.parametrize( - "iris_responses,mask_responses", + "iris_responses,mask_responses,iris_code_version", [ ( [np.ones(shape=(10, 10)), "not some string", np.ones(shape=(10, 10))], [np.random.randint(2, size=(4, 6)).astype(bool) for _ in range(3)], + 'v3.0', ), ( [np.random.randint(5, size=(4, 6)) for _ in range(3)], [np.random.randint(2, size=(4, 6)).astype(bool) for _ in range(5)], + 'v6.2', ), ( [np.ones(shape=(10, 10)), np.ones(shape=(123, 456))], [np.ones(shape=(10, 10)).astype(bool), np.ones(shape=(835, 19)).astype(bool)], + 'v2.1', + ), + ( + [np.random.randint(5, size=(4, 6)) for _ in range(3)], + [np.random.randint(2, size=(4, 6)).astype(bool) for _ in range(3)], + 3, + ), + ( + [np.random.randint(5, size=(4, 6)) for _ in range(3)], + [np.random.randint(2, size=(4, 6)).astype(bool) for _ in range(3)], + 'not_a_version', + ), + ( + [np.random.randint(5, size=(4, 6)) for _ in range(3)], + [np.random.randint(2, size=(4, 6)).astype(bool) for _ in range(3)], + 'va.b.c', ), ], ids=[ "not list of np arrays", "iris_responses / mask_responses length difference", "individual iris_responses / mask_responses shape difference", + "incorrect iris code version 1", + "incorrect iris code version 2", + "incorrect iris code version 3", ], ) def test_iris_filter_response_constructor_raises_an_exception( - iris_responses: List[np.ndarray], mask_responses: List[np.ndarray] + iris_responses: List[np.ndarray], mask_responses: List[np.ndarray], iris_code_version: str ) -> None: - with pytest.raises((ValueError, AttributeError)): - _ = dc.IrisFilterResponse(iris_responses=iris_responses, mask_responses=mask_responses) + with pytest.raises((ValueError, AttributeError, IRISPipelineError)): + _ = dc.IrisFilterResponse( + iris_responses=iris_responses, + mask_responses=mask_responses, + iris_code_version=iris_code_version + ) def test_iris_filter_response_serialize_deserialize() -> None: mock_responses = [np.random.randint(5, size=(4, 6)) for _ in range(3)] mock_masks = [np.random.randint(2, size=(4, 6)).astype(bool) for _ in range(3)] + mock_iris_code_version = 'v14.28' - iris_response = dc.IrisFilterResponse(iris_responses=mock_responses, mask_responses=mock_masks) + iris_response = dc.IrisFilterResponse( + iris_responses=mock_responses, + mask_responses=mock_masks, + iris_code_version=mock_iris_code_version, + ) serialized_iris_response = iris_response.serialize() deserialized_iris_response = dc.IrisFilterResponse.deserialize(serialized_iris_response) np.testing.assert_equal(iris_response.iris_responses, deserialized_iris_response.iris_responses) np.testing.assert_equal(iris_response.mask_responses, deserialized_iris_response.mask_responses) + assert iris_response.iris_code_version ==deserialized_iris_response.iris_code_version def test_iris_template_constructor() -> None: mock_iris_codes = [np.random.randint(2, size=(10, 10)).astype(bool) for _ in range(5)] mock_mask_codes = [np.random.randint(2, size=(10, 10)).astype(bool) for _ in range(5)] + mock_iris_code_version = 'v14.28' - _ = dc.IrisTemplate(iris_codes=mock_iris_codes, mask_codes=mock_mask_codes) + _ = dc.IrisTemplate( + iris_codes=mock_iris_codes, + mask_codes=mock_mask_codes, + iris_code_version=mock_iris_code_version, + ) @pytest.mark.parametrize( - "iris_codes,mask_codes", + "iris_codes,mask_codes,iris_code_version", [ ( [np.random.randint(2, size=(10, 10)) for _ in range(5)], [np.random.randint(2, size=(10, 10)) for _ in range(5)], + 'v3.0', ), ( "not a list of arrays", 3, + 'v3.0', ), ( [np.random.randint(2, size=(4, 6)).astype(bool) for _ in range(3)], [np.random.randint(2, size=(4, 6)).astype(bool) for _ in range(5)], + 'v3.0', ), ( [np.ones(shape=(10, 10)).astype(bool), np.ones(shape=(123, 456)).astype(bool)], [np.ones(shape=(10, 10)).astype(bool), np.ones(shape=(835, 19)).astype(bool)], + 'v3.0', + ), + ( + [np.random.randint(2, size=(10, 10)).astype(bool) for _ in range(5)], + [np.random.randint(2, size=(10, 10)).astype(bool) for _ in range(5)], + 42, + ), + ( + [np.random.randint(2, size=(10, 10)).astype(bool) for _ in range(5)], + [np.random.randint(2, size=(10, 10)).astype(bool) for _ in range(5)], + 'not_a_version', + ), + ( + [np.random.randint(2, size=(10, 10)).astype(bool) for _ in range(5)], + [np.random.randint(2, size=(10, 10)).astype(bool) for _ in range(5)], + 'va.b.c', ), ], ids=[ @@ -382,10 +438,13 @@ def test_iris_template_constructor() -> None: "not array", "iris_codes / mask_codes length difference", "individual iris_codes / mask_codes shape difference", + "incorrect iris code version 1", + "incorrect iris code version 2", + "incorrect iris code version 3", ], ) def test_iris_template_constructor_raises_an_exception( - iris_codes: List[np.ndarray], mask_codes: List[np.ndarray] + iris_codes: List[np.ndarray], mask_codes: List[np.ndarray], iris_code_version: str, ) -> None: - with pytest.raises((ValueError, ValidationError, AttributeError)): - _ = dc.IrisTemplate(iris_codes=iris_codes, mask_codes=mask_codes) + with pytest.raises((ValueError, ValidationError, AttributeError, IRISPipelineError)): + _ = dc.IrisTemplate(iris_codes=iris_codes, mask_codes=mask_codes, iris_code_version=iris_code_version) diff --git a/tests/unit_tests/nodes/validators/test_object_validators.py b/tests/unit_tests/nodes/validators/test_object_validators.py index 138b2b1..4aacab2 100644 --- a/tests/unit_tests/nodes/validators/test_object_validators.py +++ b/tests/unit_tests/nodes/validators/test_object_validators.py @@ -265,6 +265,7 @@ def test_is_mask_too_small_validator( mask_codes=[ rng.choice(2, size=(code_height, code_width, 2), p=[0.1, 0.9]).astype(bool) for _ in range(num_filters) ], + iris_code_version='v3.0', ) validator = obj_v.IsMaskTooSmallValidator(min_maskcodes_size=min_maskcodes_size) try: @@ -292,6 +293,7 @@ def test_is_mask_too_small_validator_raise_exception( mask_codes=[ rng.choice(2, size=(code_height, code_width, 2), p=[0.5, 0.5]).astype(bool) for _ in range(num_filters) ], + iris_code_version='v3.0', ) validator = obj_v.IsMaskTooSmallValidator(min_maskcodes_size=min_maskcodes_size) From 86fd719bd39a91e115d637629b62e7a57cbc2bd8 Mon Sep 17 00:00:00 2001 From: TanguyJeanneau Date: Tue, 4 Jun 2024 13:38:28 +0000 Subject: [PATCH 08/32] Adapt hamming_distance unit tests --- .../nodes/matcher/test_matcher_utils.py | 52 +++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/tests/unit_tests/nodes/matcher/test_matcher_utils.py b/tests/unit_tests/nodes/matcher/test_matcher_utils.py index 6cd910a..2032973 100644 --- a/tests/unit_tests/nodes/matcher/test_matcher_utils.py +++ b/tests/unit_tests/nodes/matcher/test_matcher_utils.py @@ -16,10 +16,12 @@ IrisTemplate( iris_codes=[np.array([[True, True], [True, True]]), np.array([[True, True], [True, True]])], mask_codes=[np.array([[True, True], [True, True]]), np.array([[True, True], [True, True]])], + iris_code_version="v2.1", ), IrisTemplate( iris_codes=[np.array([[True, True], [True, True]]), np.array([[True, True], [True, True]])], mask_codes=[np.array([[True, True], [True, True]]), np.array([[True, True], [True, True]])], + iris_code_version="v2.1", ), 1, None, @@ -29,10 +31,12 @@ IrisTemplate( iris_codes=[np.array([[True, True], [False, True]]), np.array([[True, True], [True, False]])], mask_codes=[np.array([[True, False], [True, False]]), np.array([[False, True], [False, True]])], + iris_code_version="v2.1", ), IrisTemplate( iris_codes=[np.array([[True, True], [True, False]]), np.array([[True, True], [True, True]])], mask_codes=[np.array([[True, True], [True, True]]), np.array([[True, False], [True, True]])], + iris_code_version="v2.1", ), 1, None, @@ -42,10 +46,12 @@ IrisTemplate( iris_codes=[np.array([[True, True], [True, True]]), np.array([[True, True], [True, True]])], mask_codes=[np.array([[True, True], [True, True]]), np.array([[True, True], [True, True]])], + iris_code_version="v2.1", ), IrisTemplate( iris_codes=[np.array([[False, False], [False, False]]), np.array([[False, False], [False, False]])], mask_codes=[np.array([[True, True], [True, True]]), np.array([[True, True], [True, True]])], + iris_code_version="v2.1", ), 1, None, @@ -55,10 +61,12 @@ IrisTemplate( iris_codes=[np.array([[True, True], [True, True]]), np.array([[True, True], [True, True]])], mask_codes=[np.array([[True, True], [True, True]]), np.array([[True, False], [True, True]])], + iris_code_version="v2.1", ), IrisTemplate( iris_codes=[np.array([[False, False], [True, False]]), np.array([[False, True], [False, False]])], mask_codes=[np.array([[True, False], [False, True]]), np.array([[True, True], [True, True]])], + iris_code_version="v2.1", ), 1, None, @@ -68,10 +76,12 @@ IrisTemplate( iris_codes=[np.array([[True, True], [True, True]]), np.array([[True, True], [True, True]])], mask_codes=[np.array([[True, True], [True, True]]), np.array([[True, False], [True, True]])], + iris_code_version="v2.1", ), IrisTemplate( iris_codes=[np.array([[False, False], [True, False]]), np.array([[False, True], [False, False]])], mask_codes=[np.array([[True, False], [False, True]]), np.array([[True, True], [True, True]])], + iris_code_version="v2.1", ), 0, None, @@ -81,10 +91,12 @@ IrisTemplate( iris_codes=[np.array([[True, True], [True, True]]), np.array([[True, True], [True, True]])], mask_codes=[np.array([[True, True], [True, True]]), np.array([[True, True], [True, True]])], + iris_code_version="v2.1", ), IrisTemplate( iris_codes=[np.array([[True, True], [True, True]]), np.array([[True, True], [True, True]])], mask_codes=[np.array([[True, True], [True, True]]), np.array([[True, True], [True, True]])], + iris_code_version="v2.1", ), 1, 0.45, @@ -94,10 +106,12 @@ IrisTemplate( iris_codes=[np.array([[True, True], [False, True]]), np.array([[True, True], [True, False]])], mask_codes=[np.array([[True, False], [True, False]]), np.array([[False, True], [False, True]])], + iris_code_version="v2.1", ), IrisTemplate( iris_codes=[np.array([[True, True], [True, False]]), np.array([[True, True], [True, True]])], mask_codes=[np.array([[True, True], [True, True]]), np.array([[True, False], [True, True]])], + iris_code_version="v2.1", ), 1, 0.45, @@ -107,10 +121,12 @@ IrisTemplate( iris_codes=[np.array([[True, True], [True, True]]), np.array([[True, True], [True, True]])], mask_codes=[np.array([[True, True], [True, True]]), np.array([[True, True], [True, True]])], + iris_code_version="v2.1", ), IrisTemplate( iris_codes=[np.array([[False, False], [False, False]]), np.array([[False, False], [False, False]])], mask_codes=[np.array([[True, True], [True, True]]), np.array([[True, True], [True, True]])], + iris_code_version="v2.1", ), 1, 0.45, @@ -120,10 +136,12 @@ IrisTemplate( iris_codes=[np.array([[True, True], [True, True]]), np.array([[True, True], [True, True]])], mask_codes=[np.array([[True, True], [True, True]]), np.array([[True, False], [True, True]])], + iris_code_version="v2.1", ), IrisTemplate( iris_codes=[np.array([[False, False], [True, False]]), np.array([[False, True], [False, False]])], mask_codes=[np.array([[True, False], [False, True]]), np.array([[True, True], [True, True]])], + iris_code_version="v2.1", ), 1, 0.45, @@ -133,10 +151,12 @@ IrisTemplate( iris_codes=[np.array([[True, True], [True, True]]), np.array([[True, True], [True, True]])], mask_codes=[np.array([[True, True], [True, True]]), np.array([[True, False], [True, True]])], + iris_code_version="v2.1", ), IrisTemplate( iris_codes=[np.array([[False, False], [True, False]]), np.array([[False, True], [False, False]])], mask_codes=[np.array([[True, False], [False, True]]), np.array([[True, True], [True, True]])], + iris_code_version="v2.1", ), -1, 0.45, @@ -146,10 +166,12 @@ IrisTemplate( iris_codes=[np.array([[True, True], [True, True]]), np.array([[True, True], [True, True]])], mask_codes=[np.array([[True, True], [True, True]]), np.array([[True, False], [True, True]])], + iris_code_version="v2.1", ), IrisTemplate( iris_codes=[np.array([[False, False], [True, False]]), np.array([[False, True], [False, False]])], mask_codes=[np.array([[True, False], [False, True]]), np.array([[True, True], [True, True]])], + iris_code_version="v2.1", ), 1, 0.45, @@ -159,10 +181,12 @@ IrisTemplate( iris_codes=[np.array([[True, True], [True, True]]), np.array([[True, True], [True, True]])], mask_codes=[np.array([[True, True], [True, False]]), np.array([[True, False], [False, True]])], + iris_code_version="v2.1", ), IrisTemplate( iris_codes=[np.array([[False, False], [True, False]]), np.array([[False, True], [False, False]])], mask_codes=[np.array([[True, False], [True, True]]), np.array([[True, True], [True, False]])], + iris_code_version="v2.1", ), 1, 0.45, @@ -172,10 +196,12 @@ IrisTemplate( iris_codes=[np.array([[True, True], [True, True]]), np.array([[True, True], [True, True]])], mask_codes=[np.array([[False, True], [False, True]]), np.array([[False, False], [False, False]])], + iris_code_version="v2.1", ), IrisTemplate( iris_codes=[np.array([[False, False], [True, False]]), np.array([[False, True], [False, False]])], mask_codes=[np.array([[True, True], [True, True]]), np.array([[True, False], [True, False]])], + iris_code_version="v2.1", ), 1, 0.45, @@ -217,10 +243,12 @@ def test_hamming_distance( IrisTemplate( iris_codes=[np.array([[True, True], [True, True]]), np.array([[True, True], [True, True]])], mask_codes=[np.array([[True, True], [True, True]]), np.array([[True, True], [True, True]])], + iris_code_version="v2.1", ), IrisTemplate( iris_codes=[np.array([[True, True], [True, True]]), np.array([[True, True], [True, True]])], mask_codes=[np.array([[True, True], [True, True]]), np.array([[True, True], [True, True]])], + iris_code_version="v2.1", ), 1, None, @@ -231,10 +259,12 @@ def test_hamming_distance( IrisTemplate( iris_codes=[np.array([[True, True], [False, True]]), np.array([[True, True], [True, False]])], mask_codes=[np.array([[True, False], [True, False]]), np.array([[False, True], [False, True]])], + iris_code_version="v2.1", ), IrisTemplate( iris_codes=[np.array([[True, True], [True, False]]), np.array([[True, True], [True, True]])], mask_codes=[np.array([[True, True], [True, True]]), np.array([[True, False], [True, True]])], + iris_code_version="v2.1", ), 1, None, @@ -245,10 +275,12 @@ def test_hamming_distance( IrisTemplate( iris_codes=[np.array([[True, True], [True, True]]), np.array([[True, True], [True, True]])], mask_codes=[np.array([[True, True], [True, True]]), np.array([[True, True], [True, True]])], + iris_code_version="v2.1", ), IrisTemplate( iris_codes=[np.array([[False, False], [False, False]]), np.array([[False, False], [False, False]])], mask_codes=[np.array([[True, True], [True, True]]), np.array([[True, True], [True, True]])], + iris_code_version="v2.1", ), 1, None, @@ -259,10 +291,12 @@ def test_hamming_distance( IrisTemplate( iris_codes=[np.array([[True, True], [True, True]]), np.array([[True, True], [True, True]])], mask_codes=[np.array([[True, True], [True, True]]), np.array([[True, False], [True, True]])], + iris_code_version="v2.1", ), IrisTemplate( iris_codes=[np.array([[False, False], [True, False]]), np.array([[False, True], [False, False]])], mask_codes=[np.array([[True, False], [False, True]]), np.array([[True, True], [True, True]])], + iris_code_version="v2.1", ), 1, None, @@ -273,10 +307,12 @@ def test_hamming_distance( IrisTemplate( iris_codes=[np.array([[True, True], [True, True]]), np.array([[True, True], [True, True]])], mask_codes=[np.array([[True, True], [True, True]]), np.array([[True, False], [True, True]])], + iris_code_version="v2.1", ), IrisTemplate( iris_codes=[np.array([[False, False], [True, False]]), np.array([[False, True], [False, False]])], mask_codes=[np.array([[True, False], [False, True]]), np.array([[True, True], [True, True]])], + iris_code_version="v2.1", ), 0, None, @@ -287,10 +323,12 @@ def test_hamming_distance( IrisTemplate( iris_codes=[np.array([[True, True], [True, True]]), np.array([[True, True], [True, True]])], mask_codes=[np.array([[True, True], [True, True]]), np.array([[True, True], [True, True]])], + iris_code_version="v2.1", ), IrisTemplate( iris_codes=[np.array([[True, True], [True, True]]), np.array([[True, True], [True, True]])], mask_codes=[np.array([[True, True], [True, True]]), np.array([[True, True], [True, True]])], + iris_code_version="v2.1", ), 1, 0.45, @@ -301,10 +339,12 @@ def test_hamming_distance( IrisTemplate( iris_codes=[np.array([[True, True], [False, True]]), np.array([[True, True], [True, False]])], mask_codes=[np.array([[True, False], [True, False]]), np.array([[False, True], [False, True]])], + iris_code_version="v2.1", ), IrisTemplate( iris_codes=[np.array([[True, True], [True, False]]), np.array([[True, True], [True, True]])], mask_codes=[np.array([[True, True], [True, True]]), np.array([[True, False], [True, True]])], + iris_code_version="v2.1", ), 1, 0.45, @@ -315,10 +355,12 @@ def test_hamming_distance( IrisTemplate( iris_codes=[np.array([[True, True], [True, True]]), np.array([[True, True], [True, True]])], mask_codes=[np.array([[True, True], [True, True]]), np.array([[True, True], [True, True]])], + iris_code_version="v2.1", ), IrisTemplate( iris_codes=[np.array([[False, False], [False, True]]), np.array([[False, False], [False, False]])], mask_codes=[np.array([[True, True], [True, True]]), np.array([[True, True], [True, True]])], + iris_code_version="v2.1", ), 1, 0.45, @@ -329,10 +371,12 @@ def test_hamming_distance( IrisTemplate( iris_codes=[np.array([[True, True], [True, True]]), np.array([[True, True], [True, True]])], mask_codes=[np.array([[True, True], [True, True]]), np.array([[True, False], [True, True]])], + iris_code_version="v2.1", ), IrisTemplate( iris_codes=[np.array([[False, False], [True, False]]), np.array([[False, True], [False, False]])], mask_codes=[np.array([[True, False], [False, True]]), np.array([[True, True], [True, True]])], + iris_code_version="v2.1", ), 1, 0.45, @@ -343,10 +387,12 @@ def test_hamming_distance( IrisTemplate( iris_codes=[np.array([[True, True], [True, True]]), np.array([[True, True], [True, True]])], mask_codes=[np.array([[True, True], [True, True]]), np.array([[True, False], [True, True]])], + iris_code_version="v2.1", ), IrisTemplate( iris_codes=[np.array([[False, False], [True, False]]), np.array([[False, True], [False, False]])], mask_codes=[np.array([[True, False], [False, True]]), np.array([[True, True], [True, True]])], + iris_code_version="v2.1", ), -1, 0.45, @@ -363,6 +409,7 @@ def test_hamming_distance( np.array([[True, True], [True, True]]), np.array([[True, False, False, True], [True, True, True, True]]), ], + iris_code_version="v2.1", ), IrisTemplate( iris_codes=[ @@ -373,6 +420,7 @@ def test_hamming_distance( np.array([[True, False], [False, True]]), np.array([[True, True, True, True], [True, True, False, True]]), ], + iris_code_version="v2.1", ), -1, 0.45, @@ -415,10 +463,12 @@ def test_hamming_distance_with_weights( IrisTemplate( iris_codes=[np.array([[True, True], [True, True]]), np.array([[True, True], [True, True]])], mask_codes=[np.array([[True, True], [True, True]]), np.array([[True, True], [True, True]])], + iris_code_version="v2.1", ), IrisTemplate( iris_codes=[np.array([[True, True]]), np.array([[True, True], [True, True]])], mask_codes=[np.array([[True, True]]), np.array([[True, True], [True, True]])], + iris_code_version="v2.1", ), 1, None, @@ -433,6 +483,7 @@ def test_hamming_distance_with_weights( np.array([[True, True, False], [True, True, False], [True, True, True]]), np.array([[True, True, True], [True, True, True], [True, True, True]]), ], + iris_code_version="v2.1", ), IrisTemplate( iris_codes=[ @@ -443,6 +494,7 @@ def test_hamming_distance_with_weights( np.array([[True, True, True], [True, True, False], [True, True, False]]), np.array([[True, True, True], [True, True, True], [True, True, False]]), ], + iris_code_version="v2.1", ), 1, None, From cab09716dbc4839a2a13bbf2dc25bba2a2819b9a Mon Sep 17 00:00:00 2001 From: TanguyJeanneau Date: Wed, 5 Jun 2024 21:33:28 +0000 Subject: [PATCH 09/32] Add an option to returned _signed_ polygon area in iris.utils.math.area --- src/iris/utils/math.py | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/iris/utils/math.py b/src/iris/utils/math.py index fd0b885..422d320 100644 --- a/src/iris/utils/math.py +++ b/src/iris/utils/math.py @@ -4,21 +4,22 @@ import numpy as np -def area(array: np.ndarray) -> float: +def area(array: np.ndarray, signed: bool = False) -> float: """Shoelace formula for simple polygon area calculation. - WARNING: This formula only works for simple polygons, i.e planar polygon without self-intersection nor holes. + WARNING: This formula only works for _simple polygons_, i.e planar polygon without self-intersection nor holes. These conditions are not checked within this function. Args: array (np.ndarray): np array representing a polygon as a list of points, i.e. of shape (_, 2). - - Raises: - ValueError: if the input array does not have shape (_, 2) + signed (bool): If True, the area is signed, i.e. negative if the polygon is oriented clockwise. Returns: float: Polygon area + Raises: + ValueError: if the input array does not have shape (_, 2) + References: [1] https://en.wikipedia.org/wiki/Shoelace_formula [2] https://stackoverflow.com/questions/24467972/calculate-area-of-polygon-given-x-y-coordinates @@ -27,7 +28,9 @@ def area(array: np.ndarray) -> float: raise ValueError(f"Unable to determine the area of a polygon with shape {array.shape}. Expecting (_, 2).") xs, ys = array.T - area = 0.5 * np.abs(np.dot(xs, np.roll(ys, 1)) - np.dot(ys, np.roll(xs, 1))) + area = 0.5 * (np.dot(xs, np.roll(ys, 1)) - np.dot(ys, np.roll(xs, 1))) + if not signed: + area = abs(area) return float(area) From 84666bb03bff4988d5cd7382c2c90ca71761b4e2 Mon Sep 17 00:00:00 2001 From: TanguyJeanneau Date: Wed, 5 Jun 2024 21:34:06 +0000 Subject: [PATCH 10/32] Add base 64 encoding / decoding functions + unit tests --- src/iris/utils/base64_encoding.py | 60 +++++++++++++++++++ .../unit_tests/utils/test_base64_encoding.py | 32 ++++++++++ 2 files changed, 92 insertions(+) create mode 100644 src/iris/utils/base64_encoding.py create mode 100644 tests/unit_tests/utils/test_base64_encoding.py diff --git a/src/iris/utils/base64_encoding.py b/src/iris/utils/base64_encoding.py new file mode 100644 index 0000000..4e229d1 --- /dev/null +++ b/src/iris/utils/base64_encoding.py @@ -0,0 +1,60 @@ +import base64 +from typing import Tuple + +import numpy as np + + +def base64_encode_array(array2encode: np.ndarray) -> bytes: + """Convert a numpy array to a packed base64 string. + + Args: + array2encode (np.ndarray): The array to convert. + + Returns: + bytes: The packed base64 string. + """ + co_pack = np.packbits(array2encode) + + return base64.b64encode(co_pack.tobytes()) + + +def base64_decode_array(bytes_array: str, array_shape: Tuple[int, int, int, int] = (16, 200, 2, 2)) -> np.ndarray: + """Convert a packed base64 string to a numpy array. + + Args: + bytes_array (bytes): The packed base64 byte string. + shape (Tuple[int, int, int, int], optional): The shape of the array. Defaults to (16, 200, 2, 2). + + Returns: + np.ndarray: The array. + """ + decoded_bytes = base64.b64decode(bytes_array) + + deserialized_bytes = np.frombuffer(decoded_bytes, dtype=np.uint8) + unpacked_bits = np.unpackbits(deserialized_bytes) + + return unpacked_bits.reshape(*array_shape).astype(bool) + + +def base64_encode_str(input_str: str) -> str: + """Convert a string to base64 string. Both input and output are string, but base64 encoded vs non-encoded. + + Args: + input_str (str): The string to encode. + + Returns: + str: the encoded base64 string. + """ + return base64.b64encode(input_str.encode()).decode() + + +def base64_decode_str(base64_str: str) -> str: + """Convert base64-encoded string to decoded string. Both input and output are string, but base64 encoded vs non-encoded. + + Args: + base64_str (str): The base64-encoded string + + Returns: + str: the decoded string + """ + return base64.b64decode(base64_str).decode() diff --git a/tests/unit_tests/utils/test_base64_encoding.py b/tests/unit_tests/utils/test_base64_encoding.py new file mode 100644 index 0000000..239c032 --- /dev/null +++ b/tests/unit_tests/utils/test_base64_encoding.py @@ -0,0 +1,32 @@ +import numpy as np +import pytest + +from iris.utils import base64_encoding as be + + +@pytest.mark.parametrize("mock_shape", [(3, 10, 100), (10, 3, 100), (100, 10, 3)]) +def test_base64_array_encode_decode(mock_shape: tuple) -> None: + mock_array = np.random.choice(2, size=mock_shape).astype(bool) + + result = be.base64_decode_array(be.base64_encode_array(mock_array), array_shape=mock_shape) + + np.testing.assert_equal(result, mock_array) + + +@pytest.mark.parametrize( + "plain_str,base64_str", [("test", "dGVzdA=="), ("un:\n - deux\n - trois", "dW46CiAgLSBkZXV4CiAgLSB0cm9pcw==")] +) +def test_base64_str_encode_decode(plain_str: str, base64_str: str) -> None: + # Test base64_encode_str + encoded_str = be.base64_encode_str(plain_str) + assert encoded_str == base64_str + assert isinstance(encoded_str, str) + + # Test base64_decode_str + decoded_str = be.base64_decode_str(base64_str) + assert decoded_str == plain_str + assert isinstance(decoded_str, str) + + # Test that encoding and decoding convolve + encoded_decoded_str = be.base64_decode_str(be.base64_encode_str(plain_str)) + assert encoded_decoded_str == plain_str From b28837e32c8c80917a1063b46cc9731e24a78385 Mon Sep 17 00:00:00 2001 From: TanguyJeanneau Date: Wed, 5 Jun 2024 21:34:55 +0000 Subject: [PATCH 11/32] Add base 64 encoding in IrisTemplate and IrisFilterResponse dataclasses --- src/iris/io/dataclasses.py | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/src/iris/io/dataclasses.py b/src/iris/io/dataclasses.py index 7adf650..5056f3e 100644 --- a/src/iris/io/dataclasses.py +++ b/src/iris/io/dataclasses.py @@ -7,7 +7,7 @@ from iris.io import validators as v from iris.io.class_configs import ImmutableModel -from iris.utils import math +from iris.utils import base64_encoding, math class IRImage(ImmutableModel): @@ -580,11 +580,11 @@ class IrisTemplate(ImmutableModel): _is_binary = validator("iris_codes", "mask_codes", allow_reuse=True, each_item=True)(v.is_binary) _iris_code_version_check = validator("iris_code_version", allow_reuse=True)(v.iris_code_version_check) - def serialize(self) -> Dict[str, np.ndarray]: - """Serialize IrisTemplate object. + def convert2old_format(self) -> Tuple[np.ndarray, np.ndarray]: + """Convert IrisTemplate class to an old engines pipeline output format. Returns: - Dict[str, np.ndarray]: Serialized object. + Tuple[np.ndarray, np.ndarray]: Old engines pipeline object Tuple with (iris_codes, mask_codes). """ stacked_iris_codes = np.stack(self.iris_codes) stacked_iris_codes = stacked_iris_codes.transpose(1, 2, 0, 3) @@ -592,9 +592,20 @@ def serialize(self) -> Dict[str, np.ndarray]: stacked_mask_codes = np.stack(self.mask_codes) stacked_mask_codes = stacked_mask_codes.transpose(1, 2, 0, 3) + return stacked_iris_codes, stacked_mask_codes + + def serialize(self) -> Dict[str, bytes]: + """Serialize IrisTemplate object. + + Returns: + Dict[str, bytes]: Serialized object. + """ + old_format_iris_codes, old_format_mask_codes = self.convert2old_format() + return { - "iris_codes": stacked_iris_codes, - "mask_codes": stacked_mask_codes, + "iris_codes": base64_encoding.base64_encode_array(old_format_iris_codes).decode("utf-8"), + "mask_codes": base64_encoding.base64_encode_array(old_format_mask_codes).decode("utf-8"), + "iris_code_version": self.iris_code_version, } From 27cfceb0ab26812acfcacdf59a29791cfe2fc32f Mon Sep 17 00:00:00 2001 From: TanguyJeanneau Date: Wed, 5 Jun 2024 21:37:35 +0000 Subject: [PATCH 12/32] Replace IRISPipeline's load_from_config_map by load_from_config, which takes a unique base64-encoded config instead of a map of configs --- src/iris/pipelines/iris_pipeline.py | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/iris/pipelines/iris_pipeline.py b/src/iris/pipelines/iris_pipeline.py index 9dd814d..49d1bf9 100644 --- a/src/iris/pipelines/iris_pipeline.py +++ b/src/iris/pipelines/iris_pipeline.py @@ -21,6 +21,7 @@ from iris.orchestration.output_builders import build_debugging_output, build_orb_output, build_simple_output from iris.orchestration.pipeline_dataclasses import PipelineClass, PipelineMetadata, PipelineNode from iris.orchestration.validators import pipeline_config_duplicate_node_name_check +from iris.utils.base64_encoding import base64_decode_str class IRISPipeline(Algorithm): @@ -72,7 +73,7 @@ def __init__( Args: config (Union[Dict[str, Any], Optional[str]]): Input configuration, as a YAML-formatted string or dictionary specifying all nodes configuration. Defaults to None, which loads the default config. - env (Environment, optional): Environment properties. Defaults to Environment(output_builder=build_simple_output, error_manager=store_error_manager, call_trace_initialiser=PipelineCallTraceStorage). + env (Environment, optional): Environment properties. Defaults to Environment(pipeline_output_builder=build_simple_output, error_manager=store_error_manager, call_trace_initialiser=PipelineCallTraceStorage). """ deserialized_config = self.load_config(config) if isinstance(config, str) or config is None else config super().__init__(**deserialized_config) @@ -279,7 +280,7 @@ def load_config(config: Optional[str]) -> Dict[str, Any]: Returns: Dict[str, Any]: Configuration as a dictionary. """ - if not config: + if config is None or config == "": with open(os.path.join(os.path.dirname(__file__), "confs", "pipeline.yaml"), "r") as f: deserialized_config = yaml.safe_load(f) elif isinstance(config, str): @@ -296,22 +297,22 @@ def load_config(config: Optional[str]) -> Dict[str, Any]: return deserialized_config - @staticmethod - def load_from_config_map(config_map: Dict[str, str]) -> Dict[str, Union[IRISPipeline, Optional[Dict[str, Any]]]]: - """Given a mapping between iris versions and iris config strings, initialise an IRISPipeline with config matching the current version. + @classmethod + def load_from_config(cls, config: str) -> Dict[str, Union[IRISPipeline, Optional[Dict[str, Any]]]]: + """Given a iris config string in base64, initialise an IRISPipeline with config this config. Args: - config_map (Dict[str, str]): mapping between iris versions and iris str configs + config (str): an iris str configs in base64 Returns: Dict[str, Union[IRISPipeline, Optional[Dict[str, Any]]]]: Initialised iris pipeline and standard error output. """ - current_version = iris.__version__ error = None iris_pipeline = None try: - iris_pipeline = IRISPipeline(config=config_map[current_version]) + decoded_config_str = base64_decode_str(config) + iris_pipeline = cls(config=decoded_config_str) except Exception as exception: error = { "error_type": type(exception).__name__, From ae534c3031ce13c5518363892dde9f82772558ce Mon Sep 17 00:00:00 2001 From: TanguyJeanneau Date: Wed, 5 Jun 2024 21:39:02 +0000 Subject: [PATCH 13/32] Add unit test for build_simple_output --- .../orchestration/test_e2e_output_builder.py | 23 +++++++++++++++-- tests/e2e_tests/utils.py | 25 +++++++++++++++++++ 2 files changed, 46 insertions(+), 2 deletions(-) diff --git a/tests/e2e_tests/orchestration/test_e2e_output_builder.py b/tests/e2e_tests/orchestration/test_e2e_output_builder.py index a4843a5..7fef9c7 100644 --- a/tests/e2e_tests/orchestration/test_e2e_output_builder.py +++ b/tests/e2e_tests/orchestration/test_e2e_output_builder.py @@ -7,8 +7,18 @@ from iris.callbacks.pipeline_trace import PipelineCallTraceStorage from iris.io.dataclasses import Landmarks -from iris.orchestration.output_builders import build_debugging_output, build_orb_output -from tests.e2e_tests.utils import compare_debug_pipeline_outputs, compare_iris_pipeline_outputs +from iris.orchestration.output_builders import build_debugging_output, build_orb_output, build_simple_output +from tests.e2e_tests.utils import ( + compare_debug_pipeline_outputs, + compare_iris_pipeline_outputs, + compare_simple_pipeline_outputs, +) + + +@pytest.fixture +def expected_simple_iris_pipeline_output() -> Tuple[Tuple[np.ndarray, np.ndarray], Landmarks, Dict[str, Any]]: + expected_output_path = os.path.join(os.path.dirname(__file__), "mocks", "expected_iris_pipeline_simple_output.pickle") + return pickle.load(open(expected_output_path, "rb")) @pytest.fixture @@ -32,6 +42,15 @@ def mock_iris_pipeline_call_trace() -> PipelineCallTraceStorage: return pickle.load(open(expected_call_trace_path, "rb")) +def test_e2e_build_simple_output( + mock_iris_pipeline_call_trace: PipelineCallTraceStorage, + expected_simple_iris_pipeline_output: Tuple[Tuple[np.ndarray, np.ndarray], Landmarks, Dict[str, Any]], +) -> None: + build_orb_iris_pipeline_output = build_simple_output(mock_iris_pipeline_call_trace) + + compare_simple_pipeline_outputs(expected_simple_iris_pipeline_output, build_orb_iris_pipeline_output) + + def test_e2e_build_orb_output( mock_iris_pipeline_call_trace: PipelineCallTraceStorage, expected_orb_iris_pipeline_output: Tuple[Tuple[np.ndarray, np.ndarray], Landmarks, Dict[str, Any]], diff --git a/tests/e2e_tests/utils.py b/tests/e2e_tests/utils.py index 3fd28ba..e6f3368 100644 --- a/tests/e2e_tests/utils.py +++ b/tests/e2e_tests/utils.py @@ -74,6 +74,7 @@ def compare_iris_pipeline_template_output(iris_template_1: Dict[str, Any], iris_ """ assert np.all(iris_template_2["iris_codes"] == iris_template_1["iris_codes"]) assert np.all(iris_template_2["mask_codes"] == iris_template_1["mask_codes"]) + assert iris_template_2["iris_code_version"] == iris_template_1["iris_code_version"] def compare_iris_pipeline_error_output(error_dict_1: Dict[str, str], error_dict_2: Dict[str, str]) -> None: @@ -90,6 +91,30 @@ def compare_iris_pipeline_error_output(error_dict_1: Dict[str, str], error_dict_ assert error_dict_1["message"] == error_dict_2["message"] +def compare_simple_pipeline_template_output(iris_template_1: Dict[str, Any], iris_template_2: Dict[str, Any]) -> None: + """Compare two IRISPipeline template outputs. + + Args: + iris_template_1 (Dict[str, Any]): pipeline's iris template output 1. + iris_template_2 (Dict[str, Any]): pipeline's iris template output 2. + """ + assert np.all([ic1 == ic2 for ic1, ic2 in zip(iris_template_2.iris_codes, iris_template_1.iris_codes)]) + assert np.all([ic1 == ic2 for ic1, ic2 in zip(iris_template_2.mask_codes, iris_template_1.mask_codes)]) + assert iris_template_2.iris_code_version == iris_template_1.iris_code_version + + +def compare_simple_pipeline_outputs(pipeline_output_1: Dict[str, Any], pipeline_output_2: Dict[str, Any]): + """Compare two IRISPipeline outputs for the Orb. + + Args: + pipeline_output_1 (Dict[str, Any]): pipeline output 1. + pipeline_output_2 (Dict[str, Any]): pipeline output 2. + """ + compare_simple_pipeline_template_output(pipeline_output_1["iris_template"], pipeline_output_2["iris_template"]) + compare_iris_pipeline_metadata_output(pipeline_output_1["metadata"], pipeline_output_2["metadata"]) + compare_iris_pipeline_error_output(pipeline_output_1["error"], pipeline_output_2["error"]) + + def compare_iris_pipeline_outputs(pipeline_output_1: Dict[str, Any], pipeline_output_2: Dict[str, Any]): """Compare two IRISPipeline outputs for the Orb. From e32ebb6f3a3b072881a6ca72448f3ddf7c0633d6 Mon Sep 17 00:00:00 2001 From: TanguyJeanneau Date: Wed, 5 Jun 2024 21:40:28 +0000 Subject: [PATCH 14/32] Add unit test for build_simple_output 2/2 --- ...expected_iris_pipeline_simple_output.pickle | Bin 0 -> 361716 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 tests/e2e_tests/orchestration/mocks/expected_iris_pipeline_simple_output.pickle diff --git a/tests/e2e_tests/orchestration/mocks/expected_iris_pipeline_simple_output.pickle b/tests/e2e_tests/orchestration/mocks/expected_iris_pipeline_simple_output.pickle new file mode 100644 index 0000000000000000000000000000000000000000..06b2d17a84bdaa0b03aa79f3443ef6bf4292f986 GIT binary patch literal 361716 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 literal 0 HcmV?d00001 From 1bf65e0bdd828ba0a0c6d7c7690d3d70b88147bd Mon Sep 17 00:00:00 2001 From: TanguyJeanneau Date: Wed, 5 Jun 2024 21:41:53 +0000 Subject: [PATCH 15/32] Adapt tests to base64 encoded outputs --- ...expected_iris_pipeline_debug_output.pickle | Bin 6652255 -> 6624908 bytes .../expected_iris_pipeline_orb_output.pickle | Bin 361459 -> 334112 bytes .../mock_iris_pipeline_call_trace.pickle | Bin 7617420 -> 7617506 bytes ...expected_iris_debug_pipeline_output.pickle | Bin 5227238 -> 5199925 bytes .../expected_iris_orb_pipeline_output.pickle | Bin 33612 -> 6198 bytes .../pipelines/test_iris_pipeline.py | 18 +++++++++--------- 6 files changed, 9 insertions(+), 9 deletions(-) diff --git a/tests/e2e_tests/orchestration/mocks/expected_iris_pipeline_debug_output.pickle b/tests/e2e_tests/orchestration/mocks/expected_iris_pipeline_debug_output.pickle index 6a3c39d82cab639d7839ae3fde226ea7b523ffbe..3de0db89360885880ea0a07581350dc9ef9ef8bb 100644 GIT binary patch delta 9994 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 diff --git a/tests/e2e_tests/orchestration/mocks/expected_iris_pipeline_orb_output.pickle b/tests/e2e_tests/orchestration/mocks/expected_iris_pipeline_orb_output.pickle index 4550744ece6bb95acba308ab184475c1fe25f85b..b33dcaffba8ff8e22ccb3c3de34005896a7fa93d 100644 GIT binary patch delta 5949 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 delta 16269 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 diff --git a/tests/e2e_tests/orchestration/mocks/mock_iris_pipeline_call_trace.pickle b/tests/e2e_tests/orchestration/mocks/mock_iris_pipeline_call_trace.pickle index d3e61dc997631574ac8e09e58d523dca2dec22d7..f665c925a631f03d201346ae6b98a66829e1b7bf 100644 GIT binary patch delta 495 zcmY++$xjnu7{~EeCEH~VoTZC%ggH+)J@j&c z2p8$2pG#ck3Rk(t0M{90h#TBwm=SI<${4p9XM#zlxWhDexyOAT@Q_D5<_S-ER*Y^a zfrvV!j^#F$sF)GY#S8ILyb>|-TD%c)kr1;YDdxmmp^B8y#Jszyy!-vPV%YEVX%o5i z;QPXQQ2*gyEPSohBZco<@iupRN&iygChB$D3}}`ddXrGLZ`oM%?LNCMWu~lH!kA6Q z7LulwGIYl-U+52aJ88A^lhdI^z3=C}$hbcv{FlsCi&@llv=DQ|sSZ7O-opAw(du8fn8+kvEM8m2Ryw&LL(xzxML delta 445 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 J>Q9ax`ZxQTUq=7{ delta 16770 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 diff --git a/tests/e2e_tests/pipelines/mocks/outputs/expected_iris_orb_pipeline_output.pickle b/tests/e2e_tests/pipelines/mocks/outputs/expected_iris_orb_pipeline_output.pickle index 23138ae4ad2d4f15f684f583b4e0cc917309def4..499f84c954c26275f459172afc9b3ce6ce124475 100644 GIT binary patch literal 6198 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 b(*eNx<$wKyXf@W*`tDO-Q|tWv{Ko$S&*`GN diff --git a/tests/unit_tests/pipelines/test_iris_pipeline.py b/tests/unit_tests/pipelines/test_iris_pipeline.py index 288eb54..f7ce9e3 100644 --- a/tests/unit_tests/pipelines/test_iris_pipeline.py +++ b/tests/unit_tests/pipelines/test_iris_pipeline.py @@ -18,6 +18,7 @@ from iris.orchestration.output_builders import build_debugging_output, build_orb_output from iris.orchestration.pipeline_dataclasses import PipelineClass from iris.pipelines.iris_pipeline import IRISPipeline +from iris.utils.base64_encoding import base64_encode_str @pytest.fixture @@ -633,22 +634,21 @@ def test_instanciate_nodes( @pytest.mark.parametrize( - "config_map,expected_pipeline_name", + "config,expected_pipeline_name", [ ( - { - __version__: f"metadata:\n pipeline_name: v1.5.1_pipeline\n iris_version: {__version__}\n\npipeline: []", - "v0.0.0": b"notconfig", - "v14.28.57": b"stuff", - }, + base64_encode_str( + f"metadata:\n pipeline_name: v1.5.1_pipeline\n iris_version: {__version__}\n\npipeline: []" + ), "v1.5.1_pipeline", ), - ({}, None), + (None, None), ], + ids=["specified pipeline", "default pipeline"], ) -def test_load_from_config_map(config_map: Dict[str, str], expected_pipeline_name: str) -> None: - res = IRISPipeline.load_from_config_map(config_map=config_map) +def test_load_from_config(config: Dict[str, str], expected_pipeline_name: str) -> None: + res = IRISPipeline.load_from_config(config=config) if expected_pipeline_name is not None: assert res["agent"].params.metadata.pipeline_name == expected_pipeline_name From 7ee6eeec2b85403b16f3e5877f51aa202031d763 Mon Sep 17 00:00:00 2001 From: TanguyJeanneau Date: Wed, 5 Jun 2024 21:45:21 +0000 Subject: [PATCH 16/32] Fix bug AI-2921 on wrong occlusion value when iris out of frame --- .../occlusion_calculator.py | 32 +++++++++++++++---- 1 file changed, 26 insertions(+), 6 deletions(-) diff --git a/src/iris/nodes/eye_properties_estimation/occlusion_calculator.py b/src/iris/nodes/eye_properties_estimation/occlusion_calculator.py index 6247603..aa11292 100644 --- a/src/iris/nodes/eye_properties_estimation/occlusion_calculator.py +++ b/src/iris/nodes/eye_properties_estimation/occlusion_calculator.py @@ -59,21 +59,41 @@ def run( """ if self.params.quantile_angle == 0.0: return EyeOcclusion(visible_fraction=0.0) + img_h, img_w = noise_mask.mask.shape + frame = np.array([[0, 0], [0, img_h], [img_w, img_h], [img_w, 0], [0, 0]]) - xs2mask, ys2mask = self._get_quantile_points(extrapolated_polygons.iris_array, eye_orientation, eye_centers) + # Offset all points by the minimum value to avoid negative indices + all_points = np.concatenate( + [ + extrapolated_polygons.iris_array, + extrapolated_polygons.eyeball_array, + extrapolated_polygons.pupil_array, + frame, + ] + ) + offset = np.floor(all_points.min(axis=0)) # Negative or null + total_mask_shape = (np.ceil(all_points.max(axis=0)) - offset).astype(int) - img_h, img_w = noise_mask.mask.shape - iris_mask_quantile = common.contour_to_mask(np.column_stack([xs2mask, ys2mask]), mask_shape=(img_w, img_h)) - pupil_mask = common.contour_to_mask(extrapolated_polygons.pupil_array, mask_shape=(img_w, img_h)) - eyeball_mask = common.contour_to_mask(extrapolated_polygons.eyeball_array, mask_shape=(img_w, img_h)) + overflow = np.ceil(all_points.max(axis=0)) - (img_w, img_h) + pads = np.array([(-offset[1], overflow[1]), (-offset[0], overflow[0])]).astype(int) + offseted_noise_mask = np.pad(noise_mask.mask, pads) - visible_iris_mask = iris_mask_quantile & ~pupil_mask & eyeball_mask & ~noise_mask.mask + xs2mask, ys2mask = self._get_quantile_points(extrapolated_polygons.iris_array, eye_orientation, eye_centers) + iris_mask_quantile = common.contour_to_mask( + np.column_stack([xs2mask, ys2mask]) - offset, mask_shape=total_mask_shape + ) + pupil_mask = common.contour_to_mask(extrapolated_polygons.pupil_array - offset, mask_shape=total_mask_shape) + eyeball_mask = common.contour_to_mask(extrapolated_polygons.eyeball_array - offset, mask_shape=total_mask_shape) + frame_mask = common.contour_to_mask(frame - offset, mask_shape=total_mask_shape) + + visible_iris_mask = iris_mask_quantile & ~pupil_mask & eyeball_mask & ~offseted_noise_mask & frame_mask extrapolated_iris_mask = iris_mask_quantile & ~pupil_mask if extrapolated_iris_mask.sum() == 0: return EyeOcclusion(visible_fraction=0.0) visible_fraction = visible_iris_mask.sum() / extrapolated_iris_mask.sum() + return EyeOcclusion(visible_fraction=visible_fraction) def _get_quantile_points( From 52b66a8c82ee60523971f46bc03ad8d9cbe541f5 Mon Sep 17 00:00:00 2001 From: TanguyJeanneau Date: Wed, 5 Jun 2024 22:08:44 +0000 Subject: [PATCH 17/32] downgrade open-cv version for orb compatibility --- requirements/server.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements/server.txt b/requirements/server.txt index 7b855e9..233d5af 100644 --- a/requirements/server.txt +++ b/requirements/server.txt @@ -1,3 +1,3 @@ onnx==1.15.0 onnxruntime==1.16.3 -opencv-python==4.8.1.78 +opencv-python==4.7.0.68 From 3aa0bda588708347ae8b01bb05234b840d2847da Mon Sep 17 00:00:00 2001 From: TanguyJeanneau Date: Wed, 5 Jun 2024 22:09:05 +0000 Subject: [PATCH 18/32] Adjust unit test --- .../eye_properties_estimation/test_e2e_occlusion_calculator.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/e2e_tests/nodes/eye_properties_estimation/test_e2e_occlusion_calculator.py b/tests/e2e_tests/nodes/eye_properties_estimation/test_e2e_occlusion_calculator.py index fbf572f..d931dea 100644 --- a/tests/e2e_tests/nodes/eye_properties_estimation/test_e2e_occlusion_calculator.py +++ b/tests/e2e_tests/nodes/eye_properties_estimation/test_e2e_occlusion_calculator.py @@ -3,6 +3,7 @@ from typing import Any import pytest +import numpy as np from iris.io.dataclasses import EyeOcclusion from iris.nodes.eye_properties_estimation.occlusion_calculator import OcclusionCalculator @@ -33,4 +34,4 @@ def test_e2e_occlusion_calculator() -> None: result = algorithm(mock_extrapolated_polygons, mock_noise_mask, mock_eye_orientation, mock_eye_center) - assert round(result.visible_fraction, 4) == expected_result.visible_fraction + np.testing.assert_almost_equal(result.visible_fraction, expected_result.visible_fraction, decimal=4) From becdead831c98581ecc871ba839de4363c103d2c Mon Sep 17 00:00:00 2001 From: TanguyJeanneau Date: Wed, 5 Jun 2024 22:24:32 +0000 Subject: [PATCH 19/32] More occlusion e2e tests --- ....pickle => extrapolated_polygons_1.pickle} | Bin .../extrapolated_polygons_2.pickle | Bin 0 -> 17666 bytes .../extrapolated_polygons_cropped.pickle | Bin 0 -> 13442 bytes ...{eye_center.pickle => eye_center_1.pickle} | Bin .../occlusion_calculator/eye_center_2.pickle | Bin 0 -> 208 bytes .../eye_center_cropped.pickle | Bin 0 -> 208 bytes ...tation.pickle => eye_orientation_1.pickle} | Bin .../eye_orientation_2.pickle | Bin 0 -> 243 bytes .../eye_orientation_cropped.pickle | Bin 0 -> 243 bytes ...{noise_mask.pickle => noise_mask_1.pickle} | Bin .../occlusion_calculator/noise_mask_2.pickle | Bin 0 -> 1555484 bytes .../noise_mask_cropped.pickle | Bin 0 -> 1555484 bytes .../test_e2e_occlusion_calculator.py | 41 +++++++++++++++--- 13 files changed, 34 insertions(+), 7 deletions(-) rename tests/e2e_tests/nodes/eye_properties_estimation/mocks/occlusion_calculator/{extrapolated_polygons.pickle => extrapolated_polygons_1.pickle} (100%) create mode 100644 tests/e2e_tests/nodes/eye_properties_estimation/mocks/occlusion_calculator/extrapolated_polygons_2.pickle create mode 100644 tests/e2e_tests/nodes/eye_properties_estimation/mocks/occlusion_calculator/extrapolated_polygons_cropped.pickle rename tests/e2e_tests/nodes/eye_properties_estimation/mocks/occlusion_calculator/{eye_center.pickle => eye_center_1.pickle} (100%) create mode 100644 tests/e2e_tests/nodes/eye_properties_estimation/mocks/occlusion_calculator/eye_center_2.pickle create mode 100644 tests/e2e_tests/nodes/eye_properties_estimation/mocks/occlusion_calculator/eye_center_cropped.pickle rename tests/e2e_tests/nodes/eye_properties_estimation/mocks/occlusion_calculator/{eye_orientation.pickle => eye_orientation_1.pickle} (100%) create mode 100644 tests/e2e_tests/nodes/eye_properties_estimation/mocks/occlusion_calculator/eye_orientation_2.pickle create mode 100644 tests/e2e_tests/nodes/eye_properties_estimation/mocks/occlusion_calculator/eye_orientation_cropped.pickle rename tests/e2e_tests/nodes/eye_properties_estimation/mocks/occlusion_calculator/{noise_mask.pickle => noise_mask_1.pickle} (100%) create mode 100644 tests/e2e_tests/nodes/eye_properties_estimation/mocks/occlusion_calculator/noise_mask_2.pickle create mode 100644 tests/e2e_tests/nodes/eye_properties_estimation/mocks/occlusion_calculator/noise_mask_cropped.pickle diff --git a/tests/e2e_tests/nodes/eye_properties_estimation/mocks/occlusion_calculator/extrapolated_polygons.pickle b/tests/e2e_tests/nodes/eye_properties_estimation/mocks/occlusion_calculator/extrapolated_polygons_1.pickle similarity index 100% rename from tests/e2e_tests/nodes/eye_properties_estimation/mocks/occlusion_calculator/extrapolated_polygons.pickle rename to tests/e2e_tests/nodes/eye_properties_estimation/mocks/occlusion_calculator/extrapolated_polygons_1.pickle diff --git a/tests/e2e_tests/nodes/eye_properties_estimation/mocks/occlusion_calculator/extrapolated_polygons_2.pickle b/tests/e2e_tests/nodes/eye_properties_estimation/mocks/occlusion_calculator/extrapolated_polygons_2.pickle new file mode 100644 index 0000000000000000000000000000000000000000..000727a755d580a11c42808682111b82b8bb4cfb GIT binary patch literal 17666 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 KS~9Xn*#80cT`TGU literal 0 HcmV?d00001 diff --git a/tests/e2e_tests/nodes/eye_properties_estimation/mocks/occlusion_calculator/extrapolated_polygons_cropped.pickle b/tests/e2e_tests/nodes/eye_properties_estimation/mocks/occlusion_calculator/extrapolated_polygons_cropped.pickle new file mode 100644 index 0000000000000000000000000000000000000000..ff40f404e9eb05e1cfe9abf39c77880b35ea59c4 GIT binary patch literal 13442 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 literal 0 HcmV?d00001 diff --git a/tests/e2e_tests/nodes/eye_properties_estimation/mocks/occlusion_calculator/eye_center.pickle b/tests/e2e_tests/nodes/eye_properties_estimation/mocks/occlusion_calculator/eye_center_1.pickle similarity index 100% rename from tests/e2e_tests/nodes/eye_properties_estimation/mocks/occlusion_calculator/eye_center.pickle rename to tests/e2e_tests/nodes/eye_properties_estimation/mocks/occlusion_calculator/eye_center_1.pickle diff --git a/tests/e2e_tests/nodes/eye_properties_estimation/mocks/occlusion_calculator/eye_center_2.pickle b/tests/e2e_tests/nodes/eye_properties_estimation/mocks/occlusion_calculator/eye_center_2.pickle new file mode 100644 index 0000000000000000000000000000000000000000..994b75f8ab344881ac0965c654246b62e7fa26e2 GIT binary patch literal 208 zcmZo*nR=7~0(yiqi!zJ#GV}FP5=#=3a}tY-Q;VncaJg2dI;ZB9q!txVnLI_aaZ2qJ zjUJBp_>|1#lK6NqkG-I@ATuYvVv4&%lbmf#v#oCrRHzatR4H1?-=r|1#lK6NqkG-I@ATuYvVv73#(|RAt9fvRUK!qx&xI1+AOJ#j_67FFG84ptA z;840aG=BFPs0diiV@6^aAmM(a7^ftiH|QR f$}CGPNsUh|DJjZKDh0C25_3vZi@`1_P0|AZo7_?? literal 0 HcmV?d00001 diff --git a/tests/e2e_tests/nodes/eye_properties_estimation/mocks/occlusion_calculator/eye_orientation.pickle b/tests/e2e_tests/nodes/eye_properties_estimation/mocks/occlusion_calculator/eye_orientation_1.pickle similarity index 100% rename from tests/e2e_tests/nodes/eye_properties_estimation/mocks/occlusion_calculator/eye_orientation.pickle rename to tests/e2e_tests/nodes/eye_properties_estimation/mocks/occlusion_calculator/eye_orientation_1.pickle diff --git a/tests/e2e_tests/nodes/eye_properties_estimation/mocks/occlusion_calculator/eye_orientation_2.pickle b/tests/e2e_tests/nodes/eye_properties_estimation/mocks/occlusion_calculator/eye_orientation_2.pickle new file mode 100644 index 0000000000000000000000000000000000000000..dd20e1054519de427997cc4ab0d8fb0f80b0b9ab GIT binary patch literal 243 zcmXAjy-EX75QW#Dm{e)RE)TGR-r7R25E}z4`U1o3?nDP}c9;7T*h0|89~e+@?0lF! zj&Gqin^Vl3GkgQ*Y5)5-+e!}Eu{YeB8Ut1qy(h0Sz3S=4F*N}Lw~c=3@v}Z^E)#&7 zs{oLajIg=4q;k~6W!Iakb!3*&21e(wS2^$%T6F1KMgx*|9r}(om)*sgzQ4Wdo#yjB z**VvuD6apKhB42AmY0)kefu*zIla)A#Ce$lESPNV!PBP0hvpCCPniL9j#n56Fa*bC OOm2lXk{^o3()r{DA@m%j0;7 zO})ULz*Kw37gW7hd+`DhlrN$aM(?pV-3t{u^yyp21Cn+veMg(i_WaadU!Lv8 zve}Mqomo*7SHDTan2p5Bi^&0ht-gO&=k}Dipi_W3ldA&++H`oc>~8;Ej{!Q*OB4bW Q@m$8_mgpjdp=d1gKY12r1poj5 literal 0 HcmV?d00001 diff --git a/tests/e2e_tests/nodes/eye_properties_estimation/mocks/occlusion_calculator/noise_mask.pickle b/tests/e2e_tests/nodes/eye_properties_estimation/mocks/occlusion_calculator/noise_mask_1.pickle similarity index 100% rename from tests/e2e_tests/nodes/eye_properties_estimation/mocks/occlusion_calculator/noise_mask.pickle rename to tests/e2e_tests/nodes/eye_properties_estimation/mocks/occlusion_calculator/noise_mask_1.pickle diff --git a/tests/e2e_tests/nodes/eye_properties_estimation/mocks/occlusion_calculator/noise_mask_2.pickle b/tests/e2e_tests/nodes/eye_properties_estimation/mocks/occlusion_calculator/noise_mask_2.pickle new file mode 100644 index 0000000000000000000000000000000000000000..1c3423bf2fe2b09ffe25fca668b287545817cf9b GIT binary patch literal 1555484 zcmeFa&8{^|lBQJ!T>=C%gt&!fQW7)9Fr!p8;08+dCv}Ogu2yCCpe2xCz)wp#>%*M; zV914m@9@uTzZPa5;Snp=+Uw`ZLAu#%ubF#15zFi3cTQ&gFaPZS`hWkZ|LYI``QQK3 z-~aAU|NbBU^l$&__kZ;_|MIVX_q)IQ-EV*RPyXT`|NeJ>_ve50yZ`aGfB)Nm^}qi1 zKmYd6|L`CGV@NfR* z@BjX<{^_6o>fiqMhyUy^|LO1k<{$sn?|%PJ|K@Lg-$wl5PZr~EfB297)!%l@zyIye z{^)=E?Z5luzy9t2`0aoA?VtbIAOHD(^UwbL|NfKz{kMPdpZ?)b{`U9(_Fw?|=L2fA`=1(f{*b{(2e~2tWV= z5P$##AOHafKmY;|fB*y_009U<00Izz00bZa0SG_<0uX=z1Rwwb2tWV=5P$##AOHaf zKmY;|fB*y_009U<00Izz00bZa0SG_<0uX=z1Rwwb2tWV=5P$##AOHafKmY;|fB*y_ z009U<00Izz00bZa0SG_<0uX=z1Rwwb2tWV=5P$##AOHafKmY;|fB*y_009U<00Izz z00bZa0SG_<0uX=z1Rwwb2tWV=5P$##AOHafKmY;|fB*y_009U<00Izz00bZa0SG_< z0uX=z1Rwwb2tWV=5P$##AOHafKmY;|fB*y_009U<00Izz00bZa0SG_<0uX=z1Rwwb z2tWV=5P$##AOHafKmY;|fB*y_009U<00Izz00bZa0SG_<0uX=z1Rwwb2tWV=5P$## zAOHafKmY;|fB*y_009U<00Izz00bZa0SG_<0uX=z1Rwwb2tWV=5P$##AOHafKmY;| zfB*y_009U<00Izz00bZa0SG_<0uX=z1Rwwb2tWV=5P$##AOHafKmY;|fB*y_009U< z00Izz00bZa0SG_<0uX=z1Rwwb2tWV=5P$##AOHafKmY;|fB*y_009U<00Izz00bZa z0SG_<0uX=z1Rwwb2tWV=5P$##AOHafKmY;|fB*y_009U<00Izz00bZa0SG_<0uX=z z1Rwwb2tWV=5P$##AOHafKmY;|fB*y_009U<;64QY`0HQy8O>A(KmY;|xPbuo^9}H5 zg#ZK~a4!PO<;-6{-wQ8uApijgoF>5id>S2%5P$##?n;3B`L1{w4gm;2;4}g5=hNtD zga8B}a90A{&v(Vka0oyE0;dVs{oK~mGiZbW1Rwx`>j`i_U(b|@5P$##t{}kud<8{j zKmY;|xSjy_^Yu)b2muH{;AR4HIk)iUaSVU}1Rwx`iwJN(Uj&jd5P$##E+)YJd@)eQ zK>z{}xQGDv^F<&T0|5v?;9>&Y&ldw_90VW$fr|)mKVJlrF%W<0SG*p zz~Fx7FQFg&9@q{75P-m%0Qd8n2ss2G0D)W|K%v$0vp z@g|miW1(kKCWHV4AOL~;5{TEZ>=g^#&w_>!0uX?}-3g>SSawXcMGyla1Rwwb2;7Z8 zx`Sn>RO5aYI)o5_00iz!Al<>TU8-?E3mrlTKmY>wB@pl6v{x)kmYfI>fB*y_a47+~ za$A^2$FgL}i2wlzKmY>Q5s>?~hsEgRi9*rwqtbG^A3Xt^LjVF0xPU;sYFk#!Or9td z9X~29r~AWQa)D`2H0Rad=;3o;lmDa+olSF6Cw)!4e zlS4glSGIxx1R!ud0lCk5xW7eeS%m$*+R>Wp!U;8=?8E?CAOHafJP(0*rA=Lysbvw) zKGM{dlf&c5CY!K900IzrP6FqywAi&|@$uXc<&>E(6;22?%hnSk&b3_lp8(DsfG!9? z;4KN*W!Bf(M~q!d7F*pA<&>E(6;22?%hnSk&b4qqpW{Xs1R$_Yz%H}CZhN&F`&y<& z`G36<<&?>oR68WdAPW!mpEfVMo$h{A>tM>%Eg zTMtOi9YF?JxYKb_dD-ovS@#=rE8hE^jL8syz>t7lW^EmMs)F?OFq*s^<&?Q^cl+i{ z-HRY2Vp!X+R&CF#=Uz3>&eztfMluHi5O@rMvp-+y>tQ^um!q6A_wC*u&6&CvK}N)| zwqLE$MkYM!01tyhg?4g?_Z7y|w0dvq`P(Jx`_*J`Y!Os(MpX@zJm_UVQ=LbBL@ zugo?a&NsvAd4~SW3cj%gmrZ661R(Gb0{yppbSwMC+Zy{#{aRxsWoixP)VerRe-q*e z$tv^pnr%3oY=%7)!vSXtzOe;o2hj%s2)q&j`^EQleR8d?YbmeHX{?yy04c1(=-bL# z<5qd6X4?*JX82l%rt1s3?Sr?z{$ZF10SL?!h(G>ypH|nklvn1iiB(LZwonSIF#5K# z*0@#PsoAzen;E{=q3Qa9Zu{V^uYVXOLI47j1lGU&bnB>Lccz+K9IssK7L!R|tH>!- z-$rbG8%5P-n*6R_XBuj?awp#|$&%GJF1RWP?`CVj0U zr%-(xvGr|Owo5Yu%=5KwPrIV3yIFk2TxLK30?$vte)GPrkL-mOtZOM(^Ws;*+@hKE zwThfV^=-t~w_({X%?vQl*SbCJimL8r@fCBK0Rad+KLPvA_q8~$8j5n1)SP0oO2MQn zF6TC@n(SN51TzD~)54jl=R9#A&&~~_3jz>$K?44}UxMNR)KHY8q~;WxRSG8Q%_`*{ zx@k9%)?IIDm>D3R7M5c!5#v6dT{4>?5P-nb6Yw9s1?j|A=xZt$LhR7Y%&N`A*0;aL zY5V@cnJdb%l9>%+AB+t%7qX+G&8emGqLsUuW{PG zKRIMTs)-=B7E0lwFwVE^qT!5z00drwfc^4)O{ezC5~Ok=#175OtlCU$efw*iw(n04 z8IWosh^>dcX0kYshBhLtM+f1D00dr~!2T@|9W##`J2bOy)n;Pr+h60feSdPufK(Gf zY(4BXlf`*7v=M1NItV`mAn?2d;t#w!XLPh|$5fR~>$74uoSzCif{oo@)1a-EZjKHa z5DO_2X9|;fMo3dsm>GZv0uXo=0{ZLTy?VR3dNdhRl}(ESq)EeQ*2;Elefw*gv(?hg z(IEq3A!XuBVKUDMX^ILn1Molq0e>f+u#wwOR$ASS z3IPbbK7r~Nf2+1bC64drsLf}xRc}f{lNU{9x<@n*y%+*7Zv~-am9)M^srpyeUuos6 zw7MG=0uXq80@W}6CT$AIO*?qAY*lHk?D9J3t!uUCGSE?&Dojzjb(7vNTlqlITIljR=(e@m^EhJ5x$3VPO>0&OSB+n(RmW#e zP2NHP0SLS$f%K!lRfv@Ow5av$9{aSJajW4bJ7y?3^J^Y1z7ekf%NuT7$)d-XpS&Vub(%euhB* zz21B8cq_EsqHr)vryW1U!O4|fcGL1eR?DxuGDxdLrnFFsy&ysr#%>!h)mn{}dlBfr$$LLj`sGLAV3tlheu#sUE4%Eb<$( z`hD`9-zN&U*4MQag+p07p>?OiB3SkGDr0qq$Su@Lh`TLFtE3*}>dO7N)KBo1fpL7x zhvT8%hCu&L@BKLTYw+Eoa41V>weD0{1goB2WvtE+xrJH@akm9&mDGb=UAZ5Z`U&1L zFph8ea6HtT5IFdicKeBEw|#s-6ehF2@nY69Shs2xTkW>Bnr51>^%lKc*Ie}(;i9iq zZ@PCN+rL{V=-%zoc%FA6U?1w_Gj;ohdSBc?UQD;**tTR-9Bkno9)w9lSP^e zd!Jct%GX+HR=3)1YcNe0@ekEI zDk4_gsx^<5&AwV&d8K@%)}*zhxv&Jqlv%0uCu^a_{d$)Any^%~ur*73alRCOEF7OG zR-QMD=$`kSoD2f;txm%5j>>Pvty=R~+3c&el~>AFYE4>8nhQ%%OqrEhf3g-@+^=V; zuL(;<3tO|q7w1di$HMW6V&!?Wi0*mM$;luf-|7^M9lw>USypSSl||~I6~5MW(Q8uZ zS`y4Eg`LnkR_jjJ_mgN})wI;Lkc2o(tU5YV5#wGM2eoXk;cz|e@PtBqs3$xiM}mM8 zkgwIXtzgG*7sf#?+iN&nPdhxJ5FhFZ56F=q&g(sObw2rLBR z2Rj8tS}MPlw`$E}WwZCy)UP#Gc3Q-&GuBh8u}f=gT4gf42v=rHlXEGkT02c$J-^HA zN79MAu}txBx9NbvhdybL9_2~T$*~}?5ReacmdIAiR;|r1$|AK;&oHc11=DoE5?i6K z@eEBfA@!*cXRI@I#yC2cT9o?pM$Jb=xn*7{Kqhl7$OsQ>=TX(iJ#Ua6>3NUJ$sn*0 zkZ*RD$X3f%t<5jWBDGM@FsxJs({#WRTcNM<3{5j3^{EhNtTT1SI69YFl(?TGu={2! z_VH`G@77F*z$pUq&Cd33wQSYe{Gu#U3-t`cN>wmT2Q0A_`WnyBG!s&v3US6dQ)i5$ zbE!p%`#A!;Z?<9|zqb2s&2$JHCvfhAm9DhqR;`(fvPgP6tGvXrRjk!x-M(dRHS2bN z9mFYBR`n%cAn;YSuITJ))WqD%v9|c5G{|f}T?f`lStmVjTs+M49+s0qU?Gsc+9@v5 zmHb<^W-iJi`E3=Q-630P%l8eNY`tU4+-kO!jIY8trOKwd0)ek7))gmqHELpR5SA`$iYlmYF?2Rhpc77r_TeP&>C&X@2^!dXA?*GzWx0 zBXI89wXO=G)!LfMQuA`9EseuMI(8KHca4)ROMOW&=U`3#CM4@(Qq{Ut`$iYlmYIDu z+2rI;f;n|frd>h1pFOS~=Go88`5^E`p!#-A8xXQ`Ta#m}mUipi(jpV1DuqFl__q!v zwkk}mQK(u>i%!kVVQZ^d__c~vWK%`zp%uPXDT*xWAQW>C2fjs)`*dd-@T9`vxt{d2 z918+#0)sEuewl0gS1v87yj6>>&Dp!9#kH_WH^i+{lL*n7b`@s|Q)?8GUxet?OiWAI zYAT6UWK%`zp%uPXDT*xWAQW>C2fjs)`*dd-@T9`vxt{d2918-0z~J+Za_IO=AH{04rj&s5syVE{Nh*~b6(kDJJM}xqMK>ZDus&qKJsYIz|;@6tVs@)n7W}5tz>+qSM^0XWZ0yTl*cRVb#IZ{hgw`$!ijzi(rR%y_V zxs_9k38{4;m_M$E%+jiTBDZjVZb#+QfpX{I<2LihU zQl?F@Xq8&+RjDOGYUe^bKwIMg-%1=m8OGB|_LGNVZwQtQrEEeevQ)`1``F|7U3lQM0JMXS_muSzWmQacyg0oob|_*UZh$uOQyvY$K@ zdqdzP0r`Sk*oH)5CexjxUn?Pww#h6^_>~M>_sz|5sJr{)+=By#b}z_y*IHn*YD;W| zwo0w5BHSF-`?RsTv6vnmZav_m&%%BXxQsyju-n)u%w&3&=+{b!qir$^6MiMb)_rqx z9O~}=IQQUyq1_8I-nAB(tlAP=p{-Kust7lS^*(K^ZY-uphg%Q$=(DgN1g;?vzv@0y zv?NF^h6CsDl(BmS!S}U(B*0EaGww|v{MJj!<>V<*4 zzE+z*I(IUkU-wygbBdYLn!1wXtdJ%r;p}AkAn+gp%Qsu?6+h6pJ#QKm$w3G6`QM~wb11yh_%Tqj_6rxHB&06V5YRH zDa6USFNFThWPcV82s{CS6pJ#QKm$w3G6`QM~wb11yh_%Tqj_6rxHB&06V5YRHDa6USFNFThWPcV82t15H z|5jzY^zllqg|XagqHUSQwi(75mTk6d7j1;EL%Ta95OY^Wh@muTxR_K};@H}=WEwiR zx?`=+tTfv#*7h^&OKWO1M=GdbrnIW*5pg;I0t9}VK>zUN6zP{BwHC&5uZgy07TabR zXIQq`vR$+hz7Fl~kU-2`6(NSwq~T&xVTogF&ys2A-0F_CKC{wnw^-ZHtS_yp)f}my zf|=5)rboo-00_w!ZIM1`^-wS-C}Jgv!-fIt!7FE70i@YHQh5#8zVsASqS)tzZ}Rj3F1km z(8695ZObgSLt&eF$e+ydP+Q}YGnu<1ZLtVd_m-BX)aGOx3(J65?lUXRc8j&0%$lk- zwVEjvR4`Lo)pXA|ZHxecXCdGp{&FDCB#@Je+Cndg?Amagm@f|7mi5GVsG;rqfkH!^ z*-KHgEVNA|*5_;8xP>wMZDx6;Z+9Gj{vHcuXAbI?+ZW3R}nJ<%ZWYy|vUUk>J( z1ae|gTj&LmT^o)Q^TlD?vYr?ZHMD&{P-uuVdnsy`g|>;r`h2Z>ygU=jwHCmIBvi-3ITb}*58Xoc0~XL9tL+SgdSU*@|OTX;=^!uBPJ zL&9k8g{Uo=bj*fv%CIZiuFGV@IJ6g{Hlia#zQWjK#jf@&I+o0Ay1H!(w%E|U%8 z&|Zkzh>i^T3S*NMyV|qpSTeKe>b6Dn?505H6GPzn3B=ERId7C&=1&nTCP!8M>tAbf zUL)8+zSd%*mMt!9Uy_yM8qK{HwP9->yujF-*%A7q&0S%5jb6UW?kW zH4aLdOi48n#P*o5@4enjPBactt)O-_J=~Pnb?+W-m0s<6nV!R;$@!hnf~g z5}K+SF$7+ffc*sex;#ac2AMtYpnOQSSvzZ-IWR}lk|k&1`I3@v5t6yvl053Kr=C=c zU4KXtHt|_@&SU0evx2V4vqkj&kda(~>1;;rWu1&xGi2stDb)2xh76l-aeFAqE_9?>Vs#gT)iXj-!5EIeOQ@|h6*O%wE+!TE+E3C;= z+qCmUi?-h(u1=J+v~O}eNJt0iLfpMFMaPo4O=_+!K`)5b1jz^?@KOZiLq8C5KmX)Q zo_bN~fKy#}Fr|ZZA?{R}qGQS2CN^G)4E_8D+Hc{K>IG^LDqS% z`uBY2pzRiSh=<$CX8U%1(a^K4jeG4}*Z$m_9Mz#r+Zk-O2d>qL()P)e9Pta5vb4Ht zS{E#1g}~hiET7uc^z>2MXIph!?S5E6=%FN#`Gnry}$rmAU)3+Id{x1HxlEYcsM(v-n(n#|P89b{rdVDw^+;J{S551JWvmdm z8-etR4tGi)rG2(lht=-4_FZLub&Oh*rmwIzHFf1HozXKKr3GnG`DtP~pNEGI=Uw*< z1}92eMqet&mn!ADud8C~VKkFL;3@*k_cb*veTnv!R^3*+-`aPT`PDIMO`5*K+SJsQ zuXIMwaFiCLMdhc7<$N9sGZ5e&39AB!G>%Ojvt%uP}27#*xEZ^7uF#jUW z>O%a$i?TSc4BJj|oNc|oYTuSyef49WI)jbf+WLoUc0^wfoyWWG5$v2KZ5jQk9N%j! zORE*SWSOt#OoA0yA#jSo@nZ zr?Bl5C)?KhtM+ZV)mJ~}sWaHvt*w8!W=Hh((0RP;9>LB@(w5Pm%JIF%vb0*EOP2X+ z&LmiY6#~Zz*yp63i>AU7o74ZW)`j?iTceck6tWZQaw)xIsa`sN;UYuby|;={M> z==oDenizyMK2$@JtR@7PCszHny8a#X%6I-9S+6OT(>!f8+hJHTgAfA02-xSOKo(7f zB{rvjcrK)$Vf2gd+OTaC2c|O*1??M}rsnIUuXtEobYjyoQ=#}sMJF;F=DyMncYPQ3$WCkGwei7)OQ^c`oQEFNI@I2cwN8dfObl|Zd z#~7QIia1+XnfD!4*Q;pzPq#RZKF_pXVe$^Zc*B>);KpKe_gx?rg#G+$t2;QY=3t zJMcQ>JUd`B<|1_J&X;;n_b8DjY%Sv>G&Cg<7oh36PJWmXFZrk9m>p3cfAp3hom zp^5)&=%=g)v~pGOuDZN_=Q=`K9~EvJAGbGM~sz6W%21AwJZBv zPr%=#j5bZA7Ml9C#;mxL)cmM89(OB?C%VlUXSHl=+{@PbQ&z`cDpg@hGkR0z#bh1l zR^R7f`%a&i(-C9kQCWPtNA1c!7ZdQ;80VT?t5(%DI$Ng2hs5awTY{Wu%NCzw;FMV{ z9GH*?1dQIGye(VvYHdv`C8RbphuPP77k3v0|G{GP~MiUd9}8tl@d~$8N|KBng)}-yaaJr ztUVz6;q4#rgrBm7zsdN(CaY>%y{fatwdsE9YWwXp9%IWp<(x8Y?egKZ&hL5%@x4f9 zTZyzE_NFbLjcseRN#OzEPwxGI$NQ`;{AI?+Hd$5E>Q$XBu1)t-SKDu=@fcg)Dd&`F zYnKnNb$-`Fi0?%*+e)PUus3b_Y;0SjO$rYPe{$~!Jl8Lr&Tf3; zHncy8fWJ@K&oq&~LX+!N&9&oxs_s1#$CGSzh;wWcQwzsFDm8)5uddyq#`hAlWp0#q zvVM{+lbki$q_7H`Z>8Lr&Tf3;Hncy4fW6JQS*>o~&oaQe_t)06Z<=w)?)zaHxYN@8 zTc$X1%Sr9A1mc^~Y&nMt7sT>xaheoXVe_q&Thh4em6LBd6}_hk*b9vVYIXB2l>yeh zzqY1*(~LuQ-w)HkotEz3GR28oPHK-O5Z{bu%Q;lIAeLu~)1$4EBR~pY#sis}F_AM_M7tg+*#Vu$3L*LPH!$B0TC!jBf`}umlOuR1v z`+nmtm1^2$Yv1yMaq;Z?S=@5gKlB|PHylLadII`#xSy}*%f!KW$xM5*HdLjb-ZN&pMEeVUSR#)~u@v49KEpHy^ljefB-(1hO z>-ve?`%yhcK;BPll%O8YR0WT#ir;kh8_I6jtE{w`ts}WveYn zpNv3%VewFl=90Wp*Op*V5P3dq18Z~NvaTIa$y+P_-N;jREeY#y!&{fY?NOe} zthYAzYF(REZHL)4&YkVr_T6LV_Ks8!5|CHqouZ|%o04pT&luj}=CF>wnwr<8p5-|u zZql;3He5eQz~7mip=(K4zfW&n0=GwbDzo0&+^cnMR<#{w*En~!Yuk5^ncF*3JxD-a zk#~xg!fr~k2|k1SSx^uv0sB_IR@HySUY1(dOmIi+7kLTIzWJZ_)=gizH`X7qm8(|K znoY78+K2<|%>#SUz6*OU%5BdH>{Nh-6l7ig=w`B2i7>_K;wp0W^TAB z{Vaj>mPANlKWdT}AwEX!;eL*kJ}}b{qj-ETORZ}rB!>$jF#GtLjAPv|K^XWeS;|#G zv_3+F7|IS);ceaZ7WbH0?}U020eMkAN3;|+Q_>c}yO*H$e5vM(4pY-A9jP`J;_!1b z#?z^sC!lYpnBrxrbGw-ut+)+DBO=};Nb62!%~`FU=79#RKSRJ?{_2HAc{fXEA9t8FI!vwB=B(tT z#fg?B8^X^Ku$R);bPg$7&80;cWG-8fx~-5_b5?6I&j@|cz*+6GtJ$nw;{ijTd7uI7 z&k(Sezj|R&-p$h4#~o&k4pXbOIV(A7aiV3(hVXL)?4=A|>ALKBJF&-0uslMPG_xmE zZ6h|jt{e8vQlBxf=myiV&6;l5H7*l(T^OE#fW3TQ?MyDr1lHv1R#P`6hxe|7=3Gl< zo+eKAmBSG4K|o$nHGW<8yqP^-f~7l3n%QvGHe$2ux?$fe^%(<;ZZI9&tm%eb<1%sA zh2aSZ*vt3T&g8;OU`@VmHFZ;Rc<(xB&b3tLY2svGISlb01mqQ^@lvxl?ou;*{QXu% zT7}E-m9~+lv&8*2I@cwewAR0JtDRVvaVPq!mMdpf^UH=FOm@@+}xVRJ6A zb@=LzBIu@6@ZPh*dg9qR^xchsyrQ&+)LOWdU22)f-xr-ot1x!2iaAYZiTmY-p<NE?kIw8N(Jvd8>}auokQP! z2*mf3_KAJ0{?TmN@MshMHzKh5ykUk7mn;N1O1! z5rNewXHRBw&n@${$rf9OuU1Iwc$W7b58^Jz`{}qlft^<*<$e?qB=lp77DIDHbw9Jd zw(gq;j$x||KN|L}j&|XHHv;;}$+MZm(!TFP}$t&L}A6h1%m@t{vT-bcqf5|D4p zAJ4QdSe91zTaX+gG>>E+TI<%@&F1h{3#`EUbOh`b#W(23lycWZkg)PgxrK`(O0(J4 z#XId95ORM`WNDdL2M=}qsb!+Wrb9k!-R$zTP0``i0 zjVE+@_#dwpM{g2y3KvIGRj9u{b*$qyb1fansjwr3Njqxj|rCTR5&v@-;KK}20 z;_*H@-j{%UW&I&d@JBWwu3okvmZvgn-C5hL#gS(2^ilpf0_j~Xf}barn;Zqp27W8| zbumS0mF+uL4WqwdvbI&ZpZQQ=lMXdu{s;p0ZS=K0vI+g^ElX>Jlgj%-95BoVgNA+z zKTn{3XMV0&ZgLbX8~Cl<*Tod2RkrU~HE=&Wd4eA&5I@d#925Gx|={5*mBo%y-ya#HvG=18qBn4_((?FNU;ahmLRAY5h~@MHZb0`U{GtRb*)unMGs zV_TN~aafHQyQw?_f$E+8`eUlE0YBD{ zA`m|@%Nha;2dh9DIJRZ!ABWY5v75>>5UAeS+55Sk)z$v2`wnBW<-DL~+Z3r`eue3Ne$y4dFiBYWRK^jvppay*BRW!At3|21R5D=rt_AtOjP7N#TZYAAL1^ zzYE6?6R2L>!TZ^tpq|y$ZrXSFu&-dteoHNELIQyc3G95AzSEBHVA;pk!C+c#Mtvdz z^_%kp*5#;9nY$@A#>~fj2hng{_PqlU1g<5p^OgE$JHCTuA6o~5X|);ki3rqh&JS3Z zquR|;i`mpTH-#V$x+n`fkU-#a0>ckgcN&_c(EO5Zg*rLL!>(u}dqM*ByYqwA<*0UZ z)M7R@&P^eRgD%R#4kQq`oWSry)t!bWDKx)iTcJ*l@vtk}$exfu{qFprbvdft9JQED zjdN27;-HJNumcGME+;VjP<5xFNeay`*;c5NV?6ANHnL|UFnDox%t3iAcl%n^+y;(6 zHFvB79dk{*-hmtf_aIPzt#PN?qiXl7GK%A5&qnqy-Ax2fNnr5q?5us<{p1 zes&TB?@mB}0N-(Z>Ti7%b^oZUdYtT;TGp|f2%eI_`hES%?X1IcEOdL+B(rH>tH?1_ zQ;4JO<}A!6fxuk})Ss(t=?2kS=<*UIA(kzRBgRdO&zks4=!ETw1b<60uoFhrv*P<{ z9bR4sHtlO&*F0BKh~05b5N4A=;I0JfZ&tQ+gJ>;uc?psb%a+9v z>319peZ+Tl6~!IA7-h02A>i*P1*4DQ*WXM?ei7QUN!CJn*jR{}*gTAmnao=WAnm?Ci7MT2s|BugJ1BB z{eUOokls{7_-O+2b|ynV@ua`otZS#qY`*UJQ`s8{An@D-(zp8%p<*H%S7o%HBOtG4 zGW4@f`n%1#cACuQ>yAH_y^#O{&rKkGyAKg6CbDr=M*BGe@@ghSKkLxn9I0kqsSpLT zRU@La%A}hR0xvq>2KS3b9nr*?4xMgdmSoW@UA=+-sWD`ONyc&VqKK`w4Ct)TRB(R#V zQX~910{%Yc0zc@My_sU!yViwhyDgJV2qExl1aABIx4NB#nOu;-YQ9R1@aG8l`}pp7JlUe5AtCG z>o>BO%LaSa@A|2x@AipEb|8enD-&40_MHcq_(jxXq_NsU%5{I$)HUwsF`FAI>o>BO z+g5wNP(RfaChR4)11khxoWSy_??1!dFJd&t`|2&cJnApotg9>{-ax>}&aSrAtbg&H zYC5%p?ZgU!7bOsWG2>^L`gs)X-dEI`z{t+7w$rRX{Kua8rejV! z6e|Q?p1|_y?>~k7LewmB54HFet6^R1xj1UWN)>+#S4Gm>2t>I1neDdkG>3Jx)ugLe z*@6%PuT5b2aqK^W{D#ylau2om6{}%g>$x~;!%7u@3s*(b8wf=cY^xnxEL)Sl_vpAe z^DPJ<@F4``M-!h|3SVI?E}1)uT9dq7Hm`cb+{0C!^acV6?v7wv?bxJj9bc=+heDZA z^+}l!0-r)aehTrCrSKKT;*yD@sD4TNj`bk#7)(SyLBOF-2DZhdz1gheYn|_kPMHx4 zJ(Dsa1U`j;{1oCNOW`Yw#U&F*QT>wk9qU2fF_?&ah=Aif6bxU=aEcykXsQd^YH@N} zffWLuML>QE>A|Je!c=a8IjWkJ2i?8PW^|KfgNew82pG*{>$1h67CqFV#_i9_N9lZG z2z(9!{|OYo6sfhaVq%V})-)>*x_ehNSvHu6e29S2Jhm=d9B7dv9q6jRsPNJFG}(a= z0v|-ce*(qNMQSaqpon9qJU_r^Ruj<<5pb4=f@O;X9de`tUG*2m{VX&IKc0a6pb`tI zwJ?>NAda2#`~aU>O+-6Hz*!y&+7?IscB=V4>xgUBA01>6_#guQgQ%a`%k}--n#@u6 zd%4@9$y#I0)o3O=LBLra3fe|T{dTtbKI@2U)gK*X5cnVh{)4EW*~{F|HK6BK<@Fs1 z+cO<-hc9EkuLf83*G^Ojd;kIeLDbLc1ADo?eOuFxL&`0bO@;-XDSpDE1R~*kW${##yTqoJ z4f>J~0RkUIz<(ISr#8pXpg*o;vhJ-lH8sKfRSj*5fFZo^8jmu$S8Qt8pfC9lAn;KH z{D(1oYI6(?`r}F_>)u*ZQxnWz)zD@M7{z<7@f?%8%r-?I83aC!fc~Jio_+D{8h%ZS zKHZ{@#V`AqSE_`E2^htDt?@vUyUaF49~lHbje!23ww`_Q?HYbfi$2|=j>Rwgm{+QV z#|Rk0JFf9SQyWdTL>n0dK9GR_pl*G3A7p_KG-0s1ZwVHYOV;87!65?1?~ZFc&(ubf zEzw2>fe$3$Kd4)u-3M9V15FsL?puPzFUTO*2qp;#yz9eshW#z1WoQ-; zAp}00fd5Rc;o##YgKF2M)eADnHG)Y30`K}TondDS_iy>!L;wK@_J6C5YL;JjM4SBftE0O|km&7f;{Gk4n+PBP0q*Cw{sy*g!rN*I+s?S|-!i3%2oU&i z0`_~k9)nFzYuhSnX>Th_!6g0?TA)mX&h7FbZN9TZ_ivffL<9(YI05^;T#vyfr?qVr zwY0aDrC<{Gvp|^$o!bpT+otGFn-`v0Da}HF00bHV`vu(zhK={t_Hm&HCfn-eB(#cY ztosBUFgTjrY~|aiIq$+v?>cw2Eo0`ve^14ME$b z=uUeVo>?i)LVy4S8UgzS-3f+`_to}sp$8`0>g6P~ifOF-1RUhWLE8C-`Dqpc1R$^w zu%FfaaM%ugm3@YV=J8tP7maX-fP=g^NZZ~pKg~jb00b5S_OrSl4%?xxvd^&4JYK8( zq7m*8aFAC9)t;y3SA)Y!Fy}Ive$?O7atpBlionKdNK{0)qFVFl|)N zO*RAwK;S5W^`AH#?VtDi&p5SsWeKEg1(Cl0&f()uuG_y_YzVJg6M=ekvLQeK0!Ilv z?w0ePaXO+`mO#o@5Q+O)VeH3-@S?Tauo_+Y&>#STrzN02<8(+Ze*d2gQ&)DA+q$Zd zt_cXME5h~oue_RD`OqK$fu|*)KjU;rEq>p+rmpNJw{=w^T@w&iSA^^FT+WFI0t8-> zfL;&9`L(>n%_CaukOEh9M6=42)CKWsdU!=XKA8|80D&hb;6L9M6i3za5;u=%u|o>g ztZ4q|OiA4uCy>>^WI})d1YUxG{GO}xN;!qmAIq^urUW9X+cKTI#MQWDLVy4SUV?!9 zo~!dpIfc<5%dtkL1R|+hGM)M>uErHMBnUv@1qh_yb2@W!h_Gc&HH1z;_}v&5M5~E~ z4G97ecmV?G_ngk093pI)Qw^aL5Pr9W1`%J#En0ufb=d8%f#A`n z9HKl{D2`nXC~Qa&fWT`InEgptzD4VgxemMiH4r@dltYxq3dOOj0fh|-0uXo&0<%Bq z%C~6!G1psO4S-RH4 z)IkgZ2)r7B>UUi{LuHGaH;L>Tfe{co=RzB_T8m*T2Pp&~@M;8#-*uO&2N{}MLHueH znXnO1HmBkN#bVaLi3|YkzP?ww~BDU$yuZdm#npj0IC!H_%07bv6K4nFm&62teR<2-r_s zPi&g6TKtN=kOFhYf~l+<=pwQztMOINrqyOt2teSq2rNJF)sc%IeRLFtFG)<|5cShT zp>jB^*$u|jW>g43;I#-WKk(I&iywV-6oxNJOyUsr(?g+hEF6NX#th9AAOL~aB49u8 zbZRT4wKX*TOd0wqKmY=-MZkXJgHv01 zevN~?FUlX9IgR;Xi_~DsRtq%#OX)t1|g&G7P z@InOaH@co_orGe3G@>#No@E1FXIGlU3XGn)ZY z4VdY}0|5xU3<3L@A3d=3Rco3RO{x4DLWD5RCTXMrlYQ7A0D%`G;6L=EXZFS-NVB3R zl|Mj;1jgAUiZo!d4;us^@InOqhko?T-dF@_R`jIu2MC=&d(em?X7e0kU(4oOaY6tB zuR_3o=%)_s-7%?&N-+XNMC4FGIk8=%-HX-7%t^ofL-;U5;57*N z5B=_ z1qk?W{f4vpj1UY4if7<>KUto66`p$5IOEg^njipy7a-uj^&8IWGeR&JD4v1i{bYG& z4bMKP&fp(@K1-242)qOV|GA$%tq)iSgMnfjINnT_2iEZHQ|b)v=d%>)gTPA=@SpqH z)B1pQFc>Jdf#c0&d0-E3ctoGU<$ME0S|RWf1pMcI_Ow2r2$3IPZ_ih%#z&u-`gilCn9#=87&sz0s-H$3AtQ*Jl_tq_2~qX_uV{p^N5pa|-j zZmi4iruySraKi)Ih`!!1h*k(d;57)`{9})|M#>5b;wh@{CCRh)@P@~Wvu+qfD+D0$ z8U!xBHR2u8vVwwmit2kw@~mIsO%JEDZW=^01R(Gl1nftD!)a~AV67&LwJN`lCJ*`* z-t=HP>!v|8LjVG=LBM|WH=Nc+4AyG0SgZ2;X!4-9UCs^3m9yT5Fi!%3_a?CX?r(TR zy*Gw&nlH{&dmllblKc66UMo)mfrk-T?vNXH(>rJwr}^Scwf7PHDKB`WD`#DB97aF@ z0uLat{O)ht$FGi^pxC8#6zaQ3&f|gyx^mV9$6*8nAn*VJ%kTcSef;X!35s1>N1?up zbmh}I+joJ(b3gzBGX$2O{)}<#@!MlN7!P>MN`rCPb2-?{CNc;D5O@Is%TNEdbNcPE z9o)9bM|XFyAB}*AhrnkNXg~g&AAI{}_Ug_1eCPn~=MR1HJT?TlpZNwL009WRH39#N z^X1dw4mQXT#vhj?Zx*009WREdjrXmnZaW7`!bA9t;8yfWVUx(2ICE znhL}C$sU5^KmY;|ctZku1uu`M!Z3bA5IhnDAOL~qBjC5N9nFQUz|5==fB*y_a4!OW z3)==RYz1a!g#ZK~0D*fEkXzWd5Uq#xKYizZ0ti3=0uZ>KfZWACTr}=yr$PV$2teTB z1mrrFy+v(dD@4IFX(5CF1Rwx`O9}W@8~#7Dh}y!oVH7;O^e7C200bZafk?n^+gk1S zbE}yxt+9^`0uX=z1g;`r7jB$qYFm3jgc!y?G6+Bb0uZ>0fZVomWGaYIg|V9q0uX=z z1a2fCS8be>3L;ct>?VT%1Rwx`+X={R8;7NW2z40y$shm$2teQ_0&>;HIjJB*9mak# z2tWV=5V(neT(xnI6k2GtsBUJ~!U_QhKmY<~2*?#1+ojM#t3`D)vldneKmY;|I72|L z*w`+G7FsQ;o0+w+LI45~fWSEdddbEKQW+M+Q-!gQ3<3~<00a&Y&}%ieNo80NPZh>K zG6+Bb0uVSxKyKF9C(%QA-hInpG900ba# zn1Ecvsdrf8_D!r1AOHafTuoqj2e*xeHSXuDUjy?X009V81cujeGjG*o78ka_AcOz} zAaE6dtvh(DCi`$Un53N3R#pJYrM1Rwx`lLX`rt|myig*%R_j0OP+ zKmY>g2*?Fo4UjVTa|H+u0uX?}y$Hw!Tx}s`?&k^+8U!E!fjbhAJGk0K%3RSEAT$U- z00Q?SAQy18g_OCUD?n%vfB*#UMc~i{+|B7IG9%RxAOHafK;Rq!xm#OU&4`Z5B#vqT z8U!E!0SKHUAlGYk`?jIcQC#EEaPAm%K>z{}fWV5t{^c4S+|Mh3q!54r1nx&bF5r3- zG4wFmq9c_NAOHafK;R4kxm@dZF>pWEh)^K_0SMfOfL_4$4(eFqWQ&eeMt}eWAOL|A z1oUpL+ttDSTr)z200ba#7Xo?#*BhwA;^;KBudzl30SG_<0_Ofzd|2B|e!GC~MI00Iy= zMnEpttwym`_iiu80oD>#G6+Bb0uY!ZV7F*>>&gVNRnA@(e+#W8s$>v=00bZ~N5F2; z>V}OI=AGWRw0K4U0SG_<0tX1p-@5Zo-=6!~Q4l}?0uVS#VE)#fclsH)pB)7O1Rwx` z69ncj-so)E{%%~+`;2fv00Iyg5Qw+#z_iHG$^E>=iW&qU0D(&g#EW-pWOOdsmc9Q| z9Geb`5P$##ATTBnZ{4x)`=6SVjx9U)^ClgN5P$##_6f*+yZ@)kYPNW|MK=t~6d(Wr z2tZ(yfL*g+>!$ImsAh|YP2DgoQ-A;jAOL|q0(R53b2kkUl~PKmY;|$OPo3J$AiHW1qSRc>@#(KmY;|I7L9NS?=dk1Zjc*1R!u9 z0^HB{;mTA9KmY<45IA>5r>;e~;5dwc00bZafnNmDO*=&${d{gKx*z}n2z(L&?&nW> z+dL}-An;-YZoZ;l?0GpZ1Rwx`S0TXt{3>sXb3yr2>}Q|V3xpgOV66|K>z{}fWX5D za6dl`H5)+y0uX=z1Rwwb2tWV=5P$##AOHafKmY;|fB*y_009U<00Izz00bZa0SG_< z0uX=z1Rwwb2tWV=5P$##AOHafKmY;|fB*zuj{yHg#MgUgoEQQS_zVKv&!6$0c}fUC z;Kc|mmvj5K7dtk`g#ZK~@b(0_pWpr!^MDY5z{?S^o4KzqcYF>E0SG|gZ3%EczwN8# z!5{#EmnINzXa4;8rC%n8hX4fLiU9ZXTfI&m1_BUx3j*t#nSXTSEnX`R0RafS0Risk zH+YFW0t6uNegwFj-|wCBL=b?$dl2A$evkLaQ$PR$??)is&+qqqJP`yS0D)H}!2SHH zZ;o?A00M7KfXn%0q*A)et8@n0uXp>0^HAU{d##g2teSq3G^%awV#30 zLjVF0ct--<&+qtFc_s)z00Izz00bZa0SG_<0uX=z1Rwwb2tWV=5P$##AOHafKmY;| zfB*y_009U<00Izz00bZa0SG_<0uX=z1Rwwb2tWV=5P$##AOHafKmY;|fB*y_009U< z00Izz00bZa0SG_<0uX=z1Rwwb2tWV=5P$##AOHafKmY;|fB*y_009U<00Izz00bZa z0SG_<0uX=z1Rwwb2tWV=5P$##AOHafKmY;|fB*y_009U<00Izz00bZa0SG_<0uX=z z1Rwwb2tWV=5P$##AOHafKmY;|fB*y_009U<00Qqy;E%ull^$+U$N!f62oQh(1R!uV zf#niTL&sNh?N0*`AOHafK;RsKbOE<9N!t?6ZA%vfAOHaf><~y-Z}TT@OW2uD83GW1 z00a&Y*t&0z)(&io6#@`|00edj9KD0L{5xAwh5!U00D%Jpw(i@bwFBE?g#ZK~0D&Qa zqqpzAf4CI|2tWV=5I8_!|GquyIIuZZ2tWV=5U2Z?jk-009U<;5q`;eS1{9?leq-00bZafkxoy zHN5r%5dsi^00izsV13^n&F^z6ra}M$5cp&QM{nZj|73DJI|LvAf%_7O_wCW_zGq`P z1Rwx`4<&H)G7kO^WyWJe00Izr1c7kh9!(x`K=yzD1R(Hn1csM!CowGd!b5P$86L79 zn?L{p5O@j#a{0xupDt=Cd&ncmt`FIdO&|aP2s{M=x%^_-PZzb6J>(H&*N5!KCJ=xC z1b&`?+gW-pKn-hg^QnHYpbo=#m8VA1Rwx` zXCN@X_LkF)%Qs!lTVt=RJp*CR009U<;2eSRZMe)Em${$M0jCQB5P-l8f${BY-2;b} zkFJAp_m__z6h8zY0D+$+FuoVz{}cuE5G?IuT6gUIut z4mcIhZVQ-6{8q`C_Dc=~2tWV=&q^TOVC#Qm z&J9POz#Hq}=s5fkfB*zOj6iXDMgne#@_y!xb+A7G2LvDhfsZB-FV6yn8;V*bxxWso z7BmPz00I!m1mZ24>)-!Ryo*{T#epfSTF@W>0SG`~MIhdutKyQHqpC&CoYfkgWDtM= z1R$_SAl{{w+Z2tWV=5Ev7%tF*6U-%cftv23S_ zG6Wz10SJr;*hPB7?HosVHrf##0uX=z1ZD{Mb-DyI9mx|%c$Rz!5P$##AaHz1t=iv|G*KmY=J1mu#ftXmhav~Jm|v1ky000bcLO<=i0 zztgWsZ`fE_vaMTJTZ_rXG$aT>00I!G2^6=eUb00~FBgR-X=^dLZbF3s1Rwx`Uj))E zIu?vsa#%svXwnKX1Rwwb2+R$h#>$02tZ(lK)gpcGaHvoR?sz? zv_cF42tWV=TLj|$xt}ew2DZ3QGQ~0(1Rwwb2y7Bax9E$djM#&V*WDtM=1RyXV zknYbb1ludkvH~jvAOHaf>=8&;=oNx(nr2yn6#@`|00g!PqziQGe-o}Si&Sm2Q?6Rj zAOHafK;VmjT%ZA2-sCS)n(?mPPJl!00bZafx8fh zH?n7U*^8kNfB*y_Fi*g5CNcWeoT8QJVN6009V$3HUWTwphij2LtP8g#ZK~0D%Jp{I1>aaqGcge=-gT zKmY;|NCf<@O_e0L^6C;w8HUAv*7pZEa5t0uX?}hY^VP?AF6a=UuX`?WsWk0ucBd0>u?O zc=*EU(x63Tw@wGrOb7u8K;Q!j6xZc|Lg932&?2&1rvqswga8B}@M#3R0ucBa0^_@Gc%G_dXr3=rV=vd{ zkAo8e5P-lB6Byrs!-G{VL-Ty08hg1mf1FQncD$ePq&y=8UX8%;^4ock*tcWeDaTn; zSut^{&rC)Cr%D~kr#?N;4S{DSU>8JLa8ooHdmd6Q}yjRP=wUWJtc&Pi4S! zL*NMs=udNrXU=%4sT`ap)k4`jEvA{%ogZRUKp*nRJSGHQfq?!rmw4vTr<%&aX;Lke zz0+cvIo`1YHmF@IWz7v7fYq0cAyD=655ZEPPzrthxM3r|f!R{u>5c^kI zaw9y1!0I(f`jG9}1OgC<1Wx@1x0+fl?vOI+j$v{lJeELw9hyD%9P9=G2rLBTM>tPp ztEts?21$1ecls#bn?QdZeV6!Zmh9f!GamvF5CZZW6m=_|{_!Jn00?{r0r~aKKCra3aNSi%YKhG~ z(>n2w6R2KM(tiAu8~_6EP9Xm3rk?ghi_E>PTLux{k3f7$gY16WGZ_L9SO~=5-V~G* zEi(7EZW%;)KLYV3jkEimfXNVmK=`A@DID>)I@Ka3>lTUd2?^MH+FPIS=o|?GZ$v?Hr#(FfgTRXuh(EmjhY|n28ON&=kk>s9U;QCDKLnnY zK>XG1pHlq$W*qN9Kwf$szQ<$o6cBhS0_n%Lb2|0!SSh~|0e$af{6^2pqd?$U2&CWI z&cW2bW2O8?1oXX^@f$rWj{<>bA&`D+I|oz$j+OGe5zv=k#P9aFJP!nZmVo@o`au?9 zcqXwQ{)aY(Ln*ur0e$~P{4&qMK_T!G1ms87&$|f2ql=yUc}VH43;GGH)^vU z@VF4T8v*-m=?06Yg2a_y%7mXoz&?evehvbj6ax1pU_UP1WzkfSxbjPx@bd`RC!y=- zQQ(Ooa3cZv0r@1;Tv&ppMxkf5xpJa?4gvcrbp4#C=Sd-O5drxD`9#xPSc0ZTp=Y(Z za-w}20sA(5{WJhPF9dEQ;6I&pFg4birp%Z=jeva~zJ3}2o)-eQ5%8bRI+z-3O;ct} zA4tHy5LrKv0*?%V(*)$F6T|(9-M{a{@u>vj7qw)cih}2cz!?I?PiXeuo%NkL7Cmo6 zAbnwP^JF{-1P%~LKZIL_1AAkIz<@yd3I{%92)s3c^h3B+cxzfb+~WugKE%fzfSn-l zg9HZO{{`|7J``KO4S~Uj_%_eSgFs-5z~KA8K;GJw8U*GD?0krGLvTUh6$$Ko{5O|h zkqKvhDgry-;8UHI!$9Ct0y`i7&E-oE%dk%*u=5RmA`+hYQwZ#Q{5O|Bg(J^7N8siU zYA|4KcU%y7Edud#PiK$ib6FaS@VNxeeM0+PpG%Y{e`f;m%TG@l%jdE*6yb9Toco04 zyFQmJPY!`U`lI<@iZ}rWfnNmVtGG)DXb5~Vfm1)KPX@-b2Lh+QpEFHiR}u((B7ylI z&L`60nKOa;FXl#P-Wml0A4XvQH}qj(c-#Sj`7h=o=inU35O{L}M}I(Xj)TXWA#n7g zx!FH+C_E5&KLSU8I`2n-Cpt{v=tpz0|L}QffWYe!IQy%4y(i_w*Ah7UMBl!97%~|t!PfTF> z`maA<`cVzHNKO%kO%&dofP4fR4xd_0_lSoZQ-Ht=5|H1>?CGUN!r5+oZ$&`901b!F zsHS_w!;L9G-~|cDZ)EoL(jwt(H@>$bAYZ^AAAd0IzASFXkXgqMMF#{50`g5sSQxLC z+HS{3$`L(0K821~C*Uvtvad)do{Ae%>3em+oc~D($Tub70}j`AJHCq?(Zl0Y=y-Jk z{_-#Tige)3`By!41w8CZD!U3J@v64 zcmTF~Cj#+v+Ig~Qp3ip1Qii~a!2Gx6&een;O#Mh9_`w6Q^~(|npU~c4lt=S?_Of(1 z_zw}7|F+!uLnke^^&^F#Fd}^xf$%+?PIj1`o_pD!$?P1E^6L}0;v-vT{1}Sr34ZJ_ zZ2S}is&C@Bw(}^*2YScOWXcfuMZiCq{*w?$Z~h*_)U%%5 zcxpJNS0XU{6mENnHg-5*xTO|`-S;l&A(r^w8#4bH2&C7ZgyAWZb6*(FiiI?&my;ZY zID_m(3B;E=l}$dW&)V|4X&7vE-?f%bbl2S&{tN`tJ5R#!l*zd-3}?kc8q~{4jzXM4 z_M!yhOP$IlpVVh<`Q0=Owz}_HODDSPZVdl%0_lZ!;pTG<=9+x#e4A6Px6Y>jrwLSV zbzU2vKF%2XmP$0IY^VEf<)=Bw-JX!aLLj~FE{Nk?IGAhlxuM@(Hm6#@T|}=*pn9wG z+W48{jInR2M03h^y6;wgnuFZ!2^lN|Rex;X<*Z(n6+v>XEO!@%=_8MmmeSyU-Vtx^lFP=>2=Rq%)f3plYW3e@kY-`>Ev-p zD%V16lB~8rMwMf<@G*O`%MTFfFZux+$1lpVD>t^8ZM||Vv!0oNy!1Ns!^g7A(?RU- z*>v+AjPX2o*np8wK;U8DS^S(VyOMy-Z0nU{nf1&B6e0s;^F&f@1}*_8xrW?Qcu%dBT6ATPZR{qV8u@^ldUdp6yC2V*?X9X4R(3lWH4 zgJegaL0UaLI%C`4$8LM5z9j+wqPBw@o;?k|Vb;y9@h~@!W5DYYh@XOFM;}RAJv=&N z+uz4-d#Ju80so@5gBzYb4ZdO4&8_h;H;-e$D-wtw!G31X=uQ)K_2PG+baj^;r(&ODFR!c`Jr09TN+W@lA8K%5YljjcOp=H8CN`B8hgdilQaA*C;Mr8Spxp* zZw+b!X=-gtYU;Z|NW&4{i9qpXT=95m>=i>#&hWFG?5FJo3HVE&3EJ}wZTrtIt%uCg zcTs5D%Q%x*Uz@=7uT&;3;ZO4Mv;5>C*!zJ5{Eg29?Oa3K{+XrqkXiaJ3T=BCXA z%WMPty5*z>follFZ|>IYnlp|6;{4Y0?e|~w1aCNo`#F9+Z}^INq$eT}zqSjqo1g9M z8KySg89p`RqfHCIyAs%WMX9`V81wJg4wY?YUk|lnhQJX5@l(4XJF5Uu`1~hDjlugoA%KM=<%B3vf{tO%t7!!zJ*eV;gt$e%baF7&s zhl~v}<TZqd#RuWAY@SX!??(PfS)2X&jn) zyh`ubLo;ZMvInjMXi)|c7m^xmg zKV?N@@+6^X`jVngOjZzS9GZE&O7GZ21p+4s$hXzQ6Ej*%8dC^clM8#6;(%N~uEdEC z&8owmIDm{f(S1XUZ5&6KI$oncWkqB1B%x^flA=#cRuE|%nt8lR@7P2I0w)N_x3&77 zPYkmb9lDOqzoHS*HK%oi*L(ttY+KxF3V1=wry&)!l89F zMS7(os)tgpLwBYi`coMJ0;dV+w{?2Rx>1L&~WvU(X?o?tTJ0|cCBWN_8ZUiDMO1p z>R{NnRj5Z@q;73IRqN*vYdn|~sxX=*tISQ?cMKYlA+SZDeOO!N>uT}^&xAW0&x|NN z#oW?Yh_SUWxD=g}nRhS2WRu%^x2NAh9J`r)H%&#Gvo0u39paEF9%{EW+BHk9QcN`j z2%IL+KCIKdhZ^Mzo(Xqm&x|NN#oW?Yh_SUWxD=g}nRhS2WRu%^x2NAh9J`r)H%&#G zvo0u39paEF9%{EW+BHk9QcN`j2)qaZ`HVNi;p1#}eHVxQsqo!%b(FT6666$9elvOB zB6xNxu$en+HM_S+rJf;%y|5l|R*k#OPKjNX#cor-)>t8fz>5&jk9aE{KF(H?)Q0`3 zAWavCX{#wg4l(67llLuxXQu+2xwBTYdy7=+8DiKA>j7ugxZCWM*kxJlHuYT+qfMSty@wWmdweK1Zk0)s7FJeGT-Pemj%0f$DPz}71g$5>!{Z4 z&cjvN;!v;jc_;&1Yk|mw5O_5L`YC_M{V950JkhstJ1SbYq%HO@4CF+_`3P)Nxt8@FwVUt1koQ#;eJ_FzS7v5ve@Be zlU+C)*r2|iol^D5_7j6h_R$Olw=&d%< zg+sy8ojPo0TT=}I0#8jqK3)ssc}>-C+qF|coPR@>cE4fP{aWReJDcn{rQE{dEVYTr zWRcE=HfX$_o4a($&^Vnn34xy>ARn)V@gSz^&%?D-L7aa>mUh2k*8N)L zlslX3IHlae;ViX@$z+kvg*Irso}0UL$nXKvb9hV0A#8F@!$5xpZ8+##Y^Vx9h z{0N*7cqjq+W+l`k6tYMfE?p{2#=6_VIPP$;`N8((tW$f+Qw{v)7?io6D=-GFtyLKf z0#8IhzSdPJhm?w|qFLxQ3#@QpwbuDH4xjJqPVe4nbl(!}m@g~KuBT>tzjRlcl;=Yq zBp01%F`OT(PMINcS4B7!)-w-{zzl&05sHn@?h?%TAu_wiZY zFRhg(<;l>ew4iQLhs3pcpbq9W<|jhkW+Cxu^HGp?$!btjYF9jBC~DTd7ztA6v{yk^wzw;5O@Fq`8cc4Pas9txSs{Zqe6KREj+NRG+LKPMFS81Ldnvm1Dwp%tuTGw;hw^l*aH+4f3)8*r!&dRwp|p`$ zmsm{tT17q@E@O^1b-$coa)5N`nQu5l+gOH99s?T$?oL2H%PRE!rRZAtsJlUG%_eJWrCHr-zFHx!UY>r$*gu>3 zK9ddZUxNK6@rzm1**7TN9^S;p&*0;>1NXN;oQo{^u~3E{Ys3wKdl8V2(!TB}^^HZ7 zlZg5iN@XjQt}WY%noZW$O0&Ave6>Pcy*&Mhv41x6eI^^+zXbbD;uo{1vu{wkJ-msH zpTWm%2kviyI2T#+W1$Q^)`%MdM+xkIY~tvlZJ~ul6Tv)RtH>?X%0?_r8?G~@YO-n7 zJgfD!a;zV5_8IG0`>LEs%>#L{NUeq1Rm5hNl1X2y$d3;F81owsT5fub!OZzX;e@~( z0sqv7AFMcfXoIz|Xd;;BYZbYLTG@!DX~T7-1wW+G#r1@~0;zIldFJ-B< z&^mODj)6%VZjWV4A698zT!MYahE1AF3URA@W`2^%P23+}-y%Exc>;K=r6(m8D+s31Bet{q0li%Q6F~&G=_CS z44^^|U*a2=;B>=#VU}>PMFFFpZMBOk1*AYg0lr*Yx76c&W6I@35ePpTzA-;FtIX?1 zB59wM#n$aDk(_ldojJYu70kp?F+RwF>T2w2X^JJcR&{F3=)|X@#8KKh~G-5 zcUQn}7M#XHNN4Lol*@@CuztaOefv~t)CLb`sJDGq7F+kSL~_=-bmsKpS1=Pp#rPlx zs;jZ9r74!&TGgpBqZ6Ns5?}G!GU$1|Abu;I-dzE^S#TN)A)T!UQ7$Kn!1@LA_3cxo zQ5!s%q2BgcS!~_Q63JQT(wWnXU%^Za72|^(sIJDYmZn&8YgMPlj81$iN_@p@%b@4= zg7~d;dTRyjS}jgv6%R_M0!N6H^QQ$&p;~>>&;ls5K-@uT$S!Re>F01}d;bOLi_bZE zis=*_OLaws<&d!e93(EYAQD|S5w()AM*Z!m=Kzy_Um?C)0lrqv+v-8-R7ggMl=G(r zOrctR(a-`Yv_RZJX~-^Z8R_S6W_$ky>5I=fd5Y;297}aYh2@a3030MPvmg>(Hxadx zutxpusOJEaeqSNJSOLCNt-IRC5F%@dfGLYWwib>!OGyOy!1y`w6Z%^P0@+%4aeOYF zI_UyGDewXfDkX9h%})*o;1m8D7eoeLMJs9D`F)#M+kNG#k^(CQ>_#olD*5x+#}Fdd zAYcs<$kxIUXDNvQ9~eI;enNk%KpECk0-hL8U~FqI#c6mzHaKwE&7s zfw%&8offBF7s1&3lv|+08X}Obg(D71BiIK9_)&}gR);{g7GD2(ixS!Obtj%)W8$kp zo1;sN1MmU9(t^v5KSCc#dk2jp$E(+8DPY%Waq7E8F!n0u7AUcX2xM#Fh=bAy_JIL@ z)S|!DA&{+w^*)mn+4V|Yb23u#@hFK`^pWT}@Q;apQz5=m0lQ1{Z7rvm6bLJTiO!PeUU4bVR{*yt*)})?dVa>akrkn@ z1-3>3OqS}BONrxLjIfSH;JIDN0pM({$N7#YVyAH;R$4eqFOEW(@gY+xj9R?Al^lDy z&^As)NGY&Z0JkXFb?SKzf$*WCkrknj1%N=JW2r8=9q_(1Mp$(bcy3p6061IgalYe; z*lC=Il@`v@i=z-`e8`jvqZaROCC6Saw2c!HQVKj3!2S7YpNY}~w9Gssz$BZ3b^^cx z@sEH?;G)~n*7x@(%tAC-gdUc{s|=&g@I4cM8lUg$^C@?s%o5<-_?&|)jNDLPR1FpN z!IRqd2Mh833gGhmv|9@S8o?xNf}z5mG7I?EE!Yv&TkuzMzP~@AFQUmJ^sp3OWf*ma z@0s}1_O%k!}79G`*XSj7iS?{h_*Q$qF&xOX#0#|cn3b8{Cvx;f($rc|uS z#fI#w>h?z4E>Hj|uvP$T&1f7^i}U6U0eH4GGTRq+MPpyps!8>@bgpI;lmc4{*qsc4wy(SO zN)G+~20HsRqASawwZDQMd&Ds7+6*93vsQhLLzIX>JxYMKOQq&!G>)jnS?@CxA-jk} zXSq@!ra=CznppelA~(!~SDh(YpGJU{TgM+9#mw9mr(}x&snLdkj?0<7FR{@^HP z7ONBiAZzwuQbX-LA1b8;Mf)z*v6~9%h&C2N;9?6tLo`sN_jyP*fu|{uZ)CH*UD(BG z!g9icSDh(YZ$p5UTgM+9#mr)rA^>E~9!zSeo##WPl%Qzer8;&~Asx}iLI_-J!DomD ziu67Y$tLhL1=5Xd^tQVSr-6Hh0c4xA6nVm*9;L~B=FjjjIM1RJ+`c`7=Ee*nSfG$A z672v=3Y$t0qiTd&*NLE~xZpE13`IvTfxbgz1eOAiFRh^h?v&XE?in^DIXPPdo<$Fj z(&T!~>=Op(Idp>Cx3ejB`V+wdiCmFr2T)SjRDu{)Bh#J z6sRhI+hn$iS_cjNbTZ_5et7FL`_>IsAWjJ^<86|W*>Dn=vsQbFLriM(JL#f?@ZL>G z3Q(OzDR!j`K0_Q(^sqyP9WeFnAeEK^Jq74CWw%$bG3Q(OzDR!j`K0_Q(^sqyP9WeFnAeEK^8w$`xg1dos zDZ-PRAUGxK3OJE5K7HZ|6m28|ZM#4%ZH1ROMk6^*0su7umH;x_97Zgi;3jJ&6s6de zZk=U^5f$-novKLMI!D05NP)Tnc8#7+9au(Dkuv1cDMfu1U}k;##1k0UNCet;fm+%M zFL8`Ua+(AHY62_)WVShsSUSN?)=DT!u`As=%MK$d;@vt`k+gM=fQ69)6$R`Lxeu=b zEJIWZH-m6Wma8bdxSYGd2OS%YK(;Ad1boN}&v!gCr?MkMI2T_X2$oz+`tWkH+~OF( zhc7!BA6gdKz?|5cTWk-f^F1|VtEV-8S;pJqx#W8>nUv@G+v_yoA)kVN@%%rw)0z$sG0(OB0 z?{nUos^O*t+-#f3=PPiPgdp0c2t=|4=o;3<$lMMS;F>T1vSpahET>wUV~6``>QGN` z-c>X@tUm;F#+K;!72+Edu-h~E5oB$s8g5F!&9;fW{=$N2n<5a&7N8tB`#*`1xg8F` zHDLf`%P^l=PPH`04)@d4p`PHpt7vpse+cM|Ez$2Q#K$XOS7-1GFkz^{kZTQ&ankHv zjlek<Gnz=;4R2*)n>Yec)GP?T&JI|i`MV`V~1 z0lPYTFC{e?au1e1&EC}!oO3Zw4VyqdvuorFMSeP;I!y^$LsB}qgzyQR2w;M6>~g}W2^V`jXDUyV zPp4D8+D9)w5$7Q$s2eH@f#-D&jTh)m6~Mh&eRaWYtePCX&q48qVI;D5Bb~Uy<2kc_ zeG}5B)2UwVqZglu^AHo%4HbpJ^E!vd3-pc(;L<$oF895S$8Iq^FXq zH7_VppoU+^2+qGXhbx!Br}N2o2Es?LKkeeRt0=VVyw0KV0==UGxHJ#Di$6Dr_`8No zEduL21gF9l>8Ye@%?nBtsNvT!g7a_9;mRfO>3s5?f$-7mPrG>SDhlm7uXAX;KyRl2 zZp_2(CC@#&{@x)|>%ck>!KtuCdMc@+x#yG86ez&W02SFUPO(}YrOov?gnjh-(=J}S zibA{2>l_*{(Az118}qPx$#ajczjw&gI<1; zX>&aeVIRHzw2RlSqR_7MI)}y!^mYp1#ysp^^4z2A?;SF=4y^MKoC;f{r;;j~dp;>m zfdbqNP?7!O6sy%y+FXxA*hjBF?c%koD75Rm&Y|%Fy`2KMF%P?oJU7_(ceOvK2*mR= zoK|*8QtTXRm_mb-TMz}TZ;M7npy=Td`Q$-B_~`W~@JcpLW>-pu0G5 zgGn`RuIsqE0tZBq zxI6%6Pt_unP!+)rW0vN_RVw&K1#l6^t}iKN4LS~$+{`87nHdGxU6K?yaSfW9TpNtA z1%eLh3frjfv7DelS!`qMzDN>a4b(r9i%j&7DGN zT;_&tHor-NXlF(gU{~+6sZsoL$P|go17J4xAfjp#{nUO^XSMw-1@c9#?G(A9z--!D z^E<_ee4<6CmFsgSZa_R}YCTGTO_!?8-6}B9;uJ`vi^SytFnicI8LEmG2`L4RRv=%* zT31}rS`IN_HZ8z+wM0Hqq|?gvxf8b`9yGNcCBUXj)#h#$7-(?{B+^CV@&K4UY@7^L z#fyZL0!J&*xV8Cyc8Ae5Zn)hR*8_-oB15OPdm?Ef;in?uqtO$p&2AMMXmM&tAZoeG zV<4yZ`SPDa1O^pIcehrNuXHWrVs`VJj)ULxK-?2SIkufnSrXp7) zLIG5p-KsLs;?$5p)N+@{KyKiDPMlG7Ieih30#1Q^GhL{0z#VSbM%B&2L_RU2Q^P%x zJ~1e!B3C3r0aTmasxr{x)Q~{ba+k+IPVe*OKZOXqs=&ZyHfYs(mB?xJ#6HcWQ^P%r z7>zGRZsiJaEkF&jTLU5H!H9Zm7$3gF$*2weWd*A4uz@&8fu9QCS_b~GVbH4cHi{Da z#FSAc5)YLML;2r&;v)LX;&@EuM@P4Dv^ehzWiuK=!cr~|+?8)|S5 z5YbPB=#>5u#izwTk+RLM%-qUV;H!{Tle2=jBf*mEQg$$mMegK)-2f+)G&dgTC8!iQ zL;+mNP&0sQHq_uAAflfL(Mj*~&mV|2(ffRlTPqgdsKD|iMi=*=EuEqb?tvrXE7!l) zOY$tr%%n2Y(v@64WYykj7Dix(X?D3X0d_+iP}KN0hM`^pOMwFvSgvHWkKNz36K!x0 z+?xIKtoJ$pLQh2=Uj0`yOg(*Z6zqm*ps2C&P%nX{zyS&@S2Ehi?(f=(Hn<0F&Hj1T z`<#EFry>up{;L_Lp1wE=c0)8!)Yy2am%visRRwS#Lwx`)Y^eH9pkf0Htl#GoSrdn1 zs^yYXBqITC;QU6h!!$-3rqSctu(2Ca%!;n%uIW->Q2_TbBnQBS4OQO>RBT`Yz0dh~ zGC>xn{@WR*p1wG0?DRfg{7Y#3ZUr{JzOpCXcydeJ{Ub!bip)&{AQ-2EiieF?2`L5UD6rheXluT_Pd{3HC)|^L&pV5?@u&~% zr0Yc%wd&+7K?)eiJA^=y2c(l>ip)&{AQ-2EiieF?2`L38D3C8>yfI(ipMJdfR>;aG z)zEpQZG3%XCtWYHs8uHiL5d;7wv%3s&W!~X!Jok*#=@GOvPNy;YwRdH+vhViMTjn1S<spOX=+?_VK0 zUQubHkQ6vq0lK!?J$L-E(~4zkfn!IuyJ7Y#h-NLCCtJfb)6InMVlW@R&&deZ_pcBg zuc$OpND7>*0A1VUo;&;SX?~f;>F}wIh8W*Sr*I$9tVLg?gVMj7f+<#{%DhDJ=!=p#UyyviBhLA`9B5 z?Y=l8R*vy)G!V(QLG?_qlwdZb7OM;~SS5F>P$n9rWLu3(9-JVU6gW_UPrI>D)4C+miB482kO^u zh4FnvLbgHm44QXJ31-MztTM!4mE5gDnP`xbZ8a`=aDre`;6MfJ!XDW3%2u||mNTCq zO>YkasEuf5P?~HLlM)2WZV~HOZz1lG!;T>?04E#53nT?DRlqLnr7VscZu@LG$Bk=s zqUkNj^Yv^anwe1l=LCbNhUPC$bAlna5O>I7pAZ*-lMUeok^ z+0RCosb>VBXC&sKF4=wofotwo1H*5D?vPuEMu}jnC|)=zaFGJIstyvN=^Uh zp=CcCU8bH9fS!?{ z)1SPC&E1+~p!g%ZwA)ulRo>mAm~t810li%s61*L85&j+pa7iEbbGGKKn*QW7S`Kx|*3!)w)v?>e zTViyO72el@0QY4mW?!xVuINX7eY8~1o%i!&a%Vyi=DVQiVSOfY;%fN)x&_&Xy%!>{ z|6ZKM_KOt2_nGYHY)wn`+<8l5a%Vyi=DVQiVSOfY;%fN)x&_&Xy%!>{|6ZKM_FELd z$N6cu)bC~6HNrD~Z5q{S4`c`C(t%sv7>zTjH#{Hj7=hhd7buv>3!}9rL+|I8U^fC zfb&6{W2Lw~etDYRX>Q04j?z{V|0@xyCYKr|mlATNfE2h;0sm!w*2h`w)(ss0L(s@h zb3=A;l(v#r-&AUpT$&(P3P^zq74To?XMLQ-Zr#A~KLkehI|CVg9u0rMgG#-U+vh5T z6p#XEDB!=$&-yrv-MWF}701YaXCR}`qu~#DP^njP`&@;P0#abU0{**PPV<~`De#g4{-gY~@3Pp=2a<9=fYVEou8PsK|Ku7n zIjbhsqv5OB;h?pPO&{*B0x2K`1{I)>GP{HI_l6hS`9LD?12`SI6?*ocTtg-&!octm za35psV$=H#g^&VL;57y4qs;DW?QT|GZ07@stq}RRmI7RRT3$5;5Z{5>c)iaZrIeNe(-ol4k=^NY&m6#ut$pLMPUN$tUvjv2w~>Ln$OISy zM;bmm>U|FBC6E-DsDORo-kCV@{MLRoe=;^cjFY8ba?tHzyNwLoMJB)yIMVRhF+erc zP#`HF1@y717L$5` z=ZC6~Jeh z;rG1(pI*AgBO7!wmPQV2d|^VXvC? z^lOL|^Pn9k>f5j01@5;ULJV;j>MXDnkOC0}@C_Qa2w3L59qd)J#bzN;QPl}h(+)DF zcY*tDhY&*?hB^x@1*AYk0epjoEdrK#ZwGtTY_VAgR8(~W)U<<4>0RJ{+abgdhoQ~_ zO93emQ2^hdVT*ue-rK=mHCt>J0u@!A05$C(Q+gM;-*yNw#9^qjz*0a8L=?a`n6ds8 zsec;`R;$@!i4dr$>IBfV1Iy_+IF1YrCeXajG(@fxkOEBw{5R-N&7yZqV6~bp_6dQC zs!jk+JFuLNgX751U;@qSOhe>K0Vz;dp!fibK)voAqgbtGYx@+q8fYknj-jGBIF1Yr zCeXajG(@fxkOFlDiVv^|)a%|ciq&eiwoiepfret}7%GZ`C$}3o_Jm6cpz$+F5uhAO*G+z}LtB$2i2cE8jAU)e76##^V~Gc$nIS zih%EcgRoAQMjFkNZ6Fku0#aa(0`~P$d&DU8DK1tkY-2%>Yk=ZmY8NU3z5@=zI$auR zG*7mHP*@5`fjJ7~uaA1;bEMD*x!A6-jU7F%0g8vIU8o574miU)puK5uNOo@v)ky&< zuuFmB)5hnxtK031uw7vr%ss9FiifFPs0jEDIKw)iy=ibrc5e&SNdYM^u0ZX(#^*TR z?e-N|t+Y+*1Z`pl5@;7KiQ^M8+H?>H4)Z1q5)xbre1`)0)APIHbM(=VIAOKYHrFF) z6El!NyJ$%qpODd}gE(-QH(`*F;8Ngw6~O2CfeK)?(l(bRXcIG#K)YzkC_dVE1P2bu zj+6;21*E|73gB}*zVqcBv07=H)Ck(d3?$GlS~7}{_8q~2L$V`f!b$-taJ&Nfb3DH9 z4LZ_GvvQl{Jgy0gCeUscKH7H>H%fc3z2H(n3Ir9%-(nE_UNAf}rETuvp-oscfp)X- z(Y}MYQQCv;1(yO+Aff<1#LxUUS=-J#9bpLELH5(=)kkK;C_dVE5O)(AgRBLY0w1OT zK199G&mYz_IpFVK(d*fL+Z)AiciWx4360(SRVW2MLIHe;dY_*^r)i@1`6-G#DIf(- zSHRw)(CuY`^YT>x+J>=v)m}&3J-U{wu5dd17JLWOPt(*s zI3Y&yp{Bb)V6v-NMN&Wt9HRg}M7_^W&qWp&*>f;)_2=h3@~K7&e6<4hetfD5wxDn`{{tb@SR4$RV{cq^RXWG)jLtECi>0wD$P9(?8>o|6Q&pm+M>EciaGgTT}d%+jNH zE1ckDE)yE7r4*0?AqDUreCYdZ>#d!>I7>wz)U~}-qO%l`0_Q65J@50P>%h8tpU=ICnkEILz*>QU2WwsW4rg2M=l^`d&VBNFpZgjK zAqAwsJO%8z3jVLZ<}sPn*)Ql%osoKrUs94R0LK1_j+e+{r;vgxPur_OJw z_qijG(o#SQOjQ66*3=MPeVz(LP#kf1wK=OhQg+*i_Kii=EaAgBwit;6p#W^;QbW9TW4=E1uQmmEt?k~f|7WJkWxSjNP+iL z0B@bW#T2mE%(e2op$VWkR3xwzkOETR%@n{}XKyhDEH-neJa1?MC=L|~ECr;16nHZQ z@YdzGm`>T9zs-(0U^$Egi3FXpJD=E&Ibb=ToF+&CDIf)|Qvh#W zvWWzpvOAyHjyYgCpPVL00VyB_u2TSSU9yP;ow7Th*p4}1IiH*+NC7Dz1+G&7Z(Xv9 z1f6nVGO;{wbjVUQS|q#_kOETR%@m*)IWebGa_6s-=Z(%;sz!^1mjY5i3cQm7^c-i} z6iDv;Rr0*iIZM@Ok?>ML3P^!>Qh=W0Oq&A9oxe(+H#%pj8Z8oD3P=Gd@J9NS<7 z^gf#;a-@J1kOEgIfaf^R#wpt?_-LjSkOERb3S zujZ!tQa}nwfp1no@AEhR`r2IzNP#;m(0rfoOj_KefE17dU!Z{A=P&q?wW1V|0&l56 z@z>1x!PZ|szvZtf(o#SQe3AlspFio>)Gks$3Vf~t_``GYYJcv^+Dr;a0V(hn3g~@) zi=Rm(rGOOpLIvBV^ge&_kFV9GfE2j30(zfs z{qu;N6p#X6tbpF3P=GdAO)m= z6p#W^Knh3!DIf);fE17dQa}nw0VyB_q<|EV0#ZNwo>z|Ni4&e*E~$U;p&?Km7K~k3anO+h70m$G;`- V?|=C7-+uWuf&TmVzy0yw{vSSMnWX># literal 0 HcmV?d00001 diff --git a/tests/e2e_tests/nodes/eye_properties_estimation/mocks/occlusion_calculator/noise_mask_cropped.pickle b/tests/e2e_tests/nodes/eye_properties_estimation/mocks/occlusion_calculator/noise_mask_cropped.pickle new file mode 100644 index 0000000000000000000000000000000000000000..ccf757c58c755df417b7f1c6b15dd2f7c840fd8a GIT binary patch literal 1555484 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 QyB}YofAz~BfBWbE9+Z(^IsgCw literal 0 HcmV?d00001 diff --git a/tests/e2e_tests/nodes/eye_properties_estimation/test_e2e_occlusion_calculator.py b/tests/e2e_tests/nodes/eye_properties_estimation/test_e2e_occlusion_calculator.py index d931dea..ae3fc99 100644 --- a/tests/e2e_tests/nodes/eye_properties_estimation/test_e2e_occlusion_calculator.py +++ b/tests/e2e_tests/nodes/eye_properties_estimation/test_e2e_occlusion_calculator.py @@ -22,16 +22,43 @@ def algorithm() -> OcclusionCalculator: return OcclusionCalculator(quantile_angle=30.0) -def test_e2e_occlusion_calculator() -> None: - mock_extrapolated_polygons = load_mock_pickle(name="extrapolated_polygons") - mock_noise_mask = load_mock_pickle(name="noise_mask") - mock_eye_orientation = load_mock_pickle(name="eye_orientation") - mock_eye_center = load_mock_pickle(name="eye_center") +@pytest.mark.parametrize( + "extrapolated_polygons_name, noise_mask_name, eye_orientation_name, eye_center_name, expected_result", + [ + ( + "extrapolated_polygons_1", + "noise_mask_1", + "eye_orientation_1", + "eye_center_1", + EyeOcclusion(visible_fraction=0.9953), + ), + ( + "extrapolated_polygons_2", + "noise_mask_2", + "eye_orientation_2", + "eye_center_2", + EyeOcclusion(visible_fraction=0.9904), + ), + ( + "extrapolated_polygons_cropped", + "noise_mask_cropped", + "eye_orientation_cropped", + "eye_center_cropped", + EyeOcclusion(visible_fraction=0.5652), + ), + ], + ids=["regular 1", "regular 2", "heavily cropped iris"], +) +def test_e2e_occlusion_calculator( + extrapolated_polygons_name, noise_mask_name, eye_orientation_name, eye_center_name, expected_result +) -> None: + mock_extrapolated_polygons = load_mock_pickle(extrapolated_polygons_name) + mock_noise_mask = load_mock_pickle(noise_mask_name) + mock_eye_orientation = load_mock_pickle(eye_orientation_name) + mock_eye_center = load_mock_pickle(eye_center_name) algorithm = OcclusionCalculator(quantile_angle=30.0) - expected_result = EyeOcclusion(visible_fraction=0.9953) - result = algorithm(mock_extrapolated_polygons, mock_noise_mask, mock_eye_orientation, mock_eye_center) np.testing.assert_almost_equal(result.visible_fraction, expected_result.visible_fraction, decimal=4) From 93914366b4b8fd219c1686230ba450f42786c808 Mon Sep 17 00:00:00 2001 From: TanguyJeanneau Date: Thu, 6 Jun 2024 10:11:42 +0000 Subject: [PATCH 20/32] Fix base64_encoding import structure --- src/iris/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/iris/__init__.py b/src/iris/__init__.py index 9be1e51..880d1f5 100644 --- a/src/iris/__init__.py +++ b/src/iris/__init__.py @@ -85,4 +85,4 @@ from iris.orchestration import error_managers, output_builders, pipeline_dataclasses from iris.orchestration.environment import Environment from iris.pipelines.iris_pipeline import IRISPipeline -from iris.utils import common, math, visualisation +from iris.utils import base64_encoding, common, math, visualisation From 75877b063e508008e4d8174c75af265e8a4082bf Mon Sep 17 00:00:00 2001 From: TanguyJeanneau Date: Thu, 6 Jun 2024 14:37:36 +0000 Subject: [PATCH 21/32] Fix plot_normalized_iris to accept uint8 images --- src/iris/utils/visualisation.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/iris/utils/visualisation.py b/src/iris/utils/visualisation.py index 0732bc7..301710d 100644 --- a/src/iris/utils/visualisation.py +++ b/src/iris/utils/visualisation.py @@ -370,9 +370,10 @@ def plot_normalized_iris( if isinstance(normalized_iris, dict): normalized_iris = iris_dc.NormalizedIris.deserialize(normalized_iris) - axis.imshow(np.minimum(normalized_iris.normalized_image * exposure_factor, 1.0), cmap="gray") + axis.imshow(np.minimum(normalized_iris.normalized_image * exposure_factor, 255), cmap="gray") if stretch_hist: norm = normalized_iris.normalized_image * normalized_iris.normalized_mask + norm = norm.astype(np.float32) norm[norm == 0] = np.nan axis.imshow(norm, cmap="gray") From b7d566b64009a23ae767ae1eafd2661dc2e33503 Mon Sep 17 00:00:00 2001 From: TanguyJeanneau Date: Thu, 6 Jun 2024 14:38:16 +0000 Subject: [PATCH 22/32] Make iris template serialization optional in build_debugging_output --- src/iris/orchestration/output_builders.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/iris/orchestration/output_builders.py b/src/iris/orchestration/output_builders.py index e451b5c..3b5bcd8 100644 --- a/src/iris/orchestration/output_builders.py +++ b/src/iris/orchestration/output_builders.py @@ -51,7 +51,7 @@ def build_orb_output(call_trace: PipelineCallTraceStorage) -> Dict[str, Any]: return output -def build_debugging_output(call_trace: PipelineCallTraceStorage) -> Dict[str, Any]: +def build_debugging_output(call_trace: PipelineCallTraceStorage, serialise_iris_template: bool = False) -> Dict[str, Any]: """Build the output for debugging purposes. Args: @@ -60,7 +60,10 @@ def build_debugging_output(call_trace: PipelineCallTraceStorage) -> Dict[str, An Returns: Dict[str, Any]: Returns data to be stored in MongoDB. """ - iris_template = __safe_serialize(call_trace["encoder"]) + iris_template = call_trace["encoder"] + if serialise_iris_template: + iris_template = __safe_serialize(iris_template) + metadata = __get_metadata(call_trace=call_trace) error = __get_error(call_trace=call_trace) From a119bc9118161126afb059edf6c2a5ec2ba19673 Mon Sep 17 00:00:00 2001 From: TanguyJeanneau Date: Mon, 10 Jun 2024 12:00:37 +0000 Subject: [PATCH 23/32] Add IrisTemplate conversion function --- src/iris/io/dataclasses.py | 94 +++++++++++++++--- ...expected_iris_pipeline_debug_output.pickle | Bin 6624908 -> 6652512 bytes ...expected_iris_debug_pipeline_output.pickle | Bin 5199925 -> 5227529 bytes tests/e2e_tests/utils.py | 2 +- 4 files changed, 79 insertions(+), 17 deletions(-) diff --git a/src/iris/io/dataclasses.py b/src/iris/io/dataclasses.py index 5056f3e..ccee8e4 100644 --- a/src/iris/io/dataclasses.py +++ b/src/iris/io/dataclasses.py @@ -1,13 +1,14 @@ from __future__ import annotations -from typing import Any, Dict, List, Literal, Tuple +from typing import Any, Dict, List, Literal, Tuple, Union import numpy as np from pydantic import Field, NonNegativeInt, root_validator, validator from iris.io import validators as v from iris.io.class_configs import ImmutableModel -from iris.utils import base64_encoding, math +from iris.utils.base64_encoding import base64_decode_array, base64_encode_array +from iris.utils.math import estimate_diameter class IRImage(ImmutableModel): @@ -270,7 +271,7 @@ def pupil_diameter(self) -> float: Returns: float: pupil diameter. """ - return math.estimate_diameter(self.pupil_array) + return estimate_diameter(self.pupil_array) @property def iris_diameter(self) -> float: @@ -279,7 +280,7 @@ def iris_diameter(self) -> float: Returns: float: iris diameter. """ - return math.estimate_diameter(self.iris_array) + return estimate_diameter(self.iris_array) def serialize(self) -> Dict[str, np.ndarray]: """Serialize GeometryPolygons object. @@ -580,19 +581,22 @@ class IrisTemplate(ImmutableModel): _is_binary = validator("iris_codes", "mask_codes", allow_reuse=True, each_item=True)(v.is_binary) _iris_code_version_check = validator("iris_code_version", allow_reuse=True)(v.iris_code_version_check) - def convert2old_format(self) -> Tuple[np.ndarray, np.ndarray]: - """Convert IrisTemplate class to an old engines pipeline output format. - Returns: - Tuple[np.ndarray, np.ndarray]: Old engines pipeline object Tuple with (iris_codes, mask_codes). - """ - stacked_iris_codes = np.stack(self.iris_codes) - stacked_iris_codes = stacked_iris_codes.transpose(1, 2, 0, 3) + def deserialize(serialized_template: dict[str, Union[np.ndarray, str]], array_shape: Tuple = (16, 256, 2, 2)) -> IrisTemplate: + """Deserialize a dict with iris_codes, mask_codes and iris_code_version into an IrisTemplate object. - stacked_mask_codes = np.stack(self.mask_codes) - stacked_mask_codes = stacked_mask_codes.transpose(1, 2, 0, 3) + Args: + serialized_template (dict[str, Union[np.ndarray, str]]): Serialized object to dict. + array_shape (Tuple, optional): Shape of the iris code. Defaults to (16, 256, 2, 2). - return stacked_iris_codes, stacked_mask_codes + Returns: + IrisTemplate: Serialized object. + """ + return IrisTemplate.convert_to_new_format( + iris_codes=base64_decode_array(serialized_template["iris_codes"], array_shape=array_shape), + mask_codes=base64_decode_array(serialized_template["mask_codes"], array_shape=array_shape), + iris_code_version=serialized_template["iris_code_version"] + ) def serialize(self) -> Dict[str, bytes]: """Serialize IrisTemplate object. @@ -603,11 +607,69 @@ def serialize(self) -> Dict[str, bytes]: old_format_iris_codes, old_format_mask_codes = self.convert2old_format() return { - "iris_codes": base64_encoding.base64_encode_array(old_format_iris_codes).decode("utf-8"), - "mask_codes": base64_encoding.base64_encode_array(old_format_mask_codes).decode("utf-8"), + "iris_codes": base64_encode_array(old_format_iris_codes).decode("utf-8"), + "mask_codes": base64_encode_array(old_format_mask_codes).decode("utf-8"), "iris_code_version": self.iris_code_version, } + def convert2old_format(self) -> List[np.ndarray]: + """Convert an old tempalte format and the associated iris code version into an IrisTemplate object. + + Returns: + Tuple[np.ndarray, np.ndarray]: Old engines pipeline object Tuple with (iris_codes, mask_codes). + """ + return (IrisTemplate.new_to_old_format(self.iris_codes), IrisTemplate.new_to_old_format(self.mask_codes)) + + @staticmethod + def convert_to_new_format(iris_codes: np.ndarray, mask_codes: np.ndarray, iris_code_version: str) -> IrisTemplate: + """Convert an old template format and the associated iris code version into an IrisTemplate object. + + Returns: + Tuple[np.ndarray, np.ndarray]: Old engines pipeline object Tuple with (iris_codes, mask_codes). + """ + return IrisTemplate( + iris_codes=IrisTemplate.old_to_new_format(iris_codes), + mask_codes=IrisTemplate.old_to_new_format(mask_codes), + iris_code_version=iris_code_version, + ) + + @staticmethod + def new_to_old_format(codes: List[np.ndarray]) -> np.ndarray: + """Convert new iris template format to old iris template format. + + New format is a list of arrays, each of shape (height_i, width_i, 2). The length of the list is nb_wavelets. + This enable having different convolution layout for each wavelet. + Old format is a numpy array of shape (height, width, nb_wavelets, 2) + + Args: + codes (List[np.ndarray]): New format iris/mask codes. + + Returns: + np.ndarray: Old format codes. + + Raises: + ValueError: Raised if not all codes have the same shape. In this case, the IrisTemplate cannot be converted to the old format. + """ + if not all([code.shape == codes[0].shape for code in codes]): + raise ValueError("All codes must have the same shape to be converted to the old format.") + return np.stack(codes).transpose(1, 2, 0, 3) + + @staticmethod + def old_to_new_format(codes: np.ndarray) -> List[np.ndarray]: + """Convert old iris template format to new iris template format. + + Old format is a list of arrays, each of shape (height_i, width_i, 2). The length of the list is nb_wavelets. + This enable having different convolution layout for each wavelet. + New format is a numpy array of shape (height, width, nb_wavelets, 2) + + Args: + codes (List[np.ndarray]): Old format iris/mask codes. + + Returns: + np.ndarray: New format codes. + """ + return [codes[:, :, i, :] for i in range(codes.shape[2])] + class EyeOcclusion(ImmutableModel): """Data holder for the eye occlusion.""" diff --git a/tests/e2e_tests/orchestration/mocks/expected_iris_pipeline_debug_output.pickle b/tests/e2e_tests/orchestration/mocks/expected_iris_pipeline_debug_output.pickle index 3de0db89360885880ea0a07581350dc9ef9ef8bb..1c77fe2e9703e0165d31842961bdf4b5940925cd 100644 GIT binary patch delta 16407 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 Yleuq&mcmdu_eMB~(_e(Q3qu$G7nV9S=Kufz delta 7831 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 T?(2y?5Gi}{6-7t)_Kp1qMpa>w diff --git a/tests/e2e_tests/pipelines/mocks/outputs/expected_iris_debug_pipeline_output.pickle b/tests/e2e_tests/pipelines/mocks/outputs/expected_iris_debug_pipeline_output.pickle index 558d1050b80f70f9abdd4edb4ae503d6e88e47f6..cd8ebb9972fbcca4431ac1a65b4f58efb0e2de38 100644 GIT binary patch delta 16633 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 O=5NoxGyV8or~e0F1}1a> diff --git a/tests/e2e_tests/utils.py b/tests/e2e_tests/utils.py index e6f3368..8ecbec4 100644 --- a/tests/e2e_tests/utils.py +++ b/tests/e2e_tests/utils.py @@ -134,7 +134,7 @@ def compare_debug_pipeline_outputs(pipeline_output_1: Dict[str, Any], pipeline_o pipeline_output_1 (Dict[str, Any]): pipeline output 1. pipeline_output_2 (Dict[str, Any]): pipeline output 2. """ - compare_iris_pipeline_template_output(pipeline_output_1["iris_template"], pipeline_output_2["iris_template"]) + compare_simple_pipeline_template_output(pipeline_output_1["iris_template"], pipeline_output_2["iris_template"]) compare_iris_pipeline_metadata_output(pipeline_output_1["metadata"], pipeline_output_2["metadata"]) # Debug-specific intermediary outputs From be9829c2278657c31a4d88511df7dc6e0b247f42 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wiktor=20=C5=81azarski?= Date: Mon, 10 Jun 2024 13:02:36 +0000 Subject: [PATCH 24/32] Remove warning message. --- src/iris/io/validators.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/iris/io/validators.py b/src/iris/io/validators.py index eadf2be..46169f0 100644 --- a/src/iris/io/validators.py +++ b/src/iris/io/validators.py @@ -143,7 +143,7 @@ def are_all_positive(cls: type, v: Any, field: fields.ModelField) -> Any: def iris_code_version_check(cls: type, v: str, field: fields.ModelField) -> str: """Check if the version provided in the input config matches the current iris.__version__.""" - if not re.match("v[\d]+\.[\d]+$", v): + if not re.match(r"v[\d]+\.[\d]+$", v): raise IRISPipelineError(f"Wrong iris code version. Expected standard version nuber, received {v}") return v From 355d814bf146449b758bebcb2a91904e445321e8 Mon Sep 17 00:00:00 2001 From: TanguyJeanneau Date: Mon, 10 Jun 2024 16:05:38 +0000 Subject: [PATCH 25/32] Add unit tests for IrisTemplate serialization / deserialization --- src/iris/io/dataclasses.py | 44 ++++++------ src/iris/utils/base64_encoding.py | 4 +- tests/unit_tests/io/test_dataclasses.py | 91 ++++++++++++++++++++++++- 3 files changed, 114 insertions(+), 25 deletions(-) diff --git a/src/iris/io/dataclasses.py b/src/iris/io/dataclasses.py index ccee8e4..a1671da 100644 --- a/src/iris/io/dataclasses.py +++ b/src/iris/io/dataclasses.py @@ -581,23 +581,6 @@ class IrisTemplate(ImmutableModel): _is_binary = validator("iris_codes", "mask_codes", allow_reuse=True, each_item=True)(v.is_binary) _iris_code_version_check = validator("iris_code_version", allow_reuse=True)(v.iris_code_version_check) - - def deserialize(serialized_template: dict[str, Union[np.ndarray, str]], array_shape: Tuple = (16, 256, 2, 2)) -> IrisTemplate: - """Deserialize a dict with iris_codes, mask_codes and iris_code_version into an IrisTemplate object. - - Args: - serialized_template (dict[str, Union[np.ndarray, str]]): Serialized object to dict. - array_shape (Tuple, optional): Shape of the iris code. Defaults to (16, 256, 2, 2). - - Returns: - IrisTemplate: Serialized object. - """ - return IrisTemplate.convert_to_new_format( - iris_codes=base64_decode_array(serialized_template["iris_codes"], array_shape=array_shape), - mask_codes=base64_decode_array(serialized_template["mask_codes"], array_shape=array_shape), - iris_code_version=serialized_template["iris_code_version"] - ) - def serialize(self) -> Dict[str, bytes]: """Serialize IrisTemplate object. @@ -620,6 +603,23 @@ def convert2old_format(self) -> List[np.ndarray]: """ return (IrisTemplate.new_to_old_format(self.iris_codes), IrisTemplate.new_to_old_format(self.mask_codes)) + @staticmethod + def deserialize(serialized_template: dict[str, Union[np.ndarray, str]], array_shape: Tuple = (16, 256, 2, 2)) -> IrisTemplate: + """Deserialize a dict with iris_codes, mask_codes and iris_code_version into an IrisTemplate object. + + Args: + serialized_template (dict[str, Union[np.ndarray, str]]): Serialized object to dict. + array_shape (Tuple, optional): Shape of the iris code. Defaults to (16, 256, 2, 2). + + Returns: + IrisTemplate: Serialized object. + """ + return IrisTemplate.convert_to_new_format( + iris_codes=base64_decode_array(serialized_template["iris_codes"], array_shape=array_shape), + mask_codes=base64_decode_array(serialized_template["mask_codes"], array_shape=array_shape), + iris_code_version=serialized_template["iris_code_version"] + ) + @staticmethod def convert_to_new_format(iris_codes: np.ndarray, mask_codes: np.ndarray, iris_code_version: str) -> IrisTemplate: """Convert an old template format and the associated iris code version into an IrisTemplate object. @@ -634,7 +634,7 @@ def convert_to_new_format(iris_codes: np.ndarray, mask_codes: np.ndarray, iris_c ) @staticmethod - def new_to_old_format(codes: List[np.ndarray]) -> np.ndarray: + def new_to_old_format(array: List[np.ndarray]) -> np.ndarray: """Convert new iris template format to old iris template format. New format is a list of arrays, each of shape (height_i, width_i, 2). The length of the list is nb_wavelets. @@ -650,12 +650,12 @@ def new_to_old_format(codes: List[np.ndarray]) -> np.ndarray: Raises: ValueError: Raised if not all codes have the same shape. In this case, the IrisTemplate cannot be converted to the old format. """ - if not all([code.shape == codes[0].shape for code in codes]): + if not all([code.shape == array[0].shape for code in array]): raise ValueError("All codes must have the same shape to be converted to the old format.") - return np.stack(codes).transpose(1, 2, 0, 3) + return np.stack(array).transpose(1, 2, 0, 3) @staticmethod - def old_to_new_format(codes: np.ndarray) -> List[np.ndarray]: + def old_to_new_format(array: np.ndarray) -> List[np.ndarray]: """Convert old iris template format to new iris template format. Old format is a list of arrays, each of shape (height_i, width_i, 2). The length of the list is nb_wavelets. @@ -668,7 +668,7 @@ def old_to_new_format(codes: np.ndarray) -> List[np.ndarray]: Returns: np.ndarray: New format codes. """ - return [codes[:, :, i, :] for i in range(codes.shape[2])] + return [array[:, :, i, :] for i in range(array.shape[2])] class EyeOcclusion(ImmutableModel): diff --git a/src/iris/utils/base64_encoding.py b/src/iris/utils/base64_encoding.py index 4e229d1..606dfc2 100644 --- a/src/iris/utils/base64_encoding.py +++ b/src/iris/utils/base64_encoding.py @@ -18,12 +18,12 @@ def base64_encode_array(array2encode: np.ndarray) -> bytes: return base64.b64encode(co_pack.tobytes()) -def base64_decode_array(bytes_array: str, array_shape: Tuple[int, int, int, int] = (16, 200, 2, 2)) -> np.ndarray: +def base64_decode_array(bytes_array: str, array_shape: Tuple[int, int, int, int] = (16, 256, 2, 2)) -> np.ndarray: """Convert a packed base64 string to a numpy array. Args: bytes_array (bytes): The packed base64 byte string. - shape (Tuple[int, int, int, int], optional): The shape of the array. Defaults to (16, 200, 2, 2). + shape (Tuple[int, int, int, int], optional): The shape of the array. Defaults to (16, 256, 2, 2). Returns: np.ndarray: The array. diff --git a/tests/unit_tests/io/test_dataclasses.py b/tests/unit_tests/io/test_dataclasses.py index ad74baa..e1e3417 100644 --- a/tests/unit_tests/io/test_dataclasses.py +++ b/tests/unit_tests/io/test_dataclasses.py @@ -379,7 +379,7 @@ def test_iris_filter_response_serialize_deserialize() -> None: np.testing.assert_equal(iris_response.iris_responses, deserialized_iris_response.iris_responses) np.testing.assert_equal(iris_response.mask_responses, deserialized_iris_response.mask_responses) - assert iris_response.iris_code_version ==deserialized_iris_response.iris_code_version + assert iris_response.iris_code_version == deserialized_iris_response.iris_code_version def test_iris_template_constructor() -> None: @@ -448,3 +448,92 @@ def test_iris_template_constructor_raises_an_exception( ) -> None: with pytest.raises((ValueError, ValidationError, AttributeError, IRISPipelineError)): _ = dc.IrisTemplate(iris_codes=iris_codes, mask_codes=mask_codes, iris_code_version=iris_code_version) + + + +@pytest.mark.parametrize("code_height,code_width,num_filters", [(5, 10, 2), (10, 5, 4)]) +def test_iris_template_convert2old_format(code_height: int, code_width: int, num_filters: int) -> None: + mock_iris_template = dc.IrisTemplate( + iris_codes=[np.random.choice(2, size=(code_height, code_width, 2)).astype(bool) for _ in range(num_filters)], + mask_codes=[np.random.choice(2, size=(code_height, code_width, 2)).astype(bool) for _ in range(num_filters)], + iris_code_version="v2.1", + ) + + # 2 is for the real/complex part + stacked_iris_codes_expected_shape = (code_height, code_width, num_filters, 2) + stacked_mask_codes_expected_shape = (code_height, code_width, num_filters, 2) + + result_stacked_iris_codes, result_stacked_mask_codes = mock_iris_template.convert2old_format() + + assert result_stacked_iris_codes.shape == stacked_iris_codes_expected_shape + assert result_stacked_mask_codes.shape == stacked_mask_codes_expected_shape + + for filter_idx in range(num_filters): + np.testing.assert_equal( + result_stacked_iris_codes[..., filter_idx, 0], mock_iris_template.iris_codes[filter_idx][..., 0] + ) + np.testing.assert_equal( + result_stacked_iris_codes[..., filter_idx, 1], mock_iris_template.iris_codes[filter_idx][..., 1] + ) + np.testing.assert_equal( + result_stacked_mask_codes[..., filter_idx, 0], mock_iris_template.mask_codes[filter_idx][..., 0] + ) + np.testing.assert_equal( + result_stacked_mask_codes[..., filter_idx, 1], mock_iris_template.mask_codes[filter_idx][..., 1] + ) + + +@pytest.mark.parametrize("code_height,code_width,num_filters", [(5, 10, 2), (10, 5, 4)]) +def test_iris_template_conversion(code_height: int, code_width: int, num_filters: int) -> None: + iris_codes=[np.random.choice(2, size=(code_height, code_width, 2)).astype(bool) for _ in range(num_filters)] + mask_codes=[np.random.choice(2, size=(code_height, code_width, 2)).astype(bool) for _ in range(num_filters)] + iris_code_version="v2.1" + + mock_iris_template = dc.IrisTemplate( + iris_codes=iris_codes, + mask_codes=mask_codes, + iris_code_version=iris_code_version, + ) + + old_format_iris_template = mock_iris_template.convert2old_format() + new_format_iris_template = dc.IrisTemplate.convert_to_new_format(*old_format_iris_template, iris_code_version) + + np.testing.assert_equal(new_format_iris_template.iris_codes, mock_iris_template.iris_codes) + np.testing.assert_equal(new_format_iris_template.mask_codes, mock_iris_template.mask_codes) + assert new_format_iris_template.iris_code_version == mock_iris_template.iris_code_version + + +@pytest.mark.parametrize("code_height,code_width,num_filters", [(5, 10, 2), (10, 5, 4)]) +def test_iris_template_conversion_reverse(code_height: int, code_width: int, num_filters: int) -> None: + iris_codes=np.random.choice(2, size=(code_height, code_width, num_filters, 2)).astype(bool) + mask_codes=np.random.choice(2, size=(code_height, code_width, num_filters, 2)).astype(bool) + iris_code_version="v2.1" + + mock_old_format_iris_template = (iris_codes, mask_codes) + + new_format_iris_template = dc.IrisTemplate.convert_to_new_format(*mock_old_format_iris_template, iris_code_version) + old_format_iris_template = new_format_iris_template.convert2old_format() + + + np.testing.assert_equal(old_format_iris_template[0], mock_old_format_iris_template[0]) + np.testing.assert_equal(old_format_iris_template[1], mock_old_format_iris_template[1]) + + +@pytest.mark.parametrize("code_height,code_width,num_filters", [(5, 10, 2), (10, 5, 4)]) +def test_iris_template_serialize(code_height: int, code_width: int, num_filters: int) -> None: + iris_codes=[np.random.choice(2, size=(code_height, code_width, 2)).astype(bool) for _ in range(num_filters)] + mask_codes=[np.random.choice(2, size=(code_height, code_width, 2)).astype(bool) for _ in range(num_filters)] + iris_code_version="v2.1" + + mock_iris_template = dc.IrisTemplate( + iris_codes=iris_codes, + mask_codes=mask_codes, + iris_code_version=iris_code_version, + ) + + serialized_iris_template = mock_iris_template.serialize() + deserialized_iris_template = dc.IrisTemplate.deserialize(serialized_iris_template, array_shape=(code_height, code_width, num_filters, 2)) + + np.testing.assert_equal(deserialized_iris_template.iris_codes, mock_iris_template.iris_codes) + np.testing.assert_equal(deserialized_iris_template.mask_codes, mock_iris_template.mask_codes) + assert deserialized_iris_template.iris_code_version == mock_iris_template.iris_code_version From 24809bbd7e45641d6cde591df938b270d95cb8d7 Mon Sep 17 00:00:00 2001 From: TanguyJeanneau Date: Mon, 10 Jun 2024 16:41:19 +0000 Subject: [PATCH 26/32] Format code --- src/iris/io/dataclasses.py | 12 ++-- src/iris/io/validators.py | 1 - .../fragile_bits_refinement.py | 4 +- .../perspective_normalization.py | 2 +- src/iris/orchestration/output_builders.py | 4 +- .../test_e2e_occlusion_calculator.py | 2 +- .../test_e2e_conv_filter_bank.py | 4 +- .../orchestration/test_e2e_output_builder.py | 4 +- tests/unit_tests/io/test_dataclasses.py | 65 ++++++++++--------- .../validators/test_object_validators.py | 4 +- .../pipelines/test_iris_pipeline.py | 1 - 11 files changed, 55 insertions(+), 48 deletions(-) diff --git a/src/iris/io/dataclasses.py b/src/iris/io/dataclasses.py index a1671da..ffe5c93 100644 --- a/src/iris/io/dataclasses.py +++ b/src/iris/io/dataclasses.py @@ -604,7 +604,9 @@ def convert2old_format(self) -> List[np.ndarray]: return (IrisTemplate.new_to_old_format(self.iris_codes), IrisTemplate.new_to_old_format(self.mask_codes)) @staticmethod - def deserialize(serialized_template: dict[str, Union[np.ndarray, str]], array_shape: Tuple = (16, 256, 2, 2)) -> IrisTemplate: + def deserialize( + serialized_template: dict[str, Union[np.ndarray, str]], array_shape: Tuple = (16, 256, 2, 2) + ) -> IrisTemplate: """Deserialize a dict with iris_codes, mask_codes and iris_code_version into an IrisTemplate object. Args: @@ -617,7 +619,7 @@ def deserialize(serialized_template: dict[str, Union[np.ndarray, str]], array_sh return IrisTemplate.convert_to_new_format( iris_codes=base64_decode_array(serialized_template["iris_codes"], array_shape=array_shape), mask_codes=base64_decode_array(serialized_template["mask_codes"], array_shape=array_shape), - iris_code_version=serialized_template["iris_code_version"] + iris_code_version=serialized_template["iris_code_version"], ) @staticmethod @@ -636,7 +638,7 @@ def convert_to_new_format(iris_codes: np.ndarray, mask_codes: np.ndarray, iris_c @staticmethod def new_to_old_format(array: List[np.ndarray]) -> np.ndarray: """Convert new iris template format to old iris template format. - + New format is a list of arrays, each of shape (height_i, width_i, 2). The length of the list is nb_wavelets. This enable having different convolution layout for each wavelet. Old format is a numpy array of shape (height, width, nb_wavelets, 2) @@ -653,11 +655,11 @@ def new_to_old_format(array: List[np.ndarray]) -> np.ndarray: if not all([code.shape == array[0].shape for code in array]): raise ValueError("All codes must have the same shape to be converted to the old format.") return np.stack(array).transpose(1, 2, 0, 3) - + @staticmethod def old_to_new_format(array: np.ndarray) -> List[np.ndarray]: """Convert old iris template format to new iris template format. - + Old format is a list of arrays, each of shape (height_i, width_i, 2). The length of the list is nb_wavelets. This enable having different convolution layout for each wavelet. New format is a numpy array of shape (height, width, nb_wavelets, 2) diff --git a/src/iris/io/validators.py b/src/iris/io/validators.py index 46169f0..13df4b0 100644 --- a/src/iris/io/validators.py +++ b/src/iris/io/validators.py @@ -6,7 +6,6 @@ from iris.io.errors import IRISPipelineError - # ----- validators ----- diff --git a/src/iris/nodes/iris_response_refinement/fragile_bits_refinement.py b/src/iris/nodes/iris_response_refinement/fragile_bits_refinement.py index 6d20522..51d1fc1 100644 --- a/src/iris/nodes/iris_response_refinement/fragile_bits_refinement.py +++ b/src/iris/nodes/iris_response_refinement/fragile_bits_refinement.py @@ -70,6 +70,6 @@ def run(self, iris_filter_response: IrisFilterResponse) -> IrisFilterResponse: return IrisFilterResponse( iris_responses=iris_filter_response.iris_responses, - mask_responses=fragile_masks, - iris_code_version=iris_filter_response.iris_code_version + mask_responses=fragile_masks, + iris_code_version=iris_filter_response.iris_code_version, ) diff --git a/src/iris/nodes/normalization/perspective_normalization.py b/src/iris/nodes/normalization/perspective_normalization.py index 27abbfc..2d3f166 100644 --- a/src/iris/nodes/normalization/perspective_normalization.py +++ b/src/iris/nodes/normalization/perspective_normalization.py @@ -12,7 +12,7 @@ generate_iris_mask, get_pixel_or_default, interpolate_pixel_intensity, - to_uint8 + to_uint8, ) diff --git a/src/iris/orchestration/output_builders.py b/src/iris/orchestration/output_builders.py index 3b5bcd8..4110dde 100644 --- a/src/iris/orchestration/output_builders.py +++ b/src/iris/orchestration/output_builders.py @@ -51,7 +51,9 @@ def build_orb_output(call_trace: PipelineCallTraceStorage) -> Dict[str, Any]: return output -def build_debugging_output(call_trace: PipelineCallTraceStorage, serialise_iris_template: bool = False) -> Dict[str, Any]: +def build_debugging_output( + call_trace: PipelineCallTraceStorage, serialise_iris_template: bool = False +) -> Dict[str, Any]: """Build the output for debugging purposes. Args: diff --git a/tests/e2e_tests/nodes/eye_properties_estimation/test_e2e_occlusion_calculator.py b/tests/e2e_tests/nodes/eye_properties_estimation/test_e2e_occlusion_calculator.py index ae3fc99..1177c01 100644 --- a/tests/e2e_tests/nodes/eye_properties_estimation/test_e2e_occlusion_calculator.py +++ b/tests/e2e_tests/nodes/eye_properties_estimation/test_e2e_occlusion_calculator.py @@ -2,8 +2,8 @@ import pickle from typing import Any -import pytest import numpy as np +import pytest from iris.io.dataclasses import EyeOcclusion from iris.nodes.eye_properties_estimation.occlusion_calculator import OcclusionCalculator diff --git a/tests/e2e_tests/nodes/iris_response/test_e2e_conv_filter_bank.py b/tests/e2e_tests/nodes/iris_response/test_e2e_conv_filter_bank.py index 352fd7f..2544902 100644 --- a/tests/e2e_tests/nodes/iris_response/test_e2e_conv_filter_bank.py +++ b/tests/e2e_tests/nodes/iris_response/test_e2e_conv_filter_bank.py @@ -79,7 +79,7 @@ def test_computed_responses( assert np.allclose(expected_result.mask_responses[0], result.mask_responses[0], rtol=1e-05, atol=1e-07) assert np.allclose(expected_result.mask_responses[1], result.mask_responses[1], rtol=1e-05, atol=1e-07) assert np.allclose(expected_result.mask_responses[2], result.mask_responses[2], rtol=1e-05, atol=1e-07) - assert result.iris_code_version == 'v0.1' + assert result.iris_code_version == "v0.1" @pytest.mark.parametrize( @@ -122,4 +122,4 @@ def test_convfilterbank_constructor( assert np.max(i_mask_response) > np.min(i_mask_response) assert np.max(i_iris_response.real) > np.min(i_iris_response.real) assert np.max(i_iris_response.imag) > np.min(i_iris_response.imag) - assert filter_responses.iris_code_version == 'v0.1' + assert filter_responses.iris_code_version == "v0.1" diff --git a/tests/e2e_tests/orchestration/test_e2e_output_builder.py b/tests/e2e_tests/orchestration/test_e2e_output_builder.py index 7fef9c7..ebdfc89 100644 --- a/tests/e2e_tests/orchestration/test_e2e_output_builder.py +++ b/tests/e2e_tests/orchestration/test_e2e_output_builder.py @@ -17,7 +17,9 @@ @pytest.fixture def expected_simple_iris_pipeline_output() -> Tuple[Tuple[np.ndarray, np.ndarray], Landmarks, Dict[str, Any]]: - expected_output_path = os.path.join(os.path.dirname(__file__), "mocks", "expected_iris_pipeline_simple_output.pickle") + expected_output_path = os.path.join( + os.path.dirname(__file__), "mocks", "expected_iris_pipeline_simple_output.pickle" + ) return pickle.load(open(expected_output_path, "rb")) diff --git a/tests/unit_tests/io/test_dataclasses.py b/tests/unit_tests/io/test_dataclasses.py index e1e3417..dda920a 100644 --- a/tests/unit_tests/io/test_dataclasses.py +++ b/tests/unit_tests/io/test_dataclasses.py @@ -7,6 +7,7 @@ import iris.io.dataclasses as dc from iris.io.errors import IRISPipelineError + def test_irimage_constructor() -> None: mock_image = np.ones(shape=(10, 10)) mock_side = "left" @@ -304,9 +305,11 @@ def test_normalized_iris_serialize_deserialize() -> None: def test_iris_filter_response_constructor() -> None: mock_responses = [np.random.randint(5, size=(4, 6)) for _ in range(3)] mock_masks = [np.random.randint(2, size=(4, 6)).astype(bool) for _ in range(3)] - mock_iris_code_version = 'v14.28' + mock_iris_code_version = "v14.28" - _ = dc.IrisFilterResponse(iris_responses=mock_responses, mask_responses=mock_masks, iris_code_version=mock_iris_code_version) + _ = dc.IrisFilterResponse( + iris_responses=mock_responses, mask_responses=mock_masks, iris_code_version=mock_iris_code_version + ) @pytest.mark.parametrize( @@ -315,17 +318,17 @@ def test_iris_filter_response_constructor() -> None: ( [np.ones(shape=(10, 10)), "not some string", np.ones(shape=(10, 10))], [np.random.randint(2, size=(4, 6)).astype(bool) for _ in range(3)], - 'v3.0', + "v3.0", ), ( [np.random.randint(5, size=(4, 6)) for _ in range(3)], [np.random.randint(2, size=(4, 6)).astype(bool) for _ in range(5)], - 'v6.2', + "v6.2", ), ( [np.ones(shape=(10, 10)), np.ones(shape=(123, 456))], [np.ones(shape=(10, 10)).astype(bool), np.ones(shape=(835, 19)).astype(bool)], - 'v2.1', + "v2.1", ), ( [np.random.randint(5, size=(4, 6)) for _ in range(3)], @@ -335,12 +338,12 @@ def test_iris_filter_response_constructor() -> None: ( [np.random.randint(5, size=(4, 6)) for _ in range(3)], [np.random.randint(2, size=(4, 6)).astype(bool) for _ in range(3)], - 'not_a_version', + "not_a_version", ), ( [np.random.randint(5, size=(4, 6)) for _ in range(3)], [np.random.randint(2, size=(4, 6)).astype(bool) for _ in range(3)], - 'va.b.c', + "va.b.c", ), ], ids=[ @@ -357,16 +360,14 @@ def test_iris_filter_response_constructor_raises_an_exception( ) -> None: with pytest.raises((ValueError, AttributeError, IRISPipelineError)): _ = dc.IrisFilterResponse( - iris_responses=iris_responses, - mask_responses=mask_responses, - iris_code_version=iris_code_version + iris_responses=iris_responses, mask_responses=mask_responses, iris_code_version=iris_code_version ) def test_iris_filter_response_serialize_deserialize() -> None: mock_responses = [np.random.randint(5, size=(4, 6)) for _ in range(3)] mock_masks = [np.random.randint(2, size=(4, 6)).astype(bool) for _ in range(3)] - mock_iris_code_version = 'v14.28' + mock_iris_code_version = "v14.28" iris_response = dc.IrisFilterResponse( iris_responses=mock_responses, @@ -385,7 +386,7 @@ def test_iris_filter_response_serialize_deserialize() -> None: def test_iris_template_constructor() -> None: mock_iris_codes = [np.random.randint(2, size=(10, 10)).astype(bool) for _ in range(5)] mock_mask_codes = [np.random.randint(2, size=(10, 10)).astype(bool) for _ in range(5)] - mock_iris_code_version = 'v14.28' + mock_iris_code_version = "v14.28" _ = dc.IrisTemplate( iris_codes=mock_iris_codes, @@ -400,22 +401,22 @@ def test_iris_template_constructor() -> None: ( [np.random.randint(2, size=(10, 10)) for _ in range(5)], [np.random.randint(2, size=(10, 10)) for _ in range(5)], - 'v3.0', + "v3.0", ), ( "not a list of arrays", 3, - 'v3.0', + "v3.0", ), ( [np.random.randint(2, size=(4, 6)).astype(bool) for _ in range(3)], [np.random.randint(2, size=(4, 6)).astype(bool) for _ in range(5)], - 'v3.0', + "v3.0", ), ( [np.ones(shape=(10, 10)).astype(bool), np.ones(shape=(123, 456)).astype(bool)], [np.ones(shape=(10, 10)).astype(bool), np.ones(shape=(835, 19)).astype(bool)], - 'v3.0', + "v3.0", ), ( [np.random.randint(2, size=(10, 10)).astype(bool) for _ in range(5)], @@ -425,12 +426,12 @@ def test_iris_template_constructor() -> None: ( [np.random.randint(2, size=(10, 10)).astype(bool) for _ in range(5)], [np.random.randint(2, size=(10, 10)).astype(bool) for _ in range(5)], - 'not_a_version', + "not_a_version", ), ( [np.random.randint(2, size=(10, 10)).astype(bool) for _ in range(5)], [np.random.randint(2, size=(10, 10)).astype(bool) for _ in range(5)], - 'va.b.c', + "va.b.c", ), ], ids=[ @@ -444,13 +445,14 @@ def test_iris_template_constructor() -> None: ], ) def test_iris_template_constructor_raises_an_exception( - iris_codes: List[np.ndarray], mask_codes: List[np.ndarray], iris_code_version: str, + iris_codes: List[np.ndarray], + mask_codes: List[np.ndarray], + iris_code_version: str, ) -> None: with pytest.raises((ValueError, ValidationError, AttributeError, IRISPipelineError)): _ = dc.IrisTemplate(iris_codes=iris_codes, mask_codes=mask_codes, iris_code_version=iris_code_version) - @pytest.mark.parametrize("code_height,code_width,num_filters", [(5, 10, 2), (10, 5, 4)]) def test_iris_template_convert2old_format(code_height: int, code_width: int, num_filters: int) -> None: mock_iris_template = dc.IrisTemplate( @@ -485,9 +487,9 @@ def test_iris_template_convert2old_format(code_height: int, code_width: int, num @pytest.mark.parametrize("code_height,code_width,num_filters", [(5, 10, 2), (10, 5, 4)]) def test_iris_template_conversion(code_height: int, code_width: int, num_filters: int) -> None: - iris_codes=[np.random.choice(2, size=(code_height, code_width, 2)).astype(bool) for _ in range(num_filters)] - mask_codes=[np.random.choice(2, size=(code_height, code_width, 2)).astype(bool) for _ in range(num_filters)] - iris_code_version="v2.1" + iris_codes = [np.random.choice(2, size=(code_height, code_width, 2)).astype(bool) for _ in range(num_filters)] + mask_codes = [np.random.choice(2, size=(code_height, code_width, 2)).astype(bool) for _ in range(num_filters)] + iris_code_version = "v2.1" mock_iris_template = dc.IrisTemplate( iris_codes=iris_codes, @@ -505,25 +507,24 @@ def test_iris_template_conversion(code_height: int, code_width: int, num_filters @pytest.mark.parametrize("code_height,code_width,num_filters", [(5, 10, 2), (10, 5, 4)]) def test_iris_template_conversion_reverse(code_height: int, code_width: int, num_filters: int) -> None: - iris_codes=np.random.choice(2, size=(code_height, code_width, num_filters, 2)).astype(bool) - mask_codes=np.random.choice(2, size=(code_height, code_width, num_filters, 2)).astype(bool) - iris_code_version="v2.1" + iris_codes = np.random.choice(2, size=(code_height, code_width, num_filters, 2)).astype(bool) + mask_codes = np.random.choice(2, size=(code_height, code_width, num_filters, 2)).astype(bool) + iris_code_version = "v2.1" mock_old_format_iris_template = (iris_codes, mask_codes) new_format_iris_template = dc.IrisTemplate.convert_to_new_format(*mock_old_format_iris_template, iris_code_version) old_format_iris_template = new_format_iris_template.convert2old_format() - np.testing.assert_equal(old_format_iris_template[0], mock_old_format_iris_template[0]) np.testing.assert_equal(old_format_iris_template[1], mock_old_format_iris_template[1]) @pytest.mark.parametrize("code_height,code_width,num_filters", [(5, 10, 2), (10, 5, 4)]) def test_iris_template_serialize(code_height: int, code_width: int, num_filters: int) -> None: - iris_codes=[np.random.choice(2, size=(code_height, code_width, 2)).astype(bool) for _ in range(num_filters)] - mask_codes=[np.random.choice(2, size=(code_height, code_width, 2)).astype(bool) for _ in range(num_filters)] - iris_code_version="v2.1" + iris_codes = [np.random.choice(2, size=(code_height, code_width, 2)).astype(bool) for _ in range(num_filters)] + mask_codes = [np.random.choice(2, size=(code_height, code_width, 2)).astype(bool) for _ in range(num_filters)] + iris_code_version = "v2.1" mock_iris_template = dc.IrisTemplate( iris_codes=iris_codes, @@ -532,7 +533,9 @@ def test_iris_template_serialize(code_height: int, code_width: int, num_filters: ) serialized_iris_template = mock_iris_template.serialize() - deserialized_iris_template = dc.IrisTemplate.deserialize(serialized_iris_template, array_shape=(code_height, code_width, num_filters, 2)) + deserialized_iris_template = dc.IrisTemplate.deserialize( + serialized_iris_template, array_shape=(code_height, code_width, num_filters, 2) + ) np.testing.assert_equal(deserialized_iris_template.iris_codes, mock_iris_template.iris_codes) np.testing.assert_equal(deserialized_iris_template.mask_codes, mock_iris_template.mask_codes) diff --git a/tests/unit_tests/nodes/validators/test_object_validators.py b/tests/unit_tests/nodes/validators/test_object_validators.py index 4aacab2..a67bbc9 100644 --- a/tests/unit_tests/nodes/validators/test_object_validators.py +++ b/tests/unit_tests/nodes/validators/test_object_validators.py @@ -265,7 +265,7 @@ def test_is_mask_too_small_validator( mask_codes=[ rng.choice(2, size=(code_height, code_width, 2), p=[0.1, 0.9]).astype(bool) for _ in range(num_filters) ], - iris_code_version='v3.0', + iris_code_version="v3.0", ) validator = obj_v.IsMaskTooSmallValidator(min_maskcodes_size=min_maskcodes_size) try: @@ -293,7 +293,7 @@ def test_is_mask_too_small_validator_raise_exception( mask_codes=[ rng.choice(2, size=(code_height, code_width, 2), p=[0.5, 0.5]).astype(bool) for _ in range(num_filters) ], - iris_code_version='v3.0', + iris_code_version="v3.0", ) validator = obj_v.IsMaskTooSmallValidator(min_maskcodes_size=min_maskcodes_size) diff --git a/tests/unit_tests/pipelines/test_iris_pipeline.py b/tests/unit_tests/pipelines/test_iris_pipeline.py index f7ce9e3..ac9ba6f 100644 --- a/tests/unit_tests/pipelines/test_iris_pipeline.py +++ b/tests/unit_tests/pipelines/test_iris_pipeline.py @@ -644,7 +644,6 @@ def test_instanciate_nodes( ), (None, None), ], - ids=["specified pipeline", "default pipeline"], ) def test_load_from_config(config: Dict[str, str], expected_pipeline_name: str) -> None: From 72f64eff25ec15b834cbd74ba4c84f58d947d851 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wiktor=20=C5=81azarski?= Date: Tue, 11 Jun 2024 16:14:57 +0000 Subject: [PATCH 27/32] Fix `pydocstring` issue. --- src/iris/nodes/iris_response/conv_filter_bank.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/iris/nodes/iris_response/conv_filter_bank.py b/src/iris/nodes/iris_response/conv_filter_bank.py index 78ef39c..78729ff 100644 --- a/src/iris/nodes/iris_response/conv_filter_bank.py +++ b/src/iris/nodes/iris_response/conv_filter_bank.py @@ -87,6 +87,7 @@ def __init__( """Assign parameters. Args: + iris_code_version (str): Iris code version. Defaults to "v0.1". filters (List[ImageFilter]): List of image filters. probe_schemas (List[ProbeSchema]): List of corresponding probe schemas. """ From 22900ddd0c71a973664fa8e86e9703eefe4e9d38 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wiktor=20=C5=81azarski?= Date: Tue, 11 Jun 2024 16:16:40 +0000 Subject: [PATCH 28/32] Update the package version. --- src/iris/_version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/iris/_version.py b/src/iris/_version.py index 5becc17..6849410 100644 --- a/src/iris/_version.py +++ b/src/iris/_version.py @@ -1 +1 @@ -__version__ = "1.0.0" +__version__ = "1.1.0" From 358b23813c55c8e6bf74013f25f1f515bf95b03e Mon Sep 17 00:00:00 2001 From: TanguyJeanneau Date: Tue, 11 Jun 2024 16:30:55 +0000 Subject: [PATCH 29/32] format code --- src/iris/nodes/normalization/common.py | 8 ++++---- src/iris/nodes/normalization/perspective_normalization.py | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/iris/nodes/normalization/common.py b/src/iris/nodes/normalization/common.py index 9b2c679..4070e32 100644 --- a/src/iris/nodes/normalization/common.py +++ b/src/iris/nodes/normalization/common.py @@ -1,4 +1,4 @@ -from typing import Any, Tuple +from typing import Any, Tuple, Union import numpy as np from pydantic import NonNegativeInt @@ -67,17 +67,17 @@ def getgrids(res_in_r: NonNegativeInt, p2i_ratio: NonNegativeInt) -> np.ndarray: return grids[0:-1] + np.diff(grids) / 2 -def get_pixel_or_default(image: np.ndarray, pixel_x: float, pixel_y: float, default: Any) -> Any: +def get_pixel_or_default(image: np.ndarray, pixel_x: float, pixel_y: float, default: Union[bool, int]) -> Union[bool, int]: """Get the value of a pixel in the image 2D array. Args: image (np.ndarray): 2D Array. pixel_x (float): Pixel x coordinate. pixel_y (float): Pixel y coordinate. - default (Any): Default value to return when (pixel_x, pixel_y) is out-of-bounds + default (Union[bool, int]): Default value to return when (pixel_x, pixel_y) is out-of-bounds Returns: - Any: Pixel value. + Union[bool, int]: Pixel value. """ h, w = image.shape x, y = int(pixel_x), int(pixel_y) diff --git a/src/iris/nodes/normalization/perspective_normalization.py b/src/iris/nodes/normalization/perspective_normalization.py index 2d3f166..8f8b6c6 100644 --- a/src/iris/nodes/normalization/perspective_normalization.py +++ b/src/iris/nodes/normalization/perspective_normalization.py @@ -137,7 +137,7 @@ def _run_core( src_points: np.ndarray, dst_points: np.ndarray, normalized_iris: NormalizedIris, - ): + ) -> None: for angle_point_idx in range(src_points.shape[1] - 1): for ring_idx in range(src_points.shape[0] - 1): current_src, current_dst = self._correspondence_rois_coords( From 464444b7750324447b5cef1ae4db48fa0de8d078 Mon Sep 17 00:00:00 2001 From: TanguyJeanneau Date: Tue, 11 Jun 2024 16:41:44 +0000 Subject: [PATCH 30/32] Update tutorials --- colab/ConfiguringCustomPipeline.ipynb | 12 +- colab/GettingStarted.ipynb | 162 +++++++++++++++++++++++--- colab/MatchingEntities.ipynb | 40 +++++-- 3 files changed, 188 insertions(+), 26 deletions(-) diff --git a/colab/ConfiguringCustomPipeline.ipynb b/colab/ConfiguringCustomPipeline.ipynb index b691df4..c22d3b9 100644 --- a/colab/ConfiguringCustomPipeline.ipynb +++ b/colab/ConfiguringCustomPipeline.ipynb @@ -125,7 +125,7 @@ "```yaml\n", "metadata:\n", " pipeline_name: iris_pipeline\n", - " iris_version: 1.0.0\n", + " iris_version: 1.1.0\n", "```\n", "\n", "The top YAML file contains `IRISPipeline` metadata, used to both describe `IRISPipeline` and specify package parameters that are later used to verify compatibility between `iris` package version/release and later, specified in the `pipeline` YAML file section, pipeline's graph.\n", @@ -205,7 +205,7 @@ "outputs": [], "source": [ "default_pipeline_conf = {\n", - " \"metadata\": {\"pipeline_name\": \"iris_pipeline\", \"iris_version\": \"1.0.0\"},\n", + " \"metadata\": {\"pipeline_name\": \"iris_pipeline\", \"iris_version\": \"1.1.0\"},\n", " \"pipeline\": [\n", " {\n", " \"name\": \"segmentation\",\n", @@ -480,7 +480,7 @@ "outputs": [], "source": [ "new_pipeline_conf = {\n", - " \"metadata\": {\"pipeline_name\": \"iris_pipeline\", \"iris_version\": \"1.0.0\"},\n", + " \"metadata\": {\"pipeline_name\": \"iris_pipeline\", \"iris_version\": \"1.1.0\"},\n", " \"pipeline\": [\n", " {\n", " \"name\": \"segmentation\",\n", @@ -759,7 +759,7 @@ "outputs": [], "source": [ "default_pipeline_conf = {\n", - " \"metadata\": {\"pipeline_name\": \"iris_pipeline\", \"iris_version\": \"1.0.0\"},\n", + " \"metadata\": {\"pipeline_name\": \"iris_pipeline\", \"iris_version\": \"1.1.0\"},\n", " \"pipeline\": [\n", " {\n", " \"name\": \"segmentation\",\n", @@ -1034,7 +1034,7 @@ "outputs": [], "source": [ "new_pipeline_conf = {\n", - " \"metadata\": {\"pipeline_name\": \"iris_pipeline\", \"iris_version\": \"1.0.0\"},\n", + " \"metadata\": {\"pipeline_name\": \"iris_pipeline\", \"iris_version\": \"1.1.0\"},\n", " \"pipeline\": [\n", " {\n", " \"name\": \"segmentation\",\n", @@ -1385,7 +1385,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.8.0" + "version": "3.9.0" } }, "nbformat": 4, diff --git a/colab/GettingStarted.ipynb b/colab/GettingStarted.ipynb index a1ed735..a01e5d9 100644 --- a/colab/GettingStarted.ipynb +++ b/colab/GettingStarted.ipynb @@ -111,8 +111,10 @@ "outputs": [], "source": [ "import cv2\n", + "import matplotlib.pyplot as plt\n", "\n", - "img_pixels = cv2.imread(\"./sample_ir_image.png\", cv2.IMREAD_GRAYSCALE)" + "img_pixels = cv2.imread(\"./sample_ir_image.png\", cv2.IMREAD_GRAYSCALE)\n", + "plt.imshow(img_pixels, cmap='gray')" ] }, { @@ -208,16 +210,6 @@ "output[\"error\"] is None" ] }, - { - "cell_type": "code", - "execution_count": null, - "id": "6263acc3-3e7d-406b-a1e1-c628b13be7dd", - "metadata": {}, - "outputs": [], - "source": [ - "output[\"error\"]" - ] - }, { "cell_type": "markdown", "id": "3b2896e5-5069-48db-b855-cd80ea04cd6e", @@ -284,6 +276,7 @@ "id": "d68022fc-51df-469e-8355-ecc6e12681d2", "metadata": {}, "source": [ + "----\n", "## 2. Configuring `IRISPipeline` environment" ] }, @@ -392,7 +385,8 @@ "id": "a703f17c-b4ba-4e13-933d-b4101e067fb0", "metadata": {}, "source": [ - "## 3. Visualizing intermediate results" + "----\n", + "## 3. Visualizing results" ] }, { @@ -447,6 +441,148 @@ "plt.show()" ] }, + { + "cell_type": "code", + "execution_count": null, + "id": "64c3c4f7", + "metadata": {}, + "outputs": [], + "source": [ + "output['metadata']['eye_side']" + ] + }, + { + "cell_type": "markdown", + "id": "a6ac23dc", + "metadata": {}, + "source": [ + "---\n", + "## 4. Visualising intermediary results\n", + "\n", + "Here is how you can access the actual list of nodes (i.e. the graph of algorithms that were used to process the input image into an iris code) used in the iris pipeline" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "01697a7e", + "metadata": {}, + "outputs": [], + "source": [ + "iris_pipeline.params.pipeline" + ] + }, + { + "cell_type": "markdown", + "id": "a3b02dfd", + "metadata": {}, + "source": [ + "These are declared in the yaml file used to define the computation graph. By delaut, it is located at `src/iris/pipelines/confs/pipeline.yaml`" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7eca2f39", + "metadata": {}, + "outputs": [], + "source": [ + "import yaml\n", + "with open('../src/iris/pipelines/confs/pipeline.yaml', 'r') as file:\n", + " pipeline_config = yaml.safe_load(file)\n", + "pipeline_config" + ] + }, + { + "cell_type": "markdown", + "id": "dd07bc9e", + "metadata": {}, + "source": [ + "The iris pipeline object stores all intermediary results from the previous computation in its `call_trace`. This is reseted at every call, so only the latest intermediary results are available" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bbe1e8ea", + "metadata": {}, + "outputs": [], + "source": [ + "iris_pipeline.call_trace['segmentation']" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "acdf62cb", + "metadata": {}, + "outputs": [], + "source": [ + "canvas = iris_visualizer.plot_segmentation_map(\n", + " ir_image=iris.IRImage(img_data=img_pixels, eye_side=\"right\"),\n", + " segmap=iris_pipeline.call_trace['segmentation'],\n", + ")\n", + "plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9c2a2a41", + "metadata": {}, + "outputs": [], + "source": [ + "canvas = iris_visualizer.plot_all_geometry(\n", + " ir_image=iris.IRImage(img_data=img_pixels, eye_side=\"right\"),\n", + " geometry_polygons=iris_pipeline.call_trace['vectorization'],\n", + " eye_orientation=iris_pipeline.call_trace['eye_orientation'],\n", + " eye_center=iris_pipeline.call_trace['eye_center_estimation'],\n", + ")\n", + "plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "52038fef", + "metadata": {}, + "outputs": [], + "source": [ + "canvas = iris_visualizer.plot_all_geometry(\n", + " ir_image=iris.IRImage(img_data=img_pixels, eye_side=\"right\"),\n", + " geometry_polygons=iris_pipeline.call_trace['geometry_estimation'],\n", + " eye_orientation=iris_pipeline.call_trace['eye_orientation'],\n", + " eye_center=iris_pipeline.call_trace['eye_center_estimation'],\n", + ")\n", + "plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "eba68cd5", + "metadata": {}, + "outputs": [], + "source": [ + "canvas = iris_visualizer.plot_normalized_iris(\n", + " normalized_iris=iris_pipeline.call_trace['normalization'],\n", + ")\n", + "plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d4e6399a", + "metadata": {}, + "outputs": [], + "source": [ + "canvas = iris_visualizer.plot_iris_template(\n", + " iris_template=iris_pipeline.call_trace['encoder'],\n", + ")\n", + "plt.show()" + ] + }, { "cell_type": "markdown", "id": "a3b7eab7-17cc-4e3f-a0d8-bf8e29463127", @@ -474,7 +610,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.8.0" + "version": "3.9.0" } }, "nbformat": 4, diff --git a/colab/MatchingEntities.ipynb b/colab/MatchingEntities.ipynb index 814fda5..a2feafb 100644 --- a/colab/MatchingEntities.ipynb +++ b/colab/MatchingEntities.ipynb @@ -103,6 +103,26 @@ "subject2_image = cv2.imread(\"./subject2_image.png\", cv2.IMREAD_GRAYSCALE)" ] }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import matplotlib.pyplot as plt\n", + "plt.imshow(subject1_first_image, cmap='gray')\n", + "plt.title('Subject 1, image 1')\n", + "plt.show()\n", + "\n", + "plt.imshow(subject1_second_image, cmap='gray')\n", + "plt.title('Subject 1, image 2')\n", + "plt.show()\n", + "\n", + "plt.imshow(subject2_image, cmap='gray')\n", + "plt.title('Subject 2, image 1')\n", + "plt.show()" + ] + }, { "cell_type": "markdown", "metadata": {}, @@ -120,12 +140,14 @@ "\n", "iris_pipeline = iris.IRISPipeline()\n", "\n", - "_ = iris_pipeline(subject1_first_image, eye_side=\"left\")\n", - "subject1_first_code = iris_pipeline.call_trace.get(\"encoder\")\n", - "_ = iris_pipeline(subject1_second_image, eye_side=\"left\")\n", - "subject1_second_code = iris_pipeline.call_trace.get(\"encoder\")\n", - "_ = iris_pipeline(subject2_image, eye_side=\"left\")\n", - "subject2_code = iris_pipeline.call_trace.get(\"encoder\")" + "output_1 = iris_pipeline(subject1_first_image, eye_side=\"left\")\n", + "subject1_first_code = output_1['iris_template']\n", + "\n", + "output_2 = iris_pipeline(subject1_second_image, eye_side=\"left\")\n", + "subject1_second_code = output_2['iris_template']\n", + "\n", + "output_3 = iris_pipeline(subject2_image, eye_side=\"left\")\n", + "subject2_code = output_3['iris_template']" ] }, { @@ -188,6 +210,10 @@ "cell_type": "markdown", "metadata": {}, "source": [ + "The recommended threshold is between 0.34 and 0.39 depending on the sensibitity of your use case, 0.37 is a good choice if you do not favor False Matches or False Non-Matches.\n", + "\n", + "In this example, the two iris images from the same subject are clearly matched, and those form different subjects clearly non-matched.\n", + "\n", "### Thank you for making it to the end of this tutorial!" ] } @@ -208,7 +234,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.8.0" + "version": "3.9.0" } }, "nbformat": 4, From a556e5c86778a0af83cca1f89f8e456b1b119977 Mon Sep 17 00:00:00 2001 From: TanguyJeanneau Date: Tue, 11 Jun 2024 16:42:05 +0000 Subject: [PATCH 31/32] Update pipeline.yaml version --- src/iris/pipelines/confs/pipeline.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/iris/pipelines/confs/pipeline.yaml b/src/iris/pipelines/confs/pipeline.yaml index d416b55..b66f5f1 100644 --- a/src/iris/pipelines/confs/pipeline.yaml +++ b/src/iris/pipelines/confs/pipeline.yaml @@ -1,6 +1,6 @@ metadata: pipeline_name: iris_pipeline - iris_version: 1.0.0 + iris_version: 1.1.0 pipeline: - name: segmentation From 6b35c7bf0dff52d531a990f81745d2a2083b772c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wiktor=20=C5=81azarski?= Date: Tue, 11 Jun 2024 17:00:01 +0000 Subject: [PATCH 32/32] Apply tools and fix all code quality issues. --- src/iris/nodes/normalization/common.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/iris/nodes/normalization/common.py b/src/iris/nodes/normalization/common.py index 4070e32..01d94b3 100644 --- a/src/iris/nodes/normalization/common.py +++ b/src/iris/nodes/normalization/common.py @@ -1,4 +1,4 @@ -from typing import Any, Tuple, Union +from typing import Tuple, Union import numpy as np from pydantic import NonNegativeInt @@ -67,7 +67,9 @@ def getgrids(res_in_r: NonNegativeInt, p2i_ratio: NonNegativeInt) -> np.ndarray: return grids[0:-1] + np.diff(grids) / 2 -def get_pixel_or_default(image: np.ndarray, pixel_x: float, pixel_y: float, default: Union[bool, int]) -> Union[bool, int]: +def get_pixel_or_default( + image: np.ndarray, pixel_x: float, pixel_y: float, default: Union[bool, int] +) -> Union[bool, int]: """Get the value of a pixel in the image 2D array. Args: