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