Skip to content

Commit

Permalink
Merge branch '154-disparite-initiale-variable-dichotomie' into 'release'
Browse files Browse the repository at this point in the history
Resolve "Disparité initiale variable : prise en compte de la disparité variable dans la Dichotomie"

See merge request 3d/PandoraBox/pandora2d!140
  • Loading branch information
lecontm committed Aug 14, 2024
2 parents a0f3ebc + 4b5dae0 commit a058763
Show file tree
Hide file tree
Showing 6 changed files with 447 additions and 68 deletions.
44 changes: 35 additions & 9 deletions pandora2d/refinement/dichotomy.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ def margins(self):

return self.filter.margins

def refinement_method(
def refinement_method( # pylint: disable=too-many-locals
self, cost_volumes: xr.Dataset, disp_map: xr.Dataset, img_left: xr.Dataset, img_right: xr.Dataset
) -> Tuple[np.ndarray, np.ndarray, np.ndarray]:
"""
Expand Down Expand Up @@ -137,9 +137,13 @@ def refinement_method(
invalid_disparity_map_mask = invalid_row_disparity_map_mask | invalid_col_disparity_map_mask
cost_values[invalid_disparity_map_mask] = np.nan

# row_disparity_source and col_disparity_sources contain the user disparity range
row_disparity_source = cost_volumes.attrs["row_disparity_source"]
col_disparity_source = cost_volumes.attrs["col_disparity_source"]
# Get disparities grid
# Column's min, max disparities
disp_min_col = img_left["col_disparity"].sel(band_disp="min").data
disp_max_col = img_left["col_disparity"].sel(band_disp="max").data
# Row's min, max disparities
disp_min_row = img_left["row_disparity"].sel(band_disp="min").data
disp_max_row = img_left["row_disparity"].sel(band_disp="max").data

# start iterations after subpixel precision: `subpixel.bit_length() - 1` found which power of 2 subpixel is,
# and we add 1 to start at next iteration
Expand All @@ -159,17 +163,39 @@ def refinement_method(
# See usage of np.nditer:
# https://numpy.org/doc/stable/reference/arrays.nditer.html#modifying-array-values
with np.nditer(
[cost_values, row_map, col_map],
op_flags=[["readwrite"], ["readwrite"], ["readwrite"]],
[cost_values, row_map, col_map, disp_min_row, disp_max_row, disp_min_col, disp_max_col],
op_flags=[
["readwrite"],
["readwrite"],
["readwrite"],
["readonly"],
["readonly"],
["readonly"],
["readonly"],
],
) as iterators:
for cost_surface, (cost_value, disp_row_init, disp_col_init) in zip(cost_surfaces, iterators):
for cost_surface, (
cost_value,
disp_row_init,
disp_col_init,
d_row_min,
d_row_max,
d_col_min,
d_col_max,
) in zip(cost_surfaces, iterators):

# Invalid value
if np.isnan(cost_value):
continue

# If the best candidate found at the disparity step is at the edge of the disparity range
# If the best candidate found at the disparity step is at the edge of the row disparity range
# we do no enter the dichotomy loop
if disp_row_init in (d_row_min, d_row_max):
continue

# If the best candidate found at the disparity step is at the edge of the col disparity range
# we do no enter the dichotomy loop
if (disp_row_init in row_disparity_source) or (disp_col_init in col_disparity_source):
if disp_col_init in (d_col_min, d_col_max):
continue

# pos_disp_col_init corresponds to the position in the cost surface
Expand Down
18 changes: 8 additions & 10 deletions tests/functional_tests/matching_cost/test_disparity_margins.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,14 +50,14 @@ def create_datasets(self):

add_disparity_grid(left, {"init": 2, "range": 1}, {"init": 0, "range": 2})

left.attrs = {
"no_data_img": -9999,
"valid_pixels": 0,
"no_data_mask": 1,
"crs": None,
"col_disparity_source": {"init": 2, "range": 1},
"row_disparity_source": {"init": 0, "range": 2},
}
left.attrs.update(
{
"no_data_img": -9999,
"valid_pixels": 0,
"no_data_mask": 1,
"crs": None,
}
)

data = np.full((10, 10), 1)
right = xr.Dataset(
Expand All @@ -70,8 +70,6 @@ def create_datasets(self):
"valid_pixels": 0,
"no_data_mask": 1,
"crs": None,
"col_disparity_source": {"init": 2, "range": 1},
"row_disparity_source": {"init": 0, "range": 2},
}

return left, right
Expand Down
18 changes: 9 additions & 9 deletions tests/functional_tests/matching_cost/test_subpix.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,15 +48,15 @@ def create_datasets(self, data_left, data_right):

add_disparity_grid(left, {"init": 1, "range": 2}, {"init": 1, "range": 2})

left.attrs = {
"no_data_img": -9999,
"valid_pixels": 0,
"no_data_mask": 1,
"crs": None,
"col_disparity_source": {"init": 1, "range": 2},
"row_disparity_source": {"init": 1, "range": 2},
"transform": None,
}
left.attrs.update(
{
"no_data_img": -9999,
"valid_pixels": 0,
"no_data_mask": 1,
"crs": None,
"transform": None,
}
)

right = xr.Dataset(
{"im": (["row", "col"], data_right)},
Expand Down
133 changes: 112 additions & 21 deletions tests/functional_tests/refinement/dichotomy/test_dichotomy_pipeline.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,28 +18,29 @@
"""
Test the refinement.dichotomy pipeline.
"""

import copy
import pytest

import numpy as np


import pandora2d
from pandora2d.state_machine import Pandora2DMachine
from pandora2d.check_configuration import check_conf
from pandora2d.img_tools import create_datasets_from_inputs, get_roi_processing

# Make pylint happy with fixtures:
# pylint: disable=redefined-outer-name

@pytest.mark.parametrize("method", ["bicubic", "sinc"])
@pytest.mark.parametrize("subpix", [1, 2, 4])
@pytest.mark.parametrize("iterations", [1, 2])
def test_dichotomy_execution(left_img_path, right_img_path, method, subpix, iterations):

@pytest.fixture()
def make_cfg_for_dichotomy(
left_img_path, right_img_path, method, subpix, iterations, roi, col_disparity, row_disparity
):
"""
Description : Test that execution of Pandora2d with a dichotomy refinement does not fail.
Data :
* Left_img : cones/monoband/left.png
* Right_img : cones/monoband/right.png
Requirement :
* EX_REF_BCO_00
* EX_REF_SINC_00
Creates user configuration to test dichotomy loop
"""
pandora2d_machine = pandora2d.state_machine.Pandora2DMachine()

user_cfg = {
"input": {
"left": {
Expand All @@ -50,10 +51,10 @@ def test_dichotomy_execution(left_img_path, right_img_path, method, subpix, iter
"img": str(right_img_path),
"nodata": "NaN",
},
"col_disparity": {"init": 0, "range": 3},
"row_disparity": {"init": 0, "range": 3},
"col_disparity": col_disparity,
"row_disparity": row_disparity,
},
"ROI": {"col": {"first": 100, "last": 120}, "row": {"first": 100, "last": 120}},
"ROI": roi,
"pipeline": {
"matching_cost": {
"matching_cost_method": "zncc",
Expand All @@ -71,18 +72,108 @@ def test_dichotomy_execution(left_img_path, right_img_path, method, subpix, iter
},
},
}
cfg = pandora2d.check_configuration.check_conf(user_cfg, pandora2d_machine)

return user_cfg


@pytest.mark.parametrize("method", ["bicubic", "sinc"])
@pytest.mark.parametrize("subpix", [1, 2, 4])
@pytest.mark.parametrize("iterations", [1, 2])
@pytest.mark.parametrize("roi", [{"col": {"first": 100, "last": 120}, "row": {"first": 100, "last": 120}}])
@pytest.mark.parametrize("col_disparity", [{"init": 0, "range": 1}])
@pytest.mark.parametrize("row_disparity", [{"init": 0, "range": 3}])
def test_dichotomy_execution(make_cfg_for_dichotomy):
"""
Description : Test that execution of Pandora2d with a dichotomy refinement does not fail.
Data :
* Left_img : cones/monoband/left.png
* Right_img : cones/monoband/right.png
Requirement :
* EX_REF_BCO_00
* EX_REF_SINC_00
"""
pandora2d_machine = Pandora2DMachine()

cfg = check_conf(make_cfg_for_dichotomy, pandora2d_machine)

cfg["ROI"]["margins"] = pandora2d_machine.margins.global_margins.astuple()
roi = pandora2d.img_tools.get_roi_processing(
cfg["ROI"], cfg["input"]["col_disparity"], cfg["input"]["row_disparity"]
)
roi = get_roi_processing(cfg["ROI"], cfg["input"]["col_disparity"], cfg["input"]["row_disparity"])

image_datasets = pandora2d.img_tools.create_datasets_from_inputs(input_config=cfg["input"], roi=roi)
image_datasets = create_datasets_from_inputs(input_config=cfg["input"], roi=roi)

dataset_disp_maps, _ = pandora2d.run(pandora2d_machine, image_datasets.left, image_datasets.right, cfg)

# Checking that resulting disparity maps are not full of nans
with np.testing.assert_raises(AssertionError):
assert np.all(np.isnan(dataset_disp_maps.row_map.data))
assert np.all(np.isnan(dataset_disp_maps.col_map.data))


@pytest.mark.parametrize("method", ["bicubic"])
@pytest.mark.parametrize("subpix", [1])
@pytest.mark.parametrize("iterations", [1, 2])
# This ROI has been chosen because its corresponding disparity maps
# contain extrema disparity range values and subpixel values after refinement.
@pytest.mark.parametrize("roi", [{"col": {"first": 30, "last": 40}, "row": {"first": 160, "last": 170}}])
# We use small disparity intervals to obtain extrema of disparity ranges in the disparity maps.
# Once the variable disparity grids have been introduced into pandora2d,
# this type of disparity will also need to be tested here.
@pytest.mark.parametrize("col_disparity", [{"init": -1, "range": 1}])
@pytest.mark.parametrize("row_disparity", [{"init": 0, "range": 1}])
def test_extrema_disparities_not_processed(make_cfg_for_dichotomy):
"""
Description : Test that execution of Pandora2d with a dichotomy refinement does not
take into account points for which best cost value is found at the edge of the disparity range.
Data :
* Left_img : cones/monoband/left.png
* Right_img : cones/monoband/right.png
"""
pandora2d_machine = pandora2d.state_machine.Pandora2DMachine()

cfg = check_conf(make_cfg_for_dichotomy, pandora2d_machine)

cfg["ROI"]["margins"] = pandora2d_machine.margins.global_margins.astuple()
roi = get_roi_processing(cfg["ROI"], cfg["input"]["col_disparity"], cfg["input"]["row_disparity"])

image_datasets = create_datasets_from_inputs(input_config=cfg["input"], roi=roi)

pandora2d_machine = Pandora2DMachine()

# Prepare Pandora2D machine
pandora2d_machine.run_prepare(image_datasets.left, image_datasets.right, cfg)
# Run matching cost step
pandora2d_machine.run("matching_cost", cfg)
# Run disparity step
pandora2d_machine.run("disparity", cfg)
# Make a copy of disparity maps before refinement step
copy_disp_maps = copy.deepcopy(pandora2d_machine.dataset_disp_maps)
# Run refinement step
pandora2d_machine.run("refinement", cfg)

# Get points for which best cost value is at the edge of the row disparity range
mask_min_row = np.nonzero(copy_disp_maps["row_map"].data == image_datasets.left.row_disparity[0, :, :])
mask_max_row = np.nonzero(copy_disp_maps["row_map"].data == image_datasets.left.row_disparity[1, :, :])

# Get points for which best cost value is at the edge of the column disparity range
mask_min_col = np.nonzero(copy_disp_maps["col_map"].data == image_datasets.left.col_disparity[0, :, :])
mask_max_col = np.nonzero(copy_disp_maps["col_map"].data == image_datasets.left.col_disparity[1, :, :])

# Checking that best row disparity is unchanged for points having best cost value at the edge of row disparity range
assert np.all(
pandora2d_machine.dataset_disp_maps["row_map"].data[mask_min_row[0], mask_min_row[1]]
== image_datasets.left.row_disparity.data[0, mask_min_row[0], mask_min_row[1]]
)
assert np.all(
pandora2d_machine.dataset_disp_maps["row_map"].data[mask_max_row[0], mask_max_row[1]]
== image_datasets.left.row_disparity.data[1, mask_max_row[0], mask_max_row[1]]
)

# Checking that best col disparity is unchanged for points having best cost value at the edge of col disparity range
assert np.all(
pandora2d_machine.dataset_disp_maps["col_map"].data[mask_min_col[0], mask_min_col[1]]
== image_datasets.left.col_disparity.data[0, mask_min_col[0], mask_min_col[1]]
)
assert np.all(
pandora2d_machine.dataset_disp_maps["col_map"].data[mask_max_col[0], mask_max_col[1]]
== image_datasets.left.col_disparity.data[1, mask_max_col[0], mask_max_col[1]]
)
Loading

0 comments on commit a058763

Please sign in to comment.