cars 1.0.0a2__cp310-cp310-win_amd64.whl → 1.0.0a4__cp310-cp310-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 +3 -3
- cars/applications/__init__.py +0 -3
- cars/applications/application.py +14 -6
- cars/applications/application_template.py +42 -0
- cars/applications/auxiliary_filling/abstract_auxiliary_filling_app.py +12 -2
- cars/applications/auxiliary_filling/auxiliary_filling_algo.py +2 -2
- cars/applications/auxiliary_filling/auxiliary_filling_from_sensors_app.py +95 -46
- cars/applications/auxiliary_filling/auxiliary_filling_wrappers.py +7 -6
- cars/applications/dem_generation/abstract_dem_generation_app.py +9 -5
- cars/applications/dem_generation/dem_generation_algo.py +1 -1
- cars/applications/dem_generation/dem_generation_wrappers.py +44 -59
- cars/applications/dem_generation/dichotomic_generation_app.py +9 -6
- cars/applications/dem_generation/rasterization_app.py +112 -43
- cars/applications/dense_match_filling/__init__.py +1 -1
- cars/applications/dense_match_filling/abstract_dense_match_filling_app.py +2 -15
- cars/applications/dense_match_filling/fill_disp_algo.py +32 -373
- cars/applications/dense_match_filling/fill_disp_wrappers.py +0 -343
- cars/applications/dense_match_filling/zero_padding_app.py +10 -5
- cars/applications/dense_matching/abstract_dense_matching_app.py +2 -1
- cars/applications/dense_matching/census_mccnn_sgm_app.py +48 -60
- cars/applications/dense_matching/cpp/dense_matching_cpp.cp310-win_amd64.dll.a +0 -0
- cars/applications/dense_matching/cpp/dense_matching_cpp.cp310-win_amd64.pyd +0 -0
- cars/applications/dense_matching/dense_matching_algo.py +48 -14
- cars/applications/dense_matching/dense_matching_wrappers.py +11 -3
- cars/applications/dense_matching/disparity_grid_algo.py +95 -79
- cars/applications/dense_matching/loaders/config_mapping.json +13 -0
- cars/applications/dense_matching/loaders/global_land_cover_map.tif +0 -0
- cars/applications/dense_matching/loaders/pandora_loader.py +169 -34
- cars/applications/dsm_filling/border_interpolation_app.py +11 -12
- cars/applications/dsm_filling/bulldozer_filling_app.py +16 -15
- cars/applications/dsm_filling/exogenous_filling_app.py +14 -14
- cars/applications/grid_generation/abstract_grid_generation_app.py +1 -1
- cars/applications/grid_generation/epipolar_grid_generation_app.py +4 -2
- cars/applications/grid_generation/grid_correction_app.py +4 -1
- cars/applications/grid_generation/grid_generation_algo.py +7 -2
- cars/applications/ground_truth_reprojection/abstract_ground_truth_reprojection_app.py +1 -1
- cars/applications/ground_truth_reprojection/direct_localization_app.py +2 -2
- cars/applications/ground_truth_reprojection/ground_truth_reprojection_algo.py +2 -1
- cars/applications/point_cloud_fusion/abstract_pc_fusion_app.py +0 -155
- cars/applications/point_cloud_fusion/mapping_to_terrain_tiles_app.py +0 -658
- cars/applications/point_cloud_fusion/pc_fusion_algo.py +0 -1339
- cars/applications/point_cloud_fusion/pc_fusion_wrappers.py +0 -869
- cars/applications/point_cloud_outlier_removal/abstract_outlier_removal_app.py +11 -6
- cars/applications/point_cloud_outlier_removal/outlier_removal_algo.py +9 -8
- cars/applications/point_cloud_outlier_removal/small_components_app.py +101 -270
- cars/applications/point_cloud_outlier_removal/statistical_app.py +120 -277
- cars/applications/rasterization/abstract_pc_rasterization_app.py +2 -1
- cars/applications/rasterization/rasterization_algo.py +18 -6
- cars/applications/rasterization/rasterization_wrappers.py +2 -1
- cars/applications/rasterization/simple_gaussian_app.py +88 -116
- cars/applications/resampling/abstract_resampling_app.py +1 -1
- cars/applications/resampling/bicubic_resampling_app.py +3 -1
- cars/applications/resampling/resampling_algo.py +60 -53
- cars/applications/resampling/resampling_wrappers.py +3 -1
- cars/applications/sparse_matching/abstract_sparse_matching_app.py +1 -1
- cars/applications/sparse_matching/sift_app.py +5 -25
- cars/applications/sparse_matching/sparse_matching_algo.py +3 -2
- cars/applications/sparse_matching/sparse_matching_wrappers.py +1 -1
- cars/applications/triangulation/abstract_triangulation_app.py +1 -1
- cars/applications/triangulation/line_of_sight_intersection_app.py +13 -11
- cars/applications/triangulation/pc_transform.py +552 -0
- cars/applications/triangulation/triangulation_algo.py +6 -4
- cars/applications/triangulation/triangulation_wrappers.py +1 -0
- cars/bundleadjustment.py +6 -6
- cars/cars.py +11 -9
- cars/core/cars_logging.py +80 -49
- cars/core/constants.py +0 -1
- cars/core/datasets.py +5 -2
- cars/core/geometry/abstract_geometry.py +364 -22
- cars/core/geometry/shareloc_geometry.py +112 -82
- cars/core/inputs.py +72 -19
- cars/core/outputs.py +1 -1
- cars/core/preprocessing.py +17 -3
- cars/core/projection.py +126 -6
- cars/core/tiling.py +10 -3
- cars/data_structures/cars_dataset.py +12 -10
- cars/data_structures/corresponding_tiles_tools.py +0 -103
- cars/data_structures/format_transformation.py +4 -1
- cars/devibrate.py +6 -3
- cars/extractroi.py +20 -21
- cars/orchestrator/cluster/abstract_cluster.py +15 -5
- cars/orchestrator/cluster/abstract_dask_cluster.py +6 -2
- cars/orchestrator/cluster/dask_jobqueue_utils.py +1 -1
- cars/orchestrator/cluster/log_wrapper.py +149 -22
- cars/orchestrator/cluster/mp_cluster/multiprocessing_cluster.py +12 -4
- cars/orchestrator/cluster/mp_cluster/multiprocessing_profiler.py +2 -2
- cars/orchestrator/cluster/pbs_dask_cluster.py +1 -1
- cars/orchestrator/cluster/sequential_cluster.py +5 -4
- cars/orchestrator/cluster/slurm_dask_cluster.py +1 -1
- cars/orchestrator/orchestrator.py +15 -4
- cars/orchestrator/registry/id_generator.py +1 -0
- cars/orchestrator/registry/saver_registry.py +2 -2
- cars/pipelines/conf_resolution/conf_final_resolution.json +5 -3
- cars/pipelines/default/default_pipeline.py +461 -1052
- cars/pipelines/parameters/advanced_parameters.py +91 -64
- cars/pipelines/parameters/advanced_parameters_constants.py +6 -5
- cars/pipelines/parameters/application_parameters.py +71 -0
- cars/pipelines/parameters/depth_map_inputs.py +0 -314
- cars/pipelines/parameters/dsm_inputs.py +40 -4
- cars/pipelines/parameters/output_parameters.py +44 -8
- cars/pipelines/parameters/sensor_inputs.py +122 -73
- cars/pipelines/parameters/sensor_inputs_constants.py +0 -2
- cars/pipelines/parameters/sensor_loaders/__init__.py +4 -3
- cars/pipelines/parameters/sensor_loaders/basic_classif_loader.py +106 -0
- cars/pipelines/parameters/sensor_loaders/{basic_sensor_loader.py → basic_image_loader.py} +16 -22
- cars/pipelines/parameters/sensor_loaders/pivot_classif_loader.py +121 -0
- cars/pipelines/parameters/sensor_loaders/{pivot_sensor_loader.py → pivot_image_loader.py} +10 -21
- cars/pipelines/parameters/sensor_loaders/sensor_loader.py +4 -6
- cars/pipelines/parameters/sensor_loaders/sensor_loader_template.py +1 -3
- cars/pipelines/pipeline_template.py +1 -3
- cars/pipelines/unit/unit_pipeline.py +676 -1070
- cars/starter.py +4 -3
- cars-1.0.0a4.dist-info/DELVEWHEEL +2 -0
- {cars-1.0.0a2.dist-info → cars-1.0.0a4.dist-info}/METADATA +135 -53
- {cars-1.0.0a2.dist-info → cars-1.0.0a4.dist-info}/RECORD +120 -134
- 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
- cars/applications/dense_match_filling/cpp/__init__.py +0 -0
- cars/applications/dense_match_filling/cpp/dense_match_filling_cpp.cp310-win_amd64.dll.a +0 -0
- cars/applications/dense_match_filling/cpp/dense_match_filling_cpp.cp310-win_amd64.pyd +0 -0
- cars/applications/dense_match_filling/cpp/dense_match_filling_cpp.py +0 -72
- cars/applications/dense_match_filling/cpp/includes/dense_match_filling.hpp +0 -46
- cars/applications/dense_match_filling/cpp/meson.build +0 -9
- cars/applications/dense_match_filling/cpp/src/bindings.cpp +0 -11
- cars/applications/dense_match_filling/cpp/src/dense_match_filling.cpp +0 -142
- cars/applications/dense_match_filling/plane_app.py +0 -556
- cars/applications/hole_detection/__init__.py +0 -30
- cars/applications/hole_detection/abstract_hole_detection_app.py +0 -125
- cars/applications/hole_detection/cloud_to_bbox_app.py +0 -346
- cars/applications/hole_detection/hole_detection_algo.py +0 -144
- cars/applications/hole_detection/hole_detection_wrappers.py +0 -53
- cars/applications/point_cloud_denoising/__init__.py +0 -29
- cars/applications/point_cloud_denoising/abstract_pc_denoising_app.py +0 -273
- cars/applications/point_cloud_fusion/__init__.py +0 -30
- cars/applications/point_cloud_fusion/cloud_fusion_constants.py +0 -39
- cars/applications/sparse_matching/pandora_sparse_matching_app.py +0 -0
- cars/pipelines/parameters/depth_map_inputs_constants.py +0 -25
- cars-1.0.0a2.dist-info/DELVEWHEEL +0 -2
- cars.libs/libgcc_s_seh-1-f2b6825d483bdf14050493af93b5997d.dll +0 -0
- cars.libs/libstdc++-6-6b0059df6bc601df5a0f18a5805eea05.dll +0 -0
- cars.libs/libwinpthread-1-e01b8e85fd67c2b861f64d4ccc7df607.dll +0 -0
- {cars-1.0.0a2.dist-info → cars-1.0.0a4.dist-info}/WHEEL +0 -0
- {cars-1.0.0a2.dist-info → cars-1.0.0a4.dist-info}/entry_points.txt +0 -0
|
@@ -33,42 +33,58 @@ from __future__ import print_function
|
|
|
33
33
|
import copy
|
|
34
34
|
import json
|
|
35
35
|
import logging
|
|
36
|
-
import math
|
|
37
36
|
import os
|
|
38
37
|
import shutil
|
|
38
|
+
from datetime import datetime
|
|
39
39
|
|
|
40
40
|
# CARS imports
|
|
41
|
-
from cars import __version__
|
|
42
|
-
from cars.applications.application import Application
|
|
43
41
|
from cars.core import cars_logging
|
|
44
|
-
from cars.core.inputs import get_descriptions_bands, rasterio_get_size
|
|
45
42
|
from cars.core.utils import safe_makedirs
|
|
46
|
-
from cars.
|
|
43
|
+
from cars.data_structures import cars_dataset
|
|
47
44
|
from cars.orchestrator.cluster import log_wrapper
|
|
48
45
|
from cars.orchestrator.cluster.log_wrapper import cars_profile
|
|
49
46
|
from cars.pipelines.parameters import advanced_parameters
|
|
50
47
|
from cars.pipelines.parameters import advanced_parameters_constants as adv_cst
|
|
51
|
-
from cars.pipelines.parameters import depth_map_inputs
|
|
52
|
-
from cars.pipelines.parameters import depth_map_inputs_constants as depth_cst
|
|
53
|
-
from cars.pipelines.parameters import dsm_inputs
|
|
54
48
|
from cars.pipelines.parameters import dsm_inputs_constants as dsm_cst
|
|
55
49
|
from cars.pipelines.parameters import output_constants as out_cst
|
|
56
|
-
from cars.pipelines.parameters import output_parameters
|
|
50
|
+
from cars.pipelines.parameters import output_parameters
|
|
57
51
|
from cars.pipelines.parameters import sensor_inputs_constants as sens_cst
|
|
58
52
|
from cars.pipelines.pipeline import Pipeline
|
|
59
53
|
from cars.pipelines.pipeline_constants import (
|
|
60
54
|
ADVANCED,
|
|
61
55
|
APPLICATIONS,
|
|
62
56
|
INPUTS,
|
|
63
|
-
ORCHESTRATOR,
|
|
64
57
|
OUTPUT,
|
|
65
58
|
)
|
|
66
|
-
from cars.pipelines.pipeline_template import
|
|
67
|
-
PipelineTemplate,
|
|
68
|
-
_merge_resolution_conf_rec,
|
|
69
|
-
)
|
|
59
|
+
from cars.pipelines.pipeline_template import PipelineTemplate
|
|
70
60
|
from cars.pipelines.unit.unit_pipeline import UnitPipeline
|
|
71
61
|
|
|
62
|
+
package_path = os.path.dirname(__file__)
|
|
63
|
+
FIRST_RES = "first_resolution"
|
|
64
|
+
INTERMEDIATE_RES = "intermediate_resolution"
|
|
65
|
+
FINAL_RES = "final_resolution"
|
|
66
|
+
|
|
67
|
+
PIPELINE_CONFS = {
|
|
68
|
+
FIRST_RES: os.path.join(
|
|
69
|
+
package_path,
|
|
70
|
+
"..",
|
|
71
|
+
"conf_resolution",
|
|
72
|
+
"conf_first_resolution.json",
|
|
73
|
+
),
|
|
74
|
+
INTERMEDIATE_RES: os.path.join(
|
|
75
|
+
package_path,
|
|
76
|
+
"..",
|
|
77
|
+
"conf_resolution",
|
|
78
|
+
"conf_intermediate_resolution.json",
|
|
79
|
+
),
|
|
80
|
+
FINAL_RES: os.path.join(
|
|
81
|
+
package_path,
|
|
82
|
+
"..",
|
|
83
|
+
"conf_resolution",
|
|
84
|
+
"conf_final_resolution.json",
|
|
85
|
+
),
|
|
86
|
+
}
|
|
87
|
+
|
|
72
88
|
|
|
73
89
|
@Pipeline.register(
|
|
74
90
|
"default",
|
|
@@ -84,17 +100,6 @@ class DefaultPipeline(PipelineTemplate):
|
|
|
84
100
|
"""
|
|
85
101
|
Creates pipeline
|
|
86
102
|
|
|
87
|
-
Directly creates class attributes:
|
|
88
|
-
used_conf
|
|
89
|
-
generate_terrain_products
|
|
90
|
-
debug_with_roi
|
|
91
|
-
save_output_dsm
|
|
92
|
-
save_output_depth_map
|
|
93
|
-
save_output_point_clouds
|
|
94
|
-
geom_plugin_without_dem_and_geoid
|
|
95
|
-
geom_plugin_with_dem_and_geoid
|
|
96
|
-
dem_generation_roi
|
|
97
|
-
|
|
98
103
|
:param pipeline_name: name of the pipeline.
|
|
99
104
|
:type pipeline_name: str
|
|
100
105
|
:param cfg: configuration {'matching_cost_method': value}
|
|
@@ -103,9 +108,7 @@ class DefaultPipeline(PipelineTemplate):
|
|
|
103
108
|
:type config_dir: str
|
|
104
109
|
"""
|
|
105
110
|
|
|
106
|
-
|
|
107
|
-
self.used_conf = {}
|
|
108
|
-
|
|
111
|
+
self.config_dir = config_dir
|
|
109
112
|
# Transform relative path to absolute path
|
|
110
113
|
if config_dir is not None:
|
|
111
114
|
config_dir = os.path.abspath(config_dir)
|
|
@@ -113,457 +116,125 @@ class DefaultPipeline(PipelineTemplate):
|
|
|
113
116
|
# Check global conf
|
|
114
117
|
self.check_global_schema(conf)
|
|
115
118
|
|
|
116
|
-
|
|
117
|
-
# parameters are all the same for each resolutions
|
|
118
|
-
# So we do the check once
|
|
119
|
-
|
|
120
|
-
# Check conf inputs
|
|
121
|
-
inputs = self.check_inputs(conf[INPUTS], config_dir=config_dir)
|
|
122
|
-
|
|
123
|
-
# Check conf output
|
|
124
|
-
output = self.check_output(conf[OUTPUT])
|
|
125
|
-
|
|
126
|
-
# Check advanced parameters
|
|
127
|
-
# TODO static method in the base class
|
|
128
|
-
(
|
|
129
|
-
_,
|
|
130
|
-
advanced,
|
|
131
|
-
self.geometry_plugin,
|
|
132
|
-
self.geom_plugin_without_dem_and_geoid,
|
|
133
|
-
self.geom_plugin_with_dem_and_geoid,
|
|
134
|
-
_,
|
|
135
|
-
) = advanced_parameters.check_advanced_parameters(
|
|
136
|
-
inputs, conf.get(ADVANCED, {}), check_epipolar_a_priori=True
|
|
137
|
-
)
|
|
138
|
-
|
|
139
|
-
resolutions = advanced["epipolar_resolutions"]
|
|
140
|
-
if isinstance(resolutions, int):
|
|
141
|
-
resolutions = [resolutions]
|
|
119
|
+
self.out_dir = conf[OUTPUT][out_cst.OUT_DIRECTORY]
|
|
142
120
|
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
121
|
+
# Get epipolar resolutions to use
|
|
122
|
+
self.epipolar_resolutions = (
|
|
123
|
+
advanced_parameters.get_epipolar_resolutions(conf.get(ADVANCED, {}))
|
|
124
|
+
)
|
|
125
|
+
if isinstance(self.epipolar_resolutions, int):
|
|
126
|
+
self.epipolar_resolutions = [self.epipolar_resolutions]
|
|
127
|
+
|
|
128
|
+
# Check application
|
|
129
|
+
self.check_applications(conf)
|
|
130
|
+
# Check input
|
|
131
|
+
conf[INPUTS] = self.check_inputs(conf)
|
|
132
|
+
# check advanced
|
|
133
|
+
conf[ADVANCED] = self.check_advanced(conf)
|
|
134
|
+
# check output
|
|
135
|
+
conf[OUTPUT] = self.check_output(conf)
|
|
136
|
+
|
|
137
|
+
if dsm_cst.DSMS in conf[INPUTS] and len(self.epipolar_resolutions) != 1:
|
|
138
|
+
logging.info(
|
|
147
139
|
"For the use of those pipelines, "
|
|
148
140
|
"you have to give only one resolution"
|
|
149
141
|
)
|
|
142
|
+
# overide epipolar resolutions
|
|
143
|
+
# TODO: delete with external dsm pipeline (refactoring)
|
|
144
|
+
self.epipolar_resolutions = [1]
|
|
150
145
|
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
if not isinstance(res, int) or res < 0:
|
|
156
|
-
raise RuntimeError("The resolution has to be an int > 0")
|
|
157
|
-
|
|
158
|
-
# Get the current key
|
|
159
|
-
key = "resolution_" + str(res)
|
|
160
|
-
|
|
161
|
-
# Choose the right default configuration regarding the resolution
|
|
162
|
-
package_path = os.path.dirname(__file__)
|
|
163
|
-
|
|
164
|
-
if i == 0:
|
|
165
|
-
json_file = os.path.join(
|
|
166
|
-
package_path,
|
|
167
|
-
"..",
|
|
168
|
-
"conf_resolution",
|
|
169
|
-
"conf_first_resolution.json",
|
|
170
|
-
)
|
|
171
|
-
elif i == len(resolutions) - 1 or len(resolutions) == 1:
|
|
172
|
-
json_file = os.path.join(
|
|
173
|
-
package_path,
|
|
174
|
-
"..",
|
|
175
|
-
"conf_resolution",
|
|
176
|
-
"conf_final_resolution.json",
|
|
177
|
-
)
|
|
178
|
-
else:
|
|
179
|
-
json_file = os.path.join(
|
|
180
|
-
package_path,
|
|
181
|
-
"..",
|
|
182
|
-
"conf_resolution",
|
|
183
|
-
"conf_intermediate_resolution.json",
|
|
184
|
-
)
|
|
185
|
-
|
|
186
|
-
with open(json_file, "r", encoding="utf8") as fstream:
|
|
187
|
-
resolution_config = json.load(fstream)
|
|
188
|
-
|
|
189
|
-
self.used_conf[key] = {}
|
|
190
|
-
|
|
191
|
-
# Check conf orchestrator
|
|
192
|
-
self.used_conf[key][ORCHESTRATOR] = self.check_orchestrator(
|
|
193
|
-
conf.get(ORCHESTRATOR, None)
|
|
194
|
-
)
|
|
195
|
-
|
|
196
|
-
# copy inputs
|
|
197
|
-
self.used_conf[key][INPUTS] = copy.deepcopy(inputs)
|
|
198
|
-
|
|
199
|
-
# Override the resolution
|
|
200
|
-
self.used_conf[key][ADVANCED] = copy.deepcopy(advanced)
|
|
201
|
-
self.used_conf[key][ADVANCED][adv_cst.EPIPOLAR_RESOLUTIONS] = res
|
|
202
|
-
|
|
203
|
-
# Copy output
|
|
204
|
-
self.used_conf[key][OUTPUT] = copy.deepcopy(output)
|
|
205
|
-
|
|
206
|
-
# Get save intermediate data per res
|
|
207
|
-
# If true we don't delete the resolution directory
|
|
208
|
-
if isinstance(
|
|
209
|
-
self.used_conf[key][ADVANCED][adv_cst.SAVE_INTERMEDIATE_DATA],
|
|
210
|
-
dict,
|
|
211
|
-
):
|
|
212
|
-
|
|
213
|
-
self.used_conf[key][ADVANCED][
|
|
214
|
-
adv_cst.SAVE_INTERMEDIATE_DATA
|
|
215
|
-
] = self.used_conf[key][ADVANCED][
|
|
216
|
-
adv_cst.SAVE_INTERMEDIATE_DATA
|
|
217
|
-
].get(
|
|
218
|
-
key, False
|
|
219
|
-
)
|
|
220
|
-
|
|
221
|
-
self.save_intermediate_data = self.used_conf[key][ADVANCED][
|
|
222
|
-
adv_cst.SAVE_INTERMEDIATE_DATA
|
|
223
|
-
]
|
|
146
|
+
used_configurations = {}
|
|
147
|
+
self.unit_pipelines = {}
|
|
148
|
+
self.positions = {}
|
|
224
149
|
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
if i != len(resolutions) - 1:
|
|
230
|
-
# Change output dir for lower resolution
|
|
231
|
-
new_dir = (
|
|
232
|
-
self.used_conf[key][OUTPUT][out_cst.OUT_DIRECTORY]
|
|
233
|
-
+ "/intermediate_res/out_res"
|
|
234
|
-
+ str(res)
|
|
235
|
-
)
|
|
236
|
-
self.used_conf[key][OUTPUT][out_cst.OUT_DIRECTORY] = new_dir
|
|
237
|
-
safe_makedirs(
|
|
238
|
-
self.used_conf[key][OUTPUT][out_cst.OUT_DIRECTORY]
|
|
239
|
-
)
|
|
240
|
-
|
|
241
|
-
# Put dems in auxiliary output
|
|
242
|
-
self.used_conf[key][OUTPUT][out_cst.AUXILIARY][
|
|
243
|
-
out_cst.AUX_DEM_MAX
|
|
244
|
-
] = True
|
|
245
|
-
self.used_conf[key][OUTPUT][out_cst.AUXILIARY][
|
|
246
|
-
out_cst.AUX_DEM_MIN
|
|
247
|
-
] = True
|
|
248
|
-
self.used_conf[key][OUTPUT][out_cst.AUXILIARY][
|
|
249
|
-
out_cst.AUX_DEM_MEDIAN
|
|
250
|
-
] = True
|
|
251
|
-
|
|
252
|
-
if not self.save_intermediate_data:
|
|
253
|
-
# For each resolutions we need to calculate the dsm
|
|
254
|
-
# (except the last one)
|
|
255
|
-
self.used_conf[key][OUTPUT][out_cst.PRODUCT_LEVEL] = "dsm"
|
|
256
|
-
|
|
257
|
-
# The idea is to calculate the less possible things
|
|
258
|
-
# So we override those parameters
|
|
259
|
-
self.used_conf[key][ADVANCED][adv_cst.MERGING] = False
|
|
260
|
-
self.used_conf[key][ADVANCED][adv_cst.PHASING] = None
|
|
261
|
-
self.used_conf[key][OUTPUT][out_cst.SAVE_BY_PAIR] = False
|
|
262
|
-
|
|
263
|
-
aux_items = self.used_conf[key][OUTPUT][
|
|
264
|
-
out_cst.AUXILIARY
|
|
265
|
-
].items()
|
|
266
|
-
for aux_key, _ in aux_items:
|
|
267
|
-
if aux_key not in ("dem_min", "dem_max", "dem_median"):
|
|
268
|
-
self.used_conf[key][OUTPUT][out_cst.AUXILIARY][
|
|
269
|
-
aux_key
|
|
270
|
-
] = False
|
|
271
|
-
else:
|
|
272
|
-
# If save_intermediate_data is true,
|
|
273
|
-
# we save the depth_maps also to debug
|
|
274
|
-
self.used_conf[key][OUTPUT][out_cst.PRODUCT_LEVEL] = [
|
|
275
|
-
"dsm",
|
|
276
|
-
"depth_map",
|
|
277
|
-
]
|
|
278
|
-
else:
|
|
279
|
-
# For the final res we don't change anything
|
|
280
|
-
last_res = True
|
|
150
|
+
self.intermediate_data_dir = os.path.join(
|
|
151
|
+
self.out_dir, "intermediate_data"
|
|
152
|
+
)
|
|
281
153
|
|
|
282
|
-
|
|
154
|
+
self.keep_low_res_dir = conf[ADVANCED][adv_cst.KEEP_LOW_RES_DIR]
|
|
283
155
|
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
self.save_output_point_cloud = "point_cloud" in prod_level
|
|
156
|
+
# Get first res outdir for sift matches
|
|
157
|
+
self.first_res_out_dir_with_sensors = None
|
|
287
158
|
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
159
|
+
for epipolar_resolution_index, epipolar_res in enumerate(
|
|
160
|
+
self.epipolar_resolutions
|
|
161
|
+
):
|
|
162
|
+
first_res = epipolar_resolution_index == 0
|
|
163
|
+
intermediate_res = (
|
|
164
|
+
epipolar_resolution_index == len(self.epipolar_resolutions) - 1
|
|
165
|
+
or len(self.epipolar_resolutions) == 1
|
|
295
166
|
)
|
|
296
|
-
|
|
297
|
-
|
|
167
|
+
last_res = (
|
|
168
|
+
epipolar_resolution_index == len(self.epipolar_resolutions) - 1
|
|
298
169
|
)
|
|
299
|
-
self.dsms_in_inputs = dsm_cst.DSMS in self.used_conf[key][INPUTS]
|
|
300
|
-
|
|
301
|
-
self.merging = self.used_conf[key][ADVANCED][adv_cst.MERGING]
|
|
302
|
-
|
|
303
|
-
self.phasing = self.used_conf[key][ADVANCED][adv_cst.PHASING]
|
|
304
|
-
|
|
305
|
-
self.save_all_point_clouds_by_pair = self.used_conf[key][
|
|
306
|
-
OUTPUT
|
|
307
|
-
].get(out_cst.SAVE_BY_PAIR, False)
|
|
308
|
-
|
|
309
|
-
# Check conf application
|
|
310
|
-
if APPLICATIONS in conf:
|
|
311
|
-
if all(
|
|
312
|
-
"resolution_" + str(val) not in conf[APPLICATIONS]
|
|
313
|
-
for val in resolutions
|
|
314
|
-
):
|
|
315
|
-
application_all_conf = conf.get(APPLICATIONS, {})
|
|
316
|
-
else:
|
|
317
|
-
self.check_application_keys_name(
|
|
318
|
-
resolutions, conf[APPLICATIONS]
|
|
319
|
-
)
|
|
320
|
-
application_all_conf = conf[APPLICATIONS].get(key, {})
|
|
321
|
-
else:
|
|
322
|
-
application_all_conf = {}
|
|
323
170
|
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
171
|
+
# set computed bool
|
|
172
|
+
self.positions[epipolar_resolution_index] = {
|
|
173
|
+
"first_res": first_res,
|
|
174
|
+
"intermediate_res": intermediate_res,
|
|
175
|
+
"last_res": last_res,
|
|
176
|
+
}
|
|
327
177
|
|
|
328
|
-
|
|
329
|
-
|
|
178
|
+
current_conf = copy.deepcopy(conf)
|
|
179
|
+
current_conf = extract_conf_with_resolution(
|
|
180
|
+
current_conf,
|
|
181
|
+
epipolar_res,
|
|
182
|
+
first_res,
|
|
183
|
+
intermediate_res,
|
|
184
|
+
last_res,
|
|
185
|
+
self.intermediate_data_dir,
|
|
330
186
|
)
|
|
331
187
|
|
|
332
|
-
if
|
|
333
|
-
self.
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
):
|
|
337
|
-
# Check conf application vs inputs application
|
|
338
|
-
application_conf = self.check_applications_with_inputs(
|
|
339
|
-
self.used_conf[key][INPUTS],
|
|
340
|
-
application_conf,
|
|
341
|
-
application_all_conf,
|
|
342
|
-
res,
|
|
343
|
-
)
|
|
344
|
-
|
|
345
|
-
self.used_conf[key][APPLICATIONS] = application_conf
|
|
346
|
-
|
|
347
|
-
i += 1
|
|
348
|
-
|
|
349
|
-
def quit_on_app(self, app_name):
|
|
350
|
-
"""
|
|
351
|
-
Returns whether the pipeline should end after
|
|
352
|
-
the application was called.
|
|
353
|
-
|
|
354
|
-
Only works if the output_level is empty, so that
|
|
355
|
-
the control is instead given to
|
|
356
|
-
"""
|
|
357
|
-
|
|
358
|
-
if not self.output_level_none:
|
|
359
|
-
# custom quit step was not set, never quit early
|
|
360
|
-
return False
|
|
361
|
-
|
|
362
|
-
return self.app_values[app_name] >= self.last_application_to_run
|
|
363
|
-
|
|
364
|
-
def infer_conditions_from_applications(self, conf):
|
|
365
|
-
"""
|
|
366
|
-
Fills the condition booleans used later in the pipeline by going
|
|
367
|
-
through the applications and infering which application we should
|
|
368
|
-
end the pipeline on.
|
|
369
|
-
"""
|
|
370
|
-
|
|
371
|
-
self.last_application_to_run = 0
|
|
372
|
-
|
|
373
|
-
sensor_to_depth_apps = {
|
|
374
|
-
"grid_generation": 1, # and 5
|
|
375
|
-
"resampling": 2, # and 8
|
|
376
|
-
"hole_detection": 3,
|
|
377
|
-
"sparse_matching.sift": 4,
|
|
378
|
-
"ground_truth_reprojection": 6,
|
|
379
|
-
"dense_matching": 8,
|
|
380
|
-
"dense_match_filling.1": 9,
|
|
381
|
-
"dense_match_filling.2": 10,
|
|
382
|
-
"triangulation": 11,
|
|
383
|
-
"point_cloud_outlier_removal.1": 12,
|
|
384
|
-
"point_cloud_outlier_removal.2": 13,
|
|
385
|
-
}
|
|
188
|
+
if first_res:
|
|
189
|
+
self.first_res_out_dir_with_sensors = current_conf[OUTPUT][
|
|
190
|
+
"directory"
|
|
191
|
+
]
|
|
386
192
|
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
}
|
|
193
|
+
if not isinstance(epipolar_res, int) or epipolar_res < 0:
|
|
194
|
+
raise RuntimeError("The resolution has to be an int > 0")
|
|
390
195
|
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
"dem_generation": 17,
|
|
395
|
-
"dsm_filling.1": 18,
|
|
396
|
-
"dsm_filling.2": 19,
|
|
397
|
-
"dsm_filling.3": 20,
|
|
398
|
-
"auxiliary_filling": 21,
|
|
399
|
-
}
|
|
196
|
+
# Initialize unit pipeline in order to retrieve the
|
|
197
|
+
# used configuration
|
|
198
|
+
# This pipeline will not be run
|
|
400
199
|
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
self.app_values.update(depth_merge_apps)
|
|
404
|
-
self.app_values.update(depth_to_dsm_apps)
|
|
405
|
-
|
|
406
|
-
app_conf = conf.get(APPLICATIONS, {})
|
|
407
|
-
for key in app_conf:
|
|
408
|
-
|
|
409
|
-
if adv_cst.SAVE_INTERMEDIATE_DATA not in app_conf[key]:
|
|
410
|
-
continue
|
|
411
|
-
|
|
412
|
-
if not app_conf[key][adv_cst.SAVE_INTERMEDIATE_DATA]:
|
|
413
|
-
continue
|
|
414
|
-
|
|
415
|
-
if key in sensor_to_depth_apps:
|
|
416
|
-
|
|
417
|
-
if not self.sensors_in_inputs:
|
|
418
|
-
warn_msg = (
|
|
419
|
-
"The application {} can only be used when sensor "
|
|
420
|
-
"images are given as an input. "
|
|
421
|
-
"Its configuration will be ignored."
|
|
422
|
-
).format(key)
|
|
423
|
-
logging.warning(warn_msg)
|
|
424
|
-
|
|
425
|
-
elif (
|
|
426
|
-
self.sensors_in_inputs
|
|
427
|
-
and not self.depth_maps_in_inputs
|
|
428
|
-
and not self.dsms_in_inputs
|
|
429
|
-
):
|
|
430
|
-
self.compute_depth_map = True
|
|
431
|
-
self.last_application_to_run = max(
|
|
432
|
-
self.last_application_to_run, self.app_values[key]
|
|
433
|
-
)
|
|
434
|
-
|
|
435
|
-
elif key in depth_to_dsm_apps:
|
|
436
|
-
|
|
437
|
-
if not (
|
|
438
|
-
self.sensors_in_inputs
|
|
439
|
-
or self.depth_maps_in_inputs
|
|
440
|
-
or self.dsms_in_inputs
|
|
441
|
-
):
|
|
442
|
-
warn_msg = (
|
|
443
|
-
"The application {} can only be used when sensor "
|
|
444
|
-
"images or depth maps are given as an input. "
|
|
445
|
-
"Its configuration will be ignored."
|
|
446
|
-
).format(key)
|
|
447
|
-
logging.warning(warn_msg)
|
|
448
|
-
|
|
449
|
-
else:
|
|
450
|
-
if (
|
|
451
|
-
self.sensors_in_inputs
|
|
452
|
-
and not self.depth_maps_in_inputs
|
|
453
|
-
and not self.dsms_in_inputs
|
|
454
|
-
):
|
|
455
|
-
self.compute_depth_map = True
|
|
456
|
-
|
|
457
|
-
# enabled to start the depth map to dsm process
|
|
458
|
-
self.save_output_dsm = True
|
|
459
|
-
|
|
460
|
-
self.last_application_to_run = max(
|
|
461
|
-
self.last_application_to_run, self.app_values[key]
|
|
462
|
-
)
|
|
463
|
-
|
|
464
|
-
elif key in depth_merge_apps:
|
|
465
|
-
|
|
466
|
-
if not self.merging:
|
|
467
|
-
warn_msg = (
|
|
468
|
-
"The application {} can only be used when merging "
|
|
469
|
-
"is activated (this parameter is located in the "
|
|
470
|
-
"'advanced' config key). "
|
|
471
|
-
"The application's configuration will be ignored."
|
|
472
|
-
).format(key)
|
|
473
|
-
logging.warning(warn_msg)
|
|
474
|
-
|
|
475
|
-
elif not (
|
|
476
|
-
self.sensors_in_inputs
|
|
477
|
-
or self.depth_maps_in_inputs
|
|
478
|
-
or self.dsms_in_inputs
|
|
479
|
-
):
|
|
480
|
-
warn_msg = (
|
|
481
|
-
"The application {} can only be used when sensor "
|
|
482
|
-
"images or depth maps are given as an input. "
|
|
483
|
-
"Its configuration will be ignored."
|
|
484
|
-
).format(key)
|
|
485
|
-
logging.warning(warn_msg)
|
|
486
|
-
|
|
487
|
-
else:
|
|
488
|
-
if (
|
|
489
|
-
self.sensors_in_inputs
|
|
490
|
-
and not self.depth_maps_in_inputs
|
|
491
|
-
and not self.dsms_in_inputs
|
|
492
|
-
):
|
|
493
|
-
self.compute_depth_map = True
|
|
494
|
-
|
|
495
|
-
# enabled to start the depth map to dsm process
|
|
496
|
-
self.save_output_point_cloud = True
|
|
497
|
-
|
|
498
|
-
self.last_application_to_run = max(
|
|
499
|
-
self.last_application_to_run, self.app_values[key]
|
|
500
|
-
)
|
|
501
|
-
else:
|
|
502
|
-
warn_msg = (
|
|
503
|
-
"The application {} was not recognized. Its configuration"
|
|
504
|
-
"will be ignored."
|
|
505
|
-
).format(key)
|
|
506
|
-
logging.warning(warn_msg)
|
|
507
|
-
|
|
508
|
-
if not (self.compute_depth_map or self.save_output_dsm):
|
|
509
|
-
log_msg = (
|
|
510
|
-
"No product level was given. CARS has not detected any "
|
|
511
|
-
"data you wish to save. No computation will be done."
|
|
200
|
+
current_unit_pipeline = UnitPipeline(
|
|
201
|
+
current_conf, config_dir=self.config_dir
|
|
512
202
|
)
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
log_msg = (
|
|
516
|
-
"No product level was given. CARS has detected that you "
|
|
517
|
-
+ "wish to run up to the {} application.".format(
|
|
518
|
-
next(
|
|
519
|
-
k
|
|
520
|
-
for k, v in self.app_values.items()
|
|
521
|
-
if v == self.last_application_to_run
|
|
522
|
-
)
|
|
523
|
-
)
|
|
203
|
+
self.unit_pipelines[epipolar_resolution_index] = (
|
|
204
|
+
current_unit_pipeline
|
|
524
205
|
)
|
|
525
|
-
|
|
206
|
+
# Get used_conf
|
|
207
|
+
used_configurations[epipolar_res] = current_unit_pipeline.used_conf
|
|
208
|
+
|
|
209
|
+
# Generate full used_conf
|
|
210
|
+
full_used_conf = merge_used_conf(
|
|
211
|
+
used_configurations, self.epipolar_resolutions
|
|
212
|
+
)
|
|
213
|
+
# Save used_conf
|
|
214
|
+
cars_dataset.save_dict(
|
|
215
|
+
full_used_conf,
|
|
216
|
+
os.path.join(self.out_dir, "global_used_conf.json"),
|
|
217
|
+
safe_save=True,
|
|
218
|
+
)
|
|
526
219
|
|
|
527
|
-
|
|
528
|
-
def check_inputs(conf, config_dir=None):
|
|
220
|
+
def check_inputs(self, conf, config_json_dir=None):
|
|
529
221
|
"""
|
|
530
222
|
Check the inputs given
|
|
531
223
|
|
|
532
|
-
:param conf: configuration
|
|
224
|
+
:param conf: configuration
|
|
533
225
|
:type conf: dict
|
|
534
226
|
:param config_dir: directory of used json, if
|
|
535
227
|
user filled paths with relative paths
|
|
536
228
|
:type config_dir: str
|
|
537
229
|
|
|
538
|
-
:return:
|
|
230
|
+
:return: overloader inputs
|
|
539
231
|
:rtype: dict
|
|
540
232
|
"""
|
|
233
|
+
return UnitPipeline.check_inputs(
|
|
234
|
+
conf[INPUTS], config_dir=self.config_dir
|
|
235
|
+
)
|
|
541
236
|
|
|
542
|
-
|
|
543
|
-
if (
|
|
544
|
-
sens_cst.SENSORS in conf
|
|
545
|
-
and depth_cst.DEPTH_MAPS not in conf
|
|
546
|
-
and dsm_cst.DSMS not in conf
|
|
547
|
-
):
|
|
548
|
-
output_config = sensor_inputs.sensors_check_inputs(
|
|
549
|
-
conf, config_dir=config_dir
|
|
550
|
-
)
|
|
551
|
-
elif depth_cst.DEPTH_MAPS in conf:
|
|
552
|
-
output_config = {
|
|
553
|
-
**output_config,
|
|
554
|
-
**depth_map_inputs.check_depth_maps_inputs(
|
|
555
|
-
conf, config_dir=config_dir
|
|
556
|
-
),
|
|
557
|
-
}
|
|
558
|
-
else:
|
|
559
|
-
output_config = {
|
|
560
|
-
**output_config,
|
|
561
|
-
**dsm_inputs.check_dsm_inputs(conf, config_dir=config_dir),
|
|
562
|
-
}
|
|
563
|
-
return output_config
|
|
564
|
-
|
|
565
|
-
@staticmethod
|
|
566
|
-
def check_output(conf):
|
|
237
|
+
def check_output(self, conf):
|
|
567
238
|
"""
|
|
568
239
|
Check the output given
|
|
569
240
|
|
|
@@ -573,681 +244,419 @@ class DefaultPipeline(PipelineTemplate):
|
|
|
573
244
|
:return overloader output
|
|
574
245
|
:rtype : dict
|
|
575
246
|
"""
|
|
576
|
-
|
|
247
|
+
conf_output, self.scaling_coeff = (
|
|
248
|
+
output_parameters.check_output_parameters(
|
|
249
|
+
conf[OUTPUT], self.scaling_coeff
|
|
250
|
+
)
|
|
251
|
+
)
|
|
252
|
+
return conf_output
|
|
577
253
|
|
|
578
|
-
def
|
|
254
|
+
def check_advanced(self, conf):
|
|
579
255
|
"""
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
:param conf1: configuration
|
|
583
|
-
:type conf1: dict
|
|
584
|
-
:param conf2: configuration
|
|
585
|
-
:type conf2: dict
|
|
256
|
+
Check all conf for advanced configuration
|
|
586
257
|
|
|
587
|
-
:return:
|
|
258
|
+
:return: overridden advanced conf
|
|
588
259
|
:rtype: dict
|
|
260
|
+
"""
|
|
261
|
+
(_, advanced, _, _, _, self.scaling_coeff, _, _) = (
|
|
262
|
+
advanced_parameters.check_advanced_parameters(
|
|
263
|
+
conf[INPUTS],
|
|
264
|
+
conf.get(ADVANCED, {}),
|
|
265
|
+
check_epipolar_a_priori=True,
|
|
266
|
+
)
|
|
267
|
+
)
|
|
268
|
+
return advanced
|
|
589
269
|
|
|
270
|
+
def check_applications(self, conf):
|
|
590
271
|
"""
|
|
272
|
+
Check the given configuration for applications
|
|
591
273
|
|
|
592
|
-
|
|
274
|
+
:param conf: configuration of applications
|
|
275
|
+
:type conf: dict
|
|
276
|
+
"""
|
|
277
|
+
applications_conf = conf.get(APPLICATIONS, {})
|
|
278
|
+
# check format: contains "all" of "resolutions
|
|
593
279
|
|
|
594
|
-
|
|
280
|
+
int_keys = [int(epi_res) for epi_res in self.epipolar_resolutions]
|
|
281
|
+
string_keys = [str(key) for key in int_keys]
|
|
595
282
|
|
|
596
|
-
|
|
283
|
+
possible_keys = ["all"] + int_keys + string_keys
|
|
597
284
|
|
|
598
|
-
|
|
285
|
+
# Check conf keys in possible keys
|
|
286
|
+
for app_base_key in applications_conf.keys():
|
|
287
|
+
if app_base_key not in possible_keys:
|
|
288
|
+
raise RuntimeError(
|
|
289
|
+
"Application key {} not in possibles keys in : 'all', {} , "
|
|
290
|
+
"as int or str".format(app_base_key, string_keys)
|
|
291
|
+
)
|
|
292
|
+
|
|
293
|
+
# Key str and int key are not defined for the same resolution
|
|
294
|
+
for resolution in int_keys:
|
|
295
|
+
if (
|
|
296
|
+
resolution in applications_conf
|
|
297
|
+
and str(resolution) in applications_conf
|
|
298
|
+
):
|
|
299
|
+
raise RuntimeError(
|
|
300
|
+
"Application configuration for {} resolution "
|
|
301
|
+
"is defined both "
|
|
302
|
+
"with int and str key".format(resolution)
|
|
303
|
+
)
|
|
304
|
+
|
|
305
|
+
def cleanup_low_res_dir(self):
|
|
599
306
|
"""
|
|
600
|
-
|
|
307
|
+
Clean low res dir
|
|
601
308
|
"""
|
|
602
309
|
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
"
|
|
310
|
+
if os.path.exists(self.intermediate_data_dir) and os.path.isdir(
|
|
311
|
+
self.intermediate_data_dir
|
|
312
|
+
):
|
|
313
|
+
try:
|
|
314
|
+
shutil.rmtree(self.intermediate_data_dir)
|
|
315
|
+
logging.info(
|
|
316
|
+
f"th directory {self.intermediate_data_dir} "
|
|
317
|
+
f" has been cleaned."
|
|
610
318
|
)
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
"
|
|
614
|
-
|
|
615
|
-
+ " is not in the resolution list"
|
|
319
|
+
except Exception as exception:
|
|
320
|
+
logging.error(
|
|
321
|
+
f"Error while deleting {self.intermediate_data_dir}: "
|
|
322
|
+
f"{exception}"
|
|
616
323
|
)
|
|
324
|
+
else:
|
|
325
|
+
logging.info(
|
|
326
|
+
f"The directory {self.intermediate_data_dir} has not "
|
|
327
|
+
f"been deleted"
|
|
328
|
+
)
|
|
617
329
|
|
|
618
|
-
def
|
|
619
|
-
self,
|
|
620
|
-
conf,
|
|
621
|
-
key=None,
|
|
622
|
-
res=None,
|
|
623
|
-
last_res=False,
|
|
624
|
-
):
|
|
330
|
+
def override_with_apriori(self, conf, previous_out_dir, first_res):
|
|
625
331
|
"""
|
|
626
|
-
|
|
627
|
-
and generates needed applications for pipeline.
|
|
332
|
+
Override configuration with terrain a priori
|
|
628
333
|
|
|
629
|
-
:param
|
|
630
|
-
:type
|
|
334
|
+
:param new_conf: configuration
|
|
335
|
+
:type new_conf: dict
|
|
631
336
|
"""
|
|
632
337
|
|
|
633
|
-
|
|
634
|
-
# Application in terrain_application are note used in
|
|
635
|
-
# the sensors_to_dense_depth_maps pipeline
|
|
636
|
-
needed_applications = []
|
|
637
|
-
|
|
638
|
-
if self.sensors_in_inputs:
|
|
639
|
-
needed_applications += [
|
|
640
|
-
"grid_generation",
|
|
641
|
-
"resampling",
|
|
642
|
-
"ground_truth_reprojection",
|
|
643
|
-
"hole_detection",
|
|
644
|
-
"dense_match_filling.1",
|
|
645
|
-
"dense_match_filling.2",
|
|
646
|
-
"sparse_matching.sift",
|
|
647
|
-
"dense_matching",
|
|
648
|
-
"triangulation",
|
|
649
|
-
"dem_generation",
|
|
650
|
-
"point_cloud_outlier_removal.1",
|
|
651
|
-
"point_cloud_outlier_removal.2",
|
|
652
|
-
]
|
|
653
|
-
|
|
654
|
-
if self.save_output_dsm or self.save_output_point_cloud:
|
|
655
|
-
|
|
656
|
-
needed_applications += ["pc_denoising"]
|
|
657
|
-
|
|
658
|
-
if self.save_output_dsm:
|
|
659
|
-
needed_applications += [
|
|
660
|
-
"point_cloud_rasterization",
|
|
661
|
-
"dsm_filling.1",
|
|
662
|
-
"dsm_filling.2",
|
|
663
|
-
"dsm_filling.3",
|
|
664
|
-
"auxiliary_filling",
|
|
665
|
-
]
|
|
338
|
+
new_conf = copy.deepcopy(conf)
|
|
666
339
|
|
|
667
|
-
|
|
668
|
-
|
|
340
|
+
# Extract avanced parameters configuration
|
|
341
|
+
# epipolar and terrain a priori can only be used on first resolution
|
|
342
|
+
if not first_res:
|
|
343
|
+
dem_min = os.path.join(previous_out_dir, "dsm/dem_min.tif")
|
|
344
|
+
dem_max = os.path.join(previous_out_dir, "dsm/dem_max.tif")
|
|
345
|
+
dem_median = os.path.join(previous_out_dir, "dsm/dem_median.tif")
|
|
669
346
|
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
347
|
+
new_conf[ADVANCED][adv_cst.TERRAIN_A_PRIORI] = {
|
|
348
|
+
"dem_min": dem_min,
|
|
349
|
+
"dem_max": dem_max,
|
|
350
|
+
"dem_median": dem_median,
|
|
351
|
+
}
|
|
352
|
+
# Use initial elevation or dem median according to use wish
|
|
353
|
+
if new_conf[INPUTS][sens_cst.INITIAL_ELEVATION]["dem"] is None:
|
|
354
|
+
new_conf[INPUTS][sens_cst.INITIAL_ELEVATION] = dem_median
|
|
355
|
+
else:
|
|
356
|
+
new_conf[ADVANCED][adv_cst.TERRAIN_A_PRIORI]["dem_median"] = (
|
|
357
|
+
new_conf[INPUTS][sens_cst.INITIAL_ELEVATION]["dem"]
|
|
675
358
|
)
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
# Initialize used config
|
|
680
|
-
used_conf = {}
|
|
681
|
-
|
|
682
|
-
for app_key in [
|
|
683
|
-
"point_cloud_outlier_removal.1",
|
|
684
|
-
"point_cloud_outlier_removal.2",
|
|
685
|
-
"auxiliary_filling",
|
|
686
|
-
]:
|
|
687
|
-
if conf.get(app_key) is not None:
|
|
688
|
-
config_app = conf.get(app_key)
|
|
689
|
-
if "activated" not in config_app:
|
|
690
|
-
conf[app_key]["activated"] = True
|
|
691
|
-
|
|
692
|
-
for app_key in needed_applications:
|
|
693
|
-
used_conf[app_key] = conf.get(app_key, {})
|
|
694
|
-
used_conf[app_key]["save_intermediate_data"] = (
|
|
695
|
-
self.save_intermediate_data
|
|
696
|
-
or used_conf[app_key].get("save_intermediate_data", False)
|
|
697
|
-
)
|
|
359
|
+
if new_conf[ADVANCED][adv_cst.USE_ENDOGENOUS_DEM] and not first_res:
|
|
360
|
+
new_conf[INPUTS][sens_cst.INITIAL_ELEVATION] = dem_median
|
|
698
361
|
|
|
699
|
-
|
|
700
|
-
"point_cloud_fusion",
|
|
701
|
-
"pc_denoising",
|
|
702
|
-
]:
|
|
703
|
-
if app_key in needed_applications:
|
|
704
|
-
used_conf[app_key]["save_by_pair"] = used_conf[app_key].get(
|
|
705
|
-
"save_by_pair", self.save_all_point_clouds_by_pair
|
|
706
|
-
)
|
|
362
|
+
new_conf[ADVANCED][adv_cst.EPIPOLAR_A_PRIORI] = None
|
|
707
363
|
|
|
708
|
-
|
|
709
|
-
self.resampling_application = None
|
|
710
|
-
self.ground_truth_reprojection = None
|
|
711
|
-
self.hole_detection_app = None
|
|
712
|
-
self.dense_match_filling_1 = None
|
|
713
|
-
self.dense_match_filling_2 = None
|
|
714
|
-
self.sparse_mtch_sift_app = None
|
|
715
|
-
self.dense_matching_app = None
|
|
716
|
-
self.triangulation_application = None
|
|
717
|
-
self.dem_generation_application = None
|
|
718
|
-
self.pc_denoising_application = None
|
|
719
|
-
self.pc_outlier_removal_1_app = None
|
|
720
|
-
self.pc_outlier_removal_2_app = None
|
|
721
|
-
self.rasterization_application = None
|
|
722
|
-
self.pc_fusion_application = None
|
|
723
|
-
self.dsm_filling_1_application = None
|
|
724
|
-
self.dsm_filling_2_application = None
|
|
725
|
-
self.dsm_filling_3_application = None
|
|
726
|
-
|
|
727
|
-
if self.sensors_in_inputs:
|
|
728
|
-
# Epipolar grid generation
|
|
729
|
-
self.epipolar_grid_generation_application = Application(
|
|
730
|
-
"grid_generation", cfg=used_conf.get("grid_generation", {})
|
|
731
|
-
)
|
|
732
|
-
used_conf["grid_generation"] = (
|
|
733
|
-
self.epipolar_grid_generation_application.get_conf()
|
|
734
|
-
)
|
|
364
|
+
return new_conf
|
|
735
365
|
|
|
736
|
-
|
|
366
|
+
@cars_profile(name="Run_default_pipeline", interval=0.5)
|
|
367
|
+
def run(self, args=None): # noqa C901
|
|
368
|
+
"""
|
|
369
|
+
Run pipeline
|
|
737
370
|
|
|
738
|
-
|
|
739
|
-
"resampling", cfg=used_conf.get("resampling", {})
|
|
740
|
-
)
|
|
741
|
-
used_conf["resampling"] = self.resampling_application.get_conf()
|
|
742
|
-
|
|
743
|
-
# ground truth disparity map computation
|
|
744
|
-
if self.used_conf[key][ADVANCED][adv_cst.GROUND_TRUTH_DSM]:
|
|
745
|
-
used_conf["ground_truth_reprojection"][
|
|
746
|
-
"save_intermediate_data"
|
|
747
|
-
] = True
|
|
748
|
-
|
|
749
|
-
if isinstance(
|
|
750
|
-
self.used_conf[key][ADVANCED][adv_cst.GROUND_TRUTH_DSM], str
|
|
751
|
-
):
|
|
752
|
-
self.used_conf[key][ADVANCED][adv_cst.GROUND_TRUTH_DSM] = {
|
|
753
|
-
"dsm": self.used_conf[key][ADVANCED][
|
|
754
|
-
adv_cst.GROUND_TRUTH_DSM
|
|
755
|
-
]
|
|
756
|
-
}
|
|
757
|
-
|
|
758
|
-
self.ground_truth_reprojection = Application(
|
|
759
|
-
"ground_truth_reprojection",
|
|
760
|
-
cfg=used_conf.get("ground_truth_reprojection", {}),
|
|
761
|
-
)
|
|
762
|
-
# holes detection
|
|
763
|
-
self.hole_detection_app = Application(
|
|
764
|
-
"hole_detection", cfg=used_conf.get("hole_detection", {})
|
|
765
|
-
)
|
|
766
|
-
used_conf["hole_detection"] = self.hole_detection_app.get_conf()
|
|
767
|
-
|
|
768
|
-
# disparity filling 1 plane
|
|
769
|
-
self.dense_match_filling_1 = Application(
|
|
770
|
-
"dense_match_filling",
|
|
771
|
-
cfg=used_conf.get(
|
|
772
|
-
"dense_match_filling.1",
|
|
773
|
-
{"method": "plane"},
|
|
774
|
-
),
|
|
775
|
-
)
|
|
776
|
-
used_conf["dense_match_filling.1"] = (
|
|
777
|
-
self.dense_match_filling_1.get_conf()
|
|
778
|
-
)
|
|
371
|
+
"""
|
|
779
372
|
|
|
780
|
-
|
|
781
|
-
self.
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
)
|
|
788
|
-
used_conf["dense_match_filling.2"] = (
|
|
789
|
-
self.dense_match_filling_2.get_conf()
|
|
790
|
-
)
|
|
373
|
+
global_log_file = os.path.join(
|
|
374
|
+
self.out_dir,
|
|
375
|
+
"logs",
|
|
376
|
+
"{}_{}.log".format(
|
|
377
|
+
datetime.now().strftime("%y-%m-%d_%Hh%Mm"), "default_pipeline"
|
|
378
|
+
),
|
|
379
|
+
)
|
|
791
380
|
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
used_conf["sparse_matching.sift"] = (
|
|
798
|
-
self.sparse_mtch_sift_app.get_conf()
|
|
799
|
-
)
|
|
381
|
+
previous_out_dir = None
|
|
382
|
+
updated_conf = {}
|
|
383
|
+
for resolution_index, epipolar_res in enumerate(
|
|
384
|
+
self.epipolar_resolutions
|
|
385
|
+
):
|
|
800
386
|
|
|
801
|
-
#
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
.get(out_cst.AUXILIARY, {})
|
|
805
|
-
.get(out_cst.AUX_PERFORMANCE_MAP, False)
|
|
806
|
-
)
|
|
807
|
-
generate_ambiguity = (
|
|
808
|
-
self.used_conf[key][OUTPUT]
|
|
809
|
-
.get(out_cst.AUXILIARY, {})
|
|
810
|
-
.get(out_cst.AUX_AMBIGUITY, False)
|
|
811
|
-
)
|
|
812
|
-
dense_matching_config = used_conf.get("dense_matching", {})
|
|
813
|
-
if generate_ambiguity is True:
|
|
814
|
-
dense_matching_config["generate_ambiguity"] = True
|
|
387
|
+
# Get tested unit pipeline
|
|
388
|
+
used_pipeline = self.unit_pipelines[resolution_index]
|
|
389
|
+
current_out_dir = used_pipeline.used_conf[OUTPUT]["directory"]
|
|
815
390
|
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
dense_matching_config["performance_map_method"] = "risk"
|
|
822
|
-
self.dense_matching_app = Application(
|
|
823
|
-
"dense_matching", cfg=dense_matching_config
|
|
391
|
+
# get position
|
|
392
|
+
first_res, _, last_res = (
|
|
393
|
+
self.positions[resolution_index]["first_res"],
|
|
394
|
+
self.positions[resolution_index]["intermediate_res"],
|
|
395
|
+
self.positions[resolution_index]["last_res"],
|
|
824
396
|
)
|
|
825
|
-
used_conf["dense_matching"] = self.dense_matching_app.get_conf()
|
|
826
397
|
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
"risk",
|
|
830
|
-
"intervals",
|
|
831
|
-
]
|
|
398
|
+
# setup logging
|
|
399
|
+
loglevel = getattr(args, "loglevel", "PROGRESS").upper()
|
|
832
400
|
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
"triangulation", cfg=used_conf.get("triangulation", {})
|
|
836
|
-
)
|
|
837
|
-
used_conf["triangulation"] = (
|
|
838
|
-
self.triangulation_application.get_conf()
|
|
401
|
+
current_log_dir = os.path.join(
|
|
402
|
+
self.out_dir, "logs", "res_" + str(epipolar_res)
|
|
839
403
|
)
|
|
840
404
|
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
405
|
+
cars_logging.setup_logging(
|
|
406
|
+
loglevel,
|
|
407
|
+
out_dir=current_log_dir,
|
|
408
|
+
pipeline="unit_pipeline",
|
|
409
|
+
global_log_file=global_log_file,
|
|
844
410
|
)
|
|
845
411
|
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
height_margin = [50, 250]
|
|
849
|
-
|
|
850
|
-
used_conf["dem_generation"] = (
|
|
851
|
-
self.dem_generation_application.get_conf()
|
|
412
|
+
cars_logging.add_progress_message(
|
|
413
|
+
"Starting pipeline for resolution 1/" + str(epipolar_res)
|
|
852
414
|
)
|
|
853
415
|
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
if "point_cloud_outlier_removal.1" in used_conf:
|
|
859
|
-
if "method" not in used_conf["point_cloud_outlier_removal.1"]:
|
|
860
|
-
used_conf["point_cloud_outlier_removal.1"][
|
|
861
|
-
"method"
|
|
862
|
-
] = "small_components"
|
|
863
|
-
|
|
864
|
-
self.pc_outlier_removal_1_app = Application(
|
|
865
|
-
"point_cloud_outlier_removal",
|
|
866
|
-
cfg=used_conf.get(
|
|
867
|
-
"point_cloud_outlier_removal.1",
|
|
868
|
-
{"method": "small_components"},
|
|
869
|
-
),
|
|
870
|
-
)
|
|
416
|
+
# use sift a priori if not first
|
|
417
|
+
use_sift_a_priori = False
|
|
418
|
+
if not first_res:
|
|
419
|
+
use_sift_a_priori = True
|
|
871
420
|
|
|
872
|
-
|
|
873
|
-
if
|
|
874
|
-
"
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
421
|
+
# define wich resolution
|
|
422
|
+
if first_res and last_res:
|
|
423
|
+
which_resolution = "single"
|
|
424
|
+
elif first_res:
|
|
425
|
+
which_resolution = "first"
|
|
426
|
+
elif last_res:
|
|
427
|
+
which_resolution = "final"
|
|
428
|
+
else:
|
|
429
|
+
which_resolution = "intermediate"
|
|
880
430
|
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
431
|
+
# Generate dem
|
|
432
|
+
generate_dems = True
|
|
433
|
+
if last_res:
|
|
434
|
+
generate_dems = False
|
|
884
435
|
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
self.pc_outlier_removal_2_app = Application(
|
|
892
|
-
"point_cloud_outlier_removal",
|
|
893
|
-
cfg=used_conf.get(
|
|
894
|
-
"point_cloud_outlier_removal.2",
|
|
895
|
-
{"method": "statistical"},
|
|
896
|
-
),
|
|
436
|
+
# Overide with a priori
|
|
437
|
+
overridden_conf = self.override_with_apriori(
|
|
438
|
+
used_pipeline.used_conf, previous_out_dir, first_res
|
|
439
|
+
)
|
|
440
|
+
updated_pipeline = UnitPipeline(
|
|
441
|
+
overridden_conf, config_dir=self.config_dir
|
|
897
442
|
)
|
|
898
|
-
|
|
899
|
-
|
|
443
|
+
updated_pipeline.run(
|
|
444
|
+
generate_dems=generate_dems,
|
|
445
|
+
which_resolution=which_resolution,
|
|
446
|
+
use_sift_a_priori=use_sift_a_priori,
|
|
447
|
+
first_res_out_dir=self.first_res_out_dir_with_sensors,
|
|
448
|
+
log_dir=current_log_dir,
|
|
900
449
|
)
|
|
901
450
|
|
|
902
|
-
|
|
451
|
+
# update previous out dir
|
|
452
|
+
previous_out_dir = current_out_dir
|
|
903
453
|
|
|
904
|
-
#
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
cfg=used_conf.get("pc_denoising", {"method": "none"}),
|
|
454
|
+
# generate summary
|
|
455
|
+
log_wrapper.generate_summary(
|
|
456
|
+
current_log_dir, updated_pipeline.used_conf
|
|
908
457
|
)
|
|
909
|
-
used_conf["pc_denoising"] = self.pc_denoising_application.get_conf()
|
|
910
458
|
|
|
911
|
-
|
|
459
|
+
updated_conf[epipolar_res] = updated_pipeline.used_conf
|
|
912
460
|
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
"dsm_filling",
|
|
924
|
-
cfg=conf.get(
|
|
925
|
-
"dsm_filling.1",
|
|
926
|
-
{"method": "exogenous_filling"},
|
|
927
|
-
),
|
|
928
|
-
)
|
|
929
|
-
used_conf["dsm_filling.1"] = (
|
|
930
|
-
self.dsm_filling_1_application.get_conf()
|
|
931
|
-
)
|
|
932
|
-
# DSM filling 2 : Bulldozer
|
|
933
|
-
self.dsm_filling_2_application = Application(
|
|
934
|
-
"dsm_filling",
|
|
935
|
-
cfg=conf.get(
|
|
936
|
-
"dsm_filling.2",
|
|
937
|
-
{"method": "bulldozer"},
|
|
938
|
-
),
|
|
939
|
-
)
|
|
940
|
-
used_conf["dsm_filling.2"] = (
|
|
941
|
-
self.dsm_filling_2_application.get_conf()
|
|
942
|
-
)
|
|
943
|
-
# DSM filling 3 : Border interpolation
|
|
944
|
-
self.dsm_filling_3_application = Application(
|
|
945
|
-
"dsm_filling",
|
|
946
|
-
cfg=conf.get(
|
|
947
|
-
"dsm_filling.3",
|
|
948
|
-
{"method": "border_interpolation"},
|
|
949
|
-
),
|
|
950
|
-
)
|
|
951
|
-
used_conf["dsm_filling.3"] = (
|
|
952
|
-
self.dsm_filling_3_application.get_conf()
|
|
953
|
-
)
|
|
954
|
-
# Auxiliary filling
|
|
955
|
-
self.auxiliary_filling_application = Application(
|
|
956
|
-
"auxiliary_filling", cfg=conf.get("auxiliary_filling", {})
|
|
957
|
-
)
|
|
958
|
-
used_conf["auxiliary_filling"] = (
|
|
959
|
-
self.auxiliary_filling_application.get_conf()
|
|
960
|
-
)
|
|
461
|
+
# Generate full used_conf
|
|
462
|
+
full_used_conf = merge_used_conf(
|
|
463
|
+
updated_conf, self.epipolar_resolutions
|
|
464
|
+
)
|
|
465
|
+
# Save used_conf
|
|
466
|
+
cars_dataset.save_dict(
|
|
467
|
+
full_used_conf,
|
|
468
|
+
os.path.join(self.out_dir, "global_used_conf.json"),
|
|
469
|
+
safe_save=True,
|
|
470
|
+
)
|
|
961
471
|
|
|
962
|
-
|
|
472
|
+
# Merge profiling in pdf
|
|
473
|
+
log_wrapper.generate_pdf_profiling(os.path.join(self.out_dir, "logs"))
|
|
963
474
|
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
cfg=used_conf.get("point_cloud_fusion", {}),
|
|
968
|
-
)
|
|
969
|
-
used_conf["point_cloud_fusion"] = (
|
|
970
|
-
self.pc_fusion_application.get_conf()
|
|
971
|
-
)
|
|
475
|
+
# clean outdir
|
|
476
|
+
if not self.keep_low_res_dir:
|
|
477
|
+
self.cleanup_low_res_dir()
|
|
972
478
|
|
|
973
|
-
return used_conf
|
|
974
479
|
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
Check for each application the input and output configuration
|
|
980
|
-
consistency
|
|
480
|
+
def extract_applications(current_applications_conf, res, default_conf_for_res):
|
|
481
|
+
"""
|
|
482
|
+
Extract applications for current resolution
|
|
483
|
+
"""
|
|
981
484
|
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
485
|
+
# "all" : applied to all conf
|
|
486
|
+
# int (1, 2, 4, 8, 16, ...) applied for specified resolution
|
|
487
|
+
|
|
488
|
+
all_conf = current_applications_conf.get("all", {})
|
|
489
|
+
# Overide with default_conf_for_res
|
|
490
|
+
all_conf = overide_pipeline_conf(all_conf, default_conf_for_res)
|
|
491
|
+
# Get configuration for current res
|
|
492
|
+
if res in current_applications_conf:
|
|
493
|
+
# key is int
|
|
494
|
+
key = res
|
|
495
|
+
else:
|
|
496
|
+
key = str(res)
|
|
497
|
+
|
|
498
|
+
res_conf = current_applications_conf.get(key, {})
|
|
499
|
+
|
|
500
|
+
new_application_conf = overide_pipeline_conf(all_conf, res_conf)
|
|
501
|
+
return new_application_conf
|
|
502
|
+
|
|
503
|
+
|
|
504
|
+
# pylint: disable=too-many-positional-arguments
|
|
505
|
+
def extract_conf_with_resolution(
|
|
506
|
+
current_conf,
|
|
507
|
+
res,
|
|
508
|
+
first_res,
|
|
509
|
+
intermediate_res,
|
|
510
|
+
last_res,
|
|
511
|
+
intermediate_data_dir,
|
|
512
|
+
):
|
|
513
|
+
"""
|
|
514
|
+
Extract the configuration for the given resolution
|
|
515
|
+
|
|
516
|
+
:param current_conf: current configuration
|
|
517
|
+
:type current_conf: dict
|
|
518
|
+
:param res: resolution to extract
|
|
519
|
+
:type res: int
|
|
520
|
+
:return: configuration for the given resolution
|
|
521
|
+
:rtype: dict
|
|
522
|
+
:param first_res: is first resolution
|
|
523
|
+
:type first_res: bool
|
|
524
|
+
:param intermediate_res: is intermediate resolution
|
|
525
|
+
:type intermediate_res: bool
|
|
526
|
+
:param last_res: is last resolution
|
|
527
|
+
:type last_res: bool
|
|
528
|
+
:param previous_out_dir: path to previous outdir
|
|
529
|
+
:type: previous_out_dir: str
|
|
530
|
+
"""
|
|
987
531
|
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
self.sparse_mtch_sift_app.used_config[
|
|
993
|
-
"elevation_delta_lower_bound"
|
|
994
|
-
] = (-500 if initial_elevation else -1000)
|
|
995
|
-
self.sparse_mtch_sift_app.elevation_delta_lower_bound = (
|
|
996
|
-
self.sparse_mtch_sift_app.used_config[
|
|
997
|
-
"elevation_delta_lower_bound"
|
|
998
|
-
]
|
|
999
|
-
)
|
|
1000
|
-
if self.sparse_mtch_sift_app.elevation_delta_upper_bound is None:
|
|
1001
|
-
self.sparse_mtch_sift_app.used_config[
|
|
1002
|
-
"elevation_delta_upper_bound"
|
|
1003
|
-
] = (1000 if initial_elevation else 9000)
|
|
1004
|
-
self.sparse_mtch_sift_app.elevation_delta_upper_bound = (
|
|
1005
|
-
self.sparse_mtch_sift_app.used_config[
|
|
1006
|
-
"elevation_delta_upper_bound"
|
|
1007
|
-
]
|
|
1008
|
-
)
|
|
1009
|
-
application_conf["sparse_matching.sift"] = (
|
|
1010
|
-
self.sparse_mtch_sift_app.get_conf()
|
|
532
|
+
new_dir_out_dir = current_conf[OUTPUT][out_cst.OUT_DIRECTORY]
|
|
533
|
+
if not last_res:
|
|
534
|
+
new_dir_out_dir = os.path.join(
|
|
535
|
+
intermediate_data_dir, "out_res" + str(res)
|
|
1011
536
|
)
|
|
537
|
+
safe_makedirs(new_dir_out_dir)
|
|
538
|
+
|
|
539
|
+
new_conf = copy.deepcopy(current_conf)
|
|
540
|
+
|
|
541
|
+
# Get save intermediate data
|
|
542
|
+
if isinstance(new_conf[ADVANCED][adv_cst.SAVE_INTERMEDIATE_DATA], dict):
|
|
543
|
+
# If save_intermediate_data is not a dict, we set it to False
|
|
544
|
+
new_conf[ADVANCED][adv_cst.SAVE_INTERMEDIATE_DATA] = new_conf[ADVANCED][
|
|
545
|
+
adv_cst.SAVE_INTERMEDIATE_DATA
|
|
546
|
+
].get("resolution_" + str(res), False)
|
|
547
|
+
|
|
548
|
+
# Overide epipolar resolution
|
|
549
|
+
new_conf[ADVANCED][adv_cst.EPIPOLAR_RESOLUTIONS] = res
|
|
550
|
+
|
|
551
|
+
# Overide configuration with pipeline conf
|
|
552
|
+
if first_res:
|
|
553
|
+
# read the first resolution conf with json package
|
|
554
|
+
with open(PIPELINE_CONFS[FIRST_RES], "r", encoding="utf-8") as file:
|
|
555
|
+
overiding_conf = json.load(file)
|
|
556
|
+
elif intermediate_res:
|
|
557
|
+
with open(
|
|
558
|
+
PIPELINE_CONFS[INTERMEDIATE_RES], "r", encoding="utf-8"
|
|
559
|
+
) as file:
|
|
560
|
+
overiding_conf = json.load(file)
|
|
561
|
+
else:
|
|
562
|
+
with open(PIPELINE_CONFS[FINAL_RES], "r", encoding="utf-8") as file:
|
|
563
|
+
overiding_conf = json.load(file)
|
|
564
|
+
|
|
565
|
+
# extract application
|
|
566
|
+
new_conf[APPLICATIONS] = extract_applications(
|
|
567
|
+
current_conf.get(APPLICATIONS, {}),
|
|
568
|
+
res,
|
|
569
|
+
overiding_conf.get(APPLICATIONS, {}),
|
|
570
|
+
)
|
|
571
|
+
|
|
572
|
+
# Overide output to not compute data
|
|
573
|
+
# Overide resolution to let unit pipeline manage it
|
|
574
|
+
if not last_res:
|
|
575
|
+
overiding_conf = {
|
|
576
|
+
OUTPUT: {
|
|
577
|
+
out_cst.OUT_DIRECTORY: new_dir_out_dir,
|
|
578
|
+
out_cst.RESOLUTION: None,
|
|
579
|
+
out_cst.SAVE_BY_PAIR: True,
|
|
580
|
+
out_cst.AUXILIARY: {
|
|
581
|
+
out_cst.AUX_DEM_MAX: True,
|
|
582
|
+
out_cst.AUX_DEM_MIN: True,
|
|
583
|
+
out_cst.AUX_DEM_MEDIAN: True,
|
|
584
|
+
},
|
|
585
|
+
},
|
|
586
|
+
APPLICATIONS: {
|
|
587
|
+
"dense_matching": {
|
|
588
|
+
"performance_map_method": ["risk", "intervals"]
|
|
589
|
+
}
|
|
590
|
+
},
|
|
591
|
+
}
|
|
592
|
+
new_conf = overide_pipeline_conf(new_conf, overiding_conf)
|
|
1012
593
|
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
"image"
|
|
1019
|
-
]["main_file"]
|
|
1020
|
-
first_image_size = rasterio_get_size(first_image_path)
|
|
1021
|
-
first_image_nb_pixels = math.prod(first_image_size)
|
|
1022
|
-
dem_gen_used_mem = first_image_nb_pixels / 1e8
|
|
1023
|
-
if dem_gen_used_mem > 8:
|
|
1024
|
-
logging.warning(
|
|
1025
|
-
"DEM generation method is 'bulldozer_on_raster'. "
|
|
1026
|
-
f"This method can use up to {dem_gen_used_mem} Gb "
|
|
1027
|
-
"of memory. If you think that it is too much for "
|
|
1028
|
-
"your computer, you can re-lauch the run using "
|
|
1029
|
-
"'dichotomic' method for DEM generation"
|
|
1030
|
-
)
|
|
1031
|
-
|
|
1032
|
-
# check classification application parameter compare
|
|
1033
|
-
# to each sensors inputs classification list
|
|
1034
|
-
|
|
1035
|
-
for application_key in application_conf:
|
|
1036
|
-
if "classification" in application_conf[application_key]:
|
|
1037
|
-
for item in inputs_conf["sensors"]:
|
|
1038
|
-
if "classification" in inputs_conf["sensors"][item].keys():
|
|
1039
|
-
if inputs_conf["sensors"][item]["classification"]:
|
|
1040
|
-
descriptions = get_descriptions_bands(
|
|
1041
|
-
inputs_conf["sensors"][item]["classification"]
|
|
1042
|
-
)
|
|
1043
|
-
if application_conf[application_key][
|
|
1044
|
-
"classification"
|
|
1045
|
-
] and not set(
|
|
1046
|
-
application_conf[application_key][
|
|
1047
|
-
"classification"
|
|
1048
|
-
]
|
|
1049
|
-
).issubset(
|
|
1050
|
-
set(descriptions) | {"nodata"}
|
|
1051
|
-
):
|
|
1052
|
-
raise RuntimeError(
|
|
1053
|
-
"The {} bands description {} ".format(
|
|
1054
|
-
inputs_conf["sensors"][item][
|
|
1055
|
-
"classification"
|
|
1056
|
-
],
|
|
1057
|
-
list(descriptions),
|
|
1058
|
-
)
|
|
1059
|
-
+ "and the {} config are not ".format(
|
|
1060
|
-
application_key
|
|
1061
|
-
)
|
|
1062
|
-
+ "consistent: {}".format(
|
|
1063
|
-
application_conf[application_key][
|
|
1064
|
-
"classification"
|
|
1065
|
-
]
|
|
1066
|
-
)
|
|
1067
|
-
)
|
|
1068
|
-
for key1, key2 in inputs_conf["pairing"]:
|
|
1069
|
-
corr_cfg = self.dense_matching_app.loader.get_conf()
|
|
1070
|
-
img_left = inputs_conf["sensors"][key1]["image"]["main_file"]
|
|
1071
|
-
img_right = inputs_conf["sensors"][key2]["image"]["main_file"]
|
|
1072
|
-
bands_left = list(
|
|
1073
|
-
inputs_conf["sensors"][key1]["image"]["bands"].keys()
|
|
1074
|
-
)
|
|
1075
|
-
bands_right = list(
|
|
1076
|
-
inputs_conf["sensors"][key2]["image"]["bands"].keys()
|
|
1077
|
-
)
|
|
1078
|
-
classif_left = None
|
|
1079
|
-
classif_right = None
|
|
1080
|
-
if (
|
|
1081
|
-
"classification" in inputs_conf["sensors"][key1]
|
|
1082
|
-
and inputs_conf["sensors"][key1]["classification"] is not None
|
|
1083
|
-
):
|
|
1084
|
-
classif_left = inputs_conf["sensors"][key1]["classification"][
|
|
1085
|
-
"main_file"
|
|
1086
|
-
]
|
|
1087
|
-
if (
|
|
1088
|
-
"classification" in inputs_conf["sensors"][key2]
|
|
1089
|
-
and inputs_conf["sensors"][key1]["classification"] is not None
|
|
1090
|
-
):
|
|
1091
|
-
classif_right = inputs_conf["sensors"][key2]["classification"][
|
|
1092
|
-
"main_file"
|
|
1093
|
-
]
|
|
1094
|
-
self.dense_matching_app.corr_config = (
|
|
1095
|
-
self.dense_matching_app.loader.check_conf(
|
|
1096
|
-
corr_cfg,
|
|
1097
|
-
img_left,
|
|
1098
|
-
img_right,
|
|
1099
|
-
bands_left,
|
|
1100
|
-
bands_right,
|
|
1101
|
-
classif_left,
|
|
1102
|
-
classif_right,
|
|
1103
|
-
)
|
|
1104
|
-
)
|
|
594
|
+
# set product level to dsm
|
|
595
|
+
new_conf[OUTPUT][out_cst.PRODUCT_LEVEL] = ["dsm"]
|
|
596
|
+
# remove resolution to let CARS compute it for current
|
|
597
|
+
# epipolar resolution
|
|
598
|
+
new_conf[OUTPUT]["resolution"] = None
|
|
1105
599
|
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
]
|
|
1113
|
-
first_image_size = rasterio_get_size(first_image_path)
|
|
1114
|
-
size_low_res_img_row = first_image_size[0] // res
|
|
1115
|
-
size_low_res_img_col = first_image_size[1] // res
|
|
1116
|
-
if (
|
|
1117
|
-
"grid_generation" not in initial_conf_app
|
|
1118
|
-
or "epi_step" not in initial_conf_app["grid_generation"]
|
|
1119
|
-
):
|
|
1120
|
-
if size_low_res_img_row <= 900 and size_low_res_img_col <= 900:
|
|
1121
|
-
application_conf["grid_generation"]["epi_step"] = res * 5
|
|
1122
|
-
else:
|
|
1123
|
-
application_conf["grid_generation"]["epi_step"] = res * 30
|
|
600
|
+
if not new_conf[ADVANCED][adv_cst.SAVE_INTERMEDIATE_DATA]:
|
|
601
|
+
# Save the less possible things
|
|
602
|
+
aux_items = new_conf[OUTPUT][out_cst.AUXILIARY].items()
|
|
603
|
+
for aux_key, _ in aux_items:
|
|
604
|
+
if aux_key not in ("dem_min", "dem_max", "dem_median"):
|
|
605
|
+
new_conf[OUTPUT][out_cst.AUXILIARY][aux_key] = False
|
|
1124
606
|
|
|
1125
|
-
|
|
607
|
+
return new_conf
|
|
1126
608
|
|
|
1127
|
-
def cleanup_low_res_dir(self):
|
|
1128
|
-
"""
|
|
1129
|
-
Clean low res dir
|
|
1130
|
-
"""
|
|
1131
609
|
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
610
|
+
def overide_pipeline_conf(conf, overiding_conf):
|
|
611
|
+
"""
|
|
612
|
+
Merge two dictionaries recursively without removing keys from the base conf.
|
|
613
|
+
|
|
614
|
+
:param conf: base configuration dictionary
|
|
615
|
+
:type conf: dict
|
|
616
|
+
:param overiding_conf: overriding configuration dictionary
|
|
617
|
+
:type overiding_conf: dict
|
|
618
|
+
:return: merged configuration
|
|
619
|
+
:rtype: dict
|
|
620
|
+
"""
|
|
621
|
+
result = copy.deepcopy(conf)
|
|
1143
622
|
|
|
1144
|
-
|
|
1145
|
-
def run(self, args=None): # noqa C901
|
|
623
|
+
def merge_recursive(base_dict, override_dict):
|
|
1146
624
|
"""
|
|
1147
|
-
|
|
1148
|
-
|
|
625
|
+
Main recursive function
|
|
1149
626
|
"""
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
i = 0
|
|
1158
|
-
nb_res = len(list(self.used_conf.items()))
|
|
1159
|
-
for key, conf_res in self.used_conf.items():
|
|
1160
|
-
out_dir = conf_res[OUTPUT][out_cst.OUT_DIRECTORY]
|
|
1161
|
-
|
|
1162
|
-
if nb_res != 1 and args is not None:
|
|
1163
|
-
# Logging configuration with args Loglevel
|
|
1164
|
-
loglevel = getattr(args, "loglevel", "PROGRESS").upper()
|
|
1165
|
-
|
|
1166
|
-
cars_logging.setup_logging(
|
|
1167
|
-
loglevel,
|
|
1168
|
-
out_dir=os.path.join(out_dir, "logs"),
|
|
1169
|
-
pipeline="",
|
|
1170
|
-
)
|
|
1171
|
-
|
|
1172
|
-
if int(key.split("_")[-1]) != 1:
|
|
1173
|
-
cars_logging.add_progress_message(
|
|
1174
|
-
"Starting pipeline for resolution 1/" + key.split("_")[-1]
|
|
1175
|
-
)
|
|
1176
|
-
else:
|
|
1177
|
-
cars_logging.add_progress_message(
|
|
1178
|
-
"Starting pipeline for resolution 1"
|
|
1179
|
-
)
|
|
1180
|
-
|
|
1181
|
-
# Get the resolution step
|
|
1182
|
-
if previous_out_dir is not None:
|
|
1183
|
-
if i == len(self.used_conf) - 1:
|
|
1184
|
-
which_resolution = "final"
|
|
1185
|
-
generate_dems = False
|
|
1186
|
-
else:
|
|
1187
|
-
which_resolution = "intermediate"
|
|
627
|
+
for key, value in override_dict.items():
|
|
628
|
+
if (
|
|
629
|
+
key in base_dict
|
|
630
|
+
and isinstance(base_dict[key], dict)
|
|
631
|
+
and isinstance(value, dict)
|
|
632
|
+
):
|
|
633
|
+
merge_recursive(base_dict[key], value)
|
|
1188
634
|
else:
|
|
1189
|
-
|
|
1190
|
-
which_resolution = "single"
|
|
1191
|
-
else:
|
|
1192
|
-
which_resolution = "first"
|
|
1193
|
-
first_res_out_dir = out_dir
|
|
1194
|
-
|
|
1195
|
-
# Build a priori
|
|
1196
|
-
if which_resolution in ("final", "intermediate"):
|
|
1197
|
-
dem_min = os.path.join(previous_out_dir, "dsm/dem_min.tif")
|
|
1198
|
-
dem_max = os.path.join(previous_out_dir, "dsm/dem_max.tif")
|
|
1199
|
-
dem_median = os.path.join(
|
|
1200
|
-
previous_out_dir, "dsm/dem_median.tif"
|
|
1201
|
-
)
|
|
635
|
+
base_dict[key] = value
|
|
1202
636
|
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
"dem_max": dem_max,
|
|
1206
|
-
"dem_median": dem_median,
|
|
1207
|
-
}
|
|
637
|
+
merge_recursive(result, overiding_conf)
|
|
638
|
+
return result
|
|
1208
639
|
|
|
1209
|
-
if conf_res[INPUTS][sens_cst.INITIAL_ELEVATION]["dem"] is None:
|
|
1210
|
-
conf_res[INPUTS][sens_cst.INITIAL_ELEVATION] = dem_median
|
|
1211
|
-
else:
|
|
1212
|
-
conf_res[ADVANCED][adv_cst.TERRAIN_A_PRIORI][
|
|
1213
|
-
"dem_median"
|
|
1214
|
-
] = conf_res[INPUTS][sens_cst.INITIAL_ELEVATION]["dem"]
|
|
1215
640
|
|
|
1216
|
-
|
|
1217
|
-
|
|
641
|
+
def merge_used_conf(used_configurations, epipolar_resolutions):
|
|
642
|
+
"""
|
|
643
|
+
Merge all used configuration
|
|
644
|
+
"""
|
|
645
|
+
used_configurations = copy.deepcopy(used_configurations)
|
|
1218
646
|
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
out_dir,
|
|
1225
|
-
out_cst.INFO_FILENAME,
|
|
1226
|
-
),
|
|
1227
|
-
) as self.cars_orchestrator:
|
|
1228
|
-
|
|
1229
|
-
# initialize out_json
|
|
1230
|
-
self.cars_orchestrator.update_out_info({"version": __version__})
|
|
1231
|
-
|
|
1232
|
-
used_pipeline = UnitPipeline(conf_res)
|
|
1233
|
-
|
|
1234
|
-
used_pipeline.run(
|
|
1235
|
-
self.cars_orchestrator,
|
|
1236
|
-
generate_dems,
|
|
1237
|
-
which_resolution,
|
|
1238
|
-
use_sift_a_priori,
|
|
1239
|
-
first_res_out_dir,
|
|
1240
|
-
final_out_dir,
|
|
1241
|
-
)
|
|
647
|
+
merged_conf = {
|
|
648
|
+
INPUTS: used_configurations[epipolar_resolutions[0]][INPUTS],
|
|
649
|
+
ADVANCED: used_configurations[epipolar_resolutions[0]][ADVANCED],
|
|
650
|
+
OUTPUT: used_configurations[epipolar_resolutions[0]][OUTPUT],
|
|
651
|
+
}
|
|
1242
652
|
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
log_wrapper.generate_summary(
|
|
1246
|
-
out_dir, used_pipeline.used_conf, clean_worker_logs=True
|
|
1247
|
-
)
|
|
653
|
+
merged_conf[APPLICATIONS] = {}
|
|
654
|
+
merged_conf[APPLICATIONS]["all"] = {}
|
|
1248
655
|
|
|
1249
|
-
|
|
1250
|
-
|
|
656
|
+
# Merge applications
|
|
657
|
+
for res in epipolar_resolutions:
|
|
658
|
+
merged_conf[APPLICATIONS][res] = used_configurations[res][APPLICATIONS]
|
|
1251
659
|
|
|
1252
|
-
|
|
1253
|
-
|
|
660
|
+
# apply epipolar resolutions
|
|
661
|
+
merged_conf[ADVANCED]["epipolar_resolutions"] = epipolar_resolutions
|
|
662
|
+
return merged_conf
|