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,26 @@
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
+ CARS formatting pipeline module init file
23
+ """
24
+
25
+ # Cars imports
26
+ from cars.pipelines.formatting import formatting # noqa: F401
@@ -0,0 +1,190 @@
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
+ # pylint: disable=too-many-lines
22
+ # attribute-defined-outside-init is disabled so that we can create and use
23
+ # attributes however we need, to stick to the "everything is attribute" logic
24
+ # introduced in issue#895
25
+ # pylint: disable=attribute-defined-outside-init
26
+ # pylint: disable=too-many-nested-blocks
27
+ """
28
+ CARS default pipeline class file
29
+ """
30
+ # Standard imports
31
+ from __future__ import print_function
32
+
33
+ import os
34
+ import shutil
35
+ from pathlib import Path
36
+
37
+ from json_checker import Checker
38
+
39
+ from cars.core import cars_logging
40
+
41
+ # CARS imports
42
+ from cars.pipelines.parameters import output_constants as out_cst
43
+ from cars.pipelines.pipeline import Pipeline
44
+ from cars.pipelines.pipeline_constants import (
45
+ INPUT,
46
+ OUTPUT,
47
+ )
48
+ from cars.pipelines.pipeline_template import PipelineTemplate
49
+
50
+
51
+ @Pipeline.register(
52
+ "formatting",
53
+ )
54
+ class FormattingPipeline(PipelineTemplate):
55
+ """
56
+ DefaultPipeline
57
+ """
58
+
59
+ # pylint: disable=too-many-instance-attributes
60
+
61
+ def __init__(self, conf, config_dir=None): # noqa: C901
62
+ """
63
+ Creates pipeline
64
+
65
+ :param pipeline_name: name of the pipeline.
66
+ :type pipeline_name: str
67
+ :param cfg: configuration {'matching_cost_method': value}
68
+ :type cfg: dictionary
69
+ :param config_dir: path to dir containing json or yaml file
70
+ :type config_dir: str
71
+ """
72
+
73
+ self.config_dir = config_dir
74
+ # Transform relative path to absolute path
75
+ if config_dir is not None:
76
+ config_dir = os.path.abspath(config_dir)
77
+
78
+ # Check global conf
79
+ self.check_global_schema(conf)
80
+
81
+ conf[INPUT] = self.check_inputs(conf)
82
+
83
+ conf[OUTPUT] = self.check_output(conf[OUTPUT])
84
+
85
+ self.used_conf = {}
86
+ self.used_conf[INPUT] = conf[INPUT]
87
+ self.used_conf[OUTPUT] = conf[OUTPUT]
88
+
89
+ self.out_dir = conf[OUTPUT][out_cst.OUT_DIRECTORY]
90
+
91
+ def check_inputs(self, conf, config_json_dir=None):
92
+ """
93
+ Check the inputs
94
+ """
95
+
96
+ input_conf = conf.get(INPUT, {})
97
+
98
+ overloaded_conf = input_conf.copy()
99
+
100
+ overloaded_conf["input_path"] = input_conf.get("input_path", None)
101
+
102
+ formatting_schema_input = {
103
+ "input_path": str,
104
+ }
105
+
106
+ checker_input = Checker(formatting_schema_input)
107
+ checker_input.validate(overloaded_conf)
108
+
109
+ return overloaded_conf
110
+
111
+ def check_output(self, conf):
112
+ """
113
+ Check the inputs
114
+ """
115
+
116
+ input_conf = conf.get(INPUT, {})
117
+
118
+ overloaded_conf = input_conf.copy()
119
+
120
+ overloaded_conf["directory"] = conf.get("directory", None)
121
+
122
+ formatting_schema_output = {"directory": str}
123
+
124
+ checker_input = Checker(formatting_schema_output)
125
+ checker_input.validate(overloaded_conf)
126
+
127
+ return overloaded_conf
128
+
129
+ def check_applications(self, conf):
130
+ """
131
+ Check applications
132
+ """
133
+
134
+ def move_replace_files_only(
135
+ self, source_dir: Path, destination_dir: Path, check=True
136
+ ):
137
+ """
138
+ Replace files in dsm directory
139
+ """
140
+ destination_dir.mkdir(parents=True, exist_ok=True)
141
+
142
+ for element in source_dir.iterdir():
143
+ if (
144
+ element.name in ["dsm", "depth_map", "point_cloud", "logs"]
145
+ or not check
146
+ ):
147
+ dest = destination_dir / element.name
148
+
149
+ if element.is_dir():
150
+ self.move_replace_files_only(element, dest, check=False)
151
+ else:
152
+ if dest.exists():
153
+ dest.unlink()
154
+ shutil.move(str(element), str(dest))
155
+
156
+ def move_replace_dir(self, source_dir: Path, destination_dir: Path):
157
+ """
158
+ replace directory
159
+ """
160
+ for element in source_dir.iterdir():
161
+ if element.name in ["dsm", "depth_map", "point_cloud"]:
162
+ dest = destination_dir / element.name
163
+
164
+ if dest.exists():
165
+ if dest.is_dir():
166
+ shutil.rmtree(dest)
167
+ else:
168
+ dest.unlink()
169
+
170
+ shutil.move(str(element), str(dest))
171
+
172
+ def run(self, surface_modeling_dir):
173
+ """
174
+ Run the formatting pipeline
175
+ """
176
+ cars_logging.add_progress_message("Starting formatting pipeline")
177
+
178
+ source_dir = Path(self.used_conf[INPUT]["input_path"])
179
+ destination_dir = Path(self.used_conf[OUTPUT]["directory"])
180
+
181
+ if surface_modeling_dir is not None:
182
+ if (
183
+ source_dir != Path(surface_modeling_dir)
184
+ and surface_modeling_dir is not None
185
+ ):
186
+ self.move_replace_dir(
187
+ Path(surface_modeling_dir), destination_dir
188
+ )
189
+
190
+ self.move_replace_files_only(source_dir, destination_dir)
@@ -0,0 +1,26 @@
1
+ #!/usr/bin/env python
2
+ # coding: utf8
3
+ #
4
+ # Copyright (c) 2025 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
+ CARS tie points pipeline module init file
23
+ """
24
+
25
+ # Cars imports
26
+ from cars.pipelines.merging import merging # noqa: F401
@@ -0,0 +1,439 @@
1
+ #!/usr/bin/env python
2
+ # coding: utf8
3
+ #
4
+ # Copyright (c) 2025 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
+ CARS merging pipeline class file
23
+ """
24
+
25
+ import os
26
+
27
+ from json_checker import Checker, OptionalKey, Or
28
+
29
+ from cars.applications.application import Application
30
+ from cars.core import preprocessing, roi_tools
31
+ from cars.core.inputs import rasterio_get_epsg
32
+ from cars.core.utils import safe_makedirs
33
+ from cars.orchestrator import orchestrator
34
+ from cars.pipelines.parameters import advanced_parameters_constants as adv_cst
35
+ from cars.pipelines.parameters import dsm_inputs
36
+ from cars.pipelines.parameters import dsm_inputs_constants as dsm_cst
37
+ from cars.pipelines.parameters import output_constants as out_cst
38
+ from cars.pipelines.parameters import sensor_inputs_constants as sens_cst
39
+ from cars.pipelines.pipeline import Pipeline
40
+ from cars.pipelines.pipeline_constants import (
41
+ ADVANCED,
42
+ APPLICATIONS,
43
+ INPUT,
44
+ ORCHESTRATOR,
45
+ OUTPUT,
46
+ )
47
+ from cars.pipelines.pipeline_template import PipelineTemplate
48
+
49
+ PIPELINE = "merging"
50
+
51
+
52
+ @Pipeline.register(
53
+ PIPELINE,
54
+ )
55
+ class MergingPipeline(PipelineTemplate):
56
+ """
57
+ Merging pipeline
58
+ """
59
+
60
+ def __init__(self, conf, config_dir=None):
61
+ """
62
+ Creates pipeline
63
+
64
+ Directly creates class attributes:
65
+ used_conf
66
+
67
+ :param pipeline_name: name of the pipeline.
68
+ :type pipeline_name: str
69
+ :param cfg: configuration {'matching_cost_method': value}
70
+ :type cfg: dictionary
71
+ :param config_dir: path to dir containing json/yaml
72
+ :type config_dir: str
73
+ """
74
+
75
+ # Used conf
76
+ self.used_conf = {}
77
+
78
+ # Transform relative path to absolute path
79
+ if config_dir is not None:
80
+ config_dir = os.path.abspath(config_dir)
81
+
82
+ # Check global conf
83
+ self.check_global_schema(conf)
84
+
85
+ if PIPELINE in conf:
86
+ self.check_pipeline_conf(conf)
87
+
88
+ # Check conf orchestrator
89
+ self.used_conf[ORCHESTRATOR] = self.check_orchestrator(
90
+ conf.get(ORCHESTRATOR, None)
91
+ )
92
+
93
+ # Check conf inputs
94
+ inputs = self.check_inputs(conf[INPUT], config_dir=config_dir)
95
+ self.used_conf[INPUT] = inputs
96
+
97
+ # Check advanced parameters
98
+ pipeline_conf = conf.get(PIPELINE, {})
99
+ advanced = self.check_advanced_parameters(
100
+ pipeline_conf.get(ADVANCED, {})
101
+ )
102
+ self.used_conf[ADVANCED] = advanced
103
+
104
+ # Check conf output
105
+ output = self.check_output(conf[OUTPUT])
106
+
107
+ self.used_conf[OUTPUT] = output
108
+ self.out_dir = self.used_conf[OUTPUT][out_cst.OUT_DIRECTORY]
109
+ self.dump_dir = os.path.join(self.out_dir, "dump_dir")
110
+
111
+ self.save_all_intermediate_data = self.used_conf[ADVANCED][
112
+ adv_cst.SAVE_INTERMEDIATE_DATA
113
+ ]
114
+
115
+ # Check conf application
116
+ application_conf = self.check_applications(
117
+ pipeline_conf.get(APPLICATIONS, {})
118
+ )
119
+
120
+ self.used_conf[APPLICATIONS] = application_conf
121
+
122
+ self.out_dir = self.used_conf[OUTPUT][out_cst.OUT_DIRECTORY]
123
+
124
+ def check_pipeline_conf(self, conf):
125
+ """
126
+ Check pipeline configuration
127
+ """
128
+
129
+ # Validate inputs
130
+ pipeline_schema = {
131
+ OptionalKey(ADVANCED): dict,
132
+ OptionalKey(APPLICATIONS): dict,
133
+ }
134
+
135
+ checker_inputs = Checker(pipeline_schema)
136
+ checker_inputs.validate(conf[PIPELINE])
137
+
138
+ @staticmethod
139
+ def check_inputs(conf, config_dir=None):
140
+ """
141
+ Check the inputs given
142
+
143
+ :param conf: configuration of inputs
144
+ :type conf: dict
145
+ :param config_dir: directory of used json/yaml, if
146
+ user filled paths with relative paths
147
+ :type config_dir: str
148
+
149
+ :return: overloaded inputs
150
+ :rtype: dict
151
+ """
152
+
153
+ input_config = dsm_inputs.check_dsm_inputs(conf, config_dir=config_dir)
154
+ return input_config
155
+
156
+ @staticmethod
157
+ def check_advanced_parameters(conf):
158
+ """
159
+ Check the advanced parameters consistency
160
+
161
+ :param conf: configuration of inputs
162
+ :type conf: dict
163
+ :param config_dir: directory of used json/yaml, if
164
+ user filled paths with relative paths
165
+ :type config_dir: str
166
+
167
+ :return: overloaded inputs
168
+ :rtype: dict
169
+ """
170
+
171
+ overloaded_conf = conf.copy()
172
+
173
+ overloaded_conf[adv_cst.SAVE_INTERMEDIATE_DATA] = conf.get(
174
+ adv_cst.SAVE_INTERMEDIATE_DATA, False
175
+ )
176
+
177
+ # Validate inputs
178
+ schema = {
179
+ adv_cst.SAVE_INTERMEDIATE_DATA: Or(dict, bool),
180
+ }
181
+
182
+ checker_advanced_parameters = Checker(schema)
183
+ checker_advanced_parameters.validate(overloaded_conf)
184
+
185
+ return overloaded_conf
186
+
187
+ @staticmethod
188
+ def check_output(conf):
189
+ """
190
+ Check the output given
191
+
192
+ :param conf: configuration of output
193
+ :type conf: dict
194
+ :return: overloader output
195
+ :rtype: dict
196
+ """
197
+ overloaded_conf = conf.copy()
198
+ out_dir = conf[out_cst.OUT_DIRECTORY]
199
+ out_dir = os.path.abspath(out_dir)
200
+ # Ensure that output directory and its subdirectories exist
201
+ safe_makedirs(out_dir)
202
+
203
+ # Overload some parameters
204
+ overloaded_conf[out_cst.OUT_DIRECTORY] = out_dir
205
+
206
+ # Load auxiliary and subfields
207
+ overloaded_conf[out_cst.AUXILIARY] = overloaded_conf.get(
208
+ out_cst.AUXILIARY, {}
209
+ )
210
+
211
+ overloaded_conf[out_cst.AUXILIARY][out_cst.AUX_IMAGE] = overloaded_conf[
212
+ out_cst.AUXILIARY
213
+ ].get(out_cst.AUX_IMAGE, True)
214
+ overloaded_conf[out_cst.AUXILIARY][out_cst.AUX_DEM_MIN] = (
215
+ overloaded_conf[out_cst.AUXILIARY].get(out_cst.AUX_DEM_MIN, False)
216
+ )
217
+ overloaded_conf[out_cst.AUXILIARY][out_cst.AUX_DEM_MAX] = (
218
+ overloaded_conf[out_cst.AUXILIARY].get(out_cst.AUX_DEM_MAX, False)
219
+ )
220
+ overloaded_conf[out_cst.AUXILIARY][out_cst.AUX_DEM_MEDIAN] = (
221
+ overloaded_conf[out_cst.AUXILIARY].get(
222
+ out_cst.AUX_DEM_MEDIAN, False
223
+ )
224
+ )
225
+ overloaded_conf[out_cst.AUXILIARY][out_cst.AUX_WEIGHTS] = (
226
+ overloaded_conf[out_cst.AUXILIARY].get(out_cst.AUX_WEIGHTS, False)
227
+ )
228
+ overloaded_conf[out_cst.AUXILIARY][out_cst.AUX_CLASSIFICATION] = (
229
+ overloaded_conf[out_cst.AUXILIARY].get(
230
+ out_cst.AUX_CLASSIFICATION, False
231
+ )
232
+ )
233
+ overloaded_conf[out_cst.AUXILIARY][out_cst.AUX_PERFORMANCE_MAP] = (
234
+ overloaded_conf[out_cst.AUXILIARY].get(
235
+ out_cst.AUX_PERFORMANCE_MAP, False
236
+ )
237
+ )
238
+ overloaded_conf[out_cst.AUXILIARY][out_cst.AUX_CONTRIBUTING_PAIR] = (
239
+ overloaded_conf[out_cst.AUXILIARY].get(
240
+ out_cst.AUX_CONTRIBUTING_PAIR, False
241
+ )
242
+ )
243
+ overloaded_conf[out_cst.AUXILIARY][out_cst.AUX_FILLING] = (
244
+ overloaded_conf[out_cst.AUXILIARY].get(out_cst.AUX_FILLING, False)
245
+ )
246
+ overloaded_conf[out_cst.AUXILIARY][out_cst.AUX_AMBIGUITY] = (
247
+ overloaded_conf[out_cst.AUXILIARY].get(out_cst.AUX_AMBIGUITY, False)
248
+ )
249
+
250
+ # Check schema
251
+ output_schema = {
252
+ out_cst.OUT_DIRECTORY: str,
253
+ out_cst.AUXILIARY: dict,
254
+ }
255
+ checker_output = Checker(output_schema)
256
+ checker_output.validate(overloaded_conf)
257
+
258
+ # Check auxiliary keys
259
+ auxiliary_schema = {
260
+ out_cst.AUX_IMAGE: Or(bool, str, list),
261
+ out_cst.AUX_WEIGHTS: bool,
262
+ out_cst.AUX_CLASSIFICATION: Or(bool, dict, list),
263
+ out_cst.AUX_PERFORMANCE_MAP: Or(bool, list),
264
+ out_cst.AUX_CONTRIBUTING_PAIR: bool,
265
+ out_cst.AUX_FILLING: Or(bool, dict),
266
+ out_cst.AUX_AMBIGUITY: bool,
267
+ out_cst.AUX_DEM_MIN: bool,
268
+ out_cst.AUX_DEM_MAX: bool,
269
+ out_cst.AUX_DEM_MEDIAN: bool,
270
+ }
271
+
272
+ checker_auxiliary = Checker(auxiliary_schema)
273
+ checker_auxiliary.validate(overloaded_conf[out_cst.AUXILIARY])
274
+
275
+ return overloaded_conf
276
+
277
+ def check_applications(self, conf):
278
+ """
279
+ Check the given configuration for applications,
280
+ and generates needed applications for pipeline.
281
+
282
+ :param conf: configuration of applications
283
+ :type conf: dict
284
+ """
285
+
286
+ # Initialize used config
287
+ used_conf = {}
288
+
289
+ needed_applications = ["dsm_merging"]
290
+
291
+ for app_key in needed_applications:
292
+ used_conf[app_key] = conf.get(app_key, {})
293
+ if used_conf[app_key] is not None:
294
+ used_conf[app_key]["save_intermediate_data"] = (
295
+ self.save_all_intermediate_data
296
+ or used_conf[app_key].get("save_intermediate_data", False)
297
+ )
298
+
299
+ # DSM merging
300
+ self.dsm_merging_application = Application(
301
+ "dsm_merging",
302
+ cfg=used_conf.get("dsm_merging", {}),
303
+ )
304
+ used_conf["dsm_merging"] = self.dsm_merging_application.get_conf()
305
+
306
+ return used_conf
307
+
308
+ def run(self, log_dir=None):
309
+ """
310
+ Run pipeline
311
+
312
+ """
313
+ if log_dir is None:
314
+ log_dir = os.path.join(self.out_dir, "logs")
315
+
316
+ with orchestrator.Orchestrator(
317
+ orchestrator_conf=self.used_conf[ORCHESTRATOR],
318
+ out_dir=self.out_dir,
319
+ log_dir=log_dir,
320
+ out_yaml_path=os.path.join(
321
+ self.out_dir,
322
+ out_cst.INFO_FILENAME,
323
+ ),
324
+ ) as cars_orchestrator:
325
+
326
+ dsms_merging_dump_dir = os.path.join(self.dump_dir, "dsms_merging")
327
+
328
+ dsm_dict = self.used_conf[INPUT][dsm_cst.DSMS]
329
+ dict_path = {}
330
+ for key in dsm_dict.keys():
331
+ for path_name in dsm_dict[key].keys():
332
+ if dsm_dict[key][path_name] is not None:
333
+ if isinstance(dsm_dict[key][path_name], str):
334
+ if path_name not in dict_path:
335
+ dict_path[path_name] = [
336
+ dsm_dict[key][path_name]
337
+ ]
338
+ else:
339
+ dict_path[path_name].append(
340
+ dsm_dict[key][path_name]
341
+ )
342
+
343
+ dsm_file_name = os.path.join(
344
+ self.out_dir,
345
+ out_cst.DSM_DIRECTORY,
346
+ "dsm.tif",
347
+ )
348
+
349
+ color_file_name = (
350
+ os.path.join(
351
+ self.out_dir,
352
+ out_cst.DSM_DIRECTORY,
353
+ "image.tif",
354
+ )
355
+ if "texture" in dict_path
356
+ or self.used_conf[OUTPUT][out_cst.AUXILIARY][out_cst.AUX_IMAGE]
357
+ else None
358
+ )
359
+
360
+ performance_map_file_name = (
361
+ os.path.join(
362
+ self.out_dir,
363
+ out_cst.DSM_DIRECTORY,
364
+ "performance_map.tif",
365
+ )
366
+ if "performance_map" in dict_path
367
+ else None
368
+ )
369
+
370
+ ambiguity_bool = any("ambiguity" in key for key in dict_path)
371
+ ambiguity_file_name = (
372
+ os.path.join(
373
+ self.out_dir,
374
+ out_cst.DSM_DIRECTORY,
375
+ "ambiguity.tif",
376
+ )
377
+ if ambiguity_bool
378
+ else None
379
+ )
380
+
381
+ classif_file_name = (
382
+ os.path.join(
383
+ self.out_dir,
384
+ out_cst.DSM_DIRECTORY,
385
+ "classification.tif",
386
+ )
387
+ if "merging_classification" in dict_path
388
+ or self.used_conf[OUTPUT][out_cst.AUXILIARY][
389
+ out_cst.AUX_CLASSIFICATION
390
+ ]
391
+ else None
392
+ )
393
+
394
+ contributing_all_pair_file_name = (
395
+ os.path.join(
396
+ self.out_dir,
397
+ out_cst.DSM_DIRECTORY,
398
+ "contributing_pair.tif",
399
+ )
400
+ if "contributing_pair" in dict_path
401
+ else None
402
+ )
403
+
404
+ filling_file_name = (
405
+ os.path.join(
406
+ self.out_dir,
407
+ out_cst.DSM_DIRECTORY,
408
+ "filling.tif",
409
+ )
410
+ if "merging_filling" in dict_path
411
+ else None
412
+ )
413
+
414
+ # Get ROI
415
+ epsg = rasterio_get_epsg(dict_path["dsm"][0])
416
+ (
417
+ input_roi_poly,
418
+ input_roi_epsg,
419
+ ) = roi_tools.generate_roi_poly_from_inputs(
420
+ self.used_conf[INPUT][sens_cst.ROI]
421
+ )
422
+ roi_poly = preprocessing.compute_roi_poly(
423
+ input_roi_poly, input_roi_epsg, epsg
424
+ )
425
+
426
+ # Launch merging
427
+ _ = self.dsm_merging_application.run(
428
+ dict_path,
429
+ cars_orchestrator,
430
+ roi_poly,
431
+ dsms_merging_dump_dir,
432
+ dsm_file_name,
433
+ color_file_name,
434
+ classif_file_name,
435
+ filling_file_name,
436
+ performance_map_file_name,
437
+ ambiguity_file_name,
438
+ contributing_all_pair_file_name,
439
+ )
File without changes