cars 1.0.0a3__cp313-cp313-win_amd64.whl → 1.0.0a4__cp313-cp313-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 (139) hide show
  1. cars/__init__.py +3 -3
  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.cp313-win_amd64.dll.a +0 -0
  19. cars/applications/dense_matching/cpp/dense_matching_cpp.cp313-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 +115 -131
  111. cars.libs/libgcc_s_seh-1-b2494fcbd4d80cf2c98fdd5261f6d850.dll +0 -0
  112. cars.libs/libstdc++-6-e9b0d12ae0e9555bbae55e8dfd08c3f7.dll +0 -0
  113. cars.libs/libwinpthread-1-7882d1b093714ccdfaf4e0789a817792.dll +0 -0
  114. cars/applications/dense_match_filling/cpp/__init__.py +0 -0
  115. cars/applications/dense_match_filling/cpp/dense_match_filling_cpp.cp313-win_amd64.dll.a +0 -0
  116. cars/applications/dense_match_filling/cpp/dense_match_filling_cpp.cp313-win_amd64.pyd +0 -0
  117. cars/applications/dense_match_filling/cpp/dense_match_filling_cpp.py +0 -72
  118. cars/applications/dense_match_filling/cpp/includes/dense_match_filling.hpp +0 -46
  119. cars/applications/dense_match_filling/cpp/meson.build +0 -9
  120. cars/applications/dense_match_filling/cpp/src/bindings.cpp +0 -11
  121. cars/applications/dense_match_filling/cpp/src/dense_match_filling.cpp +0 -142
  122. cars/applications/dense_match_filling/plane_app.py +0 -556
  123. cars/applications/hole_detection/__init__.py +0 -30
  124. cars/applications/hole_detection/abstract_hole_detection_app.py +0 -125
  125. cars/applications/hole_detection/cloud_to_bbox_app.py +0 -346
  126. cars/applications/hole_detection/hole_detection_algo.py +0 -144
  127. cars/applications/hole_detection/hole_detection_wrappers.py +0 -53
  128. cars/applications/point_cloud_denoising/__init__.py +0 -29
  129. cars/applications/point_cloud_denoising/abstract_pc_denoising_app.py +0 -273
  130. cars/applications/point_cloud_fusion/__init__.py +0 -30
  131. cars/applications/point_cloud_fusion/cloud_fusion_constants.py +0 -39
  132. cars/applications/sparse_matching/pandora_sparse_matching_app.py +0 -0
  133. cars/pipelines/parameters/depth_map_inputs_constants.py +0 -25
  134. cars-1.0.0a3.dist-info/DELVEWHEEL +0 -2
  135. cars.libs/libgcc_s_seh-1-ca70890bbc5723b6d0ea31e9c9cded2b.dll +0 -0
  136. cars.libs/libstdc++-6-00ee19f73d5122a1277c137b1c218401.dll +0 -0
  137. cars.libs/libwinpthread-1-f5042e8e3d21edce20c1bc99445f551b.dll +0 -0
  138. {cars-1.0.0a3.dist-info → cars-1.0.0a4.dist-info}/WHEEL +0 -0
  139. {cars-1.0.0a3.dist-info → cars-1.0.0a4.dist-info}/entry_points.txt +0 -0
@@ -144,7 +144,7 @@ def compute_disparity_grid(
144
144
  return disp_min_grid, disp_max_grid
145
145
 
146
146
 
147
- def compute_disparity(
147
+ def compute_disparity( # pylint: disable=too-many-positional-arguments
148
148
  left_dataset,
149
149
  right_dataset,
150
150
  corr_cfg,
@@ -257,19 +257,53 @@ def compute_disparity(
257
257
  right_dataset = right_dataset.drop_vars([cst.EPI_MSK])
258
258
  right_dataset[cst.EPI_MSK] = right_msk
259
259
 
260
- # Instantiate pandora state machine
261
- pandora_machine = PandoraMachine()
262
-
263
- # check datasets
264
- check_datasets(left_dataset, right_dataset)
265
-
266
- # Run the Pandora pipeline
267
- ref, _ = pandora.run(
268
- pandora_machine,
269
- left_dataset,
270
- right_dataset,
271
- corr_cfg,
272
- )
260
+ # Check that the datasets are not full of nan (would crash later)
261
+ if left_dataset["msk"].all() or right_dataset["msk"].all():
262
+ height = left_dataset.sizes["row"]
263
+ width = left_dataset.sizes["col"]
264
+ ref = xr.Dataset(
265
+ coords={
266
+ "row": left_dataset.coords["row"],
267
+ "col": left_dataset.coords["col"],
268
+ "indicator": [
269
+ "confidence_from_ambiguity.cars_1",
270
+ "confidence_from_risk_max.cars_2",
271
+ "confidence_from_risk_min.cars_2",
272
+ "confidence_from_disp_sup_from_risk.cars_2",
273
+ "confidence_from_disp_inf_from_risk.cars_2",
274
+ "confidence_from_interval_bounds_inf.cars_3",
275
+ "confidence_from_interval_bounds_sup.cars_3",
276
+ "confidence_from_left_right_consistency",
277
+ ],
278
+ },
279
+ data_vars={
280
+ "disparity_map": (
281
+ ("row", "col"),
282
+ np.full((height, width), np.nan, dtype=np.float32),
283
+ ),
284
+ "validity_mask": (
285
+ ("row", "col"),
286
+ np.ones((height, width), dtype=np.int64),
287
+ ),
288
+ "confidence_measure": (
289
+ ("row", "col", "indicator"),
290
+ np.full((height, width, 8), np.nan, dtype=np.float32),
291
+ ),
292
+ },
293
+ )
294
+ else:
295
+ # Instantiate pandora state machine
296
+ pandora_machine = PandoraMachine()
297
+ # check datasets
298
+ check_datasets(left_dataset, right_dataset)
299
+
300
+ # Run the Pandora pipeline
301
+ ref, _ = pandora.run(
302
+ pandora_machine,
303
+ left_dataset,
304
+ right_dataset,
305
+ corr_cfg,
306
+ )
273
307
 
274
308
  disp_dataset = dm_wrap.create_disp_dataset(
275
309
  ref,
@@ -21,10 +21,11 @@
21
21
  This module is responsible for the dense matching algorithms:
22
22
  - thus it creates a disparity map from a pair of images
23
23
  """
24
- # pylint: disable=too-many-lines
24
+ # pylint: disable=C0302
25
25
 
26
26
  # Standard imports
27
27
  import logging
28
+ import math
28
29
  import warnings
29
30
  from typing import Dict
30
31
 
@@ -281,6 +282,7 @@ def compute_cropped_roi(
281
282
  )
282
283
 
283
284
 
285
+ # pylint: disable=too-many-positional-arguments
284
286
  def create_disp_dataset( # noqa: C901
285
287
  disp: xr.Dataset,
286
288
  ref_dataset: xr.Dataset,
@@ -513,6 +515,7 @@ def add_crop_info(disp_ds, cropped_range):
513
515
  return disp_ds
514
516
 
515
517
 
518
+ # pylint: disable=too-many-positional-arguments
516
519
  def estimate_right_classif_on_left(
517
520
  right_classif,
518
521
  disp_map,
@@ -752,6 +755,7 @@ def estimate_right_grid_disp(disp_min_grid, disp_max_grid):
752
755
  )
753
756
 
754
757
 
758
+ # pylint: disable=too-many-positional-arguments
755
759
  def optimal_tile_size_pandora_plugin_libsgm(
756
760
  disp_min: int,
757
761
  disp_max: int,
@@ -779,7 +783,7 @@ def optimal_tile_size_pandora_plugin_libsgm(
779
783
  """
780
784
 
781
785
  memory = max_ram_per_worker
782
- disp = disp_max - disp_min
786
+ disp = max(3, abs(disp_max - disp_min))
783
787
 
784
788
  image = 32 * 2
785
789
  disp_ref = 32
@@ -809,13 +813,17 @@ def optimal_tile_size_pandora_plugin_libsgm(
809
813
  # but sqrt(nb_pixels + (disp/2)**2) - disp/2
810
814
  tile_size = np.sqrt(row_or_col + (disp / 2) ** 2) - disp / 2
811
815
  tile_size = (1.0 - margin / 100.0) * tile_size
812
- tile_size = tile_size_rounding * int(tile_size / tile_size_rounding)
816
+
817
+ if math.isinf(tile_size):
818
+ logging.warning("Tile size infinite")
813
819
 
814
820
  if tile_size > max_tile_size:
815
821
  tile_size = max_tile_size
816
822
  elif tile_size < min_tile_size:
817
823
  tile_size = min_tile_size
818
824
 
825
+ tile_size = tile_size_rounding * int(tile_size / tile_size_rounding)
826
+
819
827
  return tile_size
820
828
 
821
829
 
@@ -27,7 +27,6 @@ import itertools
27
27
  import logging
28
28
 
29
29
  # Third party imports
30
- import affine
31
30
  import numpy as np
32
31
  import rasterio
33
32
  import xarray as xr
@@ -45,6 +44,7 @@ from cars.core.projection import point_cloud_conversion
45
44
  from cars.data_structures import cars_dataset, cars_dict
46
45
 
47
46
 
47
+ # pylint: disable=too-many-positional-arguments
48
48
  def generate_disp_grids_dataset(
49
49
  grid_min,
50
50
  grid_max,
@@ -103,6 +103,7 @@ def generate_disp_grids_dataset(
103
103
  return disp_range_tile
104
104
 
105
105
 
106
+ # pylint: disable=too-many-positional-arguments
106
107
  def generate_disp_range_const_tile_wrapper(
107
108
  row_range,
108
109
  col_range,
@@ -152,6 +153,7 @@ def generate_disp_range_const_tile_wrapper(
152
153
  return disp_range, global_infos
153
154
 
154
155
 
156
+ # pylint: disable=too-many-positional-arguments
155
157
  def generate_disp_range_from_dem_wrapper(
156
158
  epipolar_grid_array_window,
157
159
  full_epi_row_range,
@@ -162,8 +164,6 @@ def generate_disp_range_from_dem_wrapper(
162
164
  dem_median,
163
165
  dem_min,
164
166
  dem_max,
165
- altitude_delta_min,
166
- altitude_delta_max,
167
167
  raster_profile,
168
168
  saving_info,
169
169
  saving_info_global_infos,
@@ -193,10 +193,6 @@ def generate_disp_range_from_dem_wrapper(
193
193
  :type dem_min: str
194
194
  :param dem_max: Path of dem max.
195
195
  :type dem_max: srt
196
- :param altitude_delta_min: The minimum altitude delta.
197
- :type altitude_delta_min: float
198
- :param altitude_delta_max: The maximum altitude delta.
199
- :type altitude_delta_max: float
200
196
  :param raster_profile: The raster profile.
201
197
  :type raster_profile: dict
202
198
  :param saving_info: The disp range grid saving information.
@@ -217,23 +213,11 @@ def generate_disp_range_from_dem_wrapper(
217
213
  """
218
214
 
219
215
  # compute reverse matrix
220
- transform_sensor = rasterio.Affine(
221
- *np.abs(
222
- inputs.rasterio_get_transform(
223
- sensor_image_right["image"]["main_file"]
224
- )
225
- )
216
+ transform_sensor = inputs.rasterio_get_transform(
217
+ sensor_image_right["image"]["main_file"], convention="north"
226
218
  )
227
219
 
228
220
  trans_inv_sensor = ~transform_sensor
229
- # Transform to positive values
230
- trans_inv_sensor = np.array(trans_inv_sensor)
231
- trans_inv_sensor = np.reshape(trans_inv_sensor, (3, 3))
232
- if trans_inv_sensor[0, 0] < 0:
233
- trans_inv_sensor[0, :] *= -1
234
- if trans_inv_sensor[1, 1] < 0:
235
- trans_inv_sensor[1, :] *= -1
236
- trans_inv_sensor = affine.Affine(*list(trans_inv_sensor.flatten()))
237
221
 
238
222
  # Geometry plugin
239
223
  geo_plugin = geom_plugin_with_dem_and_geoid
@@ -294,7 +278,9 @@ def generate_disp_range_from_dem_wrapper(
294
278
  (np.max(col_range_with_margin), np.min(row_range_with_margin)),
295
279
  (np.max(col_range_with_margin), np.max(row_range_with_margin)),
296
280
  ]
297
- sensor_bbox = geo_plugin.sensor_position_from_grid(grid_right, epi_bbox)
281
+ sensor_bbox = geo_plugin.sensor_position_from_grid(
282
+ grid_right, epi_bbox, interpolation_method="linear"
283
+ )
298
284
  row_sensor_bbox, col_sensor_bbox = transform_physical_point_to_index(
299
285
  trans_inv_sensor, sensor_bbox[:, 1], sensor_bbox[:, 0]
300
286
  )
@@ -356,18 +342,26 @@ def generate_disp_range_from_dem_wrapper(
356
342
  lon_mean = terrain_position_lon_lat[:, 0]
357
343
  lat_mean = terrain_position_lon_lat[:, 1]
358
344
 
359
- if None not in (dem_min, dem_max, dem_median):
360
- # dem min and max are in 4326
361
- dem_min_list = inputs.rasterio_get_values(
362
- dem_min, lon_mean, lat_mean, point_cloud_conversion
363
- )
364
- dem_max_list = inputs.rasterio_get_values(
365
- dem_max, lon_mean, lat_mean, point_cloud_conversion
345
+ # dem min and max are in 4326
346
+ dem_min_list = inputs.rasterio_get_values(
347
+ dem_min, lon_mean, lat_mean, point_cloud_conversion
348
+ )
349
+ dem_max_list = inputs.rasterio_get_values(
350
+ dem_max, lon_mean, lat_mean, point_cloud_conversion
351
+ )
352
+ if dem_min_list is None or dem_max_list is None:
353
+ logging.warning("DEM min and DEM max does not cover this tile")
354
+ disp_range, global_infos = empty_disparity_grids(
355
+ row_range_no_margin,
356
+ col_range_no_margin,
357
+ epipolar_grid_array_window,
358
+ raster_profile,
359
+ saving_info,
360
+ saving_info_global_infos,
366
361
  )
367
- nan_mask = nan_mask & ~np.isnan(dem_min_list) & ~np.isnan(dem_max_list)
368
- else:
369
- dem_min_list = dem_median_list - altitude_delta_min
370
- dem_max_list = dem_median_list + altitude_delta_max
362
+ return disp_range, global_infos
363
+
364
+ nan_mask = nan_mask & ~np.isnan(dem_min_list) & ~np.isnan(dem_max_list)
371
365
 
372
366
  # filter nan value from input points
373
367
  lon_mean = lon_mean[nan_mask]
@@ -410,6 +404,7 @@ def generate_disp_range_from_dem_wrapper(
410
404
  2,
411
405
  ),
412
406
  ),
407
+ interpolation_method="linear",
413
408
  )
414
409
  )
415
410
 
@@ -422,37 +417,14 @@ def generate_disp_range_from_dem_wrapper(
422
417
 
423
418
  if len(ind_rows_sensor) < 5:
424
419
  # QH6214 needs at least 4 points for interpolation
425
-
426
- grid_min = np.empty(
427
- (len(row_range_no_margin), len(col_range_no_margin))
428
- )
429
- grid_max = np.empty(
430
- (len(row_range_no_margin), len(col_range_no_margin))
431
- )
432
- grid_min[:, :] = 0
433
- grid_max[:, :] = 0
434
-
435
- disp_range = generate_disp_grids_dataset(
436
- grid_min,
437
- grid_max,
438
- saving_info,
420
+ disp_range, global_infos = empty_disparity_grids(
421
+ row_range_no_margin,
422
+ col_range_no_margin,
423
+ epipolar_grid_array_window,
439
424
  raster_profile,
440
- window=epipolar_grid_array_window,
441
- row_coords=row_range_no_margin,
442
- col_coords=col_range_no_margin,
443
- )
444
-
445
- # Generate infos on global min and max
446
- global_infos = cars_dict.CarsDict(
447
- {
448
- "global_min": 0,
449
- "global_max": 0,
450
- }
451
- )
452
- cars_dataset.fill_dict(
453
- global_infos, saving_info=saving_info_global_infos
425
+ saving_info,
426
+ saving_info_global_infos,
454
427
  )
455
-
456
428
  return disp_range, global_infos
457
429
 
458
430
  # Interpolate disparity
@@ -542,6 +514,56 @@ def generate_disp_range_from_dem_wrapper(
542
514
  return disp_range, global_infos
543
515
 
544
516
 
517
+ def empty_disparity_grids( # pylint: disable=too-many-positional-arguments
518
+ row_range_no_margin,
519
+ col_range_no_margin,
520
+ epipolar_grid_array_window,
521
+ raster_profile,
522
+ saving_info,
523
+ saving_info_global_infos,
524
+ ):
525
+ """
526
+ Return empty disparity grids
527
+ :param row_range_no_margin: Rows id in grid
528
+ :type row_range_no_margin: int
529
+ :param col_range_no_margin: Cols id in grid
530
+ :type col_range_no_margin: int
531
+ :param epipolar_grid_array_window: ROI of grid
532
+ :type epipolar_grid_array_window: dict
533
+ :param raster_profile: The raster profile.
534
+ :type raster_profile: dict
535
+ :param saving_info: The disp range grid saving information.
536
+ :type saving_info: dict
537
+ :param saving_info_global_infos: Global info saving infos.
538
+ :type saving_info_global_infos: dict
539
+ """
540
+ grid_min = np.empty((len(row_range_no_margin), len(col_range_no_margin)))
541
+ grid_max = np.empty((len(row_range_no_margin), len(col_range_no_margin)))
542
+ grid_min[:, :] = 0
543
+ grid_max[:, :] = 0
544
+
545
+ disp_range = generate_disp_grids_dataset(
546
+ grid_min,
547
+ grid_max,
548
+ saving_info,
549
+ raster_profile,
550
+ window=epipolar_grid_array_window,
551
+ row_coords=row_range_no_margin,
552
+ col_coords=col_range_no_margin,
553
+ )
554
+
555
+ # Generate infos on global min and max
556
+ global_infos = cars_dict.CarsDict(
557
+ {
558
+ "global_min": 0,
559
+ "global_max": 0,
560
+ }
561
+ )
562
+ cars_dataset.fill_dict(global_infos, saving_info=saving_info_global_infos)
563
+
564
+ return disp_range, global_infos
565
+
566
+
545
567
  def create_circular_mask(height, width):
546
568
  """
547
569
  Create a circular mask for footprint around pixel
@@ -32,6 +32,7 @@ from typing import Dict
32
32
  import numpy as np
33
33
  import pandora
34
34
  import rasterio
35
+ import xarray as xr
35
36
  from json_checker import Checker, Or
36
37
  from pandora.check_configuration import (
37
38
  check_pipeline_section,
@@ -39,12 +40,12 @@ from pandora.check_configuration import (
39
40
  get_config_pipeline,
40
41
  update_conf,
41
42
  )
42
- from pandora.img_tools import get_metadata
43
43
  from pandora.state_machine import PandoraMachine
44
44
  from rasterio.mask import mask
45
45
  from shapely.geometry import mapping
46
46
 
47
47
  from cars.core.projection import polygon_projection
48
+ from cars.orchestrator.cluster.log_wrapper import cars_profile
48
49
 
49
50
 
50
51
  class PandoraLoader:
@@ -53,7 +54,7 @@ class PandoraLoader:
53
54
 
54
55
  """
55
56
 
56
- def __init__( # noqa: C901
57
+ def __init__( # pylint: disable=too-many-positional-arguments # noqa: C901
57
58
  self,
58
59
  conf=None,
59
60
  method_name=None,
@@ -271,26 +272,6 @@ class PandoraLoader:
271
272
  if "band" not in filter_conf:
272
273
  conf["pipeline"]["filter"]["band"] = used_band
273
274
 
274
- for key in list(conf.get("pipeline")):
275
- if key.startswith("filter"):
276
- if (
277
- conf["pipeline"][key]["filter_method"]
278
- == "median_for_intervals"
279
- and "validation" in conf["pipeline"]
280
- ):
281
- if (
282
- conf["pipeline"]["validation"]["validation_method"]
283
- == "cross_checking_fast"
284
- ):
285
- conf["pipeline"]["validation"][
286
- "validation_method"
287
- ] = "cross_checking_accurate"
288
- logging.warning(
289
- "You can not use median_for_intervals with "
290
- "the fast cross checking validation for now. "
291
- "It therefore has been overrided to accurate"
292
- )
293
-
294
275
  # Check conf
295
276
  self.pandora_config = conf
296
277
 
@@ -305,6 +286,35 @@ class PandoraLoader:
305
286
 
306
287
  return self.pandora_config
307
288
 
289
+ def get_classif_bands(self):
290
+ """
291
+ Get the classification bands used in the pandora configuration
292
+
293
+ :return: list of classification bands
294
+ """
295
+
296
+ classif_bands = []
297
+
298
+ def search_classes_recursive(obj):
299
+ """
300
+ Recursive search of keys containing 'classes' in the configuration
301
+ """
302
+ if isinstance(obj, dict):
303
+ for key, value in obj.items():
304
+ if "classes" in key.lower():
305
+ if isinstance(value, list):
306
+ classif_bands.extend(value)
307
+ elif value is not None:
308
+ classif_bands.append(value)
309
+ # Continue recursive search
310
+ search_classes_recursive(value)
311
+
312
+ search_classes_recursive(self.pandora_config)
313
+
314
+ # Remove duplicates and return the list
315
+ return list(set(classif_bands))
316
+
317
+ @cars_profile(name="Find auto conf")
308
318
  def find_auto_conf(
309
319
  self, intersection_poly, land_cover_map, classif_to_config_mapping, epsg
310
320
  ):
@@ -377,15 +387,15 @@ class PandoraLoader:
377
387
 
378
388
  return conf
379
389
 
380
- def check_conf(
390
+ def check_conf( # pylint: disable=too-many-positional-arguments
381
391
  self,
382
392
  user_cfg,
383
- img_left,
384
- img_right,
393
+ nodata_left,
394
+ nodata_right,
385
395
  bands_left,
386
396
  bands_right,
387
- classif_left=None,
388
- classif_right=None,
397
+ bands_classif_left=None,
398
+ bands_classif_right=None,
389
399
  ):
390
400
  """
391
401
  Check configuration
@@ -397,15 +407,18 @@ class PandoraLoader:
397
407
  :rtype: dict
398
408
 
399
409
  """
400
-
401
410
  # Import plugins before checking configuration
402
411
  pandora.import_plugin()
403
412
  # Check configuration and update the configuration with default values
404
413
  # Instantiate pandora state machine
405
414
  pandora_machine = PandoraMachine()
406
415
  # check pipeline
407
- metadata_left = get_metadata(img_left, classif=classif_left)
408
- metadata_right = get_metadata(img_right, classif=classif_right)
416
+ metadata_left = overide_pandora_get_metadata(
417
+ bands_left, classif_bands=bands_classif_left
418
+ )
419
+ metadata_right = overide_pandora_get_metadata(
420
+ bands_right, classif_bands=bands_classif_right
421
+ )
409
422
 
410
423
  metadata_left = metadata_left.assign_coords(band_im=bands_left)
411
424
  metadata_right = metadata_right.assign_coords(band_im=bands_right)
@@ -422,7 +435,9 @@ class PandoraLoader:
422
435
  saved_schema
423
436
  )
424
437
  # check a part of input section
425
- user_cfg_input = get_config_input_custom_cars(user_cfg)
438
+ user_cfg_input = get_config_input_custom_cars(
439
+ user_cfg, nodata_left, nodata_right
440
+ )
426
441
  cfg_input = check_input_section_custom_cars(user_cfg_input)
427
442
  # concatenate updated config
428
443
  cfg = concat_conf([cfg_input, cfg_pipeline])
@@ -447,7 +462,46 @@ default_short_configuration_input_custom_cars = {
447
462
  }
448
463
 
449
464
 
450
- def get_config_input_custom_cars(user_cfg: Dict[str, dict]) -> Dict[str, dict]:
465
+ def overide_pandora_get_metadata(
466
+ im_bands: list, classif_bands: list = None
467
+ ) -> xr.Dataset:
468
+ """
469
+ Read metadata from image, and return the corresponding xarray.DataSet
470
+
471
+ :param im_bands: list of band names
472
+ :param classif_bands: list of classification band names
473
+ :return: partial xarray.DataSet (attributes and coordinates)
474
+ :rtype: xarray.DataSet
475
+ """
476
+
477
+ coords = {
478
+ "band_im": list(im_bands),
479
+ "row": np.arange(10),
480
+ "col": np.arange(10),
481
+ }
482
+
483
+ data_vars = {
484
+ "image": (["row", "col"], np.zeros((10, 10))),
485
+ }
486
+
487
+ if classif_bands is not None:
488
+ coords["band_classif"] = list(classif_bands)
489
+ data_vars["classif"] = (
490
+ ["row", "col", "band_classif"],
491
+ np.zeros((10, 10, len(classif_bands)), dtype=np.int32),
492
+ )
493
+
494
+ # create the dataset
495
+ dataset = xr.Dataset(data_vars=data_vars, coords=coords)
496
+
497
+ dataset.attrs["disparity_source"] = None
498
+
499
+ return dataset
500
+
501
+
502
+ def get_config_input_custom_cars(
503
+ user_cfg: Dict[str, dict], nodata_left, nodata_right
504
+ ) -> Dict[str, dict]:
451
505
  """
452
506
  Get the input configuration
453
507
 
@@ -464,9 +518,13 @@ def get_config_input_custom_cars(user_cfg: Dict[str, dict]) -> Dict[str, dict]:
464
518
 
465
519
  if "nodata_left" in user_cfg["input"]:
466
520
  cfg["input"]["nodata_left"] = user_cfg["input"]["nodata_left"]
521
+ else:
522
+ cfg["input"]["nodata_left"] = nodata_left
467
523
 
468
524
  if "nodata_right" in user_cfg["input"]:
469
525
  cfg["input"]["nodata_right"] = user_cfg["input"]["nodata_right"]
526
+ else:
527
+ cfg["input"]["nodata_right"] = nodata_right
470
528
 
471
529
  return cfg
472
530
 
@@ -516,7 +574,7 @@ def overload_pandora_conf_with_confidence(conf, confidence_conf):
516
574
 
517
575
  for key in confidence_conf_keys:
518
576
  if key in conf_keys:
519
- logging.error("{} pandora key already in configuration".format(key))
577
+ logging.info("{} pandora key already in configuration".format(key))
520
578
 
521
579
  # update confidence
522
580
  out_dict.update(confidence_conf)
@@ -56,7 +56,6 @@ class BorderInterpolation(DsmFilling, short_name="border_interpolation"):
56
56
 
57
57
  # check conf
58
58
  self.used_method = self.used_config["method"]
59
- self.activated = self.used_config["activated"]
60
59
  self.classification = self.used_config["classification"]
61
60
  self.component_min_size = self.used_config["component_min_size"]
62
61
  self.border_size = self.used_config["border_size"]
@@ -74,7 +73,6 @@ class BorderInterpolation(DsmFilling, short_name="border_interpolation"):
74
73
 
75
74
  # Overload conf
76
75
  overloaded_conf["method"] = conf.get("method", "bulldozer")
77
- overloaded_conf["activated"] = conf.get("activated", False)
78
76
  overloaded_conf["classification"] = conf.get("classification", "nodata")
79
77
  if isinstance(overloaded_conf["classification"], str):
80
78
  overloaded_conf["classification"] = [
@@ -91,7 +89,6 @@ class BorderInterpolation(DsmFilling, short_name="border_interpolation"):
91
89
 
92
90
  rectification_schema = {
93
91
  "method": str,
94
- "activated": bool,
95
92
  "classification": Or(None, [str]),
96
93
  "component_min_size": int,
97
94
  "border_size": int,
@@ -106,7 +103,7 @@ class BorderInterpolation(DsmFilling, short_name="border_interpolation"):
106
103
  return overloaded_conf
107
104
 
108
105
  @cars_profile(name="Border interpolation filling")
109
- def run( # noqa C901
106
+ def run( # pylint: disable=too-many-positional-arguments # noqa C901
110
107
  self,
111
108
  dsm_file,
112
109
  classif_file,
@@ -127,9 +124,6 @@ class BorderInterpolation(DsmFilling, short_name="border_interpolation"):
127
124
  - a Shapely Polygon
128
125
  """
129
126
 
130
- if not self.activated:
131
- return
132
-
133
127
  if self.classification is None:
134
128
  self.classification = ["nodata"]
135
129
  logging.error(
@@ -57,7 +57,6 @@ class BulldozerFilling(DsmFilling, short_name="bulldozer"):
57
57
 
58
58
  # check conf
59
59
  self.used_method = self.used_config["method"]
60
- self.activated = self.used_config["activated"]
61
60
  self.classification = self.used_config["classification"]
62
61
  self.save_intermediate_data = self.used_config["save_intermediate_data"]
63
62
 
@@ -72,7 +71,6 @@ class BulldozerFilling(DsmFilling, short_name="bulldozer"):
72
71
 
73
72
  # Overload conf
74
73
  overloaded_conf["method"] = conf.get("method", "bulldozer")
75
- overloaded_conf["activated"] = conf.get("activated", False)
76
74
  overloaded_conf["classification"] = conf.get("classification", "nodata")
77
75
 
78
76
  if isinstance(overloaded_conf["classification"], str):
@@ -86,7 +84,6 @@ class BulldozerFilling(DsmFilling, short_name="bulldozer"):
86
84
 
87
85
  rectification_schema = {
88
86
  "method": str,
89
- "activated": bool,
90
87
  "classification": Or(None, [str]),
91
88
  "save_intermediate_data": bool,
92
89
  }
@@ -98,7 +95,7 @@ class BulldozerFilling(DsmFilling, short_name="bulldozer"):
98
95
  return overloaded_conf
99
96
 
100
97
  @cars_profile(name="Bulldozer filling")
101
- def run( # noqa C901
98
+ def run( # pylint: disable=too-many-positional-arguments # noqa C901
102
99
  self,
103
100
  dsm_file,
104
101
  classif_file,
@@ -119,9 +116,6 @@ class BulldozerFilling(DsmFilling, short_name="bulldozer"):
119
116
  - a Shapely Polygon
120
117
  """
121
118
 
122
- if not self.activated:
123
- return None
124
-
125
119
  if self.classification is None:
126
120
  self.classification = ["nodata"]
127
121
 
@@ -206,7 +200,7 @@ class BulldozerFilling(DsmFilling, short_name="bulldozer"):
206
200
  ):
207
201
  dsm_to_dtm(bull_conf_path)
208
202
  except Exception:
209
- logging.error(
203
+ logging.warning(
210
204
  "Bulldozer failed on its second execution."
211
205
  + " The DSM could not be filled."
212
206
  )