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,1461 @@
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
+ # pylint: disable=too-many-lines
25
+ import collections
26
+
27
+ # Standard imports
28
+ import copy
29
+ import logging
30
+ import math
31
+ import os
32
+ from typing import Dict, Tuple
33
+
34
+ # Third party imports
35
+ import numpy as np
36
+ import pandora
37
+ import xarray as xr
38
+ from json_checker import And, Checker, Or
39
+ from pandora.check_configuration import check_pipeline_section
40
+ from pandora.img_tools import add_global_disparity
41
+ from pandora.state_machine import PandoraMachine
42
+ from rasterio.profiles import DefaultGTiffProfile
43
+ from scipy.ndimage import maximum_filter, minimum_filter
44
+
45
+ import cars.applications.dense_matching.dense_matching_constants as dm_cst
46
+ import cars.orchestrator.orchestrator as ocht
47
+ from cars.applications import application_constants
48
+ from cars.applications.dense_matching import dense_matching_algo as dm_algo
49
+ from cars.applications.dense_matching import (
50
+ dense_matching_wrappers as dm_wrappers,
51
+ )
52
+ from cars.applications.dense_matching.abstract_dense_matching_app import (
53
+ DenseMatching,
54
+ )
55
+ from cars.applications.dense_matching.disparity_grid_algo import (
56
+ generate_disp_range_const_tile_wrapper,
57
+ generate_disp_range_from_dem_wrapper,
58
+ )
59
+ from cars.applications.dense_matching.loaders.pandora_loader import (
60
+ PandoraLoader,
61
+ )
62
+
63
+ # CARS imports
64
+ from cars.core import constants as cst
65
+ from cars.core import constants_disparity as cst_disp
66
+ from cars.core import inputs, tiling
67
+ from cars.core.utils import safe_makedirs
68
+ from cars.data_structures import cars_dataset, format_transformation
69
+ from cars.data_structures.cars_dict import CarsDict
70
+ from cars.orchestrator.cluster.log_wrapper import cars_profile
71
+
72
+
73
+ class CensusMccnnSgm(
74
+ DenseMatching,
75
+ short_name=[
76
+ "census_sgm_default",
77
+ "census_sgm_urban",
78
+ "census_sgm_shadow",
79
+ "census_sgm_mountain_and_vegetation",
80
+ "census_sgm_homogeneous",
81
+ "mccnn_sgm",
82
+ "auto",
83
+ "custom",
84
+ ],
85
+ ): # pylint: disable=R0903,disable=R0902,disable=C0302
86
+ """
87
+ Census SGM & MCCNN SGM matching class
88
+ """
89
+
90
+ def __init__(self, conf=None):
91
+ """
92
+ Init function of DenseMatching
93
+
94
+ :param conf: configuration for matching
95
+ :return: an application_to_use object
96
+ """
97
+
98
+ super().__init__(conf=conf)
99
+
100
+ # check conf
101
+ self.used_method = self.used_config["method"]
102
+ self.min_epi_tile_size = self.used_config["min_epi_tile_size"]
103
+ self.max_epi_tile_size = self.used_config["max_epi_tile_size"]
104
+ self.epipolar_tile_margin_in_percent = self.used_config[
105
+ "epipolar_tile_margin_in_percent"
106
+ ]
107
+ self.min_elevation_offset = self.used_config["min_elevation_offset"]
108
+ self.max_elevation_offset = self.used_config["max_elevation_offset"]
109
+
110
+ # Disparity threshold
111
+ self.disp_min_threshold = self.used_config["disp_min_threshold"]
112
+ self.disp_max_threshold = self.used_config["disp_max_threshold"]
113
+
114
+ # Ambiguity
115
+ self.generate_ambiguity = self.used_config["generate_ambiguity"]
116
+
117
+ self.classification_fusion_margin = self.used_config[
118
+ "classification_fusion_margin"
119
+ ]
120
+
121
+ # Margins computation parameters
122
+ # Use local disp
123
+ self.use_global_disp_range = self.used_config["use_global_disp_range"]
124
+ self.local_disp_grid_step = self.used_config["local_disp_grid_step"]
125
+ self.disp_range_propagation_filter_size = self.used_config[
126
+ "disp_range_propagation_filter_size"
127
+ ]
128
+ self.epi_disp_grid_tile_size = self.used_config[
129
+ "epi_disp_grid_tile_size"
130
+ ]
131
+ self.use_cross_validation = self.used_config["use_cross_validation"]
132
+ self.denoise_disparity_map = self.used_config["denoise_disparity_map"]
133
+ self.required_bands = self.used_config["required_bands"]
134
+ self.used_band = self.used_config["used_band"]
135
+
136
+ # Saving files
137
+ self.save_intermediate_data = self.used_config["save_intermediate_data"]
138
+
139
+ self.confidence_filtering = self.used_config["confidence_filtering"]
140
+ self.threshold_disp_range_to_borders = self.used_config[
141
+ "threshold_disp_range_to_borders"
142
+ ]
143
+ self.filter_incomplete_disparity_range = self.used_config[
144
+ "filter_incomplete_disparity_range"
145
+ ]
146
+
147
+ # init orchestrator
148
+ self.orchestrator = None
149
+
150
+ def get_performance_map_parameters(self):
151
+ """
152
+ Get parameter linked to performance, that will be used in triangulation
153
+
154
+ :return: parameters to use
155
+ :type: dict
156
+ """
157
+
158
+ return {
159
+ "performance_map_method": self.used_config[
160
+ "performance_map_method"
161
+ ],
162
+ "perf_ambiguity_threshold": self.used_config[
163
+ "perf_ambiguity_threshold"
164
+ ],
165
+ }
166
+
167
+ def check_conf(self, conf):
168
+ """
169
+ Check configuration
170
+
171
+ :param conf: configuration to check
172
+ :type conf: dict
173
+ :return: overloaded configuration
174
+ :rtype: dict
175
+
176
+ """
177
+ # init conf
178
+ if conf is not None:
179
+ overloaded_conf = conf.copy()
180
+ else:
181
+ conf = {}
182
+ overloaded_conf = {}
183
+
184
+ # Overload conf
185
+ # check loader
186
+ loader_conf = conf.get("loader_conf", None)
187
+ default_method = "auto"
188
+ if loader_conf is not None:
189
+ default_method = "custom"
190
+ loader = conf.get("loader", "pandora")
191
+ overloaded_conf["method"] = conf.get(
192
+ "method", default_method
193
+ ) # change it if census_sgm is not default
194
+ if overloaded_conf["method"] == "auto" and loader_conf is not None:
195
+ raise RuntimeError(
196
+ "It's not possible to use auto method with custom "
197
+ "loader configuration"
198
+ )
199
+
200
+ # method called in abstract_dense_matching_app.py
201
+ overloaded_conf["min_epi_tile_size"] = conf.get(
202
+ "min_epi_tile_size", 300
203
+ )
204
+ overloaded_conf["max_epi_tile_size"] = conf.get(
205
+ "max_epi_tile_size", 1500
206
+ )
207
+ overloaded_conf["epipolar_tile_margin_in_percent"] = conf.get(
208
+ "epipolar_tile_margin_in_percent", 60
209
+ )
210
+
211
+ overloaded_conf["classification_fusion_margin"] = conf.get(
212
+ "classification_fusion_margin", -1
213
+ )
214
+ overloaded_conf["min_elevation_offset"] = conf.get(
215
+ "min_elevation_offset", None
216
+ )
217
+ overloaded_conf["max_elevation_offset"] = conf.get(
218
+ "max_elevation_offset", None
219
+ )
220
+ overloaded_conf["denoise_disparity_map"] = conf.get(
221
+ "denoise_disparity_map", False
222
+ )
223
+
224
+ # confidence filtering parameters
225
+ overloaded_conf["confidence_filtering"] = conf.get(
226
+ "confidence_filtering", {}
227
+ )
228
+
229
+ # Disparity threshold
230
+ overloaded_conf["disp_min_threshold"] = conf.get(
231
+ "disp_min_threshold", None
232
+ )
233
+ overloaded_conf["disp_max_threshold"] = conf.get(
234
+ "disp_max_threshold", None
235
+ )
236
+
237
+ overloaded_conf["perf_eta_max_ambiguity"] = conf.get(
238
+ "perf_eta_max_ambiguity", 0.99
239
+ )
240
+ overloaded_conf["perf_eta_max_risk"] = conf.get(
241
+ "perf_eta_max_risk", 0.25
242
+ )
243
+ overloaded_conf["perf_eta_step"] = conf.get("perf_eta_step", 0.04)
244
+ overloaded_conf["perf_ambiguity_threshold"] = conf.get(
245
+ "perf_ambiguity_threshold", 0.6
246
+ )
247
+ overloaded_conf["use_cross_validation"] = conf.get(
248
+ "use_cross_validation", True
249
+ )
250
+ # Margins computation parameters
251
+ overloaded_conf["use_global_disp_range"] = conf.get(
252
+ "use_global_disp_range", False
253
+ )
254
+ overloaded_conf["local_disp_grid_step"] = conf.get(
255
+ "local_disp_grid_step", 10
256
+ )
257
+ overloaded_conf["disp_range_propagation_filter_size"] = conf.get(
258
+ "disp_range_propagation_filter_size", 50
259
+ )
260
+ overloaded_conf["epi_disp_grid_tile_size"] = conf.get(
261
+ "epi_disp_grid_tile_size", 800
262
+ )
263
+ overloaded_conf["required_bands"] = conf.get("required_bands", ["b0"])
264
+ overloaded_conf["used_band"] = conf.get("used_band", "b0")
265
+ overloaded_conf["threshold_disp_range_to_borders"] = conf.get(
266
+ "threshold_disp_range_to_borders", False
267
+ )
268
+ overloaded_conf["filter_incomplete_disparity_range"] = conf.get(
269
+ "filter_incomplete_disparity_range", True
270
+ )
271
+
272
+ # Saving files
273
+ overloaded_conf["save_intermediate_data"] = conf.get(
274
+ "save_intermediate_data", False
275
+ )
276
+
277
+ # Permormance map parameters
278
+ overloaded_conf["generate_ambiguity"] = conf.get(
279
+ "generate_ambiguity",
280
+ overloaded_conf["save_intermediate_data"],
281
+ )
282
+
283
+ # Permormance map parameters
284
+ default_perf_map_method = None
285
+ if overloaded_conf["save_intermediate_data"]:
286
+ default_perf_map_method = "risk"
287
+ overloaded_conf["performance_map_method"] = conf.get(
288
+ "performance_map_method",
289
+ default_perf_map_method,
290
+ )
291
+
292
+ # Get performance map method
293
+ perf_map_method = overloaded_conf["performance_map_method"]
294
+ if isinstance(overloaded_conf["performance_map_method"], str):
295
+ perf_map_method = [perf_map_method]
296
+ elif perf_map_method is None:
297
+ perf_map_method = []
298
+
299
+ overloaded_conf["performance_map_method"] = perf_map_method
300
+
301
+ if overloaded_conf["use_cross_validation"] is True:
302
+ overloaded_conf["use_cross_validation"] = "fast"
303
+
304
+ # TODO modify, use loader directly
305
+ logger = logging.getLogger("transitions.core")
306
+ logger.addFilter(
307
+ lambda record: "to model due to model override policy"
308
+ not in record.getMessage()
309
+ )
310
+ pandora_loader = PandoraLoader(
311
+ conf=loader_conf,
312
+ method_name=overloaded_conf["method"],
313
+ generate_performance_map_from_risk="risk" in perf_map_method,
314
+ generate_performance_map_from_intervals="intervals"
315
+ in perf_map_method,
316
+ generate_ambiguity=overloaded_conf["generate_ambiguity"],
317
+ perf_eta_max_ambiguity=overloaded_conf["perf_eta_max_ambiguity"],
318
+ perf_eta_max_risk=overloaded_conf["perf_eta_max_risk"],
319
+ perf_eta_step=overloaded_conf["perf_eta_step"],
320
+ use_cross_validation=overloaded_conf["use_cross_validation"],
321
+ denoise_disparity_map=overloaded_conf["denoise_disparity_map"],
322
+ used_band=overloaded_conf["used_band"],
323
+ )
324
+
325
+ overloaded_conf["loader"] = loader
326
+
327
+ # Get params from loader
328
+ self.loader = pandora_loader
329
+ self.corr_config = collections.OrderedDict(pandora_loader.get_conf())
330
+ # Instantiate margins from pandora check conf
331
+ # create the dataset
332
+ classif_bands = pandora_loader.get_classif_bands()
333
+ fake_dataset = xr.Dataset(
334
+ data_vars={
335
+ "image": (["row", "col"], np.zeros((10, 10))),
336
+ "classif": (
337
+ ["row", "col", "band_classif"],
338
+ np.zeros((10, 10, len(classif_bands)), dtype=np.int32),
339
+ ),
340
+ },
341
+ coords={
342
+ "band_im": [overloaded_conf["used_band"]],
343
+ "band_classif": classif_bands,
344
+ "row": np.arange(10),
345
+ "col": np.arange(10),
346
+ },
347
+ attrs={"disparity_source": [-1, 1]},
348
+ )
349
+ # Import plugins before checking configuration
350
+ pandora.import_plugin()
351
+ pandora_machine = PandoraMachine()
352
+ corr_config_pipeline = {"pipeline": dict(self.corr_config["pipeline"])}
353
+ saved_schema = copy.deepcopy(
354
+ pandora.matching_cost.matching_cost.AbstractMatchingCost.schema
355
+ )
356
+ _ = check_pipeline_section(
357
+ corr_config_pipeline, fake_dataset, fake_dataset, pandora_machine
358
+ )
359
+ # quick fix to remove when the problem is solved in pandora
360
+ pandora.matching_cost.matching_cost.AbstractMatchingCost.schema = (
361
+ saved_schema
362
+ )
363
+ self.margins = pandora_machine.margins.global_margins
364
+
365
+ if overloaded_conf["method"] != "auto":
366
+ # not final configuration
367
+ overloaded_conf["loader_conf"] = self.corr_config
368
+ else:
369
+ overloaded_conf["loader_conf"] = None
370
+
371
+ application_schema = {
372
+ "method": str,
373
+ "min_epi_tile_size": And(int, lambda x: x > 0),
374
+ "max_epi_tile_size": And(int, lambda x: x > 0),
375
+ "epipolar_tile_margin_in_percent": int,
376
+ "min_elevation_offset": Or(None, int),
377
+ "max_elevation_offset": Or(None, int),
378
+ "disp_min_threshold": Or(None, int),
379
+ "disp_max_threshold": Or(None, int),
380
+ "save_intermediate_data": bool,
381
+ "generate_ambiguity": bool,
382
+ "performance_map_method": And(
383
+ list,
384
+ lambda x: all(y in ["risk", "intervals"] for y in x),
385
+ ),
386
+ "perf_eta_max_ambiguity": float,
387
+ "perf_eta_max_risk": float,
388
+ "perf_eta_step": float,
389
+ "classification_fusion_margin": int,
390
+ "perf_ambiguity_threshold": float,
391
+ "use_cross_validation": Or(bool, str),
392
+ "denoise_disparity_map": bool,
393
+ "use_global_disp_range": bool,
394
+ "local_disp_grid_step": int,
395
+ "disp_range_propagation_filter_size": And(
396
+ Or(int, float), lambda x: x >= 0
397
+ ),
398
+ "epi_disp_grid_tile_size": int,
399
+ "required_bands": [str],
400
+ "used_band": str,
401
+ "loader_conf": Or(dict, collections.OrderedDict, str, None),
402
+ "loader": str,
403
+ "confidence_filtering": dict,
404
+ "threshold_disp_range_to_borders": bool,
405
+ "filter_incomplete_disparity_range": bool,
406
+ }
407
+
408
+ # Check conf
409
+ checker = Checker(application_schema)
410
+ checker.validate(overloaded_conf)
411
+
412
+ self.check_conf_confidence_filtering(overloaded_conf)
413
+
414
+ # Check consistency between bounds for optimal tile size search
415
+ min_epi_tile_size = overloaded_conf["min_epi_tile_size"]
416
+ max_epi_tile_size = overloaded_conf["max_epi_tile_size"]
417
+ if min_epi_tile_size > max_epi_tile_size:
418
+ raise ValueError(
419
+ "Maximal tile size should be bigger than "
420
+ "minimal tile size for optimal tile size search"
421
+ )
422
+
423
+ # Check consistency between bounds for elevation offset
424
+ min_elevation_offset = overloaded_conf["min_elevation_offset"]
425
+ max_elevation_offset = overloaded_conf["max_elevation_offset"]
426
+ if (
427
+ min_elevation_offset is not None
428
+ and max_elevation_offset is not None
429
+ and min_elevation_offset > max_elevation_offset
430
+ ):
431
+ raise ValueError(
432
+ "Maximal elevation should be bigger than "
433
+ "minimal elevation for dense matching"
434
+ )
435
+
436
+ disp_min_threshold = overloaded_conf["disp_min_threshold"]
437
+ disp_max_threshold = overloaded_conf["disp_max_threshold"]
438
+ if (
439
+ disp_min_threshold is not None
440
+ and disp_max_threshold is not None
441
+ and disp_min_threshold > disp_max_threshold
442
+ ):
443
+ raise ValueError(
444
+ "Maximal disparity should be bigger than "
445
+ "minimal disparity for dense matching"
446
+ )
447
+ return overloaded_conf
448
+
449
+ def check_conf_confidence_filtering(self, overloaded_conf):
450
+ """
451
+ Check the confidence filtering conf
452
+ """
453
+ overloaded_conf["confidence_filtering"]["activated"] = overloaded_conf[
454
+ "confidence_filtering"
455
+ ].get("activated", True)
456
+ overloaded_conf["confidence_filtering"]["bounds_ratio_threshold"] = (
457
+ overloaded_conf["confidence_filtering"].get(
458
+ "bounds_ratio_threshold", 0.2
459
+ )
460
+ )
461
+ overloaded_conf["confidence_filtering"]["risk_ratio_threshold"] = (
462
+ overloaded_conf["confidence_filtering"].get(
463
+ "risk_ratio_threshold", 0.75
464
+ )
465
+ )
466
+ overloaded_conf["confidence_filtering"]["bounds_range_threshold"] = (
467
+ overloaded_conf["confidence_filtering"].get(
468
+ "bounds_range_threshold", 3
469
+ )
470
+ )
471
+ overloaded_conf["confidence_filtering"]["risk_range_threshold"] = (
472
+ overloaded_conf["confidence_filtering"].get(
473
+ "risk_range_threshold", 9
474
+ )
475
+ )
476
+ overloaded_conf["confidence_filtering"]["nan_threshold"] = (
477
+ overloaded_conf["confidence_filtering"].get("nan_threshold", 0.2)
478
+ )
479
+ overloaded_conf["confidence_filtering"]["win_nanratio"] = (
480
+ overloaded_conf["confidence_filtering"].get("win_nanratio", 20)
481
+ )
482
+
483
+ confidence_filtering_schema = {
484
+ "activated": bool,
485
+ "bounds_ratio_threshold": float,
486
+ "risk_ratio_threshold": float,
487
+ "bounds_range_threshold": int,
488
+ "risk_range_threshold": int,
489
+ "nan_threshold": float,
490
+ "win_nanratio": int,
491
+ }
492
+
493
+ checker_confidence_filtering_schema = Checker(
494
+ confidence_filtering_schema
495
+ )
496
+ checker_confidence_filtering_schema.validate(
497
+ overloaded_conf["confidence_filtering"]
498
+ )
499
+
500
+ @cars_profile(name="Get margin fun")
501
+ def get_margins_fun(self, grid_left, disp_range_grid):
502
+ """
503
+ Get Margins function that generates margins needed by
504
+ matching method, to use during resampling
505
+
506
+ :param grid_left: left epipolar grid
507
+ :type grid_left: dict
508
+ :param disp_range_grid: minimum and maximum disparity grid
509
+ :return: function that generates margin for given roi
510
+
511
+ """
512
+
513
+ disp_min_grid_arr, _ = inputs.rasterio_read_as_array(
514
+ disp_range_grid["grid_min_path"]
515
+ )
516
+ disp_max_grid_arr, _ = inputs.rasterio_read_as_array(
517
+ disp_range_grid["grid_max_path"]
518
+ )
519
+ step_row = disp_range_grid["step_row"]
520
+ step_col = disp_range_grid["step_col"]
521
+ row_range = disp_range_grid["row_range"]
522
+ col_range = disp_range_grid["col_range"]
523
+
524
+ # get disp_to_alt_ratio
525
+ disp_to_alt_ratio = grid_left["disp_to_alt_ratio"]
526
+
527
+ # Check if we need to override disp_min
528
+ if self.min_elevation_offset is not None:
529
+ user_disp_min = self.min_elevation_offset / disp_to_alt_ratio
530
+ if np.any(disp_min_grid_arr < user_disp_min):
531
+ logging.warning(
532
+ (
533
+ "Overridden disparity minimum "
534
+ "= {:.3f} pix. (= {:.3f} m.) "
535
+ "is greater than disparity minimum estimated "
536
+ "in prepare step "
537
+ "for current pair"
538
+ ).format(
539
+ user_disp_min,
540
+ self.min_elevation_offset,
541
+ )
542
+ )
543
+ disp_min_grid_arr[:, :] = user_disp_min
544
+
545
+ # Check if we need to override disp_max
546
+ if self.max_elevation_offset is not None:
547
+ user_disp_max = self.max_elevation_offset / disp_to_alt_ratio
548
+ if np.any(disp_max_grid_arr > user_disp_max):
549
+ logging.warning(
550
+ (
551
+ "Overridden disparity maximum "
552
+ "= {:.3f} pix. (or {:.3f} m.) "
553
+ "is lower than disparity maximum estimated "
554
+ "in prepare step "
555
+ "for current pair"
556
+ ).format(
557
+ user_disp_max,
558
+ self.max_elevation_offset,
559
+ )
560
+ )
561
+ disp_max_grid_arr[:, :] = user_disp_max
562
+
563
+ # Compute global range of logging
564
+ disp_min_global = np.min(disp_min_grid_arr)
565
+ disp_max_global = np.max(disp_max_grid_arr)
566
+
567
+ logging.info(
568
+ "Global Disparity range for current pair: "
569
+ "[{:.3f} pix., {:.3f} pix.] "
570
+ "(or [{:.3f} m., {:.3f} m.])".format(
571
+ disp_min_global,
572
+ disp_max_global,
573
+ disp_min_global * disp_to_alt_ratio,
574
+ disp_max_global * disp_to_alt_ratio,
575
+ )
576
+ )
577
+
578
+ def margins_wrapper(row_min, row_max, col_min, col_max):
579
+ """
580
+ Generates margins Dataset used in resampling
581
+
582
+ :param row_min: row min
583
+ :param row_max: row max
584
+ :param col_min: col min
585
+ :param col_max: col max
586
+
587
+ :return: margins
588
+ :rtype: xr.Dataset
589
+ """
590
+
591
+ assert row_min < row_max
592
+ assert col_min < col_max
593
+
594
+ # Get region in grid
595
+
596
+ grid_row_min = max(0, int(np.floor((row_min - 1) / step_row)) - 1)
597
+ grid_row_max = min(
598
+ len(row_range), int(np.ceil((row_max + 1) / step_row) + 1)
599
+ )
600
+ grid_col_min = max(0, int(np.floor((col_min - 1) / step_col)) - 1)
601
+ grid_col_max = min(
602
+ len(col_range), int(np.ceil((col_max + 1) / step_col)) + 1
603
+ )
604
+
605
+ # Compute disp min and max in row
606
+ disp_min = np.min(
607
+ disp_min_grid_arr[
608
+ grid_row_min:grid_row_max, grid_col_min:grid_col_max
609
+ ]
610
+ )
611
+ disp_max = np.max(
612
+ disp_max_grid_arr[
613
+ grid_row_min:grid_row_max, grid_col_min:grid_col_max
614
+ ]
615
+ )
616
+ # round disp min and max
617
+ disp_min = int(math.floor(disp_min))
618
+ disp_max = int(math.ceil(disp_max))
619
+
620
+ # Compute margins for the correlator
621
+ margins = dm_wrappers.get_margins(self.margins, disp_min, disp_max)
622
+
623
+ return margins
624
+
625
+ return margins_wrapper
626
+
627
+ def get_method(self):
628
+ """
629
+ return the method that will be used in dense_matching
630
+ """
631
+
632
+ return self.used_method
633
+
634
+ @cars_profile(name="Optimal size estimation")
635
+ def get_optimal_tile_size(self, disp_range_grid, max_ram_per_worker):
636
+ """
637
+ Get the optimal tile size to use during dense matching.
638
+
639
+ :param disp_range_grid: minimum and maximum disparity grid
640
+ :param max_ram_per_worker: maximum ram per worker
641
+ :return: optimal tile size
642
+
643
+ """
644
+
645
+ disp_min_grids, _ = inputs.rasterio_read_as_array(
646
+ disp_range_grid["grid_min_path"]
647
+ )
648
+ disp_max_grids, _ = inputs.rasterio_read_as_array(
649
+ disp_range_grid["grid_max_path"]
650
+ )
651
+
652
+ # use max tile size as overlap for min and max:
653
+ # max Point to point diff is less than diff of tile
654
+
655
+ # use filter of size max_epi_tile_size
656
+ overlap = 3 * int(self.max_epi_tile_size / self.local_disp_grid_step)
657
+
658
+ disp_min_grids = minimum_filter(
659
+ disp_min_grids, size=[overlap, overlap], mode="nearest"
660
+ )
661
+ disp_max_grids = maximum_filter(
662
+ disp_max_grids, size=[overlap, overlap], mode="nearest"
663
+ )
664
+
665
+ # Worst cases scenario:
666
+ # 1: [global max - max diff, global max]
667
+ # 2: [global min, global min max diff]
668
+
669
+ max_diff = np.round(np.nanmax(disp_max_grids - disp_min_grids)) + 1
670
+ global_min = np.floor(np.nanmin(disp_min_grids))
671
+ global_max = np.ceil(np.nanmax(disp_max_grids))
672
+
673
+ # Get tiling param
674
+ opt_epipolar_tile_size_1 = (
675
+ dm_wrappers.optimal_tile_size_pandora_plugin_libsgm(
676
+ global_min,
677
+ global_min + max_diff,
678
+ self.min_epi_tile_size,
679
+ self.max_epi_tile_size,
680
+ max_ram_per_worker,
681
+ margin=self.epipolar_tile_margin_in_percent,
682
+ )
683
+ )
684
+ opt_epipolar_tile_size_2 = (
685
+ dm_wrappers.optimal_tile_size_pandora_plugin_libsgm(
686
+ global_max - max_diff,
687
+ global_max,
688
+ self.min_epi_tile_size,
689
+ self.max_epi_tile_size,
690
+ max_ram_per_worker,
691
+ margin=self.epipolar_tile_margin_in_percent,
692
+ )
693
+ )
694
+
695
+ # return worst case
696
+ opt_epipolar_tile_size = min(
697
+ opt_epipolar_tile_size_1, opt_epipolar_tile_size_2
698
+ )
699
+
700
+ # Define function to compute local optimal size for each tile
701
+ def local_tile_optimal_size_fun(local_disp_min, local_disp_max):
702
+ """
703
+ Compute optimal tile size for tile
704
+
705
+ :return: local tile size, global optimal tile sizes
706
+
707
+ """
708
+ local_opt_tile_size = (
709
+ dm_wrappers.optimal_tile_size_pandora_plugin_libsgm(
710
+ local_disp_min,
711
+ local_disp_max,
712
+ 0,
713
+ 20000, # arbitrary
714
+ max_ram_per_worker,
715
+ margin=self.epipolar_tile_margin_in_percent,
716
+ )
717
+ )
718
+
719
+ # Get max range to use with current optimal size
720
+ max_range = dm_wrappers.get_max_disp_from_opt_tile_size(
721
+ opt_epipolar_tile_size,
722
+ max_ram_per_worker,
723
+ margin=self.epipolar_tile_margin_in_percent,
724
+ used_disparity_range=(local_disp_max - local_disp_min),
725
+ )
726
+
727
+ return local_opt_tile_size, opt_epipolar_tile_size, max_range
728
+
729
+ return opt_epipolar_tile_size, local_tile_optimal_size_fun
730
+
731
+ def get_required_bands(self):
732
+ """
733
+ Get bands required by this application
734
+
735
+ :return: required bands for left and right image
736
+ :rtype: dict
737
+ """
738
+ required_bands = {}
739
+ required_bands["left"] = self.required_bands
740
+ required_bands["right"] = self.required_bands
741
+ return required_bands
742
+
743
+ # pylint: disable=too-many-positional-arguments
744
+ @cars_profile(name="Disp Grid Generation")
745
+ def generate_disparity_grids( # noqa: C901
746
+ self,
747
+ sensor_image_right,
748
+ grid_right,
749
+ geom_plugin_with_dem_and_geoid,
750
+ dmin=None,
751
+ dmax=None,
752
+ dem_min=None,
753
+ dem_max=None,
754
+ pair_folder=None,
755
+ orchestrator=None,
756
+ ):
757
+ """
758
+ Generate disparity grids min and max, with given step
759
+
760
+ global mode: uses dmin and dmax
761
+ local mode: uses dems
762
+
763
+
764
+ :param sensor_image_right: sensor image right
765
+ :type sensor_image_right: dict
766
+ :param grid_right: right epipolar grid
767
+ :type grid_right: dict
768
+ :param geom_plugin_with_dem_and_geoid: geometry plugin with dem mean
769
+ used to generate epipolar grids
770
+ :type geom_plugin_with_dem_and_geoid: GeometryPlugin
771
+ :param dmin: minimum disparity
772
+ :type dmin: float
773
+ :param dmax: maximum disparity
774
+ :type dmax: float
775
+ :param dem_min: path to minimum dem
776
+ :type dem_min: str
777
+ :param dem_max: path to maximum dem
778
+ :type dem_max: str
779
+ :param pair_folder: folder used for current pair
780
+ :type pair_folder: str
781
+ :param orchestrator: orchestrator
782
+ :type orchestrator: Orchestrator
783
+
784
+
785
+ :return disparity grid range, containing grid min and max
786
+ :rtype: CarsDataset
787
+ """
788
+
789
+ # Default orchestrator
790
+ if orchestrator is None:
791
+ # Create default sequential orchestrator for current application
792
+ # be aware, no out_json will be shared between orchestrators
793
+ # No files saved
794
+ self.orchestrator = ocht.Orchestrator(
795
+ orchestrator_conf={"mode": "sequential"}
796
+ )
797
+ else:
798
+ self.orchestrator = orchestrator
799
+
800
+ epi_size_row = grid_right["epipolar_size_y"]
801
+ epi_size_col = grid_right["epipolar_size_x"]
802
+ disp_to_alt_ratio = grid_right["disp_to_alt_ratio"]
803
+
804
+ # Generate grid array
805
+ nb_rows = int(epi_size_row / self.local_disp_grid_step) + 1
806
+ nb_cols = int(epi_size_col / self.local_disp_grid_step) + 1
807
+ row_range, step_row = np.linspace(
808
+ 0, epi_size_row, nb_rows, retstep=True
809
+ )
810
+ col_range, step_col = np.linspace(
811
+ 0, epi_size_col, nb_cols, retstep=True
812
+ )
813
+
814
+ # Create CarsDataset
815
+ grid_disp_range = cars_dataset.CarsDataset(
816
+ "arrays", name="grid_disp_range_unknown_pair"
817
+ )
818
+ global_infos_cars_ds = cars_dataset.CarsDataset("dict")
819
+
820
+ # Generate profile
821
+ raster_profile = DefaultGTiffProfile(count=1)
822
+
823
+ # saving infos
824
+ # disp grids
825
+
826
+ if self.save_intermediate_data:
827
+ grid_min_path = os.path.join(pair_folder, "disp_min_grid.tif")
828
+ grid_max_path = os.path.join(pair_folder, "disp_max_grid.tif")
829
+ safe_makedirs(pair_folder)
830
+ else:
831
+ if pair_folder is None:
832
+ tmp_folder = os.path.join(self.orchestrator.out_dir, "tmp")
833
+ else:
834
+ tmp_folder = os.path.join(pair_folder, "tmp")
835
+ safe_makedirs(tmp_folder)
836
+ self.orchestrator.add_to_clean(tmp_folder)
837
+ grid_min_path = os.path.join(tmp_folder, "disp_min_grid.tif")
838
+ grid_max_path = os.path.join(tmp_folder, "disp_max_grid.tif")
839
+
840
+ if None not in (dmin, dmax):
841
+ # use global disparity range
842
+ if None not in (dem_min, dem_max):
843
+ raise RuntimeError("Mix between local and global mode")
844
+
845
+ # Only one tile
846
+ grid_disp_range.tiling_grid = np.array([[[0, nb_rows, 0, nb_cols]]])
847
+
848
+ elif None not in (dem_min, dem_max):
849
+
850
+ # Generate multiple tiles
851
+ grid_tile_size = self.epi_disp_grid_tile_size
852
+ grid_disp_range.tiling_grid = tiling.generate_tiling_grid(
853
+ 0,
854
+ 0,
855
+ nb_rows,
856
+ nb_cols,
857
+ grid_tile_size,
858
+ grid_tile_size,
859
+ )
860
+
861
+ # add tiling of global_infos_cars_ds
862
+ global_infos_cars_ds.tiling_grid = grid_disp_range.tiling_grid
863
+ self.orchestrator.add_to_replace_lists(
864
+ global_infos_cars_ds,
865
+ cars_ds_name="global infos",
866
+ )
867
+
868
+ self.orchestrator.add_to_save_lists(
869
+ grid_min_path,
870
+ dm_cst.DISP_MIN_GRID,
871
+ grid_disp_range,
872
+ dtype=np.float32,
873
+ nodata=0,
874
+ cars_ds_name="disp_min_grid",
875
+ )
876
+
877
+ self.orchestrator.add_to_save_lists(
878
+ grid_max_path,
879
+ dm_cst.DISP_MAX_GRID,
880
+ grid_disp_range,
881
+ dtype=np.float32,
882
+ nodata=0,
883
+ cars_ds_name="disp_max_grid",
884
+ )
885
+ [saving_info] = ( # pylint: disable=unbalanced-tuple-unpacking
886
+ self.orchestrator.get_saving_infos([grid_disp_range])
887
+ )
888
+ [saving_info_global_infos] = self.orchestrator.get_saving_infos(
889
+ [global_infos_cars_ds]
890
+ )
891
+
892
+ # Generate grids on dict format
893
+ grid_disp_range_dict = {
894
+ "grid_min_path": grid_min_path,
895
+ "grid_max_path": grid_max_path,
896
+ "global_min": None,
897
+ "global_max": None,
898
+ "step_row": step_row,
899
+ "step_col": step_col,
900
+ "row_range": row_range,
901
+ "col_range": col_range,
902
+ }
903
+
904
+ if None not in (dmin, dmax):
905
+ # use global disparity range
906
+ if None not in (dem_min, dem_max):
907
+ raise RuntimeError("Mix between local and global mode")
908
+
909
+ saving_info_global_infos_full = ocht.update_saving_infos(
910
+ saving_info_global_infos, row=0, col=0
911
+ )
912
+ saving_info_full = ocht.update_saving_infos(
913
+ saving_info, row=0, col=0
914
+ )
915
+
916
+ (
917
+ grid_disp_range[0, 0],
918
+ global_infos_cars_ds[0, 0],
919
+ ) = self.orchestrator.cluster.create_task(
920
+ generate_disp_range_const_tile_wrapper, nout=2
921
+ )(
922
+ row_range,
923
+ col_range,
924
+ dmin,
925
+ dmax,
926
+ raster_profile,
927
+ saving_info_full,
928
+ saving_info_global_infos_full,
929
+ )
930
+
931
+ elif None not in (dem_min, dem_max):
932
+
933
+ # use filter to propagate min and max
934
+ filter_overlap = (
935
+ 2
936
+ * int(
937
+ self.disp_range_propagation_filter_size
938
+ / self.local_disp_grid_step
939
+ )
940
+ + 1
941
+ )
942
+
943
+ for col in range(grid_disp_range.shape[1]):
944
+ for row in range(grid_disp_range.shape[0]):
945
+ # update saving infos for potential replacement
946
+ full_saving_info = ocht.update_saving_infos(
947
+ saving_info, row=row, col=col
948
+ )
949
+ saving_info_global_infos_full = ocht.update_saving_infos(
950
+ saving_info_global_infos, row=row, col=col
951
+ )
952
+ array_window = grid_disp_range.get_window_as_dict(row, col)
953
+ (
954
+ grid_disp_range[row, col],
955
+ global_infos_cars_ds[row, col],
956
+ ) = self.orchestrator.cluster.create_task(
957
+ generate_disp_range_from_dem_wrapper, nout=2
958
+ )(
959
+ array_window,
960
+ row_range,
961
+ col_range,
962
+ sensor_image_right,
963
+ grid_right,
964
+ geom_plugin_with_dem_and_geoid,
965
+ dem_min,
966
+ dem_max,
967
+ raster_profile,
968
+ full_saving_info,
969
+ saving_info_global_infos_full,
970
+ filter_overlap,
971
+ disp_to_alt_ratio,
972
+ disp_min_threshold=self.disp_min_threshold,
973
+ disp_max_threshold=self.disp_max_threshold,
974
+ )
975
+
976
+ # Compute grid
977
+ self.orchestrator.breakpoint()
978
+
979
+ # Fill global infos
980
+ mins, maxs = [], []
981
+ for row in range(global_infos_cars_ds.shape[0]):
982
+ for col in range(global_infos_cars_ds.shape[1]):
983
+ try:
984
+ dict_data = global_infos_cars_ds[row, col].data
985
+ mins.append(dict_data["global_min"])
986
+ maxs.append(dict_data["global_max"])
987
+ except Exception:
988
+ logging.info(
989
+ "Tile {} {} not computed in epi disp range"
990
+ " generation".format(row, col)
991
+ )
992
+ grid_disp_range_dict["global_min"] = np.floor(np.nanmin(mins))
993
+ grid_disp_range_dict["global_max"] = np.ceil(np.nanmax(maxs))
994
+
995
+ return grid_disp_range_dict
996
+
997
+ def run( # pylint: disable=too-many-positional-arguments
998
+ self,
999
+ epipolar_images_left,
1000
+ epipolar_images_right,
1001
+ local_tile_optimal_size_fun,
1002
+ orchestrator=None,
1003
+ pair_folder=None,
1004
+ pair_key="PAIR_0",
1005
+ disp_range_grid=None,
1006
+ compute_disparity_masks=False,
1007
+ margins_to_keep=0,
1008
+ texture_bands=None,
1009
+ classif_bands_to_mask=None,
1010
+ ):
1011
+ """
1012
+ Run Matching application.
1013
+
1014
+ Create CarsDataset filled with xarray.Dataset, corresponding
1015
+ to epipolar disparities, on the same geometry than
1016
+ epipolar_images_left.
1017
+
1018
+ :param epipolar_images_left: tiled left epipolar CarsDataset contains:
1019
+
1020
+ - N x M Delayed tiles. \
1021
+ Each tile will be a future xarray Dataset containing:
1022
+
1023
+ - data with keys : "im", "msk", "texture"
1024
+ - attrs with keys: "margins" with "disp_min" and "disp_max"\
1025
+ "transform", "crs", "valid_pixels", "no_data_mask",\
1026
+ "no_data_img"
1027
+ - attributes containing:
1028
+ "largest_epipolar_region","opt_epipolar_tile_size"
1029
+ :type epipolar_images_left: CarsDataset
1030
+ :param epipolar_images_right: tiled right epipolar CarsDataset contains:
1031
+
1032
+ - N x M Delayed tiles. \
1033
+ Each tile will be a future xarray Dataset containing:
1034
+
1035
+ - data with keys : "im", "msk", "texture"
1036
+ - attrs with keys: "margins" with "disp_min" and "disp_max"
1037
+ "transform", "crs", "valid_pixels", "no_data_mask",
1038
+ "no_data_img"
1039
+ - attributes containing:
1040
+ "largest_epipolar_region","opt_epipolar_tile_size"
1041
+ :type epipolar_images_right: CarsDataset
1042
+ :param local_tile_optimal_size_fun: function to compute local
1043
+ optimal tile size
1044
+ :type local_tile_optimal_size_fun: func
1045
+ :param orchestrator: orchestrator used
1046
+ :param pair_folder: folder used for current pair
1047
+ :type pair_folder: str
1048
+ :param pair_key: pair id
1049
+ :type pair_key: str
1050
+ :param disp_range_grid: minimum and maximum disparity grid
1051
+ :type disp_range_grid: CarsDataset
1052
+ :param margins_to_keep: margin to keep after dense matching
1053
+ :type margins_to_keep: int
1054
+ :param texture_bands: indices of bands from epipolar_images_left
1055
+ used for output texture
1056
+ :type texture_bands: list
1057
+ :param classif_bands_to_mask: bands from classif to mask
1058
+ :type classif_bands_to_mask: list of str / int
1059
+
1060
+ :return: disparity map: \
1061
+ The CarsDataset contains:
1062
+
1063
+ - N x M Delayed tiles.\
1064
+ Each tile will be a future xarray Dataset containing:
1065
+ - data with keys : "disp", "disp_msk"
1066
+ - attrs with keys: profile, window, overlaps
1067
+ - attributes containing:
1068
+ "largest_epipolar_region","opt_epipolar_tile_size",
1069
+ "disp_min_tiling", "disp_max_tiling"
1070
+
1071
+ :rtype: CarsDataset
1072
+ """
1073
+ # Default orchestrator
1074
+ if orchestrator is None:
1075
+ # Create default sequential orchestrator for current application
1076
+ # be awere, no out_json will be shared between orchestrators
1077
+ # No files saved
1078
+ self.orchestrator = ocht.Orchestrator(
1079
+ orchestrator_conf={"mode": "sequential"}
1080
+ )
1081
+ else:
1082
+ self.orchestrator = orchestrator
1083
+
1084
+ if pair_folder is None:
1085
+ pair_folder = os.path.join(self.orchestrator.out_dir, "tmp")
1086
+
1087
+ if epipolar_images_left.dataset_type == "arrays":
1088
+ # Create CarsDataset
1089
+ # Epipolar_disparity
1090
+ epipolar_disparity_map = cars_dataset.CarsDataset(
1091
+ "arrays", name="dense_matching_" + pair_key
1092
+ )
1093
+ epipolar_disparity_map.create_empty_copy(epipolar_images_left)
1094
+ # Modify overlaps
1095
+ epipolar_disparity_map.overlaps = (
1096
+ format_transformation.reduce_overlap(
1097
+ epipolar_disparity_map.overlaps, margins_to_keep
1098
+ )
1099
+ )
1100
+
1101
+ # Update attributes to get epipolar info
1102
+ epipolar_disparity_map.attributes.update(
1103
+ epipolar_images_left.attributes
1104
+ )
1105
+
1106
+ # Save disparity maps
1107
+ if self.save_intermediate_data:
1108
+ self.orchestrator.add_to_save_lists(
1109
+ os.path.join(pair_folder, "epi_disp.tif"),
1110
+ cst_disp.MAP,
1111
+ epipolar_disparity_map,
1112
+ cars_ds_name="epi_disp",
1113
+ nodata=-9999,
1114
+ )
1115
+
1116
+ self.orchestrator.add_to_save_lists(
1117
+ os.path.join(pair_folder, "epi_disp_image.tif"),
1118
+ cst.EPI_TEXTURE,
1119
+ epipolar_disparity_map,
1120
+ cars_ds_name="epi_disp_color",
1121
+ )
1122
+
1123
+ self.orchestrator.add_to_save_lists(
1124
+ os.path.join(pair_folder, "epi_disp_mask.tif"),
1125
+ cst_disp.VALID,
1126
+ epipolar_disparity_map,
1127
+ dtype=np.uint8,
1128
+ cars_ds_name="epi_disp_mask",
1129
+ optional_data=True,
1130
+ )
1131
+
1132
+ self.orchestrator.add_to_save_lists(
1133
+ os.path.join(pair_folder, "epi_disp_classif.tif"),
1134
+ cst.EPI_CLASSIFICATION,
1135
+ epipolar_disparity_map,
1136
+ dtype=np.uint8,
1137
+ cars_ds_name="epi_disp_classif",
1138
+ optional_data=True,
1139
+ )
1140
+
1141
+ self.orchestrator.add_to_save_lists(
1142
+ os.path.join(
1143
+ pair_folder,
1144
+ "epi_confidence.tif",
1145
+ ),
1146
+ cst_disp.CONFIDENCE,
1147
+ epipolar_disparity_map,
1148
+ cars_ds_name="confidence",
1149
+ optional_data=True,
1150
+ )
1151
+
1152
+ # disparity grids
1153
+ self.orchestrator.add_to_save_lists(
1154
+ os.path.join(
1155
+ pair_folder,
1156
+ "epi_disp_min.tif",
1157
+ ),
1158
+ cst_disp.EPI_DISP_MIN_GRID,
1159
+ epipolar_disparity_map,
1160
+ cars_ds_name="disp_min",
1161
+ )
1162
+ self.orchestrator.add_to_save_lists(
1163
+ os.path.join(
1164
+ pair_folder,
1165
+ "epi_disp_max.tif",
1166
+ ),
1167
+ cst_disp.EPI_DISP_MAX_GRID,
1168
+ epipolar_disparity_map,
1169
+ cars_ds_name="disp_max",
1170
+ )
1171
+ self.orchestrator.add_to_save_lists(
1172
+ os.path.join(
1173
+ pair_folder,
1174
+ "epi_disp_filling.tif",
1175
+ ),
1176
+ cst_disp.FILLING,
1177
+ epipolar_disparity_map,
1178
+ dtype=np.uint8,
1179
+ cars_ds_name="epi_disp_filling",
1180
+ nodata=255,
1181
+ )
1182
+
1183
+ # Get saving infos in order to save tiles when they are computed
1184
+ [saving_info] = self.orchestrator.get_saving_infos(
1185
+ [epipolar_disparity_map]
1186
+ )
1187
+
1188
+ # Add infos to orchestrator.out_json
1189
+ updating_dict = {
1190
+ application_constants.APPLICATION_TAG: {
1191
+ dm_cst.DENSE_MATCHING_RUN_TAG: {
1192
+ pair_key: {
1193
+ "global_disp_min": disp_range_grid["global_min"],
1194
+ "global_disp_max": disp_range_grid["global_max"],
1195
+ },
1196
+ },
1197
+ }
1198
+ }
1199
+ self.orchestrator.update_out_info(updating_dict)
1200
+ logging.info(
1201
+ "Compute disparity: number tiles: {}".format(
1202
+ epipolar_disparity_map.shape[1]
1203
+ * epipolar_disparity_map.shape[0]
1204
+ )
1205
+ )
1206
+
1207
+ nb_total_tiles_roi = 0
1208
+
1209
+ # broadcast grids
1210
+ # Transform grids to CarsDict for broadcasting
1211
+ # due to Dask issue https://github.com/dask/dask/issues/9969
1212
+ broadcasted_disp_range_grid = self.orchestrator.cluster.scatter(
1213
+ CarsDict(disp_range_grid)
1214
+ )
1215
+
1216
+ # Generate disparity maps
1217
+ for col in range(epipolar_disparity_map.shape[1]):
1218
+ for row in range(epipolar_disparity_map.shape[0]):
1219
+ use_tile = False
1220
+ crop_with_range = None
1221
+ if type(None) not in (
1222
+ type(epipolar_images_left[row, col]),
1223
+ type(epipolar_images_right[row, col]),
1224
+ ):
1225
+ use_tile = True
1226
+ nb_total_tiles_roi += 1
1227
+
1228
+ # Compute optimal tile size for tile
1229
+ (
1230
+ _,
1231
+ _,
1232
+ crop_with_range,
1233
+ ) = local_tile_optimal_size_fun(
1234
+ np.array(
1235
+ epipolar_images_left.attributes[
1236
+ "disp_min_tiling"
1237
+ ]
1238
+ )[row, col],
1239
+ np.array(
1240
+ epipolar_images_left.attributes[
1241
+ "disp_max_tiling"
1242
+ ]
1243
+ )[row, col],
1244
+ )
1245
+
1246
+ if use_tile:
1247
+ # update saving infos for potential replacement
1248
+ full_saving_info = ocht.update_saving_infos(
1249
+ saving_info, row=row, col=col
1250
+ )
1251
+ # Compute disparity
1252
+ (
1253
+ epipolar_disparity_map[row, col]
1254
+ ) = self.orchestrator.cluster.create_task(
1255
+ compute_disparity_wrapper
1256
+ )(
1257
+ epipolar_images_left[row, col],
1258
+ epipolar_images_right[row, col],
1259
+ self.corr_config,
1260
+ self.used_band,
1261
+ broadcasted_disp_range_grid,
1262
+ saving_info=full_saving_info,
1263
+ compute_disparity_masks=compute_disparity_masks,
1264
+ crop_with_range=crop_with_range,
1265
+ left_overlaps=cars_dataset.overlap_array_to_dict(
1266
+ epipolar_disparity_map.overlaps[row, col]
1267
+ ),
1268
+ margins_to_keep=margins_to_keep,
1269
+ classification_fusion_margin=(
1270
+ self.classification_fusion_margin
1271
+ ),
1272
+ texture_bands=texture_bands,
1273
+ conf_filtering=self.confidence_filtering,
1274
+ threshold_disp_range_to_borders=(
1275
+ self.threshold_disp_range_to_borders
1276
+ ),
1277
+ filter_incomplete_disparity_range=(
1278
+ self.filter_incomplete_disparity_range
1279
+ ),
1280
+ classif_bands_to_mask=classif_bands_to_mask,
1281
+ )
1282
+
1283
+ else:
1284
+ logging.error(
1285
+ "DenseMatching application doesn't "
1286
+ "support this input data format"
1287
+ )
1288
+ return epipolar_disparity_map
1289
+
1290
+
1291
+ def compute_disparity_wrapper( # pylint: disable=too-many-positional-arguments
1292
+ left_image_object: xr.Dataset,
1293
+ right_image_object: xr.Dataset,
1294
+ corr_cfg: dict,
1295
+ used_band: str,
1296
+ disp_range_grid,
1297
+ saving_info=None,
1298
+ compute_disparity_masks=False,
1299
+ crop_with_range=None,
1300
+ left_overlaps=None,
1301
+ margins_to_keep=0,
1302
+ classification_fusion_margin=-1,
1303
+ texture_bands=None,
1304
+ conf_filtering=None,
1305
+ threshold_disp_range_to_borders=False,
1306
+ filter_incomplete_disparity_range=True,
1307
+ classif_bands_to_mask=None,
1308
+ ) -> Dict[str, Tuple[xr.Dataset, xr.Dataset]]:
1309
+ """
1310
+ Compute disparity maps from image objects.
1311
+ This function will be run as a delayed task.
1312
+
1313
+ User must provide saving infos to save properly created datasets
1314
+
1315
+ :param left_image_object: tiled Left image
1316
+ - dataset with :
1317
+
1318
+ - cst.EPI_IMAGE
1319
+ - cst.EPI_MSK (if given)
1320
+ - cst.EPI_TEXTURE (for left, if given)
1321
+ :type left_image_object: xr.Dataset
1322
+ - dataset with :
1323
+
1324
+ - cst.EPI_IMAGE
1325
+ - cst.EPI_MSK (if given)
1326
+ - cst.EPI_TEXTURE (for left, if given)
1327
+ :param right_image_object: tiled Right image
1328
+ :type right_image_object: xr.Dataset
1329
+ :param corr_cfg: Correlator configuration
1330
+ :type corr_cfg: dict
1331
+ :param used_band: name of band used for correlation
1332
+ :type used_band: str
1333
+ :param disp_range_grid: minimum and maximum disparity grid
1334
+ :type disp_range_grid: np.ndarray
1335
+ :param compute_disparity_masks: Compute all the disparity \
1336
+ pandora masks(disable by default)
1337
+ :type compute_disparity_masks: bool
1338
+ :param generate_performance_map: True if generate performance map
1339
+ :type generate_performance_map: bool
1340
+ :param perf_ambiguity_threshold: ambiguity threshold used for
1341
+ performance map
1342
+ :type perf_ambiguity_threshold: float
1343
+ :param disp_to_alt_ratio: disp to alti ratio used for performance map
1344
+ :type disp_to_alt_ratio: float
1345
+ :param crop_with_range: range length to crop disparity range with
1346
+ :type crop_with_range: float
1347
+ :param left_overlaps: left overlap
1348
+ :type: left_overlaps: dict
1349
+ :param margins_to_keep: margin to keep after dense matching
1350
+ :type margins_to_keep: int
1351
+ :param classification_fusion_margin: the margin to add for the fusion
1352
+ :type classification_fusion_margin: int
1353
+ :param classif_bands_to_mask: bands from classif to mask
1354
+ :type classif_bands_to_mask: list of str / int
1355
+
1356
+
1357
+ :return: Left to right disparity dataset
1358
+ Returned dataset is composed of :
1359
+
1360
+ - cst_disp.MAP
1361
+ - cst_disp.VALID
1362
+ - cst.EPI_TEXTURE
1363
+
1364
+ """
1365
+ # transform disp_range_grid back to dict
1366
+ disp_range_grid = disp_range_grid.data
1367
+ # Generate disparity grids
1368
+ (
1369
+ disp_min_grid,
1370
+ disp_max_grid,
1371
+ ) = dm_algo.compute_disparity_grid(
1372
+ disp_range_grid,
1373
+ left_image_object,
1374
+ right_image_object,
1375
+ used_band,
1376
+ threshold_disp_range_to_borders,
1377
+ )
1378
+
1379
+ global_disp_min = disp_range_grid["global_min"]
1380
+ global_disp_max = disp_range_grid["global_max"]
1381
+
1382
+ # add global disparity in case of ambiguity normalization
1383
+ left_image_object = add_global_disparity(
1384
+ left_image_object, global_disp_min, global_disp_max
1385
+ )
1386
+
1387
+ # Crop interval if needed
1388
+ mask_crop = np.zeros(disp_min_grid.shape, dtype=int)
1389
+ is_cropped = False
1390
+ if crop_with_range is not None:
1391
+ current_min = np.min(disp_min_grid)
1392
+ current_max = np.max(disp_max_grid)
1393
+ if (current_max - current_min) > crop_with_range:
1394
+ is_cropped = True
1395
+ logging.warning("disparity range for current tile is cropped")
1396
+ # crop
1397
+ new_min = (
1398
+ current_min * crop_with_range / (current_max - current_min)
1399
+ )
1400
+ new_max = (
1401
+ current_max * crop_with_range / (current_max - current_min)
1402
+ )
1403
+
1404
+ mask_crop = np.logical_or(
1405
+ disp_min_grid < new_min, disp_max_grid > new_max
1406
+ )
1407
+ mask_crop = mask_crop.astype(bool)
1408
+ disp_min_grid[mask_crop] = new_min
1409
+ disp_max_grid[mask_crop] = new_max
1410
+
1411
+ # Compute disparity
1412
+ # TODO : remove overwriting of EPI_MSK
1413
+ disp_dataset = dm_algo.compute_disparity(
1414
+ left_image_object,
1415
+ right_image_object,
1416
+ corr_cfg,
1417
+ used_band,
1418
+ disp_min_grid=disp_min_grid,
1419
+ disp_max_grid=disp_max_grid,
1420
+ compute_disparity_masks=compute_disparity_masks,
1421
+ cropped_range=mask_crop,
1422
+ margins_to_keep=margins_to_keep,
1423
+ classification_fusion_margin=classification_fusion_margin,
1424
+ texture_bands=texture_bands,
1425
+ filter_incomplete_disparity_range=filter_incomplete_disparity_range,
1426
+ classif_bands_to_mask=classif_bands_to_mask,
1427
+ )
1428
+
1429
+ mask = disp_dataset["disp_msk"].values
1430
+ disp_map = disp_dataset["disp"].values
1431
+ disp_map[mask == 0] = np.nan
1432
+
1433
+ # Filtering by using the confidence
1434
+ requested_confidence = [
1435
+ "confidence_from_risk_min.cars_2",
1436
+ "confidence_from_risk_max.cars_2",
1437
+ "confidence_from_interval_bounds_inf.cars_3",
1438
+ "confidence_from_interval_bounds_sup.cars_3",
1439
+ ]
1440
+
1441
+ if (
1442
+ all(key in disp_dataset for key in requested_confidence)
1443
+ and conf_filtering["activated"] is True
1444
+ ):
1445
+ dm_wrappers.confidence_filtering(
1446
+ disp_dataset,
1447
+ requested_confidence,
1448
+ conf_filtering,
1449
+ )
1450
+
1451
+ # Fill with attributes
1452
+ cars_dataset.fill_dataset(
1453
+ disp_dataset,
1454
+ saving_info=saving_info,
1455
+ window=cars_dataset.get_window_dataset(left_image_object),
1456
+ profile=cars_dataset.get_profile_rasterio(left_image_object),
1457
+ attributes={cst.CROPPED_DISPARITY_RANGE: is_cropped},
1458
+ overlaps=left_overlaps,
1459
+ )
1460
+
1461
+ return disp_dataset