cars 1.0.0a3__cp39-cp39-win_amd64.whl → 1.0.0a4__cp39-cp39-win_amd64.whl

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

Potentially problematic release.


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

Files changed (141) hide show
  1. cars/__init__.py +5 -5
  2. cars/applications/__init__.py +0 -3
  3. cars/applications/application_template.py +20 -0
  4. cars/applications/auxiliary_filling/abstract_auxiliary_filling_app.py +12 -2
  5. cars/applications/auxiliary_filling/auxiliary_filling_algo.py +2 -2
  6. cars/applications/auxiliary_filling/auxiliary_filling_from_sensors_app.py +80 -36
  7. cars/applications/dem_generation/dem_generation_algo.py +1 -1
  8. cars/applications/dem_generation/dem_generation_wrappers.py +23 -57
  9. cars/applications/dem_generation/dichotomic_generation_app.py +3 -3
  10. cars/applications/dem_generation/rasterization_app.py +100 -41
  11. cars/applications/dense_match_filling/__init__.py +1 -1
  12. cars/applications/dense_match_filling/abstract_dense_match_filling_app.py +2 -15
  13. cars/applications/dense_match_filling/fill_disp_algo.py +32 -373
  14. cars/applications/dense_match_filling/fill_disp_wrappers.py +0 -343
  15. cars/applications/dense_match_filling/zero_padding_app.py +10 -5
  16. cars/applications/dense_matching/abstract_dense_matching_app.py +2 -1
  17. cars/applications/dense_matching/census_mccnn_sgm_app.py +38 -39
  18. cars/applications/dense_matching/cpp/dense_matching_cpp.cp39-win_amd64.dll.a +0 -0
  19. cars/applications/dense_matching/cpp/dense_matching_cpp.cp39-win_amd64.pyd +0 -0
  20. cars/applications/dense_matching/dense_matching_algo.py +48 -14
  21. cars/applications/dense_matching/dense_matching_wrappers.py +11 -3
  22. cars/applications/dense_matching/disparity_grid_algo.py +84 -62
  23. cars/applications/dense_matching/loaders/pandora_loader.py +91 -33
  24. cars/applications/dsm_filling/border_interpolation_app.py +1 -7
  25. cars/applications/dsm_filling/bulldozer_filling_app.py +2 -8
  26. cars/applications/dsm_filling/exogenous_filling_app.py +4 -9
  27. cars/applications/grid_generation/abstract_grid_generation_app.py +1 -1
  28. cars/applications/grid_generation/epipolar_grid_generation_app.py +4 -2
  29. cars/applications/grid_generation/grid_correction_app.py +4 -1
  30. cars/applications/grid_generation/grid_generation_algo.py +7 -2
  31. cars/applications/ground_truth_reprojection/abstract_ground_truth_reprojection_app.py +1 -1
  32. cars/applications/ground_truth_reprojection/direct_localization_app.py +2 -2
  33. cars/applications/ground_truth_reprojection/ground_truth_reprojection_algo.py +2 -1
  34. cars/applications/point_cloud_fusion/abstract_pc_fusion_app.py +0 -155
  35. cars/applications/point_cloud_fusion/mapping_to_terrain_tiles_app.py +0 -658
  36. cars/applications/point_cloud_fusion/pc_fusion_algo.py +0 -1339
  37. cars/applications/point_cloud_fusion/pc_fusion_wrappers.py +0 -869
  38. cars/applications/point_cloud_outlier_removal/abstract_outlier_removal_app.py +2 -1
  39. cars/applications/point_cloud_outlier_removal/outlier_removal_algo.py +9 -8
  40. cars/applications/point_cloud_outlier_removal/small_components_app.py +96 -267
  41. cars/applications/point_cloud_outlier_removal/statistical_app.py +116 -275
  42. cars/applications/rasterization/abstract_pc_rasterization_app.py +1 -1
  43. cars/applications/rasterization/rasterization_algo.py +18 -6
  44. cars/applications/rasterization/rasterization_wrappers.py +2 -1
  45. cars/applications/rasterization/simple_gaussian_app.py +60 -113
  46. cars/applications/resampling/abstract_resampling_app.py +1 -1
  47. cars/applications/resampling/bicubic_resampling_app.py +3 -1
  48. cars/applications/resampling/resampling_algo.py +16 -4
  49. cars/applications/resampling/resampling_wrappers.py +3 -1
  50. cars/applications/sparse_matching/abstract_sparse_matching_app.py +1 -1
  51. cars/applications/sparse_matching/sift_app.py +3 -3
  52. cars/applications/sparse_matching/sparse_matching_algo.py +3 -2
  53. cars/applications/sparse_matching/sparse_matching_wrappers.py +1 -1
  54. cars/applications/triangulation/abstract_triangulation_app.py +1 -1
  55. cars/applications/triangulation/line_of_sight_intersection_app.py +13 -11
  56. cars/applications/triangulation/pc_transform.py +552 -0
  57. cars/applications/triangulation/triangulation_algo.py +6 -4
  58. cars/applications/triangulation/triangulation_wrappers.py +1 -0
  59. cars/bundleadjustment.py +6 -6
  60. cars/cars.py +11 -9
  61. cars/core/cars_logging.py +80 -49
  62. cars/core/constants.py +0 -1
  63. cars/core/datasets.py +5 -2
  64. cars/core/geometry/abstract_geometry.py +256 -25
  65. cars/core/geometry/shareloc_geometry.py +110 -82
  66. cars/core/inputs.py +57 -19
  67. cars/core/outputs.py +1 -1
  68. cars/core/preprocessing.py +17 -3
  69. cars/core/projection.py +9 -6
  70. cars/core/tiling.py +10 -3
  71. cars/data_structures/cars_dataset.py +5 -5
  72. cars/data_structures/corresponding_tiles_tools.py +0 -103
  73. cars/data_structures/format_transformation.py +4 -1
  74. cars/devibrate.py +6 -3
  75. cars/extractroi.py +20 -21
  76. cars/orchestrator/cluster/abstract_cluster.py +15 -5
  77. cars/orchestrator/cluster/abstract_dask_cluster.py +6 -2
  78. cars/orchestrator/cluster/dask_jobqueue_utils.py +1 -1
  79. cars/orchestrator/cluster/log_wrapper.py +148 -21
  80. cars/orchestrator/cluster/mp_cluster/multiprocessing_cluster.py +11 -3
  81. cars/orchestrator/cluster/mp_cluster/multiprocessing_profiler.py +2 -2
  82. cars/orchestrator/cluster/pbs_dask_cluster.py +1 -1
  83. cars/orchestrator/cluster/sequential_cluster.py +5 -4
  84. cars/orchestrator/cluster/slurm_dask_cluster.py +1 -1
  85. cars/orchestrator/orchestrator.py +14 -3
  86. cars/orchestrator/registry/id_generator.py +1 -0
  87. cars/orchestrator/registry/saver_registry.py +2 -2
  88. cars/pipelines/conf_resolution/conf_final_resolution.json +5 -3
  89. cars/pipelines/default/default_pipeline.py +462 -1073
  90. cars/pipelines/parameters/advanced_parameters.py +74 -64
  91. cars/pipelines/parameters/advanced_parameters_constants.py +2 -5
  92. cars/pipelines/parameters/application_parameters.py +71 -0
  93. cars/pipelines/parameters/depth_map_inputs.py +0 -314
  94. cars/pipelines/parameters/dsm_inputs.py +40 -4
  95. cars/pipelines/parameters/output_parameters.py +2 -2
  96. cars/pipelines/parameters/sensor_inputs.py +30 -75
  97. cars/pipelines/parameters/sensor_inputs_constants.py +0 -2
  98. cars/pipelines/parameters/sensor_loaders/__init__.py +4 -3
  99. cars/pipelines/parameters/sensor_loaders/basic_classif_loader.py +106 -0
  100. cars/pipelines/parameters/sensor_loaders/{basic_sensor_loader.py → basic_image_loader.py} +16 -22
  101. cars/pipelines/parameters/sensor_loaders/pivot_classif_loader.py +121 -0
  102. cars/pipelines/parameters/sensor_loaders/{pivot_sensor_loader.py → pivot_image_loader.py} +10 -21
  103. cars/pipelines/parameters/sensor_loaders/sensor_loader.py +4 -6
  104. cars/pipelines/parameters/sensor_loaders/sensor_loader_template.py +1 -3
  105. cars/pipelines/pipeline_template.py +1 -3
  106. cars/pipelines/unit/unit_pipeline.py +527 -1016
  107. cars/starter.py +4 -3
  108. cars-1.0.0a4.dist-info/DELVEWHEEL +2 -0
  109. {cars-1.0.0a3.dist-info → cars-1.0.0a4.dist-info}/METADATA +135 -53
  110. {cars-1.0.0a3.dist-info → cars-1.0.0a4.dist-info}/RECORD +116 -132
  111. cars.libs/.load-order-cars-1.0.0a4 +3 -0
  112. cars.libs/libgcc_s_seh-1-b2494fcbd4d80cf2c98fdd5261f6d850.dll +0 -0
  113. cars.libs/libstdc++-6-e9b0d12ae0e9555bbae55e8dfd08c3f7.dll +0 -0
  114. cars.libs/libwinpthread-1-7882d1b093714ccdfaf4e0789a817792.dll +0 -0
  115. cars/applications/dense_match_filling/cpp/__init__.py +0 -0
  116. cars/applications/dense_match_filling/cpp/dense_match_filling_cpp.cp39-win_amd64.dll.a +0 -0
  117. cars/applications/dense_match_filling/cpp/dense_match_filling_cpp.cp39-win_amd64.pyd +0 -0
  118. cars/applications/dense_match_filling/cpp/dense_match_filling_cpp.py +0 -72
  119. cars/applications/dense_match_filling/cpp/includes/dense_match_filling.hpp +0 -46
  120. cars/applications/dense_match_filling/cpp/meson.build +0 -9
  121. cars/applications/dense_match_filling/cpp/src/bindings.cpp +0 -11
  122. cars/applications/dense_match_filling/cpp/src/dense_match_filling.cpp +0 -142
  123. cars/applications/dense_match_filling/plane_app.py +0 -556
  124. cars/applications/hole_detection/__init__.py +0 -30
  125. cars/applications/hole_detection/abstract_hole_detection_app.py +0 -125
  126. cars/applications/hole_detection/cloud_to_bbox_app.py +0 -346
  127. cars/applications/hole_detection/hole_detection_algo.py +0 -144
  128. cars/applications/hole_detection/hole_detection_wrappers.py +0 -53
  129. cars/applications/point_cloud_denoising/__init__.py +0 -29
  130. cars/applications/point_cloud_denoising/abstract_pc_denoising_app.py +0 -273
  131. cars/applications/point_cloud_fusion/__init__.py +0 -30
  132. cars/applications/point_cloud_fusion/cloud_fusion_constants.py +0 -39
  133. cars/applications/sparse_matching/pandora_sparse_matching_app.py +0 -0
  134. cars/pipelines/parameters/depth_map_inputs_constants.py +0 -25
  135. cars-1.0.0a3.dist-info/DELVEWHEEL +0 -2
  136. cars.libs/.load-order-cars-1.0.0a3 +0 -3
  137. cars.libs/libgcc_s_seh-1-ca70890bbc5723b6d0ea31e9c9cded2b.dll +0 -0
  138. cars.libs/libstdc++-6-00ee19f73d5122a1277c137b1c218401.dll +0 -0
  139. cars.libs/libwinpthread-1-f5042e8e3d21edce20c1bc99445f551b.dll +0 -0
  140. {cars-1.0.0a3.dist-info → cars-1.0.0a4.dist-info}/WHEEL +0 -0
  141. {cars-1.0.0a3.dist-info → cars-1.0.0a4.dist-info}/entry_points.txt +0 -0
@@ -32,7 +32,6 @@ from __future__ import print_function
32
32
 
33
33
  import copy
34
34
  import logging
35
- import math
36
35
  import os
37
36
 
38
37
  import numpy as np
@@ -49,10 +48,6 @@ from cars.applications.dem_generation import (
49
48
  )
50
49
  from cars.applications.grid_generation import grid_correction_app
51
50
  from cars.applications.grid_generation.transform_grid import transform_grid_func
52
- from cars.applications.point_cloud_fusion import (
53
- pc_fusion_algo,
54
- pc_fusion_wrappers,
55
- )
56
51
  from cars.core import preprocessing, projection, roi_tools
57
52
  from cars.core.geometry.abstract_geometry import AbstractGeometry
58
53
  from cars.core.inputs import (
@@ -68,13 +63,14 @@ from cars.orchestrator import orchestrator
68
63
  from cars.orchestrator.cluster.log_wrapper import cars_profile
69
64
  from cars.pipelines.parameters import advanced_parameters
70
65
  from cars.pipelines.parameters import advanced_parameters_constants as adv_cst
71
- from cars.pipelines.parameters import depth_map_inputs
72
- from cars.pipelines.parameters import depth_map_inputs_constants as depth_cst
73
- from cars.pipelines.parameters import dsm_inputs
66
+ from cars.pipelines.parameters import application_parameters, dsm_inputs
74
67
  from cars.pipelines.parameters import dsm_inputs_constants as dsm_cst
75
68
  from cars.pipelines.parameters import output_constants as out_cst
76
69
  from cars.pipelines.parameters import output_parameters, sensor_inputs
77
70
  from cars.pipelines.parameters import sensor_inputs_constants as sens_cst
71
+ from cars.pipelines.parameters.advanced_parameters_constants import (
72
+ USE_ENDOGENOUS_DEM,
73
+ )
78
74
  from cars.pipelines.pipeline import Pipeline
79
75
  from cars.pipelines.pipeline_constants import (
80
76
  ADVANCED,
@@ -109,7 +105,6 @@ class UnitPipeline(PipelineTemplate):
109
105
  save_output_point_clouds
110
106
  geom_plugin_without_dem_and_geoid
111
107
  geom_plugin_with_dem_and_geoid
112
- dem_generation_roi
113
108
 
114
109
  :param pipeline_name: name of the pipeline.
115
110
  :type pipeline_name: str
@@ -121,6 +116,8 @@ class UnitPipeline(PipelineTemplate):
121
116
 
122
117
  # Used conf
123
118
  self.used_conf = {}
119
+ # refined conf
120
+ self.refined_conf = {}
124
121
 
125
122
  # Transform relative path to absolute path
126
123
  if config_dir is not None:
@@ -137,24 +134,35 @@ class UnitPipeline(PipelineTemplate):
137
134
  # Check conf inputs
138
135
  inputs = self.check_inputs(conf[INPUTS], config_dir=config_dir)
139
136
  self.used_conf[INPUTS] = inputs
137
+ self.refined_conf[INPUTS] = copy.deepcopy(inputs)
140
138
 
141
139
  # Check advanced parameters
142
140
  # TODO static method in the base class
141
+ output_dem_dir = os.path.join(
142
+ conf[OUTPUT][out_cst.OUT_DIRECTORY], "dump_dir", "initial_elevation"
143
+ )
144
+ safe_makedirs(output_dem_dir)
143
145
  (
144
146
  inputs,
145
147
  advanced,
146
148
  self.geometry_plugin,
147
149
  self.geom_plugin_without_dem_and_geoid,
148
150
  self.geom_plugin_with_dem_and_geoid,
149
- self.dem_generation_roi,
150
151
  self.scaling_coeff,
151
152
  self.land_cover_map,
152
153
  self.classification_to_config_mapping,
153
154
  ) = advanced_parameters.check_advanced_parameters(
154
- inputs, conf.get(ADVANCED, {}), check_epipolar_a_priori=True
155
+ inputs,
156
+ conf.get(ADVANCED, {}),
157
+ check_epipolar_a_priori=True,
158
+ output_dem_dir=output_dem_dir,
155
159
  )
156
160
  self.used_conf[ADVANCED] = advanced
157
161
 
162
+ self.refined_conf[ADVANCED] = copy.deepcopy(advanced)
163
+ # Refined conf: resolutions 1
164
+ self.refined_conf[ADVANCED][adv_cst.EPIPOLAR_RESOLUTIONS] = [1]
165
+
158
166
  # Get ROI
159
167
  (
160
168
  self.input_roi_poly,
@@ -172,6 +180,10 @@ class UnitPipeline(PipelineTemplate):
172
180
  ) = self.check_output(conf[OUTPUT], self.scaling_coeff)
173
181
 
174
182
  self.used_conf[OUTPUT] = output
183
+ self.out_dir = self.used_conf[OUTPUT][out_cst.OUT_DIRECTORY]
184
+ self.dump_dir = os.path.join(self.out_dir, "dump_dir")
185
+
186
+ self.refined_conf[OUTPUT] = copy.deepcopy(output)
175
187
 
176
188
  prod_level = output[out_cst.PRODUCT_LEVEL]
177
189
 
@@ -186,11 +198,7 @@ class UnitPipeline(PipelineTemplate):
186
198
  or self.save_output_point_cloud
187
199
  )
188
200
  self.sensors_in_inputs = sens_cst.SENSORS in self.used_conf[INPUTS]
189
- self.depth_maps_in_inputs = (
190
- depth_cst.DEPTH_MAPS in self.used_conf[INPUTS]
191
- )
192
201
  self.dsms_in_inputs = dsm_cst.DSMS in self.used_conf[INPUTS]
193
- self.merging = self.used_conf[ADVANCED][adv_cst.MERGING]
194
202
 
195
203
  self.phasing = self.used_conf[ADVANCED][adv_cst.PHASING]
196
204
 
@@ -198,7 +206,6 @@ class UnitPipeline(PipelineTemplate):
198
206
  self.sensors_in_inputs
199
207
  and (not self.output_level_none)
200
208
  and not self.dsms_in_inputs
201
- and not self.depth_maps_in_inputs
202
209
  )
203
210
 
204
211
  if self.output_level_none:
@@ -233,23 +240,15 @@ class UnitPipeline(PipelineTemplate):
233
240
  # Check conf application
234
241
  application_conf = self.check_applications(conf.get(APPLICATIONS, {}))
235
242
 
236
- if (
237
- self.sensors_in_inputs
238
- and not self.depth_maps_in_inputs
239
- and not self.dsms_in_inputs
240
- ):
243
+ if self.sensors_in_inputs and not self.dsms_in_inputs:
241
244
  # Check conf application vs inputs application
242
245
  application_conf = self.check_applications_with_inputs(
243
- self.used_conf[INPUTS], application_conf
246
+ self.used_conf[INPUTS], application_conf, self.res_resamp
244
247
  )
245
248
 
246
249
  self.used_conf[APPLICATIONS] = application_conf
247
250
 
248
- self.config_full_res = copy.deepcopy(self.used_conf)
249
- self.config_full_res.__delitem__("applications")
250
- self.config_full_res[ADVANCED][adv_cst.EPIPOLAR_A_PRIORI] = {}
251
- self.config_full_res[ADVANCED][adv_cst.TERRAIN_A_PRIORI] = {}
252
- self.config_full_res[ADVANCED][adv_cst.USE_EPIPOLAR_A_PRIORI] = True
251
+ self.out_dir = self.used_conf[OUTPUT][out_cst.OUT_DIRECTORY]
253
252
 
254
253
  def quit_on_app(self, app_name):
255
254
  """
@@ -278,34 +277,26 @@ class UnitPipeline(PipelineTemplate):
278
277
  sensor_to_depth_apps = {
279
278
  "grid_generation": 1, # and 5
280
279
  "resampling": 2, # and 8
281
- "hole_detection": 3,
282
- "sparse_matching.sift": 4,
280
+ "sparse_matching": 4,
283
281
  "ground_truth_reprojection": 6,
284
282
  "dense_matching": 8,
285
- "dense_match_filling.1": 9,
286
- "dense_match_filling.2": 10,
283
+ "dense_match_filling": 9,
287
284
  "triangulation": 11,
288
285
  "point_cloud_outlier_removal.1": 12,
289
286
  "point_cloud_outlier_removal.2": 13,
290
287
  }
291
288
 
292
- depth_merge_apps = {
293
- "point_cloud_fusion": 14,
294
- }
295
-
296
289
  depth_to_dsm_apps = {
297
- "pc_denoising": 15,
298
- "point_cloud_rasterization": 16,
299
- "dem_generation": 17,
300
- "dsm_filling.1": 18,
301
- "dsm_filling.2": 19,
302
- "dsm_filling.3": 20,
303
- "auxiliary_filling": 21,
290
+ "point_cloud_rasterization": 15,
291
+ "dem_generation": 16,
292
+ "dsm_filling.1": 17,
293
+ "dsm_filling.2": 18,
294
+ "dsm_filling.3": 19,
295
+ "auxiliary_filling": 20,
304
296
  }
305
297
 
306
298
  self.app_values = {}
307
299
  self.app_values.update(sensor_to_depth_apps)
308
- self.app_values.update(depth_merge_apps)
309
300
  self.app_values.update(depth_to_dsm_apps)
310
301
 
311
302
  app_conf = conf.get(APPLICATIONS, {})
@@ -327,11 +318,7 @@ class UnitPipeline(PipelineTemplate):
327
318
  ).format(key)
328
319
  logging.warning(warn_msg)
329
320
 
330
- elif (
331
- self.sensors_in_inputs
332
- and not self.depth_maps_in_inputs
333
- and not self.dsms_in_inputs
334
- ):
321
+ elif self.sensors_in_inputs and not self.dsms_in_inputs:
335
322
  self.compute_depth_map = True
336
323
  self.last_application_to_run = max(
337
324
  self.last_application_to_run, self.app_values[key]
@@ -339,11 +326,7 @@ class UnitPipeline(PipelineTemplate):
339
326
 
340
327
  elif key in depth_to_dsm_apps:
341
328
 
342
- if not (
343
- self.sensors_in_inputs
344
- or self.depth_maps_in_inputs
345
- or self.dsms_in_inputs
346
- ):
329
+ if not (self.sensors_in_inputs or self.dsms_in_inputs):
347
330
  warn_msg = (
348
331
  "The application {} can only be used when sensor "
349
332
  "images or depth maps are given as an input. "
@@ -352,11 +335,7 @@ class UnitPipeline(PipelineTemplate):
352
335
  logging.warning(warn_msg)
353
336
 
354
337
  else:
355
- if (
356
- self.sensors_in_inputs
357
- and not self.depth_maps_in_inputs
358
- and not self.dsms_in_inputs
359
- ):
338
+ if self.sensors_in_inputs and not self.dsms_in_inputs:
360
339
  self.compute_depth_map = True
361
340
 
362
341
  # enabled to start the depth map to dsm process
@@ -366,43 +345,6 @@ class UnitPipeline(PipelineTemplate):
366
345
  self.last_application_to_run, self.app_values[key]
367
346
  )
368
347
 
369
- elif key in depth_merge_apps:
370
-
371
- if not self.merging:
372
- warn_msg = (
373
- "The application {} can only be used when merging "
374
- "is activated (this parameter is located in the "
375
- "'advanced' config key). "
376
- "The application's configuration will be ignored."
377
- ).format(key)
378
- logging.warning(warn_msg)
379
-
380
- elif not (
381
- self.sensors_in_inputs
382
- or self.depth_maps_in_inputs
383
- or self.dsms_in_inputs
384
- ):
385
- warn_msg = (
386
- "The application {} can only be used when sensor "
387
- "images or depth maps are given as an input. "
388
- "Its configuration will be ignored."
389
- ).format(key)
390
- logging.warning(warn_msg)
391
-
392
- else:
393
- if (
394
- self.sensors_in_inputs
395
- and not self.depth_maps_in_inputs
396
- and not self.dsms_in_inputs
397
- ):
398
- self.compute_depth_map = True
399
-
400
- # enabled to start the depth map to dsm process
401
- self.save_output_point_cloud = True
402
-
403
- self.last_application_to_run = max(
404
- self.last_application_to_run, self.app_values[key]
405
- )
406
348
  else:
407
349
  warn_msg = (
408
350
  "The application {} was not recognized. Its configuration"
@@ -445,30 +387,36 @@ class UnitPipeline(PipelineTemplate):
445
387
  """
446
388
 
447
389
  output_config = {}
448
- if (
449
- sens_cst.SENSORS in conf
450
- and depth_cst.DEPTH_MAPS not in conf
451
- and dsm_cst.DSMS not in conf
452
- ):
390
+ if sens_cst.SENSORS in conf and dsm_cst.DSMS not in conf:
453
391
  output_config = sensor_inputs.sensors_check_inputs(
454
392
  conf, config_dir=config_dir
455
393
  )
456
- elif depth_cst.DEPTH_MAPS in conf:
457
- output_config = {
458
- **output_config,
459
- **depth_map_inputs.check_depth_maps_inputs(
460
- conf, config_dir=config_dir
461
- ),
462
- }
463
- else:
394
+ elif dsm_cst.DSMS in conf:
464
395
  output_config = {
465
396
  **output_config,
466
397
  **dsm_inputs.check_dsm_inputs(conf, config_dir=config_dir),
467
398
  }
399
+ else:
400
+ raise RuntimeError("No sensors or dsms in inputs")
468
401
  return output_config
469
402
 
470
- @staticmethod
471
- def check_output(conf, scaling_coeff):
403
+ def save_configurations(self):
404
+ """
405
+ Save used_conf and refined_conf configurations
406
+ """
407
+
408
+ cars_dataset.save_dict(
409
+ self.used_conf,
410
+ os.path.join(self.out_dir, "current_res_used_conf.json"),
411
+ safe_save=True,
412
+ )
413
+ cars_dataset.save_dict(
414
+ self.refined_conf,
415
+ os.path.join(self.out_dir, "refined_conf.json"),
416
+ safe_save=True,
417
+ )
418
+
419
+ def check_output(self, conf, scaling_coeff):
472
420
  """
473
421
  Check the output given
474
422
 
@@ -484,7 +432,6 @@ class UnitPipeline(PipelineTemplate):
484
432
  def check_applications( # noqa: C901 : too complex
485
433
  self,
486
434
  conf,
487
- key=None,
488
435
  ):
489
436
  """
490
437
  Check the given configuration for applications,
@@ -495,42 +442,16 @@ class UnitPipeline(PipelineTemplate):
495
442
  """
496
443
  scaling_coeff = self.scaling_coeff
497
444
 
445
+ needed_applications = application_parameters.get_needed_apps(
446
+ self.sensors_in_inputs,
447
+ self.save_output_dsm,
448
+ self.save_output_point_cloud,
449
+ conf,
450
+ )
451
+
498
452
  # Check if all specified applications are used
499
453
  # Application in terrain_application are note used in
500
454
  # the sensors_to_dense_depth_maps pipeline
501
- needed_applications = []
502
-
503
- if self.sensors_in_inputs:
504
- needed_applications += [
505
- "grid_generation",
506
- "resampling",
507
- "ground_truth_reprojection",
508
- "hole_detection",
509
- "dense_match_filling.1",
510
- "dense_match_filling.2",
511
- "sparse_matching.sift",
512
- "dense_matching",
513
- "triangulation",
514
- "dem_generation",
515
- "point_cloud_outlier_removal.1",
516
- "point_cloud_outlier_removal.2",
517
- ]
518
-
519
- if self.save_output_dsm or self.save_output_point_cloud:
520
- needed_applications += ["pc_denoising"]
521
-
522
- if self.save_output_dsm:
523
- needed_applications += [
524
- "point_cloud_rasterization",
525
- "dsm_filling.1",
526
- "dsm_filling.2",
527
- "dsm_filling.3",
528
- "auxiliary_filling",
529
- ]
530
-
531
- if self.merging: # we have to merge point clouds, add merging apps
532
- needed_applications += ["point_cloud_fusion"]
533
-
534
455
  for app_key in conf.keys():
535
456
  if app_key not in needed_applications:
536
457
  msg = (
@@ -543,28 +464,24 @@ class UnitPipeline(PipelineTemplate):
543
464
  # Initialize used config
544
465
  used_conf = {}
545
466
 
546
- for app_key in [
547
- "point_cloud_outlier_removal.1",
548
- "point_cloud_outlier_removal.2",
549
- "auxiliary_filling",
550
- ]:
551
- if conf.get(app_key) is not None:
552
- config_app = conf.get(app_key)
553
- if "activated" not in config_app:
554
- conf[app_key]["activated"] = True
555
-
556
467
  for app_key in needed_applications:
557
468
  used_conf[app_key] = conf.get(app_key, {})
469
+ if used_conf[app_key] is None:
470
+ continue
558
471
  used_conf[app_key]["save_intermediate_data"] = (
559
472
  self.save_all_intermediate_data
560
473
  or used_conf[app_key].get("save_intermediate_data", False)
561
474
  )
562
475
 
563
- for app_key in [
564
- "point_cloud_fusion",
565
- "pc_denoising",
566
- ]:
567
- if app_key in needed_applications:
476
+ if app_key == "auxiliary_filling":
477
+ if used_conf[app_key] is not None:
478
+ used_conf[app_key]["activated"] = used_conf[app_key].get(
479
+ "activated", True
480
+ )
481
+
482
+ if app_key in [
483
+ "point_cloud_fusion",
484
+ ]:
568
485
  used_conf[app_key]["save_by_pair"] = used_conf[app_key].get(
569
486
  "save_by_pair", self.save_all_point_clouds_by_pair
570
487
  )
@@ -572,21 +489,18 @@ class UnitPipeline(PipelineTemplate):
572
489
  self.epipolar_grid_generation_application = None
573
490
  self.resampling_application = None
574
491
  self.ground_truth_reprojection = None
575
- self.hole_detection_app = None
576
- self.dense_match_filling_1 = None
577
- self.dense_match_filling_2 = None
578
- self.sparse_mtch_sift_app = None
492
+ self.dense_match_filling = None
493
+ self.sparse_mtch_app = None
579
494
  self.dense_matching_app = None
580
495
  self.triangulation_application = None
581
496
  self.dem_generation_application = None
582
- self.pc_denoising_application = None
583
- self.pc_outlier_removal_1_app = None
584
- self.pc_outlier_removal_2_app = None
497
+ self.pc_outlier_removal_apps = {}
585
498
  self.rasterization_application = None
586
499
  self.pc_fusion_application = None
587
500
  self.dsm_filling_1_application = None
588
501
  self.dsm_filling_2_application = None
589
502
  self.dsm_filling_3_application = None
503
+ self.dsm_filling_apps = {}
590
504
 
591
505
  if self.sensors_in_inputs:
592
506
  # Epipolar grid generation
@@ -627,49 +541,27 @@ class UnitPipeline(PipelineTemplate):
627
541
  cfg=used_conf.get("ground_truth_reprojection", {}),
628
542
  scaling_coeff=scaling_coeff,
629
543
  )
630
- # holes detection
631
- self.hole_detection_app = Application(
632
- "hole_detection",
633
- cfg=used_conf.get("hole_detection", {}),
634
- scaling_coeff=scaling_coeff,
635
- )
636
- used_conf["hole_detection"] = self.hole_detection_app.get_conf()
637
544
 
638
- # disparity filling 1 plane
639
- self.dense_match_filling_1 = Application(
545
+ # disparity filling
546
+ self.dense_match_filling = Application(
640
547
  "dense_match_filling",
641
548
  cfg=used_conf.get(
642
- "dense_match_filling.1",
643
- {"method": "plane"},
644
- ),
645
- scaling_coeff=scaling_coeff,
646
- )
647
- used_conf["dense_match_filling.1"] = (
648
- self.dense_match_filling_1.get_conf()
649
- )
650
-
651
- # disparity filling 2
652
- self.dense_match_filling_2 = Application(
653
- "dense_match_filling",
654
- cfg=used_conf.get(
655
- "dense_match_filling.2",
549
+ "dense_match_filling",
656
550
  {"method": "zero_padding"},
657
551
  ),
658
552
  scaling_coeff=scaling_coeff,
659
553
  )
660
- used_conf["dense_match_filling.2"] = (
661
- self.dense_match_filling_2.get_conf()
554
+ used_conf["dense_match_filling"] = (
555
+ self.dense_match_filling.get_conf()
662
556
  )
663
557
 
664
558
  # Sparse Matching
665
- self.sparse_mtch_sift_app = Application(
559
+ self.sparse_mtch_app = Application(
666
560
  "sparse_matching",
667
- cfg=used_conf.get("sparse_matching.sift", {"method": "sift"}),
561
+ cfg=used_conf.get("sparse_matching", {"method": "sift"}),
668
562
  scaling_coeff=scaling_coeff,
669
563
  )
670
- used_conf["sparse_matching.sift"] = (
671
- self.sparse_mtch_sift_app.get_conf()
672
- )
564
+ used_conf["sparse_matching"] = self.sparse_mtch_app.get_conf()
673
565
 
674
566
  # Matching
675
567
  generate_performance_map = (
@@ -692,6 +584,14 @@ class UnitPipeline(PipelineTemplate):
692
584
  is None
693
585
  ):
694
586
  dense_matching_config["performance_map_method"] = "risk"
587
+
588
+ # particular case for some epipolar resolutions
589
+ if not dense_matching_config:
590
+ used_conf["dense_matching"]["performance_map_method"] = [
591
+ "risk",
592
+ "intervals",
593
+ ]
594
+
695
595
  self.dense_matching_app = Application(
696
596
  "dense_matching",
697
597
  cfg=dense_matching_config,
@@ -719,47 +619,50 @@ class UnitPipeline(PipelineTemplate):
719
619
  self.dem_generation_application.get_conf()
720
620
  )
721
621
 
722
- # Points cloud small component outlier removal
723
- if "point_cloud_outlier_removal.1" in used_conf:
724
- if "method" not in used_conf["point_cloud_outlier_removal.1"]:
725
- used_conf["point_cloud_outlier_removal.1"][
726
- "method"
727
- ] = "small_components"
728
- self.pc_outlier_removal_1_app = Application(
729
- "point_cloud_outlier_removal",
730
- cfg=used_conf.get(
731
- "point_cloud_outlier_removal.1",
732
- {"method": "small_components"},
733
- ),
734
- scaling_coeff=scaling_coeff,
735
- )
736
- used_conf["point_cloud_outlier_removal.1"] = (
737
- self.pc_outlier_removal_1_app.get_conf()
738
- )
622
+ for app_key, app_conf in used_conf.items():
623
+ if not app_key.startswith("point_cloud_outlier_removal"):
624
+ continue
739
625
 
740
- # Points cloud statistical outlier removal
741
- self.pc_outlier_removal_2_app = Application(
742
- "point_cloud_outlier_removal",
743
- cfg=used_conf.get(
744
- "point_cloud_outlier_removal.2",
745
- {"method": "statistical"},
746
- ),
747
- scaling_coeff=scaling_coeff,
626
+ if app_conf is None:
627
+ self.pc_outlier_removal_apps = {}
628
+ # keep over multiple runs
629
+ used_conf["point_cloud_outlier_removal"] = None
630
+ break
631
+
632
+ if app_key in self.pc_outlier_removal_apps:
633
+ msg = (
634
+ f"The key {app_key} is defined twice in the input "
635
+ "configuration."
636
+ )
637
+ logging.error(msg)
638
+ raise NameError(msg)
639
+
640
+ if app_key[27:] == ".1":
641
+ app_conf.setdefault("method", "small_components")
642
+ if app_key[27:] == ".2":
643
+ app_conf.setdefault("method", "statistical")
644
+
645
+ self.pc_outlier_removal_apps[app_key] = Application(
646
+ "point_cloud_outlier_removal",
647
+ cfg=app_conf,
648
+ scaling_coeff=scaling_coeff,
649
+ )
650
+ used_conf[app_key] = self.pc_outlier_removal_apps[
651
+ app_key
652
+ ].get_conf()
653
+
654
+ methods_str = "\n".join(
655
+ f" - {k}={a.used_method}"
656
+ for k, a in self.pc_outlier_removal_apps.items()
748
657
  )
749
- used_conf["point_cloud_outlier_removal.2"] = (
750
- self.pc_outlier_removal_2_app.get_conf()
658
+ logging.info(
659
+ "{} point cloud outlier removal apps registered:\n{}".format(
660
+ len(self.pc_outlier_removal_apps), methods_str
661
+ )
751
662
  )
752
663
 
753
664
  if self.save_output_dsm or self.save_output_point_cloud:
754
665
 
755
- # Point cloud denoising
756
- self.pc_denoising_application = Application(
757
- "pc_denoising",
758
- cfg=used_conf.get("pc_denoising", {"method": "none"}),
759
- scaling_coeff=scaling_coeff,
760
- )
761
- used_conf["pc_denoising"] = self.pc_denoising_application.get_conf()
762
-
763
666
  if self.save_output_dsm:
764
667
 
765
668
  # Rasterization
@@ -771,41 +674,51 @@ class UnitPipeline(PipelineTemplate):
771
674
  used_conf["point_cloud_rasterization"] = (
772
675
  self.rasterization_application.get_conf()
773
676
  )
774
- # DSM filling 1 : Exogenous filling
775
- self.dsm_filling_1_application = Application(
776
- "dsm_filling",
777
- cfg=conf.get(
778
- "dsm_filling.1",
779
- {"method": "exogenous_filling"},
780
- ),
781
- scaling_coeff=scaling_coeff,
782
- )
783
- used_conf["dsm_filling.1"] = (
784
- self.dsm_filling_1_application.get_conf()
785
- )
786
- # DSM filling 2 : Bulldozer
787
- self.dsm_filling_2_application = Application(
788
- "dsm_filling",
789
- cfg=conf.get(
790
- "dsm_filling.2",
791
- {"method": "bulldozer"},
792
- ),
793
- )
794
- used_conf["dsm_filling.2"] = (
795
- self.dsm_filling_2_application.get_conf()
796
- )
797
- # DSM filling 3 : Border interpolation
798
- self.dsm_filling_3_application = Application(
799
- "dsm_filling",
800
- cfg=conf.get(
801
- "dsm_filling.3",
802
- {"method": "border_interpolation"},
803
- ),
804
- scaling_coeff=scaling_coeff,
677
+
678
+ for app_key, app_conf in used_conf.items():
679
+ if not app_key.startswith("dsm_filling"):
680
+ continue
681
+
682
+ if app_conf is None:
683
+ self.dsm_filling_apps = {}
684
+ # keep over multiple runs
685
+ used_conf["dsm_filling"] = None
686
+ break
687
+
688
+ if app_key in self.dsm_filling_apps:
689
+ msg = (
690
+ f"The key {app_key} is defined twice in the input "
691
+ "configuration."
692
+ )
693
+ logging.error(msg)
694
+ raise NameError(msg)
695
+
696
+ if app_key[11:] == ".1":
697
+ app_conf.setdefault("method", "exogenous_filling")
698
+ if app_key[11:] == ".2":
699
+ app_conf.setdefault("method", "bulldozer")
700
+ if app_key[11:] == ".3":
701
+ app_conf.setdefault("method", "border_interpolation")
702
+
703
+ self.dsm_filling_apps[app_key] = Application(
704
+ "dsm_filling",
705
+ cfg=app_conf,
706
+ scaling_coeff=scaling_coeff,
707
+ )
708
+ used_conf[app_key] = self.dsm_filling_apps[
709
+ app_key
710
+ ].get_conf()
711
+
712
+ methods_str = "\n".join(
713
+ f" - {k}={a.used_method}"
714
+ for k, a in self.dsm_filling_apps.items()
805
715
  )
806
- used_conf["dsm_filling.3"] = (
807
- self.dsm_filling_3_application.get_conf()
716
+ logging.info(
717
+ "{} dsm filling apps registered:\n{}".format(
718
+ len(self.dsm_filling_apps), methods_str
719
+ )
808
720
  )
721
+
809
722
  # Auxiliary filling
810
723
  self.auxiliary_filling_application = Application(
811
724
  "auxiliary_filling",
@@ -816,29 +729,16 @@ class UnitPipeline(PipelineTemplate):
816
729
  self.auxiliary_filling_application.get_conf()
817
730
  )
818
731
 
819
- if (
820
- self.dsm_filling_1_application.classification != ["nodata"]
821
- or self.dsm_filling_2_application.classification != ["nodata"]
822
- or self.dsm_filling_3_application.classification != ["nodata"]
732
+ if any(
733
+ app_obj.classification != ["nodata"]
734
+ for app_key, app_obj in self.dsm_filling_apps.items()
823
735
  ):
824
736
  self.save_output_classif_for_filling = True
825
737
 
826
- if self.merging:
827
-
828
- # Point cloud fusion
829
- self.pc_fusion_application = Application(
830
- "point_cloud_fusion",
831
- cfg=used_conf.get("point_cloud_fusion", {}),
832
- scaling_coeff=scaling_coeff,
833
- )
834
- used_conf["point_cloud_fusion"] = (
835
- self.pc_fusion_application.get_conf()
836
- )
837
-
838
738
  return used_conf
839
739
 
840
740
  def check_applications_with_inputs( # noqa: C901 : too complex
841
- self, inputs_conf, application_conf
741
+ self, inputs_conf, application_conf, epipolar_resolution
842
742
  ):
843
743
  """
844
744
  Check for each application the input and output configuration
@@ -848,55 +748,34 @@ class UnitPipeline(PipelineTemplate):
848
748
  :type inputs_conf: dict
849
749
  :param application_conf: application checked configuration
850
750
  :type application_conf: dict
751
+ :param epipolar_resolution: epipolar resolution
752
+ :type epipolar_resolution: int
851
753
  """
852
754
 
853
755
  initial_elevation = (
854
756
  inputs_conf[sens_cst.INITIAL_ELEVATION]["dem"] is not None
855
757
  )
856
- if self.sparse_mtch_sift_app.elevation_delta_lower_bound is None:
857
- self.sparse_mtch_sift_app.used_config[
858
- "elevation_delta_lower_bound"
859
- ] = (-500 if initial_elevation else -1000)
860
- self.sparse_mtch_sift_app.elevation_delta_lower_bound = (
861
- self.sparse_mtch_sift_app.used_config[
862
- "elevation_delta_lower_bound"
863
- ]
758
+ if self.sparse_mtch_app.elevation_delta_lower_bound is None:
759
+ self.sparse_mtch_app.used_config["elevation_delta_lower_bound"] = (
760
+ -500 if initial_elevation else -1000
864
761
  )
865
- if self.sparse_mtch_sift_app.elevation_delta_upper_bound is None:
866
- self.sparse_mtch_sift_app.used_config[
867
- "elevation_delta_upper_bound"
868
- ] = (1000 if initial_elevation else 9000)
869
- self.sparse_mtch_sift_app.elevation_delta_upper_bound = (
870
- self.sparse_mtch_sift_app.used_config[
871
- "elevation_delta_upper_bound"
872
- ]
762
+ self.sparse_mtch_app.elevation_delta_lower_bound = (
763
+ self.sparse_mtch_app.used_config["elevation_delta_lower_bound"]
873
764
  )
874
- application_conf["sparse_matching.sift"] = (
875
- self.sparse_mtch_sift_app.get_conf()
876
- )
877
-
878
- if (
879
- application_conf["dem_generation"]["method"]
880
- == "bulldozer_on_raster"
881
- ):
882
- first_image_path = next(iter(inputs_conf["sensors"].values()))[
883
- "image"
884
- ]["main_file"]
885
- first_image_size = rasterio_get_size(first_image_path)
886
- first_image_nb_pixels = math.prod(first_image_size)
887
- dem_gen_used_mem = first_image_nb_pixels / 1e8
888
- if dem_gen_used_mem > 8:
889
- logging.warning(
890
- "DEM generation method is 'bulldozer_on_raster'. "
891
- f"This method can use up to {dem_gen_used_mem} Gb "
892
- "of memory. If you think that it is too much for "
893
- "your computer, you can re-lauch the run using "
894
- "'dichotomic' method for DEM generation"
895
- )
765
+ if self.sparse_mtch_app.elevation_delta_upper_bound is None:
766
+ self.sparse_mtch_app.used_config["elevation_delta_upper_bound"] = (
767
+ 1000 if initial_elevation else 9000
768
+ )
769
+ self.sparse_mtch_app.elevation_delta_upper_bound = (
770
+ self.sparse_mtch_app.used_config["elevation_delta_upper_bound"]
771
+ )
772
+ application_conf["sparse_matching"] = self.sparse_mtch_app.get_conf()
896
773
 
897
774
  # check classification application parameter compare
898
775
  # to each sensors inputs classification list
899
776
  for application_key in application_conf:
777
+ if application_conf[application_key] is None:
778
+ continue
900
779
  if "classification" in application_conf[application_key]:
901
780
  for item in inputs_conf["sensors"]:
902
781
  if "classification" in inputs_conf["sensors"][item].keys():
@@ -931,44 +810,123 @@ class UnitPipeline(PipelineTemplate):
931
810
  )
932
811
  for key1, key2 in inputs_conf["pairing"]:
933
812
  corr_cfg = self.dense_matching_app.loader.get_conf()
934
- img_left = inputs_conf["sensors"][key1]["image"]["main_file"]
935
- img_right = inputs_conf["sensors"][key2]["image"]["main_file"]
813
+ nodata_left = inputs_conf["sensors"][key2]["image"]["no_data"]
814
+ nodata_right = inputs_conf["sensors"][key2]["image"]["no_data"]
936
815
  bands_left = list(
937
816
  inputs_conf["sensors"][key1]["image"]["bands"].keys()
938
817
  )
939
818
  bands_right = list(
940
819
  inputs_conf["sensors"][key2]["image"]["bands"].keys()
941
820
  )
942
- classif_left = None
943
- classif_right = None
821
+ bands_classif_left = None
822
+ bands_classif_right = None
944
823
  if (
945
824
  "classification" in inputs_conf["sensors"][key1]
946
825
  and inputs_conf["sensors"][key1]["classification"] is not None
947
826
  ):
948
- classif_left = inputs_conf["sensors"][key1]["classification"][
949
- "main_file"
950
- ]
827
+ bands_classif_left = inputs_conf["sensors"][key1][
828
+ "classification"
829
+ ]["bands"].keys()
951
830
  if (
952
831
  "classification" in inputs_conf["sensors"][key2]
953
832
  and inputs_conf["sensors"][key2]["classification"] is not None
954
833
  ):
955
- classif_right = inputs_conf["sensors"][key2]["classification"][
956
- "main_file"
957
- ]
834
+ bands_classif_right = inputs_conf["sensors"][key2][
835
+ "classification"
836
+ ]["bands"].keys()
958
837
  self.dense_matching_app.corr_config = (
959
838
  self.dense_matching_app.loader.check_conf(
960
839
  corr_cfg,
961
- img_left,
962
- img_right,
840
+ nodata_left,
841
+ nodata_right,
963
842
  bands_left,
964
843
  bands_right,
965
- classif_left,
966
- classif_right,
844
+ bands_classif_left,
845
+ bands_classif_right,
967
846
  )
968
847
  )
969
848
 
849
+ # Change the step regarding the resolution
850
+ # For the small resolution, the resampling perform better
851
+ # with a small step
852
+ # For the higher ones, a step at 30 should be better
853
+ first_image_path = next(iter(inputs_conf["sensors"].values()))["image"][
854
+ "main_file"
855
+ ]
856
+ first_image_size = rasterio_get_size(first_image_path)
857
+ size_low_res_img_row = first_image_size[0] // epipolar_resolution
858
+ size_low_res_img_col = first_image_size[1] // epipolar_resolution
859
+ if epipolar_resolution > 1:
860
+ if size_low_res_img_row <= 900 and size_low_res_img_col <= 900:
861
+ application_conf["grid_generation"]["epi_step"] = (
862
+ epipolar_resolution * 5
863
+ )
864
+ else:
865
+ application_conf["grid_generation"]["epi_step"] = (
866
+ epipolar_resolution * 30
867
+ )
868
+
970
869
  return application_conf
971
870
 
871
+ def generate_grid_correction_on_dem(self, pair_key, geo_plugin_on_dem):
872
+ """
873
+ Generate the epipolar grid correction for a given pair, using given dem
874
+ """
875
+
876
+ # Generate new grids with dem
877
+ # Generate rectification grids
878
+ (
879
+ grid_left_new_dem,
880
+ grid_right_new_dem,
881
+ ) = self.epipolar_grid_generation_application.run(
882
+ self.pairs[pair_key]["sensor_image_left"],
883
+ self.pairs[pair_key]["sensor_image_right"],
884
+ geo_plugin_on_dem,
885
+ orchestrator=self.cars_orchestrator,
886
+ pair_folder=os.path.join(
887
+ self.dump_dir,
888
+ "epipolar_grid_generation",
889
+ "new_dem",
890
+ pair_key,
891
+ ),
892
+ pair_key=pair_key,
893
+ )
894
+
895
+ if self.pairs[pair_key].get("sensor_matches_left", None) is None:
896
+ logging.error(
897
+ "No sensor matches available to compute grid correction"
898
+ )
899
+ return None
900
+
901
+ # Generate new matches with new grids
902
+ new_grid_matches_array = geo_plugin_on_dem.transform_matches_from_grids(
903
+ self.pairs[pair_key]["sensor_matches_left"],
904
+ self.pairs[pair_key]["sensor_matches_right"],
905
+ grid_left_new_dem,
906
+ grid_right_new_dem,
907
+ )
908
+
909
+ # Generate grid_correction
910
+ # Compute grid correction
911
+ (
912
+ new_grid_correction_coef,
913
+ _,
914
+ _,
915
+ _,
916
+ ) = grid_correction_app.estimate_right_grid_correction(
917
+ new_grid_matches_array,
918
+ grid_right_new_dem,
919
+ save_matches=False,
920
+ minimum_nb_matches=0,
921
+ pair_folder=os.path.join(
922
+ self.dump_dir, "grid_correction", " new_dem", pair_key
923
+ ),
924
+ pair_key=pair_key,
925
+ orchestrator=self.cars_orchestrator,
926
+ )
927
+
928
+ return new_grid_correction_coef
929
+
972
930
  def sensor_to_depth_maps(self): # noqa: C901
973
931
  """
974
932
  Creates the depth map from the sensor images given in the input,
@@ -992,7 +950,7 @@ class UnitPipeline(PipelineTemplate):
992
950
  self.input_roi_poly, self.input_roi_epsg, self.epsg
993
951
  )
994
952
 
995
- self.resolution = output[out_cst.RESOLUTION] * self.res_resamp
953
+ self.resolution = output[out_cst.RESOLUTION]
996
954
 
997
955
  # List of terrain roi corresponding to each epipolar pair
998
956
  # Used to generate final terrain roi
@@ -1027,7 +985,7 @@ class UnitPipeline(PipelineTemplate):
1027
985
  # used in dem generation
1028
986
  self.triangulated_matches_list = []
1029
987
 
1030
- save_matches = self.sparse_mtch_sift_app.get_save_matches()
988
+ save_matches = self.sparse_mtch_app.get_save_matches()
1031
989
 
1032
990
  save_corrected_grid = (
1033
991
  self.epipolar_grid_generation_application.get_save_grids()
@@ -1050,21 +1008,10 @@ class UnitPipeline(PipelineTemplate):
1050
1008
  # We generate grids with dem if it is provided.
1051
1009
  # If not provided, grid are generated without dem and a dem
1052
1010
  # will be generated, to use later for a new grid generation**
1053
- altitude_delta_min = inputs.get(sens_cst.INITIAL_ELEVATION, {}).get(
1054
- sens_cst.ALTITUDE_DELTA_MIN, None
1055
- )
1056
- altitude_delta_max = inputs.get(sens_cst.INITIAL_ELEVATION, {}).get(
1057
- sens_cst.ALTITUDE_DELTA_MAX, None
1058
- )
1059
1011
 
1060
1012
  if inputs[sens_cst.INITIAL_ELEVATION][sens_cst.DEM_PATH] is None:
1061
1013
  geom_plugin = self.geom_plugin_without_dem_and_geoid
1062
1014
 
1063
- if None not in (altitude_delta_min, altitude_delta_max):
1064
- raise RuntimeError(
1065
- "Dem path is mandatory for "
1066
- "the use of altitude deltas"
1067
- )
1068
1015
  else:
1069
1016
  geom_plugin = self.geom_plugin_with_dem_and_geoid
1070
1017
 
@@ -1089,43 +1036,15 @@ class UnitPipeline(PipelineTemplate):
1089
1036
  if self.quit_on_app("grid_generation"):
1090
1037
  continue # keep iterating over pairs, but don't go further
1091
1038
 
1092
- # Run holes detection
1093
- # Get classif depending on which filling is used
1094
- # For now, 2 filling application can be used, and be configured
1095
- # with any order. the .1 will be performed before the .2
1096
- self.pairs[pair_key]["holes_classif"] = []
1097
- self.pairs[pair_key]["holes_poly_margin"] = 0
1098
- add_classif = False
1099
- if self.dense_match_filling_1.used_method == "plane":
1100
- self.pairs[pair_key][
1101
- "holes_classif"
1102
- ] += self.dense_match_filling_1.get_classif()
1103
- self.pairs[pair_key]["holes_poly_margin"] = max(
1104
- self.pairs[pair_key]["holes_poly_margin"],
1105
- self.dense_match_filling_1.get_poly_margin(),
1106
- )
1107
- add_classif = True
1108
- if self.dense_match_filling_2.used_method == "plane":
1109
- self.pairs[pair_key][
1110
- "holes_classif"
1111
- ] += self.dense_match_filling_2.get_classif()
1112
- self.pairs[pair_key]["holes_poly_margin"] = max(
1113
- self.pairs[pair_key]["holes_poly_margin"],
1114
- self.dense_match_filling_2.get_poly_margin(),
1115
- )
1116
- add_classif = True
1117
-
1118
- self.pairs[pair_key]["holes_bbox_left"] = []
1119
- self.pairs[pair_key]["holes_bbox_right"] = []
1120
-
1121
- if self.used_conf[ADVANCED][
1122
- adv_cst.USE_EPIPOLAR_A_PRIORI
1123
- ] is False or (len(self.pairs[pair_key]["holes_classif"]) > 0):
1039
+ if self.used_conf[ADVANCED][adv_cst.TERRAIN_A_PRIORI] in (
1040
+ None,
1041
+ {},
1042
+ ):
1124
1043
  # Run resampling only if needed:
1125
- # no a priori or needs to detect holes
1044
+ # no a priori
1126
1045
 
1127
1046
  # Get required bands of first resampling
1128
- required_bands = self.sparse_mtch_sift_app.get_required_bands()
1047
+ required_bands = self.sparse_mtch_app.get_required_bands()
1129
1048
 
1130
1049
  # Run first epipolar resampling
1131
1050
  (
@@ -1142,49 +1061,30 @@ class UnitPipeline(PipelineTemplate):
1142
1061
  self.dump_dir, "resampling", "initial", pair_key
1143
1062
  ),
1144
1063
  pair_key=pair_key,
1145
- margins_fun=self.sparse_mtch_sift_app.get_margins_fun(),
1064
+ margins_fun=self.sparse_mtch_app.get_margins_fun(),
1146
1065
  tile_width=None,
1147
1066
  tile_height=None,
1148
- add_classif=add_classif,
1149
1067
  required_bands=required_bands,
1150
1068
  )
1151
1069
 
1152
1070
  if self.quit_on_app("resampling"):
1153
1071
  continue # keep iterating over pairs, but don't go further
1154
1072
 
1155
- # Generate the holes polygons in epipolar images
1156
- # They are only generated if dense_match_filling
1157
- # applications are used later
1158
- (
1159
- self.pairs[pair_key]["holes_bbox_left"],
1160
- self.pairs[pair_key]["holes_bbox_right"],
1161
- ) = self.hole_detection_app.run(
1162
- self.pairs[pair_key]["epipolar_image_left"],
1163
- self.pairs[pair_key]["epipolar_image_right"],
1164
- classification=self.pairs[pair_key]["holes_classif"],
1165
- margin=self.pairs[pair_key]["holes_poly_margin"],
1166
- orchestrator=self.cars_orchestrator,
1167
- pair_folder=os.path.join(
1168
- self.dump_dir, "hole_detection", pair_key
1169
- ),
1170
- pair_key=pair_key,
1171
- )
1172
-
1173
- if self.quit_on_app("hole_detection"):
1174
- continue # keep iterating over pairs, but don't go further
1175
-
1176
- if self.used_conf[ADVANCED][adv_cst.USE_EPIPOLAR_A_PRIORI] is False:
1073
+ if self.used_conf[ADVANCED][adv_cst.TERRAIN_A_PRIORI] in (
1074
+ None,
1075
+ {},
1076
+ ):
1177
1077
  # Run epipolar sparse_matching application
1178
1078
  (
1179
1079
  self.pairs[pair_key]["epipolar_matches_left"],
1180
1080
  _,
1181
- ) = self.sparse_mtch_sift_app.run(
1081
+ ) = self.sparse_mtch_app.run(
1182
1082
  self.pairs[pair_key]["epipolar_image_left"],
1183
1083
  self.pairs[pair_key]["epipolar_image_right"],
1184
1084
  self.pairs[pair_key]["grid_left"]["disp_to_alt_ratio"],
1185
1085
  orchestrator=self.cars_orchestrator,
1186
1086
  pair_folder=os.path.join(
1187
- self.dump_dir, "sparse_matching.sift", pair_key
1087
+ self.dump_dir, "sparse_matching", pair_key
1188
1088
  ),
1189
1089
  pair_key=pair_key,
1190
1090
  )
@@ -1192,16 +1092,14 @@ class UnitPipeline(PipelineTemplate):
1192
1092
  # Run cluster breakpoint to compute sifts: force computation
1193
1093
  self.cars_orchestrator.breakpoint()
1194
1094
 
1195
- minimum_nb_matches = (
1196
- self.sparse_mtch_sift_app.get_minimum_nb_matches()
1197
- )
1095
+ minimum_nb_matches = self.sparse_mtch_app.get_minimum_nb_matches()
1198
1096
 
1199
1097
  # Run grid correction application
1200
- if self.used_conf[ADVANCED][adv_cst.USE_EPIPOLAR_A_PRIORI] is False:
1098
+ if self.used_conf[ADVANCED][adv_cst.TERRAIN_A_PRIORI] in (None, {}):
1201
1099
  # Estimate grid correction if no epipolar a priori
1202
1100
  # Filter and save matches
1203
1101
  self.pairs[pair_key]["matches_array"] = (
1204
- self.sparse_mtch_sift_app.filter_matches(
1102
+ self.sparse_mtch_app.filter_matches(
1205
1103
  self.pairs[pair_key]["epipolar_matches_left"],
1206
1104
  self.pairs[pair_key]["grid_left"],
1207
1105
  self.pairs[pair_key]["grid_right"],
@@ -1209,11 +1107,9 @@ class UnitPipeline(PipelineTemplate):
1209
1107
  orchestrator=self.cars_orchestrator,
1210
1108
  pair_key=pair_key,
1211
1109
  pair_folder=os.path.join(
1212
- self.dump_dir, "sparse_matching.sift", pair_key
1213
- ),
1214
- save_matches=(
1215
- self.sparse_mtch_sift_app.get_save_matches()
1110
+ self.dump_dir, "sparse_matching", pair_key
1216
1111
  ),
1112
+ save_matches=(self.sparse_mtch_app.get_save_matches()),
1217
1113
  )
1218
1114
  )
1219
1115
 
@@ -1253,7 +1149,7 @@ class UnitPipeline(PipelineTemplate):
1253
1149
  pair_key
1254
1150
  ]["grid_left"]
1255
1151
 
1256
- if self.quit_on_app("sparse_matching.sift"):
1152
+ if self.quit_on_app("sparse_matching"):
1257
1153
  continue
1258
1154
 
1259
1155
  # Shrink disparity intervals according to SIFT disparities
@@ -1261,7 +1157,7 @@ class UnitPipeline(PipelineTemplate):
1261
1157
  "disp_to_alt_ratio"
1262
1158
  ]
1263
1159
  disp_bounds_params = (
1264
- self.sparse_mtch_sift_app.disparity_bounds_estimation
1160
+ self.sparse_mtch_app.disparity_bounds_estimation
1265
1161
  )
1266
1162
 
1267
1163
  if disp_bounds_params["activated"]:
@@ -1289,11 +1185,11 @@ class UnitPipeline(PipelineTemplate):
1289
1185
  )
1290
1186
  else:
1291
1187
  disp_min = (
1292
- -self.sparse_mtch_sift_app.elevation_delta_upper_bound
1188
+ -self.sparse_mtch_app.elevation_delta_upper_bound
1293
1189
  / disp_to_alt_ratio
1294
1190
  )
1295
1191
  disp_max = (
1296
- -self.sparse_mtch_sift_app.elevation_delta_lower_bound
1192
+ -self.sparse_mtch_app.elevation_delta_lower_bound
1297
1193
  / disp_to_alt_ratio
1298
1194
  )
1299
1195
  logging.info(
@@ -1336,12 +1232,11 @@ class UnitPipeline(PipelineTemplate):
1336
1232
  if (
1337
1233
  self.quit_on_app("grid_generation")
1338
1234
  or self.quit_on_app("resampling")
1339
- or self.quit_on_app("hole_detection")
1340
- or self.quit_on_app("sparse_matching.sift")
1235
+ or self.quit_on_app("sparse_matching")
1341
1236
  ):
1342
1237
  return True
1343
1238
 
1344
- if self.used_conf[ADVANCED][adv_cst.USE_EPIPOLAR_A_PRIORI]:
1239
+ if not self.used_conf[ADVANCED][adv_cst.TERRAIN_A_PRIORI] in (None, {}):
1345
1240
  # Use a priori
1346
1241
  dem_median = self.used_conf[ADVANCED][adv_cst.TERRAIN_A_PRIORI][
1347
1242
  adv_cst.DEM_MEDIAN
@@ -1352,50 +1247,10 @@ class UnitPipeline(PipelineTemplate):
1352
1247
  dem_max = self.used_conf[ADVANCED][adv_cst.TERRAIN_A_PRIORI][
1353
1248
  adv_cst.DEM_MAX
1354
1249
  ]
1355
- altitude_delta_min = self.used_conf[ADVANCED][
1356
- adv_cst.TERRAIN_A_PRIORI
1357
- ][adv_cst.ALTITUDE_DELTA_MIN]
1358
- altitude_delta_max = self.used_conf[ADVANCED][
1359
- adv_cst.TERRAIN_A_PRIORI
1360
- ][adv_cst.ALTITUDE_DELTA_MAX]
1361
-
1362
- # update used configuration with terrain a priori
1363
- if None not in (altitude_delta_min, altitude_delta_max):
1364
- advanced_parameters.update_conf(
1365
- self.used_conf,
1366
- dem_median=dem_median,
1367
- altitude_delta_min=altitude_delta_min,
1368
- altitude_delta_max=altitude_delta_max,
1369
- )
1370
- else:
1371
- advanced_parameters.update_conf(
1372
- self.used_conf,
1373
- dem_median=dem_median,
1374
- dem_min=dem_min,
1375
- dem_max=dem_max,
1376
- )
1377
-
1378
- advanced_parameters.update_conf(
1379
- self.config_full_res,
1380
- dem_median=dem_median,
1381
- dem_min=dem_min,
1382
- dem_max=dem_max,
1383
- )
1384
-
1385
- # quit only after the configuration was updated
1386
- if self.quit_on_app("dem_generation"):
1387
- return True
1388
1250
 
1389
1251
  # Define param
1390
1252
  use_global_disp_range = self.dense_matching_app.use_global_disp_range
1391
1253
 
1392
- if self.pc_denoising_application is not None:
1393
- denoising_overload_fun = (
1394
- self.pc_denoising_application.get_triangulation_overload()
1395
- )
1396
- else:
1397
- denoising_overload_fun = None
1398
-
1399
1254
  self.pairs_names = [
1400
1255
  pair_name for pair_name, _, _ in self.list_sensor_pairs
1401
1256
  ]
@@ -1404,7 +1259,7 @@ class UnitPipeline(PipelineTemplate):
1404
1259
  # Geometry plugin with dem will be used for the grid generation
1405
1260
  geom_plugin = self.geom_plugin_with_dem_and_geoid
1406
1261
 
1407
- if self.used_conf[ADVANCED][adv_cst.USE_EPIPOLAR_A_PRIORI] is False:
1262
+ if self.used_conf[ADVANCED][adv_cst.TERRAIN_A_PRIORI] in (None, {}):
1408
1263
  save_matches = True
1409
1264
 
1410
1265
  (
@@ -1420,7 +1275,8 @@ class UnitPipeline(PipelineTemplate):
1420
1275
  save_matches=save_matches,
1421
1276
  )
1422
1277
  elif (
1423
- self.used_conf[ADVANCED][adv_cst.USE_EPIPOLAR_A_PRIORI] is True
1278
+ not self.used_conf[ADVANCED][adv_cst.TERRAIN_A_PRIORI]
1279
+ in (None, {})
1424
1280
  and not self.use_sift_a_priori
1425
1281
  ):
1426
1282
  # Use epipolar a priori
@@ -1431,7 +1287,7 @@ class UnitPipeline(PipelineTemplate):
1431
1287
  ][pair_key][adv_cst.DISPARITY_RANGE]
1432
1288
 
1433
1289
  advanced_parameters.update_conf(
1434
- self.config_full_res,
1290
+ self.refined_conf,
1435
1291
  dmin=dmin,
1436
1292
  dmax=dmax,
1437
1293
  pair_key=pair_key,
@@ -1470,7 +1326,7 @@ class UnitPipeline(PipelineTemplate):
1470
1326
  # Correct grids with former matches
1471
1327
  # Transform matches to new grids
1472
1328
 
1473
- save_matches = self.sparse_mtch_sift_app.get_save_matches()
1329
+ save_matches = self.sparse_mtch_app.get_save_matches()
1474
1330
 
1475
1331
  self.sensor_matches_left = os.path.join(
1476
1332
  self.first_res_out_dir,
@@ -1552,28 +1408,19 @@ class UnitPipeline(PipelineTemplate):
1552
1408
  right=True,
1553
1409
  )
1554
1410
 
1555
- # Update used_conf configuration with epipolar a priori
1556
- # Add global min and max computed with grids
1557
- advanced_parameters.update_conf(
1558
- self.used_conf,
1559
- grid_correction_coef=self.pairs[pair_key][
1560
- "grid_correction_coef"
1561
- ],
1562
- pair_key=pair_key,
1563
- )
1411
+ # Update refined_conf configuration with epipolar a priori
1564
1412
  advanced_parameters.update_conf(
1565
- self.config_full_res,
1413
+ self.refined_conf,
1566
1414
  grid_correction_coef=self.pairs[pair_key][
1567
1415
  "grid_correction_coef"
1568
1416
  ],
1569
1417
  pair_key=pair_key,
1418
+ reference_dem=self.used_conf[INPUTS][
1419
+ sens_cst.INITIAL_ELEVATION
1420
+ ][sens_cst.DEM_PATH],
1570
1421
  )
1571
1422
  # saved used configuration
1572
- cars_dataset.save_dict(
1573
- self.used_conf,
1574
- os.path.join(self.out_dir, "used_conf.json"),
1575
- safe_save=True,
1576
- )
1423
+ self.save_configurations()
1577
1424
 
1578
1425
  # Generate min and max disp grids
1579
1426
  # Global disparity min and max will be computed from
@@ -1582,11 +1429,9 @@ class UnitPipeline(PipelineTemplate):
1582
1429
  self.dump_dir, "dense_matching", pair_key
1583
1430
  )
1584
1431
 
1585
- if (
1586
- self.which_resolution in ("first", "single")
1587
- and self.used_conf[ADVANCED][adv_cst.USE_EPIPOLAR_A_PRIORI]
1588
- is False
1589
- ):
1432
+ if self.which_resolution in ("first", "single") and self.used_conf[
1433
+ ADVANCED
1434
+ ][adv_cst.TERRAIN_A_PRIORI] in (None, {}):
1590
1435
  dmin = disp_min / self.res_resamp
1591
1436
  dmax = disp_max / self.res_resamp
1592
1437
  # generate_disparity_grids runs orchestrator.breakpoint()
@@ -1602,7 +1447,7 @@ class UnitPipeline(PipelineTemplate):
1602
1447
  )
1603
1448
  )
1604
1449
 
1605
- dsp_marg = self.sparse_mtch_sift_app.get_disparity_margin()
1450
+ dsp_marg = self.sparse_mtch_app.get_disparity_margin()
1606
1451
  updating_infos = {
1607
1452
  application_constants.APPLICATION_TAG: {
1608
1453
  sm_cst.DISPARITY_RANGE_COMPUTATION_TAG: {
@@ -1617,42 +1462,26 @@ class UnitPipeline(PipelineTemplate):
1617
1462
  self.cars_orchestrator.update_out_info(updating_infos)
1618
1463
 
1619
1464
  advanced_parameters.update_conf(
1620
- self.config_full_res,
1465
+ self.refined_conf,
1621
1466
  dmin=dmin,
1622
1467
  dmax=dmax,
1623
1468
  pair_key=pair_key,
1624
1469
  )
1625
1470
  else:
1626
- if None in (altitude_delta_min, altitude_delta_max):
1627
- # Generate min and max disp grids from dems
1628
- # generate_disparity_grids runs orchestrator.breakpoint()
1629
- self.pairs[pair_key]["disp_range_grid"] = (
1630
- self.dense_matching_app.generate_disparity_grids(
1631
- self.pairs[pair_key]["sensor_image_right"],
1632
- self.pairs[pair_key]["corrected_grid_right"],
1633
- self.geom_plugin_with_dem_and_geoid,
1634
- dem_min=dem_min,
1635
- dem_max=dem_max,
1636
- dem_median=dem_median,
1637
- pair_folder=dense_matching_pair_folder,
1638
- orchestrator=self.cars_orchestrator,
1639
- )
1640
- )
1641
- else:
1642
- # Generate min and max disp grids from deltas
1643
- # generate_disparity_grids runs orchestrator.breakpoint()
1644
- self.pairs[pair_key]["disp_range_grid"] = (
1645
- self.dense_matching_app.generate_disparity_grids(
1646
- self.pairs[pair_key]["sensor_image_right"],
1647
- self.pairs[pair_key]["corrected_grid_right"],
1648
- self.geom_plugin_with_dem_and_geoid,
1649
- altitude_delta_min=altitude_delta_min,
1650
- altitude_delta_max=altitude_delta_max,
1651
- dem_median=dem_median,
1652
- pair_folder=dense_matching_pair_folder,
1653
- orchestrator=self.cars_orchestrator,
1654
- )
1471
+ # Generate min and max disp grids from dems
1472
+ # generate_disparity_grids runs orchestrator.breakpoint()
1473
+ self.pairs[pair_key]["disp_range_grid"] = (
1474
+ self.dense_matching_app.generate_disparity_grids(
1475
+ self.pairs[pair_key]["sensor_image_right"],
1476
+ self.pairs[pair_key]["corrected_grid_right"],
1477
+ self.geom_plugin_with_dem_and_geoid,
1478
+ dem_min=dem_min,
1479
+ dem_max=dem_max,
1480
+ dem_median=dem_median,
1481
+ pair_folder=dense_matching_pair_folder,
1482
+ orchestrator=self.cars_orchestrator,
1655
1483
  )
1484
+ )
1656
1485
 
1657
1486
  if use_global_disp_range:
1658
1487
  # Generate min and max disp grids from constants
@@ -1668,7 +1497,7 @@ class UnitPipeline(PipelineTemplate):
1668
1497
  ]
1669
1498
 
1670
1499
  # update orchestrator_out_json
1671
- marg = self.sparse_mtch_sift_app.get_disparity_margin()
1500
+ marg = self.sparse_mtch_app.get_disparity_margin()
1672
1501
  updating_infos = {
1673
1502
  application_constants.APPLICATION_TAG: {
1674
1503
  sm_cst.DISPARITY_RANGE_COMPUTATION_TAG: {
@@ -1683,7 +1512,7 @@ class UnitPipeline(PipelineTemplate):
1683
1512
  self.cars_orchestrator.update_out_info(updating_infos)
1684
1513
 
1685
1514
  advanced_parameters.update_conf(
1686
- self.config_full_res,
1515
+ self.refined_conf,
1687
1516
  dmin=dmin,
1688
1517
  dmax=dmax,
1689
1518
  pair_key=pair_key,
@@ -1706,24 +1535,20 @@ class UnitPipeline(PipelineTemplate):
1706
1535
  # Update used_conf configuration with epipolar a priori
1707
1536
  # Add global min and max computed with grids
1708
1537
  advanced_parameters.update_conf(
1709
- self.used_conf,
1538
+ self.refined_conf,
1710
1539
  dmin=self.pairs[pair_key]["disp_range_grid"]["global_min"],
1711
1540
  dmax=self.pairs[pair_key]["disp_range_grid"]["global_max"],
1712
1541
  pair_key=pair_key,
1713
1542
  )
1714
1543
  advanced_parameters.update_conf(
1715
- self.config_full_res,
1544
+ self.refined_conf,
1716
1545
  dmin=self.pairs[pair_key]["disp_range_grid"]["global_min"],
1717
1546
  dmax=self.pairs[pair_key]["disp_range_grid"]["global_max"],
1718
1547
  pair_key=pair_key,
1719
1548
  )
1720
1549
 
1721
1550
  # saved used configuration
1722
- cars_dataset.save_dict(
1723
- self.used_conf,
1724
- os.path.join(self.out_dir, "used_conf.json"),
1725
- safe_save=True,
1726
- )
1551
+ self.save_configurations()
1727
1552
 
1728
1553
  # end of for loop, to finish computing disparity range grids
1729
1554
 
@@ -1780,6 +1605,10 @@ class UnitPipeline(PipelineTemplate):
1780
1605
  )
1781
1606
  )
1782
1607
 
1608
+ # Quick fix to reduce memory usage
1609
+ if self.res_resamp >= 16:
1610
+ optimum_tile_size = 200
1611
+
1783
1612
  # Run third epipolar resampling
1784
1613
  (
1785
1614
  new_epipolar_image_left,
@@ -1913,28 +1742,11 @@ class UnitPipeline(PipelineTemplate):
1913
1742
  )
1914
1743
 
1915
1744
  if self.which_resolution not in ("final", "single"):
1916
- # To get the correct size for the dem generation
1917
- if self.dem_generation_roi is not None:
1918
- # ROI has been computed by Shareloc
1919
- self.terrain_bounds = (
1920
- dem_wrappers.modify_terrain_bounds(
1921
- self.dem_generation_roi.bounds,
1922
- 4326,
1923
- self.epsg,
1924
- self.dem_generation_application.margin,
1925
- )
1926
- )
1927
- else:
1928
- # ROI has not been computed
1929
- self.terrain_bounds = (
1930
- dem_wrappers.modify_terrain_bounds(
1931
- self.terrain_bounds,
1932
- self.epsg,
1933
- self.epsg,
1934
- self.dem_generation_application.margin,
1935
- 0.7,
1936
- )
1937
- )
1745
+ self.terrain_bounds = dem_wrappers.modify_terrain_bounds(
1746
+ self.terrain_bounds,
1747
+ self.dem_generation_application.margin[0],
1748
+ self.dem_generation_application.margin[1],
1749
+ )
1938
1750
 
1939
1751
  if self.dense_matching_app.get_method() == "auto":
1940
1752
  # Copy the initial corr_config in order to keep
@@ -1953,9 +1765,12 @@ class UnitPipeline(PipelineTemplate):
1953
1765
  # the dense matching app
1954
1766
  # Because we kept the information regarding the ambiguity,
1955
1767
  # performance_map calculus..
1956
- self.used_conf["applications"]["dense_matching"]["loader_conf"][
1957
- "pipeline"
1958
- ] = conf["pipeline"]
1768
+ self.used_conf["applications"]["dense_matching"][
1769
+ "loader_conf"
1770
+ ] = conf
1771
+ self.used_conf["applications"]["dense_matching"][
1772
+ "method"
1773
+ ] = "custom"
1959
1774
 
1960
1775
  # Re initialization of the dense matching application
1961
1776
  self.dense_matching_app = Application(
@@ -1979,9 +1794,9 @@ class UnitPipeline(PipelineTemplate):
1979
1794
  pair_key=pair_key,
1980
1795
  disp_range_grid=self.pairs[pair_key]["disp_range_grid"],
1981
1796
  compute_disparity_masks=False,
1982
- margins_to_keep=(
1983
- self.pc_outlier_removal_1_app.get_epipolar_margin()
1984
- + self.pc_outlier_removal_2_app.get_epipolar_margin()
1797
+ margins_to_keep=sum(
1798
+ app.get_epipolar_margin()
1799
+ for _, app in self.pc_outlier_removal_apps.items()
1985
1800
  ),
1986
1801
  texture_bands=texture_bands_indices,
1987
1802
  )
@@ -1989,81 +1804,17 @@ class UnitPipeline(PipelineTemplate):
1989
1804
  if self.quit_on_app("dense_matching"):
1990
1805
  continue # keep iterating over pairs, but don't go further
1991
1806
 
1992
- # Dense matches filling
1993
- if self.dense_match_filling_1.used_method == "plane":
1994
- # Fill holes in disparity map
1995
- (filled_with_1_epipolar_disparity_map) = (
1996
- self.dense_match_filling_1.run(
1997
- epipolar_disparity_map,
1998
- self.pairs[pair_key]["holes_bbox_left"],
1999
- self.pairs[pair_key]["holes_bbox_right"],
2000
- disp_min=self.pairs[pair_key]["disp_range_grid"][
2001
- "global_min"
2002
- ],
2003
- disp_max=np.max(
2004
- self.pairs[pair_key]["disp_range_grid"][
2005
- "global_max"
2006
- ]
2007
- ),
2008
- orchestrator=self.cars_orchestrator,
2009
- pair_folder=os.path.join(
2010
- self.dump_dir, "dense_match_filling_1", pair_key
2011
- ),
2012
- pair_key=pair_key,
2013
- )
2014
- )
2015
- else:
2016
- # Fill with zeros
2017
- (filled_with_1_epipolar_disparity_map) = (
2018
- self.dense_match_filling_1.run(
2019
- epipolar_disparity_map,
2020
- orchestrator=self.cars_orchestrator,
2021
- pair_folder=os.path.join(
2022
- self.dump_dir, "dense_match_filling_1", pair_key
2023
- ),
2024
- pair_key=pair_key,
2025
- )
2026
- )
2027
-
2028
- if self.quit_on_app("dense_match_filling.1"):
2029
- continue # keep iterating over pairs, but don't go further
2030
-
2031
- if self.dense_match_filling_2.used_method == "plane":
2032
- # Fill holes in disparity map
2033
- (filled_with_2_epipolar_disparity_map) = (
2034
- self.dense_match_filling_2.run(
2035
- filled_with_1_epipolar_disparity_map,
2036
- self.pairs[pair_key]["holes_bbox_left"],
2037
- self.pairs[pair_key]["holes_bbox_right"],
2038
- disp_min=self.pairs[pair_key]["disp_range_grid"][
2039
- "global_min"
2040
- ],
2041
- disp_max=np.max(
2042
- self.pairs[pair_key]["disp_range_grid"][
2043
- "global_max"
2044
- ]
2045
- ),
2046
- orchestrator=self.cars_orchestrator,
2047
- pair_folder=os.path.join(
2048
- self.dump_dir, "dense_match_filling_2", pair_key
2049
- ),
2050
- pair_key=pair_key,
2051
- )
2052
- )
2053
- else:
2054
- # Fill with zeros
2055
- (filled_with_2_epipolar_disparity_map) = (
2056
- self.dense_match_filling_2.run(
2057
- filled_with_1_epipolar_disparity_map,
2058
- orchestrator=self.cars_orchestrator,
2059
- pair_folder=os.path.join(
2060
- self.dump_dir, "dense_match_filling_2", pair_key
2061
- ),
2062
- pair_key=pair_key,
2063
- )
2064
- )
1807
+ # Fill with zeros
1808
+ (filled_epipolar_disparity_map) = self.dense_match_filling.run(
1809
+ epipolar_disparity_map,
1810
+ orchestrator=self.cars_orchestrator,
1811
+ pair_folder=os.path.join(
1812
+ self.dump_dir, "dense_match_filling", pair_key
1813
+ ),
1814
+ pair_key=pair_key,
1815
+ )
2065
1816
 
2066
- if self.quit_on_app("dense_match_filling.2"):
1817
+ if self.quit_on_app("dense_match_filling"):
2067
1818
  continue # keep iterating over pairs, but don't go further
2068
1819
 
2069
1820
  if isinstance(output[sens_cst.GEOID], str):
@@ -2085,7 +1836,6 @@ class UnitPipeline(PipelineTemplate):
2085
1836
  output_geoid_path = None
2086
1837
 
2087
1838
  depth_map_dir = None
2088
- last_depth_map_application = None
2089
1839
  if self.save_output_depth_map:
2090
1840
  depth_map_dir = os.path.join(
2091
1841
  self.out_dir, "depth_map", pair_key
@@ -2099,33 +1849,9 @@ class UnitPipeline(PipelineTemplate):
2099
1849
  )
2100
1850
  safe_makedirs(point_cloud_dir)
2101
1851
 
2102
- if self.save_output_depth_map or self.save_output_point_cloud:
2103
- if (
2104
- self.pc_outlier_removal_2_app.used_config.get(
2105
- "activated", False
2106
- )
2107
- is True
2108
- and self.merging is False
2109
- ):
2110
- last_depth_map_application = "pc_outlier_removal_2"
2111
- elif (
2112
- self.pc_outlier_removal_1_app.used_config.get(
2113
- "activated", False
2114
- )
2115
- is True
2116
- and self.merging is False
2117
- ):
2118
- last_depth_map_application = "pc_outlier_removal_1"
2119
- else:
2120
- last_depth_map_application = "triangulation"
2121
-
2122
1852
  triangulation_point_cloud_dir = (
2123
1853
  point_cloud_dir
2124
- if (
2125
- point_cloud_dir
2126
- and last_depth_map_application == "triangulation"
2127
- and self.merging is False
2128
- )
1854
+ if (point_cloud_dir and len(self.pc_outlier_removal_apps) == 0)
2129
1855
  else None
2130
1856
  )
2131
1857
 
@@ -2135,11 +1861,11 @@ class UnitPipeline(PipelineTemplate):
2135
1861
  self.pairs[pair_key]["sensor_image_right"],
2136
1862
  self.pairs[pair_key]["corrected_grid_left"],
2137
1863
  self.pairs[pair_key]["corrected_grid_right"],
2138
- filled_with_2_epipolar_disparity_map,
1864
+ filled_epipolar_disparity_map,
2139
1865
  self.geom_plugin_without_dem_and_geoid,
2140
1866
  new_epipolar_image_left,
2141
1867
  epsg=self.epsg,
2142
- denoising_overload_fun=denoising_overload_fun,
1868
+ denoising_overload_fun=None,
2143
1869
  source_pc_names=self.pairs_names,
2144
1870
  orchestrator=self.cars_orchestrator,
2145
1871
  pair_dump_dir=os.path.join(
@@ -2154,8 +1880,10 @@ class UnitPipeline(PipelineTemplate):
2154
1880
  ),
2155
1881
  depth_map_dir=depth_map_dir,
2156
1882
  point_cloud_dir=triangulation_point_cloud_dir,
2157
- save_output_coordinates=last_depth_map_application
2158
- == "triangulation",
1883
+ save_output_coordinates=(len(self.pc_outlier_removal_apps) == 0)
1884
+ and (
1885
+ self.save_output_depth_map or self.save_output_point_cloud
1886
+ ),
2159
1887
  save_output_color=bool(depth_map_dir)
2160
1888
  and self.auxiliary[out_cst.AUX_TEXTURE],
2161
1889
  save_output_classification=bool(depth_map_dir)
@@ -2173,104 +1901,49 @@ class UnitPipeline(PipelineTemplate):
2173
1901
  if self.quit_on_app("triangulation"):
2174
1902
  continue # keep iterating over pairs, but don't go further
2175
1903
 
2176
- if self.merging:
2177
- self.list_epipolar_point_clouds.append(epipolar_point_cloud)
2178
- else:
2179
- filtering_depth_map_dir = (
2180
- depth_map_dir
2181
- if (
2182
- depth_map_dir
2183
- and last_depth_map_application == "pc_outlier_removal_1"
2184
- )
2185
- else None
2186
- )
2187
- filtering_point_cloud_dir = (
2188
- point_cloud_dir
2189
- if (
2190
- point_cloud_dir
2191
- and last_depth_map_application == "pc_outlier_removal_1"
2192
- and self.merging is False
2193
- )
2194
- else None
2195
- )
1904
+ filtered_epipolar_point_cloud = epipolar_point_cloud
1905
+ for app_key, app in self.pc_outlier_removal_apps.items():
2196
1906
 
2197
- filtered_epipolar_point_cloud_1 = (
2198
- self.pc_outlier_removal_1_app.run(
2199
- epipolar_point_cloud,
2200
- depth_map_dir=filtering_depth_map_dir,
2201
- point_cloud_dir=filtering_point_cloud_dir,
2202
- dump_dir=os.path.join(
2203
- self.dump_dir, "pc_outlier_removal_1", pair_key
2204
- ),
2205
- epsg=self.epsg,
2206
- orchestrator=self.cars_orchestrator,
2207
- )
1907
+ app_key_is_last = (
1908
+ app_key == list(self.pc_outlier_removal_apps)[-1]
2208
1909
  )
2209
- if self.quit_on_app("point_cloud_outlier_removal.1"):
2210
- continue # keep iterating over pairs, but don't go further
2211
1910
  filtering_depth_map_dir = (
2212
- depth_map_dir
2213
- if (
2214
- depth_map_dir
2215
- and last_depth_map_application == "pc_outlier_removal_2"
2216
- )
2217
- else None
1911
+ depth_map_dir if app_key_is_last else None
2218
1912
  )
2219
1913
  filtering_point_cloud_dir = (
2220
- point_cloud_dir
2221
- if (
2222
- point_cloud_dir
2223
- and last_depth_map_application == "pc_outlier_removal_2"
2224
- and self.merging is False
2225
- )
2226
- else None
2227
- )
2228
- filtered_epipolar_point_cloud_2 = (
2229
- self.pc_outlier_removal_2_app.run(
2230
- filtered_epipolar_point_cloud_1,
2231
- depth_map_dir=filtering_depth_map_dir,
2232
- point_cloud_dir=filtering_point_cloud_dir,
2233
- dump_dir=os.path.join(
2234
- self.dump_dir, "pc_outlier_removal_2", pair_key
1914
+ point_cloud_dir if app_key_is_last else None
1915
+ )
1916
+
1917
+ filtered_epipolar_point_cloud = app.run(
1918
+ filtered_epipolar_point_cloud,
1919
+ depth_map_dir=filtering_depth_map_dir,
1920
+ point_cloud_dir=filtering_point_cloud_dir,
1921
+ dump_dir=os.path.join(
1922
+ self.dump_dir,
1923
+ ( # pylint: disable=inconsistent-quotes
1924
+ f"pc_outlier_removal"
1925
+ f"{str(app_key[27:]).replace('.', '_')}"
2235
1926
  ),
2236
- epsg=self.epsg,
2237
- orchestrator=self.cars_orchestrator,
2238
- )
1927
+ pair_key,
1928
+ ),
1929
+ epsg=self.epsg,
1930
+ orchestrator=self.cars_orchestrator,
2239
1931
  )
2240
- if self.quit_on_app("point_cloud_outlier_removal.2"):
2241
- continue # keep iterating over pairs, but don't go further
2242
-
2243
- # denoising available only if we'll go further in the pipeline
2244
- if self.save_output_dsm or self.save_output_point_cloud:
2245
- denoised_epipolar_point_clouds = (
2246
- self.pc_denoising_application.run(
2247
- filtered_epipolar_point_cloud_2,
2248
- orchestrator=self.cars_orchestrator,
2249
- pair_folder=os.path.join(
2250
- self.dump_dir, "denoising", pair_key
2251
- ),
2252
- pair_key=pair_key,
2253
- )
2254
- )
2255
-
2256
- self.list_epipolar_point_clouds.append(
2257
- denoised_epipolar_point_clouds
2258
- )
1932
+ if self.quit_on_app("point_cloud_outlier_removal"):
1933
+ continue # keep iterating over pairs, but don't go further
2259
1934
 
2260
- if self.quit_on_app("pc_denoising"):
2261
- # keep iterating over pairs, but don't go further
2262
- continue
1935
+ self.list_epipolar_point_clouds.append(
1936
+ filtered_epipolar_point_cloud
1937
+ )
2263
1938
 
2264
1939
  # quit if any app in the loop over the pairs was the last one
2265
1940
  # pylint:disable=too-many-boolean-expressions
2266
1941
  if (
2267
1942
  self.quit_on_app("dense_matching")
2268
- or self.quit_on_app("dense_match_filling.1")
2269
- or self.quit_on_app("dense_match_filling.2")
1943
+ or self.quit_on_app("dense_match_filling")
2270
1944
  or self.quit_on_app("triangulation")
2271
1945
  or self.quit_on_app("point_cloud_outlier_removal.1")
2272
1946
  or self.quit_on_app("point_cloud_outlier_removal.2")
2273
- or self.quit_on_app("pc_denoising")
2274
1947
  ):
2275
1948
  return True
2276
1949
 
@@ -2512,17 +2185,56 @@ class UnitPipeline(PipelineTemplate):
2512
2185
  cars_orchestrator=self.cars_orchestrator,
2513
2186
  )
2514
2187
 
2188
+ # Update refined conf configuration with dem paths
2515
2189
  dem_median = paths["dem_median"]
2516
2190
  dem_min = paths["dem_min"]
2517
2191
  dem_max = paths["dem_max"]
2518
2192
 
2519
2193
  advanced_parameters.update_conf(
2520
- self.used_conf,
2194
+ self.refined_conf,
2521
2195
  dem_median=dem_median,
2522
2196
  dem_min=dem_min,
2523
2197
  dem_max=dem_max,
2524
2198
  )
2525
2199
 
2200
+ if self.used_conf[ADVANCED][USE_ENDOGENOUS_DEM]:
2201
+ # Generate new geom plugin with dem
2202
+ output_dem_dir = os.path.join(
2203
+ self.dump_dir, "initial_elevation"
2204
+ )
2205
+ new_geom_plugin = (
2206
+ sensor_inputs.generate_geometry_plugin_with_dem(
2207
+ self.geometry_plugin,
2208
+ self.used_conf[INPUTS],
2209
+ dem=dem_median,
2210
+ output_dem_dir=output_dem_dir,
2211
+ )
2212
+ )
2213
+
2214
+ for (
2215
+ pair_key,
2216
+ _,
2217
+ _,
2218
+ ) in self.list_sensor_pairs:
2219
+ new_grid_correction_coef = (
2220
+ self.generate_grid_correction_on_dem(
2221
+ pair_key,
2222
+ new_geom_plugin,
2223
+ )
2224
+ )
2225
+ if new_grid_correction_coef is not None:
2226
+ # Update refined_conf configuration with epipolar
2227
+ # a priori
2228
+ advanced_parameters.update_conf(
2229
+ self.refined_conf,
2230
+ grid_correction_coef=new_grid_correction_coef,
2231
+ pair_key=pair_key,
2232
+ reference_dem=dem_median,
2233
+ )
2234
+
2235
+ # saved used configuration
2236
+ self.save_configurations()
2237
+
2526
2238
  return False
2527
2239
 
2528
2240
  def filling(self): # noqa: C901 : too complex
@@ -2530,10 +2242,6 @@ class UnitPipeline(PipelineTemplate):
2530
2242
  Fill the dsm
2531
2243
  """
2532
2244
 
2533
- dsm_filling_1_dump_dir = os.path.join(self.dump_dir, "dsm_filling_1")
2534
- dsm_filling_2_dump_dir = os.path.join(self.dump_dir, "dsm_filling_2")
2535
- dsm_filling_3_dump_dir = os.path.join(self.dump_dir, "dsm_filling_3")
2536
-
2537
2245
  dsm_file_name = (
2538
2246
  os.path.join(
2539
2247
  self.out_dir,
@@ -2778,38 +2486,47 @@ class UnitPipeline(PipelineTemplate):
2778
2486
  else:
2779
2487
  self.list_intersection_poly = None
2780
2488
 
2781
- _ = self.dsm_filling_1_application.run(
2782
- dsm_file=dsm_file_name,
2783
- classif_file=classif_file_name,
2784
- filling_file=filling_file_name,
2785
- dump_dir=dsm_filling_1_dump_dir,
2786
- roi_polys=self.list_intersection_poly,
2787
- roi_epsg=self.epsg,
2788
- output_geoid=self.used_conf[OUTPUT][sens_cst.GEOID],
2789
- geom_plugin=self.geom_plugin_with_dem_and_geoid,
2790
- )
2791
-
2792
- if not self.dsm_filling_1_application.save_intermediate_data:
2793
- self.cars_orchestrator.add_to_clean(dsm_filling_1_dump_dir)
2794
-
2795
- if self.quit_on_app("dsm_filling.1"):
2796
- return True
2797
-
2798
- dtm_file_name = self.dsm_filling_2_application.run(
2799
- dsm_file=dsm_file_name,
2800
- classif_file=classif_file_name,
2801
- filling_file=filling_file_name,
2802
- dump_dir=dsm_filling_2_dump_dir,
2803
- roi_polys=self.list_intersection_poly,
2804
- roi_epsg=self.epsg,
2805
- orchestrator=self.cars_orchestrator,
2806
- )
2807
-
2808
- if not self.dsm_filling_2_application.save_intermediate_data:
2809
- self.cars_orchestrator.add_to_clean(dsm_filling_2_dump_dir)
2489
+ dtm_file_name = None
2490
+ for app_key, app in self.dsm_filling_apps.items():
2491
+
2492
+ app_dump_dir = os.path.join(
2493
+ self.dump_dir, app_key.replace(".", "_")
2494
+ )
2495
+
2496
+ if app.get_conf()["method"] == "exogenous_filling":
2497
+ _ = app.run(
2498
+ dsm_file=dsm_file_name,
2499
+ classif_file=classif_file_name,
2500
+ filling_file=filling_file_name,
2501
+ dump_dir=app_dump_dir,
2502
+ roi_polys=self.list_intersection_poly,
2503
+ roi_epsg=self.epsg,
2504
+ output_geoid=self.used_conf[OUTPUT][sens_cst.GEOID],
2505
+ geom_plugin=self.geom_plugin_with_dem_and_geoid,
2506
+ )
2507
+ elif app.get_conf()["method"] == "bulldozer":
2508
+ dtm_file_name = app.run(
2509
+ dsm_file=dsm_file_name,
2510
+ classif_file=classif_file_name,
2511
+ filling_file=filling_file_name,
2512
+ dump_dir=app_dump_dir,
2513
+ roi_polys=self.list_intersection_poly,
2514
+ roi_epsg=self.epsg,
2515
+ orchestrator=self.cars_orchestrator,
2516
+ )
2517
+ elif app.get_conf()["method"] == "border_interpolation":
2518
+ _ = app.run(
2519
+ dsm_file=dsm_file_name,
2520
+ classif_file=classif_file_name,
2521
+ filling_file=filling_file_name,
2522
+ dtm_file=dtm_file_name,
2523
+ dump_dir=app_dump_dir,
2524
+ roi_polys=self.list_intersection_poly,
2525
+ roi_epsg=self.epsg,
2526
+ )
2810
2527
 
2811
- if self.quit_on_app("dsm_filling.2"):
2812
- return True
2528
+ if not app.save_intermediate_data:
2529
+ self.cars_orchestrator.add_to_clean(app_dump_dir)
2813
2530
 
2814
2531
  _ = self.auxiliary_filling_application.run(
2815
2532
  dsm_file=dsm_file_name,
@@ -2820,203 +2537,31 @@ class UnitPipeline(PipelineTemplate):
2820
2537
  pairing=self.used_conf[INPUTS].get("pairing"),
2821
2538
  geom_plugin=self.geom_plugin_with_dem_and_geoid,
2822
2539
  texture_bands=self.texture_bands,
2540
+ output_geoid=self.used_conf[OUTPUT][sens_cst.GEOID],
2823
2541
  orchestrator=self.cars_orchestrator,
2824
2542
  )
2825
-
2826
- if self.quit_on_app("auxiliary_filling"):
2827
- return True
2828
-
2829
2543
  self.cars_orchestrator.breakpoint()
2830
2544
 
2831
- _ = self.dsm_filling_3_application.run(
2832
- dsm_file=dsm_file_name,
2833
- classif_file=classif_file_name,
2834
- filling_file=filling_file_name,
2835
- dtm_file=dtm_file_name,
2836
- dump_dir=dsm_filling_3_dump_dir,
2837
- roi_polys=self.list_intersection_poly,
2838
- roi_epsg=self.epsg,
2839
- )
2840
-
2841
- if not self.dsm_filling_3_application.save_intermediate_data:
2842
- self.cars_orchestrator.add_to_clean(dsm_filling_3_dump_dir)
2843
-
2844
- return self.quit_on_app("dsm_filling.3")
2545
+ return self.quit_on_app("auxiliary_filling")
2845
2546
 
2547
+ @cars_profile(name="Preprocess depth maps", interval=0.5)
2846
2548
  def preprocess_depth_maps(self):
2847
2549
  """
2848
2550
  Adds multiple processing steps to the depth maps :
2849
- Merging, denoising.
2551
+ Merging.
2850
2552
  Creates the point cloud that will be rasterized in
2851
2553
  the last step of the pipeline.
2852
2554
  """
2853
2555
 
2854
- if not self.merging:
2855
- self.point_cloud_to_rasterize = (
2856
- self.list_epipolar_point_clouds,
2857
- self.terrain_bounds,
2858
- )
2859
- self.color_type = self.point_cloud_to_rasterize[0][
2860
- 0
2861
- ].attributes.get("color_type", None)
2862
- else:
2863
- # find which application produce the final version of the
2864
- # point cloud. The last generated point cloud will be saved
2865
- # as official point cloud product if save_output_point_cloud
2866
- # is True.
2867
-
2868
- last_pc_application = None
2869
- # denoising application will produce a point cloud, unless
2870
- # it uses the 'none' method.
2871
- if self.pc_denoising_application.used_method != "none":
2872
- last_pc_application = "denoising"
2873
- else:
2874
- last_pc_application = "fusion"
2875
-
2876
- raster_app_margin = 0
2877
- if self.rasterization_application is not None:
2878
- raster_app_margin = self.rasterization_application.get_margins(
2879
- self.resolution
2880
- )
2881
-
2882
- merged_point_clouds = self.pc_fusion_application.run(
2883
- self.list_epipolar_point_clouds,
2884
- self.terrain_bounds,
2885
- self.epsg,
2886
- source_pc_names=(
2887
- self.pairs_names if self.compute_depth_map else None
2888
- ),
2889
- orchestrator=self.cars_orchestrator,
2890
- margins=raster_app_margin,
2891
- optimal_terrain_tile_width=self.optimal_terrain_tile_width,
2892
- roi=(self.roi_poly if self.debug_with_roi else None),
2893
- save_laz_output=self.save_output_point_cloud
2894
- and last_pc_application == "fusion",
2895
- )
2896
-
2897
- if self.quit_on_app("point_cloud_fusion"):
2898
- return True
2899
-
2900
- # denoise point cloud
2901
- denoised_merged_point_clouds = self.pc_denoising_application.run(
2902
- merged_point_clouds,
2903
- orchestrator=self.cars_orchestrator,
2904
- save_laz_output=self.save_output_point_cloud
2905
- and last_pc_application == "denoising",
2906
- )
2907
-
2908
- if self.quit_on_app("pc_denoising"):
2909
- return True
2910
-
2911
- # Rasterize merged and filtered point cloud
2912
- self.point_cloud_to_rasterize = denoised_merged_point_clouds
2913
-
2914
- # try getting the color type from multiple sources
2915
- self.color_type = self.list_epipolar_point_clouds[0].attributes.get(
2916
- "color_type",
2917
- self.point_cloud_to_rasterize.attributes.get(
2918
- "color_type", None
2919
- ),
2920
- )
2921
-
2922
- return False
2923
-
2924
- def load_input_depth_maps(self):
2925
- """
2926
- Loads all the data and creates all the variables used
2927
- later when processing a depth map, as if it was just computed.
2928
- """
2929
- # get epsg
2930
- self.epsg = self.used_conf[OUTPUT][out_cst.EPSG]
2931
-
2932
- output_parameters.intialize_product_index(
2933
- self.cars_orchestrator,
2934
- self.used_conf[OUTPUT]["product_level"],
2935
- self.used_conf[INPUTS][depth_cst.DEPTH_MAPS].keys(),
2936
- )
2937
-
2938
- # compute epsg
2939
- epsg_cloud = pc_fusion_wrappers.compute_epsg_from_point_cloud(
2940
- self.used_conf[INPUTS][depth_cst.DEPTH_MAPS]
2556
+ self.point_cloud_to_rasterize = (
2557
+ self.list_epipolar_point_clouds,
2558
+ self.terrain_bounds,
2941
2559
  )
2942
- if self.epsg is None:
2943
- self.epsg = epsg_cloud
2944
-
2945
- self.vertical_crs = projection.get_output_crs(
2946
- self.epsg, self.used_conf[OUTPUT]
2560
+ self.color_type = self.point_cloud_to_rasterize[0][0].attributes.get(
2561
+ "color_type", None
2947
2562
  )
2948
2563
 
2949
- self.resolution = (
2950
- self.used_conf[OUTPUT][out_cst.RESOLUTION] * self.res_resamp
2951
- )
2952
-
2953
- # Compute roi polygon, in input EPSG
2954
- self.roi_poly = preprocessing.compute_roi_poly(
2955
- self.input_roi_poly, self.input_roi_epsg, self.epsg
2956
- )
2957
-
2958
- if not self.merging:
2959
- # compute bounds
2960
- self.terrain_bounds = pc_fusion_wrappers.get_bounds(
2961
- self.used_conf[INPUTS][depth_cst.DEPTH_MAPS],
2962
- self.epsg,
2963
- roi_poly=self.roi_poly,
2964
- )
2965
-
2966
- self.list_epipolar_point_clouds = (
2967
- pc_fusion_algo.generate_point_clouds(
2968
- self.used_conf[INPUTS][depth_cst.DEPTH_MAPS],
2969
- self.cars_orchestrator,
2970
- tile_size=1000,
2971
- )
2972
- )
2973
- else:
2974
- # Compute terrain bounds and transform point clouds
2975
- (
2976
- self.terrain_bounds,
2977
- self.list_epipolar_point_clouds,
2978
- ) = pc_fusion_algo.transform_input_pc(
2979
- self.used_conf[INPUTS][depth_cst.DEPTH_MAPS],
2980
- self.epsg,
2981
- roi_poly=self.roi_poly,
2982
- epipolar_tile_size=1000, # TODO change it
2983
- orchestrator=self.cars_orchestrator,
2984
- )
2985
-
2986
- # Compute number of superposing point cloud for density
2987
- max_number_superposing_point_clouds = (
2988
- pc_fusion_wrappers.compute_max_nb_point_clouds(
2989
- self.list_epipolar_point_clouds
2990
- )
2991
- )
2992
-
2993
- # Compute average distance between two points
2994
- average_distance_point_cloud = (
2995
- pc_fusion_wrappers.compute_average_distance(
2996
- self.list_epipolar_point_clouds
2997
- )
2998
- )
2999
- self.optimal_terrain_tile_width = (
3000
- self.rasterization_application.get_optimal_tile_size(
3001
- self.cars_orchestrator.cluster.checked_conf_cluster[
3002
- "max_ram_per_worker"
3003
- ],
3004
- superposing_point_clouds=(
3005
- max_number_superposing_point_clouds
3006
- ),
3007
- point_cloud_resolution=average_distance_point_cloud,
3008
- )
3009
- )
3010
- # epsg_cloud and optimal_terrain_tile_width have the same epsg
3011
- self.optimal_terrain_tile_width = (
3012
- preprocessing.convert_optimal_tile_size_with_epsg(
3013
- self.terrain_bounds,
3014
- self.optimal_terrain_tile_width,
3015
- self.epsg,
3016
- epsg_cloud,
3017
- )
3018
- )
3019
-
2564
+ @cars_profile(name="Final cleanup", interval=0.5)
3020
2565
  def final_cleanup(self):
3021
2566
  """
3022
2567
  Clean temporary files and directory at the end of cars processing
@@ -3034,28 +2579,30 @@ class UnitPipeline(PipelineTemplate):
3034
2579
  not any(
3035
2580
  app.get("save_intermediate_data", False) is True
3036
2581
  for app in self.used_conf[APPLICATIONS].values()
2582
+ if app is not None
3037
2583
  )
3038
2584
  and not self.dsms_in_inputs
3039
2585
  ):
3040
2586
  self.cars_orchestrator.add_to_clean(self.dump_dir)
3041
2587
 
3042
- @cars_profile(name="run_dense_pipeline", interval=0.5)
3043
- def run(
2588
+ @cars_profile(name="run_unit_pipeline", interval=0.5)
2589
+ def run( # pylint: disable=too-many-positional-arguments
3044
2590
  self,
3045
- orchestrator_conf=None,
3046
2591
  generate_dems=False,
3047
2592
  which_resolution="single",
3048
2593
  use_sift_a_priori=False,
3049
2594
  first_res_out_dir=None,
3050
- final_out_dir=None,
2595
+ log_dir=None,
3051
2596
  ): # noqa C901
3052
2597
  """
3053
2598
  Run pipeline
3054
2599
 
3055
2600
  """
2601
+ if log_dir is not None:
2602
+ self.log_dir = log_dir
2603
+ else:
2604
+ self.log_dir = os.path.join(self.out_dir, "logs")
3056
2605
 
3057
- self.out_dir = self.used_conf[OUTPUT][out_cst.OUT_DIRECTORY]
3058
- self.dump_dir = os.path.join(self.out_dir, "dump_dir")
3059
2606
  self.first_res_out_dir = first_res_out_dir
3060
2607
  self.texture_bands = self.used_conf[ADVANCED][adv_cst.TEXTURE_BANDS]
3061
2608
 
@@ -3067,65 +2614,29 @@ class UnitPipeline(PipelineTemplate):
3067
2614
 
3068
2615
  self.which_resolution = which_resolution
3069
2616
 
3070
- # Save used conf
3071
- cars_dataset.save_dict(
3072
- self.used_conf,
3073
- os.path.join(self.out_dir, "used_conf.json"),
3074
- safe_save=True,
3075
- )
3076
-
3077
- if self.which_resolution not in ("single", "final"):
3078
- path_used_conf_res = (
3079
- "used_conf_res" + str(self.res_resamp) + ".json"
3080
- )
3081
- cars_dataset.save_dict(
3082
- self.used_conf,
3083
- os.path.join(final_out_dir, path_used_conf_res),
3084
- safe_save=True,
3085
- )
3086
-
3087
- if orchestrator_conf is None:
3088
- # start cars orchestrator
3089
- with orchestrator.Orchestrator(
3090
- orchestrator_conf=self.used_conf[ORCHESTRATOR],
3091
- out_dir=self.out_dir,
3092
- out_json_path=os.path.join(
3093
- self.out_dir,
3094
- out_cst.INFO_FILENAME,
3095
- ),
3096
- ) as self.cars_orchestrator:
3097
- # initialize out_json
3098
- self.cars_orchestrator.update_out_info({"version": __version__})
3099
-
3100
- if not self.dsms_in_inputs:
3101
- if self.compute_depth_map:
3102
- self.sensor_to_depth_maps()
3103
- else:
3104
- self.load_input_depth_maps()
3105
-
3106
- if self.save_output_dsm or self.save_output_point_cloud:
3107
- end_pipeline = self.preprocess_depth_maps()
3108
-
3109
- if self.save_output_dsm and not end_pipeline:
3110
- self.rasterize_point_cloud()
3111
- self.filling()
3112
- else:
3113
- self.filling()
3114
-
3115
- self.final_cleanup()
3116
- else:
3117
- self.cars_orchestrator = orchestrator_conf
2617
+ # saved used configuration
2618
+ self.save_configurations()
2619
+ # start cars orchestrator
2620
+ with orchestrator.Orchestrator(
2621
+ orchestrator_conf=self.used_conf[ORCHESTRATOR],
2622
+ out_dir=self.out_dir,
2623
+ log_dir=self.log_dir,
2624
+ out_json_path=os.path.join(
2625
+ self.out_dir,
2626
+ out_cst.INFO_FILENAME,
2627
+ ),
2628
+ ) as self.cars_orchestrator:
2629
+ # initialize out_json
2630
+ self.cars_orchestrator.update_out_info({"version": __version__})
3118
2631
 
3119
2632
  if not self.dsms_in_inputs:
3120
2633
  if self.compute_depth_map:
3121
2634
  self.sensor_to_depth_maps()
3122
- else:
3123
- self.load_input_depth_maps()
3124
2635
 
3125
2636
  if self.save_output_dsm or self.save_output_point_cloud:
3126
- end_pipeline = self.preprocess_depth_maps()
2637
+ self.preprocess_depth_maps()
3127
2638
 
3128
- if self.save_output_dsm and not end_pipeline:
2639
+ if self.save_output_dsm:
3129
2640
  self.rasterize_point_cloud()
3130
2641
  self.filling()
3131
2642
  else: