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,859 @@
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
+ # pylint: disable=C0302
21
+ """
22
+ CARS containing inputs checking for sensor input data
23
+ Used for full_res and low_res pipelines
24
+ """
25
+
26
+ import logging
27
+ import math
28
+ import os
29
+
30
+ import numpy as np
31
+ import rasterio as rio
32
+ from json_checker import Checker, Or
33
+
34
+ # CARS imports
35
+ from cars.core import inputs, projection
36
+ from cars.core.geometry.abstract_geometry import AbstractGeometry
37
+ from cars.core.utils import make_relative_path_absolute
38
+ from cars.orchestrator.cluster.log_wrapper import cars_profile
39
+ from cars.pipelines.parameters import sensor_inputs_constants as sens_cst
40
+ from cars.pipelines.parameters.sensor_loaders.sensor_loader import SensorLoader
41
+
42
+ CARS_GEOID_PATH = "geoid/egm96.grd" # Path in cars package (pkg)
43
+
44
+
45
+ def sensors_check_inputs(conf, config_dir=None): # noqa: C901
46
+ """
47
+ Check the inputs given
48
+
49
+ :param conf: configuration of inputs
50
+ :type conf: dict
51
+ :param config_dir: path to dir containing json
52
+ :type config_dir: str
53
+ """
54
+
55
+ overloaded_conf = conf.copy()
56
+
57
+ overloaded_conf[sens_cst.ROI] = conf.get(sens_cst.ROI, None)
58
+
59
+ overloaded_conf["dsm_to_fill"] = conf.get("dsm_to_fill", None)
60
+
61
+ overloaded_conf[sens_cst.PAIRING] = conf.get(sens_cst.PAIRING, None)
62
+
63
+ overloaded_conf[sens_cst.INITIAL_ELEVATION] = get_initial_elevation(
64
+ conf.get(sens_cst.INITIAL_ELEVATION, None)
65
+ )
66
+
67
+ overloaded_conf[sens_cst.LOW_RES_DSM] = conf.get(sens_cst.LOW_RES_DSM, None)
68
+
69
+ overloaded_conf[sens_cst.LOADERS] = check_loaders(
70
+ conf.get(sens_cst.LOADERS, {})
71
+ )
72
+
73
+ classif_loader = overloaded_conf[sens_cst.LOADERS][
74
+ sens_cst.INPUT_CLASSIFICATION
75
+ ]
76
+
77
+ overloaded_conf[sens_cst.FILLING] = check_filling(
78
+ conf.get(sens_cst.FILLING, {}), classif_loader
79
+ )
80
+
81
+ # Validate inputs
82
+ inputs_schema = {
83
+ sens_cst.SENSORS: dict,
84
+ sens_cst.PAIRING: Or([[str]], None),
85
+ sens_cst.INITIAL_ELEVATION: Or(str, dict, None),
86
+ sens_cst.LOW_RES_DSM: Or(str, None),
87
+ sens_cst.ROI: Or(str, dict, None),
88
+ sens_cst.LOADERS: dict,
89
+ sens_cst.FILLING: dict,
90
+ "dsm_to_fill": Or(str, dict, None),
91
+ }
92
+
93
+ checker_inputs = Checker(inputs_schema)
94
+ checker_inputs.validate(overloaded_conf)
95
+
96
+ overloaded_conf = check_sensors(conf, overloaded_conf, config_dir)
97
+
98
+ overloaded_conf[sens_cst.FILLING] = check_filling(
99
+ overloaded_conf.get(sens_cst.FILLING, {}), classif_loader
100
+ )
101
+
102
+ # Check srtm dir
103
+ check_srtm(overloaded_conf[sens_cst.INITIAL_ELEVATION][sens_cst.DEM_PATH])
104
+
105
+ return overloaded_conf
106
+
107
+
108
+ def check_sensors(conf, overloaded_conf, config_dir=None): # noqa: C901
109
+ """
110
+ Check sensors
111
+ """
112
+ # Validate each sensor image
113
+ sensor_schema = {
114
+ sens_cst.INPUT_IMG: Or(str, dict),
115
+ sens_cst.INPUT_GEO_MODEL: Or(str, dict),
116
+ sens_cst.INPUT_MSK: Or(str, None),
117
+ sens_cst.INPUT_CLASSIFICATION: Or(str, dict, None),
118
+ }
119
+
120
+ checker_sensor = Checker(sensor_schema)
121
+
122
+ for sensor_image_key in conf[sens_cst.SENSORS]:
123
+ # Case where the sensor is defined as a string refering to the input
124
+ # image instead of a dict
125
+ if isinstance(conf[sens_cst.SENSORS][sensor_image_key], str):
126
+ # initialize sensor dictionary
127
+ overloaded_conf[sens_cst.SENSORS][sensor_image_key] = {
128
+ sens_cst.INPUT_IMG: conf[sens_cst.SENSORS][sensor_image_key]
129
+ }
130
+
131
+ # Overload parameters
132
+ image = overloaded_conf[sens_cst.SENSORS][sensor_image_key].get(
133
+ sens_cst.INPUT_IMG, None
134
+ )
135
+ loader_name = (
136
+ overloaded_conf[sens_cst.LOADERS][sens_cst.INPUT_IMG]
137
+ + "_"
138
+ + sens_cst.INPUT_IMG
139
+ )
140
+
141
+ image_loader = SensorLoader(loader_name, image, config_dir)
142
+ image_as_pivot_format = (
143
+ image_loader.get_pivot_format() # pylint: disable=E1101
144
+ )
145
+ overloaded_conf[sens_cst.SENSORS][sensor_image_key][
146
+ sens_cst.INPUT_IMG
147
+ ] = image_as_pivot_format
148
+ image_path = image_as_pivot_format["bands"]["b0"]["path"]
149
+
150
+ geomodel = overloaded_conf[sens_cst.SENSORS][sensor_image_key].get(
151
+ "geomodel",
152
+ image_path,
153
+ )
154
+ overloaded_conf[sens_cst.SENSORS][sensor_image_key][
155
+ "geomodel"
156
+ ] = geomodel
157
+
158
+ mask = overloaded_conf[sens_cst.SENSORS][sensor_image_key].get(
159
+ sens_cst.INPUT_MSK, None
160
+ )
161
+ overloaded_conf[sens_cst.SENSORS][sensor_image_key][
162
+ sens_cst.INPUT_MSK
163
+ ] = mask
164
+
165
+ classif = overloaded_conf[sens_cst.SENSORS][sensor_image_key].get(
166
+ sens_cst.INPUT_CLASSIFICATION, None
167
+ )
168
+ if classif is not None:
169
+ loader_name = (
170
+ overloaded_conf[sens_cst.LOADERS][sens_cst.INPUT_CLASSIFICATION]
171
+ + "_"
172
+ + sens_cst.INPUT_CLASSIFICATION
173
+ )
174
+ classif_loader = SensorLoader(loader_name, classif, config_dir)
175
+ classif_as_pivot_format = (
176
+ classif_loader.get_pivot_format() # pylint: disable=E1101
177
+ )
178
+ overloaded_conf[sens_cst.SENSORS][sensor_image_key][
179
+ sens_cst.INPUT_CLASSIFICATION
180
+ ] = classif_as_pivot_format
181
+ else:
182
+ overloaded_conf[sens_cst.SENSORS][sensor_image_key][
183
+ sens_cst.INPUT_CLASSIFICATION
184
+ ] = None
185
+
186
+ # Validate
187
+ checker_sensor.validate(
188
+ overloaded_conf[sens_cst.SENSORS][sensor_image_key]
189
+ )
190
+
191
+ # Image are now in pivot format
192
+ overloaded_conf[sens_cst.LOADERS][sens_cst.INPUT_IMG] = "pivot"
193
+ overloaded_conf[sens_cst.LOADERS][sens_cst.INPUT_CLASSIFICATION] = "pivot"
194
+
195
+ # Modify to absolute path
196
+ if config_dir is not None:
197
+ modify_to_absolute_path(config_dir, overloaded_conf)
198
+
199
+ # Check image, msk and color size compatibility
200
+ for sensor_image_key in overloaded_conf[sens_cst.SENSORS]:
201
+ sensor_image = overloaded_conf[sens_cst.SENSORS][sensor_image_key]
202
+ check_input_size(
203
+ sensor_image[sens_cst.INPUT_IMG],
204
+ sensor_image[sens_cst.INPUT_MSK],
205
+ sensor_image[sens_cst.INPUT_CLASSIFICATION],
206
+ )
207
+ # check band nbits of msk
208
+ check_nbits(
209
+ sensor_image[sens_cst.INPUT_MSK],
210
+ )
211
+
212
+ # Validate pairs
213
+ # If there is two inputs with no associated pairing, consider that the first
214
+ # image is left and the second image is right
215
+ if (
216
+ overloaded_conf[sens_cst.PAIRING] is None
217
+ and len(overloaded_conf[sens_cst.SENSORS]) == 2
218
+ ):
219
+ sensor_keys = list(overloaded_conf[sens_cst.SENSORS].keys())
220
+ overloaded_conf[sens_cst.PAIRING] = [[sensor_keys[0], sensor_keys[1]]]
221
+ logging.info(
222
+ (
223
+ "Pairing is not defined, '{}' will be used as left sensor and "
224
+ + "'{}' will be used as right sensor"
225
+ ).format(sensor_keys[0], sensor_keys[1])
226
+ )
227
+
228
+ if overloaded_conf[sens_cst.PAIRING] is None:
229
+ raise RuntimeError(
230
+ "Pairing is not defined and cannot be determined "
231
+ + "because there are more than two inputs products"
232
+ )
233
+
234
+ for key1, key2 in overloaded_conf[sens_cst.PAIRING]:
235
+ if key1 not in overloaded_conf[sens_cst.SENSORS]:
236
+ logging.error("{} not in sensors images".format(key1))
237
+ raise RuntimeError("{} not in sensors images".format(key1))
238
+ if key2 not in overloaded_conf["sensors"]:
239
+ logging.error("{} not in sensors images".format(key2))
240
+ raise RuntimeError("{} not in sensors images".format(key2))
241
+
242
+ # Modify to absolute path
243
+ if config_dir is not None:
244
+ modify_to_absolute_path(config_dir, overloaded_conf)
245
+ else:
246
+ logging.debug(
247
+ "path of config file was not given,"
248
+ "relative path are not transformed to absolute paths"
249
+ )
250
+
251
+ # Check consistency of pairs images
252
+ for key1, key2 in overloaded_conf[sens_cst.PAIRING]:
253
+ compare_image_type(
254
+ overloaded_conf[sens_cst.SENSORS], sens_cst.INPUT_IMG, key1, key2
255
+ )
256
+ overloaded_conf[sens_cst.FILLING] = check_classification_values(
257
+ overloaded_conf[sens_cst.SENSORS],
258
+ sens_cst.INPUT_CLASSIFICATION,
259
+ key1,
260
+ key2,
261
+ overloaded_conf[sens_cst.FILLING],
262
+ )
263
+
264
+ return overloaded_conf
265
+
266
+
267
+ def check_loaders(conf):
268
+ """
269
+ Check loaders section
270
+ :param conf: loaders section of input conf
271
+ :type conf: dict
272
+ """
273
+ overloaded_conf = conf.copy()
274
+ overloaded_conf[sens_cst.INPUT_IMG] = conf.get(sens_cst.INPUT_IMG, "basic")
275
+ overloaded_conf[sens_cst.INPUT_CLASSIFICATION] = conf.get(
276
+ sens_cst.INPUT_CLASSIFICATION, "basic"
277
+ )
278
+
279
+ # Validate loaders
280
+ loaders_schema = {
281
+ sens_cst.INPUT_IMG: str,
282
+ sens_cst.INPUT_CLASSIFICATION: str,
283
+ }
284
+
285
+ checker_loaders = Checker(loaders_schema)
286
+ checker_loaders.validate(overloaded_conf)
287
+
288
+ return overloaded_conf
289
+
290
+
291
+ def check_filling(conf, classif_loader):
292
+ """
293
+ Check filling section
294
+ :param conf: filling section of input conf
295
+ :type conf: dict
296
+ """
297
+ basic_filling = {
298
+ "fill_with_geoid": None,
299
+ "interpolate_from_borders": None,
300
+ "fill_with_endogenous_dem": None,
301
+ "fill_with_exogenous_dem": None,
302
+ }
303
+ slurp_filling = {
304
+ "fill_with_geoid": [8],
305
+ "interpolate_from_borders": [9],
306
+ "fill_with_endogenous_dem": [10],
307
+ "fill_with_exogenous_dem": [6],
308
+ }
309
+ filling_from_loader = {}
310
+ filling_from_loader["basic"] = basic_filling
311
+ filling_from_loader["slurp"] = slurp_filling
312
+ filling_from_loader["pivot"] = basic_filling
313
+ default_filling = filling_from_loader[classif_loader]
314
+ overloaded_conf = conf.copy()
315
+ for filling_method in basic_filling:
316
+ overloaded_conf[filling_method] = conf.get(
317
+ filling_method, default_filling[filling_method]
318
+ )
319
+ if isinstance(overloaded_conf[filling_method], int):
320
+ overloaded_conf[filling_method] = [overloaded_conf[filling_method]]
321
+ if overloaded_conf[filling_method] == []:
322
+ overloaded_conf[filling_method] = None
323
+
324
+ # Validate loaders
325
+ loaders_schema = {
326
+ "fill_with_geoid": Or(None, [int]),
327
+ "interpolate_from_borders": Or(None, [int]),
328
+ "fill_with_endogenous_dem": Or(None, [int]),
329
+ "fill_with_exogenous_dem": Or(None, [int]),
330
+ }
331
+
332
+ checker_loaders = Checker(loaders_schema)
333
+ checker_loaders.validate(overloaded_conf)
334
+
335
+ return overloaded_conf
336
+
337
+
338
+ def get_sensor_resolution(
339
+ geom_plugin, sensor_path, geomodel, target_epsg=32631
340
+ ):
341
+ """
342
+ Estimate the sensor image resolution in meters per pixel
343
+ using geolocation of 3 corners of the image.
344
+
345
+ :param geom_plugin: geometry plugin instance
346
+ :param sensor_path: path to the sensor image
347
+ :type sensor_path: dict
348
+ :param geomodel: geometric model for the sensor image
349
+ :param target_epsg: target EPSG code for projection
350
+ :type target_epsg: int
351
+ :return: average resolution in meters/pixel along x and y
352
+ :rtype: float
353
+ """
354
+ width, height = inputs.rasterio_get_size(sensor_path["bands"]["b0"]["path"])
355
+
356
+ upper_left = (0.5, 0.5)
357
+ upper_right = (width - 0.5, 0.5)
358
+ bottom_left = (0.5, height - 0.5)
359
+
360
+ # get geodetic coordinates
361
+ lat_ul, lon_ul, _ = geom_plugin.direct_loc(
362
+ sensor_path["bands"]["b0"]["path"],
363
+ geomodel,
364
+ np.array([upper_left[0]]),
365
+ np.array([upper_left[1]]),
366
+ )
367
+ lat_ur, lon_ur, _ = geom_plugin.direct_loc(
368
+ sensor_path["bands"]["b0"]["path"],
369
+ geomodel,
370
+ np.array([upper_right[0]]),
371
+ np.array([upper_right[1]]),
372
+ )
373
+ lat_bl, lon_bl, _ = geom_plugin.direct_loc(
374
+ sensor_path["bands"]["b0"]["path"],
375
+ geomodel,
376
+ np.array([bottom_left[0]]),
377
+ np.array([bottom_left[1]]),
378
+ )
379
+
380
+ coords_ll = np.array(
381
+ [[lon_ul, lat_ul, 0], [lon_ur, lat_ur, 0], [lon_bl, lat_bl, 0]]
382
+ )
383
+
384
+ # Convert to target CRS
385
+ coords_xy = projection.point_cloud_conversion(coords_ll, 4326, target_epsg)
386
+
387
+ diff_x = np.linalg.norm(coords_xy[1] - coords_xy[0]) # UL to UR (width)
388
+ diff_y = np.linalg.norm(coords_xy[2] - coords_xy[0]) # UL to BL (height)
389
+
390
+ # resolution in meters per pixel
391
+ res_x = diff_x / (width - 1)
392
+ res_y = diff_y / (height - 1)
393
+
394
+ return (res_x + res_y) / 2
395
+
396
+
397
+ def check_geometry_plugin(conf_inputs, conf_geom_plugin, output_dem_dir):
398
+ """
399
+ Check the geometry plugin with inputs
400
+
401
+ :param conf_inputs: checked configuration of inputs
402
+ :type conf_inputs: type
403
+ :param conf_advanced: checked configuration of advanced
404
+ :type conf_advanced: type
405
+ :param conf_geom_plugin: name of geometry plugin
406
+ :type conf_geom_plugin: str
407
+ :return: overload inputs conf
408
+ overloaded geometry plugin conf
409
+ geometry plugin without dem
410
+ geometry plugin with dem
411
+ """
412
+ if conf_geom_plugin is None:
413
+ conf_geom_plugin = "SharelocGeometry"
414
+
415
+ # Initialize a temporary plugin, to get the product's resolution
416
+ temp_geom_plugin = (
417
+ AbstractGeometry( # pylint: disable=abstract-class-instantiated
418
+ conf_geom_plugin,
419
+ default_alt=sens_cst.CARS_DEFAULT_ALT,
420
+ )
421
+ )
422
+ average_sensor_resolution = 0
423
+ for _, sensor_image in conf_inputs[sens_cst.SENSORS].items():
424
+ sensor = sensor_image[sens_cst.INPUT_IMG]
425
+ geomodel = sensor_image[sens_cst.INPUT_GEO_MODEL]
426
+ (
427
+ sensor,
428
+ geomodel,
429
+ ) = temp_geom_plugin.check_product_consistency(sensor, geomodel)
430
+ average_sensor_resolution += get_sensor_resolution(
431
+ temp_geom_plugin, sensor, geomodel
432
+ )
433
+ average_sensor_resolution /= len(conf_inputs[sens_cst.SENSORS])
434
+ # approximate resolution to the highest digit:
435
+ # 0.47 -> 0.5
436
+ # 7.52 -> 8
437
+ # 12.9 -> 10
438
+ nb_digits = int(math.floor(math.log10(abs(average_sensor_resolution))))
439
+ scaling_coeff = round(average_sensor_resolution, -nb_digits)
440
+ # make it so 0.5 (CO3D) is the baseline for parameters
441
+ scaling_coeff *= 2
442
+
443
+ # Initialize the desired geometry plugin without elevation information
444
+ geom_plugin_without_dem_and_geoid = (
445
+ AbstractGeometry( # pylint: disable=abstract-class-instantiated
446
+ conf_geom_plugin,
447
+ default_alt=sens_cst.CARS_DEFAULT_ALT,
448
+ scaling_coeff=scaling_coeff,
449
+ )
450
+ )
451
+
452
+ # Check products consistency with this plugin
453
+ overloaded_conf_inputs = conf_inputs.copy()
454
+ for sensor_key, sensor_image in conf_inputs[sens_cst.SENSORS].items():
455
+ sensor = sensor_image[sens_cst.INPUT_IMG]
456
+ geomodel = sensor_image[sens_cst.INPUT_GEO_MODEL]
457
+ (
458
+ sensor,
459
+ geomodel,
460
+ ) = geom_plugin_without_dem_and_geoid.check_product_consistency(
461
+ sensor, geomodel
462
+ )
463
+ overloaded_conf_inputs[sens_cst.SENSORS][sensor_key][
464
+ sens_cst.INPUT_IMG
465
+ ] = sensor
466
+ overloaded_conf_inputs[sens_cst.SENSORS][sensor_key][
467
+ sens_cst.INPUT_GEO_MODEL
468
+ ] = geomodel
469
+
470
+ geom_plugin_with_dem_and_geoid = generate_geometry_plugin_with_dem(
471
+ conf_geom_plugin,
472
+ conf_inputs,
473
+ scaling_coeff=scaling_coeff,
474
+ output_dem_dir=output_dem_dir,
475
+ )
476
+
477
+ return (
478
+ overloaded_conf_inputs,
479
+ conf_geom_plugin,
480
+ geom_plugin_without_dem_and_geoid,
481
+ geom_plugin_with_dem_and_geoid,
482
+ scaling_coeff,
483
+ )
484
+
485
+
486
+ # pylint: disable=too-many-positional-arguments
487
+ def generate_geometry_plugin_with_dem(
488
+ conf_geom_plugin,
489
+ conf_inputs,
490
+ dem=None,
491
+ crop_dem=True,
492
+ output_dem_dir=None,
493
+ scaling_coeff=1,
494
+ ):
495
+ """
496
+ Generate geometry plugin with dem and geoid
497
+
498
+ :param conf_geom_plugin: plugin configuration
499
+ :param conf_inputs: inputs configuration
500
+ :param dem: dem to overide the one in inputs
501
+ :param scaling_coeff: scaling factor for resolution
502
+ :type scaling_coeff: float
503
+
504
+ :return: geometry plugin object, with a dem
505
+ """
506
+
507
+ dem_path = (
508
+ dem
509
+ if dem is not None
510
+ else conf_inputs[sens_cst.INITIAL_ELEVATION][sens_cst.DEM_PATH]
511
+ )
512
+
513
+ if crop_dem:
514
+ # Get image pairs for DEM intersection with ROI
515
+ pairs_for_roi = []
516
+ for key1, key2 in conf_inputs[sens_cst.PAIRING]:
517
+ sensor1 = conf_inputs[sens_cst.SENSORS][key1]
518
+ sensor2 = conf_inputs[sens_cst.SENSORS][key2]
519
+ image1 = sensor1[sens_cst.INPUT_IMG]
520
+ image2 = sensor2[sens_cst.INPUT_IMG]
521
+ geomodel1 = sensor1[sens_cst.INPUT_GEO_MODEL]
522
+ geomodel2 = sensor2[sens_cst.INPUT_GEO_MODEL]
523
+ pairs_for_roi.append((image1, geomodel1, image2, geomodel2))
524
+ else:
525
+ pairs_for_roi = None
526
+
527
+ # Initialize a second geometry plugin with elevation information
528
+
529
+ geom_plugin_with_dem_and_geoid = (
530
+ AbstractGeometry( # pylint: disable=abstract-class-instantiated
531
+ conf_geom_plugin,
532
+ dem=dem_path,
533
+ geoid=conf_inputs[sens_cst.INITIAL_ELEVATION][sens_cst.GEOID],
534
+ default_alt=sens_cst.CARS_DEFAULT_ALT,
535
+ pairs_for_roi=pairs_for_roi,
536
+ scaling_coeff=scaling_coeff,
537
+ output_dem_dir=output_dem_dir,
538
+ )
539
+ )
540
+
541
+ return geom_plugin_with_dem_and_geoid
542
+
543
+
544
+ def modify_to_absolute_path(config_dir, overloaded_conf):
545
+ """
546
+ Modify input file path to absolute path
547
+
548
+ :param config_dir: directory of the json configuration
549
+ :type config_dir: str
550
+ :param overloaded_conf: overloaded configuration json
551
+ :dict overloaded_conf: dict
552
+ """
553
+
554
+ for sensor_image_key in overloaded_conf[sens_cst.SENSORS]:
555
+ sensor_image = overloaded_conf[sens_cst.SENSORS][sensor_image_key]
556
+ for tag in [
557
+ sens_cst.INPUT_MSK,
558
+ sens_cst.INPUT_GEO_MODEL,
559
+ ]:
560
+ if isinstance(sensor_image[tag], dict):
561
+ sensor_image[tag][sens_cst.INPUT_PATH] = (
562
+ make_relative_path_absolute(
563
+ sensor_image[tag][sens_cst.INPUT_PATH], config_dir
564
+ )
565
+ )
566
+ elif sensor_image[tag] is not None:
567
+ sensor_image[tag] = make_relative_path_absolute(
568
+ sensor_image[tag], config_dir
569
+ )
570
+
571
+ if overloaded_conf[sens_cst.ROI] is not None:
572
+ if isinstance(overloaded_conf[sens_cst.ROI], str):
573
+ overloaded_conf[sens_cst.ROI] = make_relative_path_absolute(
574
+ overloaded_conf[sens_cst.ROI], config_dir
575
+ )
576
+
577
+ for tag in [sens_cst.DEM_PATH, sens_cst.GEOID]:
578
+ if overloaded_conf[sens_cst.INITIAL_ELEVATION][tag] is not None:
579
+ if isinstance(
580
+ overloaded_conf[sens_cst.INITIAL_ELEVATION][tag], str
581
+ ):
582
+ overloaded_conf[sens_cst.INITIAL_ELEVATION][tag] = (
583
+ make_relative_path_absolute(
584
+ overloaded_conf[sens_cst.INITIAL_ELEVATION][tag],
585
+ config_dir,
586
+ )
587
+ )
588
+
589
+
590
+ def check_srtm(srtm_dir):
591
+ """
592
+ Check srtm data
593
+
594
+ :param srtm_dir: directory of srtm
595
+ :type srtm_dir: str
596
+ """
597
+
598
+ if srtm_dir is not None:
599
+ if os.path.isdir(srtm_dir):
600
+ srtm_tiles = os.listdir(srtm_dir)
601
+ if len(srtm_tiles) == 0:
602
+ logging.warning(
603
+ "SRTM directory is empty, "
604
+ "the default altitude will be used as reference altitude."
605
+ )
606
+ else:
607
+ logging.info(
608
+ "Indicated SRTM tiles valid regions "
609
+ "will be used as reference altitudes "
610
+ "(the default altitude is used "
611
+ "for undefined regions of the SRTM)"
612
+ )
613
+ else:
614
+ # TODO add check for single file
615
+ pass
616
+ else:
617
+ logging.info("The default altitude will be used as reference altitude.")
618
+
619
+
620
+ def check_input_data(image, color):
621
+ """
622
+ Check data of the image and color
623
+
624
+ :param image: image path
625
+ :type image: str
626
+ :param color: color path
627
+ :type color: str
628
+ """
629
+
630
+ with rio.open(image) as img_reader:
631
+ trans = img_reader.transform
632
+ if trans.e < 0:
633
+ logging.warning(
634
+ "{} seems to have an incoherent pixel size. "
635
+ "Input images has to be in sensor geometry.".format(image)
636
+ )
637
+
638
+ with rio.open(color) as img_reader:
639
+ trans = img_reader.transform
640
+ if trans.e < 0:
641
+ logging.warning(
642
+ "{} seems to have an incoherent pixel size. "
643
+ "Input images has to be in sensor geometry.".format(image)
644
+ )
645
+
646
+
647
+ def get_initial_elevation(config):
648
+ """
649
+ Return initial elevation parameters (dem and geoid paths)
650
+ from input configuration.
651
+
652
+ :param config: input initial elevation
653
+ :type config: str, dict or None
654
+ """
655
+
656
+ # Case 1 config is already a dict
657
+ if isinstance(config, dict):
658
+ updated_config = config
659
+ else:
660
+ updated_config = {}
661
+ updated_config[sens_cst.DEM_PATH] = (
662
+ config if isinstance(config, str) else None
663
+ )
664
+
665
+ # Add geoid path to the initial_elevation dict
666
+ if sens_cst.GEOID not in updated_config:
667
+ # use cars geoid
668
+ logging.info("CARS will use its own internal file as geoid reference")
669
+ # Get root package directory
670
+ package_path = os.path.dirname(__file__)
671
+ geoid_path = os.path.join(
672
+ package_path, "..", "..", "conf", CARS_GEOID_PATH
673
+ )
674
+ updated_config[sens_cst.GEOID] = geoid_path
675
+
676
+ return updated_config
677
+
678
+
679
+ def check_input_size(image, mask, classif):
680
+ """
681
+ Check image, mask, classif and color given
682
+
683
+ Images must have same size
684
+
685
+ :param image: image path
686
+ :type image: str
687
+ :param mask: mask path
688
+ :type mask: str
689
+ :param color: color path
690
+ :type color: str
691
+ :param classif: classif path
692
+ :type classif: str
693
+ """
694
+ image = image["bands"]["b0"]["path"]
695
+ if classif is not None:
696
+ classif = classif[sens_cst.INPUT_PATH]
697
+
698
+ if mask is not None:
699
+ if inputs.rasterio_get_size(image) != inputs.rasterio_get_size(mask):
700
+ raise RuntimeError(
701
+ "The image {} and the mask {} "
702
+ "do not have the same size".format(image, mask)
703
+ )
704
+
705
+ if classif is not None:
706
+ if inputs.rasterio_get_size(image) != inputs.rasterio_get_size(classif):
707
+ raise RuntimeError(
708
+ "The classification bands {} and {} "
709
+ "do not have the same size".format(image, classif)
710
+ )
711
+
712
+
713
+ def check_nbits(mask):
714
+ """
715
+ Check the bits number of the mask, classif
716
+ mask and classification are limited to 1 bits per band
717
+
718
+ :param mask: mask path
719
+ :type mask: str
720
+ :param classif: classif path
721
+ :type classif: str
722
+ """
723
+ if mask is not None:
724
+ nbits = inputs.rasterio_get_nbits(mask)
725
+ if not check_all_nbits_equal_one(nbits):
726
+ logging.warning(
727
+ "The mask {} have {} nbits per band."
728
+ "Only the mask with nbits=1 is supported!".format(mask, nbits)
729
+ )
730
+
731
+
732
+ def compare_image_type(sensors, sensor_type, key1, key2):
733
+ """
734
+ Compare the data type between a pair of images
735
+
736
+ :param sensors: list of sensor paths
737
+ :type sensors: str
738
+ :param sensor_type: type of cardataset image (IMG, MASK, CLASSIF...)
739
+ :type sensor_type: int
740
+ :param key1: key of the images pair
741
+ :type key1: str
742
+ :param key2: other key of the images pair
743
+ :type key2: str
744
+ """
745
+ dtype1 = inputs.rasterio_get_image_type(
746
+ sensors[key1][sensor_type]["bands"]["b0"]["path"]
747
+ )
748
+ dtype2 = inputs.rasterio_get_image_type(
749
+ sensors[key2][sensor_type]["bands"]["b0"]["path"]
750
+ )
751
+
752
+ if dtype1 != dtype2:
753
+ raise RuntimeError(
754
+ "The pair images haven't the same data type."
755
+ + "\nSensor[{}]: {}".format(key1, dtype1)
756
+ + "; Sensor[{}]: {}".format(key2, dtype2)
757
+ )
758
+
759
+
760
+ def check_classification_values(sensors, sensor_type, key1, key2, filling):
761
+ """
762
+ Compare the classification values between a pair of images
763
+
764
+ :param imgs: list of image paths
765
+ :type imgs: str
766
+ :param classif_type: type of cardataset image (IMG, MASK, CLASSIF...)
767
+ :type classif_type: int
768
+ :param key1: key of the images pair
769
+ :type key1: str
770
+ :param key2: other key of the images pair
771
+ :type key2: str
772
+ """
773
+ classif1 = sensors[key1][sensor_type]
774
+ classif2 = sensors[key2][sensor_type]
775
+ if classif1 is not None and classif2 is not None:
776
+ values1 = classif1[sens_cst.INPUT_VALUES]
777
+ values2 = classif2[sens_cst.INPUT_VALUES]
778
+ all_values = list(set(values1) | set(values2))
779
+ classif1[sens_cst.INPUT_VALUES] = all_values
780
+ classif2[sens_cst.INPUT_VALUES] = all_values
781
+ for filling_method in filling:
782
+ filling_values = filling[filling_method]
783
+ if filling_values is not None and not all(
784
+ isinstance(val, int) for val in filling_values
785
+ ):
786
+ raise TypeError(
787
+ "Not all values defined for "
788
+ "filling {} are int : {}".format(
789
+ filling_method,
790
+ filling_values,
791
+ )
792
+ )
793
+ if filling_values is not None:
794
+ for filling_value in filling_values:
795
+ if filling_value not in all_values:
796
+ logging.warning(
797
+ "Value {} on which filling {} must be applied does"
798
+ " not exist on classifications {} and {}".format(
799
+ filling_value,
800
+ filling_method,
801
+ classif1[sens_cst.INPUT_PATH],
802
+ classif2[sens_cst.INPUT_PATH],
803
+ )
804
+ )
805
+ filling[filling_method].remove(filling_value)
806
+ return filling
807
+
808
+
809
+ def check_all_nbits_equal_one(nbits):
810
+ """
811
+ Check if all the nbits = 1
812
+ :param nbits: list of the nbits
813
+ :return: True if all the nbits = 1
814
+ """
815
+ if len(nbits) > 0 and nbits[0] == 1 and all(x == nbits[0] for x in nbits):
816
+ return True
817
+ return False
818
+
819
+
820
+ @cars_profile(name="Load geomodels")
821
+ def load_geomodels(conf, geometry_plugin):
822
+ """
823
+ Load geomodels directly on conf object
824
+ :param conf: input conf
825
+ :type conf: dict
826
+ """
827
+ # Load geomodels directly on conf object
828
+ sensors = conf[sens_cst.SENSORS]
829
+ for key in sensors:
830
+ geomodel = sensors[key][sens_cst.INPUT_GEO_MODEL]
831
+ loaded_geomodel = geometry_plugin.load_geomodel(geomodel)
832
+ sensors[key][sens_cst.INPUT_GEO_MODEL] = loaded_geomodel
833
+
834
+
835
+ @cars_profile(name="Generate pairs")
836
+ def generate_pairs(conf):
837
+ """
838
+ Generate sensors inputs form inputs conf :
839
+
840
+ a list of (sensor_left, sensor_right)
841
+
842
+ :param conf: input conf
843
+ :type conf: dict
844
+
845
+ :return: list of sensors pairs
846
+ :rtype: list(tuple(dict, dict))
847
+ """
848
+ # Get needed pairs
849
+ pairs = conf[sens_cst.PAIRING]
850
+
851
+ # Generate list of pairs
852
+ list_sensor_pairs = []
853
+ for key1, key2 in pairs:
854
+ merged_key = key1 + "_" + key2
855
+ sensor1 = conf[sens_cst.SENSORS][key1]
856
+ sensor2 = conf[sens_cst.SENSORS][key2]
857
+ list_sensor_pairs.append((merged_key, sensor1, sensor2))
858
+
859
+ return list_sensor_pairs