cars 1.0.0rc2__cp312-cp312-win_amd64.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of cars might be problematic. Click here for more details.

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