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,951 @@
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
+ This module is responsible for the dense matching algorithms:
22
+ - thus it creates a disparity map from a pair of images
23
+ """
24
+ # pylint: disable=C0302
25
+
26
+ # Standard imports
27
+ import logging
28
+ import math
29
+ import warnings
30
+ from typing import Dict
31
+
32
+ import numpy as np
33
+ import xarray as xr
34
+
35
+ # Third party imports
36
+ from pandora import constants as p_cst
37
+ from scipy.ndimage import generic_filter
38
+
39
+ from cars.applications.dense_match_filling import fill_disp_wrappers
40
+
41
+ # CARS imports
42
+ from cars.applications.dense_matching import (
43
+ dense_matching_constants as dense_match_cst,
44
+ )
45
+ from cars.conf import mask_cst as msk_cst
46
+ from cars.core import constants as cst
47
+ from cars.core import constants_disparity as cst_disp
48
+
49
+ from .cpp import dense_matching_cpp
50
+
51
+
52
+ def get_margins(margin, disp_min, disp_max):
53
+ """
54
+ Get margins for the dense matching steps
55
+
56
+ :param margin: margins object
57
+ :type margin: Margins
58
+ :param disp_min: Minimum disparity
59
+ :type disp_min: int
60
+ :param disp_max: Maximum disparity
61
+ :type disp_max: int
62
+ :return: margins of the matching algorithm used
63
+ """
64
+
65
+ corner = ["left", "up", "right", "down"]
66
+ col = np.arange(len(corner))
67
+
68
+ left_margins = [
69
+ margin.left + disp_max,
70
+ margin.up,
71
+ margin.right - disp_min,
72
+ margin.down,
73
+ ]
74
+ right_margins = [
75
+ margin.left - disp_min,
76
+ margin.up,
77
+ margin.right + disp_max,
78
+ margin.down,
79
+ ]
80
+ same_margins = [
81
+ max(left, right)
82
+ for left, right in zip(left_margins, right_margins) # noqa: B905
83
+ ]
84
+
85
+ margins = xr.Dataset(
86
+ {
87
+ "left_margin": (
88
+ ["col"],
89
+ same_margins,
90
+ )
91
+ },
92
+ coords={"col": col},
93
+ )
94
+ margins["right_margin"] = xr.DataArray(same_margins, dims=["col"])
95
+
96
+ margins.attrs["disp_min"] = disp_min
97
+ margins.attrs["disp_max"] = disp_max
98
+
99
+ return margins
100
+
101
+
102
+ def get_masks_from_pandora(
103
+ disp: xr.Dataset,
104
+ compute_disparity_masks: bool,
105
+ filter_incomplete_disparity_range: bool,
106
+ ) -> Dict[str, np.ndarray]:
107
+ """
108
+ Get masks dictionary from the disparity map in output of pandora.
109
+
110
+ :param disp: disparity map (pandora output)
111
+ :param compute_disparity_masks: compute_disparity_masks activation status
112
+ :return: masks dictionary
113
+ """
114
+ masks = {}
115
+
116
+ # Retrieve validity mask from pandora
117
+ # Invalid pixels in validity mask are:
118
+ # * Bit 0: Edge of the reference image or nodata in reference image
119
+ # * Bit 1: Disparity interval to explore is missing or nodata in the
120
+ # secondary image
121
+ # * Bit 6: Pixel is masked on the mask of the reference image
122
+ # * Bit 7: Disparity to explore is masked on the mask of the secondary
123
+ # image
124
+ # * Bit 8: Pixel located in an occlusion region
125
+ # * Bit 9: Fake match
126
+ validity_mask_cropped = disp.validity_mask.values
127
+ # Mask initialization to false (all is invalid)
128
+ masks[cst_disp.VALID] = np.full(validity_mask_cropped.shape, False)
129
+ invalid_value = p_cst.PANDORA_MSK_PIXEL_INVALID
130
+ if filter_incomplete_disparity_range:
131
+ invalid_value |= (
132
+ p_cst.PANDORA_MSK_PIXEL_INCOMPLETE_VARIABLE_DISPARITY_RANGE
133
+ )
134
+
135
+ # Identify valid points
136
+ masks[cst_disp.VALID][
137
+ np.where((validity_mask_cropped & invalid_value) == 0)
138
+ ] = True
139
+
140
+ # With compute_disparity_masks, produce one mask for each invalid flag in
141
+ if compute_disparity_masks:
142
+ msk_table = dense_match_cst.MASK_HASH_TABLE
143
+ for key, val in msk_table.items():
144
+ masks[key] = np.full(validity_mask_cropped.shape, False)
145
+ masks[key][np.where((validity_mask_cropped & val) == 0)] = True
146
+
147
+ # Build final mask with 255 for valid points and 0 for invalid points
148
+ # The mask is used by rasterize method (non zero are valid points)
149
+ for key, mask in masks.items():
150
+ final_msk = np.ndarray(mask.shape, dtype=np.int16)
151
+ final_msk[mask] = 255
152
+ final_msk[np.equal(mask, False)] = 0
153
+ masks[key] = final_msk
154
+
155
+ return masks
156
+
157
+
158
+ def add_disparity_grids(
159
+ output_dataset: xr.Dataset,
160
+ disp_min_grid: np.ndarray = None,
161
+ disp_max_grid: np.ndarray = None,
162
+ ):
163
+ """
164
+ Add disparity min and max grids to dataset
165
+
166
+ :param output_dataset: output dataset
167
+ :param disp_min_grid: dense disparity map grid min
168
+ :param disp_max_grid: dense disparity map grid max
169
+
170
+ """
171
+ if disp_min_grid is not None:
172
+ output_dataset[cst_disp.EPI_DISP_MIN_GRID] = xr.DataArray(
173
+ disp_min_grid,
174
+ dims=[cst.ROW, cst.COL],
175
+ )
176
+
177
+ # Add color mask
178
+ if disp_max_grid is not None:
179
+ output_dataset[cst_disp.EPI_DISP_MAX_GRID] = xr.DataArray(
180
+ disp_max_grid,
181
+ dims=[cst.ROW, cst.COL],
182
+ )
183
+
184
+
185
+ def add_texture(
186
+ output_dataset: xr.Dataset,
187
+ texture: np.ndarray = None,
188
+ color_type=None,
189
+ band_im: list = None,
190
+ texture_bands: list = None,
191
+ ):
192
+ """
193
+ Add image and image mask to dataset
194
+
195
+ :param output_dataset: output dataset
196
+ :param image: image array
197
+ :param image_type: data type of pixels
198
+ :param band_im: list of band names
199
+
200
+ """
201
+ if texture_bands:
202
+ output_dataset.coords[cst.BAND_IM] = band_im[texture_bands]
203
+ output_dataset[cst.EPI_TEXTURE] = xr.DataArray(
204
+ texture[texture_bands],
205
+ dims=[cst.BAND_IM, cst.ROW, cst.COL],
206
+ )
207
+
208
+ if color_type is not None:
209
+ # Add input image type
210
+ output_dataset[cst.EPI_TEXTURE].attrs["color_type"] = color_type
211
+
212
+
213
+ def add_classification(
214
+ output_dataset: xr.Dataset,
215
+ classif: np.ndarray = None,
216
+ band_classif: list = None,
217
+ ):
218
+ """
219
+ Add classification to dataset
220
+
221
+ :param output_dataset: output dataset
222
+ :param classif: classif array
223
+ :param band_im: list of band names
224
+
225
+ """
226
+ if classif is not None:
227
+ if (
228
+ band_classif is not None
229
+ and cst.BAND_CLASSIF not in output_dataset.dims
230
+ ):
231
+ output_dataset.coords[cst.BAND_CLASSIF] = band_classif
232
+ output_dataset[cst.EPI_CLASSIFICATION] = xr.DataArray(
233
+ classif,
234
+ dims=[cst.BAND_CLASSIF, cst.ROW, cst.COL],
235
+ )
236
+
237
+
238
+ def compute_cropped_roi(
239
+ current_margins, margins_to_keep, tile_roi, nb_rows, nb_cols
240
+ ):
241
+ """
242
+ Compute cropped roi, with associated margins and
243
+
244
+ :param current_margins: current dataset margins
245
+ :type current_margins: list[int]
246
+ :param margins_to_keep: margin to keep
247
+ :type margins_to_keep: int
248
+ :param nb_rows: number of current rows
249
+ :type margins_to_keep: int
250
+ :param tile_roi: roi without margin of tile
251
+ :type tile_roi: list
252
+ :param nb_cols: number of current cols
253
+ :type nb_cols: int
254
+
255
+ :return: (borders to use as roi, new dataset roi with margin,
256
+ margin associated to roi)
257
+ :rtype: tuple(list, list, list)
258
+ """
259
+
260
+ def cast_int_list(current_list):
261
+ """
262
+ Apply int cast to list
263
+ """
264
+ new_list = []
265
+ for obj in current_list:
266
+ new_list.append(int(obj))
267
+ return new_list
268
+
269
+ new_margin_neg = list(
270
+ np.clip(np.array(current_margins[0:2]), -margins_to_keep, 0)
271
+ )
272
+ new_margin_pos = list(
273
+ np.clip(np.array(current_margins[2:4]), 0, margins_to_keep)
274
+ )
275
+ new_margin = list(np.array(new_margin_neg + new_margin_pos).astype(int))
276
+
277
+ new_roi = list(np.array(tile_roi) + np.array(new_margin))
278
+
279
+ ref_roi = [
280
+ int(-current_margins[0] + new_margin[0]),
281
+ int(-current_margins[1] + new_margin[1]),
282
+ int(nb_cols - current_margins[2] + new_margin[2]),
283
+ int(nb_rows - current_margins[3] + new_margin[3]),
284
+ ]
285
+
286
+ return (
287
+ cast_int_list(ref_roi),
288
+ cast_int_list(new_roi),
289
+ cast_int_list(new_margin),
290
+ )
291
+
292
+
293
+ # pylint: disable=too-many-positional-arguments
294
+ def create_disp_dataset( # noqa: C901
295
+ disp: xr.Dataset,
296
+ ref_dataset: xr.Dataset,
297
+ secondary_dataset: xr.Dataset,
298
+ compute_disparity_masks: bool = False,
299
+ disp_min_grid=None,
300
+ disp_max_grid=None,
301
+ cropped_range=None,
302
+ margins_to_keep=0,
303
+ classification_fusion_margin=-1,
304
+ texture_bands=None,
305
+ filter_incomplete_disparity_range=True,
306
+ ) -> xr.Dataset:
307
+ """
308
+ Create the disparity dataset.
309
+
310
+ :param disp: disparity map (result of pandora)
311
+ :param ref_dataset: reference dataset for the considered disparity map
312
+ :param secondary_dataset: secondary dataset for the considered
313
+ disparity map
314
+ :param compute_disparity_masks: compute_disparity_masks activation status
315
+ :param disp_min_grid: disparity min grid
316
+ :param disp_max_grid: disparity max grid
317
+ :param cropped_range: true if disparity range was cropped
318
+ :type cropped_range: numpy array
319
+ :param margins_to_keep: margin to keep after dense matching
320
+ :type margins_to_keep: int
321
+
322
+ :return: disparity dataset as used in cars
323
+ """
324
+
325
+ # Crop disparity to ROI
326
+ ref_roi, new_roi, new_margin = compute_cropped_roi(
327
+ ref_dataset.attrs[cst.EPI_MARGINS],
328
+ margins_to_keep,
329
+ ref_dataset.attrs[cst.ROI],
330
+ ref_dataset.sizes[cst.ROW],
331
+ ref_dataset.sizes[cst.COL],
332
+ )
333
+
334
+ # Crop datasets
335
+ disp = disp.isel(
336
+ row=slice(ref_roi[1], ref_roi[3]), col=slice(ref_roi[0], ref_roi[2])
337
+ )
338
+ ref_dataset = ref_dataset.isel(
339
+ row=slice(ref_roi[1], ref_roi[3]), col=slice(ref_roi[0], ref_roi[2])
340
+ )
341
+ secondary_dataset = secondary_dataset.isel(
342
+ row=slice(ref_roi[1], ref_roi[3]), col=slice(ref_roi[0], ref_roi[2])
343
+ )
344
+ # Crop disparity min max grids
345
+ if disp_min_grid is not None:
346
+ disp_min_grid = disp_min_grid[
347
+ ref_roi[1] : ref_roi[3], ref_roi[0] : ref_roi[2]
348
+ ]
349
+ if disp_max_grid is not None:
350
+ disp_max_grid = disp_max_grid[
351
+ ref_roi[1] : ref_roi[3], ref_roi[0] : ref_roi[2]
352
+ ]
353
+ if cropped_range is not None:
354
+ cropped_range = cropped_range[
355
+ ref_roi[1] : ref_roi[3], ref_roi[0] : ref_roi[2]
356
+ ]
357
+
358
+ # Retrieve disparity values
359
+ disp_map = disp.disparity_map.values
360
+
361
+ # Transform image to texture
362
+ epi_image = ref_dataset[cst.EPI_IMAGE].values
363
+ band_im = ref_dataset.coords[cst.BAND_IM]
364
+ image_type = ref_dataset.attrs.get("image_type", "float32")
365
+ if isinstance(image_type, list):
366
+ if texture_bands is not None:
367
+ image_type = image_type[texture_bands[0]]
368
+ else:
369
+ image_type = image_type[0]
370
+ # Cast image
371
+ if np.issubdtype(image_type, np.floating):
372
+ min_value_clr = np.finfo(image_type).min
373
+ max_value_clr = np.finfo(image_type).max
374
+ else:
375
+ min_value_clr = np.iinfo(image_type).min
376
+ max_value_clr = np.iinfo(image_type).max
377
+ epi_image = np.clip(epi_image, min_value_clr, max_value_clr).astype(
378
+ image_type
379
+ )
380
+
381
+ # Retrieve original mask of panchromatic image
382
+ epi_msk = None
383
+ if cst.EPI_MSK in ref_dataset:
384
+ epi_msk = ref_dataset[cst.EPI_MSK].values
385
+
386
+ epi_msk_right = None
387
+ if cst.EPI_MSK in secondary_dataset:
388
+ epi_msk_right = secondary_dataset[cst.EPI_MSK].values
389
+
390
+ # Retrieve masks from pandora
391
+ pandora_masks = get_masks_from_pandora(
392
+ disp, compute_disparity_masks, filter_incomplete_disparity_range
393
+ )
394
+ pandora_masks[cst_disp.VALID][np.isnan(disp_map)] = 0
395
+
396
+ # retrieve classif
397
+ left_classif = None
398
+ left_band_classif = None
399
+ if cst.EPI_CLASSIFICATION in ref_dataset:
400
+ left_classif = ref_dataset[cst.EPI_CLASSIFICATION].values
401
+ left_band_classif = ref_dataset.coords[cst.BAND_CLASSIF].values
402
+
403
+ # mask left classif outside right sensor
404
+ if epi_msk_right is not None:
405
+ left_classif = mask_left_classif_from_right_mask( # here
406
+ left_classif,
407
+ epi_msk_right == msk_cst.NO_DATA_IN_EPIPOLAR_RECTIFICATION,
408
+ np.floor(disp_min_grid).astype(np.int16),
409
+ np.ceil(disp_max_grid).astype(np.int16),
410
+ classification_fusion_margin,
411
+ )
412
+
413
+ left_from_right_classif = None
414
+ right_band_classif = None
415
+ if cst.EPI_CLASSIFICATION in secondary_dataset:
416
+ right_classif = secondary_dataset[cst.EPI_CLASSIFICATION].values
417
+ right_band_classif = secondary_dataset.coords[cst.BAND_CLASSIF].values
418
+
419
+ left_from_right_classif = estimate_right_classif_on_left(
420
+ right_classif,
421
+ disp_map,
422
+ pandora_masks[cst_disp.VALID],
423
+ int(np.floor(np.min(disp_min_grid))),
424
+ int(np.ceil(np.max(disp_max_grid))),
425
+ classification_fusion_margin,
426
+ )
427
+
428
+ # mask outside left sensor
429
+ left_mask = (
430
+ ref_dataset[cst.EPI_MSK].values
431
+ == msk_cst.NO_DATA_IN_EPIPOLAR_RECTIFICATION
432
+ )
433
+ left_mask_stacked = np.repeat(
434
+ np.expand_dims(left_mask, axis=0),
435
+ left_from_right_classif.shape[0],
436
+ axis=0,
437
+ )
438
+ # pylint: disable=unsupported-assignment-operation
439
+ left_from_right_classif[left_mask_stacked] = 0
440
+ # Merge right classif
441
+ classif, band_classif = merge_classif_left_right(
442
+ left_classif,
443
+ left_band_classif,
444
+ left_from_right_classif,
445
+ right_band_classif,
446
+ )
447
+
448
+ # Fill disparity array with 0 value for invalid points
449
+ disp_map[pandora_masks[cst_disp.VALID] == 0] = np.nan
450
+
451
+ # Build output dataset
452
+ row = np.array(range(new_roi[1], new_roi[3]))
453
+ col = np.array(range(new_roi[0], new_roi[2]))
454
+
455
+ disp_ds = xr.Dataset(
456
+ {
457
+ cst_disp.MAP: ([cst.ROW, cst.COL], np.copy(disp_map)),
458
+ cst_disp.VALID: (
459
+ [cst.ROW, cst.COL],
460
+ pandora_masks[cst_disp.VALID].astype("uint8"),
461
+ ),
462
+ },
463
+ coords={cst.ROW: row, cst.COL: col},
464
+ )
465
+
466
+ # add left texture
467
+ add_texture(
468
+ disp_ds,
469
+ texture=epi_image,
470
+ color_type=image_type,
471
+ band_im=band_im,
472
+ texture_bands=texture_bands,
473
+ )
474
+
475
+ # add original mask
476
+ if epi_msk is not None:
477
+ disp_ds[cst.EPI_MSK] = xr.DataArray(
478
+ epi_msk.astype("uint8"), dims=[cst.ROW, cst.COL]
479
+ )
480
+
481
+ # add confidence
482
+ if "confidence_measure" in disp:
483
+ add_confidence(disp_ds, disp)
484
+
485
+ # add classif
486
+ add_classification(disp_ds, classif=classif, band_classif=band_classif)
487
+
488
+ # Add disparity grids
489
+ add_disparity_grids(
490
+ disp_ds, disp_min_grid=disp_min_grid, disp_max_grid=disp_max_grid
491
+ )
492
+
493
+ # Add filling infos
494
+ if cropped_range is not None:
495
+ disp_ds = add_crop_info(disp_ds, cropped_range)
496
+
497
+ if compute_disparity_masks:
498
+ for key, val in pandora_masks.items():
499
+ disp_ds[key] = xr.DataArray(np.copy(val), dims=[cst.ROW, cst.COL])
500
+
501
+ disp_ds.attrs[cst.ROI] = ref_dataset.attrs[cst.ROI]
502
+ disp_ds.attrs[cst.ROI_WITH_MARGINS] = new_roi
503
+ disp_ds.attrs[cst.EPI_MARGINS] = new_margin
504
+
505
+ disp_ds.attrs[cst.EPI_FULL_SIZE] = ref_dataset.attrs[cst.EPI_FULL_SIZE]
506
+
507
+ return disp_ds
508
+
509
+
510
+ def add_crop_info(disp_ds, cropped_range):
511
+ """
512
+ Add crop info
513
+
514
+ :param disp: disp xarray
515
+ :param cropped_range: was cropped range, bool
516
+
517
+ :return updated dataset
518
+ """
519
+
520
+ disp_ds = fill_disp_wrappers.add_empty_filling_band(
521
+ disp_ds, ["cropped_disp_range"]
522
+ )
523
+ fill_disp_wrappers.update_filling(
524
+ disp_ds, cropped_range, "cropped_disp_range"
525
+ )
526
+ return disp_ds
527
+
528
+
529
+ # pylint: disable=too-many-positional-arguments
530
+ def estimate_right_classif_on_left(
531
+ right_classif,
532
+ disp_map,
533
+ disp_mask,
534
+ disp_min,
535
+ disp_max,
536
+ classifiation_fusion_margin,
537
+ ):
538
+ """
539
+ Estimate right classif on left image
540
+
541
+ :param right_classif: right classification
542
+ :type right_classif: np ndarray
543
+ :param disp_map: disparity map
544
+ :type disp_map: np ndarray
545
+ :param disp_mask: disparity mask
546
+ :type disp_mask: np ndarray
547
+ :param disp_min: disparity min
548
+ :type disp_min: int
549
+ :param disp_max: disparity max
550
+ :type disp_max: int
551
+
552
+ :return: right classif on left image
553
+ :rtype: np nadarray
554
+ """
555
+ return dense_matching_cpp.estimate_right_classif_on_left(
556
+ right_classif,
557
+ disp_map,
558
+ disp_mask,
559
+ disp_min,
560
+ disp_max,
561
+ classifiation_fusion_margin,
562
+ )
563
+
564
+
565
+ def mask_left_classif_from_right_mask(
566
+ left_classif,
567
+ right_mask,
568
+ disp_min,
569
+ disp_max,
570
+ classifiation_fusion_margin,
571
+ ):
572
+ """
573
+ Mask left classif with right mask.
574
+
575
+ :param left_classif: right classification
576
+ :type left_classif: np ndarray
577
+ :param right_mask: right mask
578
+ :type right_mask: np ndarray
579
+ :param disp_min: disparity min
580
+ :type disp_min: np.array type int
581
+ :param disp_max: disparity max
582
+ :type disp_max: np.array type int
583
+
584
+ :return: masked left classif
585
+ :rtype: np nadarray
586
+ """
587
+ return dense_matching_cpp.mask_left_classif_from_right_mask(
588
+ left_classif,
589
+ right_mask,
590
+ disp_min,
591
+ disp_max,
592
+ classifiation_fusion_margin,
593
+ )
594
+
595
+
596
+ def merge_classif_left_right(
597
+ left_classif, left_band_classif, right_classif, right_band_classif
598
+ ):
599
+ """
600
+ Merge left and right classif
601
+
602
+ :param left_classif: left classif
603
+ :type left_classif: np nadarray
604
+ :param left_band_classif: list of tag
605
+ :type left_band_classif: list
606
+ :param right_classif: left classif
607
+ :type right_classif: np nadarray
608
+ :param right_band_classif: list of tag
609
+ :type right_band_classif: list
610
+
611
+ :return: merged classif, merged tag list
612
+ :rtype: np ndarray, list
613
+
614
+ """
615
+
616
+ classif = None
617
+ band_classif = None
618
+
619
+ if left_classif is None and right_classif is not None:
620
+ classif = right_classif
621
+ band_classif = right_band_classif
622
+
623
+ elif left_classif is not None and right_classif is None:
624
+ classif = left_classif
625
+ band_classif = left_band_classif
626
+
627
+ elif left_classif is not None and right_classif is not None:
628
+ list_classif = []
629
+ band_classif = []
630
+ seen_tag = []
631
+
632
+ # search in left
633
+ for ind_left, tag_left in enumerate(left_band_classif):
634
+ seen_tag.append(tag_left)
635
+ band_classif.append(tag_left)
636
+ if tag_left in right_band_classif:
637
+ # merge left and right
638
+ ind_right = list(right_band_classif).index(tag_left)
639
+ list_classif.append(
640
+ np.logical_or(
641
+ left_classif[ind_left, :, :],
642
+ right_classif[ind_right, :, :],
643
+ )
644
+ )
645
+ else:
646
+ list_classif.append(left_classif[ind_left, :, :])
647
+
648
+ # search in right not already seen
649
+ for ind_right, tag_right in enumerate(right_band_classif):
650
+ if tag_right not in seen_tag:
651
+ band_classif.append(tag_right)
652
+ list_classif.append(right_classif[ind_right, :, :])
653
+
654
+ # Stack classif
655
+ classif = np.stack(list_classif, axis=0)
656
+
657
+ return classif, band_classif
658
+
659
+
660
+ def add_confidence(
661
+ output_dataset: xr.Dataset,
662
+ disp: xr.Dataset,
663
+ ):
664
+ """
665
+ Add confidences to dataset
666
+
667
+ :param output_dataset: output dataset
668
+ :param disp: disp xarray
669
+
670
+ """
671
+ confidence_measure_indicator_list = np.array(
672
+ disp.confidence_measure.indicator
673
+ )
674
+ for key in confidence_measure_indicator_list:
675
+ if key == "confidence_from_ambiguity.cars_1":
676
+ confidence_idx = list(disp.confidence_measure.indicator).index(key)
677
+
678
+ output_dataset["ambiguity"] = xr.DataArray(
679
+ 1
680
+ - np.copy(
681
+ disp.confidence_measure.data[
682
+ :,
683
+ :,
684
+ confidence_idx,
685
+ ]
686
+ ),
687
+ dims=[cst.ROW, cst.COL],
688
+ )
689
+ else:
690
+ confidence_idx = list(disp.confidence_measure.indicator).index(key)
691
+ output_dataset[str(key)] = xr.DataArray(
692
+ np.copy(
693
+ disp.confidence_measure.data[
694
+ :,
695
+ :,
696
+ confidence_idx,
697
+ ]
698
+ ),
699
+ dims=[cst.ROW, cst.COL],
700
+ )
701
+
702
+
703
+ def to_safe_disp_grid(grid_disp_min, grid_disp_max):
704
+ """
705
+ Generate safe grids, with min < max for each point
706
+
707
+ :param grid_disp_min: min disp grid
708
+ :param grid_disp_max: max disp grid
709
+
710
+ :return: grid_disp_min, grid_disp_max
711
+ """
712
+
713
+ stacked_disp_range = np.dstack([grid_disp_min, grid_disp_max])
714
+ grid_disp_min = np.nanmin(stacked_disp_range, axis=2)
715
+ grid_disp_max = np.nanmax(stacked_disp_range, axis=2)
716
+
717
+ # convert nan
718
+ grid_disp_min[np.isnan(grid_disp_min)] = 0
719
+ grid_disp_max[np.isnan(grid_disp_max)] = 0
720
+
721
+ if (grid_disp_min > grid_disp_max).any():
722
+ raise RuntimeError("grid min > max")
723
+
724
+ return grid_disp_min, grid_disp_max
725
+
726
+
727
+ def estimate_right_grid_disp(disp_min_grid, disp_max_grid):
728
+ """
729
+ Estimate right grid min and max.
730
+ Correspond to the range of pixels that can be correlated
731
+ from left -> right.
732
+ If no left pixels can be associated to right, use global values
733
+
734
+ :param disp_min_grid: left disp min grid
735
+ :type disp_min_grid: numpy ndarray
736
+ :param disp_max_grid: left disp max grid
737
+ :type disp_max_grid: numpy ndarray
738
+
739
+ :return: disp_min_right_grid, disp_max_right_grid
740
+ :rtype: numpy ndarray, numpy ndarray
741
+ """
742
+ float_types = [np.float16, np.float32, np.float64, np.float128]
743
+ int_types = [
744
+ int,
745
+ np.int8,
746
+ np.uint8,
747
+ np.int16,
748
+ np.uint16,
749
+ np.int32,
750
+ np.uint32,
751
+ np.int64,
752
+ np.uint64,
753
+ ]
754
+ if disp_min_grid.dtype in float_types:
755
+ return dense_matching_cpp.estimate_right_grid_disp_float(
756
+ disp_min_grid, disp_max_grid
757
+ )
758
+ if disp_min_grid.dtype in int_types:
759
+ return dense_matching_cpp.estimate_right_grid_disp_int(
760
+ disp_min_grid, disp_max_grid
761
+ )
762
+
763
+ raise TypeError(
764
+ "estimate_right_grid_disp does not support"
765
+ f"{disp_min_grid.dtype} as an input type"
766
+ )
767
+
768
+
769
+ # pylint: disable=too-many-positional-arguments
770
+ def optimal_tile_size_pandora_plugin_libsgm(
771
+ disp_min: int,
772
+ disp_max: int,
773
+ min_tile_size: int,
774
+ max_tile_size: int,
775
+ max_ram_per_worker: int,
776
+ tile_size_rounding: int = 50,
777
+ margin: int = 0,
778
+ ) -> int:
779
+ """
780
+ Compute optimal tile size according to estimated memory usage
781
+ (pandora_plugin_libsgm)
782
+ Returned optimal tile size will be at least equal to tile_size_rounding.
783
+
784
+ :param disp_min: Minimum disparity to explore
785
+ :param disp_max: Maximum disparity to explore
786
+ :param min_tile_size: Minimal tile size
787
+ :param max_tile_size: Maximal tile size
788
+ :param max_ram_per_worker: amount of RAM allocated per worker
789
+ :param tile_size_rounding: Optimal tile size will be aligned to multiples\
790
+ of tile_size_rounding
791
+ :param margin: margin to remove to the computed tile size
792
+ (as a percent of the computed tile size)
793
+ :returns: Optimal tile size according to benchmarked memory usage
794
+ """
795
+
796
+ memory = max_ram_per_worker
797
+ disp = max(3, abs(disp_max - disp_min))
798
+
799
+ image = 32 * 2
800
+ disp_ref = 32
801
+ validity_mask_ref = 16
802
+ confidence = 32
803
+ cv_ = disp * 32
804
+ nan_ = disp * 8
805
+ cv_uint = disp * 8
806
+ penal = 8 * 32 * 2
807
+ img_crop = 32 * 2
808
+
809
+ tot = image + disp_ref + validity_mask_ref
810
+ tot += confidence + 2 * cv_ + nan_ + cv_uint + penal + img_crop
811
+ import_ = 200 # MiB
812
+
813
+ row_or_col = float(((memory - import_) * 2**23)) / tot
814
+
815
+ if row_or_col <= 0:
816
+ logging.warning(
817
+ "Optimal tile size is null, "
818
+ "forcing it to {} pixels".format(tile_size_rounding)
819
+ )
820
+ tile_size = tile_size_rounding
821
+ else:
822
+ # nb_pixels = tile_size x (tile_size + disp)
823
+ # hence tile_size not equal to sqrt(nb_pixels)
824
+ # but sqrt(nb_pixels + (disp/2)**2) - disp/2
825
+ tile_size = np.sqrt(row_or_col + (disp / 2) ** 2) - disp / 2
826
+ tile_size = (1.0 - margin / 100.0) * tile_size
827
+
828
+ if math.isinf(tile_size):
829
+ logging.warning("Tile size infinite")
830
+
831
+ if tile_size > max_tile_size:
832
+ tile_size = max_tile_size
833
+ elif tile_size < min_tile_size:
834
+ tile_size = min_tile_size
835
+
836
+ tile_size = tile_size_rounding * int(tile_size / tile_size_rounding)
837
+
838
+ return tile_size
839
+
840
+
841
+ def get_max_disp_from_opt_tile_size(
842
+ opt_epipolar_tile_size, max_ram_per_worker, margin=0, used_disparity_range=0
843
+ ):
844
+ """
845
+ Compute max range possible depending on max ram per worker
846
+ Return max range usable
847
+
848
+ :param opt_epipolar_tile_size: used tile size
849
+ :param max_ram_per_worker: amount of RAM allocated per worker
850
+ :param tile_size_rounding: Optimal tile size will be aligned to multiples\
851
+ of tile_size_rounding
852
+ :param margin: margin to remove to the computed tile size
853
+ (as a percent of the computed tile size)
854
+ :returns: max disp range to use
855
+ """
856
+
857
+ import_ = 200
858
+ memory = max_ram_per_worker
859
+
860
+ # not depending on disp
861
+ image = 32 * 2
862
+ disp_ref = 32
863
+ validity_mask_ref = 16
864
+ confidence = 32
865
+ penal = 8 * 32 * 2
866
+ img_crop = 32 * 2
867
+ # depending on disp : data * disp
868
+ cv_ = 32
869
+ nan_ = 8
870
+ cv_uint = 8
871
+
872
+ row = opt_epipolar_tile_size / (1.0 - margin / 100.0)
873
+ col = row + used_disparity_range
874
+ row_or_col = row * col
875
+ tot = float(((memory - import_) * 2**23)) / row_or_col
876
+
877
+ disp_tot = tot - (
878
+ image + disp_ref + validity_mask_ref + confidence + penal + img_crop
879
+ )
880
+ # disp tot = disp x (2 x cv_ + nan_ + cv uint)
881
+ max_range = int(disp_tot / (2 * cv_ + nan_ + cv_uint) + 1)
882
+
883
+ return max_range
884
+
885
+
886
+ def nan_ratio_func(window):
887
+ """ "
888
+ Calculate the number of nan in the window
889
+
890
+ :param window: the window in the image
891
+ """
892
+
893
+ total_pixels = window.size
894
+ nan_count = np.isnan(window).sum()
895
+ return nan_count / total_pixels
896
+
897
+
898
+ def confidence_filtering(
899
+ dataset,
900
+ requested_confidence,
901
+ conf_filtering,
902
+ ):
903
+ """
904
+ Filter the disparity map by using the confidence
905
+
906
+ :param dataset: the epipolar disparity map dataset
907
+ :type dataset: cars dataset
908
+ :param disp_map: the disparity map
909
+ :type disp_map: numpy darray
910
+ :param requested_confidence: the confidence to use
911
+ :type requested_confidence: list
912
+ :param conf_filtering: the confidence_filtering parameters
913
+ :type conf_filtering: dict
914
+ """
915
+
916
+ data_risk_inf = dataset[requested_confidence[0]].values
917
+ data_risk_sup = dataset[requested_confidence[1]].values
918
+ risk_range = data_risk_sup - data_risk_inf
919
+
920
+ data_bounds_inf = dataset[requested_confidence[2]].values
921
+ data_bounds_sup = dataset[requested_confidence[3]].values
922
+ bounds_range = data_bounds_sup - data_bounds_inf
923
+
924
+ disp_min = dataset["disp_min_grid"].values
925
+ disp_max = dataset["disp_max_grid"].values
926
+
927
+ risk_ratio = risk_range / (disp_max - disp_min)
928
+ bounds_ratio = bounds_range / (disp_max - disp_min)
929
+
930
+ bounds_mask = (bounds_ratio > conf_filtering["bounds_ratio_threshold"]) & (
931
+ bounds_range > conf_filtering["bounds_range_threshold"]
932
+ )
933
+ risk_mask = (risk_ratio > conf_filtering["risk_ratio_threshold"]) & (
934
+ risk_range > conf_filtering["risk_range_threshold"]
935
+ )
936
+ mask = bounds_mask | risk_mask
937
+ dataset["disp"].values[mask] = np.nan
938
+ dataset["disp_msk"].values[mask] = 0
939
+
940
+ with warnings.catch_warnings():
941
+ warnings.simplefilter("ignore", category=RuntimeWarning)
942
+ nan_ratio = generic_filter(
943
+ dataset["disp"], nan_ratio_func, size=conf_filtering["win_nanratio"]
944
+ )
945
+
946
+ mask = (nan_ratio > conf_filtering["nan_threshold"]) & (
947
+ (bounds_range > conf_filtering["bounds_range_threshold"])
948
+ | (risk_range > conf_filtering["risk_range_threshold"])
949
+ )
950
+ dataset["disp"].values[mask] = np.nan
951
+ dataset["disp_msk"].values[mask] = 0