From c77d9ee1e5b982013b0fbe03a27da699ec20b353 Mon Sep 17 00:00:00 2001 From: Steux Yoann Date: Wed, 2 Oct 2024 14:25:11 +0000 Subject: [PATCH] =?UTF-8?q?Chaine=20low=20cost:=20sans=20validation=20croi?= =?UTF-8?q?s=C3=A9e?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dense_matching/census_mccnn_sgm.py | 6 ++ .../loaders/config_census_sgm.json | 4 -- .../dense_matching/loaders/config_mccnn.json | 4 -- .../dense_matching/loaders/pandora_loader.py | 15 +++++ docs/source/usage.rst | 6 ++ .../dense_matching/test_pandora_loader.py | 63 +++++++++++++++++++ tests/test_end2end.py | 26 ++++++++ 7 files changed, 116 insertions(+), 8 deletions(-) diff --git a/cars/applications/dense_matching/census_mccnn_sgm.py b/cars/applications/dense_matching/census_mccnn_sgm.py index a274fbc1..947af58b 100644 --- a/cars/applications/dense_matching/census_mccnn_sgm.py +++ b/cars/applications/dense_matching/census_mccnn_sgm.py @@ -108,6 +108,7 @@ def __init__(self, conf=None): self.disp_range_propagation_filter_size = self.used_config[ "disp_range_propagation_filter_size" ] + self.use_cross_validation = self.used_config["use_cross_validation"] # Saving files self.save_intermediate_data = self.used_config["save_intermediate_data"] @@ -171,6 +172,9 @@ def check_conf(self, conf): overloaded_conf["perf_ambiguity_threshold"] = conf.get( "perf_ambiguity_threshold", 0.6 ) + overloaded_conf["use_cross_validation"] = conf.get( + "use_cross_validation", False + ) # Margins computation parameters overloaded_conf["use_global_disp_range"] = conf.get( "use_global_disp_range", False @@ -213,6 +217,7 @@ def check_conf(self, conf): perf_eta_max_ambiguity=overloaded_conf["perf_eta_max_ambiguity"], perf_eta_max_risk=overloaded_conf["perf_eta_max_risk"], perf_eta_step=overloaded_conf["perf_eta_step"], + use_cross_validation=overloaded_conf["use_cross_validation"], ) overloaded_conf["loader"] = loader overloaded_conf["loader_conf"] = loader_conf @@ -237,6 +242,7 @@ def check_conf(self, conf): "perf_eta_max_risk": float, "perf_eta_step": float, "perf_ambiguity_threshold": float, + "use_cross_validation": bool, "use_global_disp_range": bool, "local_disp_grid_step": int, "disp_range_propagation_filter_size": And( diff --git a/cars/applications/dense_matching/loaders/config_census_sgm.json b/cars/applications/dense_matching/loaders/config_census_sgm.json index f9ac772a..e5bf070b 100644 --- a/cars/applications/dense_matching/loaders/config_census_sgm.json +++ b/cars/applications/dense_matching/loaders/config_census_sgm.json @@ -26,10 +26,6 @@ "filter": { "filter_method": "median", "filter_size": 3 - }, - "validation": { - "validation_method": "cross_checking_accurate", - "cross_checking_threshold": 1.0 } } } \ No newline at end of file diff --git a/cars/applications/dense_matching/loaders/config_mccnn.json b/cars/applications/dense_matching/loaders/config_mccnn.json index 14a8605a..6ab394a5 100644 --- a/cars/applications/dense_matching/loaders/config_mccnn.json +++ b/cars/applications/dense_matching/loaders/config_mccnn.json @@ -23,10 +23,6 @@ "filter" : { "filter_method": "median", "filter_size": 3 - }, - "validation" : { - "validation_method": "cross_checking_accurate", - "cross_checking_threshold": 1 } } } \ No newline at end of file diff --git a/cars/applications/dense_matching/loaders/pandora_loader.py b/cars/applications/dense_matching/loaders/pandora_loader.py index 2e3442b9..09442bf6 100644 --- a/cars/applications/dense_matching/loaders/pandora_loader.py +++ b/cars/applications/dense_matching/loaders/pandora_loader.py @@ -58,6 +58,7 @@ def __init__( # noqa: C901 perf_eta_max_ambiguity=0.99, perf_eta_max_risk=0.25, perf_eta_step=0.04, + use_cross_validation=False, ): """ Init function of PandoraLoader @@ -70,8 +71,11 @@ def __init__( # noqa: C901 :type conf: dict :param method_name: name of method to use :param performance_map_conf: true if generate performance maps + :param use_cross_validation: true to add crossvalidation """ + if method_name is None: + method_name = "census_sgm" self.pandora_config = None @@ -141,6 +145,13 @@ def __init__( # noqa: C901 "confidence_method": "interval_bounds", } } + # Cross validation + cross_validation_conf = { + "validation": { + "validation_method": "cross_checking_accurate", + "cross_checking_threshold": 1.0, + } + } confidences = {} if generate_performance_map: @@ -175,6 +186,10 @@ def __init__( # noqa: C901 conf["pipeline"], confidences ) + # update with cross validation + if use_cross_validation and "validation" not in conf["pipeline"]: + conf["pipeline"].update(cross_validation_conf) + if generate_confidence_intervals: # To ensure the consistency between the disparity map # and the intervals, the median filter for intervals diff --git a/docs/source/usage.rst b/docs/source/usage.rst index bceba942..a110a767 100644 --- a/docs/source/usage.rst +++ b/docs/source/usage.rst @@ -1057,6 +1057,12 @@ The structure follows this organisation: - should be > 0 - 300 - No + * - use_cross_validation + - Add cross validation step + - bool + - + - false + - No See `Pandora documentation `_ for more information. diff --git a/tests/applications/dense_matching/test_pandora_loader.py b/tests/applications/dense_matching/test_pandora_loader.py index 4037a776..b5a0e9b2 100644 --- a/tests/applications/dense_matching/test_pandora_loader.py +++ b/tests/applications/dense_matching/test_pandora_loader.py @@ -25,6 +25,8 @@ # Standard imports # Third party imports +import copy + import pytest # CARS imports @@ -95,6 +97,67 @@ def test_configure_pandora_config(): assert corr_config["pipeline"]["optimization"]["penalty"]["P2"] == 24 +@pytest.mark.unit_tests +def test_configure_cross_validation(): + """ + Test configure pandora correlator cross validation + """ + + pandora_config = { + "input": {"nodata_left": "NaN", "nodata_right": "NaN"}, + "pipeline": { + "right_disp_map": {"method": "accurate"}, + "matching_cost": { + "matching_cost_method": "census", + "window_size": 5, + "subpix": 1, + }, + "optimization": { + "optimization_method": "sgm", + "penalty": { + "P1": 8, + "P2": 24, + "p2_method": "constant", + "penalty_method": "sgm_penalty", + }, + "overcounting": False, + "min_cost_paths": False, + }, + "disparity": { + "disparity_method": "wta", + "invalid_disparity": "NaN", + }, + "refinement": {"refinement_method": "vfit"}, + "filter": {"filter_method": "median", "filter_size": 3}, + }, + } + + # test 1, validation as input already + conf_with_validation = copy.deepcopy(pandora_config) + conf_with_validation["pipeline"].update( + {"validation": {"validation_method": "cross_checking"}} + ) + pandora_loader = PandoraLoader( + conf=conf_with_validation, use_cross_validation=False + ) + corr_config = pandora_loader.get_conf() + assert "validation" in corr_config["pipeline"] + + # test 2: no validation as input, add it + pandora_loader = PandoraLoader( + conf=copy.deepcopy(pandora_config), use_cross_validation=True + ) + corr_config = pandora_loader.get_conf() + assert "validation" in corr_config["pipeline"] + + # test 3: no validation as input, do not add it + pandora_loader = PandoraLoader( + conf=copy.deepcopy(pandora_config), use_cross_validation=False + ) + corr_config = pandora_loader.get_conf() + assert "validation" not in corr_config["pipeline"] + + @pytest.mark.unit_tests def test_overload_pandora_conf_with_confidence(): """ diff --git a/tests/test_end2end.py b/tests/test_end2end.py index d66841ea..01a83115 100644 --- a/tests/test_end2end.py +++ b/tests/test_end2end.py @@ -93,6 +93,7 @@ def test_end2end_gizeh_rectangle_epi_image_performance_map(): "grid_generation": {"method": "epipolar", "epi_step": 30}, "dense_matching": { "method": "census_sgm", + "use_cross_validation": True, "use_global_disp_range": True, }, "point_cloud_rasterization": { @@ -632,6 +633,7 @@ def test_end2end_ventoux_unique(): }, "dense_matching": { "method": "census_sgm", + "use_cross_validation": True, "use_global_disp_range": False, "loader_conf": { "input": {}, @@ -1029,6 +1031,7 @@ def test_end2end_ventoux_unique(): dense_dsm_applications = { "dense_matching": { "method": "census_sgm", + "use_cross_validation": True, "use_global_disp_range": False, }, "point_cloud_outliers_removing.1": { @@ -1102,6 +1105,12 @@ def test_end2end_ventoux_unique_split_epsg_4326(): "max_ram_per_worker": 1000, }, ) + input_config_pc["applications"] = { + "dense_matching": { + "method": "census_sgm", + "use_cross_validation": True, + }, + } pc_pipeline = sensor_to_dense_dsm.SensorToDenseDsmPipeline( input_config_pc ) @@ -1317,6 +1326,7 @@ def test_end2end_ventoux_unique_split(): }, "dense_matching": { "method": "census_sgm", + "use_cross_validation": True, "use_global_disp_range": False, "save_intermediate_data": True, "generate_confidence_intervals": False, @@ -2222,6 +2232,7 @@ def test_end2end_use_epipolar_a_priori(): }, "dense_matching": { "method": "census_sgm", + "use_cross_validation": True, "use_global_disp_range": False, }, } @@ -2442,6 +2453,10 @@ def test_end2end_ventoux_full_output_no_elevation(): "disparity_margin": 0.25, "save_intermediate_data": True, }, + "dense_matching": { + "method": "census_sgm", + "use_cross_validation": True, + }, } advanced_config = {"save_intermediate_data": True} @@ -2840,6 +2855,7 @@ def test_end2end_ventoux_with_color(): }, "dense_matching": { "method": "census_sgm", + "use_cross_validation": True, "loader": "pandora", "save_intermediate_data": True, "use_global_disp_range": False, @@ -3099,6 +3115,7 @@ def test_end2end_ventoux_with_classif(): }, "dense_matching": { "method": "census_sgm", + "use_cross_validation": True, "loader": "pandora", "save_intermediate_data": True, "use_global_disp_range": False, @@ -3274,6 +3291,7 @@ def test_compute_dsm_with_roi_ventoux(): "resampling": {"method": "bicubic", "strip_height": 80}, "dense_matching": { "method": "census_sgm", + "use_cross_validation": True, "use_global_disp_range": False, }, "sparse_matching": { @@ -3435,6 +3453,7 @@ def test_compute_dsm_with_snap_to_img1(): }, "dense_matching": { "method": "census_sgm", + "use_cross_validation": True, "use_global_disp_range": False, }, "triangulation": { @@ -3554,6 +3573,7 @@ def test_end2end_quality_stats(): }, "dense_matching": { "method": "census_sgm", + "use_cross_validation": True, "use_global_disp_range": False, }, "point_cloud_outliers_removing.1": { @@ -3847,6 +3867,7 @@ def test_end2end_ventoux_egm96_geoid(): }, "dense_matching": { "method": "census_sgm", + "use_cross_validation": True, "use_global_disp_range": False, }, "triangulation": {"method": "line_of_sight_intersection"}, @@ -3979,6 +4000,7 @@ def test_end2end_ventoux_egm96_geoid(): }, "dense_matching": { "method": "census_sgm", + "use_cross_validation": True, "use_global_disp_range": False, }, "triangulation": {"method": "line_of_sight_intersection"}, @@ -4069,6 +4091,7 @@ def test_end2end_ventoux_egm96_geoid(): }, "dense_matching": { "method": "census_sgm", + "use_cross_validation": True, "use_global_disp_range": False, }, "triangulation": {"method": "line_of_sight_intersection"}, @@ -4212,6 +4235,7 @@ def test_end2end_paca_with_mask(): }, "dense_matching": { "method": "census_sgm", + "use_cross_validation": True, "use_global_disp_range": False, }, "dense_matches_filling.2": { @@ -4323,6 +4347,7 @@ def test_end2end_disparity_filling(): dense_dsm_applications = { "dense_matching": { "method": "census_sgm", + "use_cross_validation": True, "min_epi_tile_size": 100, "save_intermediate_data": True, "use_global_disp_range": False, @@ -4455,6 +4480,7 @@ def test_end2end_disparity_filling_with_zeros(): dense_dsm_applications = { "dense_matching": { "method": "census_sgm", + "use_cross_validation": True, "save_intermediate_data": True, "use_global_disp_range": True, },