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