cars 1.0.0rc2__cp312-cp312-win_amd64.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of cars might be problematic. Click here for more details.
- cars/__init__.py +86 -0
- cars/applications/__init__.py +40 -0
- cars/applications/application.py +117 -0
- cars/applications/application_constants.py +29 -0
- cars/applications/application_template.py +146 -0
- cars/applications/auxiliary_filling/__init__.py +29 -0
- cars/applications/auxiliary_filling/abstract_auxiliary_filling_app.py +105 -0
- cars/applications/auxiliary_filling/auxiliary_filling_algo.py +475 -0
- cars/applications/auxiliary_filling/auxiliary_filling_from_sensors_app.py +632 -0
- cars/applications/auxiliary_filling/auxiliary_filling_wrappers.py +90 -0
- cars/applications/dem_generation/__init__.py +30 -0
- cars/applications/dem_generation/abstract_dem_generation_app.py +116 -0
- cars/applications/dem_generation/bulldozer_config/base_config.yaml +42 -0
- cars/applications/dem_generation/bulldozer_dem_app.py +641 -0
- cars/applications/dem_generation/bulldozer_memory.py +55 -0
- cars/applications/dem_generation/dem_generation_algo.py +107 -0
- cars/applications/dem_generation/dem_generation_constants.py +32 -0
- cars/applications/dem_generation/dem_generation_wrappers.py +323 -0
- cars/applications/dense_match_filling/__init__.py +30 -0
- cars/applications/dense_match_filling/abstract_dense_match_filling_app.py +242 -0
- cars/applications/dense_match_filling/fill_disp_algo.py +113 -0
- cars/applications/dense_match_filling/fill_disp_constants.py +39 -0
- cars/applications/dense_match_filling/fill_disp_wrappers.py +83 -0
- cars/applications/dense_match_filling/zero_padding_app.py +302 -0
- cars/applications/dense_matching/__init__.py +30 -0
- cars/applications/dense_matching/abstract_dense_matching_app.py +261 -0
- cars/applications/dense_matching/census_mccnn_sgm_app.py +1461 -0
- cars/applications/dense_matching/cpp/__init__.py +0 -0
- cars/applications/dense_matching/cpp/dense_matching_cpp.cp312-win_amd64.dll.a +0 -0
- cars/applications/dense_matching/cpp/dense_matching_cpp.cp312-win_amd64.pyd +0 -0
- cars/applications/dense_matching/cpp/dense_matching_cpp.py +94 -0
- cars/applications/dense_matching/cpp/includes/dense_matching.hpp +58 -0
- cars/applications/dense_matching/cpp/meson.build +9 -0
- cars/applications/dense_matching/cpp/src/bindings.cpp +13 -0
- cars/applications/dense_matching/cpp/src/dense_matching.cpp +207 -0
- cars/applications/dense_matching/dense_matching_algo.py +401 -0
- cars/applications/dense_matching/dense_matching_constants.py +89 -0
- cars/applications/dense_matching/dense_matching_wrappers.py +951 -0
- cars/applications/dense_matching/disparity_grid_algo.py +597 -0
- cars/applications/dense_matching/loaders/__init__.py +23 -0
- cars/applications/dense_matching/loaders/config_census_sgm_default.json +31 -0
- cars/applications/dense_matching/loaders/config_census_sgm_homogeneous.json +30 -0
- cars/applications/dense_matching/loaders/config_census_sgm_mountain_and_vegetation.json +30 -0
- cars/applications/dense_matching/loaders/config_census_sgm_shadow.json +30 -0
- cars/applications/dense_matching/loaders/config_census_sgm_sparse.json +36 -0
- cars/applications/dense_matching/loaders/config_census_sgm_urban.json +30 -0
- cars/applications/dense_matching/loaders/config_mapping.json +13 -0
- cars/applications/dense_matching/loaders/config_mccnn.json +28 -0
- cars/applications/dense_matching/loaders/global_land_cover_map.tif +0 -0
- cars/applications/dense_matching/loaders/pandora_loader.py +593 -0
- cars/applications/dsm_filling/__init__.py +32 -0
- cars/applications/dsm_filling/abstract_dsm_filling_app.py +101 -0
- cars/applications/dsm_filling/border_interpolation_app.py +278 -0
- cars/applications/dsm_filling/bulldozer_config/base_config.yaml +44 -0
- cars/applications/dsm_filling/bulldozer_filling_app.py +288 -0
- cars/applications/dsm_filling/exogenous_filling_app.py +341 -0
- cars/applications/dsm_merging/__init__.py +28 -0
- cars/applications/dsm_merging/abstract_dsm_merging_app.py +101 -0
- cars/applications/dsm_merging/weighted_fusion_app.py +639 -0
- cars/applications/grid_correction/__init__.py +30 -0
- cars/applications/grid_correction/abstract_grid_correction_app.py +103 -0
- cars/applications/grid_correction/grid_correction_app.py +557 -0
- cars/applications/grid_generation/__init__.py +30 -0
- cars/applications/grid_generation/abstract_grid_generation_app.py +142 -0
- cars/applications/grid_generation/epipolar_grid_generation_app.py +327 -0
- cars/applications/grid_generation/grid_generation_algo.py +388 -0
- cars/applications/grid_generation/grid_generation_constants.py +46 -0
- cars/applications/grid_generation/transform_grid.py +88 -0
- cars/applications/ground_truth_reprojection/__init__.py +30 -0
- cars/applications/ground_truth_reprojection/abstract_ground_truth_reprojection_app.py +137 -0
- cars/applications/ground_truth_reprojection/direct_localization_app.py +629 -0
- cars/applications/ground_truth_reprojection/ground_truth_reprojection_algo.py +275 -0
- cars/applications/point_cloud_outlier_removal/__init__.py +30 -0
- cars/applications/point_cloud_outlier_removal/abstract_outlier_removal_app.py +385 -0
- cars/applications/point_cloud_outlier_removal/outlier_removal_algo.py +392 -0
- cars/applications/point_cloud_outlier_removal/outlier_removal_constants.py +43 -0
- cars/applications/point_cloud_outlier_removal/small_components_app.py +522 -0
- cars/applications/point_cloud_outlier_removal/statistical_app.py +528 -0
- cars/applications/rasterization/__init__.py +30 -0
- cars/applications/rasterization/abstract_pc_rasterization_app.py +183 -0
- cars/applications/rasterization/rasterization_algo.py +534 -0
- cars/applications/rasterization/rasterization_constants.py +38 -0
- cars/applications/rasterization/rasterization_wrappers.py +639 -0
- cars/applications/rasterization/simple_gaussian_app.py +1152 -0
- cars/applications/resampling/__init__.py +28 -0
- cars/applications/resampling/abstract_resampling_app.py +187 -0
- cars/applications/resampling/bicubic_resampling_app.py +760 -0
- cars/applications/resampling/resampling_algo.py +590 -0
- cars/applications/resampling/resampling_constants.py +36 -0
- cars/applications/resampling/resampling_wrappers.py +309 -0
- cars/applications/sensors_subsampling/__init__.py +32 -0
- cars/applications/sensors_subsampling/abstract_subsampling_app.py +109 -0
- cars/applications/sensors_subsampling/rasterio_subsampling_app.py +420 -0
- cars/applications/sensors_subsampling/subsampling_algo.py +108 -0
- cars/applications/sparse_matching/__init__.py +30 -0
- cars/applications/sparse_matching/abstract_sparse_matching_app.py +599 -0
- cars/applications/sparse_matching/sift_app.py +724 -0
- cars/applications/sparse_matching/sparse_matching_algo.py +360 -0
- cars/applications/sparse_matching/sparse_matching_constants.py +66 -0
- cars/applications/sparse_matching/sparse_matching_wrappers.py +282 -0
- cars/applications/triangulation/__init__.py +32 -0
- cars/applications/triangulation/abstract_triangulation_app.py +227 -0
- cars/applications/triangulation/line_of_sight_intersection_app.py +1243 -0
- cars/applications/triangulation/pc_transform.py +552 -0
- cars/applications/triangulation/triangulation_algo.py +371 -0
- cars/applications/triangulation/triangulation_constants.py +38 -0
- cars/applications/triangulation/triangulation_wrappers.py +259 -0
- cars/bundleadjustment.py +750 -0
- cars/cars.py +179 -0
- cars/conf/__init__.py +23 -0
- cars/conf/geoid/egm96.grd +0 -0
- cars/conf/geoid/egm96.grd.hdr +15 -0
- cars/conf/input_parameters.py +156 -0
- cars/conf/mask_cst.py +35 -0
- cars/core/__init__.py +23 -0
- cars/core/cars_logging.py +402 -0
- cars/core/constants.py +191 -0
- cars/core/constants_disparity.py +50 -0
- cars/core/datasets.py +140 -0
- cars/core/geometry/__init__.py +27 -0
- cars/core/geometry/abstract_geometry.py +1119 -0
- cars/core/geometry/shareloc_geometry.py +598 -0
- cars/core/inputs.py +568 -0
- cars/core/outputs.py +176 -0
- cars/core/preprocessing.py +722 -0
- cars/core/projection.py +843 -0
- cars/core/roi_tools.py +215 -0
- cars/core/tiling.py +774 -0
- cars/core/utils.py +164 -0
- cars/data_structures/__init__.py +23 -0
- cars/data_structures/cars_dataset.py +1544 -0
- cars/data_structures/cars_dict.py +74 -0
- cars/data_structures/corresponding_tiles_tools.py +186 -0
- cars/data_structures/dataframe_converter.py +185 -0
- cars/data_structures/format_transformation.py +297 -0
- cars/devibrate.py +689 -0
- cars/extractroi.py +264 -0
- cars/orchestrator/__init__.py +23 -0
- cars/orchestrator/achievement_tracker.py +125 -0
- cars/orchestrator/cluster/__init__.py +37 -0
- cars/orchestrator/cluster/abstract_cluster.py +250 -0
- cars/orchestrator/cluster/abstract_dask_cluster.py +381 -0
- cars/orchestrator/cluster/dask_cluster_tools.py +103 -0
- cars/orchestrator/cluster/dask_config/README.md +94 -0
- cars/orchestrator/cluster/dask_config/dask.yaml +21 -0
- cars/orchestrator/cluster/dask_config/distributed.yaml +70 -0
- cars/orchestrator/cluster/dask_config/jobqueue.yaml +26 -0
- cars/orchestrator/cluster/dask_config/reference_confs/dask-schema.yaml +137 -0
- cars/orchestrator/cluster/dask_config/reference_confs/dask.yaml +26 -0
- cars/orchestrator/cluster/dask_config/reference_confs/distributed-schema.yaml +1009 -0
- cars/orchestrator/cluster/dask_config/reference_confs/distributed.yaml +273 -0
- cars/orchestrator/cluster/dask_config/reference_confs/jobqueue.yaml +212 -0
- cars/orchestrator/cluster/dask_jobqueue_utils.py +204 -0
- cars/orchestrator/cluster/local_dask_cluster.py +116 -0
- cars/orchestrator/cluster/log_wrapper.py +728 -0
- cars/orchestrator/cluster/mp_cluster/__init__.py +27 -0
- cars/orchestrator/cluster/mp_cluster/mp_factorizer.py +212 -0
- cars/orchestrator/cluster/mp_cluster/mp_objects.py +535 -0
- cars/orchestrator/cluster/mp_cluster/mp_tools.py +93 -0
- cars/orchestrator/cluster/mp_cluster/mp_wrapper.py +505 -0
- cars/orchestrator/cluster/mp_cluster/multiprocessing_cluster.py +986 -0
- cars/orchestrator/cluster/mp_cluster/multiprocessing_profiler.py +399 -0
- cars/orchestrator/cluster/pbs_dask_cluster.py +207 -0
- cars/orchestrator/cluster/sequential_cluster.py +139 -0
- cars/orchestrator/cluster/slurm_dask_cluster.py +234 -0
- cars/orchestrator/memory_tools.py +47 -0
- cars/orchestrator/orchestrator.py +755 -0
- cars/orchestrator/orchestrator_constants.py +29 -0
- cars/orchestrator/registry/__init__.py +23 -0
- cars/orchestrator/registry/abstract_registry.py +143 -0
- cars/orchestrator/registry/compute_registry.py +106 -0
- cars/orchestrator/registry/id_generator.py +116 -0
- cars/orchestrator/registry/replacer_registry.py +213 -0
- cars/orchestrator/registry/saver_registry.py +363 -0
- cars/orchestrator/registry/unseen_registry.py +118 -0
- cars/orchestrator/tiles_profiler.py +279 -0
- cars/pipelines/__init__.py +26 -0
- cars/pipelines/conf_resolution/conf_final_resolution.yaml +5 -0
- cars/pipelines/conf_resolution/conf_first_resolution.yaml +4 -0
- cars/pipelines/conf_resolution/conf_intermediate_resolution.yaml +2 -0
- cars/pipelines/default/__init__.py +26 -0
- cars/pipelines/default/default_pipeline.py +1088 -0
- cars/pipelines/filling/__init__.py +26 -0
- cars/pipelines/filling/filling.py +981 -0
- cars/pipelines/formatting/__init__.py +26 -0
- cars/pipelines/formatting/formatting.py +186 -0
- cars/pipelines/merging/__init__.py +26 -0
- cars/pipelines/merging/merging.py +439 -0
- cars/pipelines/parameters/__init__.py +0 -0
- cars/pipelines/parameters/advanced_parameters.py +256 -0
- cars/pipelines/parameters/advanced_parameters_constants.py +68 -0
- cars/pipelines/parameters/application_parameters.py +72 -0
- cars/pipelines/parameters/depth_map_inputs.py +0 -0
- cars/pipelines/parameters/dsm_inputs.py +349 -0
- cars/pipelines/parameters/dsm_inputs_constants.py +25 -0
- cars/pipelines/parameters/output_constants.py +52 -0
- cars/pipelines/parameters/output_parameters.py +438 -0
- cars/pipelines/parameters/sensor_inputs.py +859 -0
- cars/pipelines/parameters/sensor_inputs_constants.py +51 -0
- cars/pipelines/parameters/sensor_loaders/__init__.py +29 -0
- cars/pipelines/parameters/sensor_loaders/basic_classif_loader.py +86 -0
- cars/pipelines/parameters/sensor_loaders/basic_image_loader.py +98 -0
- cars/pipelines/parameters/sensor_loaders/pivot_classif_loader.py +90 -0
- cars/pipelines/parameters/sensor_loaders/pivot_image_loader.py +105 -0
- cars/pipelines/parameters/sensor_loaders/sensor_loader.py +93 -0
- cars/pipelines/parameters/sensor_loaders/sensor_loader_template.py +71 -0
- cars/pipelines/parameters/sensor_loaders/slurp_classif_loader.py +86 -0
- cars/pipelines/pipeline.py +119 -0
- cars/pipelines/pipeline_constants.py +38 -0
- cars/pipelines/pipeline_template.py +135 -0
- cars/pipelines/subsampling/__init__.py +26 -0
- cars/pipelines/subsampling/subsampling.py +358 -0
- cars/pipelines/surface_modeling/__init__.py +26 -0
- cars/pipelines/surface_modeling/surface_modeling.py +2098 -0
- cars/pipelines/tie_points/__init__.py +26 -0
- cars/pipelines/tie_points/tie_points.py +536 -0
- cars/starter.py +167 -0
- cars-1.0.0rc2.dist-info/DELVEWHEEL +2 -0
- cars-1.0.0rc2.dist-info/METADATA +289 -0
- cars-1.0.0rc2.dist-info/RECORD +225 -0
- cars-1.0.0rc2.dist-info/WHEEL +4 -0
- cars-1.0.0rc2.dist-info/entry_points.txt +8 -0
- cars.libs/libgcc_s_seh-1-b2494fcbd4d80cf2c98fdd5261f6d850.dll +0 -0
- cars.libs/libstdc++-6-e9b0d12ae0e9555bbae55e8dfd08c3f7.dll +0 -0
- cars.libs/libwinpthread-1-7882d1b093714ccdfaf4e0789a817792.dll +0 -0
|
@@ -0,0 +1,1461 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
# coding: utf8
|
|
3
|
+
#
|
|
4
|
+
# Copyright (c) 2020 Centre National d'Etudes Spatiales (CNES).
|
|
5
|
+
#
|
|
6
|
+
# This file is part of CARS
|
|
7
|
+
# (see https://github.com/CNES/cars).
|
|
8
|
+
#
|
|
9
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
10
|
+
# you may not use this file except in compliance with the License.
|
|
11
|
+
# You may obtain a copy of the License at
|
|
12
|
+
#
|
|
13
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
14
|
+
#
|
|
15
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
16
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
17
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
18
|
+
# See the License for the specific language governing permissions and
|
|
19
|
+
# limitations under the License.
|
|
20
|
+
#
|
|
21
|
+
"""
|
|
22
|
+
this module contains the dense_matching application class.
|
|
23
|
+
"""
|
|
24
|
+
# pylint: disable=too-many-lines
|
|
25
|
+
import collections
|
|
26
|
+
|
|
27
|
+
# Standard imports
|
|
28
|
+
import copy
|
|
29
|
+
import logging
|
|
30
|
+
import math
|
|
31
|
+
import os
|
|
32
|
+
from typing import Dict, Tuple
|
|
33
|
+
|
|
34
|
+
# Third party imports
|
|
35
|
+
import numpy as np
|
|
36
|
+
import pandora
|
|
37
|
+
import xarray as xr
|
|
38
|
+
from json_checker import And, Checker, Or
|
|
39
|
+
from pandora.check_configuration import check_pipeline_section
|
|
40
|
+
from pandora.img_tools import add_global_disparity
|
|
41
|
+
from pandora.state_machine import PandoraMachine
|
|
42
|
+
from rasterio.profiles import DefaultGTiffProfile
|
|
43
|
+
from scipy.ndimage import maximum_filter, minimum_filter
|
|
44
|
+
|
|
45
|
+
import cars.applications.dense_matching.dense_matching_constants as dm_cst
|
|
46
|
+
import cars.orchestrator.orchestrator as ocht
|
|
47
|
+
from cars.applications import application_constants
|
|
48
|
+
from cars.applications.dense_matching import dense_matching_algo as dm_algo
|
|
49
|
+
from cars.applications.dense_matching import (
|
|
50
|
+
dense_matching_wrappers as dm_wrappers,
|
|
51
|
+
)
|
|
52
|
+
from cars.applications.dense_matching.abstract_dense_matching_app import (
|
|
53
|
+
DenseMatching,
|
|
54
|
+
)
|
|
55
|
+
from cars.applications.dense_matching.disparity_grid_algo import (
|
|
56
|
+
generate_disp_range_const_tile_wrapper,
|
|
57
|
+
generate_disp_range_from_dem_wrapper,
|
|
58
|
+
)
|
|
59
|
+
from cars.applications.dense_matching.loaders.pandora_loader import (
|
|
60
|
+
PandoraLoader,
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
# CARS imports
|
|
64
|
+
from cars.core import constants as cst
|
|
65
|
+
from cars.core import constants_disparity as cst_disp
|
|
66
|
+
from cars.core import inputs, tiling
|
|
67
|
+
from cars.core.utils import safe_makedirs
|
|
68
|
+
from cars.data_structures import cars_dataset, format_transformation
|
|
69
|
+
from cars.data_structures.cars_dict import CarsDict
|
|
70
|
+
from cars.orchestrator.cluster.log_wrapper import cars_profile
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
class CensusMccnnSgm(
|
|
74
|
+
DenseMatching,
|
|
75
|
+
short_name=[
|
|
76
|
+
"census_sgm_default",
|
|
77
|
+
"census_sgm_urban",
|
|
78
|
+
"census_sgm_shadow",
|
|
79
|
+
"census_sgm_mountain_and_vegetation",
|
|
80
|
+
"census_sgm_homogeneous",
|
|
81
|
+
"mccnn_sgm",
|
|
82
|
+
"auto",
|
|
83
|
+
"custom",
|
|
84
|
+
],
|
|
85
|
+
): # pylint: disable=R0903,disable=R0902,disable=C0302
|
|
86
|
+
"""
|
|
87
|
+
Census SGM & MCCNN SGM matching class
|
|
88
|
+
"""
|
|
89
|
+
|
|
90
|
+
def __init__(self, conf=None):
|
|
91
|
+
"""
|
|
92
|
+
Init function of DenseMatching
|
|
93
|
+
|
|
94
|
+
:param conf: configuration for matching
|
|
95
|
+
:return: an application_to_use object
|
|
96
|
+
"""
|
|
97
|
+
|
|
98
|
+
super().__init__(conf=conf)
|
|
99
|
+
|
|
100
|
+
# check conf
|
|
101
|
+
self.used_method = self.used_config["method"]
|
|
102
|
+
self.min_epi_tile_size = self.used_config["min_epi_tile_size"]
|
|
103
|
+
self.max_epi_tile_size = self.used_config["max_epi_tile_size"]
|
|
104
|
+
self.epipolar_tile_margin_in_percent = self.used_config[
|
|
105
|
+
"epipolar_tile_margin_in_percent"
|
|
106
|
+
]
|
|
107
|
+
self.min_elevation_offset = self.used_config["min_elevation_offset"]
|
|
108
|
+
self.max_elevation_offset = self.used_config["max_elevation_offset"]
|
|
109
|
+
|
|
110
|
+
# Disparity threshold
|
|
111
|
+
self.disp_min_threshold = self.used_config["disp_min_threshold"]
|
|
112
|
+
self.disp_max_threshold = self.used_config["disp_max_threshold"]
|
|
113
|
+
|
|
114
|
+
# Ambiguity
|
|
115
|
+
self.generate_ambiguity = self.used_config["generate_ambiguity"]
|
|
116
|
+
|
|
117
|
+
self.classification_fusion_margin = self.used_config[
|
|
118
|
+
"classification_fusion_margin"
|
|
119
|
+
]
|
|
120
|
+
|
|
121
|
+
# Margins computation parameters
|
|
122
|
+
# Use local disp
|
|
123
|
+
self.use_global_disp_range = self.used_config["use_global_disp_range"]
|
|
124
|
+
self.local_disp_grid_step = self.used_config["local_disp_grid_step"]
|
|
125
|
+
self.disp_range_propagation_filter_size = self.used_config[
|
|
126
|
+
"disp_range_propagation_filter_size"
|
|
127
|
+
]
|
|
128
|
+
self.epi_disp_grid_tile_size = self.used_config[
|
|
129
|
+
"epi_disp_grid_tile_size"
|
|
130
|
+
]
|
|
131
|
+
self.use_cross_validation = self.used_config["use_cross_validation"]
|
|
132
|
+
self.denoise_disparity_map = self.used_config["denoise_disparity_map"]
|
|
133
|
+
self.required_bands = self.used_config["required_bands"]
|
|
134
|
+
self.used_band = self.used_config["used_band"]
|
|
135
|
+
|
|
136
|
+
# Saving files
|
|
137
|
+
self.save_intermediate_data = self.used_config["save_intermediate_data"]
|
|
138
|
+
|
|
139
|
+
self.confidence_filtering = self.used_config["confidence_filtering"]
|
|
140
|
+
self.threshold_disp_range_to_borders = self.used_config[
|
|
141
|
+
"threshold_disp_range_to_borders"
|
|
142
|
+
]
|
|
143
|
+
self.filter_incomplete_disparity_range = self.used_config[
|
|
144
|
+
"filter_incomplete_disparity_range"
|
|
145
|
+
]
|
|
146
|
+
|
|
147
|
+
# init orchestrator
|
|
148
|
+
self.orchestrator = None
|
|
149
|
+
|
|
150
|
+
def get_performance_map_parameters(self):
|
|
151
|
+
"""
|
|
152
|
+
Get parameter linked to performance, that will be used in triangulation
|
|
153
|
+
|
|
154
|
+
:return: parameters to use
|
|
155
|
+
:type: dict
|
|
156
|
+
"""
|
|
157
|
+
|
|
158
|
+
return {
|
|
159
|
+
"performance_map_method": self.used_config[
|
|
160
|
+
"performance_map_method"
|
|
161
|
+
],
|
|
162
|
+
"perf_ambiguity_threshold": self.used_config[
|
|
163
|
+
"perf_ambiguity_threshold"
|
|
164
|
+
],
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
def check_conf(self, conf):
|
|
168
|
+
"""
|
|
169
|
+
Check configuration
|
|
170
|
+
|
|
171
|
+
:param conf: configuration to check
|
|
172
|
+
:type conf: dict
|
|
173
|
+
:return: overloaded configuration
|
|
174
|
+
:rtype: dict
|
|
175
|
+
|
|
176
|
+
"""
|
|
177
|
+
# init conf
|
|
178
|
+
if conf is not None:
|
|
179
|
+
overloaded_conf = conf.copy()
|
|
180
|
+
else:
|
|
181
|
+
conf = {}
|
|
182
|
+
overloaded_conf = {}
|
|
183
|
+
|
|
184
|
+
# Overload conf
|
|
185
|
+
# check loader
|
|
186
|
+
loader_conf = conf.get("loader_conf", None)
|
|
187
|
+
default_method = "auto"
|
|
188
|
+
if loader_conf is not None:
|
|
189
|
+
default_method = "custom"
|
|
190
|
+
loader = conf.get("loader", "pandora")
|
|
191
|
+
overloaded_conf["method"] = conf.get(
|
|
192
|
+
"method", default_method
|
|
193
|
+
) # change it if census_sgm is not default
|
|
194
|
+
if overloaded_conf["method"] == "auto" and loader_conf is not None:
|
|
195
|
+
raise RuntimeError(
|
|
196
|
+
"It's not possible to use auto method with custom "
|
|
197
|
+
"loader configuration"
|
|
198
|
+
)
|
|
199
|
+
|
|
200
|
+
# method called in abstract_dense_matching_app.py
|
|
201
|
+
overloaded_conf["min_epi_tile_size"] = conf.get(
|
|
202
|
+
"min_epi_tile_size", 300
|
|
203
|
+
)
|
|
204
|
+
overloaded_conf["max_epi_tile_size"] = conf.get(
|
|
205
|
+
"max_epi_tile_size", 1500
|
|
206
|
+
)
|
|
207
|
+
overloaded_conf["epipolar_tile_margin_in_percent"] = conf.get(
|
|
208
|
+
"epipolar_tile_margin_in_percent", 60
|
|
209
|
+
)
|
|
210
|
+
|
|
211
|
+
overloaded_conf["classification_fusion_margin"] = conf.get(
|
|
212
|
+
"classification_fusion_margin", -1
|
|
213
|
+
)
|
|
214
|
+
overloaded_conf["min_elevation_offset"] = conf.get(
|
|
215
|
+
"min_elevation_offset", None
|
|
216
|
+
)
|
|
217
|
+
overloaded_conf["max_elevation_offset"] = conf.get(
|
|
218
|
+
"max_elevation_offset", None
|
|
219
|
+
)
|
|
220
|
+
overloaded_conf["denoise_disparity_map"] = conf.get(
|
|
221
|
+
"denoise_disparity_map", False
|
|
222
|
+
)
|
|
223
|
+
|
|
224
|
+
# confidence filtering parameters
|
|
225
|
+
overloaded_conf["confidence_filtering"] = conf.get(
|
|
226
|
+
"confidence_filtering", {}
|
|
227
|
+
)
|
|
228
|
+
|
|
229
|
+
# Disparity threshold
|
|
230
|
+
overloaded_conf["disp_min_threshold"] = conf.get(
|
|
231
|
+
"disp_min_threshold", None
|
|
232
|
+
)
|
|
233
|
+
overloaded_conf["disp_max_threshold"] = conf.get(
|
|
234
|
+
"disp_max_threshold", None
|
|
235
|
+
)
|
|
236
|
+
|
|
237
|
+
overloaded_conf["perf_eta_max_ambiguity"] = conf.get(
|
|
238
|
+
"perf_eta_max_ambiguity", 0.99
|
|
239
|
+
)
|
|
240
|
+
overloaded_conf["perf_eta_max_risk"] = conf.get(
|
|
241
|
+
"perf_eta_max_risk", 0.25
|
|
242
|
+
)
|
|
243
|
+
overloaded_conf["perf_eta_step"] = conf.get("perf_eta_step", 0.04)
|
|
244
|
+
overloaded_conf["perf_ambiguity_threshold"] = conf.get(
|
|
245
|
+
"perf_ambiguity_threshold", 0.6
|
|
246
|
+
)
|
|
247
|
+
overloaded_conf["use_cross_validation"] = conf.get(
|
|
248
|
+
"use_cross_validation", True
|
|
249
|
+
)
|
|
250
|
+
# Margins computation parameters
|
|
251
|
+
overloaded_conf["use_global_disp_range"] = conf.get(
|
|
252
|
+
"use_global_disp_range", False
|
|
253
|
+
)
|
|
254
|
+
overloaded_conf["local_disp_grid_step"] = conf.get(
|
|
255
|
+
"local_disp_grid_step", 10
|
|
256
|
+
)
|
|
257
|
+
overloaded_conf["disp_range_propagation_filter_size"] = conf.get(
|
|
258
|
+
"disp_range_propagation_filter_size", 50
|
|
259
|
+
)
|
|
260
|
+
overloaded_conf["epi_disp_grid_tile_size"] = conf.get(
|
|
261
|
+
"epi_disp_grid_tile_size", 800
|
|
262
|
+
)
|
|
263
|
+
overloaded_conf["required_bands"] = conf.get("required_bands", ["b0"])
|
|
264
|
+
overloaded_conf["used_band"] = conf.get("used_band", "b0")
|
|
265
|
+
overloaded_conf["threshold_disp_range_to_borders"] = conf.get(
|
|
266
|
+
"threshold_disp_range_to_borders", False
|
|
267
|
+
)
|
|
268
|
+
overloaded_conf["filter_incomplete_disparity_range"] = conf.get(
|
|
269
|
+
"filter_incomplete_disparity_range", True
|
|
270
|
+
)
|
|
271
|
+
|
|
272
|
+
# Saving files
|
|
273
|
+
overloaded_conf["save_intermediate_data"] = conf.get(
|
|
274
|
+
"save_intermediate_data", False
|
|
275
|
+
)
|
|
276
|
+
|
|
277
|
+
# Permormance map parameters
|
|
278
|
+
overloaded_conf["generate_ambiguity"] = conf.get(
|
|
279
|
+
"generate_ambiguity",
|
|
280
|
+
overloaded_conf["save_intermediate_data"],
|
|
281
|
+
)
|
|
282
|
+
|
|
283
|
+
# Permormance map parameters
|
|
284
|
+
default_perf_map_method = None
|
|
285
|
+
if overloaded_conf["save_intermediate_data"]:
|
|
286
|
+
default_perf_map_method = "risk"
|
|
287
|
+
overloaded_conf["performance_map_method"] = conf.get(
|
|
288
|
+
"performance_map_method",
|
|
289
|
+
default_perf_map_method,
|
|
290
|
+
)
|
|
291
|
+
|
|
292
|
+
# Get performance map method
|
|
293
|
+
perf_map_method = overloaded_conf["performance_map_method"]
|
|
294
|
+
if isinstance(overloaded_conf["performance_map_method"], str):
|
|
295
|
+
perf_map_method = [perf_map_method]
|
|
296
|
+
elif perf_map_method is None:
|
|
297
|
+
perf_map_method = []
|
|
298
|
+
|
|
299
|
+
overloaded_conf["performance_map_method"] = perf_map_method
|
|
300
|
+
|
|
301
|
+
if overloaded_conf["use_cross_validation"] is True:
|
|
302
|
+
overloaded_conf["use_cross_validation"] = "fast"
|
|
303
|
+
|
|
304
|
+
# TODO modify, use loader directly
|
|
305
|
+
logger = logging.getLogger("transitions.core")
|
|
306
|
+
logger.addFilter(
|
|
307
|
+
lambda record: "to model due to model override policy"
|
|
308
|
+
not in record.getMessage()
|
|
309
|
+
)
|
|
310
|
+
pandora_loader = PandoraLoader(
|
|
311
|
+
conf=loader_conf,
|
|
312
|
+
method_name=overloaded_conf["method"],
|
|
313
|
+
generate_performance_map_from_risk="risk" in perf_map_method,
|
|
314
|
+
generate_performance_map_from_intervals="intervals"
|
|
315
|
+
in perf_map_method,
|
|
316
|
+
generate_ambiguity=overloaded_conf["generate_ambiguity"],
|
|
317
|
+
perf_eta_max_ambiguity=overloaded_conf["perf_eta_max_ambiguity"],
|
|
318
|
+
perf_eta_max_risk=overloaded_conf["perf_eta_max_risk"],
|
|
319
|
+
perf_eta_step=overloaded_conf["perf_eta_step"],
|
|
320
|
+
use_cross_validation=overloaded_conf["use_cross_validation"],
|
|
321
|
+
denoise_disparity_map=overloaded_conf["denoise_disparity_map"],
|
|
322
|
+
used_band=overloaded_conf["used_band"],
|
|
323
|
+
)
|
|
324
|
+
|
|
325
|
+
overloaded_conf["loader"] = loader
|
|
326
|
+
|
|
327
|
+
# Get params from loader
|
|
328
|
+
self.loader = pandora_loader
|
|
329
|
+
self.corr_config = collections.OrderedDict(pandora_loader.get_conf())
|
|
330
|
+
# Instantiate margins from pandora check conf
|
|
331
|
+
# create the dataset
|
|
332
|
+
classif_bands = pandora_loader.get_classif_bands()
|
|
333
|
+
fake_dataset = xr.Dataset(
|
|
334
|
+
data_vars={
|
|
335
|
+
"image": (["row", "col"], np.zeros((10, 10))),
|
|
336
|
+
"classif": (
|
|
337
|
+
["row", "col", "band_classif"],
|
|
338
|
+
np.zeros((10, 10, len(classif_bands)), dtype=np.int32),
|
|
339
|
+
),
|
|
340
|
+
},
|
|
341
|
+
coords={
|
|
342
|
+
"band_im": [overloaded_conf["used_band"]],
|
|
343
|
+
"band_classif": classif_bands,
|
|
344
|
+
"row": np.arange(10),
|
|
345
|
+
"col": np.arange(10),
|
|
346
|
+
},
|
|
347
|
+
attrs={"disparity_source": [-1, 1]},
|
|
348
|
+
)
|
|
349
|
+
# Import plugins before checking configuration
|
|
350
|
+
pandora.import_plugin()
|
|
351
|
+
pandora_machine = PandoraMachine()
|
|
352
|
+
corr_config_pipeline = {"pipeline": dict(self.corr_config["pipeline"])}
|
|
353
|
+
saved_schema = copy.deepcopy(
|
|
354
|
+
pandora.matching_cost.matching_cost.AbstractMatchingCost.schema
|
|
355
|
+
)
|
|
356
|
+
_ = check_pipeline_section(
|
|
357
|
+
corr_config_pipeline, fake_dataset, fake_dataset, pandora_machine
|
|
358
|
+
)
|
|
359
|
+
# quick fix to remove when the problem is solved in pandora
|
|
360
|
+
pandora.matching_cost.matching_cost.AbstractMatchingCost.schema = (
|
|
361
|
+
saved_schema
|
|
362
|
+
)
|
|
363
|
+
self.margins = pandora_machine.margins.global_margins
|
|
364
|
+
|
|
365
|
+
if overloaded_conf["method"] != "auto":
|
|
366
|
+
# not final configuration
|
|
367
|
+
overloaded_conf["loader_conf"] = self.corr_config
|
|
368
|
+
else:
|
|
369
|
+
overloaded_conf["loader_conf"] = None
|
|
370
|
+
|
|
371
|
+
application_schema = {
|
|
372
|
+
"method": str,
|
|
373
|
+
"min_epi_tile_size": And(int, lambda x: x > 0),
|
|
374
|
+
"max_epi_tile_size": And(int, lambda x: x > 0),
|
|
375
|
+
"epipolar_tile_margin_in_percent": int,
|
|
376
|
+
"min_elevation_offset": Or(None, int),
|
|
377
|
+
"max_elevation_offset": Or(None, int),
|
|
378
|
+
"disp_min_threshold": Or(None, int),
|
|
379
|
+
"disp_max_threshold": Or(None, int),
|
|
380
|
+
"save_intermediate_data": bool,
|
|
381
|
+
"generate_ambiguity": bool,
|
|
382
|
+
"performance_map_method": And(
|
|
383
|
+
list,
|
|
384
|
+
lambda x: all(y in ["risk", "intervals"] for y in x),
|
|
385
|
+
),
|
|
386
|
+
"perf_eta_max_ambiguity": float,
|
|
387
|
+
"perf_eta_max_risk": float,
|
|
388
|
+
"perf_eta_step": float,
|
|
389
|
+
"classification_fusion_margin": int,
|
|
390
|
+
"perf_ambiguity_threshold": float,
|
|
391
|
+
"use_cross_validation": Or(bool, str),
|
|
392
|
+
"denoise_disparity_map": bool,
|
|
393
|
+
"use_global_disp_range": bool,
|
|
394
|
+
"local_disp_grid_step": int,
|
|
395
|
+
"disp_range_propagation_filter_size": And(
|
|
396
|
+
Or(int, float), lambda x: x >= 0
|
|
397
|
+
),
|
|
398
|
+
"epi_disp_grid_tile_size": int,
|
|
399
|
+
"required_bands": [str],
|
|
400
|
+
"used_band": str,
|
|
401
|
+
"loader_conf": Or(dict, collections.OrderedDict, str, None),
|
|
402
|
+
"loader": str,
|
|
403
|
+
"confidence_filtering": dict,
|
|
404
|
+
"threshold_disp_range_to_borders": bool,
|
|
405
|
+
"filter_incomplete_disparity_range": bool,
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
# Check conf
|
|
409
|
+
checker = Checker(application_schema)
|
|
410
|
+
checker.validate(overloaded_conf)
|
|
411
|
+
|
|
412
|
+
self.check_conf_confidence_filtering(overloaded_conf)
|
|
413
|
+
|
|
414
|
+
# Check consistency between bounds for optimal tile size search
|
|
415
|
+
min_epi_tile_size = overloaded_conf["min_epi_tile_size"]
|
|
416
|
+
max_epi_tile_size = overloaded_conf["max_epi_tile_size"]
|
|
417
|
+
if min_epi_tile_size > max_epi_tile_size:
|
|
418
|
+
raise ValueError(
|
|
419
|
+
"Maximal tile size should be bigger than "
|
|
420
|
+
"minimal tile size for optimal tile size search"
|
|
421
|
+
)
|
|
422
|
+
|
|
423
|
+
# Check consistency between bounds for elevation offset
|
|
424
|
+
min_elevation_offset = overloaded_conf["min_elevation_offset"]
|
|
425
|
+
max_elevation_offset = overloaded_conf["max_elevation_offset"]
|
|
426
|
+
if (
|
|
427
|
+
min_elevation_offset is not None
|
|
428
|
+
and max_elevation_offset is not None
|
|
429
|
+
and min_elevation_offset > max_elevation_offset
|
|
430
|
+
):
|
|
431
|
+
raise ValueError(
|
|
432
|
+
"Maximal elevation should be bigger than "
|
|
433
|
+
"minimal elevation for dense matching"
|
|
434
|
+
)
|
|
435
|
+
|
|
436
|
+
disp_min_threshold = overloaded_conf["disp_min_threshold"]
|
|
437
|
+
disp_max_threshold = overloaded_conf["disp_max_threshold"]
|
|
438
|
+
if (
|
|
439
|
+
disp_min_threshold is not None
|
|
440
|
+
and disp_max_threshold is not None
|
|
441
|
+
and disp_min_threshold > disp_max_threshold
|
|
442
|
+
):
|
|
443
|
+
raise ValueError(
|
|
444
|
+
"Maximal disparity should be bigger than "
|
|
445
|
+
"minimal disparity for dense matching"
|
|
446
|
+
)
|
|
447
|
+
return overloaded_conf
|
|
448
|
+
|
|
449
|
+
def check_conf_confidence_filtering(self, overloaded_conf):
|
|
450
|
+
"""
|
|
451
|
+
Check the confidence filtering conf
|
|
452
|
+
"""
|
|
453
|
+
overloaded_conf["confidence_filtering"]["activated"] = overloaded_conf[
|
|
454
|
+
"confidence_filtering"
|
|
455
|
+
].get("activated", True)
|
|
456
|
+
overloaded_conf["confidence_filtering"]["bounds_ratio_threshold"] = (
|
|
457
|
+
overloaded_conf["confidence_filtering"].get(
|
|
458
|
+
"bounds_ratio_threshold", 0.2
|
|
459
|
+
)
|
|
460
|
+
)
|
|
461
|
+
overloaded_conf["confidence_filtering"]["risk_ratio_threshold"] = (
|
|
462
|
+
overloaded_conf["confidence_filtering"].get(
|
|
463
|
+
"risk_ratio_threshold", 0.75
|
|
464
|
+
)
|
|
465
|
+
)
|
|
466
|
+
overloaded_conf["confidence_filtering"]["bounds_range_threshold"] = (
|
|
467
|
+
overloaded_conf["confidence_filtering"].get(
|
|
468
|
+
"bounds_range_threshold", 3
|
|
469
|
+
)
|
|
470
|
+
)
|
|
471
|
+
overloaded_conf["confidence_filtering"]["risk_range_threshold"] = (
|
|
472
|
+
overloaded_conf["confidence_filtering"].get(
|
|
473
|
+
"risk_range_threshold", 9
|
|
474
|
+
)
|
|
475
|
+
)
|
|
476
|
+
overloaded_conf["confidence_filtering"]["nan_threshold"] = (
|
|
477
|
+
overloaded_conf["confidence_filtering"].get("nan_threshold", 0.2)
|
|
478
|
+
)
|
|
479
|
+
overloaded_conf["confidence_filtering"]["win_nanratio"] = (
|
|
480
|
+
overloaded_conf["confidence_filtering"].get("win_nanratio", 20)
|
|
481
|
+
)
|
|
482
|
+
|
|
483
|
+
confidence_filtering_schema = {
|
|
484
|
+
"activated": bool,
|
|
485
|
+
"bounds_ratio_threshold": float,
|
|
486
|
+
"risk_ratio_threshold": float,
|
|
487
|
+
"bounds_range_threshold": int,
|
|
488
|
+
"risk_range_threshold": int,
|
|
489
|
+
"nan_threshold": float,
|
|
490
|
+
"win_nanratio": int,
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
checker_confidence_filtering_schema = Checker(
|
|
494
|
+
confidence_filtering_schema
|
|
495
|
+
)
|
|
496
|
+
checker_confidence_filtering_schema.validate(
|
|
497
|
+
overloaded_conf["confidence_filtering"]
|
|
498
|
+
)
|
|
499
|
+
|
|
500
|
+
@cars_profile(name="Get margin fun")
|
|
501
|
+
def get_margins_fun(self, grid_left, disp_range_grid):
|
|
502
|
+
"""
|
|
503
|
+
Get Margins function that generates margins needed by
|
|
504
|
+
matching method, to use during resampling
|
|
505
|
+
|
|
506
|
+
:param grid_left: left epipolar grid
|
|
507
|
+
:type grid_left: dict
|
|
508
|
+
:param disp_range_grid: minimum and maximum disparity grid
|
|
509
|
+
:return: function that generates margin for given roi
|
|
510
|
+
|
|
511
|
+
"""
|
|
512
|
+
|
|
513
|
+
disp_min_grid_arr, _ = inputs.rasterio_read_as_array(
|
|
514
|
+
disp_range_grid["grid_min_path"]
|
|
515
|
+
)
|
|
516
|
+
disp_max_grid_arr, _ = inputs.rasterio_read_as_array(
|
|
517
|
+
disp_range_grid["grid_max_path"]
|
|
518
|
+
)
|
|
519
|
+
step_row = disp_range_grid["step_row"]
|
|
520
|
+
step_col = disp_range_grid["step_col"]
|
|
521
|
+
row_range = disp_range_grid["row_range"]
|
|
522
|
+
col_range = disp_range_grid["col_range"]
|
|
523
|
+
|
|
524
|
+
# get disp_to_alt_ratio
|
|
525
|
+
disp_to_alt_ratio = grid_left["disp_to_alt_ratio"]
|
|
526
|
+
|
|
527
|
+
# Check if we need to override disp_min
|
|
528
|
+
if self.min_elevation_offset is not None:
|
|
529
|
+
user_disp_min = self.min_elevation_offset / disp_to_alt_ratio
|
|
530
|
+
if np.any(disp_min_grid_arr < user_disp_min):
|
|
531
|
+
logging.warning(
|
|
532
|
+
(
|
|
533
|
+
"Overridden disparity minimum "
|
|
534
|
+
"= {:.3f} pix. (= {:.3f} m.) "
|
|
535
|
+
"is greater than disparity minimum estimated "
|
|
536
|
+
"in prepare step "
|
|
537
|
+
"for current pair"
|
|
538
|
+
).format(
|
|
539
|
+
user_disp_min,
|
|
540
|
+
self.min_elevation_offset,
|
|
541
|
+
)
|
|
542
|
+
)
|
|
543
|
+
disp_min_grid_arr[:, :] = user_disp_min
|
|
544
|
+
|
|
545
|
+
# Check if we need to override disp_max
|
|
546
|
+
if self.max_elevation_offset is not None:
|
|
547
|
+
user_disp_max = self.max_elevation_offset / disp_to_alt_ratio
|
|
548
|
+
if np.any(disp_max_grid_arr > user_disp_max):
|
|
549
|
+
logging.warning(
|
|
550
|
+
(
|
|
551
|
+
"Overridden disparity maximum "
|
|
552
|
+
"= {:.3f} pix. (or {:.3f} m.) "
|
|
553
|
+
"is lower than disparity maximum estimated "
|
|
554
|
+
"in prepare step "
|
|
555
|
+
"for current pair"
|
|
556
|
+
).format(
|
|
557
|
+
user_disp_max,
|
|
558
|
+
self.max_elevation_offset,
|
|
559
|
+
)
|
|
560
|
+
)
|
|
561
|
+
disp_max_grid_arr[:, :] = user_disp_max
|
|
562
|
+
|
|
563
|
+
# Compute global range of logging
|
|
564
|
+
disp_min_global = np.min(disp_min_grid_arr)
|
|
565
|
+
disp_max_global = np.max(disp_max_grid_arr)
|
|
566
|
+
|
|
567
|
+
logging.info(
|
|
568
|
+
"Global Disparity range for current pair: "
|
|
569
|
+
"[{:.3f} pix., {:.3f} pix.] "
|
|
570
|
+
"(or [{:.3f} m., {:.3f} m.])".format(
|
|
571
|
+
disp_min_global,
|
|
572
|
+
disp_max_global,
|
|
573
|
+
disp_min_global * disp_to_alt_ratio,
|
|
574
|
+
disp_max_global * disp_to_alt_ratio,
|
|
575
|
+
)
|
|
576
|
+
)
|
|
577
|
+
|
|
578
|
+
def margins_wrapper(row_min, row_max, col_min, col_max):
|
|
579
|
+
"""
|
|
580
|
+
Generates margins Dataset used in resampling
|
|
581
|
+
|
|
582
|
+
:param row_min: row min
|
|
583
|
+
:param row_max: row max
|
|
584
|
+
:param col_min: col min
|
|
585
|
+
:param col_max: col max
|
|
586
|
+
|
|
587
|
+
:return: margins
|
|
588
|
+
:rtype: xr.Dataset
|
|
589
|
+
"""
|
|
590
|
+
|
|
591
|
+
assert row_min < row_max
|
|
592
|
+
assert col_min < col_max
|
|
593
|
+
|
|
594
|
+
# Get region in grid
|
|
595
|
+
|
|
596
|
+
grid_row_min = max(0, int(np.floor((row_min - 1) / step_row)) - 1)
|
|
597
|
+
grid_row_max = min(
|
|
598
|
+
len(row_range), int(np.ceil((row_max + 1) / step_row) + 1)
|
|
599
|
+
)
|
|
600
|
+
grid_col_min = max(0, int(np.floor((col_min - 1) / step_col)) - 1)
|
|
601
|
+
grid_col_max = min(
|
|
602
|
+
len(col_range), int(np.ceil((col_max + 1) / step_col)) + 1
|
|
603
|
+
)
|
|
604
|
+
|
|
605
|
+
# Compute disp min and max in row
|
|
606
|
+
disp_min = np.min(
|
|
607
|
+
disp_min_grid_arr[
|
|
608
|
+
grid_row_min:grid_row_max, grid_col_min:grid_col_max
|
|
609
|
+
]
|
|
610
|
+
)
|
|
611
|
+
disp_max = np.max(
|
|
612
|
+
disp_max_grid_arr[
|
|
613
|
+
grid_row_min:grid_row_max, grid_col_min:grid_col_max
|
|
614
|
+
]
|
|
615
|
+
)
|
|
616
|
+
# round disp min and max
|
|
617
|
+
disp_min = int(math.floor(disp_min))
|
|
618
|
+
disp_max = int(math.ceil(disp_max))
|
|
619
|
+
|
|
620
|
+
# Compute margins for the correlator
|
|
621
|
+
margins = dm_wrappers.get_margins(self.margins, disp_min, disp_max)
|
|
622
|
+
|
|
623
|
+
return margins
|
|
624
|
+
|
|
625
|
+
return margins_wrapper
|
|
626
|
+
|
|
627
|
+
def get_method(self):
|
|
628
|
+
"""
|
|
629
|
+
return the method that will be used in dense_matching
|
|
630
|
+
"""
|
|
631
|
+
|
|
632
|
+
return self.used_method
|
|
633
|
+
|
|
634
|
+
@cars_profile(name="Optimal size estimation")
|
|
635
|
+
def get_optimal_tile_size(self, disp_range_grid, max_ram_per_worker):
|
|
636
|
+
"""
|
|
637
|
+
Get the optimal tile size to use during dense matching.
|
|
638
|
+
|
|
639
|
+
:param disp_range_grid: minimum and maximum disparity grid
|
|
640
|
+
:param max_ram_per_worker: maximum ram per worker
|
|
641
|
+
:return: optimal tile size
|
|
642
|
+
|
|
643
|
+
"""
|
|
644
|
+
|
|
645
|
+
disp_min_grids, _ = inputs.rasterio_read_as_array(
|
|
646
|
+
disp_range_grid["grid_min_path"]
|
|
647
|
+
)
|
|
648
|
+
disp_max_grids, _ = inputs.rasterio_read_as_array(
|
|
649
|
+
disp_range_grid["grid_max_path"]
|
|
650
|
+
)
|
|
651
|
+
|
|
652
|
+
# use max tile size as overlap for min and max:
|
|
653
|
+
# max Point to point diff is less than diff of tile
|
|
654
|
+
|
|
655
|
+
# use filter of size max_epi_tile_size
|
|
656
|
+
overlap = 3 * int(self.max_epi_tile_size / self.local_disp_grid_step)
|
|
657
|
+
|
|
658
|
+
disp_min_grids = minimum_filter(
|
|
659
|
+
disp_min_grids, size=[overlap, overlap], mode="nearest"
|
|
660
|
+
)
|
|
661
|
+
disp_max_grids = maximum_filter(
|
|
662
|
+
disp_max_grids, size=[overlap, overlap], mode="nearest"
|
|
663
|
+
)
|
|
664
|
+
|
|
665
|
+
# Worst cases scenario:
|
|
666
|
+
# 1: [global max - max diff, global max]
|
|
667
|
+
# 2: [global min, global min max diff]
|
|
668
|
+
|
|
669
|
+
max_diff = np.round(np.nanmax(disp_max_grids - disp_min_grids)) + 1
|
|
670
|
+
global_min = np.floor(np.nanmin(disp_min_grids))
|
|
671
|
+
global_max = np.ceil(np.nanmax(disp_max_grids))
|
|
672
|
+
|
|
673
|
+
# Get tiling param
|
|
674
|
+
opt_epipolar_tile_size_1 = (
|
|
675
|
+
dm_wrappers.optimal_tile_size_pandora_plugin_libsgm(
|
|
676
|
+
global_min,
|
|
677
|
+
global_min + max_diff,
|
|
678
|
+
self.min_epi_tile_size,
|
|
679
|
+
self.max_epi_tile_size,
|
|
680
|
+
max_ram_per_worker,
|
|
681
|
+
margin=self.epipolar_tile_margin_in_percent,
|
|
682
|
+
)
|
|
683
|
+
)
|
|
684
|
+
opt_epipolar_tile_size_2 = (
|
|
685
|
+
dm_wrappers.optimal_tile_size_pandora_plugin_libsgm(
|
|
686
|
+
global_max - max_diff,
|
|
687
|
+
global_max,
|
|
688
|
+
self.min_epi_tile_size,
|
|
689
|
+
self.max_epi_tile_size,
|
|
690
|
+
max_ram_per_worker,
|
|
691
|
+
margin=self.epipolar_tile_margin_in_percent,
|
|
692
|
+
)
|
|
693
|
+
)
|
|
694
|
+
|
|
695
|
+
# return worst case
|
|
696
|
+
opt_epipolar_tile_size = min(
|
|
697
|
+
opt_epipolar_tile_size_1, opt_epipolar_tile_size_2
|
|
698
|
+
)
|
|
699
|
+
|
|
700
|
+
# Define function to compute local optimal size for each tile
|
|
701
|
+
def local_tile_optimal_size_fun(local_disp_min, local_disp_max):
|
|
702
|
+
"""
|
|
703
|
+
Compute optimal tile size for tile
|
|
704
|
+
|
|
705
|
+
:return: local tile size, global optimal tile sizes
|
|
706
|
+
|
|
707
|
+
"""
|
|
708
|
+
local_opt_tile_size = (
|
|
709
|
+
dm_wrappers.optimal_tile_size_pandora_plugin_libsgm(
|
|
710
|
+
local_disp_min,
|
|
711
|
+
local_disp_max,
|
|
712
|
+
0,
|
|
713
|
+
20000, # arbitrary
|
|
714
|
+
max_ram_per_worker,
|
|
715
|
+
margin=self.epipolar_tile_margin_in_percent,
|
|
716
|
+
)
|
|
717
|
+
)
|
|
718
|
+
|
|
719
|
+
# Get max range to use with current optimal size
|
|
720
|
+
max_range = dm_wrappers.get_max_disp_from_opt_tile_size(
|
|
721
|
+
opt_epipolar_tile_size,
|
|
722
|
+
max_ram_per_worker,
|
|
723
|
+
margin=self.epipolar_tile_margin_in_percent,
|
|
724
|
+
used_disparity_range=(local_disp_max - local_disp_min),
|
|
725
|
+
)
|
|
726
|
+
|
|
727
|
+
return local_opt_tile_size, opt_epipolar_tile_size, max_range
|
|
728
|
+
|
|
729
|
+
return opt_epipolar_tile_size, local_tile_optimal_size_fun
|
|
730
|
+
|
|
731
|
+
def get_required_bands(self):
|
|
732
|
+
"""
|
|
733
|
+
Get bands required by this application
|
|
734
|
+
|
|
735
|
+
:return: required bands for left and right image
|
|
736
|
+
:rtype: dict
|
|
737
|
+
"""
|
|
738
|
+
required_bands = {}
|
|
739
|
+
required_bands["left"] = self.required_bands
|
|
740
|
+
required_bands["right"] = self.required_bands
|
|
741
|
+
return required_bands
|
|
742
|
+
|
|
743
|
+
# pylint: disable=too-many-positional-arguments
|
|
744
|
+
@cars_profile(name="Disp Grid Generation")
|
|
745
|
+
def generate_disparity_grids( # noqa: C901
|
|
746
|
+
self,
|
|
747
|
+
sensor_image_right,
|
|
748
|
+
grid_right,
|
|
749
|
+
geom_plugin_with_dem_and_geoid,
|
|
750
|
+
dmin=None,
|
|
751
|
+
dmax=None,
|
|
752
|
+
dem_min=None,
|
|
753
|
+
dem_max=None,
|
|
754
|
+
pair_folder=None,
|
|
755
|
+
orchestrator=None,
|
|
756
|
+
):
|
|
757
|
+
"""
|
|
758
|
+
Generate disparity grids min and max, with given step
|
|
759
|
+
|
|
760
|
+
global mode: uses dmin and dmax
|
|
761
|
+
local mode: uses dems
|
|
762
|
+
|
|
763
|
+
|
|
764
|
+
:param sensor_image_right: sensor image right
|
|
765
|
+
:type sensor_image_right: dict
|
|
766
|
+
:param grid_right: right epipolar grid
|
|
767
|
+
:type grid_right: dict
|
|
768
|
+
:param geom_plugin_with_dem_and_geoid: geometry plugin with dem mean
|
|
769
|
+
used to generate epipolar grids
|
|
770
|
+
:type geom_plugin_with_dem_and_geoid: GeometryPlugin
|
|
771
|
+
:param dmin: minimum disparity
|
|
772
|
+
:type dmin: float
|
|
773
|
+
:param dmax: maximum disparity
|
|
774
|
+
:type dmax: float
|
|
775
|
+
:param dem_min: path to minimum dem
|
|
776
|
+
:type dem_min: str
|
|
777
|
+
:param dem_max: path to maximum dem
|
|
778
|
+
:type dem_max: str
|
|
779
|
+
:param pair_folder: folder used for current pair
|
|
780
|
+
:type pair_folder: str
|
|
781
|
+
:param orchestrator: orchestrator
|
|
782
|
+
:type orchestrator: Orchestrator
|
|
783
|
+
|
|
784
|
+
|
|
785
|
+
:return disparity grid range, containing grid min and max
|
|
786
|
+
:rtype: CarsDataset
|
|
787
|
+
"""
|
|
788
|
+
|
|
789
|
+
# Default orchestrator
|
|
790
|
+
if orchestrator is None:
|
|
791
|
+
# Create default sequential orchestrator for current application
|
|
792
|
+
# be aware, no out_json will be shared between orchestrators
|
|
793
|
+
# No files saved
|
|
794
|
+
self.orchestrator = ocht.Orchestrator(
|
|
795
|
+
orchestrator_conf={"mode": "sequential"}
|
|
796
|
+
)
|
|
797
|
+
else:
|
|
798
|
+
self.orchestrator = orchestrator
|
|
799
|
+
|
|
800
|
+
epi_size_row = grid_right["epipolar_size_y"]
|
|
801
|
+
epi_size_col = grid_right["epipolar_size_x"]
|
|
802
|
+
disp_to_alt_ratio = grid_right["disp_to_alt_ratio"]
|
|
803
|
+
|
|
804
|
+
# Generate grid array
|
|
805
|
+
nb_rows = int(epi_size_row / self.local_disp_grid_step) + 1
|
|
806
|
+
nb_cols = int(epi_size_col / self.local_disp_grid_step) + 1
|
|
807
|
+
row_range, step_row = np.linspace(
|
|
808
|
+
0, epi_size_row, nb_rows, retstep=True
|
|
809
|
+
)
|
|
810
|
+
col_range, step_col = np.linspace(
|
|
811
|
+
0, epi_size_col, nb_cols, retstep=True
|
|
812
|
+
)
|
|
813
|
+
|
|
814
|
+
# Create CarsDataset
|
|
815
|
+
grid_disp_range = cars_dataset.CarsDataset(
|
|
816
|
+
"arrays", name="grid_disp_range_unknown_pair"
|
|
817
|
+
)
|
|
818
|
+
global_infos_cars_ds = cars_dataset.CarsDataset("dict")
|
|
819
|
+
|
|
820
|
+
# Generate profile
|
|
821
|
+
raster_profile = DefaultGTiffProfile(count=1)
|
|
822
|
+
|
|
823
|
+
# saving infos
|
|
824
|
+
# disp grids
|
|
825
|
+
|
|
826
|
+
if self.save_intermediate_data:
|
|
827
|
+
grid_min_path = os.path.join(pair_folder, "disp_min_grid.tif")
|
|
828
|
+
grid_max_path = os.path.join(pair_folder, "disp_max_grid.tif")
|
|
829
|
+
safe_makedirs(pair_folder)
|
|
830
|
+
else:
|
|
831
|
+
if pair_folder is None:
|
|
832
|
+
tmp_folder = os.path.join(self.orchestrator.out_dir, "tmp")
|
|
833
|
+
else:
|
|
834
|
+
tmp_folder = os.path.join(pair_folder, "tmp")
|
|
835
|
+
safe_makedirs(tmp_folder)
|
|
836
|
+
self.orchestrator.add_to_clean(tmp_folder)
|
|
837
|
+
grid_min_path = os.path.join(tmp_folder, "disp_min_grid.tif")
|
|
838
|
+
grid_max_path = os.path.join(tmp_folder, "disp_max_grid.tif")
|
|
839
|
+
|
|
840
|
+
if None not in (dmin, dmax):
|
|
841
|
+
# use global disparity range
|
|
842
|
+
if None not in (dem_min, dem_max):
|
|
843
|
+
raise RuntimeError("Mix between local and global mode")
|
|
844
|
+
|
|
845
|
+
# Only one tile
|
|
846
|
+
grid_disp_range.tiling_grid = np.array([[[0, nb_rows, 0, nb_cols]]])
|
|
847
|
+
|
|
848
|
+
elif None not in (dem_min, dem_max):
|
|
849
|
+
|
|
850
|
+
# Generate multiple tiles
|
|
851
|
+
grid_tile_size = self.epi_disp_grid_tile_size
|
|
852
|
+
grid_disp_range.tiling_grid = tiling.generate_tiling_grid(
|
|
853
|
+
0,
|
|
854
|
+
0,
|
|
855
|
+
nb_rows,
|
|
856
|
+
nb_cols,
|
|
857
|
+
grid_tile_size,
|
|
858
|
+
grid_tile_size,
|
|
859
|
+
)
|
|
860
|
+
|
|
861
|
+
# add tiling of global_infos_cars_ds
|
|
862
|
+
global_infos_cars_ds.tiling_grid = grid_disp_range.tiling_grid
|
|
863
|
+
self.orchestrator.add_to_replace_lists(
|
|
864
|
+
global_infos_cars_ds,
|
|
865
|
+
cars_ds_name="global infos",
|
|
866
|
+
)
|
|
867
|
+
|
|
868
|
+
self.orchestrator.add_to_save_lists(
|
|
869
|
+
grid_min_path,
|
|
870
|
+
dm_cst.DISP_MIN_GRID,
|
|
871
|
+
grid_disp_range,
|
|
872
|
+
dtype=np.float32,
|
|
873
|
+
nodata=0,
|
|
874
|
+
cars_ds_name="disp_min_grid",
|
|
875
|
+
)
|
|
876
|
+
|
|
877
|
+
self.orchestrator.add_to_save_lists(
|
|
878
|
+
grid_max_path,
|
|
879
|
+
dm_cst.DISP_MAX_GRID,
|
|
880
|
+
grid_disp_range,
|
|
881
|
+
dtype=np.float32,
|
|
882
|
+
nodata=0,
|
|
883
|
+
cars_ds_name="disp_max_grid",
|
|
884
|
+
)
|
|
885
|
+
[saving_info] = ( # pylint: disable=unbalanced-tuple-unpacking
|
|
886
|
+
self.orchestrator.get_saving_infos([grid_disp_range])
|
|
887
|
+
)
|
|
888
|
+
[saving_info_global_infos] = self.orchestrator.get_saving_infos(
|
|
889
|
+
[global_infos_cars_ds]
|
|
890
|
+
)
|
|
891
|
+
|
|
892
|
+
# Generate grids on dict format
|
|
893
|
+
grid_disp_range_dict = {
|
|
894
|
+
"grid_min_path": grid_min_path,
|
|
895
|
+
"grid_max_path": grid_max_path,
|
|
896
|
+
"global_min": None,
|
|
897
|
+
"global_max": None,
|
|
898
|
+
"step_row": step_row,
|
|
899
|
+
"step_col": step_col,
|
|
900
|
+
"row_range": row_range,
|
|
901
|
+
"col_range": col_range,
|
|
902
|
+
}
|
|
903
|
+
|
|
904
|
+
if None not in (dmin, dmax):
|
|
905
|
+
# use global disparity range
|
|
906
|
+
if None not in (dem_min, dem_max):
|
|
907
|
+
raise RuntimeError("Mix between local and global mode")
|
|
908
|
+
|
|
909
|
+
saving_info_global_infos_full = ocht.update_saving_infos(
|
|
910
|
+
saving_info_global_infos, row=0, col=0
|
|
911
|
+
)
|
|
912
|
+
saving_info_full = ocht.update_saving_infos(
|
|
913
|
+
saving_info, row=0, col=0
|
|
914
|
+
)
|
|
915
|
+
|
|
916
|
+
(
|
|
917
|
+
grid_disp_range[0, 0],
|
|
918
|
+
global_infos_cars_ds[0, 0],
|
|
919
|
+
) = self.orchestrator.cluster.create_task(
|
|
920
|
+
generate_disp_range_const_tile_wrapper, nout=2
|
|
921
|
+
)(
|
|
922
|
+
row_range,
|
|
923
|
+
col_range,
|
|
924
|
+
dmin,
|
|
925
|
+
dmax,
|
|
926
|
+
raster_profile,
|
|
927
|
+
saving_info_full,
|
|
928
|
+
saving_info_global_infos_full,
|
|
929
|
+
)
|
|
930
|
+
|
|
931
|
+
elif None not in (dem_min, dem_max):
|
|
932
|
+
|
|
933
|
+
# use filter to propagate min and max
|
|
934
|
+
filter_overlap = (
|
|
935
|
+
2
|
|
936
|
+
* int(
|
|
937
|
+
self.disp_range_propagation_filter_size
|
|
938
|
+
/ self.local_disp_grid_step
|
|
939
|
+
)
|
|
940
|
+
+ 1
|
|
941
|
+
)
|
|
942
|
+
|
|
943
|
+
for col in range(grid_disp_range.shape[1]):
|
|
944
|
+
for row in range(grid_disp_range.shape[0]):
|
|
945
|
+
# update saving infos for potential replacement
|
|
946
|
+
full_saving_info = ocht.update_saving_infos(
|
|
947
|
+
saving_info, row=row, col=col
|
|
948
|
+
)
|
|
949
|
+
saving_info_global_infos_full = ocht.update_saving_infos(
|
|
950
|
+
saving_info_global_infos, row=row, col=col
|
|
951
|
+
)
|
|
952
|
+
array_window = grid_disp_range.get_window_as_dict(row, col)
|
|
953
|
+
(
|
|
954
|
+
grid_disp_range[row, col],
|
|
955
|
+
global_infos_cars_ds[row, col],
|
|
956
|
+
) = self.orchestrator.cluster.create_task(
|
|
957
|
+
generate_disp_range_from_dem_wrapper, nout=2
|
|
958
|
+
)(
|
|
959
|
+
array_window,
|
|
960
|
+
row_range,
|
|
961
|
+
col_range,
|
|
962
|
+
sensor_image_right,
|
|
963
|
+
grid_right,
|
|
964
|
+
geom_plugin_with_dem_and_geoid,
|
|
965
|
+
dem_min,
|
|
966
|
+
dem_max,
|
|
967
|
+
raster_profile,
|
|
968
|
+
full_saving_info,
|
|
969
|
+
saving_info_global_infos_full,
|
|
970
|
+
filter_overlap,
|
|
971
|
+
disp_to_alt_ratio,
|
|
972
|
+
disp_min_threshold=self.disp_min_threshold,
|
|
973
|
+
disp_max_threshold=self.disp_max_threshold,
|
|
974
|
+
)
|
|
975
|
+
|
|
976
|
+
# Compute grid
|
|
977
|
+
self.orchestrator.breakpoint()
|
|
978
|
+
|
|
979
|
+
# Fill global infos
|
|
980
|
+
mins, maxs = [], []
|
|
981
|
+
for row in range(global_infos_cars_ds.shape[0]):
|
|
982
|
+
for col in range(global_infos_cars_ds.shape[1]):
|
|
983
|
+
try:
|
|
984
|
+
dict_data = global_infos_cars_ds[row, col].data
|
|
985
|
+
mins.append(dict_data["global_min"])
|
|
986
|
+
maxs.append(dict_data["global_max"])
|
|
987
|
+
except Exception:
|
|
988
|
+
logging.info(
|
|
989
|
+
"Tile {} {} not computed in epi disp range"
|
|
990
|
+
" generation".format(row, col)
|
|
991
|
+
)
|
|
992
|
+
grid_disp_range_dict["global_min"] = np.floor(np.nanmin(mins))
|
|
993
|
+
grid_disp_range_dict["global_max"] = np.ceil(np.nanmax(maxs))
|
|
994
|
+
|
|
995
|
+
return grid_disp_range_dict
|
|
996
|
+
|
|
997
|
+
def run( # pylint: disable=too-many-positional-arguments
|
|
998
|
+
self,
|
|
999
|
+
epipolar_images_left,
|
|
1000
|
+
epipolar_images_right,
|
|
1001
|
+
local_tile_optimal_size_fun,
|
|
1002
|
+
orchestrator=None,
|
|
1003
|
+
pair_folder=None,
|
|
1004
|
+
pair_key="PAIR_0",
|
|
1005
|
+
disp_range_grid=None,
|
|
1006
|
+
compute_disparity_masks=False,
|
|
1007
|
+
margins_to_keep=0,
|
|
1008
|
+
texture_bands=None,
|
|
1009
|
+
classif_bands_to_mask=None,
|
|
1010
|
+
):
|
|
1011
|
+
"""
|
|
1012
|
+
Run Matching application.
|
|
1013
|
+
|
|
1014
|
+
Create CarsDataset filled with xarray.Dataset, corresponding
|
|
1015
|
+
to epipolar disparities, on the same geometry than
|
|
1016
|
+
epipolar_images_left.
|
|
1017
|
+
|
|
1018
|
+
:param epipolar_images_left: tiled left epipolar CarsDataset contains:
|
|
1019
|
+
|
|
1020
|
+
- N x M Delayed tiles. \
|
|
1021
|
+
Each tile will be a future xarray Dataset containing:
|
|
1022
|
+
|
|
1023
|
+
- data with keys : "im", "msk", "texture"
|
|
1024
|
+
- attrs with keys: "margins" with "disp_min" and "disp_max"\
|
|
1025
|
+
"transform", "crs", "valid_pixels", "no_data_mask",\
|
|
1026
|
+
"no_data_img"
|
|
1027
|
+
- attributes containing:
|
|
1028
|
+
"largest_epipolar_region","opt_epipolar_tile_size"
|
|
1029
|
+
:type epipolar_images_left: CarsDataset
|
|
1030
|
+
:param epipolar_images_right: tiled right epipolar CarsDataset contains:
|
|
1031
|
+
|
|
1032
|
+
- N x M Delayed tiles. \
|
|
1033
|
+
Each tile will be a future xarray Dataset containing:
|
|
1034
|
+
|
|
1035
|
+
- data with keys : "im", "msk", "texture"
|
|
1036
|
+
- attrs with keys: "margins" with "disp_min" and "disp_max"
|
|
1037
|
+
"transform", "crs", "valid_pixels", "no_data_mask",
|
|
1038
|
+
"no_data_img"
|
|
1039
|
+
- attributes containing:
|
|
1040
|
+
"largest_epipolar_region","opt_epipolar_tile_size"
|
|
1041
|
+
:type epipolar_images_right: CarsDataset
|
|
1042
|
+
:param local_tile_optimal_size_fun: function to compute local
|
|
1043
|
+
optimal tile size
|
|
1044
|
+
:type local_tile_optimal_size_fun: func
|
|
1045
|
+
:param orchestrator: orchestrator used
|
|
1046
|
+
:param pair_folder: folder used for current pair
|
|
1047
|
+
:type pair_folder: str
|
|
1048
|
+
:param pair_key: pair id
|
|
1049
|
+
:type pair_key: str
|
|
1050
|
+
:param disp_range_grid: minimum and maximum disparity grid
|
|
1051
|
+
:type disp_range_grid: CarsDataset
|
|
1052
|
+
:param margins_to_keep: margin to keep after dense matching
|
|
1053
|
+
:type margins_to_keep: int
|
|
1054
|
+
:param texture_bands: indices of bands from epipolar_images_left
|
|
1055
|
+
used for output texture
|
|
1056
|
+
:type texture_bands: list
|
|
1057
|
+
:param classif_bands_to_mask: bands from classif to mask
|
|
1058
|
+
:type classif_bands_to_mask: list of str / int
|
|
1059
|
+
|
|
1060
|
+
:return: disparity map: \
|
|
1061
|
+
The CarsDataset contains:
|
|
1062
|
+
|
|
1063
|
+
- N x M Delayed tiles.\
|
|
1064
|
+
Each tile will be a future xarray Dataset containing:
|
|
1065
|
+
- data with keys : "disp", "disp_msk"
|
|
1066
|
+
- attrs with keys: profile, window, overlaps
|
|
1067
|
+
- attributes containing:
|
|
1068
|
+
"largest_epipolar_region","opt_epipolar_tile_size",
|
|
1069
|
+
"disp_min_tiling", "disp_max_tiling"
|
|
1070
|
+
|
|
1071
|
+
:rtype: CarsDataset
|
|
1072
|
+
"""
|
|
1073
|
+
# Default orchestrator
|
|
1074
|
+
if orchestrator is None:
|
|
1075
|
+
# Create default sequential orchestrator for current application
|
|
1076
|
+
# be awere, no out_json will be shared between orchestrators
|
|
1077
|
+
# No files saved
|
|
1078
|
+
self.orchestrator = ocht.Orchestrator(
|
|
1079
|
+
orchestrator_conf={"mode": "sequential"}
|
|
1080
|
+
)
|
|
1081
|
+
else:
|
|
1082
|
+
self.orchestrator = orchestrator
|
|
1083
|
+
|
|
1084
|
+
if pair_folder is None:
|
|
1085
|
+
pair_folder = os.path.join(self.orchestrator.out_dir, "tmp")
|
|
1086
|
+
|
|
1087
|
+
if epipolar_images_left.dataset_type == "arrays":
|
|
1088
|
+
# Create CarsDataset
|
|
1089
|
+
# Epipolar_disparity
|
|
1090
|
+
epipolar_disparity_map = cars_dataset.CarsDataset(
|
|
1091
|
+
"arrays", name="dense_matching_" + pair_key
|
|
1092
|
+
)
|
|
1093
|
+
epipolar_disparity_map.create_empty_copy(epipolar_images_left)
|
|
1094
|
+
# Modify overlaps
|
|
1095
|
+
epipolar_disparity_map.overlaps = (
|
|
1096
|
+
format_transformation.reduce_overlap(
|
|
1097
|
+
epipolar_disparity_map.overlaps, margins_to_keep
|
|
1098
|
+
)
|
|
1099
|
+
)
|
|
1100
|
+
|
|
1101
|
+
# Update attributes to get epipolar info
|
|
1102
|
+
epipolar_disparity_map.attributes.update(
|
|
1103
|
+
epipolar_images_left.attributes
|
|
1104
|
+
)
|
|
1105
|
+
|
|
1106
|
+
# Save disparity maps
|
|
1107
|
+
if self.save_intermediate_data:
|
|
1108
|
+
self.orchestrator.add_to_save_lists(
|
|
1109
|
+
os.path.join(pair_folder, "epi_disp.tif"),
|
|
1110
|
+
cst_disp.MAP,
|
|
1111
|
+
epipolar_disparity_map,
|
|
1112
|
+
cars_ds_name="epi_disp",
|
|
1113
|
+
nodata=-9999,
|
|
1114
|
+
)
|
|
1115
|
+
|
|
1116
|
+
self.orchestrator.add_to_save_lists(
|
|
1117
|
+
os.path.join(pair_folder, "epi_disp_image.tif"),
|
|
1118
|
+
cst.EPI_TEXTURE,
|
|
1119
|
+
epipolar_disparity_map,
|
|
1120
|
+
cars_ds_name="epi_disp_color",
|
|
1121
|
+
)
|
|
1122
|
+
|
|
1123
|
+
self.orchestrator.add_to_save_lists(
|
|
1124
|
+
os.path.join(pair_folder, "epi_disp_mask.tif"),
|
|
1125
|
+
cst_disp.VALID,
|
|
1126
|
+
epipolar_disparity_map,
|
|
1127
|
+
dtype=np.uint8,
|
|
1128
|
+
cars_ds_name="epi_disp_mask",
|
|
1129
|
+
optional_data=True,
|
|
1130
|
+
)
|
|
1131
|
+
|
|
1132
|
+
self.orchestrator.add_to_save_lists(
|
|
1133
|
+
os.path.join(pair_folder, "epi_disp_classif.tif"),
|
|
1134
|
+
cst.EPI_CLASSIFICATION,
|
|
1135
|
+
epipolar_disparity_map,
|
|
1136
|
+
dtype=np.uint8,
|
|
1137
|
+
cars_ds_name="epi_disp_classif",
|
|
1138
|
+
optional_data=True,
|
|
1139
|
+
)
|
|
1140
|
+
|
|
1141
|
+
self.orchestrator.add_to_save_lists(
|
|
1142
|
+
os.path.join(
|
|
1143
|
+
pair_folder,
|
|
1144
|
+
"epi_confidence.tif",
|
|
1145
|
+
),
|
|
1146
|
+
cst_disp.CONFIDENCE,
|
|
1147
|
+
epipolar_disparity_map,
|
|
1148
|
+
cars_ds_name="confidence",
|
|
1149
|
+
optional_data=True,
|
|
1150
|
+
)
|
|
1151
|
+
|
|
1152
|
+
# disparity grids
|
|
1153
|
+
self.orchestrator.add_to_save_lists(
|
|
1154
|
+
os.path.join(
|
|
1155
|
+
pair_folder,
|
|
1156
|
+
"epi_disp_min.tif",
|
|
1157
|
+
),
|
|
1158
|
+
cst_disp.EPI_DISP_MIN_GRID,
|
|
1159
|
+
epipolar_disparity_map,
|
|
1160
|
+
cars_ds_name="disp_min",
|
|
1161
|
+
)
|
|
1162
|
+
self.orchestrator.add_to_save_lists(
|
|
1163
|
+
os.path.join(
|
|
1164
|
+
pair_folder,
|
|
1165
|
+
"epi_disp_max.tif",
|
|
1166
|
+
),
|
|
1167
|
+
cst_disp.EPI_DISP_MAX_GRID,
|
|
1168
|
+
epipolar_disparity_map,
|
|
1169
|
+
cars_ds_name="disp_max",
|
|
1170
|
+
)
|
|
1171
|
+
self.orchestrator.add_to_save_lists(
|
|
1172
|
+
os.path.join(
|
|
1173
|
+
pair_folder,
|
|
1174
|
+
"epi_disp_filling.tif",
|
|
1175
|
+
),
|
|
1176
|
+
cst_disp.FILLING,
|
|
1177
|
+
epipolar_disparity_map,
|
|
1178
|
+
dtype=np.uint8,
|
|
1179
|
+
cars_ds_name="epi_disp_filling",
|
|
1180
|
+
nodata=255,
|
|
1181
|
+
)
|
|
1182
|
+
|
|
1183
|
+
# Get saving infos in order to save tiles when they are computed
|
|
1184
|
+
[saving_info] = self.orchestrator.get_saving_infos(
|
|
1185
|
+
[epipolar_disparity_map]
|
|
1186
|
+
)
|
|
1187
|
+
|
|
1188
|
+
# Add infos to orchestrator.out_json
|
|
1189
|
+
updating_dict = {
|
|
1190
|
+
application_constants.APPLICATION_TAG: {
|
|
1191
|
+
dm_cst.DENSE_MATCHING_RUN_TAG: {
|
|
1192
|
+
pair_key: {
|
|
1193
|
+
"global_disp_min": disp_range_grid["global_min"],
|
|
1194
|
+
"global_disp_max": disp_range_grid["global_max"],
|
|
1195
|
+
},
|
|
1196
|
+
},
|
|
1197
|
+
}
|
|
1198
|
+
}
|
|
1199
|
+
self.orchestrator.update_out_info(updating_dict)
|
|
1200
|
+
logging.info(
|
|
1201
|
+
"Compute disparity: number tiles: {}".format(
|
|
1202
|
+
epipolar_disparity_map.shape[1]
|
|
1203
|
+
* epipolar_disparity_map.shape[0]
|
|
1204
|
+
)
|
|
1205
|
+
)
|
|
1206
|
+
|
|
1207
|
+
nb_total_tiles_roi = 0
|
|
1208
|
+
|
|
1209
|
+
# broadcast grids
|
|
1210
|
+
# Transform grids to CarsDict for broadcasting
|
|
1211
|
+
# due to Dask issue https://github.com/dask/dask/issues/9969
|
|
1212
|
+
broadcasted_disp_range_grid = self.orchestrator.cluster.scatter(
|
|
1213
|
+
CarsDict(disp_range_grid)
|
|
1214
|
+
)
|
|
1215
|
+
|
|
1216
|
+
# Generate disparity maps
|
|
1217
|
+
for col in range(epipolar_disparity_map.shape[1]):
|
|
1218
|
+
for row in range(epipolar_disparity_map.shape[0]):
|
|
1219
|
+
use_tile = False
|
|
1220
|
+
crop_with_range = None
|
|
1221
|
+
if type(None) not in (
|
|
1222
|
+
type(epipolar_images_left[row, col]),
|
|
1223
|
+
type(epipolar_images_right[row, col]),
|
|
1224
|
+
):
|
|
1225
|
+
use_tile = True
|
|
1226
|
+
nb_total_tiles_roi += 1
|
|
1227
|
+
|
|
1228
|
+
# Compute optimal tile size for tile
|
|
1229
|
+
(
|
|
1230
|
+
_,
|
|
1231
|
+
_,
|
|
1232
|
+
crop_with_range,
|
|
1233
|
+
) = local_tile_optimal_size_fun(
|
|
1234
|
+
np.array(
|
|
1235
|
+
epipolar_images_left.attributes[
|
|
1236
|
+
"disp_min_tiling"
|
|
1237
|
+
]
|
|
1238
|
+
)[row, col],
|
|
1239
|
+
np.array(
|
|
1240
|
+
epipolar_images_left.attributes[
|
|
1241
|
+
"disp_max_tiling"
|
|
1242
|
+
]
|
|
1243
|
+
)[row, col],
|
|
1244
|
+
)
|
|
1245
|
+
|
|
1246
|
+
if use_tile:
|
|
1247
|
+
# update saving infos for potential replacement
|
|
1248
|
+
full_saving_info = ocht.update_saving_infos(
|
|
1249
|
+
saving_info, row=row, col=col
|
|
1250
|
+
)
|
|
1251
|
+
# Compute disparity
|
|
1252
|
+
(
|
|
1253
|
+
epipolar_disparity_map[row, col]
|
|
1254
|
+
) = self.orchestrator.cluster.create_task(
|
|
1255
|
+
compute_disparity_wrapper
|
|
1256
|
+
)(
|
|
1257
|
+
epipolar_images_left[row, col],
|
|
1258
|
+
epipolar_images_right[row, col],
|
|
1259
|
+
self.corr_config,
|
|
1260
|
+
self.used_band,
|
|
1261
|
+
broadcasted_disp_range_grid,
|
|
1262
|
+
saving_info=full_saving_info,
|
|
1263
|
+
compute_disparity_masks=compute_disparity_masks,
|
|
1264
|
+
crop_with_range=crop_with_range,
|
|
1265
|
+
left_overlaps=cars_dataset.overlap_array_to_dict(
|
|
1266
|
+
epipolar_disparity_map.overlaps[row, col]
|
|
1267
|
+
),
|
|
1268
|
+
margins_to_keep=margins_to_keep,
|
|
1269
|
+
classification_fusion_margin=(
|
|
1270
|
+
self.classification_fusion_margin
|
|
1271
|
+
),
|
|
1272
|
+
texture_bands=texture_bands,
|
|
1273
|
+
conf_filtering=self.confidence_filtering,
|
|
1274
|
+
threshold_disp_range_to_borders=(
|
|
1275
|
+
self.threshold_disp_range_to_borders
|
|
1276
|
+
),
|
|
1277
|
+
filter_incomplete_disparity_range=(
|
|
1278
|
+
self.filter_incomplete_disparity_range
|
|
1279
|
+
),
|
|
1280
|
+
classif_bands_to_mask=classif_bands_to_mask,
|
|
1281
|
+
)
|
|
1282
|
+
|
|
1283
|
+
else:
|
|
1284
|
+
logging.error(
|
|
1285
|
+
"DenseMatching application doesn't "
|
|
1286
|
+
"support this input data format"
|
|
1287
|
+
)
|
|
1288
|
+
return epipolar_disparity_map
|
|
1289
|
+
|
|
1290
|
+
|
|
1291
|
+
def compute_disparity_wrapper( # pylint: disable=too-many-positional-arguments
|
|
1292
|
+
left_image_object: xr.Dataset,
|
|
1293
|
+
right_image_object: xr.Dataset,
|
|
1294
|
+
corr_cfg: dict,
|
|
1295
|
+
used_band: str,
|
|
1296
|
+
disp_range_grid,
|
|
1297
|
+
saving_info=None,
|
|
1298
|
+
compute_disparity_masks=False,
|
|
1299
|
+
crop_with_range=None,
|
|
1300
|
+
left_overlaps=None,
|
|
1301
|
+
margins_to_keep=0,
|
|
1302
|
+
classification_fusion_margin=-1,
|
|
1303
|
+
texture_bands=None,
|
|
1304
|
+
conf_filtering=None,
|
|
1305
|
+
threshold_disp_range_to_borders=False,
|
|
1306
|
+
filter_incomplete_disparity_range=True,
|
|
1307
|
+
classif_bands_to_mask=None,
|
|
1308
|
+
) -> Dict[str, Tuple[xr.Dataset, xr.Dataset]]:
|
|
1309
|
+
"""
|
|
1310
|
+
Compute disparity maps from image objects.
|
|
1311
|
+
This function will be run as a delayed task.
|
|
1312
|
+
|
|
1313
|
+
User must provide saving infos to save properly created datasets
|
|
1314
|
+
|
|
1315
|
+
:param left_image_object: tiled Left image
|
|
1316
|
+
- dataset with :
|
|
1317
|
+
|
|
1318
|
+
- cst.EPI_IMAGE
|
|
1319
|
+
- cst.EPI_MSK (if given)
|
|
1320
|
+
- cst.EPI_TEXTURE (for left, if given)
|
|
1321
|
+
:type left_image_object: xr.Dataset
|
|
1322
|
+
- dataset with :
|
|
1323
|
+
|
|
1324
|
+
- cst.EPI_IMAGE
|
|
1325
|
+
- cst.EPI_MSK (if given)
|
|
1326
|
+
- cst.EPI_TEXTURE (for left, if given)
|
|
1327
|
+
:param right_image_object: tiled Right image
|
|
1328
|
+
:type right_image_object: xr.Dataset
|
|
1329
|
+
:param corr_cfg: Correlator configuration
|
|
1330
|
+
:type corr_cfg: dict
|
|
1331
|
+
:param used_band: name of band used for correlation
|
|
1332
|
+
:type used_band: str
|
|
1333
|
+
:param disp_range_grid: minimum and maximum disparity grid
|
|
1334
|
+
:type disp_range_grid: np.ndarray
|
|
1335
|
+
:param compute_disparity_masks: Compute all the disparity \
|
|
1336
|
+
pandora masks(disable by default)
|
|
1337
|
+
:type compute_disparity_masks: bool
|
|
1338
|
+
:param generate_performance_map: True if generate performance map
|
|
1339
|
+
:type generate_performance_map: bool
|
|
1340
|
+
:param perf_ambiguity_threshold: ambiguity threshold used for
|
|
1341
|
+
performance map
|
|
1342
|
+
:type perf_ambiguity_threshold: float
|
|
1343
|
+
:param disp_to_alt_ratio: disp to alti ratio used for performance map
|
|
1344
|
+
:type disp_to_alt_ratio: float
|
|
1345
|
+
:param crop_with_range: range length to crop disparity range with
|
|
1346
|
+
:type crop_with_range: float
|
|
1347
|
+
:param left_overlaps: left overlap
|
|
1348
|
+
:type: left_overlaps: dict
|
|
1349
|
+
:param margins_to_keep: margin to keep after dense matching
|
|
1350
|
+
:type margins_to_keep: int
|
|
1351
|
+
:param classification_fusion_margin: the margin to add for the fusion
|
|
1352
|
+
:type classification_fusion_margin: int
|
|
1353
|
+
:param classif_bands_to_mask: bands from classif to mask
|
|
1354
|
+
:type classif_bands_to_mask: list of str / int
|
|
1355
|
+
|
|
1356
|
+
|
|
1357
|
+
:return: Left to right disparity dataset
|
|
1358
|
+
Returned dataset is composed of :
|
|
1359
|
+
|
|
1360
|
+
- cst_disp.MAP
|
|
1361
|
+
- cst_disp.VALID
|
|
1362
|
+
- cst.EPI_TEXTURE
|
|
1363
|
+
|
|
1364
|
+
"""
|
|
1365
|
+
# transform disp_range_grid back to dict
|
|
1366
|
+
disp_range_grid = disp_range_grid.data
|
|
1367
|
+
# Generate disparity grids
|
|
1368
|
+
(
|
|
1369
|
+
disp_min_grid,
|
|
1370
|
+
disp_max_grid,
|
|
1371
|
+
) = dm_algo.compute_disparity_grid(
|
|
1372
|
+
disp_range_grid,
|
|
1373
|
+
left_image_object,
|
|
1374
|
+
right_image_object,
|
|
1375
|
+
used_band,
|
|
1376
|
+
threshold_disp_range_to_borders,
|
|
1377
|
+
)
|
|
1378
|
+
|
|
1379
|
+
global_disp_min = disp_range_grid["global_min"]
|
|
1380
|
+
global_disp_max = disp_range_grid["global_max"]
|
|
1381
|
+
|
|
1382
|
+
# add global disparity in case of ambiguity normalization
|
|
1383
|
+
left_image_object = add_global_disparity(
|
|
1384
|
+
left_image_object, global_disp_min, global_disp_max
|
|
1385
|
+
)
|
|
1386
|
+
|
|
1387
|
+
# Crop interval if needed
|
|
1388
|
+
mask_crop = np.zeros(disp_min_grid.shape, dtype=int)
|
|
1389
|
+
is_cropped = False
|
|
1390
|
+
if crop_with_range is not None:
|
|
1391
|
+
current_min = np.min(disp_min_grid)
|
|
1392
|
+
current_max = np.max(disp_max_grid)
|
|
1393
|
+
if (current_max - current_min) > crop_with_range:
|
|
1394
|
+
is_cropped = True
|
|
1395
|
+
logging.warning("disparity range for current tile is cropped")
|
|
1396
|
+
# crop
|
|
1397
|
+
new_min = (
|
|
1398
|
+
current_min * crop_with_range / (current_max - current_min)
|
|
1399
|
+
)
|
|
1400
|
+
new_max = (
|
|
1401
|
+
current_max * crop_with_range / (current_max - current_min)
|
|
1402
|
+
)
|
|
1403
|
+
|
|
1404
|
+
mask_crop = np.logical_or(
|
|
1405
|
+
disp_min_grid < new_min, disp_max_grid > new_max
|
|
1406
|
+
)
|
|
1407
|
+
mask_crop = mask_crop.astype(bool)
|
|
1408
|
+
disp_min_grid[mask_crop] = new_min
|
|
1409
|
+
disp_max_grid[mask_crop] = new_max
|
|
1410
|
+
|
|
1411
|
+
# Compute disparity
|
|
1412
|
+
# TODO : remove overwriting of EPI_MSK
|
|
1413
|
+
disp_dataset = dm_algo.compute_disparity(
|
|
1414
|
+
left_image_object,
|
|
1415
|
+
right_image_object,
|
|
1416
|
+
corr_cfg,
|
|
1417
|
+
used_band,
|
|
1418
|
+
disp_min_grid=disp_min_grid,
|
|
1419
|
+
disp_max_grid=disp_max_grid,
|
|
1420
|
+
compute_disparity_masks=compute_disparity_masks,
|
|
1421
|
+
cropped_range=mask_crop,
|
|
1422
|
+
margins_to_keep=margins_to_keep,
|
|
1423
|
+
classification_fusion_margin=classification_fusion_margin,
|
|
1424
|
+
texture_bands=texture_bands,
|
|
1425
|
+
filter_incomplete_disparity_range=filter_incomplete_disparity_range,
|
|
1426
|
+
classif_bands_to_mask=classif_bands_to_mask,
|
|
1427
|
+
)
|
|
1428
|
+
|
|
1429
|
+
mask = disp_dataset["disp_msk"].values
|
|
1430
|
+
disp_map = disp_dataset["disp"].values
|
|
1431
|
+
disp_map[mask == 0] = np.nan
|
|
1432
|
+
|
|
1433
|
+
# Filtering by using the confidence
|
|
1434
|
+
requested_confidence = [
|
|
1435
|
+
"confidence_from_risk_min.cars_2",
|
|
1436
|
+
"confidence_from_risk_max.cars_2",
|
|
1437
|
+
"confidence_from_interval_bounds_inf.cars_3",
|
|
1438
|
+
"confidence_from_interval_bounds_sup.cars_3",
|
|
1439
|
+
]
|
|
1440
|
+
|
|
1441
|
+
if (
|
|
1442
|
+
all(key in disp_dataset for key in requested_confidence)
|
|
1443
|
+
and conf_filtering["activated"] is True
|
|
1444
|
+
):
|
|
1445
|
+
dm_wrappers.confidence_filtering(
|
|
1446
|
+
disp_dataset,
|
|
1447
|
+
requested_confidence,
|
|
1448
|
+
conf_filtering,
|
|
1449
|
+
)
|
|
1450
|
+
|
|
1451
|
+
# Fill with attributes
|
|
1452
|
+
cars_dataset.fill_dataset(
|
|
1453
|
+
disp_dataset,
|
|
1454
|
+
saving_info=saving_info,
|
|
1455
|
+
window=cars_dataset.get_window_dataset(left_image_object),
|
|
1456
|
+
profile=cars_dataset.get_profile_rasterio(left_image_object),
|
|
1457
|
+
attributes={cst.CROPPED_DISPARITY_RANGE: is_cropped},
|
|
1458
|
+
overlaps=left_overlaps,
|
|
1459
|
+
)
|
|
1460
|
+
|
|
1461
|
+
return disp_dataset
|