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,598 @@
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
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"] = conf.get(
150
+ "dem_roi_margin", [0.25, 0.02]
151
+ )
152
+
153
+ geometry_schema = {
154
+ "plugin_name": str,
155
+ "interpolator": And(str, lambda x: x in ["cubic", "linear"]),
156
+ "dem_roi_margin": [float],
157
+ }
158
+
159
+ # Check conf
160
+ checker = Checker(geometry_schema)
161
+ checker.validate(overloaded_conf)
162
+
163
+ return overloaded_conf
164
+
165
+ def get_roi( # pylint: disable=too-many-positional-arguments
166
+ self,
167
+ pairs_for_roi,
168
+ epsg,
169
+ z_min=0,
170
+ z_max=0,
171
+ linear_margin=0,
172
+ constant_margin=0.012,
173
+ ):
174
+ """
175
+ Compute region of interest for intersection of DEM
176
+
177
+ :param pairs_for_roi: list of pairs of images and geomodels
178
+ :type pairs_for_roi: List[(str, dict, str, dict)]
179
+ :param dem_epsg: output EPSG code for ROI
180
+ :type dem_epsg: int
181
+ :param linear_margin: margin for ROI (factor of initial ROI size)
182
+ :type linear_margin: float
183
+ :param constant_margin: margin for ROI in degrees
184
+ :type constant_margin: float
185
+ """
186
+ base_roi = super().get_roi(
187
+ pairs_for_roi, epsg, z_min, z_max, linear_margin, constant_margin
188
+ )
189
+
190
+ coords_list = []
191
+ for image1, geomodel1, _, geomodel2 in pairs_for_roi:
192
+ # Footprint of rectification grid (with margins)
193
+ image1 = SharelocGeometry.load_image(image1["bands"]["b0"]["path"])
194
+ geomodel1 = self.load_geom_model(geomodel1)
195
+ geomodel2 = self.load_geom_model(geomodel2)
196
+
197
+ # With altitude z_min
198
+ epipolar_extent = rectif.get_epipolar_extent(
199
+ image1,
200
+ geomodel1,
201
+ geomodel2,
202
+ elevation=z_min,
203
+ grid_margin=self.rectification_grid_margin,
204
+ )
205
+ lat_min, lon_min, lat_max, lon_max = list(epipolar_extent)
206
+ coords_list.extend([(lon_min, lat_min), (lon_max, lat_max)])
207
+
208
+ # With altitude z_max
209
+ epipolar_extent = rectif.get_epipolar_extent(
210
+ image1,
211
+ geomodel1,
212
+ geomodel2,
213
+ elevation=z_max,
214
+ grid_margin=self.rectification_grid_margin,
215
+ )
216
+ lat_min, lon_min, lat_max, lon_max = list(epipolar_extent)
217
+ coords_list.extend([(lon_min, lat_min), (lon_max, lat_max)])
218
+
219
+ lon_list, lat_list = list(zip(*coords_list)) # noqa: B905
220
+ roi = [
221
+ min(lon_list) - constant_margin,
222
+ min(lat_list) - constant_margin,
223
+ max(lon_list) + constant_margin,
224
+ max(lat_list) + constant_margin,
225
+ ]
226
+ points = np.array(
227
+ [
228
+ (roi[0], roi[1], 0),
229
+ (roi[2], roi[3], 0),
230
+ (roi[0], roi[1], 0),
231
+ (roi[2], roi[3], 0),
232
+ ]
233
+ )
234
+ new_points = projection.point_cloud_conversion(points, 4326, epsg)
235
+ roi = [
236
+ min(new_points[:, 0]),
237
+ min(new_points[:, 1]),
238
+ max(new_points[:, 0]),
239
+ max(new_points[:, 1]),
240
+ ]
241
+
242
+ lon_size = roi[2] - roi[0]
243
+ lat_size = roi[3] - roi[1]
244
+
245
+ roi[0] -= linear_margin * lon_size
246
+ roi[1] -= linear_margin * lat_size
247
+ roi[2] += linear_margin * lon_size
248
+ roi[3] += linear_margin * lat_size
249
+
250
+ roi = [
251
+ min(roi[0], base_roi[0]),
252
+ min(roi[1], base_roi[1]),
253
+ max(roi[2], base_roi[2]),
254
+ max(roi[3], base_roi[3]),
255
+ ]
256
+
257
+ return roi
258
+
259
+ @staticmethod
260
+ def load_geom_model(model: dict) -> Union[Grid, RPC]:
261
+ """
262
+ Load geometric model and returns it as a shareloc object
263
+
264
+ :param model: Path and attributes for geometrical model
265
+ :type model: dict with keys "path" and "model_type"
266
+ :return: geometric model as a shareloc object (Grid or RPC)
267
+ """
268
+ geomodel = model[GEO_MODEL_PATH_TAG]
269
+ # Use RPC Type if none are used
270
+ if model.get(GEO_MODEL_TYPE_TAG):
271
+ geomodel_type = model[GEO_MODEL_TYPE_TAG]
272
+ else:
273
+ geomodel_type = RPC_TYPE
274
+
275
+ # Use RPCoptim class to use optimized C++ direct localizations
276
+ if geomodel_type == "RPC":
277
+ geomodel_type = "RPCoptim"
278
+
279
+ shareloc_model = GeoModel(geomodel, geomodel_type)
280
+
281
+ if shareloc_model is None:
282
+ raise ValueError(f"Model {geomodel} could not be read by shareloc")
283
+
284
+ return shareloc_model
285
+
286
+ @staticmethod
287
+ def load_image(img: str) -> Image:
288
+ """
289
+ Load the image using the Image class of Shareloc
290
+
291
+ :param img: path to the image
292
+ :return: The Image object
293
+ """
294
+ try:
295
+ shareloc_img = Image(img, vertical_direction="north")
296
+ except Exception as error:
297
+ raise ValueError(f"Image type {img} is not supported") from error
298
+
299
+ return shareloc_img
300
+
301
+ @staticmethod
302
+ def check_product_consistency(sensor: str, geomodel: dict) -> bool:
303
+ """
304
+ Test if the product is readable by the shareloc plugin
305
+
306
+ TODO: not used
307
+ - to evolve and use in CARS configuration early in pipeline process
308
+ (new early check input common application ?)
309
+
310
+ :param sensor: path to sensor image
311
+ :param geomodel: path and attributes for geometrical model
312
+ :return: sensor path and overloaded geomodel dict
313
+ """
314
+ # Check geomodel schema consistency
315
+ if isinstance(geomodel, str):
316
+ geomodel = {
317
+ "path": geomodel,
318
+ }
319
+ overloaded_geomodel = geomodel.copy()
320
+
321
+ # If model_type is not defined, default is "RPC"
322
+ overloaded_geomodel["model_type"] = geomodel.get("model_type", "RPC")
323
+
324
+ geomodel_schema = {"path": str, "model_type": str}
325
+ checker_geomodel = Checker(geomodel_schema)
326
+ checker_geomodel.validate(overloaded_geomodel)
327
+
328
+ # Try to read them using shareloc
329
+ for band in sensor["bands"]:
330
+ SharelocGeometry.load_image(sensor["bands"][band]["path"])
331
+ SharelocGeometry.load_geom_model(overloaded_geomodel)
332
+
333
+ return sensor, overloaded_geomodel
334
+
335
+ def triangulate( # pylint: disable=too-many-positional-arguments
336
+ self,
337
+ sensor1,
338
+ sensor2,
339
+ geomodel1,
340
+ geomodel2,
341
+ mode: str,
342
+ matches: Union[xr.Dataset, np.ndarray],
343
+ grid1: Union[dict, RectificationGrid],
344
+ grid2: Union[dict, RectificationGrid],
345
+ roi_key: Union[None, str] = None,
346
+ interpolation_method=None,
347
+ ) -> np.ndarray:
348
+ """
349
+ Performs triangulation from cars disparity or matches dataset
350
+ :param sensor1: path to left sensor image
351
+ :param sensor2: path to right sensor image
352
+ :param geomodel1: path and attributes for left geomodel
353
+ :param geomodel2: path and attributes for right geomodel
354
+ :param mode: triangulation mode
355
+ (constants.DISP_MODE or constants.MATCHES)
356
+ :param matches: cars disparity dataset or matches as numpy array
357
+ :param grid1: dict or RectificationGrid for epipolar grid of sensor1
358
+ :param grid2: dict or RectificationGrid for epipolar grid of sensor2
359
+ :param roi_key: dataset roi to use
360
+ (can be cst.ROI or cst.ROI_WITH_MARGINS)
361
+
362
+ :return: the long/lat/height numpy array in output of the triangulation
363
+ """
364
+ # read geomodels using shareloc
365
+ shareloc_model1 = SharelocGeometry.load_geom_model(geomodel1)
366
+ shareloc_model2 = SharelocGeometry.load_geom_model(geomodel2)
367
+
368
+ if isinstance(grid1, dict):
369
+ # create rectificationgrid
370
+ grid1 = RectificationGrid(
371
+ grid1["path"],
372
+ interpolator=self.interpolator,
373
+ )
374
+ grid2 = RectificationGrid(
375
+ grid2["path"],
376
+ interpolator=self.interpolator,
377
+ )
378
+
379
+ # perform matches triangulation
380
+ if mode is cst.MATCHES_MODE:
381
+ __, point_wgs84, __ = epipolar_triangulation(
382
+ matches=matches,
383
+ mask=None,
384
+ matches_type="sift",
385
+ geometrical_model_left=shareloc_model1,
386
+ geometrical_model_right=shareloc_model2,
387
+ grid_left=grid1,
388
+ grid_right=grid2,
389
+ residues=True,
390
+ fill_nan=True,
391
+ )
392
+
393
+ llh = point_wgs84.reshape((point_wgs84.shape[0], 1, 3))
394
+
395
+ elif mode is cst.DISP_MODE:
396
+ __, point_wgs84, __ = epipolar_triangulation(
397
+ matches=matches,
398
+ mask=None,
399
+ matches_type="disp",
400
+ geometrical_model_left=shareloc_model1,
401
+ geometrical_model_right=shareloc_model2,
402
+ grid_left=grid1,
403
+ grid_right=grid2,
404
+ residues=True,
405
+ fill_nan=True,
406
+ )
407
+
408
+ row = np.array(
409
+ range(matches.attrs[roi_key][1], matches.attrs[roi_key][3])
410
+ )
411
+ col = np.array(
412
+ range(matches.attrs[roi_key][0], matches.attrs[roi_key][2])
413
+ )
414
+
415
+ llh = point_wgs84.reshape((row.size, col.size, 3))
416
+ else:
417
+ logging.error(
418
+ "{} mode is not available in the "
419
+ "shareloc plugin triangulation".format(mode)
420
+ )
421
+ raise ValueError(
422
+ f"{mode} mode is not available"
423
+ " in the shareloc plugin triangulation"
424
+ )
425
+
426
+ return llh
427
+
428
+ # pylint: disable=too-many-positional-arguments
429
+ def generate_epipolar_grids(
430
+ self,
431
+ sensor1,
432
+ sensor2,
433
+ geomodel1,
434
+ geomodel2,
435
+ epipolar_step: int = 30,
436
+ ) -> Tuple[
437
+ np.ndarray, np.ndarray, List[float], List[float], List[int], float
438
+ ]:
439
+ """
440
+ Computes the left and right epipolar grids
441
+
442
+ :param sensor1: path to left sensor image
443
+ :param sensor2: path to right sensor image
444
+ :param geomodel1: path and attributes for left geomodel
445
+ :param geomodel2: path and attributes for right geomodel
446
+ :param epipolar_step: step to use to construct the epipolar grids
447
+ :return: Tuple composed of :
448
+ - the left epipolar grid as a numpy array
449
+ - the right epipolar grid as a numpy array
450
+ - the left grid origin as a list of float
451
+ - the left grid spacing as a list of float
452
+ - the epipolar image size as a list of int
453
+ (x-axis size is given with the index 0, y-axis size with index 1)
454
+ - the disparity to altitude ratio as a float
455
+
456
+ """
457
+ # read inputs using shareloc
458
+ shareloc_model1 = SharelocGeometry.load_geom_model(geomodel1)
459
+ shareloc_model2 = SharelocGeometry.load_geom_model(geomodel2)
460
+
461
+ image1 = SharelocGeometry.load_image(sensor1)
462
+ image2 = SharelocGeometry.load_image(sensor2)
463
+
464
+ # compute epipolar grids
465
+ (
466
+ grid1,
467
+ grid2,
468
+ [epipolar_size_y, epipolar_size_x],
469
+ alt_to_disp_ratio,
470
+ ) = rectif.compute_stereorectification_epipolar_grids(
471
+ image1,
472
+ shareloc_model1,
473
+ image2,
474
+ shareloc_model2,
475
+ self.elevation,
476
+ epi_step=epipolar_step,
477
+ margin=self.rectification_grid_margin,
478
+ )
479
+
480
+ # rearrange output to match the expected structure of CARS
481
+ # grid[:, :, 2] with altitudes is not used
482
+ grid1 = grid1[:, :, 0:2][:, :, ::-1]
483
+ grid2 = grid2[:, :, 0:2][:, :, ::-1]
484
+
485
+ epipolar_size_x = int(np.floor(epipolar_size_x))
486
+ epipolar_size_y = int(np.floor(epipolar_size_y))
487
+
488
+ origin = [
489
+ float(-self.rectification_grid_margin * epipolar_step),
490
+ float(-self.rectification_grid_margin * epipolar_step),
491
+ ]
492
+ spacing = [float(epipolar_step), float(epipolar_step)]
493
+
494
+ # alt_to_disp_ratio does not consider image resolution
495
+ with rio.open(sensor1, "r") as rio_dst:
496
+ pixel_size_x, pixel_size_y = (
497
+ rio_dst.transform[0],
498
+ rio_dst.transform[4],
499
+ )
500
+ mean_size = (abs(pixel_size_x) + abs(pixel_size_y)) / 2
501
+ disp_to_alt_ratio = mean_size / alt_to_disp_ratio
502
+
503
+ return (
504
+ grid1,
505
+ grid2,
506
+ origin,
507
+ spacing,
508
+ [epipolar_size_x, epipolar_size_y],
509
+ disp_to_alt_ratio,
510
+ )
511
+
512
+ def direct_loc( # pylint: disable=too-many-positional-arguments
513
+ self,
514
+ sensor,
515
+ geomodel,
516
+ x_coord: np.array,
517
+ y_coord: np.array,
518
+ z_coord: np.array = None,
519
+ ) -> np.ndarray:
520
+ """
521
+ For a given image point, compute the latitude, longitude, altitude
522
+
523
+ Advice: to be sure, use x,y,z inputs only
524
+
525
+ :param sensor: path to sensor image
526
+ :param geomodel: path and attributes for geomodel
527
+ :param x_coord: X Coordinate in input image sensor
528
+ :param y_coord: Y Coordinate in input image sensor
529
+ :param z_coord: Z Altitude coordinate to take the image
530
+ :return: Latitude, Longitude, Altitude coordinates as a numpy array
531
+ """
532
+ # load model and image with shareloc
533
+ shareloc_model = SharelocGeometry.load_geom_model(geomodel)
534
+ shareloc_image = SharelocGeometry.load_image(sensor)
535
+
536
+ # perform direct localization operation
537
+ loc = localization.Localization(
538
+ shareloc_model,
539
+ image=shareloc_image,
540
+ elevation=self.elevation,
541
+ epsg=4326,
542
+ )
543
+ # Bug: y_coord and x_coord inversion to fit Shareloc standards row/col.
544
+ # TODO: clean geometry convention calls in API
545
+ lonlatalt = loc.direct(
546
+ y_coord, x_coord, z_coord, using_geotransform=True
547
+ )
548
+ lonlatalt = np.squeeze(lonlatalt)
549
+ if len(lonlatalt.shape) == 1:
550
+ latlonalt = np.array([lonlatalt[1], lonlatalt[0], lonlatalt[2]])
551
+ else:
552
+ latlonalt = np.array(
553
+ [lonlatalt[:, 1], lonlatalt[:, 0], lonlatalt[:, 2]]
554
+ )
555
+ return latlonalt
556
+
557
+ def inverse_loc( # pylint: disable=too-many-positional-arguments
558
+ self,
559
+ sensor,
560
+ geomodel,
561
+ lat_coord: np.array,
562
+ lon_coord: np.array,
563
+ z_coord: np.array = None,
564
+ ) -> np.ndarray:
565
+ """
566
+ For a given image points list, compute the latitudes,
567
+ longitudes, altitudes
568
+
569
+ Advice: to be sure, use x,y,z list inputs only
570
+
571
+ :param sensor: path to sensor image
572
+ :param geomodel: path and attributes for geomodel
573
+ :param lat_coord: latitute Coordinate list
574
+ :param lon_coord: longitude Coordinates list
575
+ :param z_coord: Z Altitude list
576
+ :return: X / Y / Z Coordinates list in input image as a numpy array
577
+ """
578
+
579
+ # load model and image with shareloc
580
+ shareloc_model = SharelocGeometry.load_geom_model(geomodel)
581
+ shareloc_image = SharelocGeometry.load_image(sensor)
582
+
583
+ # perform inverse localization operation
584
+ loc = localization.Localization(
585
+ shareloc_model,
586
+ image=shareloc_image,
587
+ elevation=self.elevation,
588
+ epsg=4326,
589
+ )
590
+ # Rows and columns order is inversed
591
+ col, row, alti = loc.inverse(
592
+ lon_coord.astype(np.float64),
593
+ lat_coord.astype(np.float64),
594
+ h=z_coord.astype(np.float64),
595
+ using_geotransform=True,
596
+ )
597
+
598
+ return row, col, alti