cars 1.0.0rc3__cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (220) hide show
  1. cars/__init__.py +74 -0
  2. cars/applications/__init__.py +40 -0
  3. cars/applications/application.py +117 -0
  4. cars/applications/application_constants.py +29 -0
  5. cars/applications/application_template.py +146 -0
  6. cars/applications/auxiliary_filling/__init__.py +29 -0
  7. cars/applications/auxiliary_filling/abstract_auxiliary_filling_app.py +105 -0
  8. cars/applications/auxiliary_filling/auxiliary_filling_algo.py +475 -0
  9. cars/applications/auxiliary_filling/auxiliary_filling_from_sensors_app.py +632 -0
  10. cars/applications/auxiliary_filling/auxiliary_filling_wrappers.py +90 -0
  11. cars/applications/dem_generation/__init__.py +30 -0
  12. cars/applications/dem_generation/abstract_dem_generation_app.py +116 -0
  13. cars/applications/dem_generation/bulldozer_config/base_config.yaml +46 -0
  14. cars/applications/dem_generation/bulldozer_dem_app.py +641 -0
  15. cars/applications/dem_generation/bulldozer_memory.py +55 -0
  16. cars/applications/dem_generation/dem_generation_algo.py +107 -0
  17. cars/applications/dem_generation/dem_generation_constants.py +32 -0
  18. cars/applications/dem_generation/dem_generation_wrappers.py +323 -0
  19. cars/applications/dense_match_filling/__init__.py +30 -0
  20. cars/applications/dense_match_filling/abstract_dense_match_filling_app.py +242 -0
  21. cars/applications/dense_match_filling/fill_disp_algo.py +113 -0
  22. cars/applications/dense_match_filling/fill_disp_constants.py +39 -0
  23. cars/applications/dense_match_filling/fill_disp_wrappers.py +83 -0
  24. cars/applications/dense_match_filling/zero_padding_app.py +302 -0
  25. cars/applications/dense_matching/__init__.py +30 -0
  26. cars/applications/dense_matching/abstract_dense_matching_app.py +261 -0
  27. cars/applications/dense_matching/census_mccnn_sgm_app.py +1461 -0
  28. cars/applications/dense_matching/cpp/__init__.py +0 -0
  29. cars/applications/dense_matching/cpp/dense_matching_cpp.cpython-313-x86_64-linux-gnu.so +0 -0
  30. cars/applications/dense_matching/cpp/dense_matching_cpp.py +94 -0
  31. cars/applications/dense_matching/cpp/includes/dense_matching.hpp +58 -0
  32. cars/applications/dense_matching/cpp/meson.build +9 -0
  33. cars/applications/dense_matching/cpp/src/bindings.cpp +13 -0
  34. cars/applications/dense_matching/cpp/src/dense_matching.cpp +207 -0
  35. cars/applications/dense_matching/dense_matching_algo.py +401 -0
  36. cars/applications/dense_matching/dense_matching_constants.py +89 -0
  37. cars/applications/dense_matching/dense_matching_wrappers.py +951 -0
  38. cars/applications/dense_matching/disparity_grid_algo.py +597 -0
  39. cars/applications/dense_matching/loaders/__init__.py +23 -0
  40. cars/applications/dense_matching/loaders/config_census_sgm_default.json +31 -0
  41. cars/applications/dense_matching/loaders/config_census_sgm_homogeneous.json +30 -0
  42. cars/applications/dense_matching/loaders/config_census_sgm_mountain_and_vegetation.json +30 -0
  43. cars/applications/dense_matching/loaders/config_census_sgm_shadow.json +30 -0
  44. cars/applications/dense_matching/loaders/config_census_sgm_sparse.json +36 -0
  45. cars/applications/dense_matching/loaders/config_census_sgm_urban.json +30 -0
  46. cars/applications/dense_matching/loaders/config_mapping.json +13 -0
  47. cars/applications/dense_matching/loaders/config_mccnn.json +28 -0
  48. cars/applications/dense_matching/loaders/global_land_cover_map.tif +0 -0
  49. cars/applications/dense_matching/loaders/pandora_loader.py +593 -0
  50. cars/applications/dsm_filling/__init__.py +32 -0
  51. cars/applications/dsm_filling/abstract_dsm_filling_app.py +101 -0
  52. cars/applications/dsm_filling/border_interpolation_app.py +278 -0
  53. cars/applications/dsm_filling/bulldozer_config/base_config.yaml +44 -0
  54. cars/applications/dsm_filling/bulldozer_filling_app.py +288 -0
  55. cars/applications/dsm_filling/exogenous_filling_app.py +341 -0
  56. cars/applications/dsm_merging/__init__.py +28 -0
  57. cars/applications/dsm_merging/abstract_dsm_merging_app.py +101 -0
  58. cars/applications/dsm_merging/weighted_fusion_app.py +639 -0
  59. cars/applications/grid_correction/__init__.py +30 -0
  60. cars/applications/grid_correction/abstract_grid_correction_app.py +103 -0
  61. cars/applications/grid_correction/grid_correction_app.py +557 -0
  62. cars/applications/grid_generation/__init__.py +30 -0
  63. cars/applications/grid_generation/abstract_grid_generation_app.py +142 -0
  64. cars/applications/grid_generation/epipolar_grid_generation_app.py +327 -0
  65. cars/applications/grid_generation/grid_generation_algo.py +388 -0
  66. cars/applications/grid_generation/grid_generation_constants.py +46 -0
  67. cars/applications/grid_generation/transform_grid.py +88 -0
  68. cars/applications/ground_truth_reprojection/__init__.py +30 -0
  69. cars/applications/ground_truth_reprojection/abstract_ground_truth_reprojection_app.py +137 -0
  70. cars/applications/ground_truth_reprojection/direct_localization_app.py +629 -0
  71. cars/applications/ground_truth_reprojection/ground_truth_reprojection_algo.py +275 -0
  72. cars/applications/point_cloud_outlier_removal/__init__.py +30 -0
  73. cars/applications/point_cloud_outlier_removal/abstract_outlier_removal_app.py +385 -0
  74. cars/applications/point_cloud_outlier_removal/outlier_removal_algo.py +392 -0
  75. cars/applications/point_cloud_outlier_removal/outlier_removal_constants.py +43 -0
  76. cars/applications/point_cloud_outlier_removal/small_components_app.py +522 -0
  77. cars/applications/point_cloud_outlier_removal/statistical_app.py +528 -0
  78. cars/applications/rasterization/__init__.py +30 -0
  79. cars/applications/rasterization/abstract_pc_rasterization_app.py +183 -0
  80. cars/applications/rasterization/rasterization_algo.py +534 -0
  81. cars/applications/rasterization/rasterization_constants.py +38 -0
  82. cars/applications/rasterization/rasterization_wrappers.py +639 -0
  83. cars/applications/rasterization/simple_gaussian_app.py +1152 -0
  84. cars/applications/resampling/__init__.py +28 -0
  85. cars/applications/resampling/abstract_resampling_app.py +187 -0
  86. cars/applications/resampling/bicubic_resampling_app.py +760 -0
  87. cars/applications/resampling/resampling_algo.py +590 -0
  88. cars/applications/resampling/resampling_constants.py +36 -0
  89. cars/applications/resampling/resampling_wrappers.py +309 -0
  90. cars/applications/sensors_subsampling/__init__.py +32 -0
  91. cars/applications/sensors_subsampling/abstract_subsampling_app.py +109 -0
  92. cars/applications/sensors_subsampling/rasterio_subsampling_app.py +420 -0
  93. cars/applications/sensors_subsampling/subsampling_algo.py +108 -0
  94. cars/applications/sparse_matching/__init__.py +30 -0
  95. cars/applications/sparse_matching/abstract_sparse_matching_app.py +599 -0
  96. cars/applications/sparse_matching/sift_app.py +724 -0
  97. cars/applications/sparse_matching/sparse_matching_algo.py +360 -0
  98. cars/applications/sparse_matching/sparse_matching_constants.py +66 -0
  99. cars/applications/sparse_matching/sparse_matching_wrappers.py +282 -0
  100. cars/applications/triangulation/__init__.py +32 -0
  101. cars/applications/triangulation/abstract_triangulation_app.py +227 -0
  102. cars/applications/triangulation/line_of_sight_intersection_app.py +1243 -0
  103. cars/applications/triangulation/pc_transform.py +552 -0
  104. cars/applications/triangulation/triangulation_algo.py +371 -0
  105. cars/applications/triangulation/triangulation_constants.py +38 -0
  106. cars/applications/triangulation/triangulation_wrappers.py +259 -0
  107. cars/bundleadjustment.py +750 -0
  108. cars/cars.py +179 -0
  109. cars/conf/__init__.py +23 -0
  110. cars/conf/geoid/egm96.grd +0 -0
  111. cars/conf/geoid/egm96.grd.hdr +15 -0
  112. cars/conf/input_parameters.py +156 -0
  113. cars/conf/mask_cst.py +35 -0
  114. cars/core/__init__.py +23 -0
  115. cars/core/cars_logging.py +402 -0
  116. cars/core/constants.py +191 -0
  117. cars/core/constants_disparity.py +50 -0
  118. cars/core/datasets.py +140 -0
  119. cars/core/geometry/__init__.py +27 -0
  120. cars/core/geometry/abstract_geometry.py +1130 -0
  121. cars/core/geometry/shareloc_geometry.py +604 -0
  122. cars/core/inputs.py +568 -0
  123. cars/core/outputs.py +176 -0
  124. cars/core/preprocessing.py +722 -0
  125. cars/core/projection.py +843 -0
  126. cars/core/roi_tools.py +215 -0
  127. cars/core/tiling.py +774 -0
  128. cars/core/utils.py +164 -0
  129. cars/data_structures/__init__.py +23 -0
  130. cars/data_structures/cars_dataset.py +1544 -0
  131. cars/data_structures/cars_dict.py +74 -0
  132. cars/data_structures/corresponding_tiles_tools.py +186 -0
  133. cars/data_structures/dataframe_converter.py +185 -0
  134. cars/data_structures/format_transformation.py +297 -0
  135. cars/devibrate.py +689 -0
  136. cars/extractroi.py +264 -0
  137. cars/orchestrator/__init__.py +23 -0
  138. cars/orchestrator/achievement_tracker.py +125 -0
  139. cars/orchestrator/cluster/__init__.py +37 -0
  140. cars/orchestrator/cluster/abstract_cluster.py +250 -0
  141. cars/orchestrator/cluster/abstract_dask_cluster.py +381 -0
  142. cars/orchestrator/cluster/dask_cluster_tools.py +103 -0
  143. cars/orchestrator/cluster/dask_config/README.md +94 -0
  144. cars/orchestrator/cluster/dask_config/dask.yaml +21 -0
  145. cars/orchestrator/cluster/dask_config/distributed.yaml +70 -0
  146. cars/orchestrator/cluster/dask_config/jobqueue.yaml +26 -0
  147. cars/orchestrator/cluster/dask_config/reference_confs/dask-schema.yaml +137 -0
  148. cars/orchestrator/cluster/dask_config/reference_confs/dask.yaml +26 -0
  149. cars/orchestrator/cluster/dask_config/reference_confs/distributed-schema.yaml +1009 -0
  150. cars/orchestrator/cluster/dask_config/reference_confs/distributed.yaml +273 -0
  151. cars/orchestrator/cluster/dask_config/reference_confs/jobqueue.yaml +212 -0
  152. cars/orchestrator/cluster/dask_jobqueue_utils.py +204 -0
  153. cars/orchestrator/cluster/local_dask_cluster.py +116 -0
  154. cars/orchestrator/cluster/log_wrapper.py +728 -0
  155. cars/orchestrator/cluster/mp_cluster/__init__.py +27 -0
  156. cars/orchestrator/cluster/mp_cluster/mp_factorizer.py +212 -0
  157. cars/orchestrator/cluster/mp_cluster/mp_objects.py +535 -0
  158. cars/orchestrator/cluster/mp_cluster/mp_tools.py +93 -0
  159. cars/orchestrator/cluster/mp_cluster/mp_wrapper.py +505 -0
  160. cars/orchestrator/cluster/mp_cluster/multiprocessing_cluster.py +986 -0
  161. cars/orchestrator/cluster/mp_cluster/multiprocessing_profiler.py +399 -0
  162. cars/orchestrator/cluster/pbs_dask_cluster.py +207 -0
  163. cars/orchestrator/cluster/sequential_cluster.py +139 -0
  164. cars/orchestrator/cluster/slurm_dask_cluster.py +234 -0
  165. cars/orchestrator/memory_tools.py +47 -0
  166. cars/orchestrator/orchestrator.py +755 -0
  167. cars/orchestrator/orchestrator_constants.py +29 -0
  168. cars/orchestrator/registry/__init__.py +23 -0
  169. cars/orchestrator/registry/abstract_registry.py +143 -0
  170. cars/orchestrator/registry/compute_registry.py +106 -0
  171. cars/orchestrator/registry/id_generator.py +116 -0
  172. cars/orchestrator/registry/replacer_registry.py +213 -0
  173. cars/orchestrator/registry/saver_registry.py +363 -0
  174. cars/orchestrator/registry/unseen_registry.py +118 -0
  175. cars/orchestrator/tiles_profiler.py +279 -0
  176. cars/pipelines/__init__.py +26 -0
  177. cars/pipelines/conf_resolution/conf_final_resolution.yaml +5 -0
  178. cars/pipelines/conf_resolution/conf_first_resolution.yaml +4 -0
  179. cars/pipelines/conf_resolution/conf_intermediate_resolution.yaml +2 -0
  180. cars/pipelines/default/__init__.py +26 -0
  181. cars/pipelines/default/default_pipeline.py +1095 -0
  182. cars/pipelines/filling/__init__.py +26 -0
  183. cars/pipelines/filling/filling.py +981 -0
  184. cars/pipelines/formatting/__init__.py +26 -0
  185. cars/pipelines/formatting/formatting.py +190 -0
  186. cars/pipelines/merging/__init__.py +26 -0
  187. cars/pipelines/merging/merging.py +439 -0
  188. cars/pipelines/parameters/__init__.py +0 -0
  189. cars/pipelines/parameters/advanced_parameters.py +256 -0
  190. cars/pipelines/parameters/advanced_parameters_constants.py +68 -0
  191. cars/pipelines/parameters/application_parameters.py +72 -0
  192. cars/pipelines/parameters/depth_map_inputs.py +0 -0
  193. cars/pipelines/parameters/dsm_inputs.py +349 -0
  194. cars/pipelines/parameters/dsm_inputs_constants.py +25 -0
  195. cars/pipelines/parameters/output_constants.py +52 -0
  196. cars/pipelines/parameters/output_parameters.py +435 -0
  197. cars/pipelines/parameters/sensor_inputs.py +859 -0
  198. cars/pipelines/parameters/sensor_inputs_constants.py +51 -0
  199. cars/pipelines/parameters/sensor_loaders/__init__.py +29 -0
  200. cars/pipelines/parameters/sensor_loaders/basic_classif_loader.py +86 -0
  201. cars/pipelines/parameters/sensor_loaders/basic_image_loader.py +98 -0
  202. cars/pipelines/parameters/sensor_loaders/pivot_classif_loader.py +90 -0
  203. cars/pipelines/parameters/sensor_loaders/pivot_image_loader.py +105 -0
  204. cars/pipelines/parameters/sensor_loaders/sensor_loader.py +93 -0
  205. cars/pipelines/parameters/sensor_loaders/sensor_loader_template.py +71 -0
  206. cars/pipelines/parameters/sensor_loaders/slurp_classif_loader.py +86 -0
  207. cars/pipelines/pipeline.py +119 -0
  208. cars/pipelines/pipeline_constants.py +38 -0
  209. cars/pipelines/pipeline_template.py +135 -0
  210. cars/pipelines/subsampling/__init__.py +26 -0
  211. cars/pipelines/subsampling/subsampling.py +358 -0
  212. cars/pipelines/surface_modeling/__init__.py +26 -0
  213. cars/pipelines/surface_modeling/surface_modeling.py +2098 -0
  214. cars/pipelines/tie_points/__init__.py +26 -0
  215. cars/pipelines/tie_points/tie_points.py +536 -0
  216. cars/starter.py +167 -0
  217. cars-1.0.0rc3.dist-info/METADATA +289 -0
  218. cars-1.0.0rc3.dist-info/RECORD +220 -0
  219. cars-1.0.0rc3.dist-info/WHEEL +6 -0
  220. cars-1.0.0rc3.dist-info/entry_points.txt +8 -0
@@ -0,0 +1,724 @@
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
+ this module contains the dense_matching application class.
23
+ """
24
+
25
+ # pylint: disable= C0302
26
+
27
+ # Standard imports
28
+ import logging
29
+ import os
30
+ from typing import Dict, Tuple
31
+
32
+ # Third party imports
33
+ import numpy as np
34
+ import pandas
35
+ import xarray as xr
36
+ from json_checker import And, Checker, Or
37
+
38
+ import cars.applications.sparse_matching.sparse_matching_constants as sm_cst
39
+ import cars.orchestrator.orchestrator as ocht
40
+ from cars.applications import application_constants
41
+
42
+ # CARS imports
43
+ from cars.applications.sparse_matching import sparse_matching_algo
44
+ from cars.applications.sparse_matching.abstract_sparse_matching_app import (
45
+ SparseMatching,
46
+ )
47
+ from cars.core import constants as cst
48
+ from cars.core.utils import safe_makedirs
49
+ from cars.data_structures import cars_dataset
50
+
51
+
52
+ class Sift(SparseMatching, short_name=["sift"]):
53
+ """
54
+ SparseMatching
55
+ """
56
+
57
+ # pylint: disable=too-many-instance-attributes
58
+
59
+ def __init__(self, conf=None):
60
+ """
61
+ Init function of SparseMatching
62
+
63
+ :param conf: configuration for matching
64
+ :return: a application_to_use object
65
+ """
66
+
67
+ super().__init__(conf=conf)
68
+
69
+ # check conf
70
+ self.used_method = self.used_config["method"]
71
+ self.elevation_delta_lower_bound = self.used_config[
72
+ "elevation_delta_lower_bound"
73
+ ]
74
+ self.elevation_delta_upper_bound = self.used_config[
75
+ "elevation_delta_upper_bound"
76
+ ]
77
+ self.tile_margin = self.used_config["tile_margin"]
78
+ self.epipolar_error_upper_bound = self.used_config[
79
+ "epipolar_error_upper_bound"
80
+ ]
81
+ self.epipolar_error_maximum_bias = self.used_config[
82
+ "epipolar_error_maximum_bias"
83
+ ]
84
+
85
+ # minimum number of matches to continue with
86
+ self.minimum_nb_matches = self.used_config["minimum_nb_matches"]
87
+
88
+ # sifts
89
+ self.sift_matching_threshold = self.used_config[
90
+ "sift_matching_threshold"
91
+ ]
92
+ self.sift_n_octave = self.used_config["sift_n_octave"]
93
+ self.sift_n_scale_per_octave = self.used_config[
94
+ "sift_n_scale_per_octave"
95
+ ]
96
+ self.sift_peak_threshold = self.used_config["sift_peak_threshold"]
97
+ self.sift_edge_threshold = self.used_config["sift_edge_threshold"]
98
+ self.sift_magnification = self.used_config["sift_magnification"]
99
+ self.sift_window_size = self.used_config["sift_window_size"]
100
+ self.sift_back_matching = self.used_config["sift_back_matching"]
101
+ self.match_filter_knn = self.used_config["match_filter_knn"]
102
+ self.match_filter_constant = self.used_config["match_filter_constant"]
103
+ self.match_filter_mean_factor = self.used_config[
104
+ "match_filter_mean_factor"
105
+ ]
106
+ self.match_filter_dev_factor = self.used_config[
107
+ "match_filter_dev_factor"
108
+ ]
109
+ self.decimation_factor = self.used_config["decimation_factor"]
110
+ self.used_band = self.used_config["used_band"]
111
+
112
+ # Saving files
113
+ self.save_intermediate_data = self.used_config["save_intermediate_data"]
114
+
115
+ self.disparity_bounds_estimation = self.used_config[
116
+ "disparity_bounds_estimation"
117
+ ]
118
+
119
+ # Init orchestrator
120
+ self.orchestrator = None
121
+
122
+ def check_conf(self, conf):
123
+ """
124
+ Check configuration
125
+
126
+ :param conf: configuration to check
127
+ :type conf: dict
128
+
129
+ :return: overloaded configuration
130
+ :rtype: dict
131
+
132
+ """
133
+
134
+ # init conf
135
+ if conf is not None:
136
+ overloaded_conf = conf.copy()
137
+ else:
138
+ conf = {}
139
+ overloaded_conf = {}
140
+
141
+ # Overload conf
142
+ overloaded_conf["method"] = conf.get("method", "sift")
143
+ overloaded_conf["elevation_delta_lower_bound"] = conf.get(
144
+ "elevation_delta_lower_bound", None
145
+ )
146
+ overloaded_conf["decimation_factor"] = conf.get("decimation_factor", 30)
147
+ overloaded_conf["elevation_delta_upper_bound"] = conf.get(
148
+ "elevation_delta_upper_bound", None
149
+ )
150
+ overloaded_conf["tile_margin"] = conf.get("tile_margin", 10)
151
+ overloaded_conf["epipolar_error_upper_bound"] = conf.get(
152
+ "epipolar_error_upper_bound", 10.0
153
+ )
154
+ overloaded_conf["epipolar_error_maximum_bias"] = conf.get(
155
+ "epipolar_error_maximum_bias", 50.0
156
+ )
157
+
158
+ # minimum number of matches to continue with
159
+ overloaded_conf["minimum_nb_matches"] = conf.get(
160
+ "minimum_nb_matches", 90
161
+ )
162
+
163
+ # sifts params
164
+ overloaded_conf["sift_matching_threshold"] = conf.get(
165
+ "sift_matching_threshold", 0.7
166
+ )
167
+ overloaded_conf["sift_n_octave"] = conf.get("sift_n_octave", 8)
168
+ overloaded_conf["sift_n_scale_per_octave"] = conf.get(
169
+ "sift_n_scale_per_octave", 3
170
+ )
171
+ overloaded_conf["sift_peak_threshold"] = conf.get(
172
+ "sift_peak_threshold", 4.0
173
+ )
174
+ overloaded_conf["sift_edge_threshold"] = conf.get(
175
+ "sift_edge_threshold", 10.0
176
+ )
177
+
178
+ overloaded_conf["sift_magnification"] = conf.get(
179
+ "sift_magnification", 7.0
180
+ )
181
+ overloaded_conf["sift_window_size"] = conf.get("sift_window_size", 2)
182
+ overloaded_conf["sift_back_matching"] = conf.get(
183
+ "sift_back_matching", True
184
+ )
185
+
186
+ overloaded_conf["match_filter_knn"] = conf.get("match_filter_knn", 25)
187
+
188
+ overloaded_conf["match_filter_constant"] = conf.get(
189
+ "match_filter_constant", 0.0
190
+ )
191
+
192
+ overloaded_conf["match_filter_mean_factor"] = conf.get(
193
+ "match_filter_mean_factor", 1.3
194
+ )
195
+
196
+ overloaded_conf["match_filter_dev_factor"] = conf.get(
197
+ "match_filter_dev_factor", 3.0
198
+ )
199
+
200
+ overloaded_conf["used_band"] = conf.get("used_band", "b0")
201
+
202
+ # Saving files
203
+ overloaded_conf["save_intermediate_data"] = conf.get(
204
+ "save_intermediate_data", False
205
+ )
206
+ self.save_intermediate_data = overloaded_conf["save_intermediate_data"]
207
+
208
+ # confidence filtering parameters
209
+ overloaded_conf["disparity_bounds_estimation"] = conf.get(
210
+ "disparity_bounds_estimation", {}
211
+ )
212
+
213
+ sparse_matching_schema = {
214
+ "method": str,
215
+ "minimum_nb_matches": And(int, lambda x: x > 0),
216
+ "elevation_delta_lower_bound": Or(int, float, None),
217
+ "elevation_delta_upper_bound": Or(int, float, None),
218
+ "tile_margin": And(int, lambda x: x > 0),
219
+ "epipolar_error_upper_bound": And(float, lambda x: x > 0),
220
+ "epipolar_error_maximum_bias": And(float, lambda x: x >= 0),
221
+ "sift_matching_threshold": And(float, lambda x: x > 0),
222
+ "sift_n_octave": And(int, lambda x: x > 0),
223
+ "sift_n_scale_per_octave": And(int, lambda x: x > 0),
224
+ "sift_peak_threshold": Or(float, int),
225
+ "sift_edge_threshold": float,
226
+ "sift_magnification": And(float, lambda x: x > 0),
227
+ "sift_window_size": And(int, lambda x: x > 0),
228
+ "decimation_factor": And(int, lambda x: x > 0),
229
+ "sift_back_matching": bool,
230
+ "match_filter_knn": int,
231
+ "match_filter_constant": Or(int, float),
232
+ "match_filter_mean_factor": Or(int, float),
233
+ "match_filter_dev_factor": Or(int, float),
234
+ "used_band": str,
235
+ "save_intermediate_data": bool,
236
+ "disparity_bounds_estimation": dict,
237
+ }
238
+
239
+ # Check conf
240
+ checker = Checker(sparse_matching_schema)
241
+ checker.validate(overloaded_conf)
242
+
243
+ self.check_conf_disparity_bounds_estimation(overloaded_conf)
244
+
245
+ # Check consistency between bounds for elevation delta
246
+ elevation_delta_lower_bound = overloaded_conf[
247
+ "elevation_delta_lower_bound"
248
+ ]
249
+ elevation_delta_upper_bound = overloaded_conf[
250
+ "elevation_delta_upper_bound"
251
+ ]
252
+ if None not in (
253
+ elevation_delta_lower_bound,
254
+ elevation_delta_upper_bound,
255
+ ):
256
+ if elevation_delta_lower_bound > elevation_delta_upper_bound:
257
+ raise ValueError(
258
+ "Upper bound must be bigger than "
259
+ "lower bound for expected elevation delta"
260
+ )
261
+
262
+ return overloaded_conf
263
+
264
+ def get_required_bands(self):
265
+ """
266
+ Get bands required by this application
267
+
268
+ :return: required bands for left and right image
269
+ :rtype: dict
270
+ """
271
+ required_bands = {}
272
+ required_bands["left"] = [self.used_band]
273
+ required_bands["right"] = [self.used_band]
274
+ return required_bands
275
+
276
+ def check_conf_disparity_bounds_estimation(self, overloaded_conf):
277
+ """
278
+ Check the disparity bounds estimation conf
279
+ """
280
+ overloaded_conf["disparity_bounds_estimation"]["activated"] = (
281
+ overloaded_conf["disparity_bounds_estimation"].get(
282
+ "activated", True
283
+ )
284
+ )
285
+ overloaded_conf["disparity_bounds_estimation"]["percentile"] = (
286
+ overloaded_conf["disparity_bounds_estimation"].get("percentile", 1)
287
+ )
288
+ overloaded_conf["disparity_bounds_estimation"]["lower_margin"] = (
289
+ overloaded_conf["disparity_bounds_estimation"].get(
290
+ "lower_margin", 500
291
+ )
292
+ )
293
+ overloaded_conf["disparity_bounds_estimation"]["upper_margin"] = (
294
+ overloaded_conf["disparity_bounds_estimation"].get(
295
+ "upper_margin", 1000
296
+ )
297
+ )
298
+
299
+ disparity_bounds_estimation_schema = {
300
+ "activated": bool,
301
+ "percentile": Or(int, float),
302
+ "upper_margin": int,
303
+ "lower_margin": int,
304
+ }
305
+
306
+ checker_disparity_bounds_estimation_schema = Checker(
307
+ disparity_bounds_estimation_schema
308
+ )
309
+ checker_disparity_bounds_estimation_schema.validate(
310
+ overloaded_conf["disparity_bounds_estimation"]
311
+ )
312
+
313
+ def get_save_matches(self):
314
+ """
315
+ Get save_matches parameter
316
+
317
+ :return: true is save_matches activated
318
+ :rtype: bool
319
+ """
320
+
321
+ return self.save_intermediate_data
322
+
323
+ def get_tile_margin(self):
324
+ """
325
+ Get strip margin corresponding to sparse matches
326
+
327
+ :return: margin in percent
328
+
329
+ """
330
+ return self.tile_margin
331
+
332
+ def get_epipolar_error_upper_bound(self):
333
+ """
334
+ Get epipolar error upper bound corresponding to sparse matches
335
+
336
+ :return: margin
337
+
338
+ """
339
+
340
+ return self.epipolar_error_upper_bound
341
+
342
+ def get_minimum_nb_matches(self):
343
+ """
344
+ Get minimum_nb_matches :
345
+ get the minimum number of matches
346
+
347
+ :return: minimum_nb_matches
348
+
349
+ """
350
+
351
+ return self.minimum_nb_matches
352
+
353
+ def get_epipolar_error_maximum_bias(self):
354
+ """
355
+ Get epipolar error maximum bias corresponding to sparse matches
356
+
357
+ :return: margin
358
+
359
+ """
360
+
361
+ return self.epipolar_error_maximum_bias
362
+
363
+ def get_match_filter_knn(self):
364
+ """
365
+ Get match_filter_knn :
366
+ number of neighboors used to measure isolation of matches
367
+
368
+ :return: match_filter_knn
369
+
370
+ """
371
+ return self.match_filter_knn
372
+
373
+ def get_match_filter_constant(self):
374
+ """
375
+ Get get_match_filter_constant :
376
+ constant in the formula to compute threshold of outliers
377
+
378
+ :return: match_filter_constant
379
+
380
+ """
381
+ return self.match_filter_constant
382
+
383
+ def get_match_filter_mean_factor(self):
384
+ """
385
+ Get match_filter_mean_factor :
386
+ factor of mean in the formula
387
+ to compute threshold of outliers
388
+
389
+ :return: match_filter_mean_factor
390
+
391
+ """
392
+ return self.match_filter_mean_factor
393
+
394
+ def get_match_filter_dev_factor(self):
395
+ """
396
+ Get match_filter_dev_factor :
397
+ factor of deviation in the formula
398
+ to compute threshold of outliers
399
+
400
+ :return: match_filter_dev_factor
401
+
402
+ """
403
+ return self.match_filter_dev_factor
404
+
405
+ def get_decimation_factor(self):
406
+ """
407
+ Get decimation_factor
408
+
409
+ :return: decimation_factor
410
+
411
+ """
412
+ return self.decimation_factor
413
+
414
+ def set_decimation_factor(self, value):
415
+ """
416
+ set decimation_factor
417
+
418
+ """
419
+ self.decimation_factor = value
420
+
421
+ def run( # pylint: disable=too-many-positional-arguments
422
+ self,
423
+ epipolar_image_left,
424
+ epipolar_image_right,
425
+ disp_to_alt_ratio,
426
+ orchestrator=None,
427
+ pair_folder=None,
428
+ pair_key="PAIR_0",
429
+ classif_bands_to_mask=None,
430
+ ):
431
+ """
432
+ Run Matching application.
433
+
434
+ Create left and right CarsDataset filled with pandas.DataFrame ,
435
+ corresponding to epipolar 2D disparities, on the same geometry
436
+ that epipolar_image_left and epipolar_image_right.
437
+
438
+ :param epipolar_image_left: tiled left epipolar. CarsDataset contains:
439
+
440
+ - N x M Delayed tiles \
441
+ Each tile will be a future xarray Dataset containing:
442
+
443
+ - data with keys : "im", "msk", "texture"
444
+ - attrs with keys: "margins" with "disp_min" and "disp_max"
445
+ "transform", "crs", "valid_pixels", "no_data_mask",
446
+ "no_data_img"
447
+ - attributes containing:
448
+ "largest_epipolar_region","opt_epipolar_tile_size"
449
+ :type epipolar_image_left: CarsDataset
450
+ :param epipolar_image_right: tiled right epipolar.CarsDataset contains:
451
+
452
+ - N x M Delayed tiles \
453
+ Each tile will be a future xarray Dataset containing:
454
+
455
+ - data with keys : "im", "msk", "texture"
456
+ - attrs with keys: "margins" with "disp_min" and "disp_max"\
457
+ "transform", "crs", "valid_pixels", "no_data_mask",\
458
+ "no_data_img"
459
+ - attributes containing:"largest_epipolar_region", \
460
+ "opt_epipolar_tile_size"
461
+ :type epipolar_image_right: CarsDataset
462
+ :param disp_to_alt_ratio: disp to alti ratio
463
+ :type disp_to_alt_ratio: float
464
+ :param orchestrator: orchestrator used
465
+ :param pair_folder: folder used for current pair
466
+ :type pair_folder: str
467
+ :param pair_key: pair key id
468
+ :type pair_key: str
469
+ :param classif_bands_to_mask: bands from classif to mask
470
+ :type classif_bands_to_mask: list of str / int
471
+
472
+ :return left matches, right matches. Each CarsDataset contains:
473
+
474
+ - N x M Delayed tiles \
475
+ Each tile will be a future pandas DataFrame containing:
476
+ - data : (L, 4) shape matches
477
+ - attributes containing "disp_lower_bound", "disp_upper_bound",\
478
+ "elevation_delta_lower_bound","elevation_delta_upper_bound"
479
+
480
+ :rtype: Tuple(CarsDataset, CarsDataset)
481
+ """
482
+ # Default orchestrator
483
+ if orchestrator is None:
484
+ # Create default sequential orchestrator for current application
485
+ # be awere, no out_json will be shared between orchestrators
486
+ # No files saved
487
+ self.orchestrator = ocht.Orchestrator(
488
+ orchestrator_conf={"mode": "sequential"}
489
+ )
490
+ else:
491
+ self.orchestrator = orchestrator
492
+
493
+ if pair_folder is None:
494
+ pair_folder = os.path.join(self.orchestrator.out_dir, "tmp")
495
+
496
+ if epipolar_image_left.dataset_type == "arrays":
497
+ # Create CarsDataset
498
+ # Epipolar_disparity
499
+ epipolar_disparity_map_left = cars_dataset.CarsDataset(
500
+ "points", name="sparse_matching_" + pair_key
501
+ )
502
+ epipolar_disparity_map_left.create_empty_copy(epipolar_image_left)
503
+
504
+ # Update attributes to get epipolar info
505
+ epipolar_disparity_map_left.attributes.update(
506
+ epipolar_image_left.attributes
507
+ )
508
+
509
+ # Save disparity maps
510
+ if self.save_intermediate_data:
511
+ safe_makedirs(pair_folder)
512
+
513
+ self.orchestrator.add_to_save_lists(
514
+ os.path.join(pair_folder, "epi_matches_left"),
515
+ None,
516
+ epipolar_disparity_map_left,
517
+ cars_ds_name="epi_matches_left",
518
+ )
519
+
520
+ # Compute disparity range
521
+ if self.elevation_delta_lower_bound is None:
522
+ disp_upper_bound = np.inf
523
+ else:
524
+ disp_upper_bound = (
525
+ -self.elevation_delta_lower_bound / disp_to_alt_ratio
526
+ )
527
+ if self.elevation_delta_upper_bound is None:
528
+ disp_lower_bound = -np.inf
529
+ else:
530
+ disp_lower_bound = (
531
+ -self.elevation_delta_upper_bound / disp_to_alt_ratio
532
+ )
533
+
534
+ attributes = {
535
+ "disp_lower_bound": disp_lower_bound,
536
+ "disp_upper_bound": disp_upper_bound,
537
+ "elevation_delta_lower_bound": self.elevation_delta_lower_bound,
538
+ "elevation_delta_upper_bound": self.elevation_delta_upper_bound,
539
+ }
540
+
541
+ epipolar_disparity_map_left.attributes.update(attributes)
542
+
543
+ # Get saving infos in order to save tiles when they are computed
544
+ [saving_info_left] = self.orchestrator.get_saving_infos(
545
+ [epipolar_disparity_map_left]
546
+ )
547
+
548
+ # Update orchestrator out_json
549
+ updating_infos = {
550
+ application_constants.APPLICATION_TAG: {
551
+ sm_cst.SPARSE_MATCHING_RUN_TAG: {
552
+ pair_key: {
553
+ sm_cst.DISP_LOWER_BOUND: disp_lower_bound,
554
+ sm_cst.DISP_UPPER_BOUND: disp_upper_bound,
555
+ },
556
+ }
557
+ }
558
+ }
559
+ orchestrator.update_out_info(updating_infos)
560
+ logging.info(
561
+ "Generate disparity: Number tiles: {}".format(
562
+ epipolar_disparity_map_left.shape[1]
563
+ * epipolar_disparity_map_left.shape[0]
564
+ )
565
+ )
566
+
567
+ # Add to replace list so tiles will be readable at the same time
568
+ self.orchestrator.add_to_replace_lists(
569
+ epipolar_disparity_map_left, cars_ds_name="epi_matches_left"
570
+ )
571
+ # Generate disparity maps
572
+ total_nb_band_sift = epipolar_disparity_map_left.shape[0]
573
+
574
+ step = int(np.round(100 / self.decimation_factor))
575
+
576
+ if total_nb_band_sift in (1, 2):
577
+ step = 1
578
+ elif total_nb_band_sift == 3:
579
+ step = 2
580
+
581
+ for row in range(0, total_nb_band_sift, step):
582
+ for col in range(len(epipolar_image_left[row])):
583
+ # initialize list of matches
584
+ full_saving_info_left = ocht.update_saving_infos(
585
+ saving_info_left, row=row, col=col
586
+ )
587
+ # Compute matches
588
+ if type(None) not in (
589
+ type(epipolar_image_left[row, col]),
590
+ type(epipolar_image_right[row, col]),
591
+ ):
592
+ (
593
+ epipolar_disparity_map_left[row, col]
594
+ ) = self.orchestrator.cluster.create_task(
595
+ compute_matches_wrapper, nout=1
596
+ )(
597
+ epipolar_image_left[row, col],
598
+ epipolar_image_right[row, col],
599
+ used_band=self.used_band,
600
+ matching_threshold=self.sift_matching_threshold,
601
+ n_octave=self.sift_n_octave,
602
+ n_scale_per_octave=self.sift_n_scale_per_octave,
603
+ peak_threshold=self.sift_peak_threshold,
604
+ edge_threshold=self.sift_edge_threshold,
605
+ magnification=self.sift_magnification,
606
+ window_size=self.sift_window_size,
607
+ backmatching=self.sift_back_matching,
608
+ disp_lower_bound=disp_lower_bound,
609
+ disp_upper_bound=disp_upper_bound,
610
+ saving_info_left=full_saving_info_left,
611
+ classif_bands_to_mask=classif_bands_to_mask,
612
+ )
613
+
614
+ else:
615
+ logging.error(
616
+ "SparseMatching application doesn't "
617
+ "support this input data format"
618
+ )
619
+
620
+ return epipolar_disparity_map_left, None
621
+
622
+
623
+ def compute_matches_wrapper( # pylint: disable=too-many-positional-arguments
624
+ left_image_object: xr.Dataset,
625
+ right_image_object: xr.Dataset,
626
+ used_band="b0",
627
+ matching_threshold=None,
628
+ n_octave=None,
629
+ n_scale_per_octave=None,
630
+ peak_threshold=None,
631
+ edge_threshold=None,
632
+ magnification=None,
633
+ window_size=None,
634
+ backmatching=None,
635
+ disp_lower_bound=None,
636
+ disp_upper_bound=None,
637
+ saving_info_left=None,
638
+ classif_bands_to_mask=None,
639
+ ) -> Dict[str, Tuple[xr.Dataset, xr.Dataset]]:
640
+ """
641
+ Compute matches from image objects.
642
+ This function will be run as a delayed task.
643
+
644
+ User must provide saving infos to save properly created datasets
645
+
646
+ :param left_image_object: tiled Left image dataset with :
647
+
648
+ - cst.EPI_IMAGE
649
+ - cst.EPI_MSK (if given)
650
+ - cst.EPI_TEXTURE (for left, if given)
651
+ :type left_image_object: xr.Dataset with :
652
+
653
+ - cst.EPI_IMAGE
654
+ - cst.EPI_MSK (if given)
655
+ - cst.EPI_TEXTURE (for left, if given)
656
+ :param right_image_object: tiled Right image
657
+ :type right_image_object: xr.Dataset
658
+ :param classif_bands_to_mask: bands from classif to mask
659
+ :type classif_bands_to_mask: list of str / int
660
+
661
+
662
+ :return: Left matches object, Right matches object (if exists)
663
+
664
+ Returned objects are composed of :
665
+
666
+ - dataframe (None for right object) with :
667
+ - TODO
668
+ """
669
+
670
+ # Create mask
671
+ # TODO : remove overwriting of EPI_MSK
672
+ saved_left_mask = np.copy(left_image_object[cst.EPI_MSK].values)
673
+ saved_right_mask = np.copy(right_image_object[cst.EPI_MSK].values)
674
+
675
+ # Compute matches
676
+ matches = sparse_matching_algo.dataset_matching(
677
+ left_image_object,
678
+ right_image_object,
679
+ used_band,
680
+ matching_threshold=matching_threshold,
681
+ n_octave=n_octave,
682
+ n_scale_per_octave=n_scale_per_octave,
683
+ peak_threshold=peak_threshold,
684
+ edge_threshold=edge_threshold,
685
+ magnification=magnification,
686
+ window_size=window_size,
687
+ backmatching=backmatching,
688
+ disp_lower_bound=disp_lower_bound,
689
+ disp_upper_bound=disp_upper_bound,
690
+ classif_bands_to_mask=classif_bands_to_mask,
691
+ )
692
+
693
+ # Filter matches outside disparity range
694
+ if disp_lower_bound is not None and disp_upper_bound is not None:
695
+ filtered_nb_matches = matches.shape[0]
696
+
697
+ matches = matches[matches[:, 2] - matches[:, 0] >= disp_lower_bound]
698
+ matches = matches[matches[:, 2] - matches[:, 0] <= disp_upper_bound]
699
+
700
+ logging.debug(
701
+ "{} matches discarded because they fall outside of disparity range "
702
+ "defined by --elevation_delta_lower_bound and "
703
+ "--elevation_delta_upper_bound: [{} pix., {} pix.]".format(
704
+ filtered_nb_matches - matches.shape[0],
705
+ disp_lower_bound,
706
+ disp_upper_bound,
707
+ )
708
+ )
709
+ else:
710
+ logging.debug("Matches outside disparity range were not filtered")
711
+
712
+ # convert to Dataframe
713
+ left_matches_dataframe = pandas.DataFrame(matches)
714
+
715
+ # recover initial mask data in input images
716
+ # TODO remove with proper dataset creation
717
+ left_image_object[cst.EPI_MSK].values = saved_left_mask
718
+ right_image_object[cst.EPI_MSK].values = saved_right_mask
719
+
720
+ cars_dataset.fill_dataframe(
721
+ left_matches_dataframe, saving_info=saving_info_left, attributes=None
722
+ )
723
+
724
+ return left_matches_dataframe