cars 1.0.0rc1__cp313-cp313-musllinux_1_2_i686.whl

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

Potentially problematic release.


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

Files changed (202) hide show
  1. cars/__init__.py +74 -0
  2. cars/applications/__init__.py +37 -0
  3. cars/applications/application.py +117 -0
  4. cars/applications/application_constants.py +29 -0
  5. cars/applications/application_template.py +146 -0
  6. cars/applications/auxiliary_filling/__init__.py +29 -0
  7. cars/applications/auxiliary_filling/abstract_auxiliary_filling_app.py +104 -0
  8. cars/applications/auxiliary_filling/auxiliary_filling_algo.py +475 -0
  9. cars/applications/auxiliary_filling/auxiliary_filling_from_sensors_app.py +630 -0
  10. cars/applications/auxiliary_filling/auxiliary_filling_wrappers.py +90 -0
  11. cars/applications/dem_generation/__init__.py +30 -0
  12. cars/applications/dem_generation/abstract_dem_generation_app.py +116 -0
  13. cars/applications/dem_generation/bulldozer_config/base_config.yaml +42 -0
  14. cars/applications/dem_generation/bulldozer_dem_app.py +655 -0
  15. cars/applications/dem_generation/bulldozer_memory.py +55 -0
  16. cars/applications/dem_generation/dem_generation_algo.py +107 -0
  17. cars/applications/dem_generation/dem_generation_constants.py +32 -0
  18. cars/applications/dem_generation/dem_generation_wrappers.py +323 -0
  19. cars/applications/dense_match_filling/__init__.py +30 -0
  20. cars/applications/dense_match_filling/abstract_dense_match_filling_app.py +242 -0
  21. cars/applications/dense_match_filling/fill_disp_algo.py +113 -0
  22. cars/applications/dense_match_filling/fill_disp_constants.py +39 -0
  23. cars/applications/dense_match_filling/fill_disp_wrappers.py +83 -0
  24. cars/applications/dense_match_filling/zero_padding_app.py +302 -0
  25. cars/applications/dense_matching/__init__.py +30 -0
  26. cars/applications/dense_matching/abstract_dense_matching_app.py +261 -0
  27. cars/applications/dense_matching/census_mccnn_sgm_app.py +1460 -0
  28. cars/applications/dense_matching/cpp/__init__.py +0 -0
  29. cars/applications/dense_matching/cpp/dense_matching_cpp.cpython-313-i386-linux-musl.so +0 -0
  30. cars/applications/dense_matching/cpp/dense_matching_cpp.py +94 -0
  31. cars/applications/dense_matching/cpp/includes/dense_matching.hpp +58 -0
  32. cars/applications/dense_matching/cpp/meson.build +9 -0
  33. cars/applications/dense_matching/cpp/src/bindings.cpp +13 -0
  34. cars/applications/dense_matching/cpp/src/dense_matching.cpp +207 -0
  35. cars/applications/dense_matching/dense_matching_algo.py +401 -0
  36. cars/applications/dense_matching/dense_matching_constants.py +89 -0
  37. cars/applications/dense_matching/dense_matching_wrappers.py +951 -0
  38. cars/applications/dense_matching/disparity_grid_algo.py +588 -0
  39. cars/applications/dense_matching/loaders/__init__.py +23 -0
  40. cars/applications/dense_matching/loaders/config_census_sgm_default.json +31 -0
  41. cars/applications/dense_matching/loaders/config_census_sgm_homogeneous.json +30 -0
  42. cars/applications/dense_matching/loaders/config_census_sgm_mountain_and_vegetation.json +30 -0
  43. cars/applications/dense_matching/loaders/config_census_sgm_shadow.json +30 -0
  44. cars/applications/dense_matching/loaders/config_census_sgm_sparse.json +36 -0
  45. cars/applications/dense_matching/loaders/config_census_sgm_urban.json +30 -0
  46. cars/applications/dense_matching/loaders/config_mapping.json +13 -0
  47. cars/applications/dense_matching/loaders/config_mccnn.json +28 -0
  48. cars/applications/dense_matching/loaders/global_land_cover_map.tif +0 -0
  49. cars/applications/dense_matching/loaders/pandora_loader.py +593 -0
  50. cars/applications/dsm_filling/__init__.py +32 -0
  51. cars/applications/dsm_filling/abstract_dsm_filling_app.py +101 -0
  52. cars/applications/dsm_filling/border_interpolation_app.py +270 -0
  53. cars/applications/dsm_filling/bulldozer_config/base_config.yaml +44 -0
  54. cars/applications/dsm_filling/bulldozer_filling_app.py +279 -0
  55. cars/applications/dsm_filling/exogenous_filling_app.py +333 -0
  56. cars/applications/grid_generation/__init__.py +30 -0
  57. cars/applications/grid_generation/abstract_grid_generation_app.py +142 -0
  58. cars/applications/grid_generation/epipolar_grid_generation_app.py +327 -0
  59. cars/applications/grid_generation/grid_correction_app.py +496 -0
  60. cars/applications/grid_generation/grid_generation_algo.py +388 -0
  61. cars/applications/grid_generation/grid_generation_constants.py +46 -0
  62. cars/applications/grid_generation/transform_grid.py +88 -0
  63. cars/applications/ground_truth_reprojection/__init__.py +30 -0
  64. cars/applications/ground_truth_reprojection/abstract_ground_truth_reprojection_app.py +137 -0
  65. cars/applications/ground_truth_reprojection/direct_localization_app.py +629 -0
  66. cars/applications/ground_truth_reprojection/ground_truth_reprojection_algo.py +275 -0
  67. cars/applications/point_cloud_outlier_removal/__init__.py +30 -0
  68. cars/applications/point_cloud_outlier_removal/abstract_outlier_removal_app.py +385 -0
  69. cars/applications/point_cloud_outlier_removal/outlier_removal_algo.py +392 -0
  70. cars/applications/point_cloud_outlier_removal/outlier_removal_constants.py +43 -0
  71. cars/applications/point_cloud_outlier_removal/small_components_app.py +527 -0
  72. cars/applications/point_cloud_outlier_removal/statistical_app.py +531 -0
  73. cars/applications/rasterization/__init__.py +30 -0
  74. cars/applications/rasterization/abstract_pc_rasterization_app.py +183 -0
  75. cars/applications/rasterization/rasterization_algo.py +534 -0
  76. cars/applications/rasterization/rasterization_constants.py +38 -0
  77. cars/applications/rasterization/rasterization_wrappers.py +634 -0
  78. cars/applications/rasterization/simple_gaussian_app.py +1152 -0
  79. cars/applications/resampling/__init__.py +28 -0
  80. cars/applications/resampling/abstract_resampling_app.py +187 -0
  81. cars/applications/resampling/bicubic_resampling_app.py +762 -0
  82. cars/applications/resampling/resampling_algo.py +614 -0
  83. cars/applications/resampling/resampling_constants.py +36 -0
  84. cars/applications/resampling/resampling_wrappers.py +309 -0
  85. cars/applications/sparse_matching/__init__.py +30 -0
  86. cars/applications/sparse_matching/abstract_sparse_matching_app.py +498 -0
  87. cars/applications/sparse_matching/sift_app.py +735 -0
  88. cars/applications/sparse_matching/sparse_matching_algo.py +360 -0
  89. cars/applications/sparse_matching/sparse_matching_constants.py +68 -0
  90. cars/applications/sparse_matching/sparse_matching_wrappers.py +238 -0
  91. cars/applications/triangulation/__init__.py +32 -0
  92. cars/applications/triangulation/abstract_triangulation_app.py +227 -0
  93. cars/applications/triangulation/line_of_sight_intersection_app.py +1243 -0
  94. cars/applications/triangulation/pc_transform.py +552 -0
  95. cars/applications/triangulation/triangulation_algo.py +371 -0
  96. cars/applications/triangulation/triangulation_constants.py +38 -0
  97. cars/applications/triangulation/triangulation_wrappers.py +259 -0
  98. cars/bundleadjustment.py +757 -0
  99. cars/cars.py +177 -0
  100. cars/conf/__init__.py +23 -0
  101. cars/conf/geoid/egm96.grd +0 -0
  102. cars/conf/geoid/egm96.grd.hdr +15 -0
  103. cars/conf/input_parameters.py +156 -0
  104. cars/conf/mask_cst.py +35 -0
  105. cars/core/__init__.py +23 -0
  106. cars/core/cars_logging.py +402 -0
  107. cars/core/constants.py +191 -0
  108. cars/core/constants_disparity.py +50 -0
  109. cars/core/datasets.py +140 -0
  110. cars/core/geometry/__init__.py +27 -0
  111. cars/core/geometry/abstract_geometry.py +1119 -0
  112. cars/core/geometry/shareloc_geometry.py +598 -0
  113. cars/core/inputs.py +568 -0
  114. cars/core/outputs.py +176 -0
  115. cars/core/preprocessing.py +722 -0
  116. cars/core/projection.py +843 -0
  117. cars/core/roi_tools.py +215 -0
  118. cars/core/tiling.py +774 -0
  119. cars/core/utils.py +164 -0
  120. cars/data_structures/__init__.py +23 -0
  121. cars/data_structures/cars_dataset.py +1541 -0
  122. cars/data_structures/cars_dict.py +74 -0
  123. cars/data_structures/corresponding_tiles_tools.py +186 -0
  124. cars/data_structures/dataframe_converter.py +185 -0
  125. cars/data_structures/format_transformation.py +297 -0
  126. cars/devibrate.py +689 -0
  127. cars/extractroi.py +264 -0
  128. cars/orchestrator/__init__.py +23 -0
  129. cars/orchestrator/achievement_tracker.py +125 -0
  130. cars/orchestrator/cluster/__init__.py +37 -0
  131. cars/orchestrator/cluster/abstract_cluster.py +244 -0
  132. cars/orchestrator/cluster/abstract_dask_cluster.py +375 -0
  133. cars/orchestrator/cluster/dask_cluster_tools.py +103 -0
  134. cars/orchestrator/cluster/dask_config/README.md +94 -0
  135. cars/orchestrator/cluster/dask_config/dask.yaml +21 -0
  136. cars/orchestrator/cluster/dask_config/distributed.yaml +70 -0
  137. cars/orchestrator/cluster/dask_config/jobqueue.yaml +26 -0
  138. cars/orchestrator/cluster/dask_config/reference_confs/dask-schema.yaml +137 -0
  139. cars/orchestrator/cluster/dask_config/reference_confs/dask.yaml +26 -0
  140. cars/orchestrator/cluster/dask_config/reference_confs/distributed-schema.yaml +1009 -0
  141. cars/orchestrator/cluster/dask_config/reference_confs/distributed.yaml +273 -0
  142. cars/orchestrator/cluster/dask_config/reference_confs/jobqueue.yaml +212 -0
  143. cars/orchestrator/cluster/dask_jobqueue_utils.py +204 -0
  144. cars/orchestrator/cluster/local_dask_cluster.py +116 -0
  145. cars/orchestrator/cluster/log_wrapper.py +1075 -0
  146. cars/orchestrator/cluster/mp_cluster/__init__.py +27 -0
  147. cars/orchestrator/cluster/mp_cluster/mp_factorizer.py +212 -0
  148. cars/orchestrator/cluster/mp_cluster/mp_objects.py +535 -0
  149. cars/orchestrator/cluster/mp_cluster/mp_tools.py +93 -0
  150. cars/orchestrator/cluster/mp_cluster/mp_wrapper.py +505 -0
  151. cars/orchestrator/cluster/mp_cluster/multiprocessing_cluster.py +873 -0
  152. cars/orchestrator/cluster/mp_cluster/multiprocessing_profiler.py +399 -0
  153. cars/orchestrator/cluster/pbs_dask_cluster.py +207 -0
  154. cars/orchestrator/cluster/sequential_cluster.py +139 -0
  155. cars/orchestrator/cluster/slurm_dask_cluster.py +234 -0
  156. cars/orchestrator/orchestrator.py +905 -0
  157. cars/orchestrator/orchestrator_constants.py +29 -0
  158. cars/orchestrator/registry/__init__.py +23 -0
  159. cars/orchestrator/registry/abstract_registry.py +143 -0
  160. cars/orchestrator/registry/compute_registry.py +106 -0
  161. cars/orchestrator/registry/id_generator.py +116 -0
  162. cars/orchestrator/registry/replacer_registry.py +213 -0
  163. cars/orchestrator/registry/saver_registry.py +363 -0
  164. cars/orchestrator/registry/unseen_registry.py +118 -0
  165. cars/orchestrator/tiles_profiler.py +279 -0
  166. cars/pipelines/__init__.py +26 -0
  167. cars/pipelines/conf_resolution/conf_final_resolution.yaml +5 -0
  168. cars/pipelines/conf_resolution/conf_first_resolution.yaml +2 -0
  169. cars/pipelines/conf_resolution/conf_intermediate_resolution.yaml +2 -0
  170. cars/pipelines/default/__init__.py +26 -0
  171. cars/pipelines/default/default_pipeline.py +786 -0
  172. cars/pipelines/parameters/__init__.py +0 -0
  173. cars/pipelines/parameters/advanced_parameters.py +417 -0
  174. cars/pipelines/parameters/advanced_parameters_constants.py +69 -0
  175. cars/pipelines/parameters/application_parameters.py +71 -0
  176. cars/pipelines/parameters/depth_map_inputs.py +0 -0
  177. cars/pipelines/parameters/dsm_inputs.py +918 -0
  178. cars/pipelines/parameters/dsm_inputs_constants.py +25 -0
  179. cars/pipelines/parameters/output_constants.py +52 -0
  180. cars/pipelines/parameters/output_parameters.py +454 -0
  181. cars/pipelines/parameters/sensor_inputs.py +842 -0
  182. cars/pipelines/parameters/sensor_inputs_constants.py +49 -0
  183. cars/pipelines/parameters/sensor_loaders/__init__.py +29 -0
  184. cars/pipelines/parameters/sensor_loaders/basic_classif_loader.py +86 -0
  185. cars/pipelines/parameters/sensor_loaders/basic_image_loader.py +98 -0
  186. cars/pipelines/parameters/sensor_loaders/pivot_classif_loader.py +90 -0
  187. cars/pipelines/parameters/sensor_loaders/pivot_image_loader.py +105 -0
  188. cars/pipelines/parameters/sensor_loaders/sensor_loader.py +93 -0
  189. cars/pipelines/parameters/sensor_loaders/sensor_loader_template.py +71 -0
  190. cars/pipelines/parameters/sensor_loaders/slurp_classif_loader.py +86 -0
  191. cars/pipelines/pipeline.py +119 -0
  192. cars/pipelines/pipeline_constants.py +31 -0
  193. cars/pipelines/pipeline_template.py +139 -0
  194. cars/pipelines/unit/__init__.py +26 -0
  195. cars/pipelines/unit/unit_pipeline.py +2850 -0
  196. cars/starter.py +167 -0
  197. cars-1.0.0rc1.dist-info/METADATA +292 -0
  198. cars-1.0.0rc1.dist-info/RECORD +202 -0
  199. cars-1.0.0rc1.dist-info/WHEEL +5 -0
  200. cars-1.0.0rc1.dist-info/entry_points.txt +8 -0
  201. cars.libs/libgcc_s-1257a076.so.1 +0 -0
  202. cars.libs/libstdc++-0530927c.so.6.0.32 +0 -0
@@ -0,0 +1,655 @@
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 dichotomic dem generation application class.
23
+ """
24
+ # Standard library
25
+ import logging
26
+ import math
27
+ import os
28
+ import shutil
29
+
30
+ # Third-party imports
31
+ import numpy as np
32
+ import rasterio as rio
33
+ import skimage
34
+ from json_checker import And, Checker, Or
35
+ from rasterio.enums import Resampling
36
+ from rasterio.warp import reproject
37
+
38
+ # CARS imports - Applications
39
+ from cars.applications import application_constants
40
+ from cars.applications.dem_generation.abstract_dem_generation_app import (
41
+ DemGeneration,
42
+ )
43
+ from cars.applications.dem_generation.bulldozer_memory import (
44
+ can_allocate_shared_memory,
45
+ )
46
+ from cars.applications.dem_generation.dem_generation_algo import (
47
+ launch_bulldozer,
48
+ )
49
+ from cars.applications.dem_generation.dem_generation_wrappers import (
50
+ compute_stats,
51
+ downsample_dem,
52
+ edit_transform,
53
+ fit_initial_elevation_on_dem_median,
54
+ reproject_dem,
55
+ reverse_dem,
56
+ )
57
+
58
+ # CARS imports - Core
59
+ from cars.core import inputs
60
+ from cars.orchestrator.cluster.log_wrapper import cars_profile
61
+
62
+
63
+ class BulldozerDem(DemGeneration, short_name="bulldozer_on_raster"):
64
+ """
65
+ Rasterization
66
+ """
67
+
68
+ # pylint: disable=too-many-instance-attributes
69
+
70
+ def __init__(self, scaling_coeff, conf=None):
71
+ """
72
+ Init function of Rasterization
73
+
74
+ :param scaling_coeff: scaling factor for resolution
75
+ :type scaling_coeff: float
76
+ :param conf: configuration for Rasterization
77
+ :return: an application_to_use object
78
+ """
79
+ super().__init__(scaling_coeff, conf=conf)
80
+
81
+ # check conf
82
+ self.used_method = self.used_config["method"]
83
+ self.resolution = self.used_config["resolution"]
84
+ self.margin = self.used_config["margin"]
85
+ height_margin = self.used_config["height_margin"]
86
+ if isinstance(height_margin, list):
87
+ self.min_height_margin = height_margin[0]
88
+ self.max_height_margin = height_margin[1]
89
+ else:
90
+ self.min_height_margin = height_margin
91
+ self.max_height_margin = height_margin
92
+ self.morphological_filters_size = self.used_config[
93
+ "morphological_filters_size"
94
+ ]
95
+ self.preprocessing_median_filter_size = self.used_config[
96
+ "preprocessing_median_filter_size"
97
+ ]
98
+ self.postprocessing_median_filter_size = self.used_config[
99
+ "postprocessing_median_filter_size"
100
+ ]
101
+ self.dem_median_downscale = self.used_config["dem_median_downscale"]
102
+ self.dem_min_max_downscale = self.used_config["dem_min_max_downscale"]
103
+ self.fillnodata_max_search_distance = self.used_config[
104
+ "fillnodata_max_search_distance"
105
+ ]
106
+ self.min_dem = self.used_config["min_dem"]
107
+ self.max_dem = self.used_config["max_dem"]
108
+ self.bulldozer_max_object_size = self.used_config[
109
+ "bulldozer_max_object_size"
110
+ ]
111
+ self.disable_bulldozer = self.used_config["disable_bulldozer"]
112
+ self.compute_stats = self.used_config["compute_stats"]
113
+ self.coregistration = self.used_config["coregistration"]
114
+ self.coregistration_max_shift = self.used_config[
115
+ "coregistration_max_shift"
116
+ ]
117
+ self.save_intermediate_data = self.used_config["save_intermediate_data"]
118
+
119
+ # Init orchestrator
120
+ self.orchestrator = None
121
+
122
+ def check_conf(self, conf):
123
+ """
124
+ Check configuration
125
+
126
+ :param conf: configuration to check
127
+ :type conf: dict
128
+
129
+ :return: overloaded configuration
130
+ :rtype: dict
131
+
132
+ """
133
+
134
+ # init conf
135
+ if conf is not None:
136
+ overloaded_conf = conf.copy()
137
+ else:
138
+ conf = {}
139
+ overloaded_conf = {}
140
+
141
+ # Overload conf
142
+ overloaded_conf["method"] = conf.get("method", "bulldozer_on_raster")
143
+ overloaded_conf["resolution"] = conf.get(
144
+ "resolution", float(self.scaling_coeff * 0.5)
145
+ )
146
+ overloaded_conf["margin"] = conf.get(
147
+ "margin", [0.1, float(math.sqrt(self.scaling_coeff) * 500)]
148
+ )
149
+ overloaded_conf["morphological_filters_size"] = conf.get(
150
+ "morphological_filters_size", 30
151
+ )
152
+ overloaded_conf["preprocessing_median_filter_size"] = conf.get(
153
+ "preprocessing_median_filter_size", 5
154
+ )
155
+ overloaded_conf["postprocessing_median_filter_size"] = conf.get(
156
+ "postprocessing_median_filter_size", 7
157
+ )
158
+ overloaded_conf["dem_median_downscale"] = conf.get(
159
+ "dem_median_downscale", 10
160
+ )
161
+ overloaded_conf["dem_min_max_downscale"] = conf.get(
162
+ "dem_min_max_downscale", 2
163
+ )
164
+ overloaded_conf["fillnodata_max_search_distance"] = conf.get(
165
+ "fillnodata_max_search_distance", 50
166
+ )
167
+ overloaded_conf["min_dem"] = conf.get("min_dem", -500)
168
+ overloaded_conf["max_dem"] = conf.get("max_dem", 1000)
169
+ overloaded_conf["height_margin"] = conf.get(
170
+ "height_margin", float(math.sqrt(self.scaling_coeff) * 20)
171
+ )
172
+ overloaded_conf["bulldozer_max_object_size"] = conf.get(
173
+ "bulldozer_max_object_size", 8
174
+ )
175
+ overloaded_conf["disable_bulldozer"] = conf.get(
176
+ "disable_bulldozer", False
177
+ )
178
+ if not overloaded_conf["disable_bulldozer"]:
179
+ can_allocate_sm, log_message = can_allocate_shared_memory()
180
+ if not can_allocate_sm:
181
+ logging.info(log_message)
182
+ logging.info(
183
+ "Dem generation disable_bulldozer parameter is set to True"
184
+ )
185
+ overloaded_conf["disable_bulldozer"] = True
186
+ overloaded_conf["compute_stats"] = conf.get("compute_stats", True)
187
+ overloaded_conf["coregistration"] = conf.get("coregistration", True)
188
+ overloaded_conf["coregistration_max_shift"] = conf.get(
189
+ "coregistration_max_shift", 180
190
+ )
191
+ overloaded_conf[application_constants.SAVE_INTERMEDIATE_DATA] = (
192
+ conf.get(application_constants.SAVE_INTERMEDIATE_DATA, False)
193
+ )
194
+
195
+ rectification_schema = {
196
+ "method": str,
197
+ "resolution": And(Or(float, int), lambda x: x > 0),
198
+ application_constants.SAVE_INTERMEDIATE_DATA: bool,
199
+ "margin": [Or(float, int)],
200
+ "morphological_filters_size": And(int, lambda x: x > 0),
201
+ "preprocessing_median_filter_size": And(int, lambda x: x > 0),
202
+ "postprocessing_median_filter_size": And(int, lambda x: x > 0),
203
+ "dem_median_downscale": And(int, lambda x: x > 0),
204
+ "dem_min_max_downscale": And(int, lambda x: x > 0),
205
+ "fillnodata_max_search_distance": And(int, lambda x: x > 0),
206
+ "min_dem": And(Or(int, float), lambda x: x < 0),
207
+ "max_dem": And(Or(int, float), lambda x: x > 0),
208
+ "height_margin": Or(list, float, int),
209
+ "bulldozer_max_object_size": And(int, lambda x: x > 0),
210
+ "disable_bulldozer": bool,
211
+ "compute_stats": bool,
212
+ "coregistration": bool,
213
+ "coregistration_max_shift": And(Or(int, float), lambda x: x > 0),
214
+ "save_intermediate_data": bool,
215
+ }
216
+
217
+ # Check conf
218
+ checker = Checker(rectification_schema)
219
+ checker.validate(overloaded_conf)
220
+
221
+ return overloaded_conf
222
+
223
+ @cars_profile(name="DEM Generation")
224
+ def run( # pylint: disable=too-many-positional-arguments # noqa: C901
225
+ self,
226
+ dsm_file_name,
227
+ output_dir,
228
+ dem_min_file_name,
229
+ dem_max_file_name,
230
+ dem_median_file_name,
231
+ input_geoid,
232
+ output_geoid,
233
+ initial_elevation=None,
234
+ default_alt=0,
235
+ cars_orchestrator=None,
236
+ ):
237
+ """
238
+ Run dichotomic dem generation using matches
239
+
240
+ :param dsm_file_name: The dsm path
241
+ :type dsm_file_name: CarsDataset
242
+ :param output_dir: directory to save dem
243
+ :type output_dir: str
244
+ :param dem_min_file_name: path of dem_min
245
+ :type dem_min_file_name: str
246
+ :param dem_max_file_name: path of dem_max
247
+ :type dem_max_file_name: str
248
+ :param dem_median_file_name: path of dem_median
249
+ :type dem_median_file_name: str
250
+ :param input_geoid: input geoid path
251
+ :param output_geoid: output geoid path
252
+ :param dem_roi_to_use: dem roi polygon to use as roi
253
+
254
+ :return: dem data computed with mean, min and max.
255
+ dem is also saved in disk, and paths are available in attributes.
256
+ (DEM_MEDIAN_PATH, DEM_MIN_PATH, DEM_MAX_PATH)
257
+ :rtype: CarsDataset
258
+ """
259
+
260
+ # Generate point cloud
261
+ epsg = 4326
262
+
263
+ # Optimize the case when input and output geoid are the same
264
+ if output_geoid is True:
265
+ input_geoid = False
266
+ output_geoid = False
267
+
268
+ resolution_in_meters = self.resolution
269
+
270
+ # rasterize point cloud
271
+ dem_min_path = dem_min_file_name
272
+ if dem_min_path is None:
273
+ # File is not part of the official product, write it in dump_dir
274
+ dem_min_path = os.path.join(output_dir, "dem_min.tif")
275
+
276
+ dem_max_path = dem_max_file_name
277
+ if dem_max_path is None:
278
+ # File is not part of the official product, write it in dump_dir
279
+ dem_max_path = os.path.join(output_dir, "dem_max.tif")
280
+
281
+ dem_median_path_out = dem_median_file_name
282
+ if dem_median_path_out is None:
283
+ # File is not part of the official product, write it in dump_dir
284
+ dem_median_path_out = os.path.join(output_dir, "dem_median.tif")
285
+
286
+ dem_median_path_in = dsm_file_name
287
+
288
+ dem_epsg = inputs.rasterio_get_epsg(dsm_file_name)
289
+
290
+ if dem_epsg != epsg:
291
+ dem_median_path_in = os.path.join(output_dir, "dem_median.tif")
292
+ reproject_dem(dsm_file_name, "EPSG:4326", dem_median_path_in)
293
+
294
+ with rio.open(dem_median_path_in) as src:
295
+ dem = src.read(1)
296
+ profile = src.profile
297
+ nodata = src.nodata
298
+
299
+ if self.save_intermediate_data:
300
+ raw_dsm_path = os.path.join(output_dir, "raw_dsm.tif")
301
+ shutil.copy2(dem_median_path_in, raw_dsm_path)
302
+
303
+ dem_data = dem
304
+ mask = dem_data == nodata
305
+
306
+ # fill nodata
307
+ max_search_distance = (
308
+ self.fillnodata_max_search_distance
309
+ + self.morphological_filters_size
310
+ )
311
+ # a margin is added for following morphological operations
312
+ # pixels further than self.fillnodata_max_search_distance
313
+ # will later be turned into nodata by eroded_mask
314
+ dem_data = rio.fill.fillnodata(
315
+ dem_data,
316
+ mask=~mask,
317
+ max_search_distance=max_search_distance,
318
+ )
319
+
320
+ not_filled_pixels = dem_data == nodata
321
+
322
+ # Add geoid
323
+ if input_geoid:
324
+ with rio.open(input_geoid) as in_geoid:
325
+ # Reproject the geoid data to match the DSM
326
+ input_geoid_data = np.empty(
327
+ dem_data.shape, dtype=in_geoid.dtypes[0]
328
+ )
329
+
330
+ logging.info("Reprojection of geoid data")
331
+
332
+ reproject(
333
+ source=rio.band(in_geoid, 1),
334
+ destination=input_geoid_data,
335
+ src_transform=in_geoid.transform,
336
+ src_crs=in_geoid.crs,
337
+ dst_transform=profile["transform"],
338
+ dst_crs=profile["crs"],
339
+ resampling=Resampling.bilinear,
340
+ )
341
+ dem_data -= input_geoid_data
342
+
343
+ if output_geoid:
344
+ with rio.open(input_geoid) as in_geoid:
345
+ # Reproject the geoid data to match the DSM
346
+ input_geoid_data = np.empty(
347
+ dem_data.shape, dtype=in_geoid.dtypes[0]
348
+ )
349
+
350
+ logging.info("Reprojection of geoid data")
351
+
352
+ reproject(
353
+ source=rio.band(in_geoid, 1),
354
+ destination=input_geoid_data,
355
+ src_transform=in_geoid.transform,
356
+ src_crs=in_geoid.crs,
357
+ dst_transform=profile["transform"],
358
+ dst_crs=profile["crs"],
359
+ resampling=Resampling.bilinear,
360
+ )
361
+ dem_data += input_geoid_data
362
+
363
+ # apply morphological filters and height margin
364
+ footprint = skimage.morphology.disk(
365
+ self.morphological_filters_size // 2, decomposition="sequence"
366
+ )
367
+ logging.info("Generation of DEM min")
368
+ dem_data[not_filled_pixels] = -nodata
369
+ dem_min = (
370
+ skimage.morphology.erosion(dem_data, footprint=footprint)
371
+ - self.min_height_margin
372
+ )
373
+ dem_data[not_filled_pixels] = nodata
374
+ logging.info("Generation of DEM max")
375
+ dem_max = (
376
+ skimage.morphology.dilation(dem_data, footprint=footprint)
377
+ + self.max_height_margin
378
+ )
379
+ logging.info("Generation of DEM median")
380
+ dem_median = skimage.filters.median(
381
+ dem_data,
382
+ footprint=np.ones(
383
+ (
384
+ self.preprocessing_median_filter_size,
385
+ self.preprocessing_median_filter_size,
386
+ )
387
+ ),
388
+ )
389
+
390
+ footprint = skimage.morphology.disk(
391
+ self.fillnodata_max_search_distance // 2, decomposition="sequence"
392
+ )
393
+ eroded_mask = skimage.morphology.binary_erosion(
394
+ mask, footprint=footprint
395
+ )
396
+
397
+ dem_min[eroded_mask] = nodata
398
+ dem_median[eroded_mask] = nodata
399
+ dem_max[eroded_mask] = nodata
400
+
401
+ # Rectify pixels where DEM min < DEM median + min_depth
402
+ dem_min = np.where(
403
+ dem_min - dem_median < self.min_dem,
404
+ dem_median + self.min_dem,
405
+ dem_min,
406
+ )
407
+ # Rectify pixels where DEM max > DEM median + max_height
408
+ dem_max = np.where(
409
+ dem_max - dem_median > self.max_dem,
410
+ dem_median + self.max_dem,
411
+ dem_max,
412
+ )
413
+
414
+ # save
415
+ with rio.open(dem_min_path, "w", **profile) as out_dem:
416
+ out_dem.write(dem_min, 1)
417
+ with rio.open(dem_median_path_out, "w", **profile) as out_dem:
418
+ out_dem.write(dem_median, 1)
419
+ with rio.open(dem_max_path, "w", **profile) as out_dem:
420
+ out_dem.write(dem_max, 1)
421
+ dem_filled_path = os.path.join(output_dir, "dem_filled.tif")
422
+ with rio.open(dem_filled_path, "w", **profile) as out_dem:
423
+ out_dem.write(dem_data, 1)
424
+
425
+ if self.save_intermediate_data:
426
+ intermediate_dem_min_path = os.path.join(
427
+ output_dir, "dem_min_before_downsampling.tif"
428
+ )
429
+ shutil.copy2(dem_min_path, intermediate_dem_min_path)
430
+ intermediate_dem_max_path = os.path.join(
431
+ output_dir, "dem_max_before_downsampling.tif"
432
+ )
433
+ shutil.copy2(dem_max_path, intermediate_dem_max_path)
434
+
435
+ intermediate_dem_median_path = os.path.join(
436
+ output_dir, "dem_median_before_downsampling.tif"
437
+ )
438
+ shutil.copy2(dem_median_path_out, intermediate_dem_median_path)
439
+
440
+ # Downsample dems
441
+ downsample_dem(
442
+ dem_median_path_out,
443
+ scale=self.dem_median_downscale,
444
+ interpolator="median",
445
+ median_filter_size=self.postprocessing_median_filter_size,
446
+ default_alt=default_alt,
447
+ )
448
+
449
+ downsample_dem(
450
+ dem_min_path,
451
+ scale=self.dem_min_max_downscale,
452
+ interpolator="min",
453
+ default_alt=default_alt,
454
+ )
455
+
456
+ downsample_dem(
457
+ dem_max_path,
458
+ scale=self.dem_min_max_downscale,
459
+ interpolator="max",
460
+ default_alt=default_alt,
461
+ )
462
+
463
+ downsample_dem(
464
+ dem_filled_path,
465
+ scale=self.dem_min_max_downscale,
466
+ interpolator="nearest",
467
+ default_alt=default_alt,
468
+ )
469
+
470
+ if self.save_intermediate_data:
471
+ intermediate_dem_min_path = os.path.join(
472
+ output_dir, "dem_min_before_bulldozer.tif"
473
+ )
474
+ shutil.copy2(dem_min_path, intermediate_dem_min_path)
475
+ intermediate_dem_max_path = os.path.join(
476
+ output_dir, "dem_max_before_bulldozer.tif"
477
+ )
478
+ shutil.copy2(dem_max_path, intermediate_dem_max_path)
479
+
480
+ if not self.disable_bulldozer:
481
+ dem_min_max_res = resolution_in_meters * self.dem_min_max_downscale
482
+ # Launch Bulldozer on dem min
483
+ saved_transform = edit_transform(
484
+ dem_min_path, resolution=dem_min_max_res
485
+ )
486
+ logging.info("Launch Bulldozer on DEM min")
487
+ temp_output_path = launch_bulldozer(
488
+ dem_min_path,
489
+ os.path.join(output_dir, "dem_min_bulldozer"),
490
+ cars_orchestrator,
491
+ self.bulldozer_max_object_size,
492
+ )
493
+ if temp_output_path is not None:
494
+ shutil.copy2(temp_output_path, dem_min_path)
495
+ edit_transform(dem_min_path, transform=saved_transform)
496
+
497
+ # Inverse dem max and launch bulldozer
498
+ saved_transform = edit_transform(
499
+ dem_max_path, resolution=dem_min_max_res
500
+ )
501
+ reverse_dem(dem_max_path)
502
+ logging.info("Launch Bulldozer on DEM max")
503
+ temp_output_path = launch_bulldozer(
504
+ dem_max_path,
505
+ os.path.join(output_dir, "dem_max_bulldozer"),
506
+ cars_orchestrator,
507
+ self.bulldozer_max_object_size,
508
+ )
509
+ if temp_output_path is not None:
510
+ shutil.copy2(temp_output_path, dem_max_path)
511
+ reverse_dem(dem_max_path)
512
+ edit_transform(dem_max_path, transform=saved_transform)
513
+
514
+ else:
515
+ # Fill nodata
516
+ def fill_dem_nodata(data):
517
+ """
518
+ Fill nodata
519
+ """
520
+ # Apply max_iter times rasterio fill nodata
521
+ max_iter = 5
522
+ current_iter = 0
523
+ while current_iter < max_iter:
524
+ data = rio.fill.fillnodata(
525
+ data,
526
+ mask=data != nodata,
527
+ max_search_distance=max_search_distance,
528
+ )
529
+ current_iter += 1
530
+ if np.sum(data == nodata) == 0:
531
+ # no nodata left, exit loop
532
+ continue
533
+ # fill the rest with median
534
+ median_value = np.nanmedian(data)
535
+ data[data == nodata] = median_value
536
+ return data
537
+
538
+ with rio.open(dem_min_path, "r+", **profile) as out_dem:
539
+ dem_min = out_dem.read()
540
+ dem_min = fill_dem_nodata(dem_min)
541
+ out_dem.write(dem_min)
542
+ with rio.open(dem_median_path_out, "r+", **profile) as out_dem:
543
+ dem_median = out_dem.read()
544
+ dem_median = fill_dem_nodata(dem_median)
545
+ out_dem.write(dem_median)
546
+ with rio.open(dem_max_path, "r+", **profile) as out_dem:
547
+ dem_max = out_dem.read()
548
+ dem_max = fill_dem_nodata(dem_max)
549
+ out_dem.write(dem_max)
550
+
551
+ # Check DEM min and max
552
+ with rio.open(dem_min_path, "r") as in_dem:
553
+ dem_min = in_dem.read()
554
+ dem_min_metadata = in_dem.meta
555
+ nodata = in_dem.nodata
556
+ with rio.open(dem_max_path, "r") as in_dem:
557
+ dem_max = in_dem.read()
558
+ dem_max_metadata = in_dem.meta
559
+ with rio.open(dem_filled_path, "r") as in_dem:
560
+ dem_data = in_dem.read()
561
+ dem_data[dem_data == nodata] = np.nan
562
+ dem_min[dem_min == nodata] = np.nan
563
+ dem_max[dem_max == nodata] = np.nan
564
+ if self.compute_stats:
565
+ diff = dem_data - dem_min
566
+ diff = diff[dem_data != 0]
567
+ logging.info(
568
+ "Statistics of difference between subsampled "
569
+ "DSM and DEM min (in meters)"
570
+ )
571
+ compute_stats(diff)
572
+
573
+ diff = dem_max - dem_data
574
+ diff = diff[dem_data != 0]
575
+ logging.info(
576
+ "Statistics of difference between DEM max "
577
+ "and subsampled DSM (in meters)"
578
+ )
579
+ compute_stats(diff)
580
+
581
+ diff = dem_max - dem_min
582
+ diff = diff[dem_data != 0]
583
+ logging.info(
584
+ "Statistics of difference between DEM max "
585
+ "and DEM min (in meters)"
586
+ )
587
+ compute_stats(diff)
588
+
589
+ # Rectify pixels where DEM min > DEM - margin
590
+ dem_min = np.where(
591
+ dem_min > dem_data - self.min_height_margin,
592
+ dem_data - self.min_height_margin,
593
+ dem_min,
594
+ )
595
+ # Rectify pixels where DEM max < DEM + margin
596
+ dem_max = np.where(
597
+ dem_max < dem_data + self.max_height_margin,
598
+ dem_data + self.max_height_margin,
599
+ dem_max,
600
+ )
601
+
602
+ # Rectify pixels where DEM min > DEM max - margin, to ensure that
603
+ # DEM min < DEM max even on filled pixels
604
+ dem_min = np.where(
605
+ dem_min > dem_max - self.min_height_margin,
606
+ dem_max - self.min_height_margin,
607
+ dem_min,
608
+ )
609
+
610
+ with rio.open(dem_min_path, "w", **dem_min_metadata) as out_dem:
611
+ out_dem.write(dem_min)
612
+ with rio.open(dem_max_path, "w", **dem_max_metadata) as out_dem:
613
+ out_dem.write(dem_max)
614
+
615
+ paths = {
616
+ "dem_median": dem_median_path_out,
617
+ "dem_min": dem_min_path,
618
+ "dem_max": dem_max_path,
619
+ }
620
+
621
+ # after saving, fit initial elevation if required
622
+ if initial_elevation is not None and self.coregistration:
623
+ initial_elevation_out_path = os.path.join(
624
+ output_dir, "initial_elevation_fit.tif"
625
+ )
626
+
627
+ coreg_offsets = fit_initial_elevation_on_dem_median(
628
+ initial_elevation,
629
+ dem_median_path_out,
630
+ initial_elevation_out_path,
631
+ )
632
+
633
+ coreg_info = {
634
+ application_constants.APPLICATION_TAG: {
635
+ "dem_generation": {"coregistration": coreg_offsets}
636
+ }
637
+ }
638
+
639
+ # save the coreg shift info in cars's main orchestrator
640
+ cars_orchestrator.update_out_info(coreg_info)
641
+
642
+ if (
643
+ coreg_offsets is None
644
+ or abs(coreg_offsets["shift_x"]) > self.coregistration_max_shift
645
+ or abs(coreg_offsets["shift_y"]) > self.coregistration_max_shift
646
+ ):
647
+ logging.warning(
648
+ "The initial elevation will be used as-is because "
649
+ "coregistration failed or gave inconsistent results"
650
+ )
651
+ return dem, paths, None
652
+
653
+ return dem, paths, initial_elevation_out_path
654
+
655
+ return dem, paths, None