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,598 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
# coding: utf8
|
|
3
|
+
#
|
|
4
|
+
# Copyright (c) 2023 Centre National d'Etudes Spatiales (CNES).
|
|
5
|
+
#
|
|
6
|
+
# This file is part of CARS
|
|
7
|
+
# (see https://github.com/CNES/cars).
|
|
8
|
+
#
|
|
9
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
10
|
+
# you may not use this file except in compliance with the License.
|
|
11
|
+
# You may obtain a copy of the License at
|
|
12
|
+
#
|
|
13
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
14
|
+
#
|
|
15
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
16
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
17
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
18
|
+
# See the License for the specific language governing permissions and
|
|
19
|
+
# limitations under the License.
|
|
20
|
+
#
|
|
21
|
+
"""
|
|
22
|
+
Shareloc geometry sub class : CARS geometry wrappers functions to shareloc ones
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
import logging
|
|
26
|
+
from typing import List, Tuple, Union
|
|
27
|
+
|
|
28
|
+
import bindings_cpp # pylint: disable=E0401
|
|
29
|
+
import numpy as np
|
|
30
|
+
import rasterio as rio
|
|
31
|
+
import shareloc.geofunctions.rectification as rectif
|
|
32
|
+
import xarray as xr
|
|
33
|
+
from json_checker import And, Checker
|
|
34
|
+
from shareloc.dtm_reader import dtm_reader
|
|
35
|
+
from shareloc.geofunctions import localization
|
|
36
|
+
from shareloc.geofunctions.rectification_grid import RectificationGrid
|
|
37
|
+
from shareloc.geofunctions.triangulation import epipolar_triangulation
|
|
38
|
+
from shareloc.geomodels.geomodel import GeoModel
|
|
39
|
+
from shareloc.geomodels.grid import Grid
|
|
40
|
+
from shareloc.geomodels.rpc import RPC
|
|
41
|
+
from shareloc.image import Image
|
|
42
|
+
|
|
43
|
+
from cars.core import constants as cst
|
|
44
|
+
from cars.core import projection
|
|
45
|
+
from cars.core.geometry.abstract_geometry import AbstractGeometry
|
|
46
|
+
|
|
47
|
+
GRID_TYPE = "GRID"
|
|
48
|
+
RPC_TYPE = "RPC"
|
|
49
|
+
GEO_MODEL_PATH_TAG = "path"
|
|
50
|
+
GEO_MODEL_TYPE_TAG = "model_type"
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
@AbstractGeometry.register_subclass("SharelocGeometry")
|
|
54
|
+
class SharelocGeometry(AbstractGeometry):
|
|
55
|
+
"""
|
|
56
|
+
Shareloc geometry class
|
|
57
|
+
"""
|
|
58
|
+
|
|
59
|
+
def __init__( # pylint: disable=too-many-positional-arguments
|
|
60
|
+
self,
|
|
61
|
+
geometry_plugin_conf,
|
|
62
|
+
dem=None,
|
|
63
|
+
geoid=None,
|
|
64
|
+
default_alt=None,
|
|
65
|
+
pairs_for_roi=None,
|
|
66
|
+
scaling_coeff=1,
|
|
67
|
+
output_dem_dir=None,
|
|
68
|
+
):
|
|
69
|
+
|
|
70
|
+
super().__init__(
|
|
71
|
+
geometry_plugin_conf,
|
|
72
|
+
dem=dem,
|
|
73
|
+
geoid=geoid,
|
|
74
|
+
default_alt=default_alt,
|
|
75
|
+
pairs_for_roi=pairs_for_roi,
|
|
76
|
+
scaling_coeff=scaling_coeff,
|
|
77
|
+
output_dem_dir=output_dem_dir,
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
if dem is not None:
|
|
81
|
+
# fill_nodata option should be set when dealing with void in DTM
|
|
82
|
+
# see shareloc DTM limitations in sphinx doc for further detailsd
|
|
83
|
+
try:
|
|
84
|
+
if self.dem_roi is None:
|
|
85
|
+
roi_shareloc = None
|
|
86
|
+
else:
|
|
87
|
+
# From (x, y) to (y, x)
|
|
88
|
+
roi_shareloc = [
|
|
89
|
+
self.dem_roi[1],
|
|
90
|
+
self.dem_roi[0],
|
|
91
|
+
self.dem_roi[3],
|
|
92
|
+
self.dem_roi[2],
|
|
93
|
+
]
|
|
94
|
+
dtm_image = dtm_reader(
|
|
95
|
+
self.dem,
|
|
96
|
+
self.geoid,
|
|
97
|
+
roi=roi_shareloc,
|
|
98
|
+
roi_is_in_physical_space=True,
|
|
99
|
+
fill_nodata="mean",
|
|
100
|
+
fill_value=0.0,
|
|
101
|
+
)
|
|
102
|
+
except RuntimeError as err:
|
|
103
|
+
mss = "the roi bounds are"
|
|
104
|
+
if mss in str(err):
|
|
105
|
+
new_except_mss = (
|
|
106
|
+
f"The extent of the roi lies outside "
|
|
107
|
+
f"the extent of the initial elevation : {err}"
|
|
108
|
+
)
|
|
109
|
+
raise RuntimeError(new_except_mss) from err
|
|
110
|
+
raise
|
|
111
|
+
|
|
112
|
+
self.elevation = (
|
|
113
|
+
bindings_cpp.DTMIntersection( # pylint: disable=I1101
|
|
114
|
+
dtm_image.epsg,
|
|
115
|
+
dtm_image.alt_data,
|
|
116
|
+
dtm_image.nb_rows,
|
|
117
|
+
dtm_image.nb_columns,
|
|
118
|
+
dtm_image.transform,
|
|
119
|
+
)
|
|
120
|
+
)
|
|
121
|
+
else:
|
|
122
|
+
self.elevation = default_alt
|
|
123
|
+
|
|
124
|
+
def check_conf(self, conf):
|
|
125
|
+
"""
|
|
126
|
+
Check configuration
|
|
127
|
+
|
|
128
|
+
:param conf: configuration to check
|
|
129
|
+
:type conf: str or dict
|
|
130
|
+
|
|
131
|
+
:return: full dict
|
|
132
|
+
:rtype: dict
|
|
133
|
+
|
|
134
|
+
"""
|
|
135
|
+
|
|
136
|
+
if conf is None:
|
|
137
|
+
raise RuntimeError("Geometry plugin configuration is None")
|
|
138
|
+
|
|
139
|
+
overloaded_conf = {}
|
|
140
|
+
|
|
141
|
+
if isinstance(conf, str):
|
|
142
|
+
conf = {"plugin_name": conf}
|
|
143
|
+
|
|
144
|
+
# overload conf
|
|
145
|
+
overloaded_conf["plugin_name"] = conf.get(
|
|
146
|
+
"plugin_name", "SharelocGeometry"
|
|
147
|
+
)
|
|
148
|
+
overloaded_conf["interpolator"] = conf.get("interpolator", "cubic")
|
|
149
|
+
overloaded_conf["dem_roi_margin"] = conf.get(
|
|
150
|
+
"dem_roi_margin", [0.25, 0.02]
|
|
151
|
+
)
|
|
152
|
+
|
|
153
|
+
geometry_schema = {
|
|
154
|
+
"plugin_name": str,
|
|
155
|
+
"interpolator": And(str, lambda x: x in ["cubic", "linear"]),
|
|
156
|
+
"dem_roi_margin": [float],
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
# Check conf
|
|
160
|
+
checker = Checker(geometry_schema)
|
|
161
|
+
checker.validate(overloaded_conf)
|
|
162
|
+
|
|
163
|
+
return overloaded_conf
|
|
164
|
+
|
|
165
|
+
def get_roi( # pylint: disable=too-many-positional-arguments
|
|
166
|
+
self,
|
|
167
|
+
pairs_for_roi,
|
|
168
|
+
epsg,
|
|
169
|
+
z_min=0,
|
|
170
|
+
z_max=0,
|
|
171
|
+
linear_margin=0,
|
|
172
|
+
constant_margin=0.012,
|
|
173
|
+
):
|
|
174
|
+
"""
|
|
175
|
+
Compute region of interest for intersection of DEM
|
|
176
|
+
|
|
177
|
+
:param pairs_for_roi: list of pairs of images and geomodels
|
|
178
|
+
:type pairs_for_roi: List[(str, dict, str, dict)]
|
|
179
|
+
:param dem_epsg: output EPSG code for ROI
|
|
180
|
+
:type dem_epsg: int
|
|
181
|
+
:param linear_margin: margin for ROI (factor of initial ROI size)
|
|
182
|
+
:type linear_margin: float
|
|
183
|
+
:param constant_margin: margin for ROI in degrees
|
|
184
|
+
:type constant_margin: float
|
|
185
|
+
"""
|
|
186
|
+
base_roi = super().get_roi(
|
|
187
|
+
pairs_for_roi, epsg, z_min, z_max, linear_margin, constant_margin
|
|
188
|
+
)
|
|
189
|
+
|
|
190
|
+
coords_list = []
|
|
191
|
+
for image1, geomodel1, _, geomodel2 in pairs_for_roi:
|
|
192
|
+
# Footprint of rectification grid (with margins)
|
|
193
|
+
image1 = SharelocGeometry.load_image(image1["bands"]["b0"]["path"])
|
|
194
|
+
geomodel1 = self.load_geom_model(geomodel1)
|
|
195
|
+
geomodel2 = self.load_geom_model(geomodel2)
|
|
196
|
+
|
|
197
|
+
# With altitude z_min
|
|
198
|
+
epipolar_extent = rectif.get_epipolar_extent(
|
|
199
|
+
image1,
|
|
200
|
+
geomodel1,
|
|
201
|
+
geomodel2,
|
|
202
|
+
elevation=z_min,
|
|
203
|
+
grid_margin=self.rectification_grid_margin,
|
|
204
|
+
)
|
|
205
|
+
lat_min, lon_min, lat_max, lon_max = list(epipolar_extent)
|
|
206
|
+
coords_list.extend([(lon_min, lat_min), (lon_max, lat_max)])
|
|
207
|
+
|
|
208
|
+
# With altitude z_max
|
|
209
|
+
epipolar_extent = rectif.get_epipolar_extent(
|
|
210
|
+
image1,
|
|
211
|
+
geomodel1,
|
|
212
|
+
geomodel2,
|
|
213
|
+
elevation=z_max,
|
|
214
|
+
grid_margin=self.rectification_grid_margin,
|
|
215
|
+
)
|
|
216
|
+
lat_min, lon_min, lat_max, lon_max = list(epipolar_extent)
|
|
217
|
+
coords_list.extend([(lon_min, lat_min), (lon_max, lat_max)])
|
|
218
|
+
|
|
219
|
+
lon_list, lat_list = list(zip(*coords_list)) # noqa: B905
|
|
220
|
+
roi = [
|
|
221
|
+
min(lon_list) - constant_margin,
|
|
222
|
+
min(lat_list) - constant_margin,
|
|
223
|
+
max(lon_list) + constant_margin,
|
|
224
|
+
max(lat_list) + constant_margin,
|
|
225
|
+
]
|
|
226
|
+
points = np.array(
|
|
227
|
+
[
|
|
228
|
+
(roi[0], roi[1], 0),
|
|
229
|
+
(roi[2], roi[3], 0),
|
|
230
|
+
(roi[0], roi[1], 0),
|
|
231
|
+
(roi[2], roi[3], 0),
|
|
232
|
+
]
|
|
233
|
+
)
|
|
234
|
+
new_points = projection.point_cloud_conversion(points, 4326, epsg)
|
|
235
|
+
roi = [
|
|
236
|
+
min(new_points[:, 0]),
|
|
237
|
+
min(new_points[:, 1]),
|
|
238
|
+
max(new_points[:, 0]),
|
|
239
|
+
max(new_points[:, 1]),
|
|
240
|
+
]
|
|
241
|
+
|
|
242
|
+
lon_size = roi[2] - roi[0]
|
|
243
|
+
lat_size = roi[3] - roi[1]
|
|
244
|
+
|
|
245
|
+
roi[0] -= linear_margin * lon_size
|
|
246
|
+
roi[1] -= linear_margin * lat_size
|
|
247
|
+
roi[2] += linear_margin * lon_size
|
|
248
|
+
roi[3] += linear_margin * lat_size
|
|
249
|
+
|
|
250
|
+
roi = [
|
|
251
|
+
min(roi[0], base_roi[0]),
|
|
252
|
+
min(roi[1], base_roi[1]),
|
|
253
|
+
max(roi[2], base_roi[2]),
|
|
254
|
+
max(roi[3], base_roi[3]),
|
|
255
|
+
]
|
|
256
|
+
|
|
257
|
+
return roi
|
|
258
|
+
|
|
259
|
+
@staticmethod
|
|
260
|
+
def load_geom_model(model: dict) -> Union[Grid, RPC]:
|
|
261
|
+
"""
|
|
262
|
+
Load geometric model and returns it as a shareloc object
|
|
263
|
+
|
|
264
|
+
:param model: Path and attributes for geometrical model
|
|
265
|
+
:type model: dict with keys "path" and "model_type"
|
|
266
|
+
:return: geometric model as a shareloc object (Grid or RPC)
|
|
267
|
+
"""
|
|
268
|
+
geomodel = model[GEO_MODEL_PATH_TAG]
|
|
269
|
+
# Use RPC Type if none are used
|
|
270
|
+
if model.get(GEO_MODEL_TYPE_TAG):
|
|
271
|
+
geomodel_type = model[GEO_MODEL_TYPE_TAG]
|
|
272
|
+
else:
|
|
273
|
+
geomodel_type = RPC_TYPE
|
|
274
|
+
|
|
275
|
+
# Use RPCoptim class to use optimized C++ direct localizations
|
|
276
|
+
if geomodel_type == "RPC":
|
|
277
|
+
geomodel_type = "RPCoptim"
|
|
278
|
+
|
|
279
|
+
shareloc_model = GeoModel(geomodel, geomodel_type)
|
|
280
|
+
|
|
281
|
+
if shareloc_model is None:
|
|
282
|
+
raise ValueError(f"Model {geomodel} could not be read by shareloc")
|
|
283
|
+
|
|
284
|
+
return shareloc_model
|
|
285
|
+
|
|
286
|
+
@staticmethod
|
|
287
|
+
def load_image(img: str) -> Image:
|
|
288
|
+
"""
|
|
289
|
+
Load the image using the Image class of Shareloc
|
|
290
|
+
|
|
291
|
+
:param img: path to the image
|
|
292
|
+
:return: The Image object
|
|
293
|
+
"""
|
|
294
|
+
try:
|
|
295
|
+
shareloc_img = Image(img, vertical_direction="north")
|
|
296
|
+
except Exception as error:
|
|
297
|
+
raise ValueError(f"Image type {img} is not supported") from error
|
|
298
|
+
|
|
299
|
+
return shareloc_img
|
|
300
|
+
|
|
301
|
+
@staticmethod
|
|
302
|
+
def check_product_consistency(sensor: str, geomodel: dict) -> bool:
|
|
303
|
+
"""
|
|
304
|
+
Test if the product is readable by the shareloc plugin
|
|
305
|
+
|
|
306
|
+
TODO: not used
|
|
307
|
+
- to evolve and use in CARS configuration early in pipeline process
|
|
308
|
+
(new early check input common application ?)
|
|
309
|
+
|
|
310
|
+
:param sensor: path to sensor image
|
|
311
|
+
:param geomodel: path and attributes for geometrical model
|
|
312
|
+
:return: sensor path and overloaded geomodel dict
|
|
313
|
+
"""
|
|
314
|
+
# Check geomodel schema consistency
|
|
315
|
+
if isinstance(geomodel, str):
|
|
316
|
+
geomodel = {
|
|
317
|
+
"path": geomodel,
|
|
318
|
+
}
|
|
319
|
+
overloaded_geomodel = geomodel.copy()
|
|
320
|
+
|
|
321
|
+
# If model_type is not defined, default is "RPC"
|
|
322
|
+
overloaded_geomodel["model_type"] = geomodel.get("model_type", "RPC")
|
|
323
|
+
|
|
324
|
+
geomodel_schema = {"path": str, "model_type": str}
|
|
325
|
+
checker_geomodel = Checker(geomodel_schema)
|
|
326
|
+
checker_geomodel.validate(overloaded_geomodel)
|
|
327
|
+
|
|
328
|
+
# Try to read them using shareloc
|
|
329
|
+
for band in sensor["bands"]:
|
|
330
|
+
SharelocGeometry.load_image(sensor["bands"][band]["path"])
|
|
331
|
+
SharelocGeometry.load_geom_model(overloaded_geomodel)
|
|
332
|
+
|
|
333
|
+
return sensor, overloaded_geomodel
|
|
334
|
+
|
|
335
|
+
def triangulate( # pylint: disable=too-many-positional-arguments
|
|
336
|
+
self,
|
|
337
|
+
sensor1,
|
|
338
|
+
sensor2,
|
|
339
|
+
geomodel1,
|
|
340
|
+
geomodel2,
|
|
341
|
+
mode: str,
|
|
342
|
+
matches: Union[xr.Dataset, np.ndarray],
|
|
343
|
+
grid1: Union[dict, RectificationGrid],
|
|
344
|
+
grid2: Union[dict, RectificationGrid],
|
|
345
|
+
roi_key: Union[None, str] = None,
|
|
346
|
+
interpolation_method=None,
|
|
347
|
+
) -> np.ndarray:
|
|
348
|
+
"""
|
|
349
|
+
Performs triangulation from cars disparity or matches dataset
|
|
350
|
+
:param sensor1: path to left sensor image
|
|
351
|
+
:param sensor2: path to right sensor image
|
|
352
|
+
:param geomodel1: path and attributes for left geomodel
|
|
353
|
+
:param geomodel2: path and attributes for right geomodel
|
|
354
|
+
:param mode: triangulation mode
|
|
355
|
+
(constants.DISP_MODE or constants.MATCHES)
|
|
356
|
+
:param matches: cars disparity dataset or matches as numpy array
|
|
357
|
+
:param grid1: dict or RectificationGrid for epipolar grid of sensor1
|
|
358
|
+
:param grid2: dict or RectificationGrid for epipolar grid of sensor2
|
|
359
|
+
:param roi_key: dataset roi to use
|
|
360
|
+
(can be cst.ROI or cst.ROI_WITH_MARGINS)
|
|
361
|
+
|
|
362
|
+
:return: the long/lat/height numpy array in output of the triangulation
|
|
363
|
+
"""
|
|
364
|
+
# read geomodels using shareloc
|
|
365
|
+
shareloc_model1 = SharelocGeometry.load_geom_model(geomodel1)
|
|
366
|
+
shareloc_model2 = SharelocGeometry.load_geom_model(geomodel2)
|
|
367
|
+
|
|
368
|
+
if isinstance(grid1, dict):
|
|
369
|
+
# create rectificationgrid
|
|
370
|
+
grid1 = RectificationGrid(
|
|
371
|
+
grid1["path"],
|
|
372
|
+
interpolator=self.interpolator,
|
|
373
|
+
)
|
|
374
|
+
grid2 = RectificationGrid(
|
|
375
|
+
grid2["path"],
|
|
376
|
+
interpolator=self.interpolator,
|
|
377
|
+
)
|
|
378
|
+
|
|
379
|
+
# perform matches triangulation
|
|
380
|
+
if mode is cst.MATCHES_MODE:
|
|
381
|
+
__, point_wgs84, __ = epipolar_triangulation(
|
|
382
|
+
matches=matches,
|
|
383
|
+
mask=None,
|
|
384
|
+
matches_type="sift",
|
|
385
|
+
geometrical_model_left=shareloc_model1,
|
|
386
|
+
geometrical_model_right=shareloc_model2,
|
|
387
|
+
grid_left=grid1,
|
|
388
|
+
grid_right=grid2,
|
|
389
|
+
residues=True,
|
|
390
|
+
fill_nan=True,
|
|
391
|
+
)
|
|
392
|
+
|
|
393
|
+
llh = point_wgs84.reshape((point_wgs84.shape[0], 1, 3))
|
|
394
|
+
|
|
395
|
+
elif mode is cst.DISP_MODE:
|
|
396
|
+
__, point_wgs84, __ = epipolar_triangulation(
|
|
397
|
+
matches=matches,
|
|
398
|
+
mask=None,
|
|
399
|
+
matches_type="disp",
|
|
400
|
+
geometrical_model_left=shareloc_model1,
|
|
401
|
+
geometrical_model_right=shareloc_model2,
|
|
402
|
+
grid_left=grid1,
|
|
403
|
+
grid_right=grid2,
|
|
404
|
+
residues=True,
|
|
405
|
+
fill_nan=True,
|
|
406
|
+
)
|
|
407
|
+
|
|
408
|
+
row = np.array(
|
|
409
|
+
range(matches.attrs[roi_key][1], matches.attrs[roi_key][3])
|
|
410
|
+
)
|
|
411
|
+
col = np.array(
|
|
412
|
+
range(matches.attrs[roi_key][0], matches.attrs[roi_key][2])
|
|
413
|
+
)
|
|
414
|
+
|
|
415
|
+
llh = point_wgs84.reshape((row.size, col.size, 3))
|
|
416
|
+
else:
|
|
417
|
+
logging.error(
|
|
418
|
+
"{} mode is not available in the "
|
|
419
|
+
"shareloc plugin triangulation".format(mode)
|
|
420
|
+
)
|
|
421
|
+
raise ValueError(
|
|
422
|
+
f"{mode} mode is not available"
|
|
423
|
+
" in the shareloc plugin triangulation"
|
|
424
|
+
)
|
|
425
|
+
|
|
426
|
+
return llh
|
|
427
|
+
|
|
428
|
+
# pylint: disable=too-many-positional-arguments
|
|
429
|
+
def generate_epipolar_grids(
|
|
430
|
+
self,
|
|
431
|
+
sensor1,
|
|
432
|
+
sensor2,
|
|
433
|
+
geomodel1,
|
|
434
|
+
geomodel2,
|
|
435
|
+
epipolar_step: int = 30,
|
|
436
|
+
) -> Tuple[
|
|
437
|
+
np.ndarray, np.ndarray, List[float], List[float], List[int], float
|
|
438
|
+
]:
|
|
439
|
+
"""
|
|
440
|
+
Computes the left and right epipolar grids
|
|
441
|
+
|
|
442
|
+
:param sensor1: path to left sensor image
|
|
443
|
+
:param sensor2: path to right sensor image
|
|
444
|
+
:param geomodel1: path and attributes for left geomodel
|
|
445
|
+
:param geomodel2: path and attributes for right geomodel
|
|
446
|
+
:param epipolar_step: step to use to construct the epipolar grids
|
|
447
|
+
:return: Tuple composed of :
|
|
448
|
+
- the left epipolar grid as a numpy array
|
|
449
|
+
- the right epipolar grid as a numpy array
|
|
450
|
+
- the left grid origin as a list of float
|
|
451
|
+
- the left grid spacing as a list of float
|
|
452
|
+
- the epipolar image size as a list of int
|
|
453
|
+
(x-axis size is given with the index 0, y-axis size with index 1)
|
|
454
|
+
- the disparity to altitude ratio as a float
|
|
455
|
+
|
|
456
|
+
"""
|
|
457
|
+
# read inputs using shareloc
|
|
458
|
+
shareloc_model1 = SharelocGeometry.load_geom_model(geomodel1)
|
|
459
|
+
shareloc_model2 = SharelocGeometry.load_geom_model(geomodel2)
|
|
460
|
+
|
|
461
|
+
image1 = SharelocGeometry.load_image(sensor1)
|
|
462
|
+
image2 = SharelocGeometry.load_image(sensor2)
|
|
463
|
+
|
|
464
|
+
# compute epipolar grids
|
|
465
|
+
(
|
|
466
|
+
grid1,
|
|
467
|
+
grid2,
|
|
468
|
+
[epipolar_size_y, epipolar_size_x],
|
|
469
|
+
alt_to_disp_ratio,
|
|
470
|
+
) = rectif.compute_stereorectification_epipolar_grids(
|
|
471
|
+
image1,
|
|
472
|
+
shareloc_model1,
|
|
473
|
+
image2,
|
|
474
|
+
shareloc_model2,
|
|
475
|
+
self.elevation,
|
|
476
|
+
epi_step=epipolar_step,
|
|
477
|
+
margin=self.rectification_grid_margin,
|
|
478
|
+
)
|
|
479
|
+
|
|
480
|
+
# rearrange output to match the expected structure of CARS
|
|
481
|
+
# grid[:, :, 2] with altitudes is not used
|
|
482
|
+
grid1 = grid1[:, :, 0:2][:, :, ::-1]
|
|
483
|
+
grid2 = grid2[:, :, 0:2][:, :, ::-1]
|
|
484
|
+
|
|
485
|
+
epipolar_size_x = int(np.floor(epipolar_size_x))
|
|
486
|
+
epipolar_size_y = int(np.floor(epipolar_size_y))
|
|
487
|
+
|
|
488
|
+
origin = [
|
|
489
|
+
float(-self.rectification_grid_margin * epipolar_step),
|
|
490
|
+
float(-self.rectification_grid_margin * epipolar_step),
|
|
491
|
+
]
|
|
492
|
+
spacing = [float(epipolar_step), float(epipolar_step)]
|
|
493
|
+
|
|
494
|
+
# alt_to_disp_ratio does not consider image resolution
|
|
495
|
+
with rio.open(sensor1, "r") as rio_dst:
|
|
496
|
+
pixel_size_x, pixel_size_y = (
|
|
497
|
+
rio_dst.transform[0],
|
|
498
|
+
rio_dst.transform[4],
|
|
499
|
+
)
|
|
500
|
+
mean_size = (abs(pixel_size_x) + abs(pixel_size_y)) / 2
|
|
501
|
+
disp_to_alt_ratio = mean_size / alt_to_disp_ratio
|
|
502
|
+
|
|
503
|
+
return (
|
|
504
|
+
grid1,
|
|
505
|
+
grid2,
|
|
506
|
+
origin,
|
|
507
|
+
spacing,
|
|
508
|
+
[epipolar_size_x, epipolar_size_y],
|
|
509
|
+
disp_to_alt_ratio,
|
|
510
|
+
)
|
|
511
|
+
|
|
512
|
+
def direct_loc( # pylint: disable=too-many-positional-arguments
|
|
513
|
+
self,
|
|
514
|
+
sensor,
|
|
515
|
+
geomodel,
|
|
516
|
+
x_coord: np.array,
|
|
517
|
+
y_coord: np.array,
|
|
518
|
+
z_coord: np.array = None,
|
|
519
|
+
) -> np.ndarray:
|
|
520
|
+
"""
|
|
521
|
+
For a given image point, compute the latitude, longitude, altitude
|
|
522
|
+
|
|
523
|
+
Advice: to be sure, use x,y,z inputs only
|
|
524
|
+
|
|
525
|
+
:param sensor: path to sensor image
|
|
526
|
+
:param geomodel: path and attributes for geomodel
|
|
527
|
+
:param x_coord: X Coordinate in input image sensor
|
|
528
|
+
:param y_coord: Y Coordinate in input image sensor
|
|
529
|
+
:param z_coord: Z Altitude coordinate to take the image
|
|
530
|
+
:return: Latitude, Longitude, Altitude coordinates as a numpy array
|
|
531
|
+
"""
|
|
532
|
+
# load model and image with shareloc
|
|
533
|
+
shareloc_model = SharelocGeometry.load_geom_model(geomodel)
|
|
534
|
+
shareloc_image = SharelocGeometry.load_image(sensor)
|
|
535
|
+
|
|
536
|
+
# perform direct localization operation
|
|
537
|
+
loc = localization.Localization(
|
|
538
|
+
shareloc_model,
|
|
539
|
+
image=shareloc_image,
|
|
540
|
+
elevation=self.elevation,
|
|
541
|
+
epsg=4326,
|
|
542
|
+
)
|
|
543
|
+
# Bug: y_coord and x_coord inversion to fit Shareloc standards row/col.
|
|
544
|
+
# TODO: clean geometry convention calls in API
|
|
545
|
+
lonlatalt = loc.direct(
|
|
546
|
+
y_coord, x_coord, z_coord, using_geotransform=True
|
|
547
|
+
)
|
|
548
|
+
lonlatalt = np.squeeze(lonlatalt)
|
|
549
|
+
if len(lonlatalt.shape) == 1:
|
|
550
|
+
latlonalt = np.array([lonlatalt[1], lonlatalt[0], lonlatalt[2]])
|
|
551
|
+
else:
|
|
552
|
+
latlonalt = np.array(
|
|
553
|
+
[lonlatalt[:, 1], lonlatalt[:, 0], lonlatalt[:, 2]]
|
|
554
|
+
)
|
|
555
|
+
return latlonalt
|
|
556
|
+
|
|
557
|
+
def inverse_loc( # pylint: disable=too-many-positional-arguments
|
|
558
|
+
self,
|
|
559
|
+
sensor,
|
|
560
|
+
geomodel,
|
|
561
|
+
lat_coord: np.array,
|
|
562
|
+
lon_coord: np.array,
|
|
563
|
+
z_coord: np.array = None,
|
|
564
|
+
) -> np.ndarray:
|
|
565
|
+
"""
|
|
566
|
+
For a given image points list, compute the latitudes,
|
|
567
|
+
longitudes, altitudes
|
|
568
|
+
|
|
569
|
+
Advice: to be sure, use x,y,z list inputs only
|
|
570
|
+
|
|
571
|
+
:param sensor: path to sensor image
|
|
572
|
+
:param geomodel: path and attributes for geomodel
|
|
573
|
+
:param lat_coord: latitute Coordinate list
|
|
574
|
+
:param lon_coord: longitude Coordinates list
|
|
575
|
+
:param z_coord: Z Altitude list
|
|
576
|
+
:return: X / Y / Z Coordinates list in input image as a numpy array
|
|
577
|
+
"""
|
|
578
|
+
|
|
579
|
+
# load model and image with shareloc
|
|
580
|
+
shareloc_model = SharelocGeometry.load_geom_model(geomodel)
|
|
581
|
+
shareloc_image = SharelocGeometry.load_image(sensor)
|
|
582
|
+
|
|
583
|
+
# perform inverse localization operation
|
|
584
|
+
loc = localization.Localization(
|
|
585
|
+
shareloc_model,
|
|
586
|
+
image=shareloc_image,
|
|
587
|
+
elevation=self.elevation,
|
|
588
|
+
epsg=4326,
|
|
589
|
+
)
|
|
590
|
+
# Rows and columns order is inversed
|
|
591
|
+
col, row, alti = loc.inverse(
|
|
592
|
+
lon_coord.astype(np.float64),
|
|
593
|
+
lat_coord.astype(np.float64),
|
|
594
|
+
h=z_coord.astype(np.float64),
|
|
595
|
+
using_geotransform=True,
|
|
596
|
+
)
|
|
597
|
+
|
|
598
|
+
return row, col, alti
|