cars 1.0.0a2__cp313-cp313-win_amd64.whl → 1.0.0a4__cp313-cp313-win_amd64.whl

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

Potentially problematic release.


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

Files changed (144) hide show
  1. cars/__init__.py +3 -3
  2. cars/applications/__init__.py +0 -3
  3. cars/applications/application.py +14 -6
  4. cars/applications/application_template.py +42 -0
  5. cars/applications/auxiliary_filling/abstract_auxiliary_filling_app.py +12 -2
  6. cars/applications/auxiliary_filling/auxiliary_filling_algo.py +2 -2
  7. cars/applications/auxiliary_filling/auxiliary_filling_from_sensors_app.py +95 -46
  8. cars/applications/auxiliary_filling/auxiliary_filling_wrappers.py +7 -6
  9. cars/applications/dem_generation/abstract_dem_generation_app.py +9 -5
  10. cars/applications/dem_generation/dem_generation_algo.py +1 -1
  11. cars/applications/dem_generation/dem_generation_wrappers.py +44 -59
  12. cars/applications/dem_generation/dichotomic_generation_app.py +9 -6
  13. cars/applications/dem_generation/rasterization_app.py +112 -43
  14. cars/applications/dense_match_filling/__init__.py +1 -1
  15. cars/applications/dense_match_filling/abstract_dense_match_filling_app.py +2 -15
  16. cars/applications/dense_match_filling/fill_disp_algo.py +32 -373
  17. cars/applications/dense_match_filling/fill_disp_wrappers.py +0 -343
  18. cars/applications/dense_match_filling/zero_padding_app.py +10 -5
  19. cars/applications/dense_matching/abstract_dense_matching_app.py +2 -1
  20. cars/applications/dense_matching/census_mccnn_sgm_app.py +48 -60
  21. cars/applications/dense_matching/cpp/dense_matching_cpp.cp313-win_amd64.dll.a +0 -0
  22. cars/applications/dense_matching/cpp/dense_matching_cpp.cp313-win_amd64.pyd +0 -0
  23. cars/applications/dense_matching/dense_matching_algo.py +48 -14
  24. cars/applications/dense_matching/dense_matching_wrappers.py +11 -3
  25. cars/applications/dense_matching/disparity_grid_algo.py +95 -79
  26. cars/applications/dense_matching/loaders/config_mapping.json +13 -0
  27. cars/applications/dense_matching/loaders/global_land_cover_map.tif +0 -0
  28. cars/applications/dense_matching/loaders/pandora_loader.py +169 -34
  29. cars/applications/dsm_filling/border_interpolation_app.py +11 -12
  30. cars/applications/dsm_filling/bulldozer_filling_app.py +16 -15
  31. cars/applications/dsm_filling/exogenous_filling_app.py +14 -14
  32. cars/applications/grid_generation/abstract_grid_generation_app.py +1 -1
  33. cars/applications/grid_generation/epipolar_grid_generation_app.py +4 -2
  34. cars/applications/grid_generation/grid_correction_app.py +4 -1
  35. cars/applications/grid_generation/grid_generation_algo.py +7 -2
  36. cars/applications/ground_truth_reprojection/abstract_ground_truth_reprojection_app.py +1 -1
  37. cars/applications/ground_truth_reprojection/direct_localization_app.py +2 -2
  38. cars/applications/ground_truth_reprojection/ground_truth_reprojection_algo.py +2 -1
  39. cars/applications/point_cloud_fusion/abstract_pc_fusion_app.py +0 -155
  40. cars/applications/point_cloud_fusion/mapping_to_terrain_tiles_app.py +0 -658
  41. cars/applications/point_cloud_fusion/pc_fusion_algo.py +0 -1339
  42. cars/applications/point_cloud_fusion/pc_fusion_wrappers.py +0 -869
  43. cars/applications/point_cloud_outlier_removal/abstract_outlier_removal_app.py +11 -6
  44. cars/applications/point_cloud_outlier_removal/outlier_removal_algo.py +9 -8
  45. cars/applications/point_cloud_outlier_removal/small_components_app.py +101 -270
  46. cars/applications/point_cloud_outlier_removal/statistical_app.py +120 -277
  47. cars/applications/rasterization/abstract_pc_rasterization_app.py +2 -1
  48. cars/applications/rasterization/rasterization_algo.py +18 -6
  49. cars/applications/rasterization/rasterization_wrappers.py +2 -1
  50. cars/applications/rasterization/simple_gaussian_app.py +88 -116
  51. cars/applications/resampling/abstract_resampling_app.py +1 -1
  52. cars/applications/resampling/bicubic_resampling_app.py +3 -1
  53. cars/applications/resampling/resampling_algo.py +60 -53
  54. cars/applications/resampling/resampling_wrappers.py +3 -1
  55. cars/applications/sparse_matching/abstract_sparse_matching_app.py +1 -1
  56. cars/applications/sparse_matching/sift_app.py +5 -25
  57. cars/applications/sparse_matching/sparse_matching_algo.py +3 -2
  58. cars/applications/sparse_matching/sparse_matching_wrappers.py +1 -1
  59. cars/applications/triangulation/abstract_triangulation_app.py +1 -1
  60. cars/applications/triangulation/line_of_sight_intersection_app.py +13 -11
  61. cars/applications/triangulation/pc_transform.py +552 -0
  62. cars/applications/triangulation/triangulation_algo.py +6 -4
  63. cars/applications/triangulation/triangulation_wrappers.py +1 -0
  64. cars/bundleadjustment.py +6 -6
  65. cars/cars.py +11 -9
  66. cars/core/cars_logging.py +80 -49
  67. cars/core/constants.py +0 -1
  68. cars/core/datasets.py +5 -2
  69. cars/core/geometry/abstract_geometry.py +364 -22
  70. cars/core/geometry/shareloc_geometry.py +112 -82
  71. cars/core/inputs.py +72 -19
  72. cars/core/outputs.py +1 -1
  73. cars/core/preprocessing.py +17 -3
  74. cars/core/projection.py +126 -6
  75. cars/core/tiling.py +10 -3
  76. cars/data_structures/cars_dataset.py +12 -10
  77. cars/data_structures/corresponding_tiles_tools.py +0 -103
  78. cars/data_structures/format_transformation.py +4 -1
  79. cars/devibrate.py +6 -3
  80. cars/extractroi.py +20 -21
  81. cars/orchestrator/cluster/abstract_cluster.py +15 -5
  82. cars/orchestrator/cluster/abstract_dask_cluster.py +6 -2
  83. cars/orchestrator/cluster/dask_jobqueue_utils.py +1 -1
  84. cars/orchestrator/cluster/log_wrapper.py +149 -22
  85. cars/orchestrator/cluster/mp_cluster/multiprocessing_cluster.py +12 -4
  86. cars/orchestrator/cluster/mp_cluster/multiprocessing_profiler.py +2 -2
  87. cars/orchestrator/cluster/pbs_dask_cluster.py +1 -1
  88. cars/orchestrator/cluster/sequential_cluster.py +5 -4
  89. cars/orchestrator/cluster/slurm_dask_cluster.py +1 -1
  90. cars/orchestrator/orchestrator.py +15 -4
  91. cars/orchestrator/registry/id_generator.py +1 -0
  92. cars/orchestrator/registry/saver_registry.py +2 -2
  93. cars/pipelines/conf_resolution/conf_final_resolution.json +5 -3
  94. cars/pipelines/default/default_pipeline.py +461 -1052
  95. cars/pipelines/parameters/advanced_parameters.py +91 -64
  96. cars/pipelines/parameters/advanced_parameters_constants.py +6 -5
  97. cars/pipelines/parameters/application_parameters.py +71 -0
  98. cars/pipelines/parameters/depth_map_inputs.py +0 -314
  99. cars/pipelines/parameters/dsm_inputs.py +40 -4
  100. cars/pipelines/parameters/output_parameters.py +44 -8
  101. cars/pipelines/parameters/sensor_inputs.py +122 -73
  102. cars/pipelines/parameters/sensor_inputs_constants.py +0 -2
  103. cars/pipelines/parameters/sensor_loaders/__init__.py +4 -3
  104. cars/pipelines/parameters/sensor_loaders/basic_classif_loader.py +106 -0
  105. cars/pipelines/parameters/sensor_loaders/{basic_sensor_loader.py → basic_image_loader.py} +16 -22
  106. cars/pipelines/parameters/sensor_loaders/pivot_classif_loader.py +121 -0
  107. cars/pipelines/parameters/sensor_loaders/{pivot_sensor_loader.py → pivot_image_loader.py} +10 -21
  108. cars/pipelines/parameters/sensor_loaders/sensor_loader.py +4 -6
  109. cars/pipelines/parameters/sensor_loaders/sensor_loader_template.py +1 -3
  110. cars/pipelines/pipeline_template.py +1 -3
  111. cars/pipelines/unit/unit_pipeline.py +676 -1070
  112. cars/starter.py +4 -3
  113. cars-1.0.0a4.dist-info/DELVEWHEEL +2 -0
  114. {cars-1.0.0a2.dist-info → cars-1.0.0a4.dist-info}/METADATA +135 -53
  115. {cars-1.0.0a2.dist-info → cars-1.0.0a4.dist-info}/RECORD +120 -134
  116. cars.libs/libgcc_s_seh-1-b2494fcbd4d80cf2c98fdd5261f6d850.dll +0 -0
  117. cars.libs/libstdc++-6-e9b0d12ae0e9555bbae55e8dfd08c3f7.dll +0 -0
  118. cars.libs/libwinpthread-1-7882d1b093714ccdfaf4e0789a817792.dll +0 -0
  119. cars/applications/dense_match_filling/cpp/__init__.py +0 -0
  120. cars/applications/dense_match_filling/cpp/dense_match_filling_cpp.cp313-win_amd64.dll.a +0 -0
  121. cars/applications/dense_match_filling/cpp/dense_match_filling_cpp.cp313-win_amd64.pyd +0 -0
  122. cars/applications/dense_match_filling/cpp/dense_match_filling_cpp.py +0 -72
  123. cars/applications/dense_match_filling/cpp/includes/dense_match_filling.hpp +0 -46
  124. cars/applications/dense_match_filling/cpp/meson.build +0 -9
  125. cars/applications/dense_match_filling/cpp/src/bindings.cpp +0 -11
  126. cars/applications/dense_match_filling/cpp/src/dense_match_filling.cpp +0 -142
  127. cars/applications/dense_match_filling/plane_app.py +0 -556
  128. cars/applications/hole_detection/__init__.py +0 -30
  129. cars/applications/hole_detection/abstract_hole_detection_app.py +0 -125
  130. cars/applications/hole_detection/cloud_to_bbox_app.py +0 -346
  131. cars/applications/hole_detection/hole_detection_algo.py +0 -144
  132. cars/applications/hole_detection/hole_detection_wrappers.py +0 -53
  133. cars/applications/point_cloud_denoising/__init__.py +0 -29
  134. cars/applications/point_cloud_denoising/abstract_pc_denoising_app.py +0 -273
  135. cars/applications/point_cloud_fusion/__init__.py +0 -30
  136. cars/applications/point_cloud_fusion/cloud_fusion_constants.py +0 -39
  137. cars/applications/sparse_matching/pandora_sparse_matching_app.py +0 -0
  138. cars/pipelines/parameters/depth_map_inputs_constants.py +0 -25
  139. cars-1.0.0a2.dist-info/DELVEWHEEL +0 -2
  140. cars.libs/libgcc_s_seh-1-f2b6825d483bdf14050493af93b5997d.dll +0 -0
  141. cars.libs/libstdc++-6-6b0059df6bc601df5a0f18a5805eea05.dll +0 -0
  142. cars.libs/libwinpthread-1-e01b8e85fd67c2b861f64d4ccc7df607.dll +0 -0
  143. {cars-1.0.0a2.dist-info → cars-1.0.0a4.dist-info}/WHEEL +0 -0
  144. {cars-1.0.0a2.dist-info → cars-1.0.0a4.dist-info}/entry_points.txt +0 -0
@@ -18,6 +18,9 @@
18
18
  # See the License for the specific language governing permissions and
19
19
  # limitations under the License.
20
20
  #
21
+
22
+ # pylint: disable=C0302
23
+
21
24
  """
22
25
  this module contains the abstract geometry class to use in the
23
26
  geometry plugins
@@ -30,7 +33,10 @@ from typing import Dict, List, Tuple, Union
30
33
  import numpy as np
31
34
  import rasterio as rio
32
35
  import xarray as xr
33
- from json_checker import And, Checker, Or
36
+ from affine import Affine
37
+ from json_checker import And, Checker
38
+ from rasterio.enums import Resampling
39
+ from rasterio.warp import reproject
34
40
  from scipy import interpolate
35
41
  from scipy.interpolate import LinearNDInterpolator
36
42
  from shapely.geometry import Polygon
@@ -38,19 +44,26 @@ from shareloc import proj_utils
38
44
  from shareloc.geofunctions.rectification_grid import RectificationGrid
39
45
 
40
46
  from cars.core import constants as cst
41
- from cars.core import inputs, outputs
47
+ from cars.core import inputs, outputs, projection
42
48
  from cars.core.utils import safe_makedirs
43
49
  from cars.data_structures import cars_dataset
50
+ from cars.orchestrator.cluster.log_wrapper import cars_profile
44
51
 
45
52
 
46
- class AbstractGeometry(metaclass=ABCMeta):
53
+ class AbstractGeometry(metaclass=ABCMeta): # pylint: disable=R0902
47
54
  """
48
55
  AbstractGeometry
49
56
  """
50
57
 
51
58
  available_plugins: Dict = {}
52
59
 
53
- def __new__(cls, geometry_plugin_conf=None, **kwargs):
60
+ def __new__(
61
+ cls,
62
+ geometry_plugin_conf=None,
63
+ pairs_for_roi=None,
64
+ scaling_coeff=1,
65
+ **kwargs,
66
+ ):
54
67
  """
55
68
  Return the required plugin
56
69
  :raises:
@@ -59,6 +72,8 @@ class AbstractGeometry(metaclass=ABCMeta):
59
72
  :param geometry_plugin_conf: plugin name or plugin configuration
60
73
  to instantiate
61
74
  :type geometry_plugin_conf: str or dict
75
+ :param scaling_coeff: scaling factor for resolution
76
+ :type scaling_coeff: float
62
77
  :return: a geometry_plugin object
63
78
  """
64
79
  if geometry_plugin_conf is not None:
@@ -94,28 +109,218 @@ class AbstractGeometry(metaclass=ABCMeta):
94
109
  )
95
110
  return super().__new__(cls)
96
111
 
97
- def __init__(
112
+ def __init__( # pylint: disable=too-many-positional-arguments
98
113
  self,
99
114
  geometry_plugin_conf,
100
115
  dem=None,
101
116
  geoid=None,
102
117
  default_alt=None,
118
+ pairs_for_roi=None,
119
+ scaling_coeff=1,
120
+ output_dem_dir=None,
103
121
  **kwargs,
104
122
  ):
123
+ self.scaling_coeff = scaling_coeff
105
124
 
106
- config = self.check_conf(geometry_plugin_conf)
125
+ self.used_config = self.check_conf(geometry_plugin_conf)
107
126
 
108
- self.plugin_name = config["plugin_name"]
109
- self.interpolator = config["interpolator"]
110
- self.dem_roi_margin = config["dem_roi_margin"]
111
-
112
- self.dem = dem
127
+ self.plugin_name = self.used_config["plugin_name"]
128
+ self.interpolator = self.used_config["interpolator"]
129
+ self.dem_roi_margin = self.used_config["dem_roi_margin"]
130
+ self.dem = None
113
131
  self.dem_roi = None
114
132
  self.dem_roi_epsg = None
115
133
  self.geoid = geoid
116
134
  self.default_alt = default_alt
135
+ self.elevation = default_alt
136
+ # a margin is needed for cubic interpolation
137
+ self.rectification_grid_margin = 0
138
+ if self.interpolator == "cubic":
139
+ self.rectification_grid_margin = 5
117
140
  self.kwargs = kwargs
118
141
 
142
+ # compute roi only when generating geometry object with dem
143
+ if dem is not None:
144
+ self.dem = dem
145
+ self.default_alt = self.get_dem_median_value()
146
+ self.elevation = self.default_alt
147
+ logging.info(
148
+ "Median value of DEM ({}) will be used as default_alt".format(
149
+ self.default_alt
150
+ )
151
+ )
152
+ if pairs_for_roi is not None:
153
+ self.dem_roi_epsg = inputs.rasterio_get_epsg(dem)
154
+ self.dem_roi = self.get_roi(
155
+ pairs_for_roi,
156
+ self.dem_roi_epsg,
157
+ z_min=-1000,
158
+ z_max=9000,
159
+ linear_margin=self.dem_roi_margin[0],
160
+ constant_margin=self.dem_roi_margin[1],
161
+ )
162
+ if output_dem_dir is not None:
163
+ self.dem = self.extend_dem_to_roi(dem, output_dem_dir)
164
+
165
+ def get_dem_median_value(self):
166
+ """
167
+ Compute dem median value
168
+ :param dem: path of DEM
169
+ """
170
+ with rio.open(self.dem) as dem_file:
171
+ dem_data = dem_file.read(1)
172
+ median_value = np.nanmedian(dem_data)
173
+ median_value = float(median_value)
174
+ return median_value
175
+
176
+ def get_roi( # pylint: disable=too-many-positional-arguments
177
+ self,
178
+ pairs_for_roi,
179
+ epsg,
180
+ z_min=0,
181
+ z_max=0,
182
+ linear_margin=0,
183
+ constant_margin=0.012,
184
+ ):
185
+ """
186
+ Compute region of interest for intersection of DEM
187
+
188
+ :param pairs_for_roi: list of pairs of images and geomodels
189
+ :type pairs_for_roi: List[(str, dict, str, dict)]
190
+ :param dem_epsg: output EPSG code for ROI
191
+ :type dem_epsg: int
192
+ :param linear_margin: margin for ROI (factor of initial ROI size)
193
+ :type linear_margin: float
194
+ :param constant_margin: margin for ROI in degrees
195
+ :type constant_margin: float
196
+ """
197
+ coords_list = []
198
+ z_min = np.array(z_min)
199
+ z_max = np.array(z_max)
200
+ for image1, geomodel1, image2, geomodel2 in pairs_for_roi:
201
+ # Footprint of left image with altitude z_min
202
+ coords_list.extend(
203
+ self.image_envelope(
204
+ image1["main_file"], geomodel1, elevation=z_min
205
+ )
206
+ )
207
+ # Footprint of left image with altitude z_max
208
+ coords_list.extend(
209
+ self.image_envelope(
210
+ image1["main_file"], geomodel1, elevation=z_max
211
+ )
212
+ )
213
+ # Footprint of right image with altitude z_min
214
+ coords_list.extend(
215
+ self.image_envelope(
216
+ image2["main_file"], geomodel2, elevation=z_min
217
+ )
218
+ )
219
+ # Footprint of right image with altitude z_max
220
+ coords_list.extend(
221
+ self.image_envelope(
222
+ image2["main_file"], geomodel2, elevation=z_max
223
+ )
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
+ return roi
257
+
258
+ def extend_dem_to_roi(self, dem, output_dem_dir):
259
+ """
260
+ Extend the size of the dem to the required ROI and fill
261
+ :param dem: path to the input DEM
262
+ :param output_dem_dir: path to write the output extended DEM
263
+ """
264
+ with rio.open(dem) as in_dem:
265
+ src_dem = in_dem.read(1)
266
+ metadata = in_dem.meta
267
+ src_transform = in_dem.transform
268
+ crs = in_dem.crs
269
+ bounds = in_dem.bounds
270
+
271
+ logging.info(
272
+ "DEM bounds : {}, {}, {}, {}".format(
273
+ bounds.left, bounds.top, bounds.right, bounds.bottom
274
+ )
275
+ )
276
+ logging.info(
277
+ "ROI bounds : {}, {}, {}, {}".format(
278
+ self.dem_roi[0],
279
+ self.dem_roi[1],
280
+ self.dem_roi[2],
281
+ self.dem_roi[3],
282
+ )
283
+ )
284
+
285
+ # Longitude
286
+ lon_res = src_transform[0]
287
+ lon_shift = (self.dem_roi[0] - bounds.left) / lon_res
288
+ dst_width = int((self.dem_roi[2] - self.dem_roi[0]) / abs(lon_res)) + 1
289
+ # Latitude
290
+ lat_res = src_transform[4]
291
+ lat_shift = (self.dem_roi[3] - bounds.top) / lat_res
292
+ dst_height = int((self.dem_roi[3] - self.dem_roi[1]) / abs(lat_res)) + 1
293
+
294
+ shift = Affine.translation(lon_shift, lat_shift)
295
+ dst_transform = src_transform * shift
296
+ dst_dem = np.zeros((dst_height, dst_width))
297
+
298
+ reproject(
299
+ source=src_dem,
300
+ destination=dst_dem,
301
+ src_transform=src_transform,
302
+ src_crs=crs,
303
+ dst_transform=dst_transform,
304
+ dst_crs=crs,
305
+ resampling=Resampling.bilinear,
306
+ )
307
+ # Fill nodata
308
+ dst_dem = rio.fill.fillnodata(
309
+ dst_dem,
310
+ mask=~(dst_dem == 0),
311
+ )
312
+ metadata["transform"] = dst_transform
313
+ metadata["height"] = dst_height
314
+ metadata["width"] = dst_width
315
+ metadata["driver"] = "GTiff"
316
+
317
+ out_dem_path = os.path.join(output_dem_dir, "initial_elevation.tif")
318
+
319
+ with rio.open(out_dem_path, "w", **metadata) as dst:
320
+ dst.write(dst_dem, 1)
321
+
322
+ return out_dem_path
323
+
119
324
  @classmethod
120
325
  def register_subclass(cls, short_name: str):
121
326
  """
@@ -160,12 +365,14 @@ class AbstractGeometry(metaclass=ABCMeta):
160
365
  "plugin_name", "SharelocGeometry"
161
366
  )
162
367
  overloaded_conf["interpolator"] = conf.get("interpolator", "cubic")
163
- overloaded_conf["dem_roi_margin"] = conf.get("dem_roi_margin", 0.012)
368
+ overloaded_conf["dem_roi_margin"] = conf.get(
369
+ "dem_roi_margin", [0.75, 0.02]
370
+ )
164
371
 
165
372
  geometry_schema = {
166
373
  "plugin_name": str,
167
374
  "interpolator": And(str, lambda x: x in ["cubic", "linear"]),
168
- "dem_roi_margin": Or(float, int),
375
+ "dem_roi_margin": [float],
169
376
  }
170
377
 
171
378
  # Check conf
@@ -175,7 +382,7 @@ class AbstractGeometry(metaclass=ABCMeta):
175
382
  return overloaded_conf
176
383
 
177
384
  @abstractmethod
178
- def triangulate(
385
+ def triangulate( # pylint: disable=too-many-positional-arguments
179
386
  self,
180
387
  sensor1,
181
388
  sensor2,
@@ -186,6 +393,7 @@ class AbstractGeometry(metaclass=ABCMeta):
186
393
  grid1: str,
187
394
  grid2: str,
188
395
  roi_key: Union[None, str] = None,
396
+ interpolation_method=None,
189
397
  ) -> np.ndarray:
190
398
  """
191
399
  Performs triangulation from cars disparity or matches dataset
@@ -215,6 +423,7 @@ class AbstractGeometry(metaclass=ABCMeta):
215
423
  :return: True if the products are readable, False otherwise
216
424
  """
217
425
 
426
+ # pylint: disable=too-many-positional-arguments
218
427
  @abstractmethod
219
428
  def generate_epipolar_grids(
220
429
  self, sensor1, sensor2, geomodel1, geomodel2, epipolar_step: int = 30
@@ -249,6 +458,7 @@ class AbstractGeometry(metaclass=ABCMeta):
249
458
  """
250
459
  return geomodel
251
460
 
461
+ # pylint: disable=too-many-positional-arguments
252
462
  def matches_to_sensor_coords(
253
463
  self,
254
464
  grid1: Union[str, cars_dataset.CarsDataset, RectificationGrid],
@@ -257,6 +467,7 @@ class AbstractGeometry(metaclass=ABCMeta):
257
467
  matches_type: str,
258
468
  matches_msk: np.ndarray = None,
259
469
  ul_matches_shift: Tuple[int, int] = None,
470
+ interpolation_method=None,
260
471
  ) -> Tuple[np.ndarray, np.ndarray]:
261
472
  """
262
473
  Convert matches (sparse or dense matches) given in epipolar
@@ -330,10 +541,10 @@ class AbstractGeometry(metaclass=ABCMeta):
330
541
 
331
542
  # convert epipolar matches to sensor coordinates
332
543
  sensor_pos_left = self.sensor_position_from_grid(
333
- grid1, vec_epi_pos_left
544
+ grid1, vec_epi_pos_left, interpolation_method=interpolation_method
334
545
  )
335
546
  sensor_pos_right = self.sensor_position_from_grid(
336
- grid2, vec_epi_pos_right
547
+ grid2, vec_epi_pos_right, interpolation_method=interpolation_method
337
548
  )
338
549
 
339
550
  if matches_type == cst.DISP_MODE:
@@ -369,6 +580,7 @@ class AbstractGeometry(metaclass=ABCMeta):
369
580
  self,
370
581
  grid: Union[dict, RectificationGrid],
371
582
  positions: np.ndarray,
583
+ interpolation_method=None,
372
584
  ) -> np.ndarray:
373
585
  """
374
586
  Interpolate the positions given as inputs using the grid
@@ -441,6 +653,11 @@ class AbstractGeometry(metaclass=ABCMeta):
441
653
  min_row_idx : max_row_idx + 1, min_col_idx : max_col_idx + 1
442
654
  ]
443
655
 
656
+ if interpolation_method is not None:
657
+ method = interpolation_method
658
+ else:
659
+ method = self.interpolator
660
+
444
661
  # interpolate sensor positions
445
662
  interpolator = interpolate.RegularGridInterpolator(
446
663
  (cols_cropped, rows_cropped),
@@ -451,13 +668,35 @@ class AbstractGeometry(metaclass=ABCMeta):
451
668
  ),
452
669
  axis=2,
453
670
  ),
454
- method=self.interpolator,
671
+ method=method,
455
672
  bounds_error=False,
456
673
  fill_value=None,
457
674
  )
458
675
 
459
676
  sensor_positions = interpolator(positions)
460
677
 
678
+ min_row = np.min(sensor_row_positions_cropped)
679
+ max_row = np.max(sensor_row_positions_cropped)
680
+ min_col = np.min(sensor_col_positions_cropped)
681
+ max_col = np.max(sensor_col_positions_cropped)
682
+
683
+ valid_rows = np.logical_and(
684
+ sensor_positions[:, 0] > min_row,
685
+ sensor_positions[:, 0] < max_row,
686
+ )
687
+ valid_cols = np.logical_and(
688
+ sensor_positions[:, 1] > min_col,
689
+ sensor_positions[:, 1] < max_col,
690
+ )
691
+ valid = np.logical_and(valid_rows, valid_cols)
692
+
693
+ if np.sum(~valid) > 0:
694
+ logging.warning(
695
+ "{}/{} points are outside of epipolar grid".format(
696
+ np.sum(~valid), valid.size
697
+ )
698
+ )
699
+
461
700
  # swap
462
701
  sensor_positions[:, [0, 1]] = sensor_positions[:, [1, 0]]
463
702
 
@@ -517,6 +756,7 @@ class AbstractGeometry(metaclass=ABCMeta):
517
756
 
518
757
  return epipolar_positions
519
758
 
759
+ @cars_profile(name="Transform matches", interval=0.5)
520
760
  def transform_matches_from_grids(
521
761
  self,
522
762
  sensor_matches_left,
@@ -553,7 +793,8 @@ class AbstractGeometry(metaclass=ABCMeta):
553
793
 
554
794
  return new_matches_array
555
795
 
556
- def get_sensor_matches(
796
+ @cars_profile(name="Get sensor matches")
797
+ def get_sensor_matches( # pylint: disable=too-many-positional-arguments
557
798
  self,
558
799
  matches_array,
559
800
  grid_left,
@@ -562,7 +803,7 @@ class AbstractGeometry(metaclass=ABCMeta):
562
803
  save_matches,
563
804
  ):
564
805
  """
565
- get sensor matches
806
+ Get sensor matches
566
807
 
567
808
  :param grid_left: path to epipolar grid of image 1
568
809
  :param grid_left: path to epipolar grid of image 2
@@ -595,7 +836,29 @@ class AbstractGeometry(metaclass=ABCMeta):
595
836
  return sensor_matches_left, sensor_matches_right
596
837
 
597
838
  @abstractmethod
598
- def direct_loc(
839
+ def direct_loc( # pylint: disable=too-many-positional-arguments
840
+ self,
841
+ sensor,
842
+ geomodel,
843
+ x_coord: np.array,
844
+ y_coord: np.array,
845
+ z_coord: np.array = None,
846
+ ) -> np.ndarray:
847
+ """
848
+ For a given image points list, compute the latitudes,
849
+ longitudes, altitudes
850
+
851
+ Advice: to be sure, use x,y,z list inputs only
852
+
853
+ :param sensor: path to sensor image
854
+ :param geomodel: path and attributes for geomodel
855
+ :param x_coord: X Coordinates list in input image sensor
856
+ :param y_coord: Y Coordinate list in input image sensor
857
+ :param z_coord: Z Altitude list coordinate to take the image
858
+ :return: Latitude, Longitude, Altitude coordinates list as a numpy array
859
+ """
860
+
861
+ def safe_direct_loc( # pylint: disable=too-many-positional-arguments
599
862
  self,
600
863
  sensor,
601
864
  geomodel,
@@ -616,9 +879,58 @@ class AbstractGeometry(metaclass=ABCMeta):
616
879
  :param z_coord: Z Altitude list coordinate to take the image
617
880
  :return: Latitude, Longitude, Altitude coordinates list as a numpy array
618
881
  """
882
+ if len(x_coord) > 0:
883
+ ground_points = self.direct_loc(
884
+ sensor,
885
+ geomodel,
886
+ x_coord,
887
+ y_coord,
888
+ z_coord,
889
+ )
890
+ else:
891
+ logging.warning("Direct loc function launched on empty list")
892
+ return []
893
+ if z_coord is None:
894
+ status = np.any(np.isnan(ground_points), axis=0)
895
+ if sum(status) > 0:
896
+ logging.warning(
897
+ "{} errors have been detected on direct "
898
+ "loc and will be re-launched".format(sum(status))
899
+ )
900
+ ground_points_retry = self.direct_loc(
901
+ sensor,
902
+ geomodel,
903
+ x_coord[status],
904
+ y_coord[status],
905
+ np.array([0]),
906
+ )
907
+ ground_points[:, status] = ground_points_retry
908
+ return ground_points
619
909
 
620
910
  @abstractmethod
621
- def inverse_loc(
911
+ def inverse_loc( # pylint: disable=too-many-positional-arguments
912
+ self,
913
+ sensor,
914
+ geomodel,
915
+ lat_coord: np.array,
916
+ lon_coord: np.array,
917
+ z_coord: np.array = None,
918
+ ) -> np.ndarray:
919
+ """
920
+ For a given image points list, compute the latitudes,
921
+ longitudes, altitudes
922
+
923
+ Advice: to be sure, use x,y,z list inputs only
924
+
925
+ :param sensor: path to sensor image
926
+ :param geomodel: path and attributes for geomodel
927
+ :param lat_coord: latitute Coordinate list
928
+ :param lon_coord: longitude Coordinates list
929
+ :param z_coord: Z Altitude list
930
+ :return: X / Y / Z Coordinates list in input image as a numpy array
931
+ """
932
+
933
+ def safe_inverse_loc( # pylint: disable=too-many-positional-arguments
622
934
  self,
623
935
  sensor,
624
936
  geomodel,
@@ -639,8 +951,38 @@ class AbstractGeometry(metaclass=ABCMeta):
639
951
  :param z_coord: Z Altitude list
640
952
  :return: X / Y / Z Coordinates list in input image as a numpy array
641
953
  """
954
+ if len(lat_coord) > 0:
955
+ image_points = self.inverse_loc(
956
+ sensor,
957
+ geomodel,
958
+ lat_coord,
959
+ lon_coord,
960
+ z_coord,
961
+ )
962
+ image_points = np.array(image_points)
963
+ else:
964
+ logging.warning("Inverse loc function launched on empty list")
965
+ return [], [], []
966
+ if z_coord is None:
967
+ image_points = np.array(image_points)
968
+ status = np.any(np.isnan(image_points), axis=0)
969
+ if sum(status) > 0:
970
+ logging.warning(
971
+ "{} errors have been detected on inverse "
972
+ "loc and will be re-launched".format(sum(status))
973
+ )
974
+ image_points_retry = self.inverse_loc(
975
+ sensor,
976
+ geomodel,
977
+ lat_coord[status],
978
+ lon_coord[status],
979
+ np.array([self.default_alt]),
980
+ )
981
+
982
+ image_points[:, status] = image_points_retry
983
+ return image_points[0], image_points[1], image_points[2]
642
984
 
643
- def image_envelope(
985
+ def image_envelope( # pylint: disable=too-many-positional-arguments
644
986
  self,
645
987
  sensor,
646
988
  geomodel,