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