cars 1.0.0rc2__cp312-cp312-win_amd64.whl

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

Potentially problematic release.


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

Files changed (225) hide show
  1. cars/__init__.py +86 -0
  2. cars/applications/__init__.py +40 -0
  3. cars/applications/application.py +117 -0
  4. cars/applications/application_constants.py +29 -0
  5. cars/applications/application_template.py +146 -0
  6. cars/applications/auxiliary_filling/__init__.py +29 -0
  7. cars/applications/auxiliary_filling/abstract_auxiliary_filling_app.py +105 -0
  8. cars/applications/auxiliary_filling/auxiliary_filling_algo.py +475 -0
  9. cars/applications/auxiliary_filling/auxiliary_filling_from_sensors_app.py +632 -0
  10. cars/applications/auxiliary_filling/auxiliary_filling_wrappers.py +90 -0
  11. cars/applications/dem_generation/__init__.py +30 -0
  12. cars/applications/dem_generation/abstract_dem_generation_app.py +116 -0
  13. cars/applications/dem_generation/bulldozer_config/base_config.yaml +42 -0
  14. cars/applications/dem_generation/bulldozer_dem_app.py +641 -0
  15. cars/applications/dem_generation/bulldozer_memory.py +55 -0
  16. cars/applications/dem_generation/dem_generation_algo.py +107 -0
  17. cars/applications/dem_generation/dem_generation_constants.py +32 -0
  18. cars/applications/dem_generation/dem_generation_wrappers.py +323 -0
  19. cars/applications/dense_match_filling/__init__.py +30 -0
  20. cars/applications/dense_match_filling/abstract_dense_match_filling_app.py +242 -0
  21. cars/applications/dense_match_filling/fill_disp_algo.py +113 -0
  22. cars/applications/dense_match_filling/fill_disp_constants.py +39 -0
  23. cars/applications/dense_match_filling/fill_disp_wrappers.py +83 -0
  24. cars/applications/dense_match_filling/zero_padding_app.py +302 -0
  25. cars/applications/dense_matching/__init__.py +30 -0
  26. cars/applications/dense_matching/abstract_dense_matching_app.py +261 -0
  27. cars/applications/dense_matching/census_mccnn_sgm_app.py +1461 -0
  28. cars/applications/dense_matching/cpp/__init__.py +0 -0
  29. cars/applications/dense_matching/cpp/dense_matching_cpp.cp312-win_amd64.dll.a +0 -0
  30. cars/applications/dense_matching/cpp/dense_matching_cpp.cp312-win_amd64.pyd +0 -0
  31. cars/applications/dense_matching/cpp/dense_matching_cpp.py +94 -0
  32. cars/applications/dense_matching/cpp/includes/dense_matching.hpp +58 -0
  33. cars/applications/dense_matching/cpp/meson.build +9 -0
  34. cars/applications/dense_matching/cpp/src/bindings.cpp +13 -0
  35. cars/applications/dense_matching/cpp/src/dense_matching.cpp +207 -0
  36. cars/applications/dense_matching/dense_matching_algo.py +401 -0
  37. cars/applications/dense_matching/dense_matching_constants.py +89 -0
  38. cars/applications/dense_matching/dense_matching_wrappers.py +951 -0
  39. cars/applications/dense_matching/disparity_grid_algo.py +597 -0
  40. cars/applications/dense_matching/loaders/__init__.py +23 -0
  41. cars/applications/dense_matching/loaders/config_census_sgm_default.json +31 -0
  42. cars/applications/dense_matching/loaders/config_census_sgm_homogeneous.json +30 -0
  43. cars/applications/dense_matching/loaders/config_census_sgm_mountain_and_vegetation.json +30 -0
  44. cars/applications/dense_matching/loaders/config_census_sgm_shadow.json +30 -0
  45. cars/applications/dense_matching/loaders/config_census_sgm_sparse.json +36 -0
  46. cars/applications/dense_matching/loaders/config_census_sgm_urban.json +30 -0
  47. cars/applications/dense_matching/loaders/config_mapping.json +13 -0
  48. cars/applications/dense_matching/loaders/config_mccnn.json +28 -0
  49. cars/applications/dense_matching/loaders/global_land_cover_map.tif +0 -0
  50. cars/applications/dense_matching/loaders/pandora_loader.py +593 -0
  51. cars/applications/dsm_filling/__init__.py +32 -0
  52. cars/applications/dsm_filling/abstract_dsm_filling_app.py +101 -0
  53. cars/applications/dsm_filling/border_interpolation_app.py +278 -0
  54. cars/applications/dsm_filling/bulldozer_config/base_config.yaml +44 -0
  55. cars/applications/dsm_filling/bulldozer_filling_app.py +288 -0
  56. cars/applications/dsm_filling/exogenous_filling_app.py +341 -0
  57. cars/applications/dsm_merging/__init__.py +28 -0
  58. cars/applications/dsm_merging/abstract_dsm_merging_app.py +101 -0
  59. cars/applications/dsm_merging/weighted_fusion_app.py +639 -0
  60. cars/applications/grid_correction/__init__.py +30 -0
  61. cars/applications/grid_correction/abstract_grid_correction_app.py +103 -0
  62. cars/applications/grid_correction/grid_correction_app.py +557 -0
  63. cars/applications/grid_generation/__init__.py +30 -0
  64. cars/applications/grid_generation/abstract_grid_generation_app.py +142 -0
  65. cars/applications/grid_generation/epipolar_grid_generation_app.py +327 -0
  66. cars/applications/grid_generation/grid_generation_algo.py +388 -0
  67. cars/applications/grid_generation/grid_generation_constants.py +46 -0
  68. cars/applications/grid_generation/transform_grid.py +88 -0
  69. cars/applications/ground_truth_reprojection/__init__.py +30 -0
  70. cars/applications/ground_truth_reprojection/abstract_ground_truth_reprojection_app.py +137 -0
  71. cars/applications/ground_truth_reprojection/direct_localization_app.py +629 -0
  72. cars/applications/ground_truth_reprojection/ground_truth_reprojection_algo.py +275 -0
  73. cars/applications/point_cloud_outlier_removal/__init__.py +30 -0
  74. cars/applications/point_cloud_outlier_removal/abstract_outlier_removal_app.py +385 -0
  75. cars/applications/point_cloud_outlier_removal/outlier_removal_algo.py +392 -0
  76. cars/applications/point_cloud_outlier_removal/outlier_removal_constants.py +43 -0
  77. cars/applications/point_cloud_outlier_removal/small_components_app.py +522 -0
  78. cars/applications/point_cloud_outlier_removal/statistical_app.py +528 -0
  79. cars/applications/rasterization/__init__.py +30 -0
  80. cars/applications/rasterization/abstract_pc_rasterization_app.py +183 -0
  81. cars/applications/rasterization/rasterization_algo.py +534 -0
  82. cars/applications/rasterization/rasterization_constants.py +38 -0
  83. cars/applications/rasterization/rasterization_wrappers.py +639 -0
  84. cars/applications/rasterization/simple_gaussian_app.py +1152 -0
  85. cars/applications/resampling/__init__.py +28 -0
  86. cars/applications/resampling/abstract_resampling_app.py +187 -0
  87. cars/applications/resampling/bicubic_resampling_app.py +760 -0
  88. cars/applications/resampling/resampling_algo.py +590 -0
  89. cars/applications/resampling/resampling_constants.py +36 -0
  90. cars/applications/resampling/resampling_wrappers.py +309 -0
  91. cars/applications/sensors_subsampling/__init__.py +32 -0
  92. cars/applications/sensors_subsampling/abstract_subsampling_app.py +109 -0
  93. cars/applications/sensors_subsampling/rasterio_subsampling_app.py +420 -0
  94. cars/applications/sensors_subsampling/subsampling_algo.py +108 -0
  95. cars/applications/sparse_matching/__init__.py +30 -0
  96. cars/applications/sparse_matching/abstract_sparse_matching_app.py +599 -0
  97. cars/applications/sparse_matching/sift_app.py +724 -0
  98. cars/applications/sparse_matching/sparse_matching_algo.py +360 -0
  99. cars/applications/sparse_matching/sparse_matching_constants.py +66 -0
  100. cars/applications/sparse_matching/sparse_matching_wrappers.py +282 -0
  101. cars/applications/triangulation/__init__.py +32 -0
  102. cars/applications/triangulation/abstract_triangulation_app.py +227 -0
  103. cars/applications/triangulation/line_of_sight_intersection_app.py +1243 -0
  104. cars/applications/triangulation/pc_transform.py +552 -0
  105. cars/applications/triangulation/triangulation_algo.py +371 -0
  106. cars/applications/triangulation/triangulation_constants.py +38 -0
  107. cars/applications/triangulation/triangulation_wrappers.py +259 -0
  108. cars/bundleadjustment.py +750 -0
  109. cars/cars.py +179 -0
  110. cars/conf/__init__.py +23 -0
  111. cars/conf/geoid/egm96.grd +0 -0
  112. cars/conf/geoid/egm96.grd.hdr +15 -0
  113. cars/conf/input_parameters.py +156 -0
  114. cars/conf/mask_cst.py +35 -0
  115. cars/core/__init__.py +23 -0
  116. cars/core/cars_logging.py +402 -0
  117. cars/core/constants.py +191 -0
  118. cars/core/constants_disparity.py +50 -0
  119. cars/core/datasets.py +140 -0
  120. cars/core/geometry/__init__.py +27 -0
  121. cars/core/geometry/abstract_geometry.py +1119 -0
  122. cars/core/geometry/shareloc_geometry.py +598 -0
  123. cars/core/inputs.py +568 -0
  124. cars/core/outputs.py +176 -0
  125. cars/core/preprocessing.py +722 -0
  126. cars/core/projection.py +843 -0
  127. cars/core/roi_tools.py +215 -0
  128. cars/core/tiling.py +774 -0
  129. cars/core/utils.py +164 -0
  130. cars/data_structures/__init__.py +23 -0
  131. cars/data_structures/cars_dataset.py +1544 -0
  132. cars/data_structures/cars_dict.py +74 -0
  133. cars/data_structures/corresponding_tiles_tools.py +186 -0
  134. cars/data_structures/dataframe_converter.py +185 -0
  135. cars/data_structures/format_transformation.py +297 -0
  136. cars/devibrate.py +689 -0
  137. cars/extractroi.py +264 -0
  138. cars/orchestrator/__init__.py +23 -0
  139. cars/orchestrator/achievement_tracker.py +125 -0
  140. cars/orchestrator/cluster/__init__.py +37 -0
  141. cars/orchestrator/cluster/abstract_cluster.py +250 -0
  142. cars/orchestrator/cluster/abstract_dask_cluster.py +381 -0
  143. cars/orchestrator/cluster/dask_cluster_tools.py +103 -0
  144. cars/orchestrator/cluster/dask_config/README.md +94 -0
  145. cars/orchestrator/cluster/dask_config/dask.yaml +21 -0
  146. cars/orchestrator/cluster/dask_config/distributed.yaml +70 -0
  147. cars/orchestrator/cluster/dask_config/jobqueue.yaml +26 -0
  148. cars/orchestrator/cluster/dask_config/reference_confs/dask-schema.yaml +137 -0
  149. cars/orchestrator/cluster/dask_config/reference_confs/dask.yaml +26 -0
  150. cars/orchestrator/cluster/dask_config/reference_confs/distributed-schema.yaml +1009 -0
  151. cars/orchestrator/cluster/dask_config/reference_confs/distributed.yaml +273 -0
  152. cars/orchestrator/cluster/dask_config/reference_confs/jobqueue.yaml +212 -0
  153. cars/orchestrator/cluster/dask_jobqueue_utils.py +204 -0
  154. cars/orchestrator/cluster/local_dask_cluster.py +116 -0
  155. cars/orchestrator/cluster/log_wrapper.py +728 -0
  156. cars/orchestrator/cluster/mp_cluster/__init__.py +27 -0
  157. cars/orchestrator/cluster/mp_cluster/mp_factorizer.py +212 -0
  158. cars/orchestrator/cluster/mp_cluster/mp_objects.py +535 -0
  159. cars/orchestrator/cluster/mp_cluster/mp_tools.py +93 -0
  160. cars/orchestrator/cluster/mp_cluster/mp_wrapper.py +505 -0
  161. cars/orchestrator/cluster/mp_cluster/multiprocessing_cluster.py +986 -0
  162. cars/orchestrator/cluster/mp_cluster/multiprocessing_profiler.py +399 -0
  163. cars/orchestrator/cluster/pbs_dask_cluster.py +207 -0
  164. cars/orchestrator/cluster/sequential_cluster.py +139 -0
  165. cars/orchestrator/cluster/slurm_dask_cluster.py +234 -0
  166. cars/orchestrator/memory_tools.py +47 -0
  167. cars/orchestrator/orchestrator.py +755 -0
  168. cars/orchestrator/orchestrator_constants.py +29 -0
  169. cars/orchestrator/registry/__init__.py +23 -0
  170. cars/orchestrator/registry/abstract_registry.py +143 -0
  171. cars/orchestrator/registry/compute_registry.py +106 -0
  172. cars/orchestrator/registry/id_generator.py +116 -0
  173. cars/orchestrator/registry/replacer_registry.py +213 -0
  174. cars/orchestrator/registry/saver_registry.py +363 -0
  175. cars/orchestrator/registry/unseen_registry.py +118 -0
  176. cars/orchestrator/tiles_profiler.py +279 -0
  177. cars/pipelines/__init__.py +26 -0
  178. cars/pipelines/conf_resolution/conf_final_resolution.yaml +5 -0
  179. cars/pipelines/conf_resolution/conf_first_resolution.yaml +4 -0
  180. cars/pipelines/conf_resolution/conf_intermediate_resolution.yaml +2 -0
  181. cars/pipelines/default/__init__.py +26 -0
  182. cars/pipelines/default/default_pipeline.py +1088 -0
  183. cars/pipelines/filling/__init__.py +26 -0
  184. cars/pipelines/filling/filling.py +981 -0
  185. cars/pipelines/formatting/__init__.py +26 -0
  186. cars/pipelines/formatting/formatting.py +186 -0
  187. cars/pipelines/merging/__init__.py +26 -0
  188. cars/pipelines/merging/merging.py +439 -0
  189. cars/pipelines/parameters/__init__.py +0 -0
  190. cars/pipelines/parameters/advanced_parameters.py +256 -0
  191. cars/pipelines/parameters/advanced_parameters_constants.py +68 -0
  192. cars/pipelines/parameters/application_parameters.py +72 -0
  193. cars/pipelines/parameters/depth_map_inputs.py +0 -0
  194. cars/pipelines/parameters/dsm_inputs.py +349 -0
  195. cars/pipelines/parameters/dsm_inputs_constants.py +25 -0
  196. cars/pipelines/parameters/output_constants.py +52 -0
  197. cars/pipelines/parameters/output_parameters.py +438 -0
  198. cars/pipelines/parameters/sensor_inputs.py +859 -0
  199. cars/pipelines/parameters/sensor_inputs_constants.py +51 -0
  200. cars/pipelines/parameters/sensor_loaders/__init__.py +29 -0
  201. cars/pipelines/parameters/sensor_loaders/basic_classif_loader.py +86 -0
  202. cars/pipelines/parameters/sensor_loaders/basic_image_loader.py +98 -0
  203. cars/pipelines/parameters/sensor_loaders/pivot_classif_loader.py +90 -0
  204. cars/pipelines/parameters/sensor_loaders/pivot_image_loader.py +105 -0
  205. cars/pipelines/parameters/sensor_loaders/sensor_loader.py +93 -0
  206. cars/pipelines/parameters/sensor_loaders/sensor_loader_template.py +71 -0
  207. cars/pipelines/parameters/sensor_loaders/slurp_classif_loader.py +86 -0
  208. cars/pipelines/pipeline.py +119 -0
  209. cars/pipelines/pipeline_constants.py +38 -0
  210. cars/pipelines/pipeline_template.py +135 -0
  211. cars/pipelines/subsampling/__init__.py +26 -0
  212. cars/pipelines/subsampling/subsampling.py +358 -0
  213. cars/pipelines/surface_modeling/__init__.py +26 -0
  214. cars/pipelines/surface_modeling/surface_modeling.py +2098 -0
  215. cars/pipelines/tie_points/__init__.py +26 -0
  216. cars/pipelines/tie_points/tie_points.py +536 -0
  217. cars/starter.py +167 -0
  218. cars-1.0.0rc2.dist-info/DELVEWHEEL +2 -0
  219. cars-1.0.0rc2.dist-info/METADATA +289 -0
  220. cars-1.0.0rc2.dist-info/RECORD +225 -0
  221. cars-1.0.0rc2.dist-info/WHEEL +4 -0
  222. cars-1.0.0rc2.dist-info/entry_points.txt +8 -0
  223. cars.libs/libgcc_s_seh-1-b2494fcbd4d80cf2c98fdd5261f6d850.dll +0 -0
  224. cars.libs/libstdc++-6-e9b0d12ae0e9555bbae55e8dfd08c3f7.dll +0 -0
  225. cars.libs/libwinpthread-1-7882d1b093714ccdfaf4e0789a817792.dll +0 -0
@@ -0,0 +1,722 @@
1
+ #!/usr/bin/env python
2
+ # coding: utf8
3
+ #
4
+ # Copyright (c) 2020 Centre National d'Etudes Spatiales (CNES).
5
+ #
6
+ # This file is part of CARS
7
+ # (see https://github.com/CNES/cars).
8
+ #
9
+ # Licensed under the Apache License, Version 2.0 (the "License");
10
+ # you may not use this file except in compliance with the License.
11
+ # You may obtain a copy of the License at
12
+ #
13
+ # http://www.apache.org/licenses/LICENSE-2.0
14
+ #
15
+ # Unless required by applicable law or agreed to in writing, software
16
+ # distributed under the License is distributed on an "AS IS" BASIS,
17
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18
+ # See the License for the specific language governing permissions and
19
+ # limitations under the License.
20
+ #
21
+ """
22
+ Preprocessing contains function used in pipelines
23
+ """
24
+ # pylint: disable=too-many-lines
25
+
26
+ # Standard imports
27
+ from __future__ import print_function
28
+
29
+ import json
30
+ import logging
31
+ import math
32
+ import os
33
+
34
+ import numpy as np
35
+ import utm
36
+ from pyproj import CRS
37
+ from shapely.geometry import Polygon
38
+
39
+ import cars.orchestrator.orchestrator as ocht
40
+ from cars.applications.grid_generation import grid_generation_algo as grids_algo
41
+
42
+ # CARS imports
43
+ from cars.core import inputs, projection, tiling
44
+ from cars.core.utils import safe_makedirs
45
+ from cars.orchestrator.cluster.log_wrapper import cars_profile
46
+ from cars.pipelines.parameters import sensor_inputs_constants as sens_cst
47
+
48
+ PREPROCESSING_TAG = "pair_preprocessing"
49
+ LEFT_ENVELOPE_TAG = "left_envelope"
50
+ RIGHT_ENVELOPE_TAG = "right_envelope"
51
+ ENVELOPES_INTERSECTION_TAG = "envelopes_intersection"
52
+ ENVELOPES_INTERSECTION_BB_TAG = "envelopes_intersection_bounding_box"
53
+
54
+
55
+ def get_utm_zone_as_epsg_code(lon, lat):
56
+ """
57
+ Returns the EPSG code of the UTM zone where the lat, lon point falls in
58
+
59
+ :param lon: longitude of the point
60
+ :type lon: float
61
+ :param lat: latitude of the point
62
+ :type lat: float
63
+ :return: The EPSG code corresponding to the UTM zone
64
+ :rtype: int
65
+ """
66
+
67
+ if lon is None or lat is None or np.isnan(lon) or np.isnan(lat):
68
+ logging.warning(
69
+ "An incorrect position was given when trying "
70
+ "to select the right EPSG for computations. "
71
+ "The default EPSG 32632 will be used."
72
+ )
73
+ return 32632
74
+
75
+ if lat > 84:
76
+ logging.warning(
77
+ "Since the latitude is above 84°, the EPSG 32661 will be used."
78
+ )
79
+ return 32661
80
+
81
+ if lat < -80:
82
+ logging.warning(
83
+ "Since the latitude is under -80°, the EPSG 32761 will be used."
84
+ )
85
+ return 32761
86
+
87
+ zone = utm.from_latlon(lat, lon)[2]
88
+
89
+ north_south = 600 if lat >= 0 else 700
90
+ return 32000 + north_south + zone
91
+
92
+
93
+ @cars_profile(name="Compute terrain bbox")
94
+ def compute_terrain_bbox( # pylint: disable=too-many-positional-arguments # noqa: 751
95
+ sensor_image_left,
96
+ sensor_image_right,
97
+ epipolar_image_left,
98
+ grid_left,
99
+ grid_right,
100
+ epsg,
101
+ geometry_plugin,
102
+ disp_min=-10,
103
+ disp_max=10,
104
+ resolution=0.5,
105
+ roi_poly=None,
106
+ pair_key="PAIR_0",
107
+ pair_folder=None,
108
+ orchestrator=None,
109
+ check_inputs=False,
110
+ ):
111
+ """
112
+ Compute terrain bounding box of current pair
113
+
114
+
115
+ :param srtm_dir: srtm directory
116
+ :type srtm_dir: str
117
+ :param default_alt: default altitude
118
+ :type default_alt: int
119
+ :param geoid: geoid path
120
+ :type geoid: str
121
+ :param sensor_image_left: left image
122
+ Dict Must contain keys : "image", "texture", "geomodel",
123
+ "no_data", "mask". Paths must be absolutes
124
+ :type sensor_image_left: dict
125
+ :param sensor_image_right: right image
126
+ Dict Must contain keys : "image", "texture", "geomodel",
127
+ "no_data", "mask". Paths must be absolutes
128
+ :type sensor_image_right: dict
129
+ :param grid_left: left grid. Grid dict contains :
130
+ - "grid_spacing", "grid_origin", \
131
+ "epipolar_size_x", "epipolar_size_y", \
132
+ "epipolar_origin_x", "epipolar_origin_y", \
133
+ "epipolar_spacing_x", "epipolar_spacing", \
134
+ "disp_to_alt_ratio", "path"
135
+ :type grid_left: dict
136
+ :param grid_right: right grid. Grid dict contains :
137
+ - "grid_spacing", "grid_origin",\
138
+ "epipolar_size_x", "epipolar_size_y", \
139
+ "epipolar_origin_x", "epipolar_origin_y", \
140
+ "epipolar_spacing_x", "epipolar_spacing", \
141
+ "disp_to_alt_ratio", "path"
142
+ :type grid_right: dict
143
+ :param epsg: epsg to use
144
+ :type epsg: str
145
+ :param geometry_plugin: geometry plugin to use
146
+ :type geometry_plugin: AbstractGeometry
147
+ :param disp_min: minimum disparity
148
+ :type disp_min: int
149
+ :param disp_max: maximum disparity
150
+ :type disp_max: int
151
+ :param resolution: resolution
152
+ :type resolution: float
153
+ :param roi_poly: roi polygon
154
+ :type roi_poly: Polygon
155
+ :param pair_key: pair key id
156
+ :type pair_key: str
157
+ :param pair_folder: pair folder to save data to
158
+ :type pair_folder: str
159
+ :param orchestrator: orchestrator
160
+ :type orchestrator: Orchestrator
161
+ :param check_inputs: true if user wants to check inputs
162
+ :type check_inputs: bool
163
+
164
+ :return: former post prepare configuration
165
+ :rtype: dict
166
+
167
+ """
168
+
169
+ # Default orchestrator
170
+ if orchestrator is None:
171
+ # Create default sequential orchestrator for current application
172
+ # be awere, no out_json will be shared between orchestrators
173
+ # No files saved
174
+ orchestrator = ocht.Orchestrator(
175
+ orchestrator_conf={"mode": "sequential"}
176
+ )
177
+
178
+ if pair_folder is None:
179
+ pair_folder = os.path.join(orchestrator.out_dir, "tmp")
180
+ safe_makedirs(pair_folder)
181
+
182
+ out_dir = pair_folder
183
+
184
+ # Check that the envelopes intersect one another
185
+ logging.info("Computing images envelopes and their intersection")
186
+ geojson1 = os.path.join(out_dir, "left_envelope.geojson")
187
+ geojson2 = os.path.join(out_dir, "right_envelope.geojson")
188
+ out_envelopes_intersection = os.path.join(
189
+ out_dir, "envelopes_intersection.geojson"
190
+ )
191
+
192
+ sensor1 = sensor_image_left[sens_cst.INPUT_IMG]
193
+ sensor2 = sensor_image_right[sens_cst.INPUT_IMG]
194
+ geomodel1 = sensor_image_left[sens_cst.INPUT_GEO_MODEL]
195
+ geomodel2 = sensor_image_right[sens_cst.INPUT_GEO_MODEL]
196
+
197
+ inter_poly, (
198
+ inter_xmin,
199
+ inter_ymin,
200
+ inter_xmax,
201
+ inter_ymax,
202
+ ) = projection.ground_intersection_envelopes(
203
+ sensor1["bands"]["b0"]["path"],
204
+ sensor2["bands"]["b0"]["path"],
205
+ geomodel1,
206
+ geomodel2,
207
+ geometry_plugin,
208
+ geojson1,
209
+ geojson2,
210
+ out_envelopes_intersection,
211
+ envelope_file_driver="GeoJSON",
212
+ intersect_file_driver="GeoJSON",
213
+ )
214
+
215
+ json_data_1 = None
216
+ json_data_2 = None
217
+ json_data_intersect = None
218
+
219
+ try:
220
+ with open(geojson1, "r", encoding="utf-8") as file_1:
221
+ json_data_1 = json.load(file_1)
222
+
223
+ with open(geojson2, "r", encoding="utf-8") as file_2:
224
+ json_data_2 = json.load(file_2)
225
+
226
+ with open(out_envelopes_intersection, "r", encoding="utf-8") as file_3:
227
+ json_data_intersect = json.load(file_3)
228
+ except Exception as exc:
229
+ raise FileNotFoundError("Unable to read bbox GeoJSON files") from exc
230
+
231
+ # update out_json
232
+ updating_dict = {
233
+ PREPROCESSING_TAG: {
234
+ pair_key: {
235
+ LEFT_ENVELOPE_TAG: json_data_1,
236
+ RIGHT_ENVELOPE_TAG: json_data_2,
237
+ ENVELOPES_INTERSECTION_TAG: json_data_intersect,
238
+ ENVELOPES_INTERSECTION_BB_TAG: [
239
+ inter_xmin,
240
+ inter_ymin,
241
+ inter_xmax,
242
+ inter_ymax,
243
+ ],
244
+ }
245
+ }
246
+ }
247
+
248
+ orchestrator.update_out_info(updating_dict)
249
+
250
+ if check_inputs:
251
+ logging.info("Checking DEM coverage")
252
+ _, epsg1 = inputs.read_vector(geojson1)
253
+ __, dem_coverage = projection.compute_dem_intersection_with_poly(
254
+ geometry_plugin.dem, inter_poly, epsg1
255
+ )
256
+
257
+ if dem_coverage < 100.0:
258
+ logging.warning(
259
+ "The input DEM covers {}% of the useful zone".format(
260
+ int(dem_coverage)
261
+ )
262
+ )
263
+
264
+ # Get largest epipolar regions from configuration file
265
+ largest_epipolar_region = [
266
+ 0,
267
+ 0,
268
+ grid_left["epipolar_size_x"],
269
+ grid_left["epipolar_size_y"],
270
+ ]
271
+
272
+ # Numpy array with corners of largest epipolar region.
273
+ # Order does not matter here,
274
+ # since it will be passed to grids.compute_epipolar_grid_min_max
275
+ corners = np.array(
276
+ [
277
+ [
278
+ [largest_epipolar_region[0], largest_epipolar_region[1]],
279
+ [largest_epipolar_region[0], largest_epipolar_region[3]],
280
+ ],
281
+ [
282
+ [largest_epipolar_region[2], largest_epipolar_region[3]],
283
+ [largest_epipolar_region[2], largest_epipolar_region[1]],
284
+ ],
285
+ ],
286
+ dtype=np.float64,
287
+ )
288
+
289
+ # Compute terrain min and max again, this time using estimated epsg code
290
+ terrain_dispmin, terrain_dispmax = grids_algo.compute_epipolar_grid_min_max(
291
+ geometry_plugin,
292
+ corners,
293
+ sensor1,
294
+ sensor2,
295
+ geomodel1,
296
+ geomodel2,
297
+ grid_left,
298
+ grid_right,
299
+ epsg,
300
+ disp_min,
301
+ disp_max,
302
+ )
303
+
304
+ # Compute bounds from epipolar image corners and dispmin/dispmax
305
+ terrain_bounds = np.stack((terrain_dispmin, terrain_dispmax), axis=0)
306
+ terrain_min = np.nanmin(terrain_bounds, axis=(0, 1))
307
+ terrain_max = np.nanmax(terrain_bounds, axis=(0, 1))
308
+
309
+ terrain_area = (terrain_max[0] - terrain_min[0]) * (
310
+ terrain_max[1] - terrain_min[1]
311
+ )
312
+
313
+ logging.info(
314
+ "Terrain area covered: {} square meters (or square degrees)".format(
315
+ terrain_area
316
+ )
317
+ )
318
+
319
+ # Retrieve bounding box of the ground intersection of the envelopes
320
+ inter_poly, inter_epsg = inputs.read_vector(out_envelopes_intersection)
321
+
322
+ # Project polygon if epsg is different
323
+ if epsg != inter_epsg:
324
+ inter_poly = projection.polygon_projection(inter_poly, inter_epsg, epsg)
325
+
326
+ (inter_xmin, inter_ymin, inter_xmax, inter_ymax) = inter_poly.bounds
327
+
328
+ # Align bounding box to integer resolution steps
329
+ xmin, ymin, xmax, ymax = tiling.snap_to_grid(
330
+ inter_xmin, inter_ymin, inter_xmax, inter_ymax, resolution
331
+ )
332
+
333
+ logging.info(
334
+ "Terrain bounding box : [{}, {}] x [{}, {}]".format(
335
+ xmin, xmax, ymin, ymax
336
+ )
337
+ )
338
+
339
+ terrain_bounding_box = [xmin, ymin, xmax, ymax]
340
+
341
+ # Check if roi given by user intersects with current terrain region
342
+ if roi_poly is not None:
343
+ if not roi_poly.intersects(inter_poly):
344
+ logging.warning(
345
+ "The pair composed of {} and {} "
346
+ "does not intersect the requested ROI".format(
347
+ sensor_image_left[sens_cst.INPUT_IMG],
348
+ sensor_image_right[sens_cst.INPUT_IMG],
349
+ )
350
+ )
351
+
352
+ # Get number of epipolar tiles that are previously used
353
+ nb_epipolar_tiles = (
354
+ epipolar_image_left.shape[0] * epipolar_image_left.shape[1]
355
+ )
356
+
357
+ # Compute average epipolar tile width
358
+ epipolar_average_tile_width = math.sqrt(terrain_area / nb_epipolar_tiles)
359
+
360
+ return (terrain_bounding_box, epipolar_average_tile_width), inter_poly
361
+
362
+
363
+ @cars_profile(name="Compute roi poly")
364
+ def compute_roi_poly(input_roi_poly, input_roi_epsg, epsg):
365
+ """
366
+ Compute roi polygon from input roi
367
+
368
+ :param input_roi_poly: roi polygon
369
+ :type input_roi_poly: shapely Polygon
370
+ :param input_roi_epsg: epsg of roi
371
+ :type input_roi_epsg: str
372
+ :param epsg: epsg to use
373
+ :type epsg: str
374
+
375
+ :return: polygon of roi with right epsg
376
+ :rtype: Polygon
377
+
378
+ """
379
+ roi_poly = input_roi_poly
380
+
381
+ if input_roi_poly is not None:
382
+ if input_roi_epsg != epsg:
383
+ roi_poly = projection.polygon_projection(
384
+ roi_poly, input_roi_epsg, epsg
385
+ )
386
+
387
+ return roi_poly
388
+
389
+
390
+ @cars_profile(name="Compute epsg")
391
+ def compute_epsg( # pylint: disable=too-many-positional-arguments
392
+ sensor_image_left,
393
+ sensor_image_right,
394
+ grid_left,
395
+ grid_right,
396
+ geometry_plugin,
397
+ disp_min=-10,
398
+ disp_max=10,
399
+ ):
400
+ """
401
+ Compute epsg to use
402
+
403
+ :param sensor_image_left: left image
404
+ Dict Must contain keys : "image", "texture", "geomodel",
405
+ "no_data", "mask". Paths must be absolutes
406
+ :type sensor_image_left: dict
407
+ :param sensor_image_right: right image
408
+ Dict Must contain keys : "image", "texture", "geomodel",
409
+ "no_data", "mask". Paths must be absolutes
410
+ :type sensor_image_right: dict
411
+ :param grid_left: left grid. Grid dict contains :
412
+ - Attributes containing: "grid_spacing", "grid_origin", \
413
+ "epipolar_size_x", "epipolar_size_y", \
414
+ "epipolar_origin_x", "epipolar_origin_y", \
415
+ "epipolar_spacing_x", "epipolar_spacing", \
416
+ "disp_to_alt_ratio", "path"
417
+ :type grid_left: dict
418
+ :param grid_right: right grid. Grid dict contains :
419
+ - Attributes containing: "grid_spacing", "grid_origin", \
420
+ "epipolar_size_x", "epipolar_size_y", \
421
+ "epipolar_origin_x", "epipolar_origin_y", \
422
+ "epipolar_spacing_x", "epipolar_spacing", \
423
+ "disp_to_alt_ratio", "path"
424
+ :type grid_right: dict
425
+ :param geometry_plugin: geometry plugin to use
426
+ :type geometry_plugin: AbstractGeometry
427
+ :param srtm_dir: srtm directory
428
+ :type srtm_dir: str
429
+ :param default_alt: default altitude
430
+ :type default_alt: int
431
+ :param disp_min: minimum disparity
432
+ :type disp_min: int
433
+ :param disp_max: maximum disparity
434
+ :type disp_max: int
435
+
436
+ :return: epsg
437
+ :rtype: str
438
+
439
+ """
440
+ sensor1 = sensor_image_left[sens_cst.INPUT_IMG]
441
+ sensor2 = sensor_image_right[sens_cst.INPUT_IMG]
442
+ geomodel1 = sensor_image_left[sens_cst.INPUT_GEO_MODEL]
443
+ geomodel2 = sensor_image_right[sens_cst.INPUT_GEO_MODEL]
444
+
445
+ # Get largest epipolar regions from configuration file
446
+ largest_epipolar_region = [
447
+ 0,
448
+ 0,
449
+ grid_left["epipolar_size_x"],
450
+ grid_left["epipolar_size_y"],
451
+ ]
452
+
453
+ # Numpy array with corners of largest epipolar region.
454
+ # Order does not matter here,
455
+ # since it will be passed to grids.compute_epipolar_grid_min_max
456
+ corners = np.array(
457
+ [
458
+ [
459
+ [largest_epipolar_region[0], largest_epipolar_region[1]],
460
+ [largest_epipolar_region[0], largest_epipolar_region[3]],
461
+ ],
462
+ [
463
+ [largest_epipolar_region[2], largest_epipolar_region[3]],
464
+ [largest_epipolar_region[2], largest_epipolar_region[1]],
465
+ ],
466
+ ],
467
+ dtype=np.float64,
468
+ )
469
+
470
+ # Compute epipolar image terrain position corners
471
+ # for min and max disparity
472
+ (
473
+ terrain_dispmin,
474
+ _,
475
+ ) = grids_algo.compute_epipolar_grid_min_max(
476
+ geometry_plugin,
477
+ corners,
478
+ sensor1,
479
+ sensor2,
480
+ geomodel1,
481
+ geomodel2,
482
+ grid_left,
483
+ grid_right,
484
+ 4326,
485
+ disp_min,
486
+ disp_max,
487
+ )
488
+
489
+ epsg = get_utm_zone_as_epsg_code(*np.nanmean(terrain_dispmin, axis=0))
490
+
491
+ logging.info("EPSG code: {}".format(epsg))
492
+
493
+ return epsg
494
+
495
+
496
+ def crop_terrain_bounds_with_roi(roi_poly, xmin, ymin, xmax, ymax):
497
+ """
498
+ Crop current terrain bounds with roi
499
+
500
+ :param roi_poly: Polygon of ROI
501
+ :type roi_poly: Shapely Polygon
502
+ :param xmin: xmin
503
+ :type xmin: float
504
+ :param ymin: ymin
505
+ :type ymin: float
506
+ :param xmax: xmax
507
+ :type xmax: float
508
+ :param ymax: ymax
509
+ :type ymax: float
510
+
511
+ :return: new xmin, ymin, xmax, ymax
512
+ :rtype: (float, float, float, float)
513
+ """
514
+ # terrain bounding box polygon
515
+ terrain_poly = Polygon(
516
+ [
517
+ (xmin, ymin),
518
+ (xmax, ymin),
519
+ (xmax, ymax),
520
+ (xmin, ymax),
521
+ (xmin, ymin),
522
+ ]
523
+ )
524
+
525
+ if not roi_poly.intersects(terrain_poly):
526
+ raise RuntimeError("None of the input data intersect the requested ROI")
527
+ # Show ROI if valid (no exception raised) :
528
+ logging.info("Setting terrain bounding box to the requested ROI")
529
+ new_xmin, new_ymin, new_xmax, new_ymax = roi_poly.bounds
530
+
531
+ return new_xmin, new_ymin, new_xmax, new_ymax
532
+
533
+
534
+ @cars_profile(name="Compute terrain bounds")
535
+ def compute_terrain_bounds(list_of_terrain_roi, roi_poly=None, resolution=0.5):
536
+ """
537
+ Compute Terrain bounds of merged pairs
538
+
539
+ :param list_of_terrain_roi: list of terrain roi
540
+ list of (terrain bbox, terrain epi_tile_size)
541
+ :type list_of_terrain_roi: list
542
+ :param roi_poly: terrain roi of given roi
543
+ :type roi_poly: Polygon
544
+ :param resolution: list of terrain roi
545
+ :type resolution: float
546
+
547
+ :return: bounds, optimal_terrain_tile_width_average
548
+
549
+ """
550
+
551
+ # get lists
552
+ (
553
+ list_terrain_bounding_box,
554
+ list_terrain_epi_tile_width,
555
+ ) = zip( # noqa: B905
556
+ *list_of_terrain_roi
557
+ )
558
+ list_terrain_bounding_box = list(list_terrain_bounding_box)
559
+ list_terrain_epi_tile_width = list(list_terrain_epi_tile_width)
560
+
561
+ xmin, ymin, xmax, ymax = tiling.union(list_terrain_bounding_box)
562
+
563
+ if roi_poly is not None:
564
+ (xmin, ymin, xmax, ymax) = crop_terrain_bounds_with_roi(
565
+ roi_poly, xmin, ymin, xmax, ymax
566
+ )
567
+
568
+ xmin, ymin, xmax, ymax = tiling.snap_to_grid(
569
+ xmin, ymin, xmax, ymax, resolution
570
+ )
571
+
572
+ logging.info(
573
+ "Total terrain bounding box : [{}, {}] x [{}, {}]".format(
574
+ xmin, xmax, ymin, ymax
575
+ )
576
+ )
577
+
578
+ bounds = [xmin, ymin, xmax, ymax]
579
+
580
+ # Compute optimal terrain tile width
581
+ optimal_terrain_tile_width_average = np.nanmean(list_terrain_epi_tile_width)
582
+
583
+ optimal_terrain_tile_width = (
584
+ int(math.ceil(optimal_terrain_tile_width_average / resolution))
585
+ * resolution
586
+ )
587
+
588
+ logging.info(
589
+ "Optimal terrain tile size: {}x{} pixels".format(
590
+ int(optimal_terrain_tile_width / resolution),
591
+ int(optimal_terrain_tile_width / resolution),
592
+ )
593
+ )
594
+
595
+ return bounds, optimal_terrain_tile_width
596
+
597
+
598
+ def get_conversion_factor(bounds, epsg, epsg_cloud):
599
+ """
600
+ Conmpute conversion factor
601
+
602
+ :param bounds: terrain bounds
603
+ :type bounds: list
604
+ :param epsg: epsg of bounds
605
+ :type epsg: int
606
+ :param epsg_cloud: epsg of the input cloud
607
+ :type epsg_cloud: int
608
+ :return: conversion factor
609
+ :rtype: float
610
+ """
611
+
612
+ conversion_factor = 1
613
+
614
+ # only if epsg and epsg_cloud are different
615
+ spatial_ref = CRS.from_epsg(epsg)
616
+ spatial_ref_cloud = CRS.from_epsg(epsg_cloud)
617
+ is_geographic = spatial_ref.is_geographic or spatial_ref_cloud.is_geographic
618
+ if is_geographic and epsg != epsg_cloud:
619
+ # Compute bounds and terrain grid
620
+ [xmin, ymin, xmax, ymax] = bounds
621
+ bounds_points = [
622
+ [xmin, ymin],
623
+ [xmax, ymax],
624
+ ]
625
+ bounds_point_epsg_cloud = projection.point_cloud_conversion(
626
+ bounds_points, epsg, epsg_cloud
627
+ )
628
+ # Compute area in both epsg
629
+ terrain_area_epsg = (xmax - xmin) * (ymax - ymin)
630
+ terrain_area_epsg_cloud = (
631
+ bounds_point_epsg_cloud[1][0] - bounds_point_epsg_cloud[0][0]
632
+ ) * (bounds_point_epsg_cloud[1][1] - bounds_point_epsg_cloud[0][1])
633
+ # Compute conversion factor
634
+ conversion_factor = math.sqrt(
635
+ terrain_area_epsg / terrain_area_epsg_cloud
636
+ )
637
+
638
+ return conversion_factor
639
+
640
+
641
+ def convert_optimal_tile_size_with_epsg(
642
+ bounds, optimal_terrain_tile_width, epsg, epsg_cloud
643
+ ):
644
+ """
645
+ Convert optimal_tile_size according to epsg.
646
+ Only if epsg_cloud is different of the output epsg.
647
+
648
+ :param bounds: terrain bounds
649
+ :type bounds: list
650
+ :param optimal_terrain_tile_width: initial optimal_terrain_tile_width
651
+ :type optimal_terrain_tile_width: float
652
+ :param epsg: target epsg
653
+ :type epsg: int
654
+ :param epsg_cloud: epsg of the input cloud
655
+ :type epsg_cloud: int
656
+ :return: converted optimal tile size
657
+ :rtype: float
658
+ """
659
+
660
+ # Convert optimal terrain tile width
661
+ conversion_factor = get_conversion_factor(bounds, epsg, epsg_cloud)
662
+ optimal_terrain_tile_width *= conversion_factor
663
+ return optimal_terrain_tile_width
664
+
665
+
666
+ @cars_profile(name="Compute epipolar roi")
667
+ def compute_epipolar_roi( # pylint: disable=too-many-positional-arguments
668
+ terrain_roi_poly,
669
+ terrain_roi_epsg,
670
+ geometry_plugin,
671
+ sensor_image_left,
672
+ sensor_image_right,
673
+ grid_left,
674
+ grid_right,
675
+ output_path,
676
+ disp_min=0,
677
+ disp_max=0,
678
+ ):
679
+ """
680
+ Compute epipolar roi to use
681
+
682
+ :param terrain_roi_poly: terrain roi polygon
683
+ :param terrain_roi_epsg: terrain roi epsg
684
+ :param geometry_plugin: geometry plugin to use
685
+ :param epsg: epsg
686
+ :param disp_min: minimum disparity
687
+ :param disp_max: maximum disparity
688
+
689
+ :return: epipolar region to use, with tile_size a sample
690
+ """
691
+
692
+ if terrain_roi_poly is None:
693
+ return None
694
+
695
+ roi_bbox = terrain_roi_poly.bounds
696
+
697
+ pair_folder = os.path.join(output_path, "tmp")
698
+ safe_makedirs(pair_folder)
699
+
700
+ sensor1 = sensor_image_left[sens_cst.INPUT_IMG]
701
+ sensor2 = sensor_image_right[sens_cst.INPUT_IMG]
702
+ geomodel1 = sensor_image_left[sens_cst.INPUT_GEO_MODEL]
703
+ geomodel2 = sensor_image_right[sens_cst.INPUT_GEO_MODEL]
704
+
705
+ epipolar_roi = grids_algo.terrain_region_to_epipolar(
706
+ roi_bbox,
707
+ sensor1,
708
+ sensor2,
709
+ geomodel1,
710
+ geomodel2,
711
+ grid_left,
712
+ grid_right,
713
+ geometry_plugin,
714
+ epsg=terrain_roi_epsg,
715
+ disp_min=disp_min,
716
+ disp_max=disp_max,
717
+ tile_size=100,
718
+ epipolar_size_x=grid_left["epipolar_size_x"],
719
+ epipolar_size_y=grid_left["epipolar_size_y"],
720
+ )
721
+
722
+ return epipolar_roi