cars 1.0.0a3__cp311-cp311-win_amd64.whl → 1.0.0a4__cp311-cp311-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.cp311-win_amd64.dll.a +0 -0
  19. cars/applications/dense_matching/cpp/dense_matching_cpp.cp311-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.cp311-win_amd64.dll.a +0 -0
  116. cars/applications/dense_match_filling/cpp/dense_match_filling_cpp.cp311-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
@@ -27,8 +27,6 @@ Contains functions for array reconstructions and crop for multiple tiles
27
27
  import numpy as np
28
28
  import xarray as xr
29
29
 
30
- import cars.core.constants as cst
31
-
32
30
 
33
31
  def reconstruct_data(tiles, window, overlap): # noqa: C901
34
32
  """
@@ -186,104 +184,3 @@ def reconstruct_data(tiles, window, overlap): # noqa: C901
186
184
  dims=dims,
187
185
  ).astype(data_type)
188
186
  return new_dataset, row_min - ol_row_min, col_min - ol_col_min
189
-
190
-
191
- def find_tile_dataset(corresponding_tiles, window):
192
- """
193
- Find the dataset corresponding to window, in the list of tiles.
194
-
195
- :param corresponding_tiles: list of tiles
196
- :type corresponding_tiles: list(tuple)
197
- :param window: window of base tile [row min, row max, col min col max]
198
- :type window: list
199
-
200
- :return: dataset corresponding to window
201
- :rtype: xr.Dataset
202
-
203
- """
204
-
205
- dataset = None
206
- for tile_window, _, tile_dataset in corresponding_tiles:
207
- if tuple(tile_window) == tuple(window):
208
- dataset = tile_dataset
209
-
210
- return dataset
211
-
212
-
213
- def crop_dataset(full_dataset, in_dataset, window, overlap, row_min, col_min):
214
- """
215
- Crop full dataset to fit with a given tile dataset
216
-
217
- :param full_dataset: Combined dataset
218
- :type full_dataset: xr.Dataset
219
- :param in_dataset: dataset to use as template dataset
220
- :type in_dataset: xr.Dataset
221
- :param window: window of base tile [row min, row max, col min col max]
222
- :type window: list
223
- :param overlap: overlap of base tile [row min, row max, col min col max]
224
- :type overlap: list
225
- :param row_min: position of row min in full image
226
- :type row_min: int
227
- :param col_min: position of col min in full image
228
- :type col_min: int
229
-
230
- :return: cropped dataset
231
- :rtype: xr.Dataset
232
-
233
- """
234
- cropped = xr.Dataset()
235
-
236
- coords = {}
237
- if cst.ROW in in_dataset:
238
- coords[cst.ROW] = in_dataset.coords[cst.ROW]
239
- if cst.COL in in_dataset:
240
- coords[cst.COL] = in_dataset.coords[cst.COL]
241
-
242
- list_tags = list(full_dataset.keys())
243
-
244
- for tag in list_tags:
245
- full_data = full_dataset[tag].values
246
-
247
- offset_row = int(window[0] - overlap[0] - row_min)
248
- offset_col = int(window[2] - overlap[2] - col_min)
249
-
250
- if len(full_data.shape) == 2:
251
- nb_row = int(in_dataset[tag].values.shape[0])
252
- nb_col = int(in_dataset[tag].values.shape[1])
253
-
254
- values = np.ascontiguousarray(
255
- full_data[
256
- offset_row : offset_row + nb_row,
257
- offset_col : offset_col + nb_col,
258
- ]
259
- )
260
-
261
- else:
262
- nb_row = int(in_dataset[tag].values.shape[1])
263
- nb_col = int(in_dataset[tag].values.shape[2])
264
-
265
- values = np.ascontiguousarray(
266
- full_data[
267
- :,
268
- offset_row : offset_row + nb_row,
269
- offset_col : offset_col + nb_col,
270
- ]
271
- )
272
-
273
- xarray_coords = {}
274
- for coord_tag in full_dataset[tag].coords:
275
- if coord_tag in coords:
276
- xarray_coords[coord_tag] = coords[coord_tag]
277
- else:
278
- xarray_coords[coord_tag] = full_dataset[tag].coords[coord_tag]
279
-
280
- dims = full_dataset[tag].dims
281
-
282
- data_array = xr.DataArray(values, coords=xarray_coords, dims=dims)
283
- if tag in in_dataset:
284
- data_array.attrs = in_dataset[tag].attrs
285
- cropped[tag] = data_array
286
-
287
- cropped.attrs = in_dataset.attrs
288
-
289
- return cropped
@@ -30,9 +30,12 @@ import math
30
30
  import numpy as np
31
31
  import xarray as xr
32
32
 
33
+ from cars.orchestrator.cluster.log_wrapper import cars_profile
34
+
33
35
  # CARS imports
34
36
 
35
37
 
38
+ @cars_profile(name="Grid margins 2 overlaps", interval=0.5)
36
39
  def grid_margins_2_overlaps(grid, margins_fun):
37
40
  """
38
41
  Convert margins to overlap grid format used in CarsDatasets
@@ -48,7 +51,7 @@ def grid_margins_2_overlaps(grid, margins_fun):
48
51
 
49
52
  """
50
53
 
51
- def fill_overlap(
54
+ def fill_overlap( # pylint: disable=too-many-positional-arguments
52
55
  cars_ds_overlaps,
53
56
  margins,
54
57
  row_up,
cars/devibrate.py CHANGED
@@ -95,7 +95,7 @@ def acquisition_direction(
95
95
  return time_direction_vector, vec1, vec2
96
96
 
97
97
 
98
- def get_time_ground_direction(
98
+ def get_time_ground_direction( # pylint: disable=too-many-positional-arguments
99
99
  sensor,
100
100
  geomodel,
101
101
  geometry_plugin,
@@ -180,6 +180,7 @@ def project_coordinates_on_line(
180
180
  return dist_to_origin * np.cos(proj_angle)
181
181
 
182
182
 
183
+ # pylint: disable=too-many-positional-arguments
183
184
  def lowres_initial_dem_splines_fit(
184
185
  lowres_dsm_from_matches: xr.Dataset,
185
186
  lowres_initial_dem: xr.Dataset,
@@ -379,7 +380,7 @@ def read_lowres_dsm(srtm_path, startx, starty, endx, endy):
379
380
  return dsm_as_ds, newstartx, newstarty, sizex, sizey, resolution
380
381
 
381
382
 
382
- def compute_splines(
383
+ def compute_splines( # pylint: disable=too-many-positional-arguments
383
384
  sensor1,
384
385
  geomodel1,
385
386
  sensor2,
@@ -507,7 +508,7 @@ def compute_splines(
507
508
  }
508
509
 
509
510
 
510
- def cars_devibrate(
511
+ def cars_devibrate( # pylint: disable=too-many-positional-arguments
511
512
  used_conf,
512
513
  srtm_path,
513
514
  geoid_path,
@@ -594,6 +595,8 @@ def cars_devibrate(
594
595
  )
595
596
 
596
597
  x_values_2d, y_values_2d = np.meshgrid(x_values_1d, y_values_1d)
598
+
599
+ positions = None
597
600
  if src.crs != "EPSG:4326":
598
601
  transformer = pyproj.Transformer.from_crs(
599
602
  src.crs, "EPSG:4326", always_xy=True
cars/extractroi.py CHANGED
@@ -29,6 +29,7 @@ import numpy as np
29
29
  import rasterio as rio
30
30
  from affine import Affine
31
31
  from shapely.geometry import box
32
+ from shareloc.geomodels.rpc_writers import write_rio_rpc_as_rpb
32
33
 
33
34
 
34
35
  def is_bbx_in_image(bbx, image_dataset):
@@ -75,7 +76,7 @@ def get_slices_from_bbx(image_dataset, bbx, rpc_options):
75
76
 
76
77
 
77
78
  def process_image_file(
78
- bbx, input_image_path, output_image_path, geom_file_path, rpc_options
79
+ bbx, input_image_path, output_image_path, rpb_file_path, rpc_options
79
80
  ):
80
81
  """
81
82
  Processes an image file by extracting a region based on the given geometry.
@@ -84,7 +85,7 @@ def process_image_file(
84
85
  region_geometry (dict): GeoJSON-like dictionary defining the region.
85
86
  input_image_path (str): Path to the input image file.
86
87
  output_image_path (str): Path to save the output image.
87
- geom_file_path (str): Path to save the .geom file.
88
+ rpb_file_path (str): Path to save the .RPB file.
88
89
  rpc_options (dict): Options for GDALCreateRPCTransformer.
89
90
  """
90
91
 
@@ -110,7 +111,8 @@ def process_image_file(
110
111
  # copy rpc
111
112
  dst.rpcs = image_dataset.rpcs
112
113
 
113
- create_geom_file(image_dataset, geom_file_path)
114
+ if rpb_file_path is not None:
115
+ create_rpb_file(image_dataset, rpb_file_path)
114
116
 
115
117
 
116
118
  def get_human_readable_bbox(image_dataset, rpc_options):
@@ -167,34 +169,23 @@ def validate_bounding_box(bbx, image_dataset, rpc_options):
167
169
  image_dataset, rpc_options
168
170
  )
169
171
  raise ValueError(
170
- f"Coordinates must between "
172
+ f"Coordinates must be between "
171
173
  f"({min_x}, {min_y}) and ({max_x}, {max_y})"
172
174
  )
173
175
 
174
176
 
175
- def create_geom_file(image_dataset, geom_filename):
177
+ def create_rpb_file(image_dataset, rpb_filename):
176
178
  """
177
- Create and save a .geom file from a rasterio dataset
179
+ Create and save a .RPB file from a rasterio dataset
178
180
 
179
181
  Parameters:
180
182
  image_dataset (rio.DatasetReader): Opened image dataset.
181
- geom_filename (str): Path to save the .geom file.
183
+ rpb_filename (str): Path to save the .RPB file.
182
184
  """
183
185
  if not image_dataset.rpcs:
184
186
  raise ValueError("Image dataset has no RPCs")
185
187
  rpcs_as_dict = image_dataset.rpcs.to_dict()
186
- with open(geom_filename, "w", encoding="utf-8") as writer:
187
- for key in rpcs_as_dict:
188
- if isinstance(rpcs_as_dict[key], list):
189
- for idx, coef in enumerate(rpcs_as_dict[key]):
190
- writer.write(key + "_%02d" % idx + ": " + str(coef))
191
- writer.write("\n")
192
- else:
193
- writer.write(key + ": " + str(rpcs_as_dict[key]))
194
- writer.write("\n")
195
-
196
- writer.write("type: ossimRpcModel\n")
197
- writer.write("polynomial_format: B\n")
188
+ write_rio_rpc_as_rpb(rpcs_as_dict, rpb_filename)
198
189
 
199
190
 
200
191
  def main():
@@ -240,6 +231,12 @@ def main():
240
231
  help="Digital Elevation Model used for projection",
241
232
  )
242
233
 
234
+ parser.add_argument(
235
+ "--generate_rpb",
236
+ action="store_true",
237
+ help="Generate RPB file",
238
+ )
239
+
243
240
  args = parser.parse_args()
244
241
  if not os.path.exists(args.out):
245
242
  os.makedirs(args.out)
@@ -254,10 +251,12 @@ def main():
254
251
  # check first input in list to determine pipeline
255
252
  for idx, image_path in enumerate(args.il):
256
253
  output_image_path = os.path.join(args.out, "ext_%03d.tif" % idx)
257
- geom_file_path = os.path.splitext(output_image_path)[0] + ".geom"
254
+ rpb_file_path = None
255
+ if args.generate_rpb:
256
+ rpb_file_path = os.path.splitext(output_image_path)[0] + ".RPB"
258
257
 
259
258
  process_image_file(
260
- args.bbx, image_path, output_image_path, geom_file_path, rpc_options
259
+ args.bbx, image_path, output_image_path, rpb_file_path, rpc_options
261
260
  )
262
261
 
263
262
 
@@ -48,8 +48,13 @@ class AbstractCluster(metaclass=ABCMeta):
48
48
  # cluster mode output directory
49
49
  out_dir: str
50
50
 
51
- def __new__( # pylint: disable=W0613
52
- cls, conf_cluster, out_dir, launch_worker=True, data_to_propagate=None
51
+ def __new__( # pylint: disable=too-many-positional-arguments
52
+ cls,
53
+ conf_cluster,
54
+ out_dir,
55
+ log_dir,
56
+ launch_worker=True,
57
+ data_to_propagate=None,
53
58
  ):
54
59
  """
55
60
  Return the required cluster
@@ -98,8 +103,13 @@ class AbstractCluster(metaclass=ABCMeta):
98
103
 
99
104
  return decorator
100
105
 
101
- def __init__(
102
- self, conf_cluster, out_dir, launch_worker=True, data_to_propagate=None
106
+ def __init__( # pylint: disable=too-many-positional-arguments
107
+ self,
108
+ conf_cluster,
109
+ out_dir,
110
+ log_dir,
111
+ launch_worker=True,
112
+ data_to_propagate=None,
103
113
  ): # pylint: disable=W0613
104
114
  """
105
115
  Init function of AbstractCluster
@@ -114,7 +124,7 @@ class AbstractCluster(metaclass=ABCMeta):
114
124
  # data to propagate
115
125
  self.data_to_propagate = data_to_propagate
116
126
 
117
- self.worker_log_dir = os.path.join(out_dir, "logs", "workers_log")
127
+ self.worker_log_dir = os.path.join(log_dir, "workers_log")
118
128
  if not os.path.exists(self.worker_log_dir):
119
129
  os.makedirs(self.worker_log_dir)
120
130
 
@@ -56,7 +56,7 @@ class AbstractDaskCluster(
56
56
  AbstractDaskCluster
57
57
  """
58
58
 
59
- def __init__(self, conf_cluster, out_dir, launch_worker=True):
59
+ def __init__(self, conf_cluster, out_dir, log_dir, launch_worker=True):
60
60
  """
61
61
  Init function of AbstractDaskCluster
62
62
 
@@ -64,8 +64,12 @@ class AbstractDaskCluster(
64
64
 
65
65
  """
66
66
 
67
+ print("cluster log_dir", log_dir)
68
+
67
69
  # call parent init
68
- super().__init__(conf_cluster, out_dir, launch_worker=launch_worker)
70
+ super().__init__(
71
+ conf_cluster, out_dir, log_dir, launch_worker=launch_worker
72
+ )
69
73
  # retrieve parameters
70
74
  self.nb_workers = self.checked_conf_cluster["nb_workers"]
71
75
  self.task_timeout = self.checked_conf_cluster["task_timeout"]
@@ -30,7 +30,7 @@ import warnings
30
30
  from datetime import timedelta
31
31
 
32
32
 
33
- def init_cluster_variables(
33
+ def init_cluster_variables( # pylint: disable=too-many-positional-arguments
34
34
  nb_workers,
35
35
  walltime,
36
36
  out_dir,
@@ -26,6 +26,7 @@ Contains functions for wrapper logs
26
26
  import copy
27
27
  import cProfile
28
28
  import datetime
29
+ import functools
29
30
  import gc
30
31
  import io
31
32
  import logging
@@ -44,6 +45,8 @@ import numpy as np
44
45
  import pandas as pd
45
46
  import psutil
46
47
  from json_checker import Checker
48
+ from matplotlib.backends.backend_pdf import PdfPages
49
+ from PIL import Image
47
50
 
48
51
  from cars.core import cars_logging
49
52
  from cars.core.utils import safe_makedirs
@@ -496,11 +499,37 @@ def log_delta_memory(func, memory_start, memory_end):
496
499
  log_message(func, message)
497
500
 
498
501
 
502
+ def exception_safe(func):
503
+ """
504
+ Decorator for consistent exception handling in profiling functions
505
+
506
+ :param func: function to wrap
507
+ :return: wrapped function
508
+ """
509
+
510
+ @functools.wraps(func)
511
+ def wrapper(*args, **kwargs):
512
+ """
513
+ Catch error
514
+ """
515
+ try:
516
+ return func(*args, **kwargs)
517
+ except Exception as exc:
518
+ error_msg = (
519
+ f"Error in {func.__name__}: {type(exc).__name__}: {str(exc)}"
520
+ )
521
+ logging.error(error_msg)
522
+ cars_logging.add_profiling_message(f"ERROR - {error_msg}")
523
+ return None
524
+
525
+ return wrapper
526
+
527
+
528
+ @exception_safe
499
529
  def generate_summary(out_dir, used_conf, clean_worker_logs=False):
500
530
  """
501
531
  Generate Profiling summary
502
532
  """
503
-
504
533
  nb_workers = 1
505
534
  if "orchestrator" not in used_conf:
506
535
  first_key = next(iter(used_conf))
@@ -510,16 +539,15 @@ def generate_summary(out_dir, used_conf, clean_worker_logs=False):
510
539
  if "nb_workers" in used_conf["orchestrator"]:
511
540
  nb_workers = used_conf["orchestrator"]["nb_workers"]
512
541
 
513
- workers_log_dir = os.path.join(out_dir, "logs", "workers_log")
542
+ workers_log_dir = os.path.join(out_dir, "workers_log")
543
+ os.makedirs(workers_log_dir, exist_ok=True)
514
544
 
515
545
  log_file_main = os.path.join(
516
546
  workers_log_dir,
517
547
  "profiling.log",
518
548
  )
519
549
 
520
- out_profiling_main = os.path.join(
521
- out_dir, "logs", "profiling", "profiling.log"
522
- )
550
+ out_profiling_main = os.path.join(out_dir, "profiling", "profiling.log")
523
551
 
524
552
  log_files = [log_file_main, out_profiling_main]
525
553
 
@@ -640,7 +668,7 @@ def generate_summary(out_dir, used_conf, clean_worker_logs=False):
640
668
  cars_logging.add_profiling_message(message)
641
669
 
642
670
  # Generate png
643
- _, axs = plt.subplots(4, 2, figsize=(15, 15), layout="tight")
671
+ _, axs = plt.subplots(3, 2, figsize=(20, 20), layout="tight")
644
672
  # Fill
645
673
 
646
674
  generate_boxplot(
@@ -689,6 +717,14 @@ def generate_summary(out_dir, used_conf, clean_worker_logs=False):
689
717
  "calls",
690
718
  )
691
719
 
720
+ # file_name
721
+ profiling_plot = os.path.join(
722
+ out_dir,
723
+ "profiling",
724
+ "profiling_plots_histograms.png",
725
+ )
726
+ plt.savefig(profiling_plot)
727
+
692
728
  # Pie chart
693
729
 
694
730
  (name_task_workers, summary_workers) = filter_lists(
@@ -704,7 +740,7 @@ def generate_summary(out_dir, used_conf, clean_worker_logs=False):
704
740
  )
705
741
 
706
742
  (_, [pipeline_time]) = filter_lists(
707
- summary_names, summary_total_time, lambda name: "pipeline" in name
743
+ summary_names, summary_total_time, lambda name: "unit_pipeline" in name
708
744
  )
709
745
 
710
746
  (_, [multiprocessing_time]) = filter_lists(
@@ -717,8 +753,10 @@ def generate_summary(out_dir, used_conf, clean_worker_logs=False):
717
753
 
718
754
  total_time_workers = nb_workers * multiprocessing_time
719
755
 
756
+ _, axs2 = plt.subplots(2, 1, figsize=(40, 40), layout="tight")
757
+
720
758
  generate_pie_chart(
721
- axs.flat[6],
759
+ axs2.flat[0],
722
760
  name_task_workers,
723
761
  100 * np.array(summary_workers) / total_time_workers,
724
762
  "Total time in parallel tasks ({} workers) : {}".format(
@@ -728,7 +766,7 @@ def generate_summary(out_dir, used_conf, clean_worker_logs=False):
728
766
  )
729
767
 
730
768
  generate_pie_chart(
731
- axs.flat[7],
769
+ axs2.flat[1],
732
770
  name_task_main,
733
771
  100 * np.array(summary_main) / sequential_time,
734
772
  "Total time in sequential tasks : {}".format(
@@ -736,19 +774,103 @@ def generate_summary(out_dir, used_conf, clean_worker_logs=False):
736
774
  ),
737
775
  )
738
776
 
739
- # file_name
740
- profiling_plot = os.path.join(
777
+ profiling_plot2 = os.path.join(
741
778
  out_dir,
742
- "logs",
743
779
  "profiling",
744
- "profiling_plots.png",
780
+ "profiling_plots_pie_chart.png",
745
781
  )
746
- plt.savefig(profiling_plot)
782
+ plt.savefig(profiling_plot2)
747
783
 
748
784
  if clean_worker_logs and os.path.exists(workers_log_dir):
749
785
  shutil.rmtree(workers_log_dir)
750
786
 
751
787
 
788
+ def generate_pdf_profiling(log_dir):
789
+ """
790
+ Generate PDF profiling summary for all res
791
+ """
792
+
793
+ pages_data = {}
794
+ resolutions = []
795
+
796
+ for item in os.listdir(log_dir):
797
+ item_path = os.path.join(log_dir, item)
798
+ if os.path.isdir(item_path) and item.startswith("res"):
799
+ # Get resolution
800
+ res = int(item[4:])
801
+ resolutions.append(res)
802
+
803
+ # Add paths
804
+ pages_data[res] = {
805
+ "function_profiling_histo": os.path.join(
806
+ item_path, "profiling", "profiling_plots_histo.png"
807
+ ),
808
+ "function_profiling_pie_chart": os.path.join(
809
+ item_path, "profiling", "profiling_plots_pie_chart.png"
810
+ ),
811
+ "global_profiling": os.path.join(
812
+ item_path, "profiling", "memory_profiling.png"
813
+ ),
814
+ }
815
+
816
+ # ordered resolutions
817
+ resolutions.sort(reverse=True)
818
+
819
+ # Build pdf
820
+ pdf_path = os.path.join(log_dir, "profiling_summary.pdf")
821
+
822
+ with PdfPages(pdf_path) as pdf:
823
+ for res in resolutions:
824
+ # function_profiling
825
+ if os.path.exists(pages_data[res]["function_profiling_histo"]):
826
+ img = Image.open(pages_data[res]["function_profiling_histo"])
827
+ fig, axis = plt.subplots(1, 1, figsize=(11.69, 8.27), dpi=300)
828
+ axis.imshow(img, interpolation="none")
829
+ axis.set_title(
830
+ f"Function Profiling Histograms - "
831
+ f"Epipolar Resolution {res}",
832
+ fontsize=16,
833
+ fontweight="bold",
834
+ )
835
+ axis.axis("off")
836
+ plt.subplots_adjust(left=0, right=1, top=0.95, bottom=0)
837
+ pdf.savefig(fig, bbox_inches="tight", dpi=300)
838
+ plt.close(fig)
839
+
840
+ if os.path.exists(pages_data[res]["function_profiling_pie_chart"]):
841
+ img = Image.open(
842
+ pages_data[res]["function_profiling_pie_chart"]
843
+ )
844
+ fig, axis = plt.subplots(1, 1, figsize=(11.69, 8.27), dpi=300)
845
+ axis.imshow(img, interpolation="none")
846
+ axis.set_title(
847
+ f"Function Profiling Pie Chart - Epipolar Resolution {res}",
848
+ fontsize=16,
849
+ fontweight="bold",
850
+ )
851
+ axis.axis("off")
852
+ plt.subplots_adjust(left=0, right=1, top=0.95, bottom=0)
853
+ pdf.savefig(fig, bbox_inches="tight", dpi=300)
854
+ plt.close(fig)
855
+
856
+ # global_profiling
857
+ if os.path.exists(pages_data[res]["global_profiling"]):
858
+ img = Image.open(pages_data[res]["global_profiling"])
859
+ fig, axis = plt.subplots(1, 1, figsize=(11.69, 8.27), dpi=300)
860
+ axis.imshow(img, interpolation="none")
861
+ axis.set_title(
862
+ f"Global Profiling - Epipolar Resolution {res}",
863
+ fontsize=16,
864
+ fontweight="bold",
865
+ )
866
+ axis.axis("off")
867
+ plt.subplots_adjust(left=0, right=1, top=0.95, bottom=0)
868
+ pdf.savefig(fig, bbox_inches="tight", dpi=300)
869
+ plt.close(fig)
870
+
871
+ logging.info("PDF profiling summary generated: {}".format(pdf_path))
872
+
873
+
752
874
  def filter_lists(names, data, cond):
753
875
  """
754
876
  Filter lists with condition on name
@@ -776,7 +898,7 @@ def generate_boxplot(axis, names, data_full, title, data_type):
776
898
  axis.set_title(title)
777
899
 
778
900
 
779
- def generate_histo(
901
+ def generate_histo( # pylint: disable=too-many-positional-arguments
780
902
  axis, names, data, title, data_type, data_min_err=None, data_max_err=None
781
903
  ):
782
904
  """
@@ -815,8 +937,14 @@ def generate_pie_chart(axis, names, data, title):
815
937
  data.append(others)
816
938
  names.append("other")
817
939
 
818
- axis.pie(data, labels=names, autopct="%1.1f%%")
819
- axis.set_title(title)
940
+ axis.pie(
941
+ data,
942
+ labels=names,
943
+ autopct="%1.1f%%",
944
+ labeldistance=1.1,
945
+ textprops={"fontsize": 30},
946
+ )
947
+ axis.set_title(title, fontsize=40)
820
948
 
821
949
 
822
950
  def cars_profile(name=None, interval=0.1):
@@ -926,15 +1054,14 @@ class CarsMemProf(Thread):
926
1054
  # Get memory
927
1055
  current_mem = self.process.memory_info().rss
928
1056
 
929
- if current_mem > max_mem:
930
- max_mem = current_mem
1057
+ max_mem = max(max_mem, current_mem)
931
1058
 
932
1059
  # Get cpu max
933
1060
  current_cpu = self.process.cpu_percent(
934
1061
  interval=self.cpu_interval
935
1062
  )
936
- if current_cpu > max_cpu:
937
- max_cpu = current_cpu
1063
+
1064
+ max_cpu = max(max_cpu, current_cpu)
938
1065
 
939
1066
  if stop:
940
1067
  break
@@ -83,8 +83,13 @@ class MultiprocessingCluster(abstract_cluster.AbstractCluster):
83
83
 
84
84
  # pylint: disable=too-many-instance-attributes
85
85
  @cars_profile(name="Multiprocessing orchestrator initialization")
86
- def __init__(
87
- self, conf_cluster, out_dir, launch_worker=True, data_to_propagate=None
86
+ def __init__( # pylint: disable=too-many-positional-arguments
87
+ self,
88
+ conf_cluster,
89
+ out_dir,
90
+ log_dir,
91
+ launch_worker=True,
92
+ data_to_propagate=None,
88
93
  ):
89
94
  """
90
95
  Init function of MultiprocessingCluster
@@ -102,10 +107,12 @@ class MultiprocessingCluster(abstract_cluster.AbstractCluster):
102
107
  logging.warning(message)
103
108
 
104
109
  self.out_dir = out_dir
110
+ self.log_dir = log_dir
105
111
  # call parent init
106
112
  super().__init__(
107
113
  conf_cluster,
108
114
  out_dir,
115
+ log_dir,
109
116
  launch_worker=launch_worker,
110
117
  data_to_propagate=data_to_propagate,
111
118
  )
@@ -202,7 +209,7 @@ class MultiprocessingCluster(abstract_cluster.AbstractCluster):
202
209
 
203
210
  self.profiler = MultiprocessingProfiler(
204
211
  self.pool,
205
- self.out_dir,
212
+ self.log_dir,
206
213
  self.checked_conf_cluster["max_ram_per_worker"],
207
214
  mp_dataframe=mp_dataframe,
208
215
  timer=timer,
@@ -459,6 +466,7 @@ class MultiprocessingCluster(abstract_cluster.AbstractCluster):
459
466
 
460
467
  return object_future
461
468
 
469
+ # pylint: disable=too-many-positional-arguments
462
470
  @staticmethod # noqa: C901
463
471
  def refresh_task_cache( # noqa: C901
464
472
  pool,