cars 1.0.0rc2__cp312-cp312-win_amd64.whl

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

Potentially problematic release.


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

Files changed (225) hide show
  1. cars/__init__.py +86 -0
  2. cars/applications/__init__.py +40 -0
  3. cars/applications/application.py +117 -0
  4. cars/applications/application_constants.py +29 -0
  5. cars/applications/application_template.py +146 -0
  6. cars/applications/auxiliary_filling/__init__.py +29 -0
  7. cars/applications/auxiliary_filling/abstract_auxiliary_filling_app.py +105 -0
  8. cars/applications/auxiliary_filling/auxiliary_filling_algo.py +475 -0
  9. cars/applications/auxiliary_filling/auxiliary_filling_from_sensors_app.py +632 -0
  10. cars/applications/auxiliary_filling/auxiliary_filling_wrappers.py +90 -0
  11. cars/applications/dem_generation/__init__.py +30 -0
  12. cars/applications/dem_generation/abstract_dem_generation_app.py +116 -0
  13. cars/applications/dem_generation/bulldozer_config/base_config.yaml +42 -0
  14. cars/applications/dem_generation/bulldozer_dem_app.py +641 -0
  15. cars/applications/dem_generation/bulldozer_memory.py +55 -0
  16. cars/applications/dem_generation/dem_generation_algo.py +107 -0
  17. cars/applications/dem_generation/dem_generation_constants.py +32 -0
  18. cars/applications/dem_generation/dem_generation_wrappers.py +323 -0
  19. cars/applications/dense_match_filling/__init__.py +30 -0
  20. cars/applications/dense_match_filling/abstract_dense_match_filling_app.py +242 -0
  21. cars/applications/dense_match_filling/fill_disp_algo.py +113 -0
  22. cars/applications/dense_match_filling/fill_disp_constants.py +39 -0
  23. cars/applications/dense_match_filling/fill_disp_wrappers.py +83 -0
  24. cars/applications/dense_match_filling/zero_padding_app.py +302 -0
  25. cars/applications/dense_matching/__init__.py +30 -0
  26. cars/applications/dense_matching/abstract_dense_matching_app.py +261 -0
  27. cars/applications/dense_matching/census_mccnn_sgm_app.py +1461 -0
  28. cars/applications/dense_matching/cpp/__init__.py +0 -0
  29. cars/applications/dense_matching/cpp/dense_matching_cpp.cp312-win_amd64.dll.a +0 -0
  30. cars/applications/dense_matching/cpp/dense_matching_cpp.cp312-win_amd64.pyd +0 -0
  31. cars/applications/dense_matching/cpp/dense_matching_cpp.py +94 -0
  32. cars/applications/dense_matching/cpp/includes/dense_matching.hpp +58 -0
  33. cars/applications/dense_matching/cpp/meson.build +9 -0
  34. cars/applications/dense_matching/cpp/src/bindings.cpp +13 -0
  35. cars/applications/dense_matching/cpp/src/dense_matching.cpp +207 -0
  36. cars/applications/dense_matching/dense_matching_algo.py +401 -0
  37. cars/applications/dense_matching/dense_matching_constants.py +89 -0
  38. cars/applications/dense_matching/dense_matching_wrappers.py +951 -0
  39. cars/applications/dense_matching/disparity_grid_algo.py +597 -0
  40. cars/applications/dense_matching/loaders/__init__.py +23 -0
  41. cars/applications/dense_matching/loaders/config_census_sgm_default.json +31 -0
  42. cars/applications/dense_matching/loaders/config_census_sgm_homogeneous.json +30 -0
  43. cars/applications/dense_matching/loaders/config_census_sgm_mountain_and_vegetation.json +30 -0
  44. cars/applications/dense_matching/loaders/config_census_sgm_shadow.json +30 -0
  45. cars/applications/dense_matching/loaders/config_census_sgm_sparse.json +36 -0
  46. cars/applications/dense_matching/loaders/config_census_sgm_urban.json +30 -0
  47. cars/applications/dense_matching/loaders/config_mapping.json +13 -0
  48. cars/applications/dense_matching/loaders/config_mccnn.json +28 -0
  49. cars/applications/dense_matching/loaders/global_land_cover_map.tif +0 -0
  50. cars/applications/dense_matching/loaders/pandora_loader.py +593 -0
  51. cars/applications/dsm_filling/__init__.py +32 -0
  52. cars/applications/dsm_filling/abstract_dsm_filling_app.py +101 -0
  53. cars/applications/dsm_filling/border_interpolation_app.py +278 -0
  54. cars/applications/dsm_filling/bulldozer_config/base_config.yaml +44 -0
  55. cars/applications/dsm_filling/bulldozer_filling_app.py +288 -0
  56. cars/applications/dsm_filling/exogenous_filling_app.py +341 -0
  57. cars/applications/dsm_merging/__init__.py +28 -0
  58. cars/applications/dsm_merging/abstract_dsm_merging_app.py +101 -0
  59. cars/applications/dsm_merging/weighted_fusion_app.py +639 -0
  60. cars/applications/grid_correction/__init__.py +30 -0
  61. cars/applications/grid_correction/abstract_grid_correction_app.py +103 -0
  62. cars/applications/grid_correction/grid_correction_app.py +557 -0
  63. cars/applications/grid_generation/__init__.py +30 -0
  64. cars/applications/grid_generation/abstract_grid_generation_app.py +142 -0
  65. cars/applications/grid_generation/epipolar_grid_generation_app.py +327 -0
  66. cars/applications/grid_generation/grid_generation_algo.py +388 -0
  67. cars/applications/grid_generation/grid_generation_constants.py +46 -0
  68. cars/applications/grid_generation/transform_grid.py +88 -0
  69. cars/applications/ground_truth_reprojection/__init__.py +30 -0
  70. cars/applications/ground_truth_reprojection/abstract_ground_truth_reprojection_app.py +137 -0
  71. cars/applications/ground_truth_reprojection/direct_localization_app.py +629 -0
  72. cars/applications/ground_truth_reprojection/ground_truth_reprojection_algo.py +275 -0
  73. cars/applications/point_cloud_outlier_removal/__init__.py +30 -0
  74. cars/applications/point_cloud_outlier_removal/abstract_outlier_removal_app.py +385 -0
  75. cars/applications/point_cloud_outlier_removal/outlier_removal_algo.py +392 -0
  76. cars/applications/point_cloud_outlier_removal/outlier_removal_constants.py +43 -0
  77. cars/applications/point_cloud_outlier_removal/small_components_app.py +522 -0
  78. cars/applications/point_cloud_outlier_removal/statistical_app.py +528 -0
  79. cars/applications/rasterization/__init__.py +30 -0
  80. cars/applications/rasterization/abstract_pc_rasterization_app.py +183 -0
  81. cars/applications/rasterization/rasterization_algo.py +534 -0
  82. cars/applications/rasterization/rasterization_constants.py +38 -0
  83. cars/applications/rasterization/rasterization_wrappers.py +639 -0
  84. cars/applications/rasterization/simple_gaussian_app.py +1152 -0
  85. cars/applications/resampling/__init__.py +28 -0
  86. cars/applications/resampling/abstract_resampling_app.py +187 -0
  87. cars/applications/resampling/bicubic_resampling_app.py +760 -0
  88. cars/applications/resampling/resampling_algo.py +590 -0
  89. cars/applications/resampling/resampling_constants.py +36 -0
  90. cars/applications/resampling/resampling_wrappers.py +309 -0
  91. cars/applications/sensors_subsampling/__init__.py +32 -0
  92. cars/applications/sensors_subsampling/abstract_subsampling_app.py +109 -0
  93. cars/applications/sensors_subsampling/rasterio_subsampling_app.py +420 -0
  94. cars/applications/sensors_subsampling/subsampling_algo.py +108 -0
  95. cars/applications/sparse_matching/__init__.py +30 -0
  96. cars/applications/sparse_matching/abstract_sparse_matching_app.py +599 -0
  97. cars/applications/sparse_matching/sift_app.py +724 -0
  98. cars/applications/sparse_matching/sparse_matching_algo.py +360 -0
  99. cars/applications/sparse_matching/sparse_matching_constants.py +66 -0
  100. cars/applications/sparse_matching/sparse_matching_wrappers.py +282 -0
  101. cars/applications/triangulation/__init__.py +32 -0
  102. cars/applications/triangulation/abstract_triangulation_app.py +227 -0
  103. cars/applications/triangulation/line_of_sight_intersection_app.py +1243 -0
  104. cars/applications/triangulation/pc_transform.py +552 -0
  105. cars/applications/triangulation/triangulation_algo.py +371 -0
  106. cars/applications/triangulation/triangulation_constants.py +38 -0
  107. cars/applications/triangulation/triangulation_wrappers.py +259 -0
  108. cars/bundleadjustment.py +750 -0
  109. cars/cars.py +179 -0
  110. cars/conf/__init__.py +23 -0
  111. cars/conf/geoid/egm96.grd +0 -0
  112. cars/conf/geoid/egm96.grd.hdr +15 -0
  113. cars/conf/input_parameters.py +156 -0
  114. cars/conf/mask_cst.py +35 -0
  115. cars/core/__init__.py +23 -0
  116. cars/core/cars_logging.py +402 -0
  117. cars/core/constants.py +191 -0
  118. cars/core/constants_disparity.py +50 -0
  119. cars/core/datasets.py +140 -0
  120. cars/core/geometry/__init__.py +27 -0
  121. cars/core/geometry/abstract_geometry.py +1119 -0
  122. cars/core/geometry/shareloc_geometry.py +598 -0
  123. cars/core/inputs.py +568 -0
  124. cars/core/outputs.py +176 -0
  125. cars/core/preprocessing.py +722 -0
  126. cars/core/projection.py +843 -0
  127. cars/core/roi_tools.py +215 -0
  128. cars/core/tiling.py +774 -0
  129. cars/core/utils.py +164 -0
  130. cars/data_structures/__init__.py +23 -0
  131. cars/data_structures/cars_dataset.py +1544 -0
  132. cars/data_structures/cars_dict.py +74 -0
  133. cars/data_structures/corresponding_tiles_tools.py +186 -0
  134. cars/data_structures/dataframe_converter.py +185 -0
  135. cars/data_structures/format_transformation.py +297 -0
  136. cars/devibrate.py +689 -0
  137. cars/extractroi.py +264 -0
  138. cars/orchestrator/__init__.py +23 -0
  139. cars/orchestrator/achievement_tracker.py +125 -0
  140. cars/orchestrator/cluster/__init__.py +37 -0
  141. cars/orchestrator/cluster/abstract_cluster.py +250 -0
  142. cars/orchestrator/cluster/abstract_dask_cluster.py +381 -0
  143. cars/orchestrator/cluster/dask_cluster_tools.py +103 -0
  144. cars/orchestrator/cluster/dask_config/README.md +94 -0
  145. cars/orchestrator/cluster/dask_config/dask.yaml +21 -0
  146. cars/orchestrator/cluster/dask_config/distributed.yaml +70 -0
  147. cars/orchestrator/cluster/dask_config/jobqueue.yaml +26 -0
  148. cars/orchestrator/cluster/dask_config/reference_confs/dask-schema.yaml +137 -0
  149. cars/orchestrator/cluster/dask_config/reference_confs/dask.yaml +26 -0
  150. cars/orchestrator/cluster/dask_config/reference_confs/distributed-schema.yaml +1009 -0
  151. cars/orchestrator/cluster/dask_config/reference_confs/distributed.yaml +273 -0
  152. cars/orchestrator/cluster/dask_config/reference_confs/jobqueue.yaml +212 -0
  153. cars/orchestrator/cluster/dask_jobqueue_utils.py +204 -0
  154. cars/orchestrator/cluster/local_dask_cluster.py +116 -0
  155. cars/orchestrator/cluster/log_wrapper.py +728 -0
  156. cars/orchestrator/cluster/mp_cluster/__init__.py +27 -0
  157. cars/orchestrator/cluster/mp_cluster/mp_factorizer.py +212 -0
  158. cars/orchestrator/cluster/mp_cluster/mp_objects.py +535 -0
  159. cars/orchestrator/cluster/mp_cluster/mp_tools.py +93 -0
  160. cars/orchestrator/cluster/mp_cluster/mp_wrapper.py +505 -0
  161. cars/orchestrator/cluster/mp_cluster/multiprocessing_cluster.py +986 -0
  162. cars/orchestrator/cluster/mp_cluster/multiprocessing_profiler.py +399 -0
  163. cars/orchestrator/cluster/pbs_dask_cluster.py +207 -0
  164. cars/orchestrator/cluster/sequential_cluster.py +139 -0
  165. cars/orchestrator/cluster/slurm_dask_cluster.py +234 -0
  166. cars/orchestrator/memory_tools.py +47 -0
  167. cars/orchestrator/orchestrator.py +755 -0
  168. cars/orchestrator/orchestrator_constants.py +29 -0
  169. cars/orchestrator/registry/__init__.py +23 -0
  170. cars/orchestrator/registry/abstract_registry.py +143 -0
  171. cars/orchestrator/registry/compute_registry.py +106 -0
  172. cars/orchestrator/registry/id_generator.py +116 -0
  173. cars/orchestrator/registry/replacer_registry.py +213 -0
  174. cars/orchestrator/registry/saver_registry.py +363 -0
  175. cars/orchestrator/registry/unseen_registry.py +118 -0
  176. cars/orchestrator/tiles_profiler.py +279 -0
  177. cars/pipelines/__init__.py +26 -0
  178. cars/pipelines/conf_resolution/conf_final_resolution.yaml +5 -0
  179. cars/pipelines/conf_resolution/conf_first_resolution.yaml +4 -0
  180. cars/pipelines/conf_resolution/conf_intermediate_resolution.yaml +2 -0
  181. cars/pipelines/default/__init__.py +26 -0
  182. cars/pipelines/default/default_pipeline.py +1088 -0
  183. cars/pipelines/filling/__init__.py +26 -0
  184. cars/pipelines/filling/filling.py +981 -0
  185. cars/pipelines/formatting/__init__.py +26 -0
  186. cars/pipelines/formatting/formatting.py +186 -0
  187. cars/pipelines/merging/__init__.py +26 -0
  188. cars/pipelines/merging/merging.py +439 -0
  189. cars/pipelines/parameters/__init__.py +0 -0
  190. cars/pipelines/parameters/advanced_parameters.py +256 -0
  191. cars/pipelines/parameters/advanced_parameters_constants.py +68 -0
  192. cars/pipelines/parameters/application_parameters.py +72 -0
  193. cars/pipelines/parameters/depth_map_inputs.py +0 -0
  194. cars/pipelines/parameters/dsm_inputs.py +349 -0
  195. cars/pipelines/parameters/dsm_inputs_constants.py +25 -0
  196. cars/pipelines/parameters/output_constants.py +52 -0
  197. cars/pipelines/parameters/output_parameters.py +438 -0
  198. cars/pipelines/parameters/sensor_inputs.py +859 -0
  199. cars/pipelines/parameters/sensor_inputs_constants.py +51 -0
  200. cars/pipelines/parameters/sensor_loaders/__init__.py +29 -0
  201. cars/pipelines/parameters/sensor_loaders/basic_classif_loader.py +86 -0
  202. cars/pipelines/parameters/sensor_loaders/basic_image_loader.py +98 -0
  203. cars/pipelines/parameters/sensor_loaders/pivot_classif_loader.py +90 -0
  204. cars/pipelines/parameters/sensor_loaders/pivot_image_loader.py +105 -0
  205. cars/pipelines/parameters/sensor_loaders/sensor_loader.py +93 -0
  206. cars/pipelines/parameters/sensor_loaders/sensor_loader_template.py +71 -0
  207. cars/pipelines/parameters/sensor_loaders/slurp_classif_loader.py +86 -0
  208. cars/pipelines/pipeline.py +119 -0
  209. cars/pipelines/pipeline_constants.py +38 -0
  210. cars/pipelines/pipeline_template.py +135 -0
  211. cars/pipelines/subsampling/__init__.py +26 -0
  212. cars/pipelines/subsampling/subsampling.py +358 -0
  213. cars/pipelines/surface_modeling/__init__.py +26 -0
  214. cars/pipelines/surface_modeling/surface_modeling.py +2098 -0
  215. cars/pipelines/tie_points/__init__.py +26 -0
  216. cars/pipelines/tie_points/tie_points.py +536 -0
  217. cars/starter.py +167 -0
  218. cars-1.0.0rc2.dist-info/DELVEWHEEL +2 -0
  219. cars-1.0.0rc2.dist-info/METADATA +289 -0
  220. cars-1.0.0rc2.dist-info/RECORD +225 -0
  221. cars-1.0.0rc2.dist-info/WHEEL +4 -0
  222. cars-1.0.0rc2.dist-info/entry_points.txt +8 -0
  223. cars.libs/libgcc_s_seh-1-b2494fcbd4d80cf2c98fdd5261f6d850.dll +0 -0
  224. cars.libs/libstdc++-6-e9b0d12ae0e9555bbae55e8dfd08c3f7.dll +0 -0
  225. cars.libs/libwinpthread-1-7882d1b093714ccdfaf4e0789a817792.dll +0 -0
@@ -0,0 +1,728 @@
1
+ #!/usr/bin/env python
2
+ # coding: utf8
3
+ #
4
+ # Copyright (c) 2020 Centre National d'Etudes Spatiales (CNES).
5
+ #
6
+ # This file is part of CARS
7
+ # (see https://github.com/CNES/cars).
8
+ #
9
+ # Licensed under the Apache License, Version 2.0 (the "License");
10
+ # you may not use this file except in compliance with the License.
11
+ # You may obtain a copy of the License at
12
+ #
13
+ # http://www.apache.org/licenses/LICENSE-2.0
14
+ #
15
+ # Unless required by applicable law or agreed to in writing, software
16
+ # distributed under the License is distributed on an "AS IS" BASIS,
17
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18
+ # See the License for the specific language governing permissions and
19
+ # limitations under the License.
20
+ #
21
+ """
22
+ Contains functions for wrapper logs
23
+ """
24
+ # pylint: disable=C0302
25
+
26
+ import copy
27
+ import datetime
28
+ import functools
29
+ import gc
30
+ import logging
31
+ import os
32
+ import shutil
33
+ import time
34
+ from multiprocessing import Pipe
35
+ from threading import Thread
36
+
37
+ import matplotlib.pyplot as plt
38
+ import numpy as np
39
+ import pandas as pd
40
+ import psutil
41
+ from matplotlib.backends.backend_pdf import PdfPages
42
+ from PIL import Image
43
+
44
+ from cars.core import cars_logging
45
+
46
+ THREAD_TIMEOUT = 2
47
+
48
+
49
+ def log_function(*argv, **kwargs):
50
+ """
51
+ Create a wrapper for function running it
52
+
53
+ :param argv: args of func
54
+ :param kwargs: kwargs of func
55
+
56
+ :return: path to results
57
+ """
58
+ func = kwargs["fun_log_wrapper"]
59
+ loop_testing = kwargs["loop_testing"]
60
+ kwargs.pop("fun_log_wrapper")
61
+ kwargs.pop("loop_testing")
62
+
63
+ if loop_testing:
64
+ # Profile
65
+ res = cars_profile(name=func.__name__ + "_looped", interval=0.2)(
66
+ loop_function
67
+ )(argv, kwargs, func)
68
+ else:
69
+ res = cars_profile(interval=0.2)(func)(*argv, **kwargs)
70
+
71
+ return res
72
+
73
+
74
+ def log_message(func, message):
75
+ """
76
+ log profiling message
77
+
78
+ :param func : logged function
79
+ :param message : log message
80
+ """
81
+ cars_logging.add_profiling_message(message)
82
+ cars_logging.add_profiling_message(func.__module__)
83
+
84
+
85
+ def loop_function(argv, kwargs, func, nb_iteration=5):
86
+ """
87
+ generate a loop on each cluster function to eval possible leak
88
+
89
+ :param argv : input argv
90
+ :param kwargs : input kwargs
91
+ :param func : function to evaluation
92
+ :param nb_iteration (int, optional): number of the iteration loop.
93
+ :param Defaults to 5.
94
+
95
+ Returns:
96
+ _type_: result of the function
97
+ """
98
+ logging.info("{} {}".format(func.__module__, func.__name__.capitalize()))
99
+ argv_temp = copy.copy(argv)
100
+ kwargs_temp = copy.deepcopy(kwargs)
101
+ # execute sevral time the function to observe possible leaks
102
+ for k in range(1, nb_iteration):
103
+ logging.info("loop iteration {}".format(k))
104
+ func(*argv, **kwargs)
105
+ del argv
106
+ del kwargs
107
+ gc.collect()
108
+ argv = copy.deepcopy(argv_temp)
109
+ kwargs = copy.deepcopy(kwargs_temp)
110
+ return func(*argv, **kwargs)
111
+
112
+
113
+ def get_current_memory():
114
+ """
115
+ Get current memory of process
116
+
117
+ :return: memory
118
+ :rtype: float
119
+
120
+ """
121
+
122
+ # Use psutil to capture python process memory as well
123
+ process = psutil.Process(os.getpid())
124
+ process_memory = process.memory_info().rss
125
+
126
+ # Convert nbytes size for logger
127
+ process_memory = float(process_memory) / 1000000
128
+
129
+ return process_memory
130
+
131
+
132
+ def log_delta_memory(func, memory_start, memory_end):
133
+ """
134
+ Log memory infos
135
+
136
+ :param func: profiled function
137
+ :param memory_start: memory before the run of function
138
+ :type memory_start: float
139
+ :param memory_end: memory after the run of function
140
+ :type memory_end: float
141
+
142
+ """
143
+
144
+ message = "Memory before run: {}Mb, Memory after run: {}Mb".format(
145
+ str(memory_start), str(memory_end)
146
+ )
147
+
148
+ log_message(func, message)
149
+
150
+
151
+ def exception_safe(func):
152
+ """
153
+ Decorator for consistent exception handling in profiling functions
154
+
155
+ :param func: function to wrap
156
+ :return: wrapped function
157
+ """
158
+
159
+ @functools.wraps(func)
160
+ def wrapper(*args, **kwargs):
161
+ """
162
+ Catch error
163
+ """
164
+ try:
165
+ return func(*args, **kwargs)
166
+ except Exception as exc:
167
+ error_msg = (
168
+ f"Error in {func.__name__}: {type(exc).__name__}: {str(exc)}"
169
+ )
170
+ logging.error(error_msg)
171
+ cars_logging.add_profiling_message(f"ERROR - {error_msg}")
172
+ return None
173
+
174
+ return wrapper
175
+
176
+
177
+ @exception_safe
178
+ def generate_summary(
179
+ out_dir, used_conf, pipeline_name, clean_worker_logs=False
180
+ ):
181
+ """
182
+ Generate Profiling summary
183
+ """
184
+ nb_workers = 1
185
+ if "orchestrator" not in used_conf:
186
+ first_key = next(iter(used_conf))
187
+ if "nb_workers" in used_conf[first_key]["orchestrator"]:
188
+ nb_workers = used_conf[first_key]["orchestrator"]["nb_workers"]
189
+ else:
190
+ if "nb_workers" in used_conf["orchestrator"]:
191
+ nb_workers = used_conf["orchestrator"]["nb_workers"]
192
+
193
+ workers_log_dir = os.path.join(out_dir, "workers_log")
194
+ os.makedirs(workers_log_dir, exist_ok=True)
195
+
196
+ log_file_main = os.path.join(
197
+ workers_log_dir,
198
+ "profiling.log",
199
+ )
200
+
201
+ out_profiling_main = os.path.join(out_dir, "profiling", "profiling.log")
202
+
203
+ log_files = [log_file_main, out_profiling_main]
204
+
205
+ names = []
206
+ times = []
207
+ max_ram = []
208
+ start_ram = []
209
+ end_ram = []
210
+ max_cpu = []
211
+
212
+ for log_file in log_files:
213
+ if not os.path.exists(log_file):
214
+ logging.debug("{} log file does not exist".format(log_file))
215
+ return
216
+
217
+ with open(log_file, encoding="UTF-8") as file_desc:
218
+ for item in file_desc:
219
+ if "CarsProfiling" in item:
220
+ splited_items = item.split("%")
221
+ names.append(splited_items[1])
222
+ times.append(float(splited_items[3]))
223
+ max_ram.append(float(splited_items[5]))
224
+ start_ram.append(float(splited_items[7]))
225
+ end_ram.append(float(splited_items[9]))
226
+ max_cpu.append(float(splited_items[11]))
227
+
228
+ times_df = pd.DataFrame(
229
+ {
230
+ "name": names,
231
+ "time": times,
232
+ "max_ram": max_ram,
233
+ "start_ram": start_ram,
234
+ "end_ram": end_ram,
235
+ "max_cpu": max_cpu,
236
+ }
237
+ )
238
+
239
+ # Generate summary message
240
+ cars_logging.add_profiling_message(
241
+ "\n \n \n "
242
+ "----------------------------------------"
243
+ " SUMMARY PROFILLING "
244
+ "----------------------------------------"
245
+ " \n \n \n"
246
+ )
247
+
248
+ summary_names = []
249
+ summary_max_ram = []
250
+ summary_max_ram_err_min = []
251
+ summary_max_ram_err_max = []
252
+ summary_max_ram_relative = []
253
+ summary_mean_time_per_task = []
254
+ summary_mean_time_per_task_err_min = []
255
+ summary_mean_time_per_task_err_max = []
256
+ summary_total_time = []
257
+ summary_max_cpu = []
258
+ summary_nb_calls = []
259
+ full_max_ram = []
260
+ full_added_ram = []
261
+ full_time = []
262
+ full_max_cpu = []
263
+
264
+ for name in pd.unique(times_df["name"]):
265
+ current_df = times_df.loc[times_df["name"] == name]
266
+
267
+ current_med_time = current_df["time"].mean()
268
+ current_med_time_err_min = current_med_time - current_df["time"].min()
269
+ current_med_time_err_max = current_df["time"].max() - current_med_time
270
+ total_time = current_df["time"].sum()
271
+ max_ram = current_df["max_ram"].mean()
272
+ max_ram_err_min = max_ram - current_df["max_ram"].min()
273
+ max_ram_err_max = current_df["max_ram"].max() - max_ram
274
+ max_cpu = current_df["max_cpu"].max()
275
+ max_ram_without_start = (
276
+ current_df["max_ram"] - current_df["start_ram"]
277
+ ).max()
278
+ diff_end_start = (current_df["end_ram"] - current_df["start_ram"]).max()
279
+ nb_values = len(current_df)
280
+
281
+ # Fill lists with all data
282
+ full_max_ram.append(list(current_df["max_ram"]))
283
+ full_added_ram.append(
284
+ list(current_df["max_ram"] - current_df["start_ram"])
285
+ )
286
+ full_time.append(list(current_df["time"]))
287
+ full_max_cpu.append(list(current_df["max_cpu"]))
288
+
289
+ # fill lists for figures
290
+ summary_names.append(name)
291
+ summary_max_ram.append(max_ram)
292
+ summary_max_ram_err_min.append(max_ram_err_min)
293
+ summary_max_ram_err_max.append(max_ram_err_max)
294
+ summary_max_ram_relative.append(max_ram_without_start)
295
+ summary_mean_time_per_task.append(current_med_time)
296
+ summary_mean_time_per_task_err_min.append(current_med_time_err_min)
297
+ summary_mean_time_per_task_err_max.append(current_med_time_err_max)
298
+ summary_total_time.append(total_time)
299
+ summary_max_cpu.append(max_cpu)
300
+ summary_nb_calls.append(nb_values)
301
+
302
+ message = (
303
+ "Task {} ran {} times, with mean time {} sec, "
304
+ "total time: {} sec, Max cpu: {} %"
305
+ " max ram in process during task: {} MiB, "
306
+ "max ram - start ram: {}, "
307
+ " end - start ram : {}".format(
308
+ name,
309
+ nb_values,
310
+ current_med_time,
311
+ total_time,
312
+ max_cpu,
313
+ max_ram,
314
+ max_ram_without_start,
315
+ diff_end_start,
316
+ )
317
+ )
318
+
319
+ cars_logging.add_profiling_message(message)
320
+
321
+ # Generate png
322
+ _, axs = plt.subplots(3, 2, figsize=(20, 20), layout="tight")
323
+ # Fill
324
+
325
+ generate_boxplot(
326
+ axs.flat[0], summary_names, full_max_cpu, "Max CPU usage", "%"
327
+ )
328
+ generate_histo(
329
+ axs.flat[1], summary_names, summary_total_time, "Total Time", "s"
330
+ )
331
+
332
+ (
333
+ summary_names_without_pipeline,
334
+ total_full_time_without_pipeline,
335
+ ) = filter_lists(
336
+ summary_names,
337
+ full_time,
338
+ lambda name: "pipeline" not in name,
339
+ )
340
+ generate_boxplot(
341
+ axs.flat[2],
342
+ summary_names_without_pipeline,
343
+ total_full_time_without_pipeline,
344
+ "Time per task",
345
+ "s",
346
+ )
347
+
348
+ generate_boxplot(
349
+ axs.flat[3],
350
+ summary_names,
351
+ full_max_ram,
352
+ "Max RAM used",
353
+ "MiB",
354
+ )
355
+
356
+ generate_boxplot(
357
+ axs.flat[4],
358
+ summary_names,
359
+ full_added_ram,
360
+ "Max RAM added",
361
+ "MiB",
362
+ )
363
+ generate_histo(
364
+ axs.flat[5],
365
+ summary_names,
366
+ summary_nb_calls,
367
+ "NB calls",
368
+ "calls",
369
+ )
370
+
371
+ # file_name
372
+ profiling_plot = os.path.join(
373
+ out_dir,
374
+ "profiling",
375
+ "profiling_plots_histograms.png",
376
+ )
377
+ plt.savefig(profiling_plot)
378
+
379
+ # Pie chart
380
+
381
+ (name_task_workers, summary_workers) = filter_lists(
382
+ summary_names, summary_total_time, lambda name: "wrapper" in name
383
+ )
384
+
385
+ (name_task_main, summary_main) = filter_lists(
386
+ summary_names,
387
+ summary_total_time,
388
+ lambda name: "wrapper" not in name
389
+ and "pipeline" not in name
390
+ and "Compute futures" not in name,
391
+ )
392
+
393
+ (_, [pipeline_time]) = filter_lists(
394
+ summary_names,
395
+ summary_total_time,
396
+ lambda name: pipeline_name in name,
397
+ )
398
+
399
+ (_, [multiprocessing_time]) = filter_lists(
400
+ summary_names,
401
+ summary_total_time,
402
+ lambda name: "Compute futures" in name,
403
+ )
404
+
405
+ sequential_time = pipeline_time - multiprocessing_time
406
+
407
+ total_time_workers = nb_workers * multiprocessing_time
408
+
409
+ _, axs2 = plt.subplots(2, 1, figsize=(40, 40), layout="tight")
410
+
411
+ generate_pie_chart(
412
+ axs2.flat[0],
413
+ name_task_workers,
414
+ 100 * np.array(summary_workers) / total_time_workers,
415
+ "Total time in parallel tasks ({} workers) : {}".format(
416
+ nb_workers,
417
+ str(datetime.timedelta(seconds=int(multiprocessing_time))),
418
+ ),
419
+ )
420
+
421
+ generate_pie_chart(
422
+ axs2.flat[1],
423
+ name_task_main,
424
+ 100 * np.array(summary_main) / sequential_time,
425
+ "Total time in sequential tasks : {}".format(
426
+ str(datetime.timedelta(seconds=int(sequential_time)))
427
+ ),
428
+ )
429
+
430
+ profiling_plot2 = os.path.join(
431
+ out_dir,
432
+ "profiling",
433
+ "profiling_plots_pie_chart.png",
434
+ )
435
+ plt.savefig(profiling_plot2)
436
+
437
+ if clean_worker_logs and os.path.exists(workers_log_dir):
438
+ shutil.rmtree(workers_log_dir)
439
+
440
+
441
+ def generate_pdf_profiling(log_dir):
442
+ """
443
+ Generate PDF profiling summary for all res
444
+ """
445
+
446
+ pages_data = {}
447
+ resolutions = []
448
+
449
+ for item in os.listdir(log_dir):
450
+ item_path = os.path.join(log_dir, item)
451
+ if os.path.isdir(item_path) and item.startswith("res"):
452
+ # Get resolution
453
+ res = int(item[4:])
454
+ resolutions.append(res)
455
+
456
+ # Add paths
457
+ pages_data[res] = {
458
+ "function_profiling_histo": os.path.join(
459
+ item_path, "profiling", "profiling_plots_histo.png"
460
+ ),
461
+ "function_profiling_pie_chart": os.path.join(
462
+ item_path, "profiling", "profiling_plots_pie_chart.png"
463
+ ),
464
+ "global_profiling": os.path.join(
465
+ item_path, "profiling", "memory_profiling.png"
466
+ ),
467
+ }
468
+
469
+ # ordered resolutions
470
+ resolutions.sort(reverse=True)
471
+
472
+ # Build pdf
473
+ pdf_path = os.path.join(log_dir, "profiling_summary.pdf")
474
+
475
+ with PdfPages(pdf_path) as pdf:
476
+ for res in resolutions:
477
+ # function_profiling
478
+ if os.path.exists(pages_data[res]["function_profiling_histo"]):
479
+ img = Image.open(pages_data[res]["function_profiling_histo"])
480
+ fig, axis = plt.subplots(1, 1, figsize=(11.69, 8.27), dpi=300)
481
+ axis.imshow(img, interpolation="none")
482
+ axis.set_title(
483
+ f"Function Profiling Histograms - "
484
+ f"Epipolar Resolution {res}",
485
+ fontsize=16,
486
+ fontweight="bold",
487
+ )
488
+ axis.axis("off")
489
+ plt.subplots_adjust(left=0, right=1, top=0.95, bottom=0)
490
+ pdf.savefig(fig, bbox_inches="tight", dpi=300)
491
+ plt.close(fig)
492
+
493
+ if os.path.exists(pages_data[res]["function_profiling_pie_chart"]):
494
+ img = Image.open(
495
+ pages_data[res]["function_profiling_pie_chart"]
496
+ )
497
+ fig, axis = plt.subplots(1, 1, figsize=(11.69, 8.27), dpi=300)
498
+ axis.imshow(img, interpolation="none")
499
+ axis.set_title(
500
+ f"Function Profiling Pie Chart - Epipolar Resolution {res}",
501
+ fontsize=16,
502
+ fontweight="bold",
503
+ )
504
+ axis.axis("off")
505
+ plt.subplots_adjust(left=0, right=1, top=0.95, bottom=0)
506
+ pdf.savefig(fig, bbox_inches="tight", dpi=300)
507
+ plt.close(fig)
508
+
509
+ # global_profiling
510
+ if os.path.exists(pages_data[res]["global_profiling"]):
511
+ img = Image.open(pages_data[res]["global_profiling"])
512
+ fig, axis = plt.subplots(1, 1, figsize=(11.69, 8.27), dpi=300)
513
+ axis.imshow(img, interpolation="none")
514
+ axis.set_title(
515
+ f"Global Profiling - Epipolar Resolution {res}",
516
+ fontsize=16,
517
+ fontweight="bold",
518
+ )
519
+ axis.axis("off")
520
+ plt.subplots_adjust(left=0, right=1, top=0.95, bottom=0)
521
+ pdf.savefig(fig, bbox_inches="tight", dpi=300)
522
+ plt.close(fig)
523
+
524
+ logging.info("PDF profiling summary generated: {}".format(pdf_path))
525
+
526
+
527
+ def filter_lists(names, data, cond):
528
+ """
529
+ Filter lists with condition on name
530
+ """
531
+
532
+ filtered_names = []
533
+ filtered_data = []
534
+
535
+ for name, dat in zip(names, data): # noqa: B905
536
+ if cond(name):
537
+ filtered_names.append(name)
538
+ filtered_data.append(dat)
539
+
540
+ return filtered_names, filtered_data
541
+
542
+
543
+ def generate_boxplot(axis, names, data_full, title, data_type):
544
+ """
545
+ Generate boxplot
546
+ """
547
+
548
+ axis.boxplot(data_full, vert=False, showfliers=False, labels=names)
549
+ axis.invert_yaxis()
550
+ axis.set_xlabel(data_type)
551
+ axis.set_title(title)
552
+
553
+
554
+ def generate_histo( # pylint: disable=too-many-positional-arguments
555
+ axis, names, data, title, data_type, data_min_err=None, data_max_err=None
556
+ ):
557
+ """
558
+ Generate histogram
559
+ """
560
+ y_pos = np.arange(len(names))
561
+ if None not in (data_min_err, data_max_err):
562
+ data_min_err = np.array(data_min_err)
563
+ data_max_err = np.array(data_max_err)
564
+ xerr = np.empty((2, data_min_err.shape[0]))
565
+ xerr[0, :] = data_min_err
566
+ xerr[1, :] = data_max_err
567
+ axis.barh(y_pos, data, xerr=xerr, align="center")
568
+ else:
569
+ axis.barh(y_pos, data, align="center")
570
+ axis.set_yticks(y_pos, labels=names)
571
+ axis.invert_yaxis()
572
+ axis.set_xlabel(data_type)
573
+ axis.set_title(title)
574
+
575
+
576
+ def generate_pie_chart(axis, names, data, title):
577
+ """
578
+ Generate pie chart, data in %
579
+ """
580
+ names = list(names)
581
+ data = list(data)
582
+
583
+ if np.sum(data) > 100:
584
+ cars_logging.add_profiling_message(
585
+ "Chart: sum of data {}> 100%".format(title)
586
+ )
587
+ title += " (with sum > 100%) "
588
+ else:
589
+ others = 100 - np.sum(data)
590
+ data.append(others)
591
+ names.append("other")
592
+
593
+ axis.pie(
594
+ data,
595
+ labels=names,
596
+ autopct="%1.1f%%",
597
+ labeldistance=1.1,
598
+ textprops={"fontsize": 30},
599
+ )
600
+ axis.set_title(title, fontsize=40)
601
+
602
+
603
+ def cars_profile(name=None, interval=0.1):
604
+ """
605
+ CARS profiling decorator
606
+
607
+ :param: func: function to monitor
608
+
609
+ """
610
+
611
+ def decorator_generator(func):
612
+ """
613
+ Inner function
614
+ """
615
+
616
+ def wrapper_cars_profile(*args, **kwargs):
617
+ """
618
+ Profiling wrapper
619
+
620
+ Generate profiling logs of functio, run
621
+
622
+ :return: func(*args, **kwargs)
623
+
624
+ """
625
+ start_time = time.time()
626
+
627
+ memory_start = get_current_memory()
628
+
629
+ # Launch memory profiling thread
630
+ child_pipe, parent_pipe = Pipe()
631
+ thread_monitoring = CarsMemProf(
632
+ os.getpid(), child_pipe, interval=interval
633
+ )
634
+ thread_monitoring.start()
635
+ if parent_pipe.poll(THREAD_TIMEOUT):
636
+ parent_pipe.recv()
637
+
638
+ res = func(*args, **kwargs)
639
+ total_time = time.time() - start_time
640
+
641
+ # end memprofiling monitoring
642
+ parent_pipe.send(0)
643
+ max_memory = None
644
+ max_cpu = None
645
+ if parent_pipe.poll(THREAD_TIMEOUT):
646
+ max_memory = parent_pipe.recv()
647
+ if parent_pipe.poll(THREAD_TIMEOUT):
648
+ max_cpu = parent_pipe.recv()
649
+ memory_end = get_current_memory()
650
+
651
+ func_name = name
652
+ if name is None:
653
+ func_name = func.__name__.capitalize()
654
+
655
+ message = (
656
+ "CarsProfiling# %{}%: %{:.4f}% s Max ram : %{}% MiB"
657
+ " Start Ram: %{}% MiB, End Ram: %{}% MiB, "
658
+ " Max CPU usage: %{}%".format(
659
+ func_name,
660
+ total_time,
661
+ max_memory,
662
+ memory_start,
663
+ memory_end,
664
+ max_cpu,
665
+ )
666
+ )
667
+
668
+ cars_logging.add_profiling_message(message)
669
+
670
+ return res
671
+
672
+ return wrapper_cars_profile
673
+
674
+ return decorator_generator
675
+
676
+
677
+ class CarsMemProf(Thread):
678
+ """
679
+ CarsMemProf
680
+
681
+ Profiling thread
682
+ """
683
+
684
+ def __init__(self, pid, pipe, interval=0.1):
685
+ """
686
+ Init function of CarsMemProf
687
+ """
688
+ super().__init__()
689
+ self.pipe = pipe
690
+ self.interval = interval
691
+ self.cpu_interval = 0.1
692
+ self.process = psutil.Process(pid)
693
+
694
+ def run(self):
695
+ """
696
+ Run
697
+ """
698
+
699
+ try:
700
+ max_mem = 0
701
+ max_cpu = 0
702
+
703
+ # tell parent profiling is ready
704
+ self.pipe.send(0)
705
+ stop = False
706
+ while True:
707
+ # Get memory
708
+ current_mem = self.process.memory_info().rss
709
+
710
+ max_mem = max(max_mem, current_mem)
711
+
712
+ # Get cpu max
713
+ current_cpu = self.process.cpu_percent(
714
+ interval=self.cpu_interval
715
+ )
716
+
717
+ max_cpu = max(max_cpu, current_cpu)
718
+
719
+ if stop:
720
+ break
721
+ stop = self.pipe.poll(self.interval)
722
+
723
+ # Convert nbytes size for logger
724
+ self.pipe.send(float(max_mem) / 1000000)
725
+ self.pipe.send(max_cpu)
726
+
727
+ except BrokenPipeError:
728
+ logging.debug("broken pipe error in log wrapper ")