cars 1.0.0rc1__cp313-cp313-musllinux_1_2_i686.whl

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

Potentially problematic release.


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

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