Skip to content

Commit

Permalink
Merge branch '97-remove-roi-margins-disparity-maps' into 'release'
Browse files Browse the repository at this point in the history
Resolve "Retrait des marges de traitement des cartes de disparités finales dans le cas d'une ROI"

See merge request 3d/PandoraBox/pandora2d!80
  • Loading branch information
lecontm committed Mar 22, 2024
2 parents 9cd520f + 0b6eb6a commit 1feb56f
Show file tree
Hide file tree
Showing 8 changed files with 305 additions and 6 deletions.
4 changes: 2 additions & 2 deletions docs/source/userguide/as_an_api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -72,15 +72,15 @@ Pandora2D provides a full python API which can be used to compute disparity maps
)

# trigger all the steps of the machine at ones
dataset = pandora2d.run(
dataset, completed_cfg = pandora2d.run(
pandora2d_machine,
image_datasets.left,
image_datasets.right,
pipeline_cfg
)

# save dataset
common.save_dataset(dataset, path_output)
common.save_dataset(dataset, completed_cfg, path_output)


Pandora2D's data
Expand Down
4 changes: 4 additions & 0 deletions docs/source/userguide/faq.rst
Original file line number Diff line number Diff line change
Expand Up @@ -85,3 +85,7 @@ It is possible to work on only one section of the image with an ROI. For this, t
img_left = create_dataset_from_inputs(input_config=input_config["left"], roi=roi)
img_right = create_dataset_from_inputs(input_config=input_config["right"], roi=roi)
.. note::
When the usage_step_roi_config.ipynb notebook is run, disparity maps are displayed.
Margins can be present on these disparity maps, which is why they may be larger than the ROI given by the user.
To remove these margins and display only the user ROI, you can use the `pandora2d.img_tools.remove_roi_margins()` method.
8 changes: 8 additions & 0 deletions notebooks/usage_step_roi_config.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -764,6 +764,14 @@
"#### Visualize output disparity maps"
]
},
{
"cell_type": "markdown",
"id": "69e62604",
"metadata": {},
"source": [
"Processing margins are included in the disparity map view below. These can be removed by calling the method pandora2d.img_tools.remove_roi_margins()"
]
},
{
"cell_type": "markdown",
"id": "4ea5cba4-b57b-415f-97fb-21387913d32d",
Expand Down
2 changes: 1 addition & 1 deletion pandora2d/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,6 @@ def main(cfg_path: str, path_output: str, verbose: bool) -> None:

# save dataset if not empty
if bool(dataset_disp_maps.data_vars):
common.save_dataset(dataset_disp_maps, path_output)
common.save_dataset(dataset_disp_maps, completed_cfg, path_output)
# save config
save_config(path_output, completed_cfg)
10 changes: 9 additions & 1 deletion pandora2d/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,13 +35,15 @@
from xarray import Coordinate as Coordinates

import os
from typing import Dict
import xarray as xr
import numpy as np

from pandora.common import mkdir_p, write_data_array
from pandora2d.img_tools import remove_roi_margins


def save_dataset(dataset: xr.Dataset, output: str) -> None:
def save_dataset(dataset: xr.Dataset, cfg: Dict, output: str) -> None:
"""
Save results in the output directory
Expand All @@ -50,11 +52,17 @@ def save_dataset(dataset: xr.Dataset, output: str) -> None:
- lines : the disparity map for the lines 2D DataArray (row, col)
- columns : the disparity map for the columns 2D DataArray (row, col)
:type dataset: xr.Dataset
:param cfg: user configuration
:type cfg: Dict
:param output: output directory
:type output: string
:return: None
"""

# remove ROI margins to save only user ROI in tif files
if "ROI" in cfg:
dataset = remove_roi_margins(dataset, cfg)

# create output dir
mkdir_p(output)

Expand Down
60 changes: 60 additions & 0 deletions pandora2d/img_tools.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
from collections.abc import Sequence
from typing import List, Dict, NamedTuple, Any

from math import floor
import xarray as xr
import numpy as np
from scipy.ndimage import shift
Expand Down Expand Up @@ -261,3 +262,62 @@ def get_roi_processing(roi: dict, col_disparity: List[int], row_disparity: List[
new_roi["margins"][3] = max(abs(row_disparity[1]), roi["margins"][3])

return new_roi


def remove_roi_margins(dataset: xr.Dataset, cfg: Dict):
"""
Remove ROI margins before saving output dataset
:param dataset: dataset containing disparity row and col maps
:type dataset: xr.Dataset
:param cfg: output configuration of the pandora2d machine
:type cfg: Dict
"""

step = cfg["pipeline"]["matching_cost"]["step"]

row = dataset.row.data
col = dataset.col.data

# Initialized indexes to get right rows and columns
(left, up, right, down) = (0, 0, len(col), len(row))

# Example with col = [8,10,12,14,16], step_col=2, row = [0,4,8,12], step_row=4
# ROI={
# {"col": "first": 10, "last": 14},
# {"row": "first": 0, "last": 10} }
# {"margins": (3,3,3,3)}

# According to ROI, we want new_col=[10,12,14]=col[1:-1]
# with 1=floor((cfg["ROI"]["col"]["first"] - col[0]) / step_col)=left
# and -1=floor((cfg["ROI"]["col"]["last"] - col[-1]) / step_col)=right

# According to ROI, we want new_row=[0,4,8]=row[0:-1]
# with 0=initialized up
# and -1=floor((cfg["ROI"]["row"]["last"] - row[-1]) / step[0])=down

# Get the correct indexes to get the right columns based on the user ROI
if col[0] < cfg["ROI"]["col"]["first"]:
left = floor((cfg["ROI"]["col"]["first"] - col[0]) / step[1])
if col[-1] > cfg["ROI"]["col"]["last"]:
right = floor((cfg["ROI"]["col"]["last"] - col[-1]) / step[1])

# Get the correct indexes to get the right rows based on the user ROI
if row[0] < cfg["ROI"]["row"]["first"]:
up = floor((cfg["ROI"]["row"]["first"] - row[0]) / step[0])
if row[-1] > cfg["ROI"]["row"]["last"]:
down = floor((cfg["ROI"]["row"]["last"] - row[-1]) / step[0])

# Create a new dataset with right rows and columns.
data_variables = {
"row_map": (("row", "col"), dataset["row_map"].data[up:down, left:right]),
"col_map": (("row", "col"), dataset["col_map"].data[up:down, left:right]),
}

coords = {"row": row[up:down], "col": col[left:right]}

new_dataset = xr.Dataset(data_variables, coords)

new_dataset.attrs = dataset.attrs

return new_dataset
4 changes: 2 additions & 2 deletions tests/unit_tests/test_common.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,12 +62,12 @@ def create_test_dataset(self):

return dataset

def test_save_dataset(self, create_test_dataset):
def test_save_dataset(self, create_test_dataset, correct_input_cfg):
"""
Function for testing the dataset_save function
"""

common.save_dataset(create_test_dataset, "./tests/res_test/")
common.save_dataset(create_test_dataset, correct_input_cfg, "./tests/res_test/")
assert os.path.exists("./tests/res_test/")

assert os.path.exists("./tests/res_test/columns_disparity.tif")
Expand Down
219 changes: 219 additions & 0 deletions tests/unit_tests/test_img_tools/test_remove_roi_margins.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,219 @@
# Copyright (c) 2024 Centre National d'Etudes Spatiales (CNES).
#
# This file is part of PANDORA2D
#
# https://github.com/CNES/Pandora2D
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

"""
Test remove_roi_margins.
"""

import pytest
import numpy as np
import xarray as xr

from pandora2d.img_tools import remove_roi_margins


def create_dataset(row, col):
"""
Create dataset to test remove_roi_margins method
"""

data_variables = {
"row_map": (("row", "col"), np.full((len(row), len(col)), 1)),
"col_map": (("row", "col"), np.full((len(row), len(col)), 1)),
}

coords = {"row": row, "col": col}

dataset = xr.Dataset(data_variables, coords)

return dataset


def make_cfg(roi, step):
"""
Create user configuration to test remove_roi_margins method
"""
return {
"ROI": roi,
"pipeline": {
"matching_cost": {"matching_cost_method": "zncc", "window_size": 7, "step": step},
},
}


class TestRemoveRoiMargins:
"""
Test remove_roi_margins function
"""

# pylint:disable=too-few-public-methods

@pytest.mark.parametrize(
["roi", "step", "row", "col", "row_gt", "col_gt"],
[
pytest.param(
{
"col": {"first": 10, "last": 100},
"row": {"first": 10, "last": 100},
"margins": (3, 3, 3, 3),
},
[1, 1],
np.arange(7, 104),
np.arange(7, 104),
np.arange(10, 101),
np.arange(10, 101),
id="Centered ROI, step_row=1 and step_col=1 ",
),
pytest.param(
{
"col": {"first": 10, "last": 100},
"row": {"first": 0, "last": 100},
"margins": (2, 2, 2, 2),
},
[1, 1],
np.arange(0, 103),
np.arange(8, 103),
np.arange(0, 101),
np.arange(10, 101),
id="ROI overlap at the top, step_row=1 and step_col=1 ",
),
pytest.param(
{
"col": {"first": 10, "last": 100},
"row": {"first": 100, "last": 201},
"margins": (3, 3, 3, 3),
},
[1, 1],
np.arange(97, 200),
np.arange(7, 104),
np.arange(100, 200),
np.arange(10, 101),
id="ROI overlap at the bottom, step_row=1 and step_col=1 ",
),
pytest.param(
{
"col": {"first": 0, "last": 100},
"row": {"first": 10, "last": 100},
"margins": (3, 3, 3, 3),
},
[1, 1],
np.arange(7, 104),
np.arange(0, 104),
np.arange(10, 101),
np.arange(0, 101),
id="ROI overlap on the left, step_row=1 and step_col=1 ",
),
pytest.param(
{
"col": {"first": 100, "last": 202},
"row": {"first": 10, "last": 100},
"margins": (3, 3, 3, 3),
},
[1, 1],
np.arange(7, 104),
np.arange(97, 200),
np.arange(10, 101),
np.arange(100, 200),
id="ROI overlap on the right, step_row=1 and step_col=1 ",
),
pytest.param(
{
"col": {"first": 110, "last": 200},
"row": {"first": 110, "last": 200},
"margins": (3, 3, 3, 3),
},
[2, 3],
np.arange(108, 204, 2), # step=2 --> we start at 108 even if margin=3
np.arange(107, 204, 3),
np.arange(110, 201, 2),
np.arange(110, 201, 3),
id="Centered ROI, step_row=2 and step_col=3 ",
),
pytest.param(
{
"col": {"first": 0, "last": 200},
"row": {"first": 110, "last": 200},
"margins": (3, 3, 3, 3),
},
[2, 3],
np.arange(108, 204, 2), # step=2 --> we start at 108 even if margin=3
np.arange(0, 204, 3),
np.arange(110, 201, 2),
np.arange(0, 201, 3),
id="ROI overlap on the left, step_row=2 and step_col=3 ",
),
pytest.param(
{
"col": {"first": 100, "last": 203},
"row": {"first": 10, "last": 100},
"margins": (3, 3, 3, 3),
},
[3, 2],
np.arange(7, 104, 3),
np.arange(98, 200, 2), # step=2 --> we start at 98 even if margin=3
np.arange(10, 101, 3),
np.arange(100, 200, 2),
id="ROI overlap on the right, step_row=3 and step_col=2 ",
),
pytest.param(
{
"col": {"first": 10, "last": 100},
"row": {"first": 0, "last": 100},
"margins": (3, 3, 3, 3),
},
[4, 2],
np.arange(0, 104, 4),
np.arange(8, 104, 2), # step=2 --> we start at 8 even if margin=3
np.arange(0, 101, 4),
np.arange(10, 101, 2),
id="ROI overlap at the top, step_row=4 and step_col=2",
),
pytest.param(
{
"col": {"first": 10, "last": 100},
"row": {"first": 100, "last": 203},
"margins": (3, 3, 3, 3),
},
[5, 3],
np.arange(100, 200, 5), # step=5 --> we start at 100 even if margin=3
np.arange(7, 104, 3),
np.arange(100, 200, 5),
np.arange(10, 101, 3),
id="ROI overlap at the bottom, step_row=5 and step_col=3",
),
],
)
def test_remove_roi_margins(self, roi, step, row, col, row_gt, col_gt):
"""
Test remove_roi_margins method
"""

# Create user configuration with given roi and step
cfg = make_cfg(roi, step)

# Create dataset input for remove_roi_margins
# row & col are supposed to be the correct coordinates according to the roi and the step
dataset = create_dataset(row, col)

# Create ground truth dataset
dataset_gt = create_dataset(row_gt, col_gt)

dataset_test = remove_roi_margins(dataset, cfg)

xr.testing.assert_identical(dataset_test, dataset_gt)

0 comments on commit 1feb56f

Please sign in to comment.