From 999a7f6367b9ac5f638645a4bbc9d1c8066c26e7 Mon Sep 17 00:00:00 2001 From: duzelis Date: Tue, 10 Sep 2024 17:49:46 +0200 Subject: [PATCH 1/2] feat: add get_criteria_dataarray method --- pandora2d/criteria.py | 50 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/pandora2d/criteria.py b/pandora2d/criteria.py index ee92be9..3eae676 100644 --- a/pandora2d/criteria.py +++ b/pandora2d/criteria.py @@ -56,6 +56,56 @@ def allocate_criteria_dataarray( ) +def get_criteria_dataarray(left_image: xr.Dataset, right_image: xr.Dataset, cv: xr.Dataset) -> xr.DataArray: + """ + This method fill the criteria dataarray with the different criteria obtained thanks to + the methods implemented in this file + """ + + # Allocate criteria dataarray + criteria_dataarray = allocate_criteria_dataarray(cv) + + if "msk" in left_image.data_vars: + + # Raise criteria PANDORA2D_MSK_PIXEL_LEFT_NODATA + # for points having no data in left mask, for each disparity + mask_left_no_data(left_image, cv.attrs["window_size"], criteria_dataarray) + # Raise criteria PANDORA2D_MSK_PIXEL_INVALIDITY_MASK_LEFT + # for points having invalid in left mask, for each disparity + mask_left_invalid(left_image, criteria_dataarray) + + if "msk" in right_image.data_vars: + + # Raise criteria PANDORA2D_MSK_PIXEL_RIGHT_NODATA + # for points having no data in right mask according to disparity value + mask_right_no_data(right_image, cv.attrs["window_size"], criteria_dataarray) + # Raise criteria PANDORA2D_MSK_PIXEL_INVALIDITY_MASK_RIGHT + # for points having invalid in right mask according to disparity value + mask_right_invalid(right_image, criteria_dataarray) + + # Raise criteria PANDORA2D_MSK_PIXEL_RIGHT_DISPARITY_OUTSIDE + # for points for which window is outside right image according to disparity value + mask_disparity_outside_right_image(cv.attrs["offset_row_col"], criteria_dataarray) + + # Raise criteria PANDORA2D_MSK_PIXEL_LEFT_BORDER + # on the border according to offset value, for each disparity + mask_border(cv.attrs["offset_row_col"], criteria_dataarray) + + # Get columns disparity grid + d_min_col_grid = left_image["col_disparity"].sel(band_disp="min").data.copy() + d_max_col_grid = left_image["col_disparity"].sel(band_disp="max").data.copy() + + # Get rows disparity grid + d_min_row_grid = left_image["row_disparity"].sel(band_disp="min").data.copy() + d_max_row_grid = left_image["row_disparity"].sel(band_disp="max").data.copy() + + # Put PANDORA2D_MSK_PIXEL_DISPARITY_UNPROCESSED + # on points for which corresponding disparity is not processed + set_unprocessed_disp(criteria_dataarray, d_min_col_grid, d_max_col_grid, d_min_row_grid, d_max_row_grid) + + return criteria_dataarray + + def set_unprocessed_disp( criteria_dataarray: xr.DataArray, min_grid_col: NDArray[np.floating], From afe09f034c469c93132f3c4760cb434fb6b70cbf Mon Sep 17 00:00:00 2001 From: duzelis Date: Tue, 10 Sep 2024 17:50:44 +0200 Subject: [PATCH 2/2] test: add tests for get_criteria_dataarray method --- tests/unit_tests/test_criteria.py | 336 ++++++++++++++++++++++++++++++ 1 file changed, 336 insertions(+) diff --git a/tests/unit_tests/test_criteria.py b/tests/unit_tests/test_criteria.py index 4ce192c..1892154 100644 --- a/tests/unit_tests/test_criteria.py +++ b/tests/unit_tests/test_criteria.py @@ -21,6 +21,7 @@ """ # pylint: disable=too-many-lines # pylint: disable=redefined-outer-name +import copy import pytest import numpy as np import xarray as xr @@ -1001,3 +1002,338 @@ def test_combination(self, image, criteria_dataarray, disp_col, disp_row): criteria_dataarray.sel(row=2, col=3, disp_row=disp_row, disp_col=disp_col).data == Criteria.PANDORA2D_MSK_PIXEL_RIGHT_DISPARITY_OUTSIDE | Criteria.PANDORA2D_MSK_PIXEL_INVALIDITY_MASK_RIGHT ) + + +@pytest.mark.parametrize("img_size", [(4, 5)]) +class TestGetCriteriaDataarray: + """Test get_criteria_dataarray function.""" + + @pytest.fixture() + def image_variable_disp(self, image, img_size): + """Make image with variable disparity grids""" + + # Make so when we change image_variable_disp mask it + # does not change image mask + img = copy.copy(image) + row, col = img_size + + nb_col_set = int(col / 2) + nb_row_set = int(row / 2) + + # Get variable col disparities + + # Minimal col disparity grid is equal to: + # [[-3, -3, -5, -5, -5] + # [-3, -3, -5, -5, -5] + # [-3, -3, -5, -5, -5] + # [-3, -3, -5, -5, -5]] + img["col_disparity"].sel(band_disp="min")[:, :nb_col_set] = -3 + + # Maximal col disparity grid is equal to: + # [[ 3, 3, 1, 1, 1] + # [ 3, 3, 1, 1, 1] + # [ 3, 3, 1, 1, 1] + # [ 3, 3, 1, 1, 1]] + img["col_disparity"].sel(band_disp="max")[:, nb_col_set:] = 1 + + # Get variable row disparities + + # Minimal row disparity grid is equal to: + # [[ 0, 0, 0, 0, 0] + # [ 0, 0, 0, 0, 0] + # [-1, -1, -1, -1, -1] + # [-1, -1, -1, -1, -1]] + img["row_disparity"].sel(band_disp="min")[:nb_row_set, :] = 0 + + # Maximal row disparity grid is equal to: + # [[ 3, 3, 3, 3, 3] + # [ 3, 3, 3, 3, 3] + # [ 2, 2, 2, 2, 2] + # [ 2, 2, 2, 2, 2]] + + img["row_disparity"].sel(band_disp="max")[nb_row_set:, :] = 2 + + return img + + @pytest.mark.usefixtures("mask_image") + @pytest.mark.parametrize( + ["left_msk", "msk", "disp_col", "disp_row", "window_size", "expected_criteria"], + [ + # pylint: disable=line-too-long + pytest.param( + np.array( # left msk + [ + [0, 0, 0, 0, 0], + [0, 0, 0, 0, 0], + [0, 0, 0, 0, 0], + [0, 0, 0, 0, 0], + ] + ), + np.array( # right msk + [ + [0, 0, 0, 0, 0], + [0, 0, 0, 0, 0], + [0, 0, 0, 0, 0], + [0, 0, 0, 0, 0], + ] + ), + 0, # disp_col + 0, # disp_row + 1, # window_size + np.array( + [ + # fmt: off + [Criteria.VALID, Criteria.VALID, Criteria.VALID, Criteria.VALID, Criteria.VALID], + [Criteria.VALID, Criteria.VALID, Criteria.VALID, Criteria.VALID, Criteria.VALID], + [Criteria.VALID, Criteria.VALID, Criteria.VALID, Criteria.VALID, Criteria.VALID], + [Criteria.VALID, Criteria.VALID, Criteria.VALID, Criteria.VALID, Criteria.VALID], + # fmt: on + ] + ), + id="Everything is valid", + ), + pytest.param( + np.array( # left msk + [ + [0, 0, 0, 0, 0], + [0, 0, 0, 0, 0], + [0, 0, 0, 0, 0], + [0, 0, 0, 0, 0], + ] + ), + np.array( # right msk + [ + [0, 0, 0, 0, 0], + [0, 0, 0, 0, 0], + [0, 0, 0, 0, 0], + [0, 0, 0, 0, 0], + ] + ), + 2, # disp_col + -1, # disp_row + 1, # window_size + np.array( + [ + # fmt: off + [Criteria.PANDORA2D_MSK_PIXEL_DISPARITY_UNPROCESSED, Criteria.PANDORA2D_MSK_PIXEL_DISPARITY_UNPROCESSED, Criteria.PANDORA2D_MSK_PIXEL_DISPARITY_UNPROCESSED, Criteria.PANDORA2D_MSK_PIXEL_DISPARITY_UNPROCESSED, Criteria.PANDORA2D_MSK_PIXEL_DISPARITY_UNPROCESSED], + [Criteria.PANDORA2D_MSK_PIXEL_DISPARITY_UNPROCESSED, Criteria.PANDORA2D_MSK_PIXEL_DISPARITY_UNPROCESSED, Criteria.PANDORA2D_MSK_PIXEL_DISPARITY_UNPROCESSED, Criteria.PANDORA2D_MSK_PIXEL_DISPARITY_UNPROCESSED, Criteria.PANDORA2D_MSK_PIXEL_DISPARITY_UNPROCESSED], + [Criteria.VALID , Criteria.VALID , Criteria.PANDORA2D_MSK_PIXEL_DISPARITY_UNPROCESSED, Criteria.PANDORA2D_MSK_PIXEL_DISPARITY_UNPROCESSED, Criteria.PANDORA2D_MSK_PIXEL_DISPARITY_UNPROCESSED], + [Criteria.VALID , Criteria.VALID , Criteria.PANDORA2D_MSK_PIXEL_DISPARITY_UNPROCESSED, Criteria.PANDORA2D_MSK_PIXEL_DISPARITY_UNPROCESSED, Criteria.PANDORA2D_MSK_PIXEL_DISPARITY_UNPROCESSED], + # fmt: on + ] + ), + id="Criteria.PANDORA2D_MSK_PIXEL_DISPARITY_UNPROCESSED overcome other criteria", + ), + pytest.param( + np.array( # left msk + [ + [0, 0, 0, 0, 0], + [0, 1, 0, 0, 0], + [0, 0, 0, 2, 0], + [0, 0, 0, 0, 0], + ] + ), + np.array( # right msk + [ + [0, 0, 0, 0, 0], + [0, 0, 0, 0, 0], + [0, 2, 0, 1, 0], + [0, 0, 0, 0, 0], + ] + ), + -1, # disp_col + 1, # disp_row + 1, # window_size + np.array( + [ + # fmt: off + [Criteria.PANDORA2D_MSK_PIXEL_RIGHT_DISPARITY_OUTSIDE , Criteria.VALID, Criteria.VALID, Criteria.VALID, Criteria.VALID], + [Criteria.PANDORA2D_MSK_PIXEL_RIGHT_DISPARITY_OUTSIDE , Criteria.PANDORA2D_MSK_PIXEL_LEFT_NODATA, Criteria.PANDORA2D_MSK_PIXEL_INVALIDITY_MASK_RIGHT, Criteria.VALID, Criteria.PANDORA2D_MSK_PIXEL_RIGHT_NODATA], + [Criteria.PANDORA2D_MSK_PIXEL_RIGHT_DISPARITY_OUTSIDE , Criteria.VALID, Criteria.VALID, Criteria.PANDORA2D_MSK_PIXEL_INVALIDITY_MASK_LEFT, Criteria.VALID], + [Criteria.PANDORA2D_MSK_PIXEL_RIGHT_DISPARITY_OUTSIDE , Criteria.PANDORA2D_MSK_PIXEL_RIGHT_DISPARITY_OUTSIDE , Criteria.PANDORA2D_MSK_PIXEL_RIGHT_DISPARITY_OUTSIDE , Criteria.PANDORA2D_MSK_PIXEL_RIGHT_DISPARITY_OUTSIDE , Criteria.PANDORA2D_MSK_PIXEL_RIGHT_DISPARITY_OUTSIDE ], + # fmt: on + ] + ), + id="Mix of criteria with window_size=1", + ), + pytest.param( + np.array( # left msk + [ + [0, 0, 0, 0, 0], + [0, 1, 0, 0, 0], + [0, 0, 0, 2, 0], + [0, 0, 0, 0, 0], + ] + ), + np.array( # right msk + [ + [0, 0, 0, 0, 0], + [0, 0, 0, 0, 0], + [0, 3, 0, 1, 0], + [0, 0, 0, 0, 0], + ] + ), + -1, # disp_col + 1, # disp_row + 3, # window_size + np.array( + [ + # fmt: off + [Criteria.PANDORA2D_MSK_PIXEL_LEFT_BORDER , Criteria.PANDORA2D_MSK_PIXEL_LEFT_BORDER, Criteria.PANDORA2D_MSK_PIXEL_LEFT_BORDER, Criteria.PANDORA2D_MSK_PIXEL_LEFT_BORDER, Criteria.PANDORA2D_MSK_PIXEL_LEFT_BORDER], + [Criteria.PANDORA2D_MSK_PIXEL_LEFT_BORDER , Criteria.PANDORA2D_MSK_PIXEL_LEFT_NODATA | Criteria.PANDORA2D_MSK_PIXEL_RIGHT_DISPARITY_OUTSIDE, Criteria.PANDORA2D_MSK_PIXEL_LEFT_NODATA | Criteria.PANDORA2D_MSK_PIXEL_INVALIDITY_MASK_RIGHT, Criteria.PANDORA2D_MSK_PIXEL_RIGHT_NODATA, Criteria.PANDORA2D_MSK_PIXEL_LEFT_BORDER], + [Criteria.PANDORA2D_MSK_PIXEL_LEFT_BORDER , Criteria.PANDORA2D_MSK_PIXEL_LEFT_NODATA | Criteria.PANDORA2D_MSK_PIXEL_RIGHT_DISPARITY_OUTSIDE, Criteria.PANDORA2D_MSK_PIXEL_LEFT_NODATA | Criteria.PANDORA2D_MSK_PIXEL_RIGHT_DISPARITY_OUTSIDE, Criteria.PANDORA2D_MSK_PIXEL_RIGHT_NODATA | Criteria.PANDORA2D_MSK_PIXEL_RIGHT_DISPARITY_OUTSIDE | Criteria.PANDORA2D_MSK_PIXEL_INVALIDITY_MASK_LEFT, Criteria.PANDORA2D_MSK_PIXEL_LEFT_BORDER], + [Criteria.PANDORA2D_MSK_PIXEL_LEFT_BORDER , Criteria.PANDORA2D_MSK_PIXEL_LEFT_BORDER, Criteria.PANDORA2D_MSK_PIXEL_LEFT_BORDER , Criteria.PANDORA2D_MSK_PIXEL_LEFT_BORDER , Criteria.PANDORA2D_MSK_PIXEL_LEFT_BORDER], + # fmt: on + ] + ), + id="Mix of criteria with window_size=3", + ), + pytest.param( + np.array( # left msk + [ + [0, 0, 0, 0, 0], + [0, 0, 1, 0, 0], + [0, 0, 2, 0, 0], + [0, 0, 0, 0, 0], + ] + ), + np.array( # right msk + [ + [0, 0, 0, 0, 0], + [0, 0, 1, 0, 0], + [0, 0, 2, 0, 0], + [0, 0, 0, 0, 0], + ] + ), + 0, # disp_col + 1, # disp_row + 1, # window_size + np.array( + [ + # fmt: off + [Criteria.VALID, Criteria.VALID, Criteria.PANDORA2D_MSK_PIXEL_RIGHT_NODATA, Criteria.VALID, Criteria.VALID], + [Criteria.VALID, Criteria.VALID, Criteria.PANDORA2D_MSK_PIXEL_LEFT_NODATA | Criteria.PANDORA2D_MSK_PIXEL_INVALIDITY_MASK_RIGHT, Criteria.VALID, Criteria.VALID], + [Criteria.VALID, Criteria.VALID, Criteria.PANDORA2D_MSK_PIXEL_INVALIDITY_MASK_LEFT, Criteria.VALID, Criteria.VALID], + [Criteria.PANDORA2D_MSK_PIXEL_RIGHT_DISPARITY_OUTSIDE, Criteria.PANDORA2D_MSK_PIXEL_RIGHT_DISPARITY_OUTSIDE, Criteria.PANDORA2D_MSK_PIXEL_RIGHT_DISPARITY_OUTSIDE, Criteria.PANDORA2D_MSK_PIXEL_RIGHT_DISPARITY_OUTSIDE, Criteria.PANDORA2D_MSK_PIXEL_RIGHT_DISPARITY_OUTSIDE], + # fmt: on + ] + ), + id="Centered invalid and no data in msk with window_size=1", + ), + pytest.param( + np.array( # left msk + [ + [0, 0, 0, 0, 0], + [0, 0, 0, 0, 0], + [0, 0, 0, 0, 0], + [0, 0, 0, 0, 0], + ] + ), + np.array( # right msk + [ + [0, 0, 0, 0, 0], + [0, 0, 0, 0, 0], + [0, 0, 0, 0, 1], + [0, 0, 0, 0, 0], + ] + ), + 1, # disp_col + 1, # disp_row + 3, # window_size + np.array( + [ + # fmt: off + [Criteria.PANDORA2D_MSK_PIXEL_LEFT_BORDER, Criteria.PANDORA2D_MSK_PIXEL_LEFT_BORDER, Criteria.PANDORA2D_MSK_PIXEL_LEFT_BORDER, Criteria.PANDORA2D_MSK_PIXEL_LEFT_BORDER, Criteria.PANDORA2D_MSK_PIXEL_LEFT_BORDER], + [Criteria.PANDORA2D_MSK_PIXEL_LEFT_BORDER, Criteria.VALID, Criteria.PANDORA2D_MSK_PIXEL_RIGHT_NODATA, Criteria.PANDORA2D_MSK_PIXEL_RIGHT_NODATA | Criteria.PANDORA2D_MSK_PIXEL_RIGHT_DISPARITY_OUTSIDE, Criteria.PANDORA2D_MSK_PIXEL_LEFT_BORDER], + [Criteria.PANDORA2D_MSK_PIXEL_LEFT_BORDER, Criteria.PANDORA2D_MSK_PIXEL_RIGHT_DISPARITY_OUTSIDE, Criteria.PANDORA2D_MSK_PIXEL_RIGHT_NODATA | Criteria.PANDORA2D_MSK_PIXEL_RIGHT_DISPARITY_OUTSIDE, Criteria.PANDORA2D_MSK_PIXEL_RIGHT_NODATA | Criteria.PANDORA2D_MSK_PIXEL_RIGHT_DISPARITY_OUTSIDE, Criteria.PANDORA2D_MSK_PIXEL_LEFT_BORDER], + [Criteria.PANDORA2D_MSK_PIXEL_LEFT_BORDER, Criteria.PANDORA2D_MSK_PIXEL_LEFT_BORDER, Criteria.PANDORA2D_MSK_PIXEL_LEFT_BORDER, Criteria.PANDORA2D_MSK_PIXEL_LEFT_BORDER, Criteria.PANDORA2D_MSK_PIXEL_LEFT_BORDER], + # fmt: on + ] + ), + id="Right no data on the border and window_size=3", + ), + pytest.param( + np.array( # left msk + [ + [0, 0, 0, 0, 0], + [0, 1, 0, 0, 0], + [0, 0, 0, 2, 0], + [0, 0, 0, 0, 0], + ] + ), + np.array( # right msk + [ + [0, 0, 0, 0, 0], + [0, 0, 0, 0, 0], + [0, 2, 0, 1, 0], + [0, 0, 0, 0, 0], + ] + ), + -1, # disp_col + 1, # disp_row + 5, # window_size + np.array( + [ + # fmt: off + [Criteria.PANDORA2D_MSK_PIXEL_LEFT_BORDER, Criteria.PANDORA2D_MSK_PIXEL_LEFT_BORDER, Criteria.PANDORA2D_MSK_PIXEL_LEFT_BORDER, Criteria.PANDORA2D_MSK_PIXEL_LEFT_BORDER, Criteria.PANDORA2D_MSK_PIXEL_LEFT_BORDER], + [Criteria.PANDORA2D_MSK_PIXEL_LEFT_BORDER, Criteria.PANDORA2D_MSK_PIXEL_LEFT_BORDER, Criteria.PANDORA2D_MSK_PIXEL_LEFT_BORDER, Criteria.PANDORA2D_MSK_PIXEL_LEFT_BORDER, Criteria.PANDORA2D_MSK_PIXEL_LEFT_BORDER], + [Criteria.PANDORA2D_MSK_PIXEL_LEFT_BORDER, Criteria.PANDORA2D_MSK_PIXEL_LEFT_BORDER, Criteria.PANDORA2D_MSK_PIXEL_LEFT_BORDER, Criteria.PANDORA2D_MSK_PIXEL_LEFT_BORDER, Criteria.PANDORA2D_MSK_PIXEL_LEFT_BORDER], + [Criteria.PANDORA2D_MSK_PIXEL_LEFT_BORDER, Criteria.PANDORA2D_MSK_PIXEL_LEFT_BORDER, Criteria.PANDORA2D_MSK_PIXEL_LEFT_BORDER, Criteria.PANDORA2D_MSK_PIXEL_LEFT_BORDER, Criteria.PANDORA2D_MSK_PIXEL_LEFT_BORDER], + # fmt: on + ] + ), + id="Window_size=5, only Criteria.PANDORA2D_MSK_PIXEL_LEFT_BORDER is raised", + ), + pytest.param( + np.array( # left msk + [ + [0, 0, 0, 0, 0], + [0, 0, 0, 0, 0], + [0, 0, 0, 0, 0], + [0, 0, 0, 0, 0], + ] + ), + np.array( # right msk + [ + [0, 0, 0, 0, 0], + [0, 0, 0, 0, 0], + [0, 0, 0, 0, 0], + [0, 0, 0, 0, 0], + ] + ), + -5, # disp_col + 0, # disp_row + 1, # window_size + np.array( + [ + # fmt: off + [Criteria.PANDORA2D_MSK_PIXEL_DISPARITY_UNPROCESSED, Criteria.PANDORA2D_MSK_PIXEL_DISPARITY_UNPROCESSED, Criteria.PANDORA2D_MSK_PIXEL_RIGHT_DISPARITY_OUTSIDE, Criteria.PANDORA2D_MSK_PIXEL_RIGHT_DISPARITY_OUTSIDE, Criteria.PANDORA2D_MSK_PIXEL_RIGHT_DISPARITY_OUTSIDE], + [Criteria.PANDORA2D_MSK_PIXEL_DISPARITY_UNPROCESSED, Criteria.PANDORA2D_MSK_PIXEL_DISPARITY_UNPROCESSED, Criteria.PANDORA2D_MSK_PIXEL_RIGHT_DISPARITY_OUTSIDE, Criteria.PANDORA2D_MSK_PIXEL_RIGHT_DISPARITY_OUTSIDE, Criteria.PANDORA2D_MSK_PIXEL_RIGHT_DISPARITY_OUTSIDE], + [Criteria.PANDORA2D_MSK_PIXEL_DISPARITY_UNPROCESSED, Criteria.PANDORA2D_MSK_PIXEL_DISPARITY_UNPROCESSED, Criteria.PANDORA2D_MSK_PIXEL_RIGHT_DISPARITY_OUTSIDE, Criteria.PANDORA2D_MSK_PIXEL_RIGHT_DISPARITY_OUTSIDE, Criteria.PANDORA2D_MSK_PIXEL_RIGHT_DISPARITY_OUTSIDE], + [Criteria.PANDORA2D_MSK_PIXEL_DISPARITY_UNPROCESSED, Criteria.PANDORA2D_MSK_PIXEL_DISPARITY_UNPROCESSED, Criteria.PANDORA2D_MSK_PIXEL_RIGHT_DISPARITY_OUTSIDE, Criteria.PANDORA2D_MSK_PIXEL_RIGHT_DISPARITY_OUTSIDE, Criteria.PANDORA2D_MSK_PIXEL_RIGHT_DISPARITY_OUTSIDE], + # fmt: on + ] + ), + id="Column disparity out of the image or unprocessed for all points", + ), + # pylint: enable=line-too-long + ], + ) + def test_get_criteria_dataarray( + self, image_variable_disp, image, left_msk, cost_volumes, disp_col, disp_row, expected_criteria + ): + """ + Test get_criteria_dataarray method with + different disparities, window sizes and masks + """ + + image_variable_disp["msk"].data = left_msk + + criteria_dataarray = criteria.get_criteria_dataarray( + left_image=image_variable_disp, right_image=image, cv=cost_volumes + ) + + np.testing.assert_array_equal( + criteria_dataarray.sel(disp_row=disp_row, disp_col=disp_col), + expected_criteria, + )