diff --git a/Dockerfile b/Dockerfile index 4907780e..3dea5d8c 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,8 +1,9 @@ FROM python:3.8 AS xaitk_base # install poetry and configure to install to pip environment -RUN pip install poetry \ -&& poetry config virtualenvs.create false +ENV PATH=/root/.local/bin:${PATH} +RUN (curl -sSL 'https://install.python-poetry.org' | python3 -) \ + && poetry config virtualenvs.create false WORKDIR /xaitk @@ -11,5 +12,9 @@ COPY poetry.lock pyproject.toml /xaitk/ RUN poetry install --no-root # install package -COPY . /xaitk/ +COPY docs /xaitk/docs/ +COPY scripts /xaitk/scripts/ +COPY tests /xaitk/tests/ +COPY xaitk_saliency /xaitk/xaitk_saliency +COPY .flake8 .mypy.ini CONTRIBUTING.md LICENSE.txt README.md /xaitk/ RUN poetry install diff --git a/docs/release_notes/pending_release.rst b/docs/release_notes/pending_release.rst index 4b1e03e6..2990e286 100644 --- a/docs/release_notes/pending_release.rst +++ b/docs/release_notes/pending_release.rst @@ -4,9 +4,20 @@ Pending Release Notes Updates / New Features ---------------------- +Docker + +* Update Dockerfile install of poetry, and make separate & specific directory + copies. + Utils * Updated logging format of occlusion masking benchmark utility. Fixes ----- + +Tests + +* Update various floating point equality comparisons to not use exact match. + +* Fix random number usage from numpy to use `np.random.default_rng`. diff --git a/poetry.lock b/poetry.lock index cc9e2147..76176e16 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 1.5.1 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.7.1 and should not be changed by hand. [[package]] name = "affine" @@ -1371,6 +1371,7 @@ optional = true python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*, !=3.6.*" files = [ {file = "jsonpointer-2.4-py2.py3-none-any.whl", hash = "sha256:15d51bba20eea3165644553647711d150376234112651b4f1811022aecad7d7a"}, + {file = "jsonpointer-2.4.tar.gz", hash = "sha256:585cee82b70211fa9e6043b7bb89db6e1aa49524340dde8ad6b63206ea689d88"}, ] [[package]] @@ -2553,7 +2554,7 @@ files = [ [package.dependencies] numpy = [ {version = ">=1.20.3", markers = "python_version < \"3.10\""}, - {version = ">=1.21.0", markers = "python_version >= \"3.10\""}, + {version = ">=1.21.0", markers = "python_version >= \"3.10\" and python_version < \"3.11\""}, {version = ">=1.23.2", markers = "python_version >= \"3.11\""}, ] python-dateutil = ">=2.8.2" @@ -2634,6 +2635,7 @@ description = "parse() is the opposite of format()" optional = true python-versions = "*" files = [ + {file = "parse-1.19.0-py2.py3-none-any.whl", hash = "sha256:6ce007645384a91150cb7cd7c8a9db2559e273c2e2542b508cd1e342508c2601"}, {file = "parse-1.19.0.tar.gz", hash = "sha256:9ff82852bcb65d139813e2a5197627a94966245c897796760a3a2a8eb66f020b"}, ] @@ -3478,6 +3480,7 @@ files = [ {file = "scikit_image-0.21.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8ef5d8d1099317b7b315b530348cbfa68ab8ce32459de3c074d204166951025c"}, {file = "scikit_image-0.21.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:78b1e96c59cab640ca5c5b22c501524cfaf34cbe0cb51ba73bd9a9ede3fb6e1d"}, {file = "scikit_image-0.21.0-cp39-cp39-win_amd64.whl", hash = "sha256:9cffcddd2a5594c0a06de2ae3e1e25d662745a26f94fda31520593669677c010"}, + {file = "scikit_image-0.21.0.tar.gz", hash = "sha256:b33e823c54e6f11873ea390ee49ef832b82b9f70752c8759efd09d5a4e3d87f0"}, ] [package.dependencies] diff --git a/tests/impls/gen_classifier_conf_sal/test_occlusion_scoring.py b/tests/impls/gen_classifier_conf_sal/test_occlusion_scoring.py index 228388bd..6d03b9f3 100644 --- a/tests/impls/gen_classifier_conf_sal/test_occlusion_scoring.py +++ b/tests/impls/gen_classifier_conf_sal/test_occlusion_scoring.py @@ -58,11 +58,11 @@ def test_1class_scores(self) -> None: Test basic scoring with a single class for broadcasting sanity check. """ impl = OcclusionScoring() - np.random.seed(2) + rng = np.random.default_rng(2) # Three Perturbation masks of height and width 10px for 1 class - image_confs_1_class_ = np.random.rand(1) - pertb_confs_1_class_ = np.random.rand(3, 1) - mask_confs_1_class_ = np.random.randint(low=0, high=2, size=(3, 10, 10), dtype='int') + image_confs_1_class_ = rng.standard_normal((1)) + pertb_confs_1_class_ = rng.standard_normal((3, 1)) + mask_confs_1_class_ = rng.integers(low=0, high=2, size=(3, 10, 10), dtype='int') sal = impl.generate(image_confs_1_class_, pertb_confs_1_class_, mask_confs_1_class_) assert sal.shape == (1, 10, 10) @@ -85,10 +85,10 @@ def test_20class_scores(self) -> None: Test scoring for 20 classes. """ impl = OcclusionScoring() - np.random.seed(2) + rng = np.random.default_rng(2) # Three Perturbation masks of height and width 10px for 20 classes - image_confs_1_class_ = np.random.rand(20) - pertb_confs_1_class_ = np.random.rand(3, 20) - mask_confs_1_class_ = np.random.randint(low=0, high=2, size=(3, 10, 10), dtype='int') + image_confs_1_class_ = rng.standard_normal((20)) + pertb_confs_1_class_ = rng.standard_normal((3, 20)) + mask_confs_1_class_ = rng.integers(low=0, high=2, size=(3, 10, 10), dtype='int') sal = impl.generate(image_confs_1_class_, pertb_confs_1_class_, mask_confs_1_class_) assert sal.shape == (20, 10, 10) diff --git a/tests/impls/gen_descriptor_sim_sal/test_similarity_scoring.py b/tests/impls/gen_descriptor_sim_sal/test_similarity_scoring.py index 2288b095..9c5e707e 100644 --- a/tests/impls/gen_descriptor_sim_sal/test_similarity_scoring.py +++ b/tests/impls/gen_descriptor_sim_sal/test_similarity_scoring.py @@ -57,11 +57,11 @@ def test_generate_mismatch_ref_descriptors(self) -> None: Test that we appropriately error when the input reference descriptors are not the same dimensionality. """ - np.random.seed(0) - test_ref_descr = np.random.rand(16) - test_query_descrs = np.random.rand(5, 15) # Different than above - test_pert_descrs = np.random.rand(32, 16) - test_masks = np.random.rand(32, 16, 16) + rng = np.random.default_rng(seed=0) + test_ref_descr = rng.standard_normal(16) + test_query_descrs = rng.standard_normal((5, 15)) # Different than above + test_pert_descrs = rng.standard_normal((32, 16)) + test_masks = rng.standard_normal((32, 16, 16)) impl = SimilarityScoring() @@ -76,11 +76,11 @@ def test_generate_mismatched_perturbed(self) -> None: Test that we appropriately error when the input perturbation descriptors and mask arrays are not equal in first-dimension length. """ - np.random.seed(0) - test_ref_descr = np.random.rand(16) - test_query_descrs = np.random.rand(1, 16) - test_pert_descrs = np.random.rand(32, 16) - test_masks = np.random.rand(30, 16, 16) # Different than above + rng = np.random.default_rng(seed=0) + test_ref_descr = rng.standard_normal(16) + test_query_descrs = rng.standard_normal((1, 16)) + test_pert_descrs = rng.standard_normal((32, 16)) + test_masks = rng.standard_normal((30, 16, 16)) # Different than above impl = SimilarityScoring() @@ -96,11 +96,11 @@ def test_1_featurevec(self) -> None: Test basic scoring with a single feature for broadcasting sanity check. """ impl = SimilarityScoring() - np.random.seed(2) - ref_feat = np.random.rand(2048) - query_feats = np.random.rand(2, 2048) - pertb_feats = np.random.rand(3, 2048) - pertb_mask = np.random.randint(low=0, high=2, size=(3, 10, 10), dtype='int') + rng = np.random.default_rng(2) + ref_feat = rng.standard_normal(2048) + query_feats = rng.standard_normal((2, 2048)) + pertb_feats = rng.standard_normal((3, 2048)) + pertb_mask = rng.integers(low=0, high=2, size=(3, 10, 10), dtype='int') sal = impl.generate(ref_feat, query_feats, pertb_feats, pertb_mask) assert sal.shape == (2, 10, 10) @@ -123,10 +123,10 @@ def test_512_featdim(self) -> None: Test scoring for features of 512 dims. """ impl = SimilarityScoring() - np.random.seed(2) - ref_feat = np.random.rand(512) - query_feats = np.random.rand(1, 512) - pertb_feats = np.random.rand(15, 512) - pertb_mask = np.random.randint(low=0, high=2, size=(15, 10, 10), dtype='int') + rng = np.random.default_rng(2) + ref_feat = rng.standard_normal(512) + query_feats = rng.standard_normal((1, 512)) + pertb_feats = rng.standard_normal((15, 512)) + pertb_mask = rng.integers(low=0, high=2, size=(15, 10, 10), dtype='int') sal = impl.generate(ref_feat, query_feats, pertb_feats, pertb_mask) assert sal.shape == (1, 10, 10) diff --git a/tests/impls/gen_detector_prop_sal/test_drise_scoring.py b/tests/impls/gen_detector_prop_sal/test_drise_scoring.py index f9028b45..171c858a 100644 --- a/tests/impls/gen_detector_prop_sal/test_drise_scoring.py +++ b/tests/impls/gen_detector_prop_sal/test_drise_scoring.py @@ -23,9 +23,10 @@ def test_shape_sanity(self) -> None: Test basic scoring with a single feature for broadcasting sanity check. """ impl = DRISEScoring() - ref_dets = np.random.rand(2, 7) - pert_dets = np.random.rand(10, 3, 7) - pert_mask = np.random.randint( + rng = np.random.default_rng(seed=0) + ref_dets = rng.standard_normal((2, 7)) + pert_dets = rng.standard_normal((10, 3, 7)) + pert_mask = rng.integers( low=0, high=2, size=(10, 15, 25), dtype='int') sal = impl.generate(ref_dets, pert_dets, pert_mask) assert sal.shape == (2, 15, 25) @@ -52,9 +53,10 @@ def test_mask_detections_size_mismatch(self) -> None: Test size mismatch between perturbed detections and masks. """ impl = DRISEScoring() - ref_dets = np.random.rand(2, 7) - pert_dets = np.random.rand(9, 3, 7) # ONE LESS than pert mask mat. - pert_mask = np.random.randint( + rng = np.random.default_rng(seed=0) + ref_dets = rng.standard_normal((2, 7)) + pert_dets = rng.standard_normal((9, 3, 7)) # ONE LESS than pert mask mat. + pert_mask = rng.integers( low=0, high=2, size=(10, 15, 25), dtype='int') with pytest.raises( ValueError, @@ -68,9 +70,10 @@ def test_detections_classes_mismatch(self) -> None: Test mismatch in number of classes between perturbed and reference detections. """ impl = DRISEScoring() - ref_dets = np.random.rand(2, 8) # ONE MORE than pert dets mat. - pert_dets = np.random.rand(10, 3, 7) - pert_mask = np.random.randint( + rng = np.random.default_rng(seed=0) + ref_dets = rng.standard_normal((2, 8)) # ONE MORE than pert dets mat. + pert_dets = rng.standard_normal((10, 3, 7)) + pert_mask = rng.integers( low=0, high=2, size=(10, 15, 25), dtype='int') with pytest.raises( ValueError, diff --git a/tests/impls/gen_image_classifier_blackbox_sal/test_rise.py b/tests/impls/gen_image_classifier_blackbox_sal/test_rise.py index 9d766f0a..00167a06 100644 --- a/tests/impls/gen_image_classifier_blackbox_sal/test_rise.py +++ b/tests/impls/gen_image_classifier_blackbox_sal/test_rise.py @@ -35,10 +35,10 @@ def test_configuration(self) -> None: assert isinstance(inst_g, RISEScoring) assert inst_p.n == 444 assert inst_p.s == 33 - assert inst_p.p1 == .22 + assert np.allclose(inst_p.p1, .22) assert inst_p.seed == 42 assert inst_p.threads == 99 - assert inst_g.p1 == .22 + assert np.allclose(inst_g.p1, .22) assert inst_i.get_config()['debiased'] is True def test_configuration_not_debiased(self) -> None: @@ -58,8 +58,8 @@ def test_configuration_not_debiased(self) -> None: inst_g = inst_i._po._generator assert isinstance(inst_p, RISEGrid) assert isinstance(inst_g, RISEScoring) - assert inst_p.p1 == .22 - assert inst_g.p1 == 0.0 + assert np.allclose(inst_p.p1, .22) + assert np.allclose(inst_g.p1, 0.0) assert inst_i.get_config()['debiased'] is False def test_generation_rgb(self) -> None: diff --git a/tests/impls/gen_object_detector_blackbox_sal/test_drise.py b/tests/impls/gen_object_detector_blackbox_sal/test_drise.py index f69e68b2..48cbc071 100644 --- a/tests/impls/gen_object_detector_blackbox_sal/test_drise.py +++ b/tests/impls/gen_object_detector_blackbox_sal/test_drise.py @@ -37,7 +37,7 @@ def test_configuration(self) -> None: assert isinstance(inst_g, DRISEScoring) assert inst_p.n == 123 assert inst_p.s == 8 - assert inst_p.p1 == 0.73 + assert np.allclose(inst_p.p1, .73) assert inst_p.seed == 98 assert inst_p.threads == 5 assert inst_g.get_config() == {} diff --git a/tests/impls/gen_object_detector_blackbox_sal/test_random_grid.py b/tests/impls/gen_object_detector_blackbox_sal/test_random_grid.py index 23f56c2d..f0f3294e 100644 --- a/tests/impls/gen_object_detector_blackbox_sal/test_random_grid.py +++ b/tests/impls/gen_object_detector_blackbox_sal/test_random_grid.py @@ -38,7 +38,7 @@ def test_configuration(self) -> None: assert isinstance(inst_g, DRISEScoring) assert inst_p.n == 55 assert inst_p.s == (15, 8) - assert inst_p.p1 == 0.34 + assert np.allclose(inst_p.p1, 0.34) assert inst_p.seed == 7 assert inst_p.threads == 2 assert inst_g.get_config() == {} diff --git a/tests/impls/perturb_image/test_random_grid.py b/tests/impls/perturb_image/test_random_grid.py index f9f34244..49c509a5 100644 --- a/tests/impls/perturb_image/test_random_grid.py +++ b/tests/impls/perturb_image/test_random_grid.py @@ -21,7 +21,7 @@ def test_init_valued(self) -> None: assert impl.n == test_n assert impl.s == test_s - assert impl.p1 == test_p1 + assert np.allclose(impl.p1, test_p1) assert impl.seed == test_seed assert impl.threads == test_threads @@ -41,7 +41,7 @@ def test_standard_config(self) -> None: for inst in configuration_test_helper(impl): assert inst.n == test_n assert inst.s == test_s - assert inst.p1 == test_p1 + assert np.allclose(impl.p1, test_p1) assert inst.seed == test_seed assert inst.threads == test_threads @@ -53,7 +53,8 @@ def test_if_random(self) -> None: impl1 = RandomGrid(n=3, s=(5, 4), p1=0.5) impl2 = RandomGrid(n=3, s=(5, 4), p1=0.5) - img = np.random.randint(0, 255, size=(20, 20), dtype=np.uint8) + rng = np.random.default_rng(seed=0) + img = rng.integers(0, 255, size=(20, 20), dtype=np.uint8) masks1 = impl1(img) masks2 = impl2(img) @@ -68,7 +69,8 @@ def test_seed(self) -> None: impl1 = RandomGrid(n=3, s=(2, 1), p1=0.6, seed=5) impl2 = RandomGrid(n=3, s=(2, 1), p1=0.6, seed=5) - img = np.random.randint(0, 255, size=(10, 10), dtype=np.uint8) + rng = np.random.default_rng(seed=0) + img = rng.integers(0, 255, size=(10, 10), dtype=np.uint8) masks1 = impl1(img) masks2 = impl2(img) @@ -80,8 +82,8 @@ def test_call_idempotency(self) -> None: Test that perturbation generation is idempotent, at least when seeded and single-threaded. """ - - img = np.random.randint(0, 255, size=(10, 10), dtype=np.uint8) + rng = np.random.default_rng(seed=0) + img = rng.integers(0, 255, size=(10, 10), dtype=np.uint8) impl = RandomGrid(n=2, s=(1, 2), p1=0.4, seed=1, threads=0) @@ -95,8 +97,8 @@ def test_perturb_1_channel(self) -> None: Test mask generation on a one-channel image of a known size. Number of channels should not affect output masks. """ - - img = np.random.randint(0, 255, size=(4, 6), dtype=np.uint8) + rng = np.random.default_rng(seed=0) + img = rng.integers(0, 255, size=(4, 6), dtype=np.uint8) impl = RandomGrid(n=2, s=(2, 2), p1=0.5, seed=123, threads=0) @@ -112,8 +114,8 @@ def test_perturb_3_channel(self) -> None: Test mask generation on a three-channel image of a known size. Number of channels should not affect output masks. """ - - img = np.random.randint(0, 255, size=(4, 6, 3), dtype=np.uint8) + rng = np.random.default_rng(seed=0) + img = rng.integers(0, 255, size=(4, 6, 3), dtype=np.uint8) impl = RandomGrid(n=2, s=(2, 2), p1=0.5, seed=123, threads=0) @@ -129,11 +131,11 @@ def test_multiple_image_size(self) -> None: Test that a single implementation can be used for images of varying sizes. """ - impl = RandomGrid(n=5, s=(3, 4), p1=0.2, seed=42, threads=0) - img_small = np.random.randint(0, 255, size=(5, 7), dtype=np.uint8) - img_large = np.random.randint(0, 255, size=(55, 77), dtype=np.uint8) + rng = np.random.default_rng(seed=0) + img_small = rng.integers(0, 255, size=(5, 7), dtype=np.uint8) + img_large = rng.integers(0, 255, size=(55, 77), dtype=np.uint8) masks_small = impl(img_small) masks_large = impl(img_large) @@ -148,8 +150,8 @@ def test_threading(self) -> None: """ Test that using threading does not affect results. """ - - img = np.random.randint(0, 255, size=(4, 6), dtype=np.uint8) + rng = np.random.default_rng(seed=0) + img = rng.integers(0, 255, size=(4, 6), dtype=np.uint8) impl = RandomGrid(n=2, s=(2, 2), p1=0.5, seed=123, threads=1) diff --git a/tests/utils/test_detection.py b/tests/utils/test_detection.py index 5eadbb82..c9e49913 100644 --- a/tests/utils/test_detection.py +++ b/tests/utils/test_detection.py @@ -11,8 +11,9 @@ def test_default_objectness_fill(self) -> None: Test that when objectness scores are not provided the expected default is filled into the appropriate column. """ - test_bbox_mat = np.random.randint(0, 255, (16, 4)) - test_class_mat = np.random.rand(16, 8) + rng = np.random.default_rng(seed=0) + test_bbox_mat = rng.integers(0, 255, (16, 4)) + test_class_mat = rng.standard_normal((16, 8)) combined_mat = format_detection(test_bbox_mat, test_class_mat) # The objectness scores should be in the index 4 column and they should # all be 1's. @@ -22,8 +23,9 @@ def test_explicit_objectness(self) -> None: """ Test that input objectness vector is represented in the output. """ - test_bbox_mat = np.random.randint(0, 255, (16, 4)) - test_class_mat = np.random.rand(16, 8) + rng = np.random.default_rng(seed=0) + test_bbox_mat = rng.integers(0, 255, (16, 4)) + test_class_mat = rng.standard_normal((16, 8)) test_obj_v = np.tile([.1, .2, .3, .4], 4) combined_mat = format_detection(test_bbox_mat, test_class_mat, test_obj_v) # that the output objectness column is equivalent to the input. @@ -34,8 +36,9 @@ def test_explicit_objectness_2d(self) -> None: Test that an input objectness vector that is of shape `[nDets x 1]` is treated fine. """ - test_bbox_mat = np.random.randint(0, 255, (16, 4)) - test_class_mat = np.random.rand(16, 8) + rng = np.random.default_rng(seed=0) + test_bbox_mat = rng.integers(0, 255, (16, 4)) + test_class_mat = rng.standard_normal((16, 8)) test_obj_v = np.tile([.1, .2, .3, .4], 4).reshape(16, 1) combined_mat = format_detection(test_bbox_mat, test_class_mat, test_obj_v) # Test that the output objectness column is equivalent to the input. @@ -73,8 +76,9 @@ def test_bbox_class_shape_mismatch(self) -> None: mismatch in input bbox and classification matrices. """ # 16 boxes, 14 classifications. - test_bbox_mat = np.random.randint(0, 255, (16, 4)) - test_class_mat = np.random.rand(14, 8) + rng = np.random.default_rng(seed=0) + test_bbox_mat = rng.integers(0, 255, (16, 4)) + test_class_mat = rng.standard_normal((14, 8)) with pytest.raises( ValueError, match=r"along dimension 0, the array at index 0 has size 16 and " @@ -87,9 +91,10 @@ def test_objectness_shape_mismatch(self) -> None: Test that an error is raised when the explicitly input objectness array is not a matching size. """ - test_bbox_mat = np.random.randint(0, 255, (16, 4)) - test_class_mat = np.random.rand(16, 8) - test_objnes_v = np.random.rand(11) + rng = np.random.default_rng(seed=0) + test_bbox_mat = rng.integers(0, 255, (16, 4)) + test_class_mat = rng.standard_normal((16, 8)) + test_objnes_v = rng.standard_normal((11)) with pytest.raises( ValueError, match=r"along dimension 0, the array at index 0 has size 16 and " @@ -111,8 +116,9 @@ def test_against_type_upcasting( Test that the output is not of a type that is larger than is input. E.g. when input is float32, output is *not* float64, but still float32. """ - bbox_mat = np.random.randn(100, 4).astype(bbox_type) - classification_mat = np.random.randn(100, 10).astype(clf_type) + rng = np.random.default_rng(seed=0) + bbox_mat = rng.standard_normal((100, 4)).astype(bbox_type) + classification_mat = rng.standard_normal((100, 10)).astype(clf_type) output = format_detection(bbox_mat, classification_mat) assert output.dtype == expected_type @@ -132,8 +138,9 @@ def test_against_type_upcasting_obj( Same as ``test_against_type_upcasting`` but including the objectness input vector instead of it being ``None``. """ - bbox_mat = np.random.randn(100, 4).astype(bbox_type) - classification_mat = np.random.randn(100, 10).astype(clf_type) - objectness = np.random.randn(100).astype(obj_type) + rng = np.random.default_rng(seed=0) + bbox_mat = rng.standard_normal((100, 4)).astype(bbox_type) + classification_mat = rng.standard_normal((100, 10)).astype(clf_type) + objectness = rng.standard_normal(100).astype(obj_type) output = format_detection(bbox_mat, classification_mat, objectness) assert output.dtype == expected_type diff --git a/tests/utils/test_masking.py b/tests/utils/test_masking.py index ebb86859..bc125b84 100644 --- a/tests/utils/test_masking.py +++ b/tests/utils/test_masking.py @@ -467,7 +467,8 @@ def test_against_type_upcasting( based on the input data types. E.g. when input is float32, output is *not* float64, but still float32. """ - scalar_vec = np.random.randn(100, 10).astype(scalar_type) + rng = np.random.default_rng(seed=0) + scalar_vec = rng.standard_normal((100, 10)).astype(scalar_type) masks = np.ones((100, 224, 224)).astype(mask_type) output = weight_regions_by_scalar(scalar_vec, masks, inv_masks, normalize) assert output.dtype == expected_output_type diff --git a/xaitk_saliency/utils/masking.py b/xaitk_saliency/utils/masking.py index e22c9583..764cd7a5 100644 --- a/xaitk_saliency/utils/masking.py +++ b/xaitk_saliency/utils/masking.py @@ -227,7 +227,8 @@ def benchmark_occlude_image( `[H x W [x C]]`. """ img_mat = np.ones((*img_shape, img_channels), dtype=np.uint8) - masks = (np.random.rand(num_masks, *img_shape[:2]) < 0.5) + rng = np.random.default_rng(seed=0) + masks = (rng.standard_normal((num_masks, *img_shape[:2])) < 0.5) fill_1c: int = 0 fill_mc = [0] * img_channels perf_counter = time.perf_counter