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.
- cars/__init__.py +86 -0
- cars/applications/__init__.py +40 -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 +105 -0
- cars/applications/auxiliary_filling/auxiliary_filling_algo.py +475 -0
- cars/applications/auxiliary_filling/auxiliary_filling_from_sensors_app.py +632 -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 +641 -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 +1461 -0
- cars/applications/dense_matching/cpp/__init__.py +0 -0
- cars/applications/dense_matching/cpp/dense_matching_cpp.cp312-win_amd64.dll.a +0 -0
- cars/applications/dense_matching/cpp/dense_matching_cpp.cp312-win_amd64.pyd +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 +597 -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 +278 -0
- cars/applications/dsm_filling/bulldozer_config/base_config.yaml +44 -0
- cars/applications/dsm_filling/bulldozer_filling_app.py +288 -0
- cars/applications/dsm_filling/exogenous_filling_app.py +341 -0
- cars/applications/dsm_merging/__init__.py +28 -0
- cars/applications/dsm_merging/abstract_dsm_merging_app.py +101 -0
- cars/applications/dsm_merging/weighted_fusion_app.py +639 -0
- cars/applications/grid_correction/__init__.py +30 -0
- cars/applications/grid_correction/abstract_grid_correction_app.py +103 -0
- cars/applications/grid_correction/grid_correction_app.py +557 -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_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 +522 -0
- cars/applications/point_cloud_outlier_removal/statistical_app.py +528 -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 +639 -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 +760 -0
- cars/applications/resampling/resampling_algo.py +590 -0
- cars/applications/resampling/resampling_constants.py +36 -0
- cars/applications/resampling/resampling_wrappers.py +309 -0
- cars/applications/sensors_subsampling/__init__.py +32 -0
- cars/applications/sensors_subsampling/abstract_subsampling_app.py +109 -0
- cars/applications/sensors_subsampling/rasterio_subsampling_app.py +420 -0
- cars/applications/sensors_subsampling/subsampling_algo.py +108 -0
- cars/applications/sparse_matching/__init__.py +30 -0
- cars/applications/sparse_matching/abstract_sparse_matching_app.py +599 -0
- cars/applications/sparse_matching/sift_app.py +724 -0
- cars/applications/sparse_matching/sparse_matching_algo.py +360 -0
- cars/applications/sparse_matching/sparse_matching_constants.py +66 -0
- cars/applications/sparse_matching/sparse_matching_wrappers.py +282 -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 +750 -0
- cars/cars.py +179 -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 +1544 -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 +250 -0
- cars/orchestrator/cluster/abstract_dask_cluster.py +381 -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 +728 -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 +986 -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/memory_tools.py +47 -0
- cars/orchestrator/orchestrator.py +755 -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 +4 -0
- cars/pipelines/conf_resolution/conf_intermediate_resolution.yaml +2 -0
- cars/pipelines/default/__init__.py +26 -0
- cars/pipelines/default/default_pipeline.py +1088 -0
- cars/pipelines/filling/__init__.py +26 -0
- cars/pipelines/filling/filling.py +981 -0
- cars/pipelines/formatting/__init__.py +26 -0
- cars/pipelines/formatting/formatting.py +186 -0
- cars/pipelines/merging/__init__.py +26 -0
- cars/pipelines/merging/merging.py +439 -0
- cars/pipelines/parameters/__init__.py +0 -0
- cars/pipelines/parameters/advanced_parameters.py +256 -0
- cars/pipelines/parameters/advanced_parameters_constants.py +68 -0
- cars/pipelines/parameters/application_parameters.py +72 -0
- cars/pipelines/parameters/depth_map_inputs.py +0 -0
- cars/pipelines/parameters/dsm_inputs.py +349 -0
- cars/pipelines/parameters/dsm_inputs_constants.py +25 -0
- cars/pipelines/parameters/output_constants.py +52 -0
- cars/pipelines/parameters/output_parameters.py +438 -0
- cars/pipelines/parameters/sensor_inputs.py +859 -0
- cars/pipelines/parameters/sensor_inputs_constants.py +51 -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 +38 -0
- cars/pipelines/pipeline_template.py +135 -0
- cars/pipelines/subsampling/__init__.py +26 -0
- cars/pipelines/subsampling/subsampling.py +358 -0
- cars/pipelines/surface_modeling/__init__.py +26 -0
- cars/pipelines/surface_modeling/surface_modeling.py +2098 -0
- cars/pipelines/tie_points/__init__.py +26 -0
- cars/pipelines/tie_points/tie_points.py +536 -0
- cars/starter.py +167 -0
- cars-1.0.0rc2.dist-info/DELVEWHEEL +2 -0
- cars-1.0.0rc2.dist-info/METADATA +289 -0
- cars-1.0.0rc2.dist-info/RECORD +225 -0
- cars-1.0.0rc2.dist-info/WHEEL +4 -0
- cars-1.0.0rc2.dist-info/entry_points.txt +8 -0
- cars.libs/libgcc_s_seh-1-b2494fcbd4d80cf2c98fdd5261f6d850.dll +0 -0
- cars.libs/libstdc++-6-e9b0d12ae0e9555bbae55e8dfd08c3f7.dll +0 -0
- cars.libs/libwinpthread-1-7882d1b093714ccdfaf4e0789a817792.dll +0 -0
|
@@ -0,0 +1,981 @@
|
|
|
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 filling pipeline class file
|
|
29
|
+
"""
|
|
30
|
+
# pylint: disable=C0302
|
|
31
|
+
from __future__ import print_function
|
|
32
|
+
|
|
33
|
+
import copy
|
|
34
|
+
import logging
|
|
35
|
+
import os
|
|
36
|
+
import warnings
|
|
37
|
+
from collections import OrderedDict
|
|
38
|
+
|
|
39
|
+
import numpy as np
|
|
40
|
+
import rasterio
|
|
41
|
+
from json_checker import Checker, OptionalKey, Or
|
|
42
|
+
from pyproj import CRS
|
|
43
|
+
from rasterio.errors import NodataShadowWarning
|
|
44
|
+
|
|
45
|
+
from cars.applications.application import Application
|
|
46
|
+
from cars.core import cars_logging, inputs, projection
|
|
47
|
+
from cars.core.inputs import read_vector
|
|
48
|
+
from cars.core.utils import safe_makedirs
|
|
49
|
+
from cars.orchestrator import orchestrator
|
|
50
|
+
from cars.orchestrator.cluster.log_wrapper import cars_profile
|
|
51
|
+
from cars.pipelines.parameters import advanced_parameters
|
|
52
|
+
from cars.pipelines.parameters import advanced_parameters_constants as adv_cst
|
|
53
|
+
from cars.pipelines.parameters import output_constants as out_cst
|
|
54
|
+
from cars.pipelines.parameters import output_parameters, sensor_inputs
|
|
55
|
+
from cars.pipelines.parameters import sensor_inputs_constants as sens_cst
|
|
56
|
+
from cars.pipelines.parameters.output_constants import AUXILIARY
|
|
57
|
+
from cars.pipelines.pipeline import Pipeline
|
|
58
|
+
from cars.pipelines.pipeline_constants import (
|
|
59
|
+
ADVANCED,
|
|
60
|
+
APPLICATIONS,
|
|
61
|
+
INPUT,
|
|
62
|
+
ORCHESTRATOR,
|
|
63
|
+
OUTPUT,
|
|
64
|
+
)
|
|
65
|
+
from cars.pipelines.pipeline_template import PipelineTemplate
|
|
66
|
+
|
|
67
|
+
PIPELINE = "filling"
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
@Pipeline.register(
|
|
71
|
+
PIPELINE,
|
|
72
|
+
)
|
|
73
|
+
class FillingPipeline(PipelineTemplate):
|
|
74
|
+
"""
|
|
75
|
+
FillingPipeline
|
|
76
|
+
"""
|
|
77
|
+
|
|
78
|
+
# pylint: disable=too-many-instance-attributes
|
|
79
|
+
|
|
80
|
+
def __init__(self, conf, config_dir=None, pre_check=False): # noqa: C901
|
|
81
|
+
"""
|
|
82
|
+
Creates pipeline
|
|
83
|
+
|
|
84
|
+
:param pipeline_name: name of the pipeline.
|
|
85
|
+
:type pipeline_name: str
|
|
86
|
+
:param cfg: configuration {'matching_cost_method': value}
|
|
87
|
+
:type cfg: dictionary
|
|
88
|
+
:param config_dir: path to dir containing json or yaml file
|
|
89
|
+
:type config_dir: str
|
|
90
|
+
"""
|
|
91
|
+
|
|
92
|
+
self.config_dir = config_dir
|
|
93
|
+
# Transform relative path to absolute path
|
|
94
|
+
if config_dir is not None:
|
|
95
|
+
config_dir = os.path.abspath(config_dir)
|
|
96
|
+
|
|
97
|
+
# Check global conf
|
|
98
|
+
self.check_global_schema(conf)
|
|
99
|
+
|
|
100
|
+
if PIPELINE in conf:
|
|
101
|
+
self.check_pipeline_conf(conf)
|
|
102
|
+
|
|
103
|
+
self.out_dir = os.path.abspath(conf[OUTPUT][out_cst.OUT_DIRECTORY])
|
|
104
|
+
|
|
105
|
+
self.filling_dir = os.path.join(self.out_dir, "filling")
|
|
106
|
+
|
|
107
|
+
# Check input
|
|
108
|
+
conf[INPUT] = self.check_inputs(conf)
|
|
109
|
+
|
|
110
|
+
pipeline_conf = conf.get(PIPELINE, {})
|
|
111
|
+
|
|
112
|
+
# check advanced
|
|
113
|
+
conf[PIPELINE][ADVANCED] = self.check_advanced(
|
|
114
|
+
pipeline_conf, conf[INPUT]
|
|
115
|
+
)
|
|
116
|
+
# check output
|
|
117
|
+
conf[OUTPUT] = self.check_output(conf)
|
|
118
|
+
|
|
119
|
+
self.used_conf = {}
|
|
120
|
+
|
|
121
|
+
# Check conf orchestrator
|
|
122
|
+
self.used_conf[ORCHESTRATOR] = self.check_orchestrator(
|
|
123
|
+
conf.get(ORCHESTRATOR, None)
|
|
124
|
+
)
|
|
125
|
+
self.used_conf[INPUT] = conf[INPUT]
|
|
126
|
+
self.used_conf[OUTPUT] = conf[OUTPUT]
|
|
127
|
+
self.used_conf[ADVANCED] = conf[PIPELINE][ADVANCED]
|
|
128
|
+
self.save_all_intermediate_data = self.used_conf[ADVANCED][
|
|
129
|
+
adv_cst.SAVE_INTERMEDIATE_DATA
|
|
130
|
+
]
|
|
131
|
+
|
|
132
|
+
filling_applications = self.generate_filling_applications(
|
|
133
|
+
self.used_conf[INPUT]
|
|
134
|
+
)
|
|
135
|
+
|
|
136
|
+
applications_conf = self.overide_pipeline_conf(
|
|
137
|
+
pipeline_conf.get(APPLICATIONS, {}),
|
|
138
|
+
filling_applications,
|
|
139
|
+
append_classification=True,
|
|
140
|
+
)
|
|
141
|
+
|
|
142
|
+
self.used_conf[APPLICATIONS] = self.check_applications(
|
|
143
|
+
applications_conf
|
|
144
|
+
)
|
|
145
|
+
# Used classification values, for filling -> will be masked
|
|
146
|
+
self.used_classif_values_for_filling = self.get_classif_values_filling(
|
|
147
|
+
self.used_conf[INPUT]
|
|
148
|
+
)
|
|
149
|
+
self.dump_dir = os.path.join(self.filling_dir, "dump_dir")
|
|
150
|
+
|
|
151
|
+
if isinstance(self.used_conf[INPUT]["dsm_to_fill"], str):
|
|
152
|
+
self.dsm_to_fill = {"dsm": self.used_conf[INPUT]["dsm_to_fill"]}
|
|
153
|
+
else:
|
|
154
|
+
self.dsm_to_fill = self.used_conf[INPUT]["dsm_to_fill"]
|
|
155
|
+
|
|
156
|
+
if not pre_check:
|
|
157
|
+
for key, path in self.dsm_to_fill.items():
|
|
158
|
+
self.dsm_to_fill[key] = os.path.abspath(path)
|
|
159
|
+
|
|
160
|
+
raster_crs = inputs.rasterio_get_crs(self.dsm_to_fill["dsm"])
|
|
161
|
+
|
|
162
|
+
crs = CRS.from_user_input(raster_crs)
|
|
163
|
+
|
|
164
|
+
# Un CRS COMPOSÉ contient 2 sous-CRS : horizontal + vertical
|
|
165
|
+
if len(crs.sub_crs_list) == 2:
|
|
166
|
+
self.epsg = crs.sub_crs_list[0].to_epsg()
|
|
167
|
+
self.vertical_crs = inputs.rasterio_get_crs(
|
|
168
|
+
self.dsm_to_fill["dsm"]
|
|
169
|
+
)
|
|
170
|
+
|
|
171
|
+
def check_pipeline_conf(self, conf):
|
|
172
|
+
"""
|
|
173
|
+
Check pipeline configuration
|
|
174
|
+
"""
|
|
175
|
+
|
|
176
|
+
# Validate inputs
|
|
177
|
+
pipeline_schema = {
|
|
178
|
+
OptionalKey(ADVANCED): dict,
|
|
179
|
+
OptionalKey(APPLICATIONS): dict,
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
checker_inputs = Checker(pipeline_schema)
|
|
183
|
+
checker_inputs.validate(conf[PIPELINE])
|
|
184
|
+
|
|
185
|
+
def check_inputs(self, conf, config_json_dir=None):
|
|
186
|
+
"""
|
|
187
|
+
Check the inputs given
|
|
188
|
+
|
|
189
|
+
:param conf: configuration
|
|
190
|
+
:type conf: dict
|
|
191
|
+
:param config_dir: directory of used json, if
|
|
192
|
+
user filled paths with relative paths
|
|
193
|
+
:type config_dir: str
|
|
194
|
+
|
|
195
|
+
:return: overloader inputs
|
|
196
|
+
:rtype: dict
|
|
197
|
+
"""
|
|
198
|
+
return sensor_inputs.sensors_check_inputs(
|
|
199
|
+
conf[INPUT], config_dir=config_json_dir
|
|
200
|
+
)
|
|
201
|
+
|
|
202
|
+
def check_output(self, conf):
|
|
203
|
+
"""
|
|
204
|
+
Check the output given
|
|
205
|
+
|
|
206
|
+
:param conf: configuration of output
|
|
207
|
+
:type conf: dict
|
|
208
|
+
|
|
209
|
+
:return overloader output
|
|
210
|
+
:rtype : dict
|
|
211
|
+
"""
|
|
212
|
+
conf_output, _ = output_parameters.check_output_parameters(
|
|
213
|
+
conf[INPUT], conf[OUTPUT], self.scaling_coeff
|
|
214
|
+
)
|
|
215
|
+
return conf_output
|
|
216
|
+
|
|
217
|
+
def check_advanced(self, conf, conf_input, output_dem_dir=None):
|
|
218
|
+
"""
|
|
219
|
+
Check all conf for advanced configuration
|
|
220
|
+
|
|
221
|
+
:return: overridden advanced conf
|
|
222
|
+
:rtype: dict
|
|
223
|
+
"""
|
|
224
|
+
|
|
225
|
+
conf_advanced = conf.get(ADVANCED, {})
|
|
226
|
+
|
|
227
|
+
inputs_conf = conf_input
|
|
228
|
+
|
|
229
|
+
overloaded_conf = conf_advanced.copy()
|
|
230
|
+
|
|
231
|
+
overloaded_conf[adv_cst.SAVE_INTERMEDIATE_DATA] = conf.get(
|
|
232
|
+
adv_cst.SAVE_INTERMEDIATE_DATA, False
|
|
233
|
+
)
|
|
234
|
+
# Check geometry plugin and overwrite geomodel in conf inputs
|
|
235
|
+
(
|
|
236
|
+
inputs_conf,
|
|
237
|
+
overloaded_conf[adv_cst.GEOMETRY_PLUGIN],
|
|
238
|
+
self.geom_plugin_without_dem_and_geoid,
|
|
239
|
+
self.geom_plugin_with_dem_and_geoid,
|
|
240
|
+
self.scaling_coeff,
|
|
241
|
+
) = sensor_inputs.check_geometry_plugin(
|
|
242
|
+
inputs_conf,
|
|
243
|
+
conf_advanced.get(adv_cst.GEOMETRY_PLUGIN, None),
|
|
244
|
+
output_dem_dir,
|
|
245
|
+
)
|
|
246
|
+
|
|
247
|
+
schema = {
|
|
248
|
+
adv_cst.SAVE_INTERMEDIATE_DATA: Or(dict, bool),
|
|
249
|
+
adv_cst.GEOMETRY_PLUGIN: Or(str, dict),
|
|
250
|
+
}
|
|
251
|
+
checker_advanced_parameters = Checker(schema)
|
|
252
|
+
checker_advanced_parameters.validate(overloaded_conf)
|
|
253
|
+
|
|
254
|
+
return overloaded_conf
|
|
255
|
+
|
|
256
|
+
def check_applications(self, conf):
|
|
257
|
+
"""
|
|
258
|
+
Check the given configuration for applications
|
|
259
|
+
|
|
260
|
+
:param conf: configuration of applications
|
|
261
|
+
:type conf: dict
|
|
262
|
+
"""
|
|
263
|
+
used_conf = {}
|
|
264
|
+
self.dsm_filling_apps = {}
|
|
265
|
+
|
|
266
|
+
needed_applications = []
|
|
267
|
+
needed_applications += [
|
|
268
|
+
"auxiliary_filling",
|
|
269
|
+
]
|
|
270
|
+
|
|
271
|
+
for key in conf:
|
|
272
|
+
if key.startswith("dsm_filling"):
|
|
273
|
+
needed_applications += [key]
|
|
274
|
+
|
|
275
|
+
for app_key in needed_applications:
|
|
276
|
+
used_conf[app_key] = conf.get(app_key, {})
|
|
277
|
+
|
|
278
|
+
if used_conf[app_key] is None:
|
|
279
|
+
continue
|
|
280
|
+
used_conf[app_key]["save_intermediate_data"] = (
|
|
281
|
+
self.save_all_intermediate_data
|
|
282
|
+
or used_conf[app_key].get("save_intermediate_data", False)
|
|
283
|
+
)
|
|
284
|
+
|
|
285
|
+
if app_key == "auxiliary_filling":
|
|
286
|
+
if used_conf[app_key] is not None:
|
|
287
|
+
used_conf[app_key]["activated"] = used_conf[app_key].get(
|
|
288
|
+
"activated", True
|
|
289
|
+
)
|
|
290
|
+
|
|
291
|
+
for app_key, app_conf in used_conf.items():
|
|
292
|
+
if not app_key.startswith("dsm_filling"):
|
|
293
|
+
continue
|
|
294
|
+
|
|
295
|
+
if app_conf is None:
|
|
296
|
+
self.dsm_filling_apps = {}
|
|
297
|
+
# keep over multiple runs
|
|
298
|
+
used_conf["dsm_filling"] = None
|
|
299
|
+
break
|
|
300
|
+
|
|
301
|
+
if app_key in self.dsm_filling_apps:
|
|
302
|
+
msg = (
|
|
303
|
+
f"The key {app_key} is defined twice in the input "
|
|
304
|
+
"configuration."
|
|
305
|
+
)
|
|
306
|
+
logging.error(msg)
|
|
307
|
+
raise NameError(msg)
|
|
308
|
+
|
|
309
|
+
if app_key[11:] == ".1":
|
|
310
|
+
app_conf.setdefault("method", "exogenous_filling")
|
|
311
|
+
if app_key[11:] == ".2":
|
|
312
|
+
app_conf.setdefault("method", "bulldozer")
|
|
313
|
+
if app_key[11:] == ".3":
|
|
314
|
+
app_conf.setdefault("method", "border_interpolation")
|
|
315
|
+
|
|
316
|
+
self.dsm_filling_apps[app_key] = Application(
|
|
317
|
+
"dsm_filling",
|
|
318
|
+
cfg=app_conf,
|
|
319
|
+
scaling_coeff=self.scaling_coeff,
|
|
320
|
+
)
|
|
321
|
+
used_conf[app_key] = self.dsm_filling_apps[app_key].get_conf()
|
|
322
|
+
|
|
323
|
+
methods_str = "\n".join(
|
|
324
|
+
f" - {k}={a.used_method}" for k, a in self.dsm_filling_apps.items()
|
|
325
|
+
)
|
|
326
|
+
logging.info(
|
|
327
|
+
"{} dsm filling apps registered:\n{}".format(
|
|
328
|
+
len(self.dsm_filling_apps), methods_str
|
|
329
|
+
)
|
|
330
|
+
)
|
|
331
|
+
|
|
332
|
+
# Auxiliary filling
|
|
333
|
+
self.auxiliary_filling_application = Application(
|
|
334
|
+
"auxiliary_filling",
|
|
335
|
+
cfg=conf.get("auxiliary_filling", {}),
|
|
336
|
+
scaling_coeff=self.scaling_coeff,
|
|
337
|
+
)
|
|
338
|
+
used_conf["auxiliary_filling"] = (
|
|
339
|
+
self.auxiliary_filling_application.get_conf()
|
|
340
|
+
)
|
|
341
|
+
|
|
342
|
+
# MNT generation
|
|
343
|
+
self.dem_generation_application = Application(
|
|
344
|
+
"dem_generation",
|
|
345
|
+
cfg=used_conf.get("dem_generation", {}),
|
|
346
|
+
scaling_coeff=self.scaling_coeff,
|
|
347
|
+
)
|
|
348
|
+
used_conf["dem_generation"] = self.dem_generation_application.get_conf()
|
|
349
|
+
|
|
350
|
+
return used_conf
|
|
351
|
+
|
|
352
|
+
def generate_filling_applications(self, inputs_conf):
|
|
353
|
+
"""
|
|
354
|
+
Generate filling applications configuration according to inputs
|
|
355
|
+
|
|
356
|
+
:param inputs_conf: inputs configuration
|
|
357
|
+
:type inputs_conf: dict
|
|
358
|
+
"""
|
|
359
|
+
|
|
360
|
+
filling_applications = {}
|
|
361
|
+
|
|
362
|
+
# Generate applications configuration
|
|
363
|
+
for filling_name, classif_values in inputs_conf[
|
|
364
|
+
sens_cst.FILLING
|
|
365
|
+
].items():
|
|
366
|
+
# No filling
|
|
367
|
+
if classif_values is None:
|
|
368
|
+
continue
|
|
369
|
+
|
|
370
|
+
classif_values = list(map(str, classif_values))
|
|
371
|
+
|
|
372
|
+
# Update application configuration
|
|
373
|
+
if filling_name == "fill_with_geoid":
|
|
374
|
+
new_filling_conf = {
|
|
375
|
+
"dsm_filling.1": {
|
|
376
|
+
"method": "exogenous_filling",
|
|
377
|
+
"classification": classif_values,
|
|
378
|
+
"fill_with_geoid": classif_values,
|
|
379
|
+
},
|
|
380
|
+
}
|
|
381
|
+
elif filling_name == "interpolate_from_borders":
|
|
382
|
+
new_filling_conf = {
|
|
383
|
+
"dsm_filling.2": {
|
|
384
|
+
"method": "bulldozer",
|
|
385
|
+
"classification": classif_values,
|
|
386
|
+
},
|
|
387
|
+
"dsm_filling.3": {
|
|
388
|
+
"method": "border_interpolation",
|
|
389
|
+
"classification": classif_values,
|
|
390
|
+
},
|
|
391
|
+
}
|
|
392
|
+
elif filling_name == "fill_with_endogenous_dem":
|
|
393
|
+
new_filling_conf = {
|
|
394
|
+
"dsm_filling.1": {
|
|
395
|
+
"method": "exogenous_filling",
|
|
396
|
+
"classification": classif_values,
|
|
397
|
+
},
|
|
398
|
+
"dsm_filling.2": {
|
|
399
|
+
"method": "bulldozer",
|
|
400
|
+
"classification": classif_values,
|
|
401
|
+
},
|
|
402
|
+
}
|
|
403
|
+
elif filling_name == "fill_with_exogenous_dem":
|
|
404
|
+
new_filling_conf = {
|
|
405
|
+
"dsm_filling.2": {
|
|
406
|
+
"method": "bulldozer",
|
|
407
|
+
"classification": classif_values,
|
|
408
|
+
},
|
|
409
|
+
}
|
|
410
|
+
else:
|
|
411
|
+
new_filling_conf = {}
|
|
412
|
+
|
|
413
|
+
# Update application configuration
|
|
414
|
+
filling_applications = self.overide_pipeline_conf(
|
|
415
|
+
filling_applications,
|
|
416
|
+
new_filling_conf,
|
|
417
|
+
append_classification=True,
|
|
418
|
+
)
|
|
419
|
+
|
|
420
|
+
return filling_applications
|
|
421
|
+
|
|
422
|
+
def overide_pipeline_conf(
|
|
423
|
+
self, conf, overiding_conf, append_classification=False
|
|
424
|
+
):
|
|
425
|
+
"""
|
|
426
|
+
Merge two dictionaries recursively without removing keys
|
|
427
|
+
from the base conf.
|
|
428
|
+
|
|
429
|
+
:param conf: base configuration dictionary
|
|
430
|
+
:type conf: dict
|
|
431
|
+
:param overiding_conf: overriding configuration dictionary
|
|
432
|
+
:type overiding_conf: dict
|
|
433
|
+
:return: merged configuration
|
|
434
|
+
:rtype: dict
|
|
435
|
+
"""
|
|
436
|
+
result = copy.deepcopy(conf)
|
|
437
|
+
|
|
438
|
+
def merge_recursive(base_dict, override_dict):
|
|
439
|
+
"""
|
|
440
|
+
Main recursive function
|
|
441
|
+
"""
|
|
442
|
+
for key, value in override_dict.items():
|
|
443
|
+
if (
|
|
444
|
+
key in base_dict
|
|
445
|
+
and isinstance(base_dict[key], dict)
|
|
446
|
+
and isinstance(value, dict)
|
|
447
|
+
):
|
|
448
|
+
merge_recursive(base_dict[key], value)
|
|
449
|
+
elif (
|
|
450
|
+
append_classification
|
|
451
|
+
and key in base_dict
|
|
452
|
+
and isinstance(base_dict[key], list)
|
|
453
|
+
and isinstance(value, list)
|
|
454
|
+
and key == "classification"
|
|
455
|
+
):
|
|
456
|
+
# extend list, avoiding duplicates
|
|
457
|
+
base_dict[key] = list(
|
|
458
|
+
OrderedDict.fromkeys(base_dict[key] + value)
|
|
459
|
+
)
|
|
460
|
+
else:
|
|
461
|
+
base_dict[key] = value
|
|
462
|
+
|
|
463
|
+
merge_recursive(result, overiding_conf)
|
|
464
|
+
|
|
465
|
+
return result
|
|
466
|
+
|
|
467
|
+
def get_classif_values_filling(self, inputs_conf):
|
|
468
|
+
"""
|
|
469
|
+
Get values in classif, used for filling
|
|
470
|
+
|
|
471
|
+
:param inputs_conf: inputs
|
|
472
|
+
:type inputs_conf: dict
|
|
473
|
+
|
|
474
|
+
:return: list of values
|
|
475
|
+
:rtype: list
|
|
476
|
+
"""
|
|
477
|
+
|
|
478
|
+
if (
|
|
479
|
+
sens_cst.FILLING not in inputs_conf
|
|
480
|
+
or inputs_conf[sens_cst.FILLING] is None
|
|
481
|
+
):
|
|
482
|
+
logging.info("No filling in input configuration")
|
|
483
|
+
return None
|
|
484
|
+
|
|
485
|
+
filling_classif_values = []
|
|
486
|
+
for _, classif_values in inputs_conf[sens_cst.FILLING].items():
|
|
487
|
+
# Add new value to filling bands
|
|
488
|
+
if classif_values is not None:
|
|
489
|
+
if isinstance(classif_values, str):
|
|
490
|
+
classif_values = [classif_values]
|
|
491
|
+
filling_classif_values += classif_values
|
|
492
|
+
|
|
493
|
+
simplified_list = list(OrderedDict.fromkeys(filling_classif_values))
|
|
494
|
+
res_as_string_list = [str(value) for value in simplified_list]
|
|
495
|
+
return res_as_string_list
|
|
496
|
+
|
|
497
|
+
@cars_profile(name="merge filling bands", interval=0.5)
|
|
498
|
+
def merge_filling_bands(self, filling_path, aux_filling, dsm_file):
|
|
499
|
+
"""
|
|
500
|
+
Merge filling bands to get mono band in output
|
|
501
|
+
"""
|
|
502
|
+
|
|
503
|
+
with rasterio.open(dsm_file) as in_dsm:
|
|
504
|
+
dsm_msk = in_dsm.read_masks(1)
|
|
505
|
+
|
|
506
|
+
with rasterio.open(filling_path) as src:
|
|
507
|
+
nb_bands = src.count
|
|
508
|
+
|
|
509
|
+
if nb_bands == 1:
|
|
510
|
+
return False
|
|
511
|
+
|
|
512
|
+
filling_multi_bands = src.read()
|
|
513
|
+
filling_mono_bands = np.zeros(filling_multi_bands.shape[1:3])
|
|
514
|
+
descriptions = src.descriptions
|
|
515
|
+
dict_temp = {name: i for i, name in enumerate(descriptions)}
|
|
516
|
+
profile = src.profile
|
|
517
|
+
|
|
518
|
+
with warnings.catch_warnings():
|
|
519
|
+
warnings.simplefilter("ignore", NodataShadowWarning)
|
|
520
|
+
filling_mask = src.read_masks(1)
|
|
521
|
+
|
|
522
|
+
filling_mono_bands[filling_mask == 0] = 0
|
|
523
|
+
|
|
524
|
+
filling_bands_list = {
|
|
525
|
+
"fill_with_geoid": ["filling_exogenous"],
|
|
526
|
+
"interpolate_from_borders": [
|
|
527
|
+
"bulldozer",
|
|
528
|
+
"border_interpolation",
|
|
529
|
+
],
|
|
530
|
+
"fill_with_endogenous_dem": [
|
|
531
|
+
"filling_exogenous",
|
|
532
|
+
"bulldozer",
|
|
533
|
+
],
|
|
534
|
+
"fill_with_exogenous_dem": ["bulldozer"],
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
# To get the right footprint
|
|
538
|
+
filling_mono_bands = np.logical_or(dsm_msk, filling_mask).astype(
|
|
539
|
+
np.uint8
|
|
540
|
+
)
|
|
541
|
+
|
|
542
|
+
# to keep the previous classif convention
|
|
543
|
+
filling_mono_bands[filling_mono_bands == 0] = src.nodata
|
|
544
|
+
filling_mono_bands[filling_mono_bands == 1] = 0
|
|
545
|
+
|
|
546
|
+
no_match = False
|
|
547
|
+
for key, value in aux_filling.items():
|
|
548
|
+
if isinstance(value, str):
|
|
549
|
+
value = [value]
|
|
550
|
+
|
|
551
|
+
if isinstance(value, list):
|
|
552
|
+
for elem in value:
|
|
553
|
+
if elem != "other":
|
|
554
|
+
filling_method = filling_bands_list[elem]
|
|
555
|
+
|
|
556
|
+
if all(
|
|
557
|
+
method in descriptions
|
|
558
|
+
for method in filling_method
|
|
559
|
+
):
|
|
560
|
+
indices_true = [
|
|
561
|
+
dict_temp[m] for m in filling_method
|
|
562
|
+
]
|
|
563
|
+
|
|
564
|
+
mask_true = np.all(
|
|
565
|
+
filling_multi_bands[indices_true, :, :]
|
|
566
|
+
== 1,
|
|
567
|
+
axis=0,
|
|
568
|
+
)
|
|
569
|
+
|
|
570
|
+
indices_false = [
|
|
571
|
+
i
|
|
572
|
+
for i in range(filling_multi_bands.shape[0])
|
|
573
|
+
if i not in indices_true
|
|
574
|
+
]
|
|
575
|
+
|
|
576
|
+
mask_false = np.all(
|
|
577
|
+
filling_multi_bands[indices_false, :, :]
|
|
578
|
+
== 0,
|
|
579
|
+
axis=0,
|
|
580
|
+
)
|
|
581
|
+
|
|
582
|
+
mask = mask_true & mask_false
|
|
583
|
+
|
|
584
|
+
filling_mono_bands[mask] = key
|
|
585
|
+
else:
|
|
586
|
+
no_match = True
|
|
587
|
+
|
|
588
|
+
if no_match:
|
|
589
|
+
mask_1 = np.all(
|
|
590
|
+
filling_multi_bands[1:, :, :] == 1,
|
|
591
|
+
axis=0,
|
|
592
|
+
)
|
|
593
|
+
|
|
594
|
+
mask_2 = np.all(
|
|
595
|
+
filling_mono_bands == 0,
|
|
596
|
+
axis=0,
|
|
597
|
+
)
|
|
598
|
+
|
|
599
|
+
filling_mono_bands[mask_1 & mask_2] = (
|
|
600
|
+
aux_filling["other"] if "other" in aux_filling else 50
|
|
601
|
+
)
|
|
602
|
+
|
|
603
|
+
profile.update(count=1, dtype=filling_mono_bands.dtype)
|
|
604
|
+
with rasterio.open(filling_path, "w", **profile) as src:
|
|
605
|
+
src.write(filling_mono_bands, 1)
|
|
606
|
+
|
|
607
|
+
return True
|
|
608
|
+
|
|
609
|
+
@cars_profile(name="merge classif bands", interval=0.5)
|
|
610
|
+
def merge_classif_bands(self, classif_path, aux_classif, dsm_file):
|
|
611
|
+
"""
|
|
612
|
+
Merge classif bands to get mono band in output
|
|
613
|
+
"""
|
|
614
|
+
with rasterio.open(dsm_file) as in_dsm:
|
|
615
|
+
dsm_msk = in_dsm.read_masks(1)
|
|
616
|
+
|
|
617
|
+
with rasterio.open(classif_path) as src:
|
|
618
|
+
nb_bands = src.count
|
|
619
|
+
|
|
620
|
+
if nb_bands == 1:
|
|
621
|
+
return False
|
|
622
|
+
|
|
623
|
+
classif_multi_bands = src.read()
|
|
624
|
+
classif_mono_band = np.zeros(classif_multi_bands.shape[1:3])
|
|
625
|
+
descriptions = src.descriptions
|
|
626
|
+
profile = src.profile
|
|
627
|
+
classif_mask = src.read_masks(1)
|
|
628
|
+
classif_mono_band[classif_mask == 0] = 0
|
|
629
|
+
|
|
630
|
+
# To get the right footprint
|
|
631
|
+
classif_mono_band = np.logical_or(dsm_msk, classif_mask).astype(
|
|
632
|
+
np.uint8
|
|
633
|
+
)
|
|
634
|
+
|
|
635
|
+
# to keep the previous classif convention
|
|
636
|
+
classif_mono_band[classif_mono_band == 0] = src.nodata
|
|
637
|
+
classif_mono_band[classif_mono_band == 1] = 0
|
|
638
|
+
|
|
639
|
+
for key, value in aux_classif.items():
|
|
640
|
+
if isinstance(value, int):
|
|
641
|
+
num_band = descriptions.index(str(value))
|
|
642
|
+
mask_1 = classif_mono_band == 0
|
|
643
|
+
mask_2 = classif_multi_bands[num_band, :, :] == 1
|
|
644
|
+
classif_mono_band[mask_1 & mask_2] = key
|
|
645
|
+
elif isinstance(value, list):
|
|
646
|
+
for elem in value:
|
|
647
|
+
num_band = descriptions.index(str(elem))
|
|
648
|
+
mask_1 = classif_mono_band == 0
|
|
649
|
+
mask_2 = classif_multi_bands[num_band, :, :] == 1
|
|
650
|
+
classif_mono_band[mask_1 & mask_2] = key
|
|
651
|
+
|
|
652
|
+
profile.update(count=1, dtype=classif_mono_band.dtype)
|
|
653
|
+
with rasterio.open(classif_path, "w", **profile) as src:
|
|
654
|
+
src.write(classif_mono_band, 1)
|
|
655
|
+
|
|
656
|
+
return True
|
|
657
|
+
|
|
658
|
+
def monoband_to_multiband(self, input_raster, output_raster, bands_classif):
|
|
659
|
+
"""
|
|
660
|
+
Convert classification from monoband to multiband
|
|
661
|
+
|
|
662
|
+
:param input_raster: the intput classification path
|
|
663
|
+
:type input_raster: str
|
|
664
|
+
:param output_raster: the output classification path
|
|
665
|
+
:type output_raster: str
|
|
666
|
+
:param bands_classif: the bands values
|
|
667
|
+
:type bands_classif: list
|
|
668
|
+
"""
|
|
669
|
+
|
|
670
|
+
with rasterio.open(input_raster) as src:
|
|
671
|
+
mono = src.read(1)
|
|
672
|
+
mono_msk = src.read_masks(1)
|
|
673
|
+
profile = src.profile
|
|
674
|
+
nodata_value = src.nodata
|
|
675
|
+
|
|
676
|
+
multiband = np.zeros(
|
|
677
|
+
(len(bands_classif), mono.shape[0], mono.shape[1]), dtype=np.uint8
|
|
678
|
+
)
|
|
679
|
+
multiband_msk = np.broadcast_to(mono_msk, multiband.shape)
|
|
680
|
+
|
|
681
|
+
for i, cls in enumerate(bands_classif):
|
|
682
|
+
multiband[i] = mono == cls
|
|
683
|
+
|
|
684
|
+
multiband[multiband_msk == 0] = nodata_value
|
|
685
|
+
|
|
686
|
+
profile.update(count=len(bands_classif))
|
|
687
|
+
|
|
688
|
+
with rasterio.open(output_raster, "w", **profile) as dst:
|
|
689
|
+
dst.write(multiband)
|
|
690
|
+
|
|
691
|
+
for i, cls in enumerate(bands_classif, start=1):
|
|
692
|
+
dst.update_tags(band=i, class_name=str(cls))
|
|
693
|
+
dst.set_band_description(i, str(cls))
|
|
694
|
+
|
|
695
|
+
return output_raster
|
|
696
|
+
|
|
697
|
+
def filling(self): # noqa: C901
|
|
698
|
+
"""
|
|
699
|
+
Filling method
|
|
700
|
+
"""
|
|
701
|
+
inputs_conf = self.used_conf[INPUT]
|
|
702
|
+
|
|
703
|
+
dsm_file_name = self.dsm_to_fill["dsm"]
|
|
704
|
+
|
|
705
|
+
self.texture_bands = self.used_conf[OUTPUT][AUXILIARY][
|
|
706
|
+
out_cst.AUX_IMAGE
|
|
707
|
+
]
|
|
708
|
+
|
|
709
|
+
dsm_filled_dir = os.path.join(self.filling_dir, "dsm/")
|
|
710
|
+
os.makedirs(dsm_filled_dir, exist_ok=True)
|
|
711
|
+
|
|
712
|
+
color_file_name = (
|
|
713
|
+
self.dsm_to_fill["image"]
|
|
714
|
+
if "image" in self.used_conf[INPUT]["dsm_to_fill"]
|
|
715
|
+
else None
|
|
716
|
+
)
|
|
717
|
+
|
|
718
|
+
first_key = list(inputs_conf[sens_cst.SENSORS].keys())[0]
|
|
719
|
+
input_classif = inputs_conf[sens_cst.SENSORS][first_key][
|
|
720
|
+
sens_cst.INPUT_CLASSIFICATION
|
|
721
|
+
]
|
|
722
|
+
bands_classif = None
|
|
723
|
+
if input_classif is not None:
|
|
724
|
+
bands_classif = input_classif["values"]
|
|
725
|
+
|
|
726
|
+
classif_file_name = (
|
|
727
|
+
self.monoband_to_multiband(
|
|
728
|
+
self.dsm_to_fill["classification"],
|
|
729
|
+
os.path.join(dsm_filled_dir, "classification.tif"),
|
|
730
|
+
bands_classif,
|
|
731
|
+
)
|
|
732
|
+
if sens_cst.INPUT_CLASSIFICATION
|
|
733
|
+
in self.used_conf[INPUT]["dsm_to_fill"]
|
|
734
|
+
else None
|
|
735
|
+
)
|
|
736
|
+
|
|
737
|
+
filling_file_name = (
|
|
738
|
+
self.dsm_to_fill["filling"]
|
|
739
|
+
if "filling" in self.used_conf[INPUT]["dsm_to_fill"]
|
|
740
|
+
else None
|
|
741
|
+
)
|
|
742
|
+
|
|
743
|
+
if inputs_conf[sens_cst.INITIAL_ELEVATION][sens_cst.DEM_PATH] is None:
|
|
744
|
+
dems = {}
|
|
745
|
+
|
|
746
|
+
dem_generation_output_dir = os.path.join(
|
|
747
|
+
self.dump_dir, "dem_generation"
|
|
748
|
+
)
|
|
749
|
+
safe_makedirs(dem_generation_output_dir)
|
|
750
|
+
|
|
751
|
+
# Use initial elevation if provided, and generate dems
|
|
752
|
+
_, paths, _ = self.dem_generation_application.run(
|
|
753
|
+
dsm_file_name,
|
|
754
|
+
dem_generation_output_dir,
|
|
755
|
+
input_geoid=self.used_conf[INPUT][sens_cst.INITIAL_ELEVATION][
|
|
756
|
+
sens_cst.GEOID
|
|
757
|
+
],
|
|
758
|
+
output_geoid=self.used_conf[OUTPUT][out_cst.OUT_GEOID],
|
|
759
|
+
initial_elevation=(
|
|
760
|
+
self.used_conf[INPUT][sens_cst.INITIAL_ELEVATION][
|
|
761
|
+
sens_cst.DEM_PATH
|
|
762
|
+
]
|
|
763
|
+
),
|
|
764
|
+
default_alt=self.geom_plugin_with_dem_and_geoid.default_alt,
|
|
765
|
+
cars_orchestrator=self.cars_orchestrator,
|
|
766
|
+
)
|
|
767
|
+
|
|
768
|
+
dem_median = paths["dem_median"]
|
|
769
|
+
dem_min = paths["dem_min"]
|
|
770
|
+
dem_max = paths["dem_max"]
|
|
771
|
+
|
|
772
|
+
dems["dem_median"] = dem_median
|
|
773
|
+
dems["dem_min"] = dem_min
|
|
774
|
+
dems["dem_max"] = dem_max
|
|
775
|
+
|
|
776
|
+
# Override initial elevation
|
|
777
|
+
inputs_conf[sens_cst.INITIAL_ELEVATION][
|
|
778
|
+
sens_cst.DEM_PATH
|
|
779
|
+
] = dem_median
|
|
780
|
+
|
|
781
|
+
# Check advanced parameters with new initial elevation
|
|
782
|
+
output_dem_dir = os.path.join(
|
|
783
|
+
self.used_conf[OUTPUT][out_cst.OUT_DIRECTORY],
|
|
784
|
+
"dump_dir",
|
|
785
|
+
"initial_elevation",
|
|
786
|
+
)
|
|
787
|
+
safe_makedirs(output_dem_dir)
|
|
788
|
+
(
|
|
789
|
+
inputs_conf,
|
|
790
|
+
_,
|
|
791
|
+
self.geometry_plugin,
|
|
792
|
+
self.geom_plugin_without_dem_and_geoid,
|
|
793
|
+
self.geom_plugin_with_dem_and_geoid,
|
|
794
|
+
_,
|
|
795
|
+
_,
|
|
796
|
+
_,
|
|
797
|
+
) = advanced_parameters.check_advanced_parameters(
|
|
798
|
+
inputs_conf,
|
|
799
|
+
self.used_conf.get(ADVANCED, {}),
|
|
800
|
+
output_dem_dir=output_dem_dir,
|
|
801
|
+
)
|
|
802
|
+
|
|
803
|
+
if not hasattr(self, "list_intersection_poly"):
|
|
804
|
+
if (
|
|
805
|
+
self.used_conf[INPUT][sens_cst.INITIAL_ELEVATION][
|
|
806
|
+
sens_cst.DEM_PATH
|
|
807
|
+
]
|
|
808
|
+
is not None
|
|
809
|
+
):
|
|
810
|
+
sensor_inputs.load_geomodels(
|
|
811
|
+
inputs_conf, self.geom_plugin_without_dem_and_geoid
|
|
812
|
+
)
|
|
813
|
+
self.list_sensor_pairs = sensor_inputs.generate_pairs(
|
|
814
|
+
self.used_conf[INPUT]
|
|
815
|
+
)
|
|
816
|
+
|
|
817
|
+
self.list_intersection_poly = []
|
|
818
|
+
for _, (
|
|
819
|
+
pair_key,
|
|
820
|
+
sensor_image_left,
|
|
821
|
+
sensor_image_right,
|
|
822
|
+
) in enumerate(self.list_sensor_pairs):
|
|
823
|
+
pair_folder = os.path.join(
|
|
824
|
+
self.dump_dir, "terrain_bbox", pair_key
|
|
825
|
+
)
|
|
826
|
+
safe_makedirs(pair_folder)
|
|
827
|
+
geojson1 = os.path.join(
|
|
828
|
+
pair_folder, "left_envelope.geojson"
|
|
829
|
+
)
|
|
830
|
+
geojson2 = os.path.join(
|
|
831
|
+
pair_folder, "right_envelope.geojson"
|
|
832
|
+
)
|
|
833
|
+
out_envelopes_intersection = os.path.join(
|
|
834
|
+
pair_folder, "envelopes_intersection.geojson"
|
|
835
|
+
)
|
|
836
|
+
|
|
837
|
+
inter_poly, _ = projection.ground_intersection_envelopes(
|
|
838
|
+
sensor_image_left[sens_cst.INPUT_IMG]["bands"]["b0"][
|
|
839
|
+
"path"
|
|
840
|
+
],
|
|
841
|
+
sensor_image_right[sens_cst.INPUT_IMG]["bands"]["b0"][
|
|
842
|
+
"path"
|
|
843
|
+
],
|
|
844
|
+
sensor_image_left[sens_cst.INPUT_GEO_MODEL],
|
|
845
|
+
sensor_image_right[sens_cst.INPUT_GEO_MODEL],
|
|
846
|
+
self.geom_plugin_with_dem_and_geoid,
|
|
847
|
+
geojson1,
|
|
848
|
+
geojson2,
|
|
849
|
+
out_envelopes_intersection,
|
|
850
|
+
envelope_file_driver="GeoJSON",
|
|
851
|
+
intersect_file_driver="GeoJSON",
|
|
852
|
+
)
|
|
853
|
+
|
|
854
|
+
# Retrieve bounding box of the grd inters of the envelopes
|
|
855
|
+
inter_poly, inter_epsg = read_vector(
|
|
856
|
+
out_envelopes_intersection
|
|
857
|
+
)
|
|
858
|
+
|
|
859
|
+
# Project polygon if epsg is different
|
|
860
|
+
if self.vertical_crs != CRS(inter_epsg):
|
|
861
|
+
inter_poly = projection.polygon_projection_crs(
|
|
862
|
+
inter_poly, CRS(inter_epsg), self.vertical_crs
|
|
863
|
+
)
|
|
864
|
+
|
|
865
|
+
self.list_intersection_poly.append(inter_poly)
|
|
866
|
+
else:
|
|
867
|
+
self.list_intersection_poly = None
|
|
868
|
+
|
|
869
|
+
dtm_file_name = None
|
|
870
|
+
for app_key, app in self.dsm_filling_apps.items():
|
|
871
|
+
|
|
872
|
+
app_dump_dir = os.path.join(
|
|
873
|
+
self.dump_dir, app_key.replace(".", "_")
|
|
874
|
+
)
|
|
875
|
+
|
|
876
|
+
if app.get_conf()["method"] == "exogenous_filling":
|
|
877
|
+
_ = app.run(
|
|
878
|
+
dsm_file=dsm_file_name,
|
|
879
|
+
classif_file=classif_file_name,
|
|
880
|
+
filling_file=filling_file_name,
|
|
881
|
+
dump_dir=app_dump_dir,
|
|
882
|
+
roi_polys=self.list_intersection_poly,
|
|
883
|
+
roi_epsg=self.epsg,
|
|
884
|
+
output_geoid=self.used_conf[OUTPUT][sens_cst.GEOID],
|
|
885
|
+
geom_plugin=self.geom_plugin_with_dem_and_geoid,
|
|
886
|
+
dsm_dir=dsm_filled_dir,
|
|
887
|
+
)
|
|
888
|
+
elif app.get_conf()["method"] == "bulldozer":
|
|
889
|
+
dtm_file_name = app.run(
|
|
890
|
+
dsm_file=dsm_file_name,
|
|
891
|
+
classif_file=classif_file_name,
|
|
892
|
+
filling_file=filling_file_name,
|
|
893
|
+
dump_dir=app_dump_dir,
|
|
894
|
+
roi_polys=self.list_intersection_poly,
|
|
895
|
+
roi_epsg=self.epsg,
|
|
896
|
+
orchestrator=self.cars_orchestrator,
|
|
897
|
+
dsm_dir=dsm_filled_dir,
|
|
898
|
+
)
|
|
899
|
+
elif app.get_conf()["method"] == "border_interpolation":
|
|
900
|
+
_ = app.run(
|
|
901
|
+
dsm_file=dsm_file_name,
|
|
902
|
+
classif_file=classif_file_name,
|
|
903
|
+
filling_file=filling_file_name,
|
|
904
|
+
dtm_file=dtm_file_name,
|
|
905
|
+
dump_dir=app_dump_dir,
|
|
906
|
+
roi_polys=self.list_intersection_poly,
|
|
907
|
+
roi_epsg=self.epsg,
|
|
908
|
+
dsm_dir=dsm_filled_dir,
|
|
909
|
+
)
|
|
910
|
+
|
|
911
|
+
if not app.save_intermediate_data:
|
|
912
|
+
self.cars_orchestrator.add_to_clean(app_dump_dir)
|
|
913
|
+
|
|
914
|
+
if dsm_file_name == self.dsm_to_fill["dsm"]:
|
|
915
|
+
dsm_file_name = os.path.join(dsm_filled_dir, "dsm.tif")
|
|
916
|
+
|
|
917
|
+
if "filling" in self.used_conf[INPUT]["dsm_to_fill"]:
|
|
918
|
+
if filling_file_name == self.dsm_to_fill["filling"]:
|
|
919
|
+
filling_file_name = os.path.join(
|
|
920
|
+
dsm_filled_dir, "filling.tif"
|
|
921
|
+
)
|
|
922
|
+
|
|
923
|
+
_ = self.auxiliary_filling_application.run(
|
|
924
|
+
dsm_file=os.path.join(dsm_filled_dir, "dsm.tif"),
|
|
925
|
+
color_file=color_file_name,
|
|
926
|
+
classif_file=classif_file_name,
|
|
927
|
+
dump_dir=self.dump_dir,
|
|
928
|
+
sensor_inputs=self.used_conf[INPUT].get("sensors"),
|
|
929
|
+
pairing=self.used_conf[INPUT].get("pairing"),
|
|
930
|
+
geom_plugin=self.geom_plugin_with_dem_and_geoid,
|
|
931
|
+
texture_bands=self.texture_bands,
|
|
932
|
+
output_geoid=self.used_conf[OUTPUT][sens_cst.GEOID],
|
|
933
|
+
orchestrator=self.cars_orchestrator,
|
|
934
|
+
dsm_dir=dsm_filled_dir,
|
|
935
|
+
)
|
|
936
|
+
self.cars_orchestrator.breakpoint()
|
|
937
|
+
|
|
938
|
+
if (
|
|
939
|
+
os.path.join(dsm_filled_dir, "classification.tif") is not None
|
|
940
|
+
and self.used_conf[OUTPUT][out_cst.AUXILIARY][
|
|
941
|
+
out_cst.AUX_CLASSIFICATION
|
|
942
|
+
]
|
|
943
|
+
):
|
|
944
|
+
self.merge_classif_bands(
|
|
945
|
+
classif_file_name,
|
|
946
|
+
self.used_conf[OUTPUT][out_cst.AUXILIARY][
|
|
947
|
+
out_cst.AUX_CLASSIFICATION
|
|
948
|
+
],
|
|
949
|
+
dsm_file_name,
|
|
950
|
+
)
|
|
951
|
+
if (
|
|
952
|
+
filling_file_name is not None
|
|
953
|
+
and self.used_conf[OUTPUT][out_cst.AUXILIARY][out_cst.AUX_FILLING]
|
|
954
|
+
):
|
|
955
|
+
self.merge_filling_bands(
|
|
956
|
+
os.path.join(dsm_filled_dir, "filling.tif"),
|
|
957
|
+
self.used_conf[OUTPUT][out_cst.AUXILIARY][out_cst.AUX_FILLING],
|
|
958
|
+
dsm_file_name,
|
|
959
|
+
)
|
|
960
|
+
|
|
961
|
+
return True
|
|
962
|
+
|
|
963
|
+
@cars_profile(name="Run_subsampling_pipeline", interval=0.5)
|
|
964
|
+
def run(self): # noqa C901
|
|
965
|
+
"""
|
|
966
|
+
Run pipeline
|
|
967
|
+
"""
|
|
968
|
+
cars_logging.add_progress_message("Starting filling pipeline")
|
|
969
|
+
|
|
970
|
+
self.log_dir = os.path.join(self.filling_dir, "logs")
|
|
971
|
+
|
|
972
|
+
with orchestrator.Orchestrator(
|
|
973
|
+
orchestrator_conf=self.used_conf[ORCHESTRATOR],
|
|
974
|
+
out_dir=self.filling_dir,
|
|
975
|
+
log_dir=self.log_dir,
|
|
976
|
+
out_yaml_path=os.path.join(
|
|
977
|
+
self.filling_dir,
|
|
978
|
+
out_cst.INFO_FILENAME,
|
|
979
|
+
),
|
|
980
|
+
) as self.cars_orchestrator:
|
|
981
|
+
self.filling()
|