cars 1.0.0rc1__cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.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 (200) hide show
  1. cars/__init__.py +74 -0
  2. cars/applications/__init__.py +37 -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 +104 -0
  8. cars/applications/auxiliary_filling/auxiliary_filling_algo.py +475 -0
  9. cars/applications/auxiliary_filling/auxiliary_filling_from_sensors_app.py +630 -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 +655 -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 +1460 -0
  28. cars/applications/dense_matching/cpp/__init__.py +0 -0
  29. cars/applications/dense_matching/cpp/dense_matching_cpp.cpython-312-i386-linux-gnu.so +0 -0
  30. cars/applications/dense_matching/cpp/dense_matching_cpp.py +94 -0
  31. cars/applications/dense_matching/cpp/includes/dense_matching.hpp +58 -0
  32. cars/applications/dense_matching/cpp/meson.build +9 -0
  33. cars/applications/dense_matching/cpp/src/bindings.cpp +13 -0
  34. cars/applications/dense_matching/cpp/src/dense_matching.cpp +207 -0
  35. cars/applications/dense_matching/dense_matching_algo.py +401 -0
  36. cars/applications/dense_matching/dense_matching_constants.py +89 -0
  37. cars/applications/dense_matching/dense_matching_wrappers.py +951 -0
  38. cars/applications/dense_matching/disparity_grid_algo.py +588 -0
  39. cars/applications/dense_matching/loaders/__init__.py +23 -0
  40. cars/applications/dense_matching/loaders/config_census_sgm_default.json +31 -0
  41. cars/applications/dense_matching/loaders/config_census_sgm_homogeneous.json +30 -0
  42. cars/applications/dense_matching/loaders/config_census_sgm_mountain_and_vegetation.json +30 -0
  43. cars/applications/dense_matching/loaders/config_census_sgm_shadow.json +30 -0
  44. cars/applications/dense_matching/loaders/config_census_sgm_sparse.json +36 -0
  45. cars/applications/dense_matching/loaders/config_census_sgm_urban.json +30 -0
  46. cars/applications/dense_matching/loaders/config_mapping.json +13 -0
  47. cars/applications/dense_matching/loaders/config_mccnn.json +28 -0
  48. cars/applications/dense_matching/loaders/global_land_cover_map.tif +0 -0
  49. cars/applications/dense_matching/loaders/pandora_loader.py +593 -0
  50. cars/applications/dsm_filling/__init__.py +32 -0
  51. cars/applications/dsm_filling/abstract_dsm_filling_app.py +101 -0
  52. cars/applications/dsm_filling/border_interpolation_app.py +270 -0
  53. cars/applications/dsm_filling/bulldozer_config/base_config.yaml +44 -0
  54. cars/applications/dsm_filling/bulldozer_filling_app.py +279 -0
  55. cars/applications/dsm_filling/exogenous_filling_app.py +333 -0
  56. cars/applications/grid_generation/__init__.py +30 -0
  57. cars/applications/grid_generation/abstract_grid_generation_app.py +142 -0
  58. cars/applications/grid_generation/epipolar_grid_generation_app.py +327 -0
  59. cars/applications/grid_generation/grid_correction_app.py +496 -0
  60. cars/applications/grid_generation/grid_generation_algo.py +388 -0
  61. cars/applications/grid_generation/grid_generation_constants.py +46 -0
  62. cars/applications/grid_generation/transform_grid.py +88 -0
  63. cars/applications/ground_truth_reprojection/__init__.py +30 -0
  64. cars/applications/ground_truth_reprojection/abstract_ground_truth_reprojection_app.py +137 -0
  65. cars/applications/ground_truth_reprojection/direct_localization_app.py +629 -0
  66. cars/applications/ground_truth_reprojection/ground_truth_reprojection_algo.py +275 -0
  67. cars/applications/point_cloud_outlier_removal/__init__.py +30 -0
  68. cars/applications/point_cloud_outlier_removal/abstract_outlier_removal_app.py +385 -0
  69. cars/applications/point_cloud_outlier_removal/outlier_removal_algo.py +392 -0
  70. cars/applications/point_cloud_outlier_removal/outlier_removal_constants.py +43 -0
  71. cars/applications/point_cloud_outlier_removal/small_components_app.py +527 -0
  72. cars/applications/point_cloud_outlier_removal/statistical_app.py +531 -0
  73. cars/applications/rasterization/__init__.py +30 -0
  74. cars/applications/rasterization/abstract_pc_rasterization_app.py +183 -0
  75. cars/applications/rasterization/rasterization_algo.py +534 -0
  76. cars/applications/rasterization/rasterization_constants.py +38 -0
  77. cars/applications/rasterization/rasterization_wrappers.py +634 -0
  78. cars/applications/rasterization/simple_gaussian_app.py +1152 -0
  79. cars/applications/resampling/__init__.py +28 -0
  80. cars/applications/resampling/abstract_resampling_app.py +187 -0
  81. cars/applications/resampling/bicubic_resampling_app.py +762 -0
  82. cars/applications/resampling/resampling_algo.py +614 -0
  83. cars/applications/resampling/resampling_constants.py +36 -0
  84. cars/applications/resampling/resampling_wrappers.py +309 -0
  85. cars/applications/sparse_matching/__init__.py +30 -0
  86. cars/applications/sparse_matching/abstract_sparse_matching_app.py +498 -0
  87. cars/applications/sparse_matching/sift_app.py +735 -0
  88. cars/applications/sparse_matching/sparse_matching_algo.py +360 -0
  89. cars/applications/sparse_matching/sparse_matching_constants.py +68 -0
  90. cars/applications/sparse_matching/sparse_matching_wrappers.py +238 -0
  91. cars/applications/triangulation/__init__.py +32 -0
  92. cars/applications/triangulation/abstract_triangulation_app.py +227 -0
  93. cars/applications/triangulation/line_of_sight_intersection_app.py +1243 -0
  94. cars/applications/triangulation/pc_transform.py +552 -0
  95. cars/applications/triangulation/triangulation_algo.py +371 -0
  96. cars/applications/triangulation/triangulation_constants.py +38 -0
  97. cars/applications/triangulation/triangulation_wrappers.py +259 -0
  98. cars/bundleadjustment.py +757 -0
  99. cars/cars.py +177 -0
  100. cars/conf/__init__.py +23 -0
  101. cars/conf/geoid/egm96.grd +0 -0
  102. cars/conf/geoid/egm96.grd.hdr +15 -0
  103. cars/conf/input_parameters.py +156 -0
  104. cars/conf/mask_cst.py +35 -0
  105. cars/core/__init__.py +23 -0
  106. cars/core/cars_logging.py +402 -0
  107. cars/core/constants.py +191 -0
  108. cars/core/constants_disparity.py +50 -0
  109. cars/core/datasets.py +140 -0
  110. cars/core/geometry/__init__.py +27 -0
  111. cars/core/geometry/abstract_geometry.py +1119 -0
  112. cars/core/geometry/shareloc_geometry.py +598 -0
  113. cars/core/inputs.py +568 -0
  114. cars/core/outputs.py +176 -0
  115. cars/core/preprocessing.py +722 -0
  116. cars/core/projection.py +843 -0
  117. cars/core/roi_tools.py +215 -0
  118. cars/core/tiling.py +774 -0
  119. cars/core/utils.py +164 -0
  120. cars/data_structures/__init__.py +23 -0
  121. cars/data_structures/cars_dataset.py +1541 -0
  122. cars/data_structures/cars_dict.py +74 -0
  123. cars/data_structures/corresponding_tiles_tools.py +186 -0
  124. cars/data_structures/dataframe_converter.py +185 -0
  125. cars/data_structures/format_transformation.py +297 -0
  126. cars/devibrate.py +689 -0
  127. cars/extractroi.py +264 -0
  128. cars/orchestrator/__init__.py +23 -0
  129. cars/orchestrator/achievement_tracker.py +125 -0
  130. cars/orchestrator/cluster/__init__.py +37 -0
  131. cars/orchestrator/cluster/abstract_cluster.py +244 -0
  132. cars/orchestrator/cluster/abstract_dask_cluster.py +375 -0
  133. cars/orchestrator/cluster/dask_cluster_tools.py +103 -0
  134. cars/orchestrator/cluster/dask_config/README.md +94 -0
  135. cars/orchestrator/cluster/dask_config/dask.yaml +21 -0
  136. cars/orchestrator/cluster/dask_config/distributed.yaml +70 -0
  137. cars/orchestrator/cluster/dask_config/jobqueue.yaml +26 -0
  138. cars/orchestrator/cluster/dask_config/reference_confs/dask-schema.yaml +137 -0
  139. cars/orchestrator/cluster/dask_config/reference_confs/dask.yaml +26 -0
  140. cars/orchestrator/cluster/dask_config/reference_confs/distributed-schema.yaml +1009 -0
  141. cars/orchestrator/cluster/dask_config/reference_confs/distributed.yaml +273 -0
  142. cars/orchestrator/cluster/dask_config/reference_confs/jobqueue.yaml +212 -0
  143. cars/orchestrator/cluster/dask_jobqueue_utils.py +204 -0
  144. cars/orchestrator/cluster/local_dask_cluster.py +116 -0
  145. cars/orchestrator/cluster/log_wrapper.py +1075 -0
  146. cars/orchestrator/cluster/mp_cluster/__init__.py +27 -0
  147. cars/orchestrator/cluster/mp_cluster/mp_factorizer.py +212 -0
  148. cars/orchestrator/cluster/mp_cluster/mp_objects.py +535 -0
  149. cars/orchestrator/cluster/mp_cluster/mp_tools.py +93 -0
  150. cars/orchestrator/cluster/mp_cluster/mp_wrapper.py +505 -0
  151. cars/orchestrator/cluster/mp_cluster/multiprocessing_cluster.py +873 -0
  152. cars/orchestrator/cluster/mp_cluster/multiprocessing_profiler.py +399 -0
  153. cars/orchestrator/cluster/pbs_dask_cluster.py +207 -0
  154. cars/orchestrator/cluster/sequential_cluster.py +139 -0
  155. cars/orchestrator/cluster/slurm_dask_cluster.py +234 -0
  156. cars/orchestrator/orchestrator.py +905 -0
  157. cars/orchestrator/orchestrator_constants.py +29 -0
  158. cars/orchestrator/registry/__init__.py +23 -0
  159. cars/orchestrator/registry/abstract_registry.py +143 -0
  160. cars/orchestrator/registry/compute_registry.py +106 -0
  161. cars/orchestrator/registry/id_generator.py +116 -0
  162. cars/orchestrator/registry/replacer_registry.py +213 -0
  163. cars/orchestrator/registry/saver_registry.py +363 -0
  164. cars/orchestrator/registry/unseen_registry.py +118 -0
  165. cars/orchestrator/tiles_profiler.py +279 -0
  166. cars/pipelines/__init__.py +26 -0
  167. cars/pipelines/conf_resolution/conf_final_resolution.yaml +5 -0
  168. cars/pipelines/conf_resolution/conf_first_resolution.yaml +2 -0
  169. cars/pipelines/conf_resolution/conf_intermediate_resolution.yaml +2 -0
  170. cars/pipelines/default/__init__.py +26 -0
  171. cars/pipelines/default/default_pipeline.py +786 -0
  172. cars/pipelines/parameters/__init__.py +0 -0
  173. cars/pipelines/parameters/advanced_parameters.py +417 -0
  174. cars/pipelines/parameters/advanced_parameters_constants.py +69 -0
  175. cars/pipelines/parameters/application_parameters.py +71 -0
  176. cars/pipelines/parameters/depth_map_inputs.py +0 -0
  177. cars/pipelines/parameters/dsm_inputs.py +918 -0
  178. cars/pipelines/parameters/dsm_inputs_constants.py +25 -0
  179. cars/pipelines/parameters/output_constants.py +52 -0
  180. cars/pipelines/parameters/output_parameters.py +454 -0
  181. cars/pipelines/parameters/sensor_inputs.py +842 -0
  182. cars/pipelines/parameters/sensor_inputs_constants.py +49 -0
  183. cars/pipelines/parameters/sensor_loaders/__init__.py +29 -0
  184. cars/pipelines/parameters/sensor_loaders/basic_classif_loader.py +86 -0
  185. cars/pipelines/parameters/sensor_loaders/basic_image_loader.py +98 -0
  186. cars/pipelines/parameters/sensor_loaders/pivot_classif_loader.py +90 -0
  187. cars/pipelines/parameters/sensor_loaders/pivot_image_loader.py +105 -0
  188. cars/pipelines/parameters/sensor_loaders/sensor_loader.py +93 -0
  189. cars/pipelines/parameters/sensor_loaders/sensor_loader_template.py +71 -0
  190. cars/pipelines/parameters/sensor_loaders/slurp_classif_loader.py +86 -0
  191. cars/pipelines/pipeline.py +119 -0
  192. cars/pipelines/pipeline_constants.py +31 -0
  193. cars/pipelines/pipeline_template.py +139 -0
  194. cars/pipelines/unit/__init__.py +26 -0
  195. cars/pipelines/unit/unit_pipeline.py +2850 -0
  196. cars/starter.py +167 -0
  197. cars-1.0.0rc1.dist-info/METADATA +292 -0
  198. cars-1.0.0rc1.dist-info/RECORD +200 -0
  199. cars-1.0.0rc1.dist-info/WHEEL +6 -0
  200. cars-1.0.0rc1.dist-info/entry_points.txt +8 -0
@@ -0,0 +1,1243 @@
1
+ #!/usr/bin/env python
2
+ # coding: utf8
3
+ #
4
+ # Copyright (c) 2020 Centre National d'Etudes Spatiales (CNES).
5
+ #
6
+ # This file is part of CARS
7
+ # (see https://github.com/CNES/cars).
8
+ #
9
+ # Licensed under the Apache License, Version 2.0 (the "License");
10
+ # you may not use this file except in compliance with the License.
11
+ # You may obtain a copy of the License at
12
+ #
13
+ # http://www.apache.org/licenses/LICENSE-2.0
14
+ #
15
+ # Unless required by applicable law or agreed to in writing, software
16
+ # distributed under the License is distributed on an "AS IS" BASIS,
17
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18
+ # See the License for the specific language governing permissions and
19
+ # limitations under the License.
20
+ #
21
+ # pylint: disable=too-many-lines
22
+ """
23
+ this module contains the LineOfSightIntersection application class.
24
+ """
25
+
26
+ # Standard imports
27
+ import logging
28
+ import os
29
+ from typing import Dict, Tuple
30
+
31
+ # Third party imports
32
+ import numpy as np
33
+ import pandas
34
+ import xarray as xr
35
+ from json_checker import Checker
36
+ from shareloc.geofunctions.rectification_grid import RectificationGrid
37
+
38
+ # CARS imports
39
+ import cars.orchestrator.orchestrator as ocht
40
+ from cars.applications import application_constants
41
+ from cars.applications.grid_generation import grid_generation_algo
42
+ from cars.applications.triangulation import (
43
+ pc_transform,
44
+ triangulation_algo,
45
+ triangulation_constants,
46
+ )
47
+ from cars.applications.triangulation import (
48
+ triangulation_wrappers as triangulation_wrap,
49
+ )
50
+ from cars.applications.triangulation.abstract_triangulation_app import (
51
+ Triangulation,
52
+ )
53
+ from cars.core import constants as cst
54
+ from cars.core import inputs, projection, tiling
55
+ from cars.core.utils import safe_makedirs
56
+ from cars.data_structures import cars_dataset
57
+ from cars.orchestrator.cluster.log_wrapper import cars_profile
58
+ from cars.pipelines.parameters import sensor_inputs_constants as sens_cst
59
+
60
+
61
+ class LineOfSightIntersection(
62
+ Triangulation, short_name="line_of_sight_intersection"
63
+ ):
64
+ """
65
+ Triangulation
66
+ """
67
+
68
+ def __init__(self, conf=None):
69
+ """
70
+ Init function of Triangulation
71
+
72
+ :param conf: configuration for triangulation
73
+ :return: an application_to_use object
74
+ """
75
+
76
+ super().__init__(conf=conf)
77
+ # check conf
78
+ self.used_method = self.used_config["method"]
79
+ self.snap_to_img1 = self.used_config["snap_to_img1"]
80
+ # Saving files
81
+ self.save_intermediate_data = self.used_config["save_intermediate_data"]
82
+
83
+ # global value for left image to check if snap_to_img1 can
84
+ # be applied : Need than same application object is run
85
+ # for all pairs
86
+ self.ref_left_img = None
87
+
88
+ # Init orchestrator
89
+ self.orchestrator = None
90
+
91
+ def check_conf(self, conf):
92
+ """
93
+ Check configuration
94
+
95
+ :param conf: configuration to check
96
+ :type conf: dict
97
+
98
+ :return: overloaded configuration
99
+ :rtype: dict
100
+
101
+ """
102
+
103
+ # init conf
104
+ if conf is not None:
105
+ overloaded_conf = conf.copy()
106
+ else:
107
+ conf = {}
108
+ overloaded_conf = {}
109
+
110
+ # Overload conf
111
+ overloaded_conf["method"] = conf.get(
112
+ "method", "line_of_sight_intersection"
113
+ )
114
+ overloaded_conf["snap_to_img1"] = conf.get("snap_to_img1", False)
115
+
116
+ # Saving files
117
+ overloaded_conf["save_intermediate_data"] = conf.get(
118
+ "save_intermediate_data", False
119
+ )
120
+
121
+ triangulation_schema = {
122
+ "method": str,
123
+ "snap_to_img1": bool,
124
+ "save_intermediate_data": bool,
125
+ }
126
+
127
+ # Check conf
128
+ checker = Checker(triangulation_schema)
129
+ checker.validate(overloaded_conf)
130
+
131
+ return overloaded_conf
132
+
133
+ # pylint: disable=too-many-positional-arguments
134
+ @cars_profile(name="Save triangulation output", interval=0.5)
135
+ def save_triangulation_output( # noqa: C901 function is too complex
136
+ self,
137
+ epipolar_point_cloud,
138
+ sensor_image_left,
139
+ output_dir,
140
+ dump_dir=None,
141
+ performance_maps_to_generate=None,
142
+ save_output_coordinates=True,
143
+ save_output_color=True,
144
+ save_output_classification=False,
145
+ save_output_filling=False,
146
+ save_output_performance_map=False,
147
+ save_output_ambiguity=False,
148
+ ):
149
+ """
150
+ Save the triangulation output. The different TIFs composing the depth
151
+ map are written to the output directory. Auxiliary products can be
152
+ requested or not using the parameters. A dump directory can also be
153
+ provided to write any additionnal files that have not been written
154
+ to the output directory (because they are not part of the depth map
155
+ definition, or because they have not been requested).
156
+
157
+ :param epipolar_point_cloud: tiled epipolar left image
158
+ :type epipolar_point_cloud: CarsDataset
159
+ :param sensor_image_left: tiled sensor left image
160
+ Dict Must contain keys : "image", "texture", "geomodel",
161
+ "no_data", "mask". Paths must be absolutes
162
+ :type sensor_image_left: CarsDataset
163
+ :param output_dir: directory to write triangulation output depth
164
+ map.
165
+ :type output_dir: None or str
166
+ :param dump_dir: folder used as dump directory for current pair, None to
167
+ deactivate intermediate data writing
168
+ :type dump_dir: str
169
+ :param performance_maps_to_generate: None or list containing
170
+ "risk" or "intervals"
171
+ :type performance_maps_to_generate: None or list containing
172
+ "risk" or "intervals"
173
+ :param save_output_coordinates: Save X, Y and Z coords in output_dir
174
+ :type save_output_coordinates: bool
175
+ :param save_output_color: Save color depth map in output_dir
176
+ :type save_output_color: bool
177
+ :param save_output_classification: Save classification depth map in
178
+ output_dir
179
+ :type save_output_classification: bool
180
+ :param save_output_filling: Save filling depth map in output_dir
181
+ :type save_output_filling: bool
182
+ :param save_output_performance_map: Save performance map in output_dir
183
+ :type save_output_performance_map: bool
184
+ :param save_output_ambiguity: Save ambiguity in output_dir
185
+ :type save_output_ambiguity: bool
186
+ """
187
+
188
+ if dump_dir:
189
+ safe_makedirs(dump_dir)
190
+
191
+ # Propagate color type in output file
192
+ color_type = inputs.rasterio_get_image_type(
193
+ sensor_image_left[sens_cst.INPUT_IMG]["bands"]["b0"]["path"]
194
+ )
195
+
196
+ if output_dir is None:
197
+ output_dir = dump_dir
198
+
199
+ if save_output_coordinates or dump_dir:
200
+ coords_output_dir = (
201
+ output_dir if save_output_coordinates else dump_dir
202
+ )
203
+ self.orchestrator.add_to_save_lists(
204
+ os.path.join(coords_output_dir, "X.tif"),
205
+ cst.X,
206
+ epipolar_point_cloud,
207
+ cars_ds_name="depth_map_x",
208
+ dtype=np.float64,
209
+ )
210
+
211
+ self.orchestrator.add_to_save_lists(
212
+ os.path.join(coords_output_dir, "Y.tif"),
213
+ cst.Y,
214
+ epipolar_point_cloud,
215
+ cars_ds_name="depth_map_y",
216
+ dtype=np.float64,
217
+ )
218
+
219
+ self.orchestrator.add_to_save_lists(
220
+ os.path.join(coords_output_dir, "Z.tif"),
221
+ cst.Z,
222
+ epipolar_point_cloud,
223
+ cars_ds_name="depth_map_z",
224
+ dtype=np.float64,
225
+ )
226
+
227
+ if save_output_color or dump_dir:
228
+ color_output_dir = output_dir if save_output_color else dump_dir
229
+ self.orchestrator.add_to_save_lists(
230
+ os.path.join(color_output_dir, "image.tif"),
231
+ cst.EPI_TEXTURE,
232
+ epipolar_point_cloud,
233
+ cars_ds_name="depth_map_color",
234
+ dtype=color_type,
235
+ )
236
+
237
+ if save_output_performance_map or dump_dir:
238
+ map_output_dir = (
239
+ output_dir if save_output_performance_map else dump_dir
240
+ )
241
+ performance_map_tail_path = "performance_map.tif"
242
+ if "intervals" in performance_maps_to_generate:
243
+ tail_path = performance_map_tail_path
244
+ if len(performance_maps_to_generate) == 2:
245
+ tail_path = "performance_map_from_intervals.tif"
246
+ self.orchestrator.add_to_save_lists(
247
+ os.path.join(map_output_dir, tail_path),
248
+ cst.POINT_CLOUD_PERFORMANCE_MAP_FROM_INTERVALS,
249
+ epipolar_point_cloud,
250
+ cars_ds_name="depth_map_performance_map_from_intervals",
251
+ optional_data=True,
252
+ dtype=np.float64,
253
+ )
254
+
255
+ if "risk" in performance_maps_to_generate:
256
+ tail_path = performance_map_tail_path
257
+ if len(performance_maps_to_generate) == 2:
258
+ tail_path = "performance_map_from_risk.tif"
259
+ self.orchestrator.add_to_save_lists(
260
+ os.path.join(map_output_dir, tail_path),
261
+ cst.POINT_CLOUD_PERFORMANCE_MAP_FROM_RISK,
262
+ epipolar_point_cloud,
263
+ cars_ds_name="depth_map_performance_map_from_risk",
264
+ optional_data=True,
265
+ dtype=np.float64,
266
+ )
267
+
268
+ if save_output_ambiguity or dump_dir:
269
+ map_output_dir = output_dir if save_output_ambiguity else dump_dir
270
+ self.orchestrator.add_to_save_lists(
271
+ os.path.join(map_output_dir, "ambiguity.tif"),
272
+ cst.EPI_AMBIGUITY,
273
+ epipolar_point_cloud,
274
+ cars_ds_name="depth_map_ambiguity",
275
+ optional_data=True,
276
+ dtype=np.float64,
277
+ )
278
+
279
+ if save_output_classification or dump_dir:
280
+ classif_output_dir = (
281
+ output_dir if save_output_classification else dump_dir
282
+ )
283
+ self.orchestrator.add_to_save_lists(
284
+ os.path.join(classif_output_dir, "classification.tif"),
285
+ cst.EPI_CLASSIFICATION,
286
+ epipolar_point_cloud,
287
+ cars_ds_name="depth_map_classification",
288
+ optional_data=True,
289
+ dtype=np.uint8,
290
+ )
291
+
292
+ if save_output_filling or dump_dir:
293
+ filling_output_dir = output_dir if save_output_filling else dump_dir
294
+ self.orchestrator.add_to_save_lists(
295
+ os.path.join(filling_output_dir, "filling.tif"),
296
+ cst.EPI_FILLING,
297
+ epipolar_point_cloud,
298
+ cars_ds_name="depth_map_filling",
299
+ optional_data=True,
300
+ dtype=np.uint8,
301
+ nodata=255,
302
+ )
303
+
304
+ if dump_dir and performance_maps_to_generate is not None:
305
+ if "intervals" in performance_maps_to_generate:
306
+ self.orchestrator.add_to_save_lists(
307
+ os.path.join(dump_dir, "Z_inf_from_intervals.tif"),
308
+ cst.POINT_CLOUD_LAYER_INF_FROM_INTERVALS,
309
+ epipolar_point_cloud,
310
+ cars_ds_name="depth_map_z_inf_from_intervals",
311
+ )
312
+ self.orchestrator.add_to_save_lists(
313
+ os.path.join(dump_dir, "Z_sup_from_intervals.tif"),
314
+ cst.POINT_CLOUD_LAYER_SUP_FROM_INTERVALS,
315
+ epipolar_point_cloud,
316
+ cars_ds_name="depth_map_z_sup_from_intervals",
317
+ )
318
+
319
+ if "risk" in performance_maps_to_generate:
320
+ self.orchestrator.add_to_save_lists(
321
+ os.path.join(dump_dir, "Z_inf_from_risk.tif"),
322
+ cst.POINT_CLOUD_LAYER_INF_FROM_RISK,
323
+ epipolar_point_cloud,
324
+ cars_ds_name="depth_map_z_inf_from_risk",
325
+ )
326
+ self.orchestrator.add_to_save_lists(
327
+ os.path.join(dump_dir, "Z_sup_from_risk.tif"),
328
+ cst.POINT_CLOUD_LAYER_SUP_FROM_RISK,
329
+ epipolar_point_cloud,
330
+ cars_ds_name="depth_map_z_sup_from_risk",
331
+ )
332
+
333
+ if dump_dir:
334
+ self.orchestrator.add_to_save_lists(
335
+ os.path.join(dump_dir, "corr_mask.tif"),
336
+ cst.POINT_CLOUD_CORR_MSK,
337
+ epipolar_point_cloud,
338
+ cars_ds_name="depth_map_corr_msk",
339
+ optional_data=True,
340
+ )
341
+
342
+ @cars_profile(name="Fill index", interval=0.5)
343
+ def fill_index( # pylint: disable=too-many-positional-arguments
344
+ self,
345
+ save_output_coordinates=True,
346
+ save_output_color=True,
347
+ save_output_classification=False,
348
+ save_output_filling=False,
349
+ save_output_performance_map=False,
350
+ save_output_ambiguity=False,
351
+ pair_key="PAIR_0",
352
+ ):
353
+ """
354
+ Fill depth map index for current pair, according to which product
355
+ should be saved
356
+
357
+ :param save_output_coordinates: Save X, Y and Z coords in output_dir
358
+ :type save_output_coordinates: bool
359
+ :param save_output_color: Save color depth map in output_dir
360
+ :type save_output_color: bool
361
+ :param save_output_classification: Save classification depth map in
362
+ output_dir
363
+ :type save_output_classification: bool
364
+ :param save_output_filling: Save filling depth map in output_dir
365
+ :type save_output_filling: bool
366
+ :param save_output_performance_map: Save performance map in output_dir
367
+ :type save_output_performance_map: bool
368
+ :param save_output_ambiguity: Save ambiguity in output_dir
369
+ :type save_output_ambiguity: bool
370
+ :param pair_key: name of the current pair
371
+ :type pair_key: str
372
+ """
373
+
374
+ # index file for this depth map
375
+ index = {}
376
+
377
+ if save_output_coordinates:
378
+ index[cst.INDEX_DEPTH_MAP_X] = os.path.join(pair_key, "X.tif")
379
+ index[cst.INDEX_DEPTH_MAP_Y] = os.path.join(pair_key, "Y.tif")
380
+ index[cst.INDEX_DEPTH_MAP_Z] = os.path.join(pair_key, "Z.tif")
381
+
382
+ if save_output_color:
383
+ index[cst.INDEX_DEPTH_MAP_COLOR] = os.path.join(
384
+ pair_key, "image.tif"
385
+ )
386
+
387
+ if save_output_performance_map:
388
+ index[cst.INDEX_DEPTH_MAP_PERFORMANCE_MAP] = os.path.join(
389
+ pair_key, "performance_map.tif"
390
+ )
391
+
392
+ if save_output_ambiguity:
393
+ index[cst.INDEX_DEPTH_MAP_AMBIGUITY] = os.path.join(
394
+ pair_key, "ambiguity.tif"
395
+ )
396
+
397
+ if save_output_classification:
398
+ index[cst.INDEX_DEPTH_MAP_CLASSIFICATION] = os.path.join(
399
+ pair_key, "classification.tif"
400
+ )
401
+
402
+ if save_output_filling:
403
+ index[cst.INDEX_DEPTH_MAP_FILLING] = os.path.join(
404
+ pair_key, "filling.tif"
405
+ )
406
+
407
+ # update orchestrator index if it has been filled
408
+ if index:
409
+ # Add epsg code (always lon/lat in triangulation)
410
+ index[cst.INDEX_DEPTH_MAP_EPSG] = 4326
411
+ self.orchestrator.update_index({"depth_map": {pair_key: index}})
412
+
413
+ def create_point_cloud_directories(
414
+ self, pair_dump_dir, point_cloud_dir, point_cloud
415
+ ):
416
+ """
417
+ Set and create directories for point cloud disk output (laz and csv)
418
+ The function return None path if the point cloud should not be saved
419
+
420
+ :param pair_dump_dir: folder used as dump directory for current pair
421
+ :type pair_dump_dir: str
422
+ :param point_cloud_dir: folder used for laz official product directory
423
+ :type point_cloud_dir: str
424
+ :param point_cloud: input point cloud (for orchestrator registration)
425
+ :type point_cloud: Dataset
426
+ """
427
+
428
+ csv_pc_dir_name = None
429
+ if self.save_intermediate_data:
430
+ csv_pc_dir_name = os.path.join(pair_dump_dir, "csv")
431
+ safe_makedirs(csv_pc_dir_name)
432
+ self.orchestrator.add_to_compute_lists(
433
+ point_cloud, cars_ds_name="point_cloud_csv"
434
+ )
435
+ laz_pc_dir_name = None
436
+ if self.save_intermediate_data or point_cloud_dir is not None:
437
+ if point_cloud_dir is not None:
438
+ laz_pc_dir_name = point_cloud_dir
439
+ else:
440
+ laz_pc_dir_name = os.path.join(pair_dump_dir, "laz")
441
+ safe_makedirs(laz_pc_dir_name)
442
+ self.orchestrator.add_to_compute_lists(
443
+ point_cloud, cars_ds_name="point_cloud_laz"
444
+ )
445
+
446
+ return csv_pc_dir_name, laz_pc_dir_name
447
+
448
+ def run( # pylint: disable=too-many-positional-arguments # noqa: C901
449
+ self,
450
+ sensor_image_left,
451
+ sensor_image_right,
452
+ grid_left,
453
+ grid_right,
454
+ epipolar_disparity_map,
455
+ geometry_plugin,
456
+ epipolar_image,
457
+ epsg=None,
458
+ denoising_overload_fun=None,
459
+ source_pc_names=None,
460
+ orchestrator=None,
461
+ pair_dump_dir=None,
462
+ pair_key="PAIR_0",
463
+ uncorrected_grid_right=None,
464
+ geoid_path=None,
465
+ cloud_id=None,
466
+ performance_maps_param=None,
467
+ depth_map_dir=None,
468
+ point_cloud_dir=None,
469
+ save_output_coordinates=False,
470
+ save_output_color=False,
471
+ save_output_classification=False,
472
+ save_output_filling=False,
473
+ save_output_performance_map=False,
474
+ save_output_ambiguity=False,
475
+ ):
476
+ """
477
+ Run Triangulation application.
478
+
479
+ Created left and right CarsDataset filled with xarray.Dataset,
480
+ corresponding to 3D point clouds, stored on epipolar geometry grid.
481
+
482
+ :param sensor_image_left: tiled sensor left image
483
+ Dict Must contain keys : "image", "texture", "geomodel",
484
+ "no_data", "mask". Paths must be absolutes
485
+ :type sensor_image_left: CarsDataset
486
+ :param sensor_image_right: tiled sensor right image
487
+ Dict Must contain keys : "image", "texture", "geomodel",
488
+ "no_data", "mask". Paths must be absolutes
489
+ :type sensor_image_right: CarsDataset
490
+ :param grid_left: left epipolar grid. Grid dict contains :
491
+ - "grid_spacing", "grid_origin",\
492
+ "epipolar_size_x", epipolar_size_y", "epipolar_origin_x",\
493
+ "epipolar_origin_y","epipolar_spacing_x",\
494
+ "epipolar_spacing", "disp_to_alt_ratio", "path"
495
+ :type grid_left: dict
496
+ :param grid_right: right epipolar grid. Grid dict contains :
497
+ - "grid_spacing", "grid_origin",\
498
+ "epipolar_size_x", epipolar_size_y", "epipolar_origin_x",\
499
+ "epipolar_origin_y","epipolar_spacing_x",\
500
+ "epipolar_spacing", "disp_to_alt_ratio", "path"
501
+ :type grid_right: dict
502
+ :param epipolar_disparity_map: tiled left disparity map or \
503
+ sparse matches:
504
+
505
+ - if CarsDataset is instance of "arrays", CarsDataset contains:
506
+
507
+ - N x M Delayed tiles \
508
+ Each tile will be a future xarray Dataset containing:
509
+
510
+ - data with keys : "disp", "disp_msk"
511
+ - attrs with keys: profile, window, overlaps
512
+ - attributes containing:"largest_epipolar_region"\
513
+ "opt_epipolar_tile_size"
514
+
515
+ - if CarsDataset is instance of "points", CarsDataset contains:
516
+
517
+ - N x M Delayed tiles \
518
+ Each tile will be a future pandas DataFrame containing:
519
+
520
+ - data : (L, 4) shape matches
521
+ - attributes containing:"disp_lower_bound","disp_upper_bound",\
522
+ "elevation_delta_lower_bound","elevation_delta_upper_bound"
523
+
524
+ :type epipolar_disparity_map: CarsDataset
525
+ :param epipolar_image: tiled epipolar left image
526
+ :type epipolar_image: CarsDataset
527
+ :param denoising_overload_fun: function to overload dataset
528
+ :type denoising_overload_fun: fun
529
+ :param source_pc_names: source pc names
530
+ :type source_pc_names: list[str]
531
+ :param orchestrator: orchestrator used
532
+ :param pair_dump_dir: folder used as dump directory for current pair
533
+ :type pair_dump_dir: str
534
+ :param pair_key: pair key id
535
+ :type pair_key: str
536
+ :param uncorrected_grid_right: not corrected right epipolar grid
537
+ used if self.snap_to_img1
538
+ :type uncorrected_grid_right: CarsDataset
539
+ :param geoid_path: geoid path
540
+ :type geoid_path: str
541
+ :param performance_maps_param: parameters used
542
+ to generate performance map
543
+ :type performance_maps_param: dict or None
544
+ :param depth_map_dir: directory to write triangulation output depth
545
+ map.
546
+ :type depth_map_dir: None or str
547
+ :param save_output_coordinates: Save X, Y, Z coords in depth_map_dir
548
+ :type save_output_coordinates: bool
549
+ :param save_output_color: Save color depth map in depth_map_dir
550
+ :type save_output_color: bool
551
+ :param save_output_classification: Save classification depth map in
552
+ depth_map_dir
553
+ :type save_output_classification: bool
554
+ :param save_output_filling: Save filling depth map in depth_map_dir
555
+ :type save_output_filling: bool
556
+ :param save_output_performance_map: Save performance map in
557
+ depth_map_dir
558
+ :type save_output_performance_map: bool
559
+ :param save_output_ambiguity: Save ambiguity in
560
+ depth_map_dir
561
+ :type save_output_ambiguity: bool
562
+
563
+ :return: point cloud \
564
+ The CarsDataset contains:
565
+
566
+ - N x M Delayed tiles \
567
+ Each tile will be a future xarray Dataset containing:
568
+
569
+ - data : with keys : "x", "y", "z", "corr_msk"\
570
+ optional: "texture", "msk", "z_inf", "z_sup"
571
+ - attrs with keys: "margins", "epi_full_size", "epsg"
572
+ - attributes containing: "disp_lower_bound", "disp_upper_bound", \
573
+ "elevation_delta_lower_bound","elevation_delta_upper_bound"
574
+
575
+ :rtype: Tuple(CarsDataset, CarsDataset)
576
+ """
577
+ # Default orchestrator
578
+ if orchestrator is None:
579
+ # Create default sequential orchestrator for current application
580
+ # be awere, no out_json will be shared between orchestrators
581
+ # No files saved
582
+ self.orchestrator = ocht.Orchestrator(
583
+ orchestrator_conf={"mode": "sequential"}
584
+ )
585
+ else:
586
+ self.orchestrator = orchestrator
587
+
588
+ if epipolar_disparity_map.dataset_type not in ("arrays", "points"):
589
+ raise RuntimeError(
590
+ "Triangulation application doesn't support this input "
591
+ "data format"
592
+ )
593
+
594
+ # Interpolate grid
595
+ interpolated_grid_left = RectificationGrid(
596
+ grid_left["path"],
597
+ interpolator=geometry_plugin.interpolator,
598
+ )
599
+ interpolated_grid_right = RectificationGrid(
600
+ grid_right["path"],
601
+ interpolator=geometry_plugin.interpolator,
602
+ )
603
+
604
+ broadcasted_interpolated_grid_left = self.orchestrator.cluster.scatter(
605
+ interpolated_grid_left
606
+ )
607
+ broadcasted_interpolated_grid_right = self.orchestrator.cluster.scatter(
608
+ interpolated_grid_right
609
+ )
610
+
611
+ sensor1 = sensor_image_left[sens_cst.INPUT_IMG]
612
+ sensor2 = sensor_image_right[sens_cst.INPUT_IMG]
613
+ geomodel1 = sensor_image_left[sens_cst.INPUT_GEO_MODEL]
614
+ geomodel2 = sensor_image_right[sens_cst.INPUT_GEO_MODEL]
615
+
616
+ if epipolar_disparity_map.dataset_type != "points":
617
+ if source_pc_names is None:
618
+ source_pc_names = ["PAIR_0"]
619
+
620
+ if pair_dump_dir is None:
621
+ pair_dump_dir = os.path.join(self.orchestrator.out_dir, "tmp")
622
+
623
+ # Get local conf left image for this in_json iteration
624
+ conf_left_img = sensor_image_left[sens_cst.INPUT_IMG]
625
+ # Check left image and raise a warning
626
+ # if different left images are used along with snap_to_img1 mode
627
+ if self.ref_left_img is None:
628
+ self.ref_left_img = conf_left_img
629
+ else:
630
+ if self.snap_to_img1 and self.ref_left_img != conf_left_img:
631
+ logging.warning(
632
+ "snap_to_left_image mode is used but inputs "
633
+ "have different images as their "
634
+ "left image in pair. This may result in "
635
+ "increasing registration discrepancies between pairs"
636
+ )
637
+
638
+ # Add log about geoid
639
+ if geoid_path is not None:
640
+ alt_reference = "geoid"
641
+ else:
642
+ alt_reference = "ellipsoid"
643
+
644
+ # Add infos to orchestrator.out_json
645
+ updating_dict = {
646
+ application_constants.APPLICATION_TAG: {
647
+ triangulation_constants.TRIANGULATION_RUN_TAG: {
648
+ pair_key: {
649
+ triangulation_constants.ALT_REFERENCE_TAG: (
650
+ alt_reference
651
+ )
652
+ },
653
+ }
654
+ }
655
+ }
656
+ self.orchestrator.update_out_info(updating_dict)
657
+
658
+ if self.snap_to_img1:
659
+ grid_right = uncorrected_grid_right
660
+ if grid_right is None:
661
+ logging.error(
662
+ "Uncorrected grid was not given in order "
663
+ "to snap it to img1"
664
+ )
665
+
666
+ # Compute disp_min and disp_max location for epipolar grid
667
+
668
+ # Transform
669
+ disp_min_tiling = epipolar_disparity_map.attributes[
670
+ "disp_min_tiling"
671
+ ]
672
+ disp_max_tiling = epipolar_disparity_map.attributes[
673
+ "disp_max_tiling"
674
+ ]
675
+
676
+ # change to N+1 M+1 dimension, fitting to tiling
677
+ (
678
+ disp_min_tiling,
679
+ disp_max_tiling,
680
+ ) = tiling.transform_disp_range_grid_to_two_layers(
681
+ disp_min_tiling, disp_max_tiling
682
+ )
683
+ (
684
+ epipolar_grid_min,
685
+ epipolar_grid_max,
686
+ ) = grid_generation_algo.compute_epipolar_grid_min_max(
687
+ geometry_plugin,
688
+ tiling.transform_four_layers_to_two_layers_grid(
689
+ epipolar_image.tiling_grid
690
+ ),
691
+ sensor1,
692
+ sensor2,
693
+ geomodel1,
694
+ geomodel2,
695
+ grid_left,
696
+ grid_right,
697
+ epsg,
698
+ disp_min_tiling,
699
+ disp_max_tiling,
700
+ )
701
+ # update attributes for corresponding tiles in cloud fusion
702
+ # TODO remove with refactoring
703
+ pc_attributes = {
704
+ "used_epsg_for_terrain_grid": epsg,
705
+ "epipolar_grid_min": epipolar_grid_min,
706
+ "epipolar_grid_max": epipolar_grid_max,
707
+ "largest_epipolar_region": epipolar_image.attributes[
708
+ "largest_epipolar_region"
709
+ ],
710
+ "source_pc_names": source_pc_names,
711
+ "source_pc_name": pair_key,
712
+ "color_type": epipolar_image.attributes["image_type"],
713
+ "opt_epipolar_tile_size": epipolar_image.attributes[
714
+ "tile_width"
715
+ ],
716
+ }
717
+
718
+ if geoid_path:
719
+ pc_attributes["geoid"] = (geoid_path,)
720
+
721
+ if epipolar_disparity_map.dataset_type not in ("arrays", "points"):
722
+ raise RuntimeError(
723
+ "Triangulation application doesn't support this input "
724
+ "data format"
725
+ )
726
+
727
+ # Check performance_maps_parameters
728
+ performance_maps_to_generate = None
729
+ if performance_maps_param is not None:
730
+ if "performance_map_method" not in performance_maps_param:
731
+ raise RuntimeError("No performance_map_method specified")
732
+ performance_maps_to_generate = performance_maps_param[
733
+ "performance_map_method"
734
+ ]
735
+ if "perf_ambiguity_threshold" not in performance_maps_param:
736
+ raise RuntimeError("No perf_ambiguity_threshold specified")
737
+
738
+ # Create CarsDataset
739
+ # Epipolar_point_cloud
740
+ epipolar_point_cloud = cars_dataset.CarsDataset(
741
+ epipolar_disparity_map.dataset_type,
742
+ name="triangulation_" + pair_key,
743
+ )
744
+ epipolar_point_cloud.create_empty_copy(epipolar_disparity_map)
745
+
746
+ # Update attributes to get epipolar info
747
+ epipolar_point_cloud.attributes.update(pc_attributes)
748
+
749
+ # Save objects
750
+ # Save as depth map
751
+ self.save_triangulation_output(
752
+ epipolar_point_cloud,
753
+ sensor_image_left,
754
+ depth_map_dir,
755
+ pair_dump_dir if self.save_intermediate_data else None,
756
+ performance_maps_to_generate,
757
+ save_output_coordinates,
758
+ save_output_color,
759
+ save_output_classification,
760
+ save_output_filling,
761
+ save_output_performance_map,
762
+ save_output_ambiguity,
763
+ )
764
+ self.fill_index(
765
+ save_output_coordinates,
766
+ save_output_color,
767
+ save_output_classification,
768
+ save_output_filling,
769
+ save_output_performance_map,
770
+ save_output_ambiguity,
771
+ pair_key,
772
+ )
773
+ # Save as point cloud
774
+ point_cloud = cars_dataset.CarsDataset(
775
+ "points",
776
+ name="triangulation_flatten_" + pair_key,
777
+ )
778
+ point_cloud.create_empty_copy(epipolar_point_cloud)
779
+ point_cloud.attributes = epipolar_point_cloud.attributes
780
+
781
+ csv_pc_dir_name, laz_pc_dir_name = (
782
+ self.create_point_cloud_directories(
783
+ pair_dump_dir, point_cloud_dir, point_cloud
784
+ )
785
+ )
786
+
787
+ # Get saving infos in order to save tiles when they are computed
788
+ [saving_info_epipolar] = self.orchestrator.get_saving_infos(
789
+ [epipolar_point_cloud]
790
+ )
791
+ saving_info_flatten = None
792
+ if self.save_intermediate_data or point_cloud_dir is not None:
793
+ [saving_info_flatten] = self.orchestrator.get_saving_infos(
794
+ [point_cloud]
795
+ )
796
+
797
+ # Generate Point clouds
798
+
799
+ # initialize empty index file for point cloud product if official
800
+ # product is requested
801
+ pc_index = None
802
+ if point_cloud_dir:
803
+ pc_index = {}
804
+
805
+ for col in range(epipolar_disparity_map.shape[1]):
806
+ for row in range(epipolar_disparity_map.shape[0]):
807
+ if epipolar_disparity_map[row, col] is not None:
808
+ # update saving infos for potential replacement
809
+ full_saving_info_epipolar = ocht.update_saving_infos(
810
+ saving_info_epipolar, row=row, col=col
811
+ )
812
+ full_saving_info_flatten = None
813
+ if saving_info_flatten is not None:
814
+ full_saving_info_flatten = ocht.update_saving_infos(
815
+ saving_info_flatten, row=row, col=col
816
+ )
817
+
818
+ csv_pc_file_name, laz_pc_file_name = (
819
+ triangulation_wrap.generate_point_cloud_file_names(
820
+ csv_pc_dir_name,
821
+ laz_pc_dir_name,
822
+ row,
823
+ col,
824
+ pc_index,
825
+ pair_key,
826
+ )
827
+ )
828
+
829
+ # Compute points
830
+ (
831
+ epipolar_point_cloud[row][col],
832
+ point_cloud[row][col],
833
+ ) = self.orchestrator.cluster.create_task(
834
+ triangulation_wrapper, nout=2
835
+ )(
836
+ epipolar_disparity_map[row, col],
837
+ sensor1,
838
+ sensor2,
839
+ geomodel1,
840
+ geomodel2,
841
+ broadcasted_interpolated_grid_left,
842
+ broadcasted_interpolated_grid_right,
843
+ geometry_plugin,
844
+ epsg,
845
+ geoid_path=geoid_path,
846
+ denoising_overload_fun=denoising_overload_fun,
847
+ cloud_id=cloud_id,
848
+ performance_maps_to_generate=(
849
+ performance_maps_to_generate
850
+ ),
851
+ performance_maps_parameters=performance_maps_param,
852
+ point_cloud_csv_file_name=csv_pc_file_name,
853
+ point_cloud_laz_file_name=laz_pc_file_name,
854
+ saving_info_epipolar=full_saving_info_epipolar,
855
+ saving_info_flatten=full_saving_info_flatten,
856
+ )
857
+
858
+ # update point cloud index
859
+ if point_cloud_dir:
860
+ self.orchestrator.update_index(pc_index)
861
+
862
+ else:
863
+ epipolar_point_cloud = cars_dataset.CarsDataset(
864
+ "points", name="pandora_sparse_matching_" + pair_key
865
+ )
866
+ epipolar_point_cloud.create_empty_copy(epipolar_disparity_map)
867
+
868
+ # Update attributes to get epipolar info
869
+ epipolar_point_cloud.attributes.update(epipolar_image.attributes)
870
+
871
+ # Add to replace list so tiles will be readable at the same time
872
+ self.orchestrator.add_to_replace_lists(
873
+ epipolar_point_cloud,
874
+ cars_ds_name="triangulated_matches",
875
+ )
876
+
877
+ # Get saving infos in order to save tiles when they are computed
878
+ [saving_info_matches] = self.orchestrator.get_saving_infos(
879
+ [epipolar_point_cloud]
880
+ )
881
+ for col in range(epipolar_disparity_map.shape[1]):
882
+ for row in range(epipolar_disparity_map.shape[0]):
883
+ if epipolar_disparity_map[row, col] is not None:
884
+ # initialize list of matches
885
+ full_saving_info_matches = ocht.update_saving_infos(
886
+ saving_info_matches, row=row, col=col
887
+ )
888
+
889
+ # Compute points
890
+ (
891
+ epipolar_point_cloud[row][col]
892
+ ) = self.orchestrator.cluster.create_task(
893
+ triangulation_wrapper_matches, nout=1
894
+ )(
895
+ epipolar_disparity_map[row, col],
896
+ sensor1,
897
+ sensor2,
898
+ geomodel1,
899
+ geomodel2,
900
+ broadcasted_interpolated_grid_left,
901
+ broadcasted_interpolated_grid_right,
902
+ geometry_plugin,
903
+ full_saving_info_matches,
904
+ epsg,
905
+ )
906
+
907
+ return epipolar_point_cloud
908
+
909
+
910
+ def triangulation_wrapper( # pylint: disable=too-many-positional-arguments
911
+ disparity_object: xr.Dataset,
912
+ sensor1,
913
+ sensor2,
914
+ geomodel1,
915
+ geomodel2,
916
+ grid1,
917
+ grid2,
918
+ geometry_plugin,
919
+ epsg,
920
+ geoid_path=None,
921
+ denoising_overload_fun=None,
922
+ cloud_id=None,
923
+ performance_maps_to_generate=None,
924
+ performance_maps_parameters=None,
925
+ point_cloud_csv_file_name=None,
926
+ point_cloud_laz_file_name=None,
927
+ saving_info_epipolar=None,
928
+ saving_info_flatten=None,
929
+ ) -> Dict[str, Tuple[xr.Dataset, xr.Dataset]]:
930
+ """
931
+ Compute point clouds from image objects and disparity objects.
932
+
933
+ :param disparity_object: Left disparity map dataset with :
934
+ - cst_disp.MAP
935
+ - cst_disp.VALID
936
+ - cst.EPI_TEXTURE
937
+ :type disparity_object: xr.Dataset
938
+ :param sensor1: path to left sensor image
939
+ :type sensor1: str
940
+ :param sensor2: path to right sensor image
941
+ :type sensor2: str
942
+ :param geomodel1: path and attributes for left geomodel
943
+ :type geomodel1: dict
944
+ :param geomodel2: path and attributes for right geomodel
945
+ :type geomodel2: dict
946
+ :param grid1: dataset of the reference image grid file
947
+ :type grid1: CarsDataset
948
+ :param grid2: dataset of the secondary image grid file
949
+ :type grid2: CarsDataset
950
+ :param geometry_plugin: geometry plugin to use
951
+ :type geometry_plugin: AbstractGeometry
952
+ :param geoid_path: Geoid used for altimetric reference. Defaults to None
953
+ for using ellipsoid as altimetric reference.
954
+ :type geoid_path: str
955
+ :param performance_maps_to_generate: None or list containing
956
+ "risk" or "intervals"
957
+ :param performance_maps_parameters: parameters used to
958
+ generate performance map
959
+ :type performance_maps_parameters: dict or None
960
+ :param denoising_overload_fun: function to overload dataset
961
+ :type denoising_overload_fun: fun
962
+
963
+ :return: Left disparity object
964
+
965
+ Returned object is composed of :
966
+ - dataset with :
967
+ - cst.X
968
+ - cst.Y
969
+ - cst.Z
970
+ - cst.EPI_TEXTURE
971
+ - cst.Z_INF (optional)
972
+ - cst.Z_SUP (optional)
973
+ """
974
+
975
+ # Get disparity maps
976
+ disp_ref = disparity_object
977
+
978
+ # Triangulate
979
+ if isinstance(disp_ref, xr.Dataset):
980
+ # Triangulate epipolar dense disparities
981
+ points = triangulation_algo.triangulate(
982
+ geometry_plugin,
983
+ sensor1,
984
+ sensor2,
985
+ geomodel1,
986
+ geomodel2,
987
+ grid1,
988
+ grid2,
989
+ disp_ref,
990
+ )
991
+
992
+ if performance_maps_to_generate is not None:
993
+ for perf_method in performance_maps_to_generate:
994
+ # Generate keys to use
995
+ if perf_method == "risk":
996
+ # From pandora loader overload
997
+ disp_keys = [
998
+ "confidence_from_disp_inf_from_risk.cars_2",
999
+ "confidence_from_disp_sup_from_risk.cars_2",
1000
+ ]
1001
+ cars_inf_key = cst.POINT_CLOUD_LAYER_INF_FROM_RISK
1002
+ cars_sup_key = cst.POINT_CLOUD_LAYER_SUP_FROM_RISK
1003
+ cars_perf_key = cst.POINT_CLOUD_PERFORMANCE_MAP_FROM_RISK
1004
+ use_ambiguity = True
1005
+ else:
1006
+ # From pandora loader overload
1007
+ disp_keys = [
1008
+ "confidence_from_interval_bounds_inf.cars_3",
1009
+ "confidence_from_interval_bounds_sup.cars_3",
1010
+ ]
1011
+ cars_inf_key = cst.POINT_CLOUD_LAYER_INF_FROM_INTERVALS
1012
+ cars_sup_key = cst.POINT_CLOUD_LAYER_SUP_FROM_INTERVALS
1013
+ cars_perf_key = (
1014
+ cst.POINT_CLOUD_PERFORMANCE_MAP_FROM_INTERVALS
1015
+ )
1016
+ use_ambiguity = False
1017
+
1018
+ ambiguity_map = None
1019
+ perf_ambiguity_threshold = None
1020
+ if use_ambiguity:
1021
+ ambiguity_map = disp_ref["ambiguity"]
1022
+ perf_ambiguity_threshold = performance_maps_parameters[
1023
+ "perf_ambiguity_threshold"
1024
+ ]
1025
+ # triangulate disp inf and supp
1026
+ points_inf = triangulation_algo.triangulate(
1027
+ geometry_plugin,
1028
+ sensor1,
1029
+ sensor2,
1030
+ geomodel1,
1031
+ geomodel2,
1032
+ grid1,
1033
+ grid2,
1034
+ disp_ref,
1035
+ disp_key=disp_keys[0],
1036
+ )
1037
+
1038
+ points_sup = triangulation_algo.triangulate(
1039
+ geometry_plugin,
1040
+ sensor1,
1041
+ sensor2,
1042
+ geomodel1,
1043
+ geomodel2,
1044
+ grid1,
1045
+ grid2,
1046
+ disp_ref,
1047
+ disp_key=disp_keys[1],
1048
+ )
1049
+
1050
+ points[cst.STEREO_REF][cars_inf_key] = points_inf[
1051
+ cst.STEREO_REF
1052
+ ][cst.Z]
1053
+ points[cst.STEREO_REF][cars_sup_key] = points_sup[
1054
+ cst.STEREO_REF
1055
+ ][cst.Z]
1056
+ # Generate performance map
1057
+ points[cst.STEREO_REF][cars_perf_key] = (
1058
+ triangulation_wrap.compute_performance_map(
1059
+ points[cst.STEREO_REF][cst.Z],
1060
+ points[cst.STEREO_REF][cars_inf_key],
1061
+ points[cst.STEREO_REF][cars_sup_key],
1062
+ ambiguity_map=ambiguity_map,
1063
+ perf_ambiguity_threshold=perf_ambiguity_threshold,
1064
+ )
1065
+ )
1066
+
1067
+ elif isinstance(disp_ref, pandas.DataFrame):
1068
+ # Triangulate epipolar sparse matches
1069
+ points = {}
1070
+ points[cst.STEREO_REF] = triangulation_algo.triangulate_matches(
1071
+ geometry_plugin,
1072
+ sensor1,
1073
+ sensor2,
1074
+ geomodel1,
1075
+ geomodel2,
1076
+ grid1,
1077
+ grid2,
1078
+ disp_ref.to_numpy(),
1079
+ )
1080
+ else:
1081
+ logging.error(
1082
+ "Disp ref is neither xarray Dataset nor pandas DataFrame"
1083
+ )
1084
+ raise TypeError(
1085
+ "Disp ref is neither xarray Dataset nor pandas DataFrame"
1086
+ )
1087
+
1088
+ if geoid_path is not None: # if user pass a geoid, use it as alt reference
1089
+ for key, point in points.items():
1090
+ points[key] = triangulation_wrap.geoid_offset(point, geoid_path)
1091
+
1092
+ # Fill datasets
1093
+ pc_dataset = points[cst.STEREO_REF]
1094
+ pc_dataset.attrs["cloud_id"] = cloud_id
1095
+
1096
+ # Overload dataset with denoising fun
1097
+ if denoising_overload_fun is not None:
1098
+ if isinstance(pc_dataset, xr.Dataset):
1099
+ denoising_overload_fun(
1100
+ pc_dataset,
1101
+ sensor1,
1102
+ sensor2,
1103
+ geomodel1,
1104
+ geomodel2,
1105
+ grid1,
1106
+ grid2,
1107
+ geometry_plugin,
1108
+ disp_ref,
1109
+ )
1110
+ else:
1111
+ raise RuntimeError("wrong pc type for denoising func")
1112
+
1113
+ attributes = {
1114
+ cst.CROPPED_DISPARITY_RANGE: (
1115
+ ocht.get_disparity_range_cropped(disparity_object)
1116
+ )
1117
+ }
1118
+ cars_dataset.fill_dataset(
1119
+ pc_dataset,
1120
+ saving_info=saving_info_epipolar,
1121
+ window=cars_dataset.get_window_dataset(disparity_object),
1122
+ profile=cars_dataset.get_profile_rasterio(disparity_object),
1123
+ attributes=attributes,
1124
+ overlaps=cars_dataset.get_overlaps_dataset(disparity_object),
1125
+ )
1126
+
1127
+ # Flatten point cloud to save it as LAZ
1128
+ flatten_pc_dataset = None
1129
+ if point_cloud_csv_file_name or point_cloud_laz_file_name:
1130
+ # Convert epipolar array into point cloud
1131
+ flatten_pc_dataset, cloud_epsg = (
1132
+ pc_transform.depth_map_dataset_to_dataframe(pc_dataset, epsg)
1133
+ )
1134
+ # Convert to wanted epsg
1135
+ if epsg is not None and cloud_epsg != epsg:
1136
+ projection.point_cloud_conversion_dataframe(
1137
+ flatten_pc_dataset, cloud_epsg, epsg
1138
+ )
1139
+ cloud_epsg = epsg
1140
+
1141
+ # Fill attributes for LAZ saving
1142
+ color_type = pc_transform.get_color_type([pc_dataset])
1143
+ attributes = {
1144
+ "epsg": cloud_epsg,
1145
+ "color_type": color_type,
1146
+ }
1147
+ cars_dataset.fill_dataframe(
1148
+ flatten_pc_dataset,
1149
+ saving_info=saving_info_flatten,
1150
+ attributes=attributes,
1151
+ )
1152
+ # Save point cloud in worker
1153
+ if point_cloud_csv_file_name:
1154
+ cars_dataset.run_save_points(
1155
+ flatten_pc_dataset,
1156
+ point_cloud_csv_file_name,
1157
+ overwrite=True,
1158
+ point_cloud_format="csv",
1159
+ overwrite_file_name=False,
1160
+ )
1161
+ if point_cloud_laz_file_name:
1162
+ cars_dataset.run_save_points(
1163
+ flatten_pc_dataset,
1164
+ point_cloud_laz_file_name,
1165
+ overwrite=True,
1166
+ point_cloud_format="laz",
1167
+ overwrite_file_name=False,
1168
+ )
1169
+
1170
+ return pc_dataset, flatten_pc_dataset
1171
+
1172
+
1173
+ # pylint: disable=too-many-positional-arguments
1174
+ def triangulation_wrapper_matches(
1175
+ matches,
1176
+ sensor1,
1177
+ sensor2,
1178
+ geomodel1,
1179
+ geomodel2,
1180
+ grid1,
1181
+ grid2,
1182
+ geometry_plugin,
1183
+ full_saving_info_matches,
1184
+ epsg,
1185
+ ):
1186
+ """
1187
+ :param matches: matches
1188
+ :type matches: cars_dataset
1189
+ :param sensor1: path to left sensor image
1190
+ :type sensor1: str
1191
+ :param sensor2: path to right sensor image
1192
+ :type sensor2: str
1193
+ :param interpolated_grid1: rectification grid left
1194
+ :type interpolated_grid1: shareloc.rectificationGrid
1195
+ :param interpolated_grid2: rectification grid right
1196
+ :type interpolated_grid2: shareloc.rectificationGrid
1197
+ :param geometry_plugin: geometry plugin to use
1198
+ :type geometry_plugin: AbstractGeometry
1199
+ :param epsg: ground epsg
1200
+ :type epsg: int
1201
+ """
1202
+
1203
+ # Ignore this tile if there are not match
1204
+ if matches is None or matches.empty:
1205
+ return None
1206
+
1207
+ epipolar_matches = matches.to_numpy()
1208
+
1209
+ sensor_matches = geometry_plugin.matches_to_sensor_coords(
1210
+ grid1,
1211
+ grid2,
1212
+ epipolar_matches,
1213
+ cst.MATCHES_MODE,
1214
+ )
1215
+ sensor_matches = np.concatenate(sensor_matches, axis=1)
1216
+ matches = np.concatenate(
1217
+ [
1218
+ epipolar_matches,
1219
+ sensor_matches,
1220
+ ],
1221
+ axis=1,
1222
+ )
1223
+
1224
+ # Triangulate matches
1225
+ triangulated_matches = triangulation_algo.triangulate_sparse_matches(
1226
+ sensor1,
1227
+ sensor2,
1228
+ geomodel1,
1229
+ geomodel2,
1230
+ grid1,
1231
+ grid2,
1232
+ matches,
1233
+ geometry_plugin,
1234
+ epsg,
1235
+ )
1236
+
1237
+ cars_dataset.fill_dataframe(
1238
+ triangulated_matches,
1239
+ saving_info=full_saving_info_matches,
1240
+ attributes={"epsg": triangulated_matches.attrs["epsg"]},
1241
+ )
1242
+
1243
+ return triangulated_matches