cars 1.0.0rc3__cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.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.
Files changed (220) hide show
  1. cars/__init__.py +74 -0
  2. cars/applications/__init__.py +40 -0
  3. cars/applications/application.py +117 -0
  4. cars/applications/application_constants.py +29 -0
  5. cars/applications/application_template.py +146 -0
  6. cars/applications/auxiliary_filling/__init__.py +29 -0
  7. cars/applications/auxiliary_filling/abstract_auxiliary_filling_app.py +105 -0
  8. cars/applications/auxiliary_filling/auxiliary_filling_algo.py +475 -0
  9. cars/applications/auxiliary_filling/auxiliary_filling_from_sensors_app.py +632 -0
  10. cars/applications/auxiliary_filling/auxiliary_filling_wrappers.py +90 -0
  11. cars/applications/dem_generation/__init__.py +30 -0
  12. cars/applications/dem_generation/abstract_dem_generation_app.py +116 -0
  13. cars/applications/dem_generation/bulldozer_config/base_config.yaml +46 -0
  14. cars/applications/dem_generation/bulldozer_dem_app.py +641 -0
  15. cars/applications/dem_generation/bulldozer_memory.py +55 -0
  16. cars/applications/dem_generation/dem_generation_algo.py +107 -0
  17. cars/applications/dem_generation/dem_generation_constants.py +32 -0
  18. cars/applications/dem_generation/dem_generation_wrappers.py +323 -0
  19. cars/applications/dense_match_filling/__init__.py +30 -0
  20. cars/applications/dense_match_filling/abstract_dense_match_filling_app.py +242 -0
  21. cars/applications/dense_match_filling/fill_disp_algo.py +113 -0
  22. cars/applications/dense_match_filling/fill_disp_constants.py +39 -0
  23. cars/applications/dense_match_filling/fill_disp_wrappers.py +83 -0
  24. cars/applications/dense_match_filling/zero_padding_app.py +302 -0
  25. cars/applications/dense_matching/__init__.py +30 -0
  26. cars/applications/dense_matching/abstract_dense_matching_app.py +261 -0
  27. cars/applications/dense_matching/census_mccnn_sgm_app.py +1461 -0
  28. cars/applications/dense_matching/cpp/__init__.py +0 -0
  29. cars/applications/dense_matching/cpp/dense_matching_cpp.cpython-313-x86_64-linux-gnu.so +0 -0
  30. cars/applications/dense_matching/cpp/dense_matching_cpp.py +94 -0
  31. cars/applications/dense_matching/cpp/includes/dense_matching.hpp +58 -0
  32. cars/applications/dense_matching/cpp/meson.build +9 -0
  33. cars/applications/dense_matching/cpp/src/bindings.cpp +13 -0
  34. cars/applications/dense_matching/cpp/src/dense_matching.cpp +207 -0
  35. cars/applications/dense_matching/dense_matching_algo.py +401 -0
  36. cars/applications/dense_matching/dense_matching_constants.py +89 -0
  37. cars/applications/dense_matching/dense_matching_wrappers.py +951 -0
  38. cars/applications/dense_matching/disparity_grid_algo.py +597 -0
  39. cars/applications/dense_matching/loaders/__init__.py +23 -0
  40. cars/applications/dense_matching/loaders/config_census_sgm_default.json +31 -0
  41. cars/applications/dense_matching/loaders/config_census_sgm_homogeneous.json +30 -0
  42. cars/applications/dense_matching/loaders/config_census_sgm_mountain_and_vegetation.json +30 -0
  43. cars/applications/dense_matching/loaders/config_census_sgm_shadow.json +30 -0
  44. cars/applications/dense_matching/loaders/config_census_sgm_sparse.json +36 -0
  45. cars/applications/dense_matching/loaders/config_census_sgm_urban.json +30 -0
  46. cars/applications/dense_matching/loaders/config_mapping.json +13 -0
  47. cars/applications/dense_matching/loaders/config_mccnn.json +28 -0
  48. cars/applications/dense_matching/loaders/global_land_cover_map.tif +0 -0
  49. cars/applications/dense_matching/loaders/pandora_loader.py +593 -0
  50. cars/applications/dsm_filling/__init__.py +32 -0
  51. cars/applications/dsm_filling/abstract_dsm_filling_app.py +101 -0
  52. cars/applications/dsm_filling/border_interpolation_app.py +278 -0
  53. cars/applications/dsm_filling/bulldozer_config/base_config.yaml +44 -0
  54. cars/applications/dsm_filling/bulldozer_filling_app.py +288 -0
  55. cars/applications/dsm_filling/exogenous_filling_app.py +341 -0
  56. cars/applications/dsm_merging/__init__.py +28 -0
  57. cars/applications/dsm_merging/abstract_dsm_merging_app.py +101 -0
  58. cars/applications/dsm_merging/weighted_fusion_app.py +639 -0
  59. cars/applications/grid_correction/__init__.py +30 -0
  60. cars/applications/grid_correction/abstract_grid_correction_app.py +103 -0
  61. cars/applications/grid_correction/grid_correction_app.py +557 -0
  62. cars/applications/grid_generation/__init__.py +30 -0
  63. cars/applications/grid_generation/abstract_grid_generation_app.py +142 -0
  64. cars/applications/grid_generation/epipolar_grid_generation_app.py +327 -0
  65. cars/applications/grid_generation/grid_generation_algo.py +388 -0
  66. cars/applications/grid_generation/grid_generation_constants.py +46 -0
  67. cars/applications/grid_generation/transform_grid.py +88 -0
  68. cars/applications/ground_truth_reprojection/__init__.py +30 -0
  69. cars/applications/ground_truth_reprojection/abstract_ground_truth_reprojection_app.py +137 -0
  70. cars/applications/ground_truth_reprojection/direct_localization_app.py +629 -0
  71. cars/applications/ground_truth_reprojection/ground_truth_reprojection_algo.py +275 -0
  72. cars/applications/point_cloud_outlier_removal/__init__.py +30 -0
  73. cars/applications/point_cloud_outlier_removal/abstract_outlier_removal_app.py +385 -0
  74. cars/applications/point_cloud_outlier_removal/outlier_removal_algo.py +392 -0
  75. cars/applications/point_cloud_outlier_removal/outlier_removal_constants.py +43 -0
  76. cars/applications/point_cloud_outlier_removal/small_components_app.py +522 -0
  77. cars/applications/point_cloud_outlier_removal/statistical_app.py +528 -0
  78. cars/applications/rasterization/__init__.py +30 -0
  79. cars/applications/rasterization/abstract_pc_rasterization_app.py +183 -0
  80. cars/applications/rasterization/rasterization_algo.py +534 -0
  81. cars/applications/rasterization/rasterization_constants.py +38 -0
  82. cars/applications/rasterization/rasterization_wrappers.py +639 -0
  83. cars/applications/rasterization/simple_gaussian_app.py +1152 -0
  84. cars/applications/resampling/__init__.py +28 -0
  85. cars/applications/resampling/abstract_resampling_app.py +187 -0
  86. cars/applications/resampling/bicubic_resampling_app.py +760 -0
  87. cars/applications/resampling/resampling_algo.py +590 -0
  88. cars/applications/resampling/resampling_constants.py +36 -0
  89. cars/applications/resampling/resampling_wrappers.py +309 -0
  90. cars/applications/sensors_subsampling/__init__.py +32 -0
  91. cars/applications/sensors_subsampling/abstract_subsampling_app.py +109 -0
  92. cars/applications/sensors_subsampling/rasterio_subsampling_app.py +420 -0
  93. cars/applications/sensors_subsampling/subsampling_algo.py +108 -0
  94. cars/applications/sparse_matching/__init__.py +30 -0
  95. cars/applications/sparse_matching/abstract_sparse_matching_app.py +599 -0
  96. cars/applications/sparse_matching/sift_app.py +724 -0
  97. cars/applications/sparse_matching/sparse_matching_algo.py +360 -0
  98. cars/applications/sparse_matching/sparse_matching_constants.py +66 -0
  99. cars/applications/sparse_matching/sparse_matching_wrappers.py +282 -0
  100. cars/applications/triangulation/__init__.py +32 -0
  101. cars/applications/triangulation/abstract_triangulation_app.py +227 -0
  102. cars/applications/triangulation/line_of_sight_intersection_app.py +1243 -0
  103. cars/applications/triangulation/pc_transform.py +552 -0
  104. cars/applications/triangulation/triangulation_algo.py +371 -0
  105. cars/applications/triangulation/triangulation_constants.py +38 -0
  106. cars/applications/triangulation/triangulation_wrappers.py +259 -0
  107. cars/bundleadjustment.py +750 -0
  108. cars/cars.py +179 -0
  109. cars/conf/__init__.py +23 -0
  110. cars/conf/geoid/egm96.grd +0 -0
  111. cars/conf/geoid/egm96.grd.hdr +15 -0
  112. cars/conf/input_parameters.py +156 -0
  113. cars/conf/mask_cst.py +35 -0
  114. cars/core/__init__.py +23 -0
  115. cars/core/cars_logging.py +402 -0
  116. cars/core/constants.py +191 -0
  117. cars/core/constants_disparity.py +50 -0
  118. cars/core/datasets.py +140 -0
  119. cars/core/geometry/__init__.py +27 -0
  120. cars/core/geometry/abstract_geometry.py +1130 -0
  121. cars/core/geometry/shareloc_geometry.py +604 -0
  122. cars/core/inputs.py +568 -0
  123. cars/core/outputs.py +176 -0
  124. cars/core/preprocessing.py +722 -0
  125. cars/core/projection.py +843 -0
  126. cars/core/roi_tools.py +215 -0
  127. cars/core/tiling.py +774 -0
  128. cars/core/utils.py +164 -0
  129. cars/data_structures/__init__.py +23 -0
  130. cars/data_structures/cars_dataset.py +1544 -0
  131. cars/data_structures/cars_dict.py +74 -0
  132. cars/data_structures/corresponding_tiles_tools.py +186 -0
  133. cars/data_structures/dataframe_converter.py +185 -0
  134. cars/data_structures/format_transformation.py +297 -0
  135. cars/devibrate.py +689 -0
  136. cars/extractroi.py +264 -0
  137. cars/orchestrator/__init__.py +23 -0
  138. cars/orchestrator/achievement_tracker.py +125 -0
  139. cars/orchestrator/cluster/__init__.py +37 -0
  140. cars/orchestrator/cluster/abstract_cluster.py +250 -0
  141. cars/orchestrator/cluster/abstract_dask_cluster.py +381 -0
  142. cars/orchestrator/cluster/dask_cluster_tools.py +103 -0
  143. cars/orchestrator/cluster/dask_config/README.md +94 -0
  144. cars/orchestrator/cluster/dask_config/dask.yaml +21 -0
  145. cars/orchestrator/cluster/dask_config/distributed.yaml +70 -0
  146. cars/orchestrator/cluster/dask_config/jobqueue.yaml +26 -0
  147. cars/orchestrator/cluster/dask_config/reference_confs/dask-schema.yaml +137 -0
  148. cars/orchestrator/cluster/dask_config/reference_confs/dask.yaml +26 -0
  149. cars/orchestrator/cluster/dask_config/reference_confs/distributed-schema.yaml +1009 -0
  150. cars/orchestrator/cluster/dask_config/reference_confs/distributed.yaml +273 -0
  151. cars/orchestrator/cluster/dask_config/reference_confs/jobqueue.yaml +212 -0
  152. cars/orchestrator/cluster/dask_jobqueue_utils.py +204 -0
  153. cars/orchestrator/cluster/local_dask_cluster.py +116 -0
  154. cars/orchestrator/cluster/log_wrapper.py +728 -0
  155. cars/orchestrator/cluster/mp_cluster/__init__.py +27 -0
  156. cars/orchestrator/cluster/mp_cluster/mp_factorizer.py +212 -0
  157. cars/orchestrator/cluster/mp_cluster/mp_objects.py +535 -0
  158. cars/orchestrator/cluster/mp_cluster/mp_tools.py +93 -0
  159. cars/orchestrator/cluster/mp_cluster/mp_wrapper.py +505 -0
  160. cars/orchestrator/cluster/mp_cluster/multiprocessing_cluster.py +986 -0
  161. cars/orchestrator/cluster/mp_cluster/multiprocessing_profiler.py +399 -0
  162. cars/orchestrator/cluster/pbs_dask_cluster.py +207 -0
  163. cars/orchestrator/cluster/sequential_cluster.py +139 -0
  164. cars/orchestrator/cluster/slurm_dask_cluster.py +234 -0
  165. cars/orchestrator/memory_tools.py +47 -0
  166. cars/orchestrator/orchestrator.py +755 -0
  167. cars/orchestrator/orchestrator_constants.py +29 -0
  168. cars/orchestrator/registry/__init__.py +23 -0
  169. cars/orchestrator/registry/abstract_registry.py +143 -0
  170. cars/orchestrator/registry/compute_registry.py +106 -0
  171. cars/orchestrator/registry/id_generator.py +116 -0
  172. cars/orchestrator/registry/replacer_registry.py +213 -0
  173. cars/orchestrator/registry/saver_registry.py +363 -0
  174. cars/orchestrator/registry/unseen_registry.py +118 -0
  175. cars/orchestrator/tiles_profiler.py +279 -0
  176. cars/pipelines/__init__.py +26 -0
  177. cars/pipelines/conf_resolution/conf_final_resolution.yaml +5 -0
  178. cars/pipelines/conf_resolution/conf_first_resolution.yaml +4 -0
  179. cars/pipelines/conf_resolution/conf_intermediate_resolution.yaml +2 -0
  180. cars/pipelines/default/__init__.py +26 -0
  181. cars/pipelines/default/default_pipeline.py +1095 -0
  182. cars/pipelines/filling/__init__.py +26 -0
  183. cars/pipelines/filling/filling.py +981 -0
  184. cars/pipelines/formatting/__init__.py +26 -0
  185. cars/pipelines/formatting/formatting.py +190 -0
  186. cars/pipelines/merging/__init__.py +26 -0
  187. cars/pipelines/merging/merging.py +439 -0
  188. cars/pipelines/parameters/__init__.py +0 -0
  189. cars/pipelines/parameters/advanced_parameters.py +256 -0
  190. cars/pipelines/parameters/advanced_parameters_constants.py +68 -0
  191. cars/pipelines/parameters/application_parameters.py +72 -0
  192. cars/pipelines/parameters/depth_map_inputs.py +0 -0
  193. cars/pipelines/parameters/dsm_inputs.py +349 -0
  194. cars/pipelines/parameters/dsm_inputs_constants.py +25 -0
  195. cars/pipelines/parameters/output_constants.py +52 -0
  196. cars/pipelines/parameters/output_parameters.py +435 -0
  197. cars/pipelines/parameters/sensor_inputs.py +859 -0
  198. cars/pipelines/parameters/sensor_inputs_constants.py +51 -0
  199. cars/pipelines/parameters/sensor_loaders/__init__.py +29 -0
  200. cars/pipelines/parameters/sensor_loaders/basic_classif_loader.py +86 -0
  201. cars/pipelines/parameters/sensor_loaders/basic_image_loader.py +98 -0
  202. cars/pipelines/parameters/sensor_loaders/pivot_classif_loader.py +90 -0
  203. cars/pipelines/parameters/sensor_loaders/pivot_image_loader.py +105 -0
  204. cars/pipelines/parameters/sensor_loaders/sensor_loader.py +93 -0
  205. cars/pipelines/parameters/sensor_loaders/sensor_loader_template.py +71 -0
  206. cars/pipelines/parameters/sensor_loaders/slurp_classif_loader.py +86 -0
  207. cars/pipelines/pipeline.py +119 -0
  208. cars/pipelines/pipeline_constants.py +38 -0
  209. cars/pipelines/pipeline_template.py +135 -0
  210. cars/pipelines/subsampling/__init__.py +26 -0
  211. cars/pipelines/subsampling/subsampling.py +358 -0
  212. cars/pipelines/surface_modeling/__init__.py +26 -0
  213. cars/pipelines/surface_modeling/surface_modeling.py +2098 -0
  214. cars/pipelines/tie_points/__init__.py +26 -0
  215. cars/pipelines/tie_points/tie_points.py +536 -0
  216. cars/starter.py +167 -0
  217. cars-1.0.0rc3.dist-info/METADATA +289 -0
  218. cars-1.0.0rc3.dist-info/RECORD +220 -0
  219. cars-1.0.0rc3.dist-info/WHEEL +6 -0
  220. cars-1.0.0rc3.dist-info/entry_points.txt +8 -0
@@ -0,0 +1,1095 @@
1
+ #!/usr/bin/env python
2
+ # coding: utf8
3
+ #
4
+ # Copyright (c) 2020 Centre National d'Etudes Spatiales (CNES).
5
+ #
6
+ # This file is part of CARS
7
+ # (see https://github.com/CNES/cars).
8
+ #
9
+ # Licensed under the Apache License, Version 2.0 (the "License");
10
+ # you may not use this file except in compliance with the License.
11
+ # You may obtain a copy of the License at
12
+ #
13
+ # http://www.apache.org/licenses/LICENSE-2.0
14
+ #
15
+ # Unless required by applicable law or agreed to in writing, software
16
+ # distributed under the License is distributed on an "AS IS" BASIS,
17
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18
+ # See the License for the specific language governing permissions and
19
+ # limitations under the License.
20
+ #
21
+ # pylint: disable=too-many-lines
22
+ # attribute-defined-outside-init is disabled so that we can create and use
23
+ # attributes however we need, to stick to the "everything is attribute" logic
24
+ # introduced in issue#895
25
+ # pylint: disable=attribute-defined-outside-init
26
+ # pylint: disable=too-many-nested-blocks
27
+ """
28
+ CARS default pipeline class file
29
+ """
30
+ # Standard imports
31
+ from __future__ import print_function
32
+
33
+ import copy
34
+ import json
35
+ import logging
36
+ import os
37
+ import shutil
38
+ from collections import OrderedDict
39
+ from datetime import datetime
40
+
41
+ import yaml
42
+
43
+ # CARS imports
44
+ from cars.core import cars_logging
45
+ from cars.core.utils import safe_makedirs
46
+ from cars.data_structures import cars_dataset
47
+ from cars.orchestrator.cluster import log_wrapper
48
+ from cars.orchestrator.cluster.log_wrapper import cars_profile
49
+ from cars.pipelines import pipeline_constants as pipeline_cst
50
+ from cars.pipelines.filling.filling import FillingPipeline
51
+ from cars.pipelines.formatting.formatting import FormattingPipeline
52
+ from cars.pipelines.merging.merging import MergingPipeline
53
+ from cars.pipelines.parameters import advanced_parameters_constants as adv_cst
54
+ from cars.pipelines.parameters import dsm_inputs
55
+ from cars.pipelines.parameters import dsm_inputs_constants as dsm_cst
56
+ from cars.pipelines.parameters import output_constants as out_cst
57
+ from cars.pipelines.parameters import output_parameters, sensor_inputs
58
+ from cars.pipelines.parameters import sensor_inputs_constants as sens_cst
59
+ from cars.pipelines.parameters.output_constants import AUXILIARY
60
+ from cars.pipelines.pipeline import Pipeline
61
+ from cars.pipelines.pipeline_constants import (
62
+ ADVANCED,
63
+ APPLICATIONS,
64
+ INPUT,
65
+ ORCHESTRATOR,
66
+ OUTPUT,
67
+ PIPELINE,
68
+ )
69
+ from cars.pipelines.pipeline_template import PipelineTemplate
70
+ from cars.pipelines.subsampling.subsampling import SubsamplingPipeline
71
+ from cars.pipelines.surface_modeling.surface_modeling import (
72
+ SurfaceModelingPipeline,
73
+ )
74
+
75
+ package_path = os.path.dirname(__file__)
76
+ FIRST_RES = "first_resolution"
77
+ INTERMEDIATE_RES = "intermediate_resolution"
78
+ FINAL_RES = "final_resolution"
79
+
80
+ PIPELINE_CONFS = {
81
+ FIRST_RES: os.path.join(
82
+ package_path,
83
+ "..",
84
+ "conf_resolution",
85
+ "conf_first_resolution.yaml",
86
+ ),
87
+ INTERMEDIATE_RES: os.path.join(
88
+ package_path,
89
+ "..",
90
+ "conf_resolution",
91
+ "conf_intermediate_resolution.yaml",
92
+ ),
93
+ FINAL_RES: os.path.join(
94
+ package_path,
95
+ "..",
96
+ "conf_resolution",
97
+ "conf_final_resolution.yaml",
98
+ ),
99
+ }
100
+
101
+
102
+ @Pipeline.register(
103
+ "default",
104
+ )
105
+ class DefaultPipeline(PipelineTemplate):
106
+ """
107
+ DefaultPipeline
108
+ """
109
+
110
+ # pylint: disable=too-many-instance-attributes
111
+
112
+ def __init__(self, conf, config_dir=None): # noqa: C901
113
+ """
114
+ Creates pipeline
115
+
116
+ :param pipeline_name: name of the pipeline.
117
+ :type pipeline_name: str
118
+ :param cfg: configuration {'matching_cost_method': value}
119
+ :type cfg: dictionary
120
+ :param config_dir: path to dir containing json or yaml file
121
+ :type config_dir: str
122
+ """
123
+
124
+ self.config_dir = config_dir
125
+ # Transform relative path to absolute path
126
+ if config_dir is not None:
127
+ config_dir = os.path.abspath(config_dir)
128
+
129
+ # Check global conf
130
+ self.check_global_schema(conf)
131
+
132
+ self.out_dir = conf[OUTPUT][out_cst.OUT_DIRECTORY]
133
+
134
+ conf[PIPELINE] = self.check_pipeline(conf)
135
+
136
+ self.pipeline_to_use = conf[PIPELINE]
137
+
138
+ # Check input
139
+ conf[INPUT] = self.check_inputs(conf, config_json_dir=config_dir)
140
+
141
+ # check output
142
+ conf[OUTPUT] = self.check_output(conf)
143
+
144
+ self.intermediate_data_dir = os.path.join(
145
+ self.out_dir, "intermediate_data"
146
+ )
147
+
148
+ conf[ORCHESTRATOR] = self.check_orchestrator(
149
+ conf.get(ORCHESTRATOR, None)
150
+ )
151
+
152
+ if self.pipeline_to_use[pipeline_cst.SUBSAMPLING]:
153
+ self.subsampling_conf = self.construct_subsampling_conf(conf)
154
+ conf[pipeline_cst.SUBSAMPLING] = self.check_subsampling(
155
+ self.subsampling_conf,
156
+ )
157
+
158
+ # Get epipolar resolutions to use
159
+ self.resolutions = conf[pipeline_cst.SUBSAMPLING][ADVANCED][
160
+ adv_cst.RESOLUTIONS
161
+ ]
162
+ else:
163
+ self.resolutions = [1]
164
+
165
+ for pipeline, activated in self.pipeline_to_use.items():
166
+ if pipeline in conf and not activated:
167
+ logging.warning(
168
+ f"You tried to override the {pipeline} pipeline but "
169
+ f"didn't specify it in the pipeline section. "
170
+ "Therefore, this pipeline will not be used"
171
+ )
172
+ if pipeline in conf and activated:
173
+ # Check pipeline conf format
174
+ self.check_pipeline_section(pipeline, conf[pipeline])
175
+
176
+ if pipeline_cst.SURFACE_MODELING not in conf:
177
+ conf[pipeline_cst.SURFACE_MODELING] = {}
178
+ if pipeline_cst.TIE_POINTS not in conf:
179
+ conf[pipeline_cst.TIE_POINTS] = {}
180
+
181
+ used_configurations = {}
182
+ self.positions = {}
183
+ self.used_conf = {}
184
+
185
+ self.keep_low_res_dir = True
186
+
187
+ if dsm_cst.DSMS in conf[INPUT] and len(self.resolutions) != 1:
188
+ logging.info(
189
+ "For the use of those pipelines, "
190
+ "you have to give only one resolution"
191
+ )
192
+ # overide epipolar resolutions
193
+ # TODO: delete with external dsm pipeline (refactoring)
194
+ self.resolutions = [1]
195
+ elif (
196
+ not self.pipeline_to_use[pipeline_cst.SUBSAMPLING]
197
+ and len(self.resolutions) != 1
198
+ ):
199
+ logging.warning(
200
+ "As you're not using the subsampling pipeline, "
201
+ "the working resolution will be 1"
202
+ )
203
+
204
+ self.resolutions = [1]
205
+
206
+ if self.pipeline_to_use[pipeline_cst.FILLING]:
207
+ self.filling_conf = self.construct_filling_conf(conf)
208
+ conf[pipeline_cst.FILLING] = self.check_filling(self.filling_conf)
209
+ consistent_filling = False
210
+ for app in conf[pipeline_cst.FILLING][APPLICATIONS]:
211
+ if "dsm_filling" in app:
212
+ consistent_filling = True
213
+ for filling_method in conf[INPUT][sens_cst.FILLING]:
214
+ if conf[INPUT][sens_cst.FILLING][filling_method]:
215
+ consistent_filling = True
216
+ if not consistent_filling:
217
+ self.pipeline_to_use[pipeline_cst.FILLING] = False
218
+
219
+ subsampling_used_conf = conf.get(pipeline_cst.SUBSAMPLING, {})
220
+ filling_used_conf = conf.get(pipeline_cst.FILLING, {})
221
+
222
+ if self.pipeline_to_use[pipeline_cst.SURFACE_MODELING]:
223
+ for epipolar_resolution_index, epipolar_res in enumerate(
224
+ self.resolutions
225
+ ):
226
+ first_res = epipolar_resolution_index == 0
227
+ last_res = (
228
+ epipolar_resolution_index == len(self.resolutions) - 1
229
+ )
230
+ intermediate_res = not first_res and not last_res
231
+
232
+ # set computed bool
233
+ self.positions[epipolar_resolution_index] = {
234
+ "first_res": first_res,
235
+ "intermediate_res": intermediate_res,
236
+ "last_res": last_res,
237
+ }
238
+
239
+ current_conf = copy.deepcopy(conf)
240
+ current_conf = extract_conf_with_resolution(
241
+ current_conf,
242
+ epipolar_res,
243
+ first_res,
244
+ intermediate_res,
245
+ last_res,
246
+ self.intermediate_data_dir,
247
+ )
248
+
249
+ if not isinstance(epipolar_res, int) or epipolar_res < 0:
250
+ raise RuntimeError("The resolution has to be an int > 0")
251
+
252
+ self.used_conf[epipolar_resolution_index] = current_conf
253
+
254
+ # Initialize unit pipeline in order to retrieve the
255
+ # used configuration
256
+ # This pipeline will not be run
257
+ _ = current_conf.pop(pipeline_cst.SUBSAMPLING, None)
258
+ _ = current_conf.pop(pipeline_cst.FILLING, None)
259
+
260
+ current_unit_pipeline = SurfaceModelingPipeline(
261
+ current_conf,
262
+ config_dir=self.config_dir,
263
+ )
264
+ if last_res and self.pipeline_to_use[pipeline_cst.FILLING]:
265
+ # Force classification saving for filling
266
+ if not current_unit_pipeline.used_conf[OUTPUT][AUXILIARY][
267
+ sens_cst.INPUT_CLASSIFICATION
268
+ ]:
269
+ current_unit_pipeline.used_conf[OUTPUT][AUXILIARY][
270
+ sens_cst.INPUT_CLASSIFICATION
271
+ ] = True
272
+ if not self.filling_conf[OUTPUT][AUXILIARY][
273
+ sens_cst.INPUT_CLASSIFICATION
274
+ ]:
275
+ self.filling_conf[OUTPUT][AUXILIARY][
276
+ sens_cst.INPUT_CLASSIFICATION
277
+ ] = True
278
+ # Get used_conf
279
+ used_configurations[epipolar_res] = (
280
+ current_unit_pipeline.used_conf
281
+ )
282
+
283
+ # Generate full used_conf
284
+ full_used_conf = merge_used_conf(
285
+ used_configurations,
286
+ self.resolutions,
287
+ os.path.abspath(self.out_dir),
288
+ )
289
+ else:
290
+ self.used_conf = copy.deepcopy(conf)
291
+ full_used_conf = self.used_conf
292
+
293
+ full_used_conf[pipeline_cst.SUBSAMPLING] = subsampling_used_conf
294
+ full_used_conf[pipeline_cst.PIPELINE] = conf[PIPELINE]
295
+ full_used_conf[pipeline_cst.FILLING] = filling_used_conf
296
+
297
+ # Save used_conf
298
+ cars_dataset.save_dict(
299
+ full_used_conf,
300
+ os.path.join(self.out_dir, "global_used_conf.yaml"),
301
+ )
302
+
303
+ def check_inputs(self, conf, config_json_dir=None):
304
+ """
305
+ Check the inputs given
306
+
307
+ :param conf: configuration
308
+ :type conf: dict
309
+ :param config_dir: directory of used json, if
310
+ user filled paths with relative paths
311
+ :type config_dir: str
312
+
313
+ :return: overloader inputs
314
+ :rtype: dict
315
+ """
316
+ output_config = {}
317
+ if sens_cst.SENSORS in conf[INPUT] and dsm_cst.DSMS not in conf[INPUT]:
318
+ output_config = sensor_inputs.sensors_check_inputs(
319
+ conf[INPUT], config_dir=config_json_dir
320
+ )
321
+ elif dsm_cst.DSMS in conf[INPUT]:
322
+ output_config = {
323
+ **output_config,
324
+ **dsm_inputs.check_dsm_inputs(
325
+ conf[INPUT], config_dir=config_json_dir
326
+ ),
327
+ }
328
+ else:
329
+ raise RuntimeError("No sensors or dsms in inputs")
330
+
331
+ return output_config
332
+
333
+ def check_output(self, conf):
334
+ """
335
+ Check the output given
336
+
337
+ :param conf: configuration of output
338
+ :type conf: dict
339
+
340
+ :return overloader output
341
+ :rtype : dict
342
+ """
343
+ conf_output, _ = output_parameters.check_output_parameters(
344
+ conf[INPUT], conf[OUTPUT]
345
+ )
346
+ return conf_output
347
+
348
+ def check_pipeline(self, conf): # noqa: C901
349
+ """
350
+ Check the pipeline section
351
+ """
352
+ possible_pipeline = [
353
+ pipeline_cst.SUBSAMPLING,
354
+ pipeline_cst.SURFACE_MODELING,
355
+ pipeline_cst.FILLING,
356
+ pipeline_cst.MERGING,
357
+ pipeline_cst.FORMATTING,
358
+ ]
359
+ dict_pipeline = {}
360
+
361
+ if PIPELINE not in conf:
362
+ if dsm_cst.DSMS in conf[INPUT]:
363
+ conf[PIPELINE] = [pipeline_cst.MERGING, pipeline_cst.FORMATTING]
364
+ elif sens_cst.SENSORS in conf[INPUT]:
365
+ conf[PIPELINE] = [
366
+ pipeline_cst.SUBSAMPLING,
367
+ pipeline_cst.SURFACE_MODELING,
368
+ pipeline_cst.FORMATTING,
369
+ ]
370
+
371
+ if isinstance(conf[PIPELINE], str):
372
+ if conf[PIPELINE] not in possible_pipeline:
373
+ raise RuntimeError("This pipeline does not exist")
374
+ dict_pipeline = {conf[PIPELINE]: True}
375
+ elif isinstance(conf[PIPELINE], list):
376
+ for elem in conf[PIPELINE]:
377
+ if elem not in possible_pipeline:
378
+ raise RuntimeError(f"The pipeline {elem} does not exist")
379
+ dict_pipeline.update({elem: True})
380
+ elif isinstance(conf[PIPELINE], dict):
381
+ for key, _ in conf[PIPELINE].items():
382
+ if key not in possible_pipeline:
383
+ raise RuntimeError(f"The pipeline {key} does not exist")
384
+ dict_pipeline = copy.deepcopy(conf[PIPELINE])
385
+
386
+ for key in possible_pipeline:
387
+ if key not in dict_pipeline:
388
+ dict_pipeline.update({key: False})
389
+
390
+ if (
391
+ dsm_cst.DSMS in conf[INPUT]
392
+ and not dict_pipeline[pipeline_cst.MERGING]
393
+ ):
394
+ dict_pipeline[pipeline_cst.MERGING] = True
395
+ elif (
396
+ dsm_cst.DSMS in conf[INPUT]
397
+ and dict_pipeline[pipeline_cst.SURFACE_MODELING]
398
+ ):
399
+ raise RuntimeError(
400
+ "You can not use the surface modeling pipeline with dsm inputs"
401
+ )
402
+ elif (
403
+ sens_cst.SENSORS in conf[INPUT]
404
+ and dict_pipeline[pipeline_cst.MERGING]
405
+ and dsm_cst.DSMS not in conf[INPUT]
406
+ ):
407
+ raise RuntimeError(
408
+ "You can not use the merging pipeline with sensors inputs only"
409
+ )
410
+
411
+ activate_filling = False
412
+ if pipeline_cst.FILLING in conf[INPUT]:
413
+ for filling_method in conf[INPUT][pipeline_cst.FILLING]:
414
+ if conf[INPUT][pipeline_cst.FILLING][filling_method]:
415
+ activate_filling = True
416
+ if pipeline_cst.FILLING in conf and conf[pipeline_cst.FILLING]:
417
+ activate_filling = True
418
+
419
+ if activate_filling:
420
+ dict_pipeline[pipeline_cst.FILLING] = True
421
+
422
+ if (
423
+ pipeline_cst.SURFACE_MODELING in conf[INPUT]
424
+ and not dict_pipeline[pipeline_cst.SURFACE_MODELING]
425
+ ):
426
+ dict_pipeline[pipeline_cst.SURFACE_MODELING] = True
427
+
428
+ if (
429
+ pipeline_cst.MERGING in conf[INPUT]
430
+ and not dict_pipeline[pipeline_cst.MERGING]
431
+ ):
432
+ dict_pipeline[pipeline_cst.MERGING] = True
433
+
434
+ if (
435
+ pipeline_cst.SUBSAMPLING in conf[INPUT]
436
+ and not dict_pipeline[pipeline_cst.SUBSAMPLING]
437
+ ):
438
+ dict_pipeline[pipeline_cst.SUBSAMPLING] = True
439
+
440
+ return dict_pipeline
441
+
442
+ def check_subsampling(self, conf):
443
+ """
444
+ Check the subsampling section
445
+
446
+ :param conf: configuration of subsampling
447
+ :type conf: dict
448
+ """
449
+ pipeline = SubsamplingPipeline(conf)
450
+ advanced = pipeline.check_advanced(
451
+ conf[pipeline_cst.SUBSAMPLING].get(ADVANCED, {}),
452
+ conf[INPUT],
453
+ )
454
+ applications = pipeline.check_applications(
455
+ conf[pipeline_cst.SUBSAMPLING].get(APPLICATIONS, {})
456
+ )
457
+
458
+ return {ADVANCED: advanced, APPLICATIONS: applications}
459
+
460
+ def check_filling(self, conf):
461
+ """
462
+ Check the filling section
463
+
464
+ :param conf: configuration of subsampling
465
+ :type conf: dict
466
+ """
467
+
468
+ pipeline = FillingPipeline(conf, pre_check=True)
469
+ advanced = pipeline.check_advanced(
470
+ conf[pipeline_cst.FILLING],
471
+ conf[INPUT],
472
+ )
473
+ applications = pipeline.check_applications(
474
+ conf[pipeline_cst.FILLING].get(APPLICATIONS, {})
475
+ )
476
+
477
+ return {ADVANCED: advanced, APPLICATIONS: applications}
478
+
479
+ def check_pipeline_section(self, pipeline_name, pipeline_conf):
480
+ """
481
+ Check any pipeline section
482
+
483
+ :param pipeline_name: key name in conf
484
+ :type pipeline_name: str
485
+ :param pipeline_conf: pipeline configuration
486
+ :type pipeline_conf: dict
487
+ """
488
+ for key in pipeline_conf:
489
+ if key not in [APPLICATIONS, ADVANCED]:
490
+ raise KeyError(
491
+ "Keys of a pipeline must be 'applications' or 'advanced'"
492
+ )
493
+ if pipeline_name in (
494
+ pipeline_cst.SURFACE_MODELING,
495
+ pipeline_cst.TIE_POINTS,
496
+ ):
497
+ int_keys = [int(epi_res) for epi_res in self.resolutions]
498
+ string_keys = [str(key) for key in int_keys]
499
+ possible_keys = ["all"] + int_keys + string_keys
500
+
501
+ for section in pipeline_conf:
502
+ for key in pipeline_conf[section]:
503
+ if key not in possible_keys:
504
+ raise KeyError(
505
+ "When meta pipeline is used, keys of {} pipeline"
506
+ "must be in {}".format(
507
+ pipeline_name,
508
+ string_keys + ["all"],
509
+ )
510
+ )
511
+
512
+ def cleanup_low_res_dir(self):
513
+ """
514
+ Clean low res dir
515
+ """
516
+
517
+ if os.path.exists(self.intermediate_data_dir) and os.path.isdir(
518
+ self.intermediate_data_dir
519
+ ):
520
+ try:
521
+ shutil.rmtree(self.intermediate_data_dir)
522
+ logging.info(
523
+ f"th directory {self.intermediate_data_dir} "
524
+ f" has been cleaned."
525
+ )
526
+ except Exception as exception:
527
+ logging.error(
528
+ f"Error while deleting {self.intermediate_data_dir}: "
529
+ f"{exception}"
530
+ )
531
+ else:
532
+ logging.info(
533
+ f"The directory {self.intermediate_data_dir} has not "
534
+ f"been deleted"
535
+ )
536
+
537
+ def construct_merging_conf(self, conf):
538
+ """
539
+ Construct the right conf for merging
540
+ """
541
+ merging_conf = {}
542
+ merging_conf[INPUT] = copy.deepcopy(conf[INPUT])
543
+ merging_conf[ORCHESTRATOR] = copy.deepcopy(conf[ORCHESTRATOR])
544
+ merging_conf[OUTPUT] = {}
545
+ merging_conf[OUTPUT]["directory"] = os.path.join(
546
+ self.intermediate_data_dir, pipeline_cst.MERGING
547
+ )
548
+ merging_conf[OUTPUT][AUXILIARY] = conf[OUTPUT].get(AUXILIARY, {})
549
+
550
+ merging_conf[pipeline_cst.MERGING] = conf.get(pipeline_cst.MERGING, {})
551
+
552
+ return merging_conf
553
+
554
+ def construct_subsampling_conf(self, conf):
555
+ """
556
+ Construct the right conf for subsampling
557
+ """
558
+ subsampling_conf = {}
559
+ subsampling_conf[INPUT] = copy.deepcopy(conf[INPUT])
560
+ subsampling_conf[ORCHESTRATOR] = copy.deepcopy(conf[ORCHESTRATOR])
561
+ subsampling_conf[OUTPUT] = {}
562
+ subsampling_conf[OUTPUT]["directory"] = self.intermediate_data_dir
563
+
564
+ subsampling_conf[pipeline_cst.SUBSAMPLING] = conf.get(
565
+ pipeline_cst.SUBSAMPLING, {}
566
+ )
567
+
568
+ return subsampling_conf
569
+
570
+ def construct_formatting_conf(self, input_dir):
571
+ """
572
+ Construct the right conf for formatting
573
+ """
574
+
575
+ formatting_conf = {}
576
+ formatting_conf[INPUT] = {}
577
+ formatting_conf[INPUT]["input_path"] = input_dir
578
+ formatting_conf[OUTPUT] = {}
579
+ formatting_conf[OUTPUT]["directory"] = self.out_dir
580
+
581
+ return formatting_conf
582
+
583
+ def construct_filling_conf(self, conf):
584
+ """
585
+ Construct the right conf for filling
586
+ """
587
+ filling_conf = {}
588
+ filling_conf[INPUT] = copy.deepcopy(conf[INPUT])
589
+ _ = filling_conf[INPUT].pop(dsm_cst.DSMS, None)
590
+ filling_conf[OUTPUT] = copy.deepcopy(conf[OUTPUT])
591
+ filling_conf[OUTPUT]["directory"] = self.intermediate_data_dir
592
+ filling_conf[pipeline_cst.FILLING] = conf.get(pipeline_cst.FILLING, {})
593
+ return filling_conf
594
+
595
+ @cars_profile(name="Run_default_pipeline", interval=0.5)
596
+ def run(self, args=None): # noqa C901
597
+ """
598
+ Run pipeline
599
+
600
+ """
601
+
602
+ global_log_file = os.path.join(
603
+ self.out_dir,
604
+ "logs",
605
+ "{}_{}.log".format(
606
+ datetime.now().strftime("%y-%m-%d_%Hh%Mm"), "default_pipeline"
607
+ ),
608
+ )
609
+
610
+ previous_out_dir = None
611
+ current_surface_modeling_out_dir = None
612
+ updated_conf = {}
613
+
614
+ if self.pipeline_to_use[pipeline_cst.SUBSAMPLING]:
615
+ subsampling_pipeline = SubsamplingPipeline(
616
+ self.subsampling_conf, self.config_dir
617
+ )
618
+ subsampling_pipeline.run()
619
+
620
+ if self.pipeline_to_use[pipeline_cst.SURFACE_MODELING]:
621
+ for resolution_index, epipolar_res in enumerate(self.resolutions):
622
+
623
+ # Get tested unit pipeline
624
+ current_conf = self.used_conf[resolution_index]
625
+ current_surface_modeling_out_dir = current_conf[OUTPUT][
626
+ "directory"
627
+ ]
628
+
629
+ # Put right directory for subsampling
630
+ if self.pipeline_to_use[pipeline_cst.SUBSAMPLING]:
631
+ if epipolar_res != 1:
632
+ yaml_file = os.path.join(
633
+ self.intermediate_data_dir,
634
+ "subsampling/res_"
635
+ + str(epipolar_res)
636
+ + "/input.yaml",
637
+ )
638
+ with open(yaml_file, encoding="utf-8") as f:
639
+ data = yaml.safe_load(f)
640
+
641
+ json_str = json.dumps(data, indent=4)
642
+ data = json.loads(json_str)
643
+
644
+ current_conf[INPUT] = data
645
+
646
+ # update directory for unit pipeline
647
+ current_conf[OUTPUT][
648
+ "directory"
649
+ ] = current_surface_modeling_out_dir
650
+
651
+ # get position
652
+ first_res, _, last_res = (
653
+ self.positions[resolution_index]["first_res"],
654
+ self.positions[resolution_index]["intermediate_res"],
655
+ self.positions[resolution_index]["last_res"],
656
+ )
657
+
658
+ # setup logging
659
+ loglevel = getattr(args, "loglevel", "PROGRESS").upper()
660
+
661
+ current_log_dir = os.path.join(
662
+ self.out_dir, "logs", "res_" + str(epipolar_res)
663
+ )
664
+
665
+ cars_logging.setup_logging(
666
+ loglevel,
667
+ out_dir=current_log_dir,
668
+ pipeline="surface_modeling",
669
+ global_log_file=global_log_file,
670
+ )
671
+
672
+ cars_logging.add_progress_message(
673
+ "Starting surface modeling pipeline for resolution 1/"
674
+ + str(epipolar_res)
675
+ )
676
+
677
+ # define wich resolution
678
+ if first_res and last_res:
679
+ which_resolution = "single"
680
+ elif first_res:
681
+ which_resolution = "first"
682
+ elif last_res:
683
+ which_resolution = "final"
684
+ else:
685
+ which_resolution = "intermediate"
686
+
687
+ # Overide with a priori
688
+ if not first_res:
689
+ dsm = os.path.join(previous_out_dir, "dsm/dsm.tif")
690
+ current_conf[INPUT][sens_cst.LOW_RES_DSM] = dsm
691
+
692
+ updated_pipeline = SurfaceModelingPipeline(
693
+ current_conf,
694
+ config_dir=self.config_dir,
695
+ )
696
+ updated_pipeline.run(
697
+ which_resolution=which_resolution,
698
+ log_dir=current_log_dir,
699
+ )
700
+
701
+ # update previous out dir
702
+ previous_out_dir = current_surface_modeling_out_dir
703
+
704
+ # generate summary
705
+ log_wrapper.generate_summary(
706
+ current_log_dir,
707
+ updated_pipeline.used_conf,
708
+ pipeline_cst.SURFACE_MODELING,
709
+ )
710
+
711
+ updated_conf[epipolar_res] = updated_pipeline.used_conf
712
+
713
+ # Generate full used_conf
714
+ full_used_conf = merge_used_conf(
715
+ updated_conf,
716
+ self.resolutions,
717
+ os.path.abspath(self.out_dir),
718
+ )
719
+ else:
720
+ full_used_conf = self.used_conf
721
+
722
+ final_conf = None
723
+ if self.pipeline_to_use[pipeline_cst.MERGING]:
724
+ merging_conf = self.construct_merging_conf(self.used_conf)
725
+ merging_pipeline = MergingPipeline(merging_conf, self.config_dir)
726
+ merging_pipeline.run()
727
+
728
+ final_conf = merging_pipeline.used_conf
729
+
730
+ if updated_conf and final_conf is None:
731
+ last_key = list(updated_conf.keys())[-1]
732
+ final_conf = updated_conf[last_key]
733
+ elif not updated_conf and final_conf is None:
734
+ final_conf = self.used_conf
735
+
736
+ formatting_input_dir = final_conf[OUTPUT][out_cst.OUT_DIRECTORY]
737
+
738
+ if self.pipeline_to_use[pipeline_cst.FILLING]:
739
+ if self.filling_conf[INPUT]["dsm_to_fill"] is None:
740
+ if (
741
+ not self.pipeline_to_use[pipeline_cst.SURFACE_MODELING]
742
+ and not self.pipeline_to_use[pipeline_cst.MERGING]
743
+ ):
744
+ raise RuntimeError(
745
+ "You have to fill the dsm_to_fill part of the input if "
746
+ "you want to use the filling pipeline separately"
747
+ )
748
+
749
+ self.filling_conf[INPUT]["dsm_to_fill"] = {}
750
+ aux_path = os.path.join(
751
+ final_conf[OUTPUT][out_cst.OUT_DIRECTORY], "dsm/"
752
+ )
753
+ self.filling_conf[INPUT]["dsm_to_fill"]["dsm"] = os.path.join(
754
+ aux_path, "dsm.tif"
755
+ )
756
+
757
+ for aux_output, val in final_conf[OUTPUT][
758
+ out_cst.AUXILIARY
759
+ ].items():
760
+ if val:
761
+ self.filling_conf[INPUT]["dsm_to_fill"][aux_output] = (
762
+ os.path.join(aux_path, aux_output + ".tif")
763
+ )
764
+ initial_elevation = final_conf[INPUT][
765
+ sens_cst.INITIAL_ELEVATION
766
+ ].get("dem", None)
767
+
768
+ if (
769
+ initial_elevation is not None
770
+ and "dem_median" in initial_elevation
771
+ ):
772
+ self.filling_conf[INPUT][sens_cst.INITIAL_ELEVATION] = None
773
+
774
+ filling_pipeline = FillingPipeline(
775
+ self.filling_conf, self.config_dir
776
+ )
777
+ filling_pipeline.run()
778
+
779
+ formatting_input_dir = os.path.join(
780
+ filling_pipeline.used_conf[OUTPUT][out_cst.OUT_DIRECTORY],
781
+ pipeline_cst.FILLING,
782
+ )
783
+
784
+ if self.pipeline_to_use[pipeline_cst.FORMATTING]:
785
+ formatting_conf = self.construct_formatting_conf(
786
+ formatting_input_dir
787
+ )
788
+ formatting_pipeline = FormattingPipeline(
789
+ formatting_conf, self.config_dir
790
+ )
791
+ formatting_pipeline.run(current_surface_modeling_out_dir)
792
+
793
+ if self.pipeline_to_use[pipeline_cst.FILLING]:
794
+ full_used_conf[pipeline_cst.FILLING] = {
795
+ ADVANCED: filling_pipeline.used_conf[ADVANCED],
796
+ APPLICATIONS: filling_pipeline.used_conf[APPLICATIONS],
797
+ }
798
+
799
+ # Save used_conf
800
+ cars_dataset.save_dict(
801
+ full_used_conf,
802
+ os.path.join(self.out_dir, "global_used_conf.yaml"),
803
+ )
804
+
805
+ # Merge profiling in pdf
806
+ log_wrapper.generate_pdf_profiling(os.path.join(self.out_dir, "logs"))
807
+
808
+ # clean outdir
809
+ if not self.keep_low_res_dir:
810
+ self.cleanup_low_res_dir()
811
+
812
+
813
+ def extract_conf_section(
814
+ current_conf_section,
815
+ res,
816
+ default_conf_for_res=None,
817
+ filling_applications=None,
818
+ ):
819
+ """
820
+ Extract applications for current resolution
821
+
822
+ :param current_applications_conf: current applications configuration
823
+ :type current_applications_conf: dict
824
+ :param res: resolution to extract
825
+ :type res: int
826
+ :param default_conf_for_res: default configuration for resolution
827
+ :type default_conf_for_res: dict
828
+ :param filling_applications: filling applications configuration
829
+ :type filling_applications: dict
830
+
831
+ :return: configuration for the given resolution
832
+ :rtype: dict
833
+ """
834
+
835
+ # "all" : applied to all conf
836
+ # int (1, 2, 4, 8, 16, ...) applied for specified resolution
837
+
838
+ all_conf = current_conf_section.get("all", {})
839
+ # Overide with default_conf_for_res
840
+ if default_conf_for_res is not None:
841
+ all_conf = overide_pipeline_conf(all_conf, default_conf_for_res)
842
+ # Get configuration for current res
843
+ if res in current_conf_section:
844
+ # key is int
845
+ key = res
846
+ else:
847
+ key = str(res)
848
+
849
+ res_conf = current_conf_section.get(key, {})
850
+
851
+ # Overide all conf with current res conf
852
+ new_application_conf = overide_pipeline_conf(all_conf, res_conf)
853
+
854
+ # Overide with filling applications
855
+ if filling_applications is not None:
856
+ new_application_conf = overide_pipeline_conf(
857
+ new_application_conf,
858
+ filling_applications,
859
+ append_classification=True,
860
+ )
861
+ return new_application_conf
862
+
863
+
864
+ # pylint: disable=too-many-positional-arguments
865
+ def extract_conf_with_resolution(
866
+ current_conf,
867
+ res,
868
+ first_res,
869
+ intermediate_res,
870
+ last_res,
871
+ intermediate_data_dir,
872
+ ):
873
+ """
874
+ Extract the configuration for the given resolution
875
+
876
+ :param current_conf: current configuration
877
+ :type current_conf: dict
878
+ :param res: resolution to extract
879
+ :type res: int
880
+ :return: configuration for the given resolution
881
+ :rtype: dict
882
+ :param first_res: is first resolution
883
+ :type first_res: bool
884
+ :param intermediate_res: is intermediate resolution
885
+ :type intermediate_res: bool
886
+ :param last_res: is last resolution
887
+ :type last_res: bool
888
+ :param previous_out_dir: path to previous outdir
889
+ :type: previous_out_dir: str
890
+ """
891
+
892
+ surface_modeling_out_dir = os.path.join(
893
+ intermediate_data_dir, "surface_modeling", "res" + str(res)
894
+ )
895
+ safe_makedirs(surface_modeling_out_dir)
896
+
897
+ new_conf = copy.deepcopy(current_conf)
898
+
899
+ # Overide configuration with pipeline conf
900
+ if first_res:
901
+ # read the first resolution conf with json package
902
+ with open(PIPELINE_CONFS[FIRST_RES], "r", encoding="utf-8") as file:
903
+ overiding_conf = yaml.safe_load(file)
904
+ elif intermediate_res:
905
+ with open(
906
+ PIPELINE_CONFS[INTERMEDIATE_RES], "r", encoding="utf-8"
907
+ ) as file:
908
+ overiding_conf = yaml.safe_load(file)
909
+ else:
910
+ with open(PIPELINE_CONFS[FINAL_RES], "r", encoding="utf-8") as file:
911
+ overiding_conf = yaml.safe_load(file)
912
+
913
+ if last_res and dsm_cst.DSMS not in current_conf[INPUT]:
914
+ # Use filling applications only for last resolution
915
+ filling_applications_for_surface_modeling = (
916
+ generate_filling_applications_for_surface_modeling(
917
+ current_conf[INPUT]
918
+ )
919
+ )
920
+ else:
921
+ filling_applications_for_surface_modeling = {}
922
+
923
+ # Extract surface modeling conf
924
+ new_conf[pipeline_cst.SURFACE_MODELING] = {}
925
+ new_conf[pipeline_cst.SURFACE_MODELING][APPLICATIONS] = (
926
+ extract_conf_section(
927
+ current_conf[pipeline_cst.SURFACE_MODELING].get(APPLICATIONS, {}),
928
+ res,
929
+ overiding_conf.get(APPLICATIONS, {}),
930
+ filling_applications_for_surface_modeling,
931
+ )
932
+ )
933
+ new_conf[pipeline_cst.SURFACE_MODELING][ADVANCED] = extract_conf_section(
934
+ current_conf[pipeline_cst.SURFACE_MODELING].get(ADVANCED, {}),
935
+ res,
936
+ )
937
+
938
+ # Extract tie points conf
939
+ if current_conf[pipeline_cst.TIE_POINTS] is not None:
940
+ new_conf[pipeline_cst.TIE_POINTS] = {}
941
+ new_conf[pipeline_cst.TIE_POINTS][APPLICATIONS] = extract_conf_section(
942
+ current_conf[pipeline_cst.TIE_POINTS].get(APPLICATIONS, {}),
943
+ res,
944
+ overiding_conf.get(APPLICATIONS, {}),
945
+ )
946
+ new_conf[pipeline_cst.TIE_POINTS][ADVANCED] = extract_conf_section(
947
+ current_conf[pipeline_cst.TIE_POINTS].get(ADVANCED, {}),
948
+ res,
949
+ )
950
+
951
+ overiding_conf = {
952
+ OUTPUT: {out_cst.OUT_DIRECTORY: surface_modeling_out_dir},
953
+ }
954
+ new_conf = overide_pipeline_conf(new_conf, overiding_conf)
955
+
956
+ # Overide output to not compute data
957
+ if not last_res:
958
+ overiding_conf = {
959
+ pipeline_cst.SURFACE_MODELING: {
960
+ APPLICATIONS: {
961
+ "dense_matching": {
962
+ "performance_map_method": ["risk", "intervals"]
963
+ }
964
+ }
965
+ },
966
+ }
967
+ new_conf = overide_pipeline_conf(new_conf, overiding_conf)
968
+
969
+ # set product level to dsm
970
+ new_conf[OUTPUT][out_cst.PRODUCT_LEVEL] = ["dsm"]
971
+ # remove resolution to let CARS compute it for current
972
+ # epipolar resolution
973
+ new_conf[OUTPUT]["resolution"] = None
974
+
975
+ # Save the less possible things
976
+ for aux_key in new_conf[OUTPUT][out_cst.AUXILIARY]:
977
+ if aux_key != "image":
978
+ new_conf[OUTPUT][out_cst.AUXILIARY][aux_key] = False
979
+
980
+ return new_conf
981
+
982
+
983
+ def generate_filling_applications_for_surface_modeling(inputs_conf):
984
+ """
985
+ Generate filling applications configuration according to inputs
986
+
987
+ :param inputs_conf: inputs configuration
988
+ :type inputs_conf: dict
989
+ """
990
+
991
+ filling_applications = {}
992
+
993
+ # Generate applications configuration
994
+ for _, classif_values in inputs_conf[sens_cst.FILLING].items():
995
+ # No filling
996
+ if classif_values is None:
997
+ continue
998
+
999
+ classif_values = list(map(str, classif_values))
1000
+
1001
+ # Update application configuration
1002
+ new_filling_conf = {
1003
+ "dense_match_filling": {
1004
+ "method": "zero_padding",
1005
+ "classification": classif_values,
1006
+ }
1007
+ }
1008
+
1009
+ # Update application configuration
1010
+ filling_applications = overide_pipeline_conf(
1011
+ filling_applications, new_filling_conf, append_classification=True
1012
+ )
1013
+
1014
+ return filling_applications
1015
+
1016
+
1017
+ def overide_pipeline_conf(conf, overiding_conf, append_classification=False):
1018
+ """
1019
+ Merge two dictionaries recursively without removing keys from the base conf.
1020
+
1021
+ :param conf: base configuration dictionary
1022
+ :type conf: dict
1023
+ :param overiding_conf: overriding configuration dictionary
1024
+ :type overiding_conf: dict
1025
+ :return: merged configuration
1026
+ :rtype: dict
1027
+ """
1028
+ result = copy.deepcopy(conf)
1029
+
1030
+ def merge_recursive(base_dict, override_dict):
1031
+ """
1032
+ Main recursive function
1033
+ """
1034
+ for key, value in override_dict.items():
1035
+ if (
1036
+ key in base_dict
1037
+ and isinstance(base_dict[key], dict)
1038
+ and isinstance(value, dict)
1039
+ ):
1040
+ merge_recursive(base_dict[key], value)
1041
+ elif (
1042
+ append_classification
1043
+ and key in base_dict
1044
+ and isinstance(base_dict[key], list)
1045
+ and isinstance(value, list)
1046
+ and key == "classification"
1047
+ ):
1048
+ # extend list, avoiding duplicates
1049
+ base_dict[key] = list(
1050
+ OrderedDict.fromkeys(base_dict[key] + value)
1051
+ )
1052
+ else:
1053
+ base_dict[key] = value
1054
+
1055
+ merge_recursive(result, overiding_conf)
1056
+ return result
1057
+
1058
+
1059
+ def merge_used_conf(used_configurations, resolutions, out_dir):
1060
+ """
1061
+ Merge all used configuration
1062
+ """
1063
+ used_configurations = copy.deepcopy(used_configurations)
1064
+
1065
+ merged_conf = {
1066
+ INPUT: used_configurations[resolutions[-1]][INPUT],
1067
+ OUTPUT: used_configurations[resolutions[0]][OUTPUT],
1068
+ ORCHESTRATOR: used_configurations[resolutions[0]][ORCHESTRATOR],
1069
+ }
1070
+
1071
+ merged_conf[OUTPUT]["directory"] = out_dir
1072
+
1073
+ merged_conf[pipeline_cst.TIE_POINTS] = {APPLICATIONS: {}, ADVANCED: {}}
1074
+ merged_conf[pipeline_cst.SURFACE_MODELING] = {
1075
+ APPLICATIONS: {},
1076
+ ADVANCED: {},
1077
+ }
1078
+
1079
+ for resolution in resolutions:
1080
+ used_conf = used_configurations[resolution]
1081
+ if pipeline_cst.TIE_POINTS in used_conf:
1082
+ merged_conf[pipeline_cst.TIE_POINTS][APPLICATIONS][
1083
+ str(resolution)
1084
+ ] = used_conf[pipeline_cst.TIE_POINTS][APPLICATIONS]
1085
+ merged_conf[pipeline_cst.TIE_POINTS][ADVANCED][str(resolution)] = (
1086
+ used_conf[pipeline_cst.TIE_POINTS][ADVANCED]
1087
+ )
1088
+ merged_conf[pipeline_cst.SURFACE_MODELING][APPLICATIONS][
1089
+ str(resolution)
1090
+ ] = used_conf[pipeline_cst.SURFACE_MODELING][APPLICATIONS]
1091
+ merged_conf[pipeline_cst.SURFACE_MODELING][ADVANCED][
1092
+ str(resolution)
1093
+ ] = used_conf[pipeline_cst.SURFACE_MODELING][ADVANCED]
1094
+
1095
+ return merged_conf