cars 1.0.0rc3__cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.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.
Files changed (220) hide show
  1. cars/__init__.py +74 -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 +46 -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.cpython-313-x86_64-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 +597 -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 +278 -0
  53. cars/applications/dsm_filling/bulldozer_config/base_config.yaml +44 -0
  54. cars/applications/dsm_filling/bulldozer_filling_app.py +288 -0
  55. cars/applications/dsm_filling/exogenous_filling_app.py +341 -0
  56. cars/applications/dsm_merging/__init__.py +28 -0
  57. cars/applications/dsm_merging/abstract_dsm_merging_app.py +101 -0
  58. cars/applications/dsm_merging/weighted_fusion_app.py +639 -0
  59. cars/applications/grid_correction/__init__.py +30 -0
  60. cars/applications/grid_correction/abstract_grid_correction_app.py +103 -0
  61. cars/applications/grid_correction/grid_correction_app.py +557 -0
  62. cars/applications/grid_generation/__init__.py +30 -0
  63. cars/applications/grid_generation/abstract_grid_generation_app.py +142 -0
  64. cars/applications/grid_generation/epipolar_grid_generation_app.py +327 -0
  65. cars/applications/grid_generation/grid_generation_algo.py +388 -0
  66. cars/applications/grid_generation/grid_generation_constants.py +46 -0
  67. cars/applications/grid_generation/transform_grid.py +88 -0
  68. cars/applications/ground_truth_reprojection/__init__.py +30 -0
  69. cars/applications/ground_truth_reprojection/abstract_ground_truth_reprojection_app.py +137 -0
  70. cars/applications/ground_truth_reprojection/direct_localization_app.py +629 -0
  71. cars/applications/ground_truth_reprojection/ground_truth_reprojection_algo.py +275 -0
  72. cars/applications/point_cloud_outlier_removal/__init__.py +30 -0
  73. cars/applications/point_cloud_outlier_removal/abstract_outlier_removal_app.py +385 -0
  74. cars/applications/point_cloud_outlier_removal/outlier_removal_algo.py +392 -0
  75. cars/applications/point_cloud_outlier_removal/outlier_removal_constants.py +43 -0
  76. cars/applications/point_cloud_outlier_removal/small_components_app.py +522 -0
  77. cars/applications/point_cloud_outlier_removal/statistical_app.py +528 -0
  78. cars/applications/rasterization/__init__.py +30 -0
  79. cars/applications/rasterization/abstract_pc_rasterization_app.py +183 -0
  80. cars/applications/rasterization/rasterization_algo.py +534 -0
  81. cars/applications/rasterization/rasterization_constants.py +38 -0
  82. cars/applications/rasterization/rasterization_wrappers.py +639 -0
  83. cars/applications/rasterization/simple_gaussian_app.py +1152 -0
  84. cars/applications/resampling/__init__.py +28 -0
  85. cars/applications/resampling/abstract_resampling_app.py +187 -0
  86. cars/applications/resampling/bicubic_resampling_app.py +760 -0
  87. cars/applications/resampling/resampling_algo.py +590 -0
  88. cars/applications/resampling/resampling_constants.py +36 -0
  89. cars/applications/resampling/resampling_wrappers.py +309 -0
  90. cars/applications/sensors_subsampling/__init__.py +32 -0
  91. cars/applications/sensors_subsampling/abstract_subsampling_app.py +109 -0
  92. cars/applications/sensors_subsampling/rasterio_subsampling_app.py +420 -0
  93. cars/applications/sensors_subsampling/subsampling_algo.py +108 -0
  94. cars/applications/sparse_matching/__init__.py +30 -0
  95. cars/applications/sparse_matching/abstract_sparse_matching_app.py +599 -0
  96. cars/applications/sparse_matching/sift_app.py +724 -0
  97. cars/applications/sparse_matching/sparse_matching_algo.py +360 -0
  98. cars/applications/sparse_matching/sparse_matching_constants.py +66 -0
  99. cars/applications/sparse_matching/sparse_matching_wrappers.py +282 -0
  100. cars/applications/triangulation/__init__.py +32 -0
  101. cars/applications/triangulation/abstract_triangulation_app.py +227 -0
  102. cars/applications/triangulation/line_of_sight_intersection_app.py +1243 -0
  103. cars/applications/triangulation/pc_transform.py +552 -0
  104. cars/applications/triangulation/triangulation_algo.py +371 -0
  105. cars/applications/triangulation/triangulation_constants.py +38 -0
  106. cars/applications/triangulation/triangulation_wrappers.py +259 -0
  107. cars/bundleadjustment.py +750 -0
  108. cars/cars.py +179 -0
  109. cars/conf/__init__.py +23 -0
  110. cars/conf/geoid/egm96.grd +0 -0
  111. cars/conf/geoid/egm96.grd.hdr +15 -0
  112. cars/conf/input_parameters.py +156 -0
  113. cars/conf/mask_cst.py +35 -0
  114. cars/core/__init__.py +23 -0
  115. cars/core/cars_logging.py +402 -0
  116. cars/core/constants.py +191 -0
  117. cars/core/constants_disparity.py +50 -0
  118. cars/core/datasets.py +140 -0
  119. cars/core/geometry/__init__.py +27 -0
  120. cars/core/geometry/abstract_geometry.py +1130 -0
  121. cars/core/geometry/shareloc_geometry.py +604 -0
  122. cars/core/inputs.py +568 -0
  123. cars/core/outputs.py +176 -0
  124. cars/core/preprocessing.py +722 -0
  125. cars/core/projection.py +843 -0
  126. cars/core/roi_tools.py +215 -0
  127. cars/core/tiling.py +774 -0
  128. cars/core/utils.py +164 -0
  129. cars/data_structures/__init__.py +23 -0
  130. cars/data_structures/cars_dataset.py +1544 -0
  131. cars/data_structures/cars_dict.py +74 -0
  132. cars/data_structures/corresponding_tiles_tools.py +186 -0
  133. cars/data_structures/dataframe_converter.py +185 -0
  134. cars/data_structures/format_transformation.py +297 -0
  135. cars/devibrate.py +689 -0
  136. cars/extractroi.py +264 -0
  137. cars/orchestrator/__init__.py +23 -0
  138. cars/orchestrator/achievement_tracker.py +125 -0
  139. cars/orchestrator/cluster/__init__.py +37 -0
  140. cars/orchestrator/cluster/abstract_cluster.py +250 -0
  141. cars/orchestrator/cluster/abstract_dask_cluster.py +381 -0
  142. cars/orchestrator/cluster/dask_cluster_tools.py +103 -0
  143. cars/orchestrator/cluster/dask_config/README.md +94 -0
  144. cars/orchestrator/cluster/dask_config/dask.yaml +21 -0
  145. cars/orchestrator/cluster/dask_config/distributed.yaml +70 -0
  146. cars/orchestrator/cluster/dask_config/jobqueue.yaml +26 -0
  147. cars/orchestrator/cluster/dask_config/reference_confs/dask-schema.yaml +137 -0
  148. cars/orchestrator/cluster/dask_config/reference_confs/dask.yaml +26 -0
  149. cars/orchestrator/cluster/dask_config/reference_confs/distributed-schema.yaml +1009 -0
  150. cars/orchestrator/cluster/dask_config/reference_confs/distributed.yaml +273 -0
  151. cars/orchestrator/cluster/dask_config/reference_confs/jobqueue.yaml +212 -0
  152. cars/orchestrator/cluster/dask_jobqueue_utils.py +204 -0
  153. cars/orchestrator/cluster/local_dask_cluster.py +116 -0
  154. cars/orchestrator/cluster/log_wrapper.py +728 -0
  155. cars/orchestrator/cluster/mp_cluster/__init__.py +27 -0
  156. cars/orchestrator/cluster/mp_cluster/mp_factorizer.py +212 -0
  157. cars/orchestrator/cluster/mp_cluster/mp_objects.py +535 -0
  158. cars/orchestrator/cluster/mp_cluster/mp_tools.py +93 -0
  159. cars/orchestrator/cluster/mp_cluster/mp_wrapper.py +505 -0
  160. cars/orchestrator/cluster/mp_cluster/multiprocessing_cluster.py +986 -0
  161. cars/orchestrator/cluster/mp_cluster/multiprocessing_profiler.py +399 -0
  162. cars/orchestrator/cluster/pbs_dask_cluster.py +207 -0
  163. cars/orchestrator/cluster/sequential_cluster.py +139 -0
  164. cars/orchestrator/cluster/slurm_dask_cluster.py +234 -0
  165. cars/orchestrator/memory_tools.py +47 -0
  166. cars/orchestrator/orchestrator.py +755 -0
  167. cars/orchestrator/orchestrator_constants.py +29 -0
  168. cars/orchestrator/registry/__init__.py +23 -0
  169. cars/orchestrator/registry/abstract_registry.py +143 -0
  170. cars/orchestrator/registry/compute_registry.py +106 -0
  171. cars/orchestrator/registry/id_generator.py +116 -0
  172. cars/orchestrator/registry/replacer_registry.py +213 -0
  173. cars/orchestrator/registry/saver_registry.py +363 -0
  174. cars/orchestrator/registry/unseen_registry.py +118 -0
  175. cars/orchestrator/tiles_profiler.py +279 -0
  176. cars/pipelines/__init__.py +26 -0
  177. cars/pipelines/conf_resolution/conf_final_resolution.yaml +5 -0
  178. cars/pipelines/conf_resolution/conf_first_resolution.yaml +4 -0
  179. cars/pipelines/conf_resolution/conf_intermediate_resolution.yaml +2 -0
  180. cars/pipelines/default/__init__.py +26 -0
  181. cars/pipelines/default/default_pipeline.py +1095 -0
  182. cars/pipelines/filling/__init__.py +26 -0
  183. cars/pipelines/filling/filling.py +981 -0
  184. cars/pipelines/formatting/__init__.py +26 -0
  185. cars/pipelines/formatting/formatting.py +190 -0
  186. cars/pipelines/merging/__init__.py +26 -0
  187. cars/pipelines/merging/merging.py +439 -0
  188. cars/pipelines/parameters/__init__.py +0 -0
  189. cars/pipelines/parameters/advanced_parameters.py +256 -0
  190. cars/pipelines/parameters/advanced_parameters_constants.py +68 -0
  191. cars/pipelines/parameters/application_parameters.py +72 -0
  192. cars/pipelines/parameters/depth_map_inputs.py +0 -0
  193. cars/pipelines/parameters/dsm_inputs.py +349 -0
  194. cars/pipelines/parameters/dsm_inputs_constants.py +25 -0
  195. cars/pipelines/parameters/output_constants.py +52 -0
  196. cars/pipelines/parameters/output_parameters.py +435 -0
  197. cars/pipelines/parameters/sensor_inputs.py +859 -0
  198. cars/pipelines/parameters/sensor_inputs_constants.py +51 -0
  199. cars/pipelines/parameters/sensor_loaders/__init__.py +29 -0
  200. cars/pipelines/parameters/sensor_loaders/basic_classif_loader.py +86 -0
  201. cars/pipelines/parameters/sensor_loaders/basic_image_loader.py +98 -0
  202. cars/pipelines/parameters/sensor_loaders/pivot_classif_loader.py +90 -0
  203. cars/pipelines/parameters/sensor_loaders/pivot_image_loader.py +105 -0
  204. cars/pipelines/parameters/sensor_loaders/sensor_loader.py +93 -0
  205. cars/pipelines/parameters/sensor_loaders/sensor_loader_template.py +71 -0
  206. cars/pipelines/parameters/sensor_loaders/slurp_classif_loader.py +86 -0
  207. cars/pipelines/pipeline.py +119 -0
  208. cars/pipelines/pipeline_constants.py +38 -0
  209. cars/pipelines/pipeline_template.py +135 -0
  210. cars/pipelines/subsampling/__init__.py +26 -0
  211. cars/pipelines/subsampling/subsampling.py +358 -0
  212. cars/pipelines/surface_modeling/__init__.py +26 -0
  213. cars/pipelines/surface_modeling/surface_modeling.py +2098 -0
  214. cars/pipelines/tie_points/__init__.py +26 -0
  215. cars/pipelines/tie_points/tie_points.py +536 -0
  216. cars/starter.py +167 -0
  217. cars-1.0.0rc3.dist-info/METADATA +289 -0
  218. cars-1.0.0rc3.dist-info/RECORD +220 -0
  219. cars-1.0.0rc3.dist-info/WHEEL +6 -0
  220. cars-1.0.0rc3.dist-info/entry_points.txt +8 -0
@@ -0,0 +1,604 @@
1
+ #!/usr/bin/env python
2
+ # coding: utf8
3
+ #
4
+ # Copyright (c) 2023 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
+ Shareloc geometry sub class : CARS geometry wrappers functions to shareloc ones
23
+ """
24
+
25
+ import logging
26
+ from typing import List, Tuple, Union
27
+
28
+ import bindings_cpp # pylint: disable=E0401
29
+ import numpy as np
30
+ import rasterio as rio
31
+ import shareloc.geofunctions.rectification as rectif
32
+ import xarray as xr
33
+ from json_checker import And, Checker, Or
34
+ from shareloc.dtm_reader import dtm_reader
35
+ from shareloc.geofunctions import localization
36
+ from shareloc.geofunctions.rectification_grid import RectificationGrid
37
+ from shareloc.geofunctions.triangulation import epipolar_triangulation
38
+ from shareloc.geomodels.geomodel import GeoModel
39
+ from shareloc.geomodels.grid import Grid
40
+ from shareloc.geomodels.rpc import RPC
41
+ from shareloc.image import Image
42
+
43
+ from cars.core import constants as cst
44
+ from cars.core import projection
45
+ from cars.core.geometry.abstract_geometry import AbstractGeometry
46
+
47
+ GRID_TYPE = "GRID"
48
+ RPC_TYPE = "RPC"
49
+ GEO_MODEL_PATH_TAG = "path"
50
+ GEO_MODEL_TYPE_TAG = "model_type"
51
+
52
+
53
+ @AbstractGeometry.register_subclass("SharelocGeometry")
54
+ class SharelocGeometry(AbstractGeometry):
55
+ """
56
+ Shareloc geometry class
57
+ """
58
+
59
+ def __init__( # pylint: disable=too-many-positional-arguments
60
+ self,
61
+ geometry_plugin_conf,
62
+ dem=None,
63
+ geoid=None,
64
+ default_alt=None,
65
+ pairs_for_roi=None,
66
+ scaling_coeff=1,
67
+ output_dem_dir=None,
68
+ ):
69
+
70
+ super().__init__(
71
+ geometry_plugin_conf,
72
+ dem=dem,
73
+ geoid=geoid,
74
+ default_alt=default_alt,
75
+ pairs_for_roi=pairs_for_roi,
76
+ scaling_coeff=scaling_coeff,
77
+ output_dem_dir=output_dem_dir,
78
+ )
79
+
80
+ if dem is not None:
81
+ # fill_nodata option should be set when dealing with void in DTM
82
+ # see shareloc DTM limitations in sphinx doc for further detailsd
83
+ try:
84
+ if self.dem_roi is None:
85
+ roi_shareloc = None
86
+ else:
87
+ # From (x, y) to (y, x)
88
+ roi_shareloc = [
89
+ self.dem_roi[1],
90
+ self.dem_roi[0],
91
+ self.dem_roi[3],
92
+ self.dem_roi[2],
93
+ ]
94
+ dtm_image = dtm_reader(
95
+ self.dem,
96
+ self.geoid,
97
+ roi=roi_shareloc,
98
+ roi_is_in_physical_space=True,
99
+ fill_nodata="mean",
100
+ fill_value=0.0,
101
+ )
102
+ except RuntimeError as err:
103
+ mss = "the roi bounds are"
104
+ if mss in str(err):
105
+ new_except_mss = (
106
+ f"The extent of the roi lies outside "
107
+ f"the extent of the initial elevation : {err}"
108
+ )
109
+ raise RuntimeError(new_except_mss) from err
110
+ raise
111
+
112
+ self.elevation = (
113
+ bindings_cpp.DTMIntersection( # pylint: disable=I1101
114
+ dtm_image.epsg,
115
+ dtm_image.alt_data,
116
+ dtm_image.nb_rows,
117
+ dtm_image.nb_columns,
118
+ dtm_image.transform,
119
+ )
120
+ )
121
+ else:
122
+ self.elevation = default_alt
123
+
124
+ def check_conf(self, conf):
125
+ """
126
+ Check configuration
127
+
128
+ :param conf: configuration to check
129
+ :type conf: str or dict
130
+
131
+ :return: full dict
132
+ :rtype: dict
133
+
134
+ """
135
+
136
+ if conf is None:
137
+ raise RuntimeError("Geometry plugin configuration is None")
138
+
139
+ overloaded_conf = {}
140
+
141
+ if isinstance(conf, str):
142
+ conf = {"plugin_name": conf}
143
+
144
+ # overload conf
145
+ overloaded_conf["plugin_name"] = conf.get(
146
+ "plugin_name", "SharelocGeometry"
147
+ )
148
+ overloaded_conf["interpolator"] = conf.get("interpolator", "cubic")
149
+ overloaded_conf["dem_roi_margin_initial_elevation"] = conf.get(
150
+ "dem_roi_margin_initial_elevation", [0.25, 0.02]
151
+ )
152
+ overloaded_conf["dem_roi_margin_rectification"] = conf.get(
153
+ "dem_roi_margin_rectification", 0.5
154
+ )
155
+
156
+ geometry_schema = {
157
+ "plugin_name": str,
158
+ "interpolator": And(str, lambda x: x in ["cubic", "linear"]),
159
+ "dem_roi_margin_initial_elevation": [float],
160
+ "dem_roi_margin_rectification": And(
161
+ Or(float, int), lambda x: x > 0
162
+ ),
163
+ }
164
+
165
+ # Check conf
166
+ checker = Checker(geometry_schema)
167
+ checker.validate(overloaded_conf)
168
+
169
+ return overloaded_conf
170
+
171
+ def get_roi( # pylint: disable=too-many-positional-arguments
172
+ self,
173
+ pairs_for_roi,
174
+ epsg,
175
+ z_min=0,
176
+ z_max=0,
177
+ linear_margin=0,
178
+ constant_margin=0.012,
179
+ ):
180
+ """
181
+ Compute region of interest for intersection of DEM
182
+
183
+ :param pairs_for_roi: list of pairs of images and geomodels
184
+ :type pairs_for_roi: List[(str, dict, str, dict)]
185
+ :param dem_epsg: output EPSG code for ROI
186
+ :type dem_epsg: int
187
+ :param linear_margin: margin for ROI (factor of initial ROI size)
188
+ :type linear_margin: float
189
+ :param constant_margin: margin for ROI in degrees
190
+ :type constant_margin: float
191
+ """
192
+ base_roi = super().get_roi(
193
+ pairs_for_roi, epsg, z_min, z_max, linear_margin, constant_margin
194
+ )
195
+
196
+ coords_list = []
197
+ for image1, geomodel1, _, geomodel2 in pairs_for_roi:
198
+ # Footprint of rectification grid (with margins)
199
+ image1 = SharelocGeometry.load_image(image1["bands"]["b0"]["path"])
200
+ geomodel1 = self.load_geom_model(geomodel1)
201
+ geomodel2 = self.load_geom_model(geomodel2)
202
+
203
+ # With altitude z_min
204
+ epipolar_extent = rectif.get_epipolar_extent(
205
+ image1,
206
+ geomodel1,
207
+ geomodel2,
208
+ elevation=z_min,
209
+ grid_margin=self.rectification_grid_margin,
210
+ )
211
+ lat_min, lon_min, lat_max, lon_max = list(epipolar_extent)
212
+ coords_list.extend([(lon_min, lat_min), (lon_max, lat_max)])
213
+
214
+ # With altitude z_max
215
+ epipolar_extent = rectif.get_epipolar_extent(
216
+ image1,
217
+ geomodel1,
218
+ geomodel2,
219
+ elevation=z_max,
220
+ grid_margin=self.rectification_grid_margin,
221
+ )
222
+ lat_min, lon_min, lat_max, lon_max = list(epipolar_extent)
223
+ coords_list.extend([(lon_min, lat_min), (lon_max, lat_max)])
224
+
225
+ lon_list, lat_list = list(zip(*coords_list)) # noqa: B905
226
+ roi = [
227
+ min(lon_list) - constant_margin,
228
+ min(lat_list) - constant_margin,
229
+ max(lon_list) + constant_margin,
230
+ max(lat_list) + constant_margin,
231
+ ]
232
+ points = np.array(
233
+ [
234
+ (roi[0], roi[1], 0),
235
+ (roi[2], roi[3], 0),
236
+ (roi[0], roi[1], 0),
237
+ (roi[2], roi[3], 0),
238
+ ]
239
+ )
240
+ new_points = projection.point_cloud_conversion(points, 4326, epsg)
241
+ roi = [
242
+ min(new_points[:, 0]),
243
+ min(new_points[:, 1]),
244
+ max(new_points[:, 0]),
245
+ max(new_points[:, 1]),
246
+ ]
247
+
248
+ lon_size = roi[2] - roi[0]
249
+ lat_size = roi[3] - roi[1]
250
+
251
+ roi[0] -= linear_margin * lon_size
252
+ roi[1] -= linear_margin * lat_size
253
+ roi[2] += linear_margin * lon_size
254
+ roi[3] += linear_margin * lat_size
255
+
256
+ roi = [
257
+ min(roi[0], base_roi[0]),
258
+ min(roi[1], base_roi[1]),
259
+ max(roi[2], base_roi[2]),
260
+ max(roi[3], base_roi[3]),
261
+ ]
262
+
263
+ return roi
264
+
265
+ @staticmethod
266
+ def load_geom_model(model: dict) -> Union[Grid, RPC]:
267
+ """
268
+ Load geometric model and returns it as a shareloc object
269
+
270
+ :param model: Path and attributes for geometrical model
271
+ :type model: dict with keys "path" and "model_type"
272
+ :return: geometric model as a shareloc object (Grid or RPC)
273
+ """
274
+ geomodel = model[GEO_MODEL_PATH_TAG]
275
+ # Use RPC Type if none are used
276
+ if model.get(GEO_MODEL_TYPE_TAG):
277
+ geomodel_type = model[GEO_MODEL_TYPE_TAG]
278
+ else:
279
+ geomodel_type = RPC_TYPE
280
+
281
+ # Use RPCoptim class to use optimized C++ direct localizations
282
+ if geomodel_type == "RPC":
283
+ geomodel_type = "RPCoptim"
284
+
285
+ shareloc_model = GeoModel(geomodel, geomodel_type)
286
+
287
+ if shareloc_model is None:
288
+ raise ValueError(f"Model {geomodel} could not be read by shareloc")
289
+
290
+ return shareloc_model
291
+
292
+ @staticmethod
293
+ def load_image(img: str) -> Image:
294
+ """
295
+ Load the image using the Image class of Shareloc
296
+
297
+ :param img: path to the image
298
+ :return: The Image object
299
+ """
300
+ try:
301
+ shareloc_img = Image(img, vertical_direction="north")
302
+ except Exception as error:
303
+ raise ValueError(f"Image type {img} is not supported") from error
304
+
305
+ return shareloc_img
306
+
307
+ @staticmethod
308
+ def check_product_consistency(sensor: str, geomodel: dict) -> bool:
309
+ """
310
+ Test if the product is readable by the shareloc plugin
311
+
312
+ TODO: not used
313
+ - to evolve and use in CARS configuration early in pipeline process
314
+ (new early check input common application ?)
315
+
316
+ :param sensor: path to sensor image
317
+ :param geomodel: path and attributes for geometrical model
318
+ :return: sensor path and overloaded geomodel dict
319
+ """
320
+ # Check geomodel schema consistency
321
+ if isinstance(geomodel, str):
322
+ geomodel = {
323
+ "path": geomodel,
324
+ }
325
+ overloaded_geomodel = geomodel.copy()
326
+
327
+ # If model_type is not defined, default is "RPC"
328
+ overloaded_geomodel["model_type"] = geomodel.get("model_type", "RPC")
329
+
330
+ geomodel_schema = {"path": str, "model_type": str}
331
+ checker_geomodel = Checker(geomodel_schema)
332
+ checker_geomodel.validate(overloaded_geomodel)
333
+
334
+ # Try to read them using shareloc
335
+ for band in sensor["bands"]:
336
+ SharelocGeometry.load_image(sensor["bands"][band]["path"])
337
+ SharelocGeometry.load_geom_model(overloaded_geomodel)
338
+
339
+ return sensor, overloaded_geomodel
340
+
341
+ def triangulate( # pylint: disable=too-many-positional-arguments
342
+ self,
343
+ sensor1,
344
+ sensor2,
345
+ geomodel1,
346
+ geomodel2,
347
+ mode: str,
348
+ matches: Union[xr.Dataset, np.ndarray],
349
+ grid1: Union[dict, RectificationGrid],
350
+ grid2: Union[dict, RectificationGrid],
351
+ roi_key: Union[None, str] = None,
352
+ interpolation_method=None,
353
+ ) -> np.ndarray:
354
+ """
355
+ Performs triangulation from cars disparity or matches dataset
356
+ :param sensor1: path to left sensor image
357
+ :param sensor2: path to right sensor image
358
+ :param geomodel1: path and attributes for left geomodel
359
+ :param geomodel2: path and attributes for right geomodel
360
+ :param mode: triangulation mode
361
+ (constants.DISP_MODE or constants.MATCHES)
362
+ :param matches: cars disparity dataset or matches as numpy array
363
+ :param grid1: dict or RectificationGrid for epipolar grid of sensor1
364
+ :param grid2: dict or RectificationGrid for epipolar grid of sensor2
365
+ :param roi_key: dataset roi to use
366
+ (can be cst.ROI or cst.ROI_WITH_MARGINS)
367
+
368
+ :return: the long/lat/height numpy array in output of the triangulation
369
+ """
370
+ # read geomodels using shareloc
371
+ shareloc_model1 = SharelocGeometry.load_geom_model(geomodel1)
372
+ shareloc_model2 = SharelocGeometry.load_geom_model(geomodel2)
373
+
374
+ if isinstance(grid1, dict):
375
+ # create rectificationgrid
376
+ grid1 = RectificationGrid(
377
+ grid1["path"],
378
+ interpolator=self.interpolator,
379
+ )
380
+ grid2 = RectificationGrid(
381
+ grid2["path"],
382
+ interpolator=self.interpolator,
383
+ )
384
+
385
+ # perform matches triangulation
386
+ if mode is cst.MATCHES_MODE:
387
+ __, point_wgs84, __ = epipolar_triangulation(
388
+ matches=matches,
389
+ mask=None,
390
+ matches_type="sift",
391
+ geometrical_model_left=shareloc_model1,
392
+ geometrical_model_right=shareloc_model2,
393
+ grid_left=grid1,
394
+ grid_right=grid2,
395
+ residues=True,
396
+ fill_nan=True,
397
+ )
398
+
399
+ llh = point_wgs84.reshape((point_wgs84.shape[0], 1, 3))
400
+
401
+ elif mode is cst.DISP_MODE:
402
+ __, point_wgs84, __ = epipolar_triangulation(
403
+ matches=matches,
404
+ mask=None,
405
+ matches_type="disp",
406
+ geometrical_model_left=shareloc_model1,
407
+ geometrical_model_right=shareloc_model2,
408
+ grid_left=grid1,
409
+ grid_right=grid2,
410
+ residues=True,
411
+ fill_nan=True,
412
+ )
413
+
414
+ row = np.array(
415
+ range(matches.attrs[roi_key][1], matches.attrs[roi_key][3])
416
+ )
417
+ col = np.array(
418
+ range(matches.attrs[roi_key][0], matches.attrs[roi_key][2])
419
+ )
420
+
421
+ llh = point_wgs84.reshape((row.size, col.size, 3))
422
+ else:
423
+ logging.error(
424
+ "{} mode is not available in the "
425
+ "shareloc plugin triangulation".format(mode)
426
+ )
427
+ raise ValueError(
428
+ f"{mode} mode is not available"
429
+ " in the shareloc plugin triangulation"
430
+ )
431
+
432
+ return llh
433
+
434
+ # pylint: disable=too-many-positional-arguments
435
+ def generate_epipolar_grids(
436
+ self,
437
+ sensor1,
438
+ sensor2,
439
+ geomodel1,
440
+ geomodel2,
441
+ epipolar_step: int = 30,
442
+ ) -> Tuple[
443
+ np.ndarray, np.ndarray, List[float], List[float], List[int], float
444
+ ]:
445
+ """
446
+ Computes the left and right epipolar grids
447
+
448
+ :param sensor1: path to left sensor image
449
+ :param sensor2: path to right sensor image
450
+ :param geomodel1: path and attributes for left geomodel
451
+ :param geomodel2: path and attributes for right geomodel
452
+ :param epipolar_step: step to use to construct the epipolar grids
453
+ :return: Tuple composed of :
454
+ - the left epipolar grid as a numpy array
455
+ - the right epipolar grid as a numpy array
456
+ - the left grid origin as a list of float
457
+ - the left grid spacing as a list of float
458
+ - the epipolar image size as a list of int
459
+ (x-axis size is given with the index 0, y-axis size with index 1)
460
+ - the disparity to altitude ratio as a float
461
+
462
+ """
463
+ # read inputs using shareloc
464
+ shareloc_model1 = SharelocGeometry.load_geom_model(geomodel1)
465
+ shareloc_model2 = SharelocGeometry.load_geom_model(geomodel2)
466
+
467
+ image1 = SharelocGeometry.load_image(sensor1)
468
+ image2 = SharelocGeometry.load_image(sensor2)
469
+
470
+ # compute epipolar grids
471
+ (
472
+ grid1,
473
+ grid2,
474
+ [epipolar_size_y, epipolar_size_x],
475
+ alt_to_disp_ratio,
476
+ ) = rectif.compute_stereorectification_epipolar_grids(
477
+ image1,
478
+ shareloc_model1,
479
+ image2,
480
+ shareloc_model2,
481
+ self.elevation,
482
+ epi_step=epipolar_step,
483
+ margin=self.rectification_grid_margin,
484
+ )
485
+
486
+ # rearrange output to match the expected structure of CARS
487
+ # grid[:, :, 2] with altitudes is not used
488
+ grid1 = grid1[:, :, 0:2][:, :, ::-1]
489
+ grid2 = grid2[:, :, 0:2][:, :, ::-1]
490
+
491
+ epipolar_size_x = int(np.floor(epipolar_size_x))
492
+ epipolar_size_y = int(np.floor(epipolar_size_y))
493
+
494
+ origin = [
495
+ float(-self.rectification_grid_margin * epipolar_step),
496
+ float(-self.rectification_grid_margin * epipolar_step),
497
+ ]
498
+ spacing = [float(epipolar_step), float(epipolar_step)]
499
+
500
+ # alt_to_disp_ratio does not consider image resolution
501
+ with rio.open(sensor1, "r") as rio_dst:
502
+ pixel_size_x, pixel_size_y = (
503
+ rio_dst.transform[0],
504
+ rio_dst.transform[4],
505
+ )
506
+ mean_size = (abs(pixel_size_x) + abs(pixel_size_y)) / 2
507
+ disp_to_alt_ratio = mean_size / alt_to_disp_ratio
508
+
509
+ return (
510
+ grid1,
511
+ grid2,
512
+ origin,
513
+ spacing,
514
+ [epipolar_size_x, epipolar_size_y],
515
+ disp_to_alt_ratio,
516
+ )
517
+
518
+ def direct_loc( # pylint: disable=too-many-positional-arguments
519
+ self,
520
+ sensor,
521
+ geomodel,
522
+ x_coord: np.array,
523
+ y_coord: np.array,
524
+ z_coord: np.array = None,
525
+ ) -> np.ndarray:
526
+ """
527
+ For a given image point, compute the latitude, longitude, altitude
528
+
529
+ Advice: to be sure, use x,y,z inputs only
530
+
531
+ :param sensor: path to sensor image
532
+ :param geomodel: path and attributes for geomodel
533
+ :param x_coord: X Coordinate in input image sensor
534
+ :param y_coord: Y Coordinate in input image sensor
535
+ :param z_coord: Z Altitude coordinate to take the image
536
+ :return: Latitude, Longitude, Altitude coordinates as a numpy array
537
+ """
538
+ # load model and image with shareloc
539
+ shareloc_model = SharelocGeometry.load_geom_model(geomodel)
540
+ shareloc_image = SharelocGeometry.load_image(sensor)
541
+
542
+ # perform direct localization operation
543
+ loc = localization.Localization(
544
+ shareloc_model,
545
+ image=shareloc_image,
546
+ elevation=self.elevation,
547
+ epsg=4326,
548
+ )
549
+ # Bug: y_coord and x_coord inversion to fit Shareloc standards row/col.
550
+ # TODO: clean geometry convention calls in API
551
+ lonlatalt = loc.direct(
552
+ y_coord, x_coord, z_coord, using_geotransform=True
553
+ )
554
+ lonlatalt = np.squeeze(lonlatalt)
555
+ if len(lonlatalt.shape) == 1:
556
+ latlonalt = np.array([lonlatalt[1], lonlatalt[0], lonlatalt[2]])
557
+ else:
558
+ latlonalt = np.array(
559
+ [lonlatalt[:, 1], lonlatalt[:, 0], lonlatalt[:, 2]]
560
+ )
561
+ return latlonalt
562
+
563
+ def inverse_loc( # pylint: disable=too-many-positional-arguments
564
+ self,
565
+ sensor,
566
+ geomodel,
567
+ lat_coord: np.array,
568
+ lon_coord: np.array,
569
+ z_coord: np.array = None,
570
+ ) -> np.ndarray:
571
+ """
572
+ For a given image points list, compute the latitudes,
573
+ longitudes, altitudes
574
+
575
+ Advice: to be sure, use x,y,z list inputs only
576
+
577
+ :param sensor: path to sensor image
578
+ :param geomodel: path and attributes for geomodel
579
+ :param lat_coord: latitute Coordinate list
580
+ :param lon_coord: longitude Coordinates list
581
+ :param z_coord: Z Altitude list
582
+ :return: X / Y / Z Coordinates list in input image as a numpy array
583
+ """
584
+
585
+ # load model and image with shareloc
586
+ shareloc_model = SharelocGeometry.load_geom_model(geomodel)
587
+ shareloc_image = SharelocGeometry.load_image(sensor)
588
+
589
+ # perform inverse localization operation
590
+ loc = localization.Localization(
591
+ shareloc_model,
592
+ image=shareloc_image,
593
+ elevation=self.elevation,
594
+ epsg=4326,
595
+ )
596
+ # Rows and columns order is inversed
597
+ col, row, alti = loc.inverse(
598
+ lon_coord.astype(np.float64),
599
+ lat_coord.astype(np.float64),
600
+ h=z_coord.astype(np.float64),
601
+ using_geotransform=True,
602
+ )
603
+
604
+ return row, col, alti