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
cars/devibrate.py
ADDED
|
@@ -0,0 +1,689 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
# coding: utf8
|
|
3
|
+
#
|
|
4
|
+
# Copyright (c) 2024 Centre National d'Etudes Spatiales (CNES).
|
|
5
|
+
#
|
|
6
|
+
# This file is part of CARS
|
|
7
|
+
# (see https://github.com/CNES/cars).
|
|
8
|
+
#
|
|
9
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
10
|
+
# you may not use this file except in compliance with the License.
|
|
11
|
+
# You may obtain a copy of the License at
|
|
12
|
+
#
|
|
13
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
14
|
+
#
|
|
15
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
16
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
17
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
18
|
+
# See the License for the specific language governing permissions and
|
|
19
|
+
# limitations under the License.
|
|
20
|
+
#
|
|
21
|
+
"""
|
|
22
|
+
cars-devibrate: devibrate a high resolution DSM using a low resolution DSM
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
import argparse
|
|
26
|
+
import json
|
|
27
|
+
import logging
|
|
28
|
+
import math
|
|
29
|
+
|
|
30
|
+
# Standard imports
|
|
31
|
+
import os
|
|
32
|
+
import pickle
|
|
33
|
+
from typing import List, Union
|
|
34
|
+
|
|
35
|
+
# Third party imports
|
|
36
|
+
import numpy as np
|
|
37
|
+
import pandas as pd
|
|
38
|
+
import pyproj
|
|
39
|
+
import rasterio as rio
|
|
40
|
+
import xarray as xr
|
|
41
|
+
from rasterio.windows import bounds as to_bounds
|
|
42
|
+
from rasterio.windows import from_bounds
|
|
43
|
+
from scipy import interpolate
|
|
44
|
+
from scipy.signal import butter, filtfilt, lfilter, lfilter_zi
|
|
45
|
+
|
|
46
|
+
# CARS / SHARELOC imports
|
|
47
|
+
from shareloc.dtm_reader import interpolate_geoid_height
|
|
48
|
+
from shareloc.geofunctions import triangulation
|
|
49
|
+
|
|
50
|
+
from cars.applications.rasterization import rasterization_algo as rasterization
|
|
51
|
+
from cars.core.geometry.abstract_geometry import AbstractGeometry
|
|
52
|
+
from cars.core.geometry.shareloc_geometry import SharelocGeometry
|
|
53
|
+
|
|
54
|
+
# Get full path geoid
|
|
55
|
+
package_path = os.path.dirname(__file__)
|
|
56
|
+
GEOID_DEFAULT = os.path.join(package_path, "conf", "geoid/egm96.grd")
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def acquisition_direction(
|
|
60
|
+
sensor1, geomodel1, sensor2, geomodel2, geometry_plugin
|
|
61
|
+
):
|
|
62
|
+
"""
|
|
63
|
+
Computes the mean acquisition of the input images pair
|
|
64
|
+
|
|
65
|
+
:param sensor1: sensor image name of the first product
|
|
66
|
+
:param geomodel1: geomodel name of the first product
|
|
67
|
+
:param sensor2: sensor image name of the second product
|
|
68
|
+
:param geomodel2: geomodel name of the second product
|
|
69
|
+
:return: a tuple composed of :
|
|
70
|
+
- the mean acquisition direction as a numpy array
|
|
71
|
+
- the acquisition direction of the first product as a numpy array
|
|
72
|
+
- the acquisition direction of the second product as a numpy array
|
|
73
|
+
"""
|
|
74
|
+
vec1 = get_time_ground_direction(sensor1, geomodel1, geometry_plugin)
|
|
75
|
+
vec2 = get_time_ground_direction(sensor2, geomodel2, geometry_plugin)
|
|
76
|
+
time_direction_vector = (vec1 + vec2) / 2
|
|
77
|
+
|
|
78
|
+
def display_angle(vec):
|
|
79
|
+
"""
|
|
80
|
+
Display angle in degree from a vector x
|
|
81
|
+
:param vec: vector to display
|
|
82
|
+
:return: angle in degree
|
|
83
|
+
"""
|
|
84
|
+
return 180 * math.atan2(vec[1], vec[0]) / math.pi
|
|
85
|
+
|
|
86
|
+
logging.info(
|
|
87
|
+
"Time direction average azimuth: "
|
|
88
|
+
"{}deg (img1: {}deg, img2: {}deg)".format(
|
|
89
|
+
display_angle(time_direction_vector),
|
|
90
|
+
display_angle(vec1),
|
|
91
|
+
display_angle(vec2),
|
|
92
|
+
)
|
|
93
|
+
)
|
|
94
|
+
|
|
95
|
+
return time_direction_vector, vec1, vec2
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def get_time_ground_direction( # pylint: disable=too-many-positional-arguments
|
|
99
|
+
sensor,
|
|
100
|
+
geomodel,
|
|
101
|
+
geometry_plugin,
|
|
102
|
+
x_loc: float = None,
|
|
103
|
+
y_loc: float = None,
|
|
104
|
+
y_offset: float = None,
|
|
105
|
+
) -> np.ndarray:
|
|
106
|
+
"""
|
|
107
|
+
For a given image, compute the direction of increasing acquisition
|
|
108
|
+
time on ground.
|
|
109
|
+
Done by two localizations at "y" and "y+y_offset" values.
|
|
110
|
+
|
|
111
|
+
:param sensor: sensor image name
|
|
112
|
+
:param geomodel: geomodel name
|
|
113
|
+
:param x_loc: x location in image for estimation (default=center)
|
|
114
|
+
:param y_loc: y location in image for estimation (default=1/4)
|
|
115
|
+
:param y_offset: y location in image for estimation (default=1/2)
|
|
116
|
+
:param dem: DEM for direct localisation function
|
|
117
|
+
:param geoid: path to geoid file
|
|
118
|
+
:return: normalized direction vector as a numpy array
|
|
119
|
+
"""
|
|
120
|
+
# Define x: image center, y: 1/4 of image,
|
|
121
|
+
# y_offset: 3/4 of image if not defined
|
|
122
|
+
with rio.open(sensor) as src:
|
|
123
|
+
img_size_x, img_size_y = src.width, src.height
|
|
124
|
+
if x_loc is None:
|
|
125
|
+
x_loc = img_size_x / 2
|
|
126
|
+
if y_loc is None:
|
|
127
|
+
y_loc = img_size_y / 4
|
|
128
|
+
if y_offset is None:
|
|
129
|
+
y_offset = img_size_y / 2
|
|
130
|
+
|
|
131
|
+
# Check x, y, y_offset to be in image
|
|
132
|
+
assert x_loc >= 0
|
|
133
|
+
assert x_loc <= img_size_x
|
|
134
|
+
assert y_loc >= 0
|
|
135
|
+
assert y_loc <= img_size_y
|
|
136
|
+
assert y_offset > 0
|
|
137
|
+
assert y_loc + y_offset <= img_size_y
|
|
138
|
+
|
|
139
|
+
# Get coordinates of time direction vectors
|
|
140
|
+
lat1, lon1, __ = geometry_plugin.direct_loc(sensor, geomodel, x_loc, y_loc)
|
|
141
|
+
lat2, lon2, __ = geometry_plugin.direct_loc(
|
|
142
|
+
sensor, geomodel, x_loc, y_loc + y_offset
|
|
143
|
+
)
|
|
144
|
+
|
|
145
|
+
# Create and normalize the time direction vector
|
|
146
|
+
vec = np.array([lon1 - lon2, lat1 - lat2])
|
|
147
|
+
vec = vec / np.linalg.norm(vec)
|
|
148
|
+
|
|
149
|
+
return vec
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
def project_coordinates_on_line(
|
|
153
|
+
x_coord: Union[float, np.ndarray],
|
|
154
|
+
y_coord: Union[float, np.ndarray],
|
|
155
|
+
origin: Union[List[float], np.ndarray],
|
|
156
|
+
vec: Union[List[float], np.ndarray],
|
|
157
|
+
) -> Union[float, np.ndarray]:
|
|
158
|
+
"""
|
|
159
|
+
Project coordinates (x, y) on a line starting from origin with a
|
|
160
|
+
direction vector vec, and return the euclidean distances between
|
|
161
|
+
projected points and origin.
|
|
162
|
+
|
|
163
|
+
:param x_coord: scalar or vector of coordinates x
|
|
164
|
+
:param y_coord: scalar or vector of coordinates y
|
|
165
|
+
:param origin: coordinates of origin point for line
|
|
166
|
+
:param vec: direction vector of line
|
|
167
|
+
:return: vector of distances of projected points to origin
|
|
168
|
+
"""
|
|
169
|
+
assert len(x_coord) == len(y_coord)
|
|
170
|
+
assert len(origin) == 2
|
|
171
|
+
assert len(vec) == 2
|
|
172
|
+
|
|
173
|
+
vec_angle = math.atan2(vec[1], vec[0])
|
|
174
|
+
point_angle = np.arctan2(y_coord - origin[1], x_coord - origin[0])
|
|
175
|
+
proj_angle = point_angle - vec_angle
|
|
176
|
+
dist_to_origin = np.sqrt(
|
|
177
|
+
np.square(x_coord - origin[0]) + np.square(y_coord - origin[1])
|
|
178
|
+
)
|
|
179
|
+
|
|
180
|
+
return dist_to_origin * np.cos(proj_angle)
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
# pylint: disable=too-many-positional-arguments
|
|
184
|
+
def lowres_initial_dem_splines_fit(
|
|
185
|
+
lowres_dsm_from_matches: xr.Dataset,
|
|
186
|
+
lowres_initial_dem: xr.Dataset,
|
|
187
|
+
origin: np.ndarray,
|
|
188
|
+
time_direction_vector: np.ndarray,
|
|
189
|
+
ext: int = 3,
|
|
190
|
+
order: int = 3,
|
|
191
|
+
min_pts_per_time: int = 100,
|
|
192
|
+
min_pts_along_time_direction: int = 100,
|
|
193
|
+
butterworth_filter_order: int = 3,
|
|
194
|
+
butterworth_critical_frequency: float = 0.05,
|
|
195
|
+
):
|
|
196
|
+
"""
|
|
197
|
+
This function takes 2 datasets containing DSM and models the
|
|
198
|
+
difference between the two as an UnivariateSpline along the
|
|
199
|
+
direction given by origin and time_direction_vector. Internally,
|
|
200
|
+
it looks for the highest smoothing factor that satisfies the rmse threshold.
|
|
201
|
+
|
|
202
|
+
:param lowres_dsm_from_matches: Dataset containing the low resolution DSM
|
|
203
|
+
obtained from matches, as returned by the
|
|
204
|
+
rasterization.simple_rasterization_dataset function.
|
|
205
|
+
:param lowres_initial_dem: Dataset containing the low resolution DEM,
|
|
206
|
+
on the same grid as lowres_dsm_from_matches
|
|
207
|
+
:param origin: coordinates of origin point for line
|
|
208
|
+
:type origin: list(float) or np.array(float) of size 2
|
|
209
|
+
:param time_direction_vector: direction vector of line
|
|
210
|
+
:type time_direction_vector: list(float) or np.array(float) of size 2
|
|
211
|
+
:param ext: behavior outside of interpolation domain
|
|
212
|
+
:param order: spline order
|
|
213
|
+
:param min_pts_per_time: minimum number of points for
|
|
214
|
+
each measurement
|
|
215
|
+
:param min_pts_along_time_direction: minimum number of points for
|
|
216
|
+
time direction
|
|
217
|
+
:param butterworth_filter_order: Order of the filter.
|
|
218
|
+
See scipy.signal.butter
|
|
219
|
+
:param butterworth_critical_frequency: The filter critical frequency
|
|
220
|
+
or frequencies. See scipy.signal.butter
|
|
221
|
+
"""
|
|
222
|
+
# Initial DSM difference
|
|
223
|
+
dsm_diff = lowres_initial_dem.hgt - lowres_dsm_from_matches.hgt
|
|
224
|
+
|
|
225
|
+
# Form arrays of coordinates
|
|
226
|
+
x_values_2d, y_values_2d = np.meshgrid(dsm_diff.x, dsm_diff.y)
|
|
227
|
+
|
|
228
|
+
# Project coordinates on the line
|
|
229
|
+
# of increasing acquisition time to form 1D coordinates
|
|
230
|
+
# If there are sensor oscillations, they will occur in this direction
|
|
231
|
+
linear_coords = project_coordinates_on_line(
|
|
232
|
+
x_values_2d, y_values_2d, origin, time_direction_vector
|
|
233
|
+
)
|
|
234
|
+
|
|
235
|
+
# Form a 1D array with diff values indexed with linear coords
|
|
236
|
+
linear_diff_array = xr.DataArray(
|
|
237
|
+
dsm_diff.values.ravel(), coords={"l": linear_coords.ravel()}, dims=("l")
|
|
238
|
+
)
|
|
239
|
+
linear_diff_array = linear_diff_array.dropna(dim="l")
|
|
240
|
+
linear_diff_array = linear_diff_array.sortby("l")
|
|
241
|
+
|
|
242
|
+
# Apply median filtering within cell matching input dsm resolution
|
|
243
|
+
min_l = np.min(linear_diff_array.l)
|
|
244
|
+
max_l = np.max(linear_diff_array.l)
|
|
245
|
+
nbins = int(
|
|
246
|
+
math.ceil((max_l - min_l) / (lowres_dsm_from_matches.resolution))
|
|
247
|
+
)
|
|
248
|
+
median_linear_diff_array = linear_diff_array.groupby_bins(
|
|
249
|
+
"l", nbins
|
|
250
|
+
).median()
|
|
251
|
+
median_linear_diff_array = median_linear_diff_array.rename({"l_bins": "l"})
|
|
252
|
+
median_linear_diff_array = median_linear_diff_array.assign_coords(
|
|
253
|
+
{"l": np.array([d.mid for d in median_linear_diff_array.l.data])}
|
|
254
|
+
)
|
|
255
|
+
|
|
256
|
+
count_linear_diff_array = linear_diff_array.groupby_bins("l", nbins).count()
|
|
257
|
+
count_linear_diff_array = count_linear_diff_array.rename({"l_bins": "l"})
|
|
258
|
+
count_linear_diff_array = count_linear_diff_array.assign_coords(
|
|
259
|
+
{"l": np.array([d.mid for d in count_linear_diff_array.l.data])}
|
|
260
|
+
)
|
|
261
|
+
|
|
262
|
+
# Filter measurements with insufficient amount of points
|
|
263
|
+
median_linear_diff_array = median_linear_diff_array.where(
|
|
264
|
+
count_linear_diff_array > min_pts_per_time
|
|
265
|
+
).dropna(dim="l")
|
|
266
|
+
|
|
267
|
+
if len(median_linear_diff_array) < min_pts_along_time_direction:
|
|
268
|
+
raise RuntimeError(
|
|
269
|
+
"Insufficient amount of points ({} < {}) along time direction "
|
|
270
|
+
"after measurements filtering to estimate correction "
|
|
271
|
+
"to fit initial DEM".format(
|
|
272
|
+
len(median_linear_diff_array), min_pts_along_time_direction
|
|
273
|
+
)
|
|
274
|
+
)
|
|
275
|
+
|
|
276
|
+
# Apply butterworth lowpass filter to retrieve only the low frequency
|
|
277
|
+
# (from example of doc: https://docs.scipy.org/doc/scipy/reference/
|
|
278
|
+
# generated/scipy.signal.butter.html#scipy.signal.butter )
|
|
279
|
+
b, a = butter(butterworth_filter_order, butterworth_critical_frequency)
|
|
280
|
+
zi_filter = lfilter_zi(b, a)
|
|
281
|
+
z_filter, _ = lfilter(
|
|
282
|
+
b,
|
|
283
|
+
a,
|
|
284
|
+
median_linear_diff_array.values,
|
|
285
|
+
zi=zi_filter * median_linear_diff_array.values[0],
|
|
286
|
+
)
|
|
287
|
+
lfilter(b, a, z_filter, zi=zi_filter * z_filter[0])
|
|
288
|
+
filtered_median_linear_diff_array = xr.DataArray(
|
|
289
|
+
filtfilt(b, a, median_linear_diff_array.values),
|
|
290
|
+
coords=median_linear_diff_array.coords,
|
|
291
|
+
)
|
|
292
|
+
|
|
293
|
+
# Estimate performances of spline s = 100 * length of diff array
|
|
294
|
+
smoothing_factor = 100 * len(filtered_median_linear_diff_array.l)
|
|
295
|
+
splines = interpolate.UnivariateSpline(
|
|
296
|
+
filtered_median_linear_diff_array.l,
|
|
297
|
+
filtered_median_linear_diff_array.values,
|
|
298
|
+
ext=ext,
|
|
299
|
+
k=order,
|
|
300
|
+
s=smoothing_factor,
|
|
301
|
+
)
|
|
302
|
+
estimated_correction = xr.DataArray(
|
|
303
|
+
splines(filtered_median_linear_diff_array.l),
|
|
304
|
+
coords=filtered_median_linear_diff_array.coords,
|
|
305
|
+
)
|
|
306
|
+
rmse = (filtered_median_linear_diff_array - estimated_correction).std(
|
|
307
|
+
dim="l"
|
|
308
|
+
)
|
|
309
|
+
|
|
310
|
+
target_rmse = 0.3
|
|
311
|
+
|
|
312
|
+
# If RMSE is too high, try to decrease smoothness until it fits
|
|
313
|
+
while rmse > target_rmse and smoothing_factor > 0.001:
|
|
314
|
+
# divide s by 2
|
|
315
|
+
smoothing_factor /= 2
|
|
316
|
+
|
|
317
|
+
# Estimate splines
|
|
318
|
+
splines = interpolate.UnivariateSpline(
|
|
319
|
+
filtered_median_linear_diff_array.l,
|
|
320
|
+
filtered_median_linear_diff_array.values,
|
|
321
|
+
ext=ext,
|
|
322
|
+
k=order,
|
|
323
|
+
s=smoothing_factor,
|
|
324
|
+
)
|
|
325
|
+
|
|
326
|
+
# Compute RMSE
|
|
327
|
+
estimated_correction = xr.DataArray(
|
|
328
|
+
splines(filtered_median_linear_diff_array.l),
|
|
329
|
+
coords=filtered_median_linear_diff_array.coords,
|
|
330
|
+
)
|
|
331
|
+
|
|
332
|
+
rmse = (filtered_median_linear_diff_array - estimated_correction).std(
|
|
333
|
+
dim="l"
|
|
334
|
+
)
|
|
335
|
+
|
|
336
|
+
logging.debug(
|
|
337
|
+
"Best smoothing factor for splines regression: "
|
|
338
|
+
"{} (rmse={})".format(smoothing_factor, rmse)
|
|
339
|
+
)
|
|
340
|
+
|
|
341
|
+
# Return estimated spline
|
|
342
|
+
return splines
|
|
343
|
+
|
|
344
|
+
|
|
345
|
+
def read_lowres_dsm(srtm_path, startx, starty, endx, endy):
|
|
346
|
+
"""
|
|
347
|
+
Read an extract of the low resolution input DSM and return it as an Array
|
|
348
|
+
"""
|
|
349
|
+
|
|
350
|
+
with rio.open(srtm_path) as src:
|
|
351
|
+
window = from_bounds(startx, starty, endx, endy, src.transform)
|
|
352
|
+
window = window.round_offsets()
|
|
353
|
+
window = window.round_lengths()
|
|
354
|
+
bounds = to_bounds(window, src.transform)
|
|
355
|
+
resolution = src.res[0]
|
|
356
|
+
dem_as_array = src.read(1, window=window)
|
|
357
|
+
|
|
358
|
+
newstartx, newstarty = bounds[0], bounds[-1]
|
|
359
|
+
sizex, sizey = window.width, window.height
|
|
360
|
+
|
|
361
|
+
x_values_1d = np.linspace(
|
|
362
|
+
newstartx + 0.5 * resolution,
|
|
363
|
+
newstartx + resolution * (sizex + 0.5),
|
|
364
|
+
sizex,
|
|
365
|
+
endpoint=False,
|
|
366
|
+
)
|
|
367
|
+
|
|
368
|
+
y_values_1d = np.linspace(
|
|
369
|
+
newstarty - 0.5 * resolution,
|
|
370
|
+
newstarty - resolution * (sizey + 0.5),
|
|
371
|
+
sizey,
|
|
372
|
+
endpoint=False,
|
|
373
|
+
)
|
|
374
|
+
|
|
375
|
+
dims = ["y", "x"]
|
|
376
|
+
coords = {"x": x_values_1d, "y": y_values_1d}
|
|
377
|
+
dsm_as_ds = xr.Dataset({"hgt": (dims, dem_as_array)}, coords=coords)
|
|
378
|
+
dsm_as_ds["epsg"] = 4326
|
|
379
|
+
dsm_as_ds["resolution"] = resolution
|
|
380
|
+
return dsm_as_ds, newstartx, newstarty, sizex, sizey, resolution
|
|
381
|
+
|
|
382
|
+
|
|
383
|
+
def compute_splines( # pylint: disable=too-many-positional-arguments
|
|
384
|
+
sensor1,
|
|
385
|
+
geomodel1,
|
|
386
|
+
sensor2,
|
|
387
|
+
geomodel2,
|
|
388
|
+
matches,
|
|
389
|
+
srtm_path,
|
|
390
|
+
geoid_path,
|
|
391
|
+
out_dir,
|
|
392
|
+
min_pts_per_time: int = 100,
|
|
393
|
+
min_pts_along_time_direction: int = 100,
|
|
394
|
+
butterworth_filter_order: int = 3,
|
|
395
|
+
butterworth_critical_frequency: float = 0.05,
|
|
396
|
+
):
|
|
397
|
+
"""
|
|
398
|
+
Compute a spline dict containing estimated splines, origin
|
|
399
|
+
and time_direction_vector
|
|
400
|
+
"""
|
|
401
|
+
geometry_plugin = AbstractGeometry( # pylint: disable=E0110
|
|
402
|
+
"SharelocGeometry"
|
|
403
|
+
)
|
|
404
|
+
|
|
405
|
+
# retrieve time direction from models
|
|
406
|
+
time_direction_vector, _, _ = acquisition_direction(
|
|
407
|
+
sensor1, geomodel1, sensor2, geomodel2, geometry_plugin
|
|
408
|
+
)
|
|
409
|
+
|
|
410
|
+
# load matches and triangulate them
|
|
411
|
+
corrected_matches = np.load(matches)
|
|
412
|
+
disp = corrected_matches[:, 0] - corrected_matches[:, 2]
|
|
413
|
+
mini = np.percentile(disp, 5.0)
|
|
414
|
+
maxi = np.percentile(disp, 95)
|
|
415
|
+
corrected_matches = corrected_matches[
|
|
416
|
+
np.logical_and(disp >= mini, disp <= maxi), :
|
|
417
|
+
]
|
|
418
|
+
|
|
419
|
+
shareloc_model1 = SharelocGeometry.load_geom_model(geomodel1)
|
|
420
|
+
shareloc_model2 = SharelocGeometry.load_geom_model(geomodel2)
|
|
421
|
+
|
|
422
|
+
matches_sensor = corrected_matches[:, 4:8]
|
|
423
|
+
[__, pc_from_matches, __] = triangulation.sensor_triangulation(
|
|
424
|
+
matches_sensor, shareloc_model1, shareloc_model2
|
|
425
|
+
)
|
|
426
|
+
|
|
427
|
+
positions = np.array(pc_from_matches[:, 0:2])
|
|
428
|
+
geoid_height = interpolate_geoid_height(geoid_path, positions)
|
|
429
|
+
pc_from_matches[:, 2] = pc_from_matches[:, 2] - geoid_height
|
|
430
|
+
|
|
431
|
+
# deduce area from sift
|
|
432
|
+
pc_xx = pc_from_matches[:, 0]
|
|
433
|
+
pc_yy = pc_from_matches[:, 1]
|
|
434
|
+
startx, endx = pc_xx.min(), pc_xx.max()
|
|
435
|
+
starty, endy = pc_yy.min(), pc_yy.max()
|
|
436
|
+
|
|
437
|
+
# read corresponding dem
|
|
438
|
+
lowres_initial_dem, startx, starty, sizex, sizey, resolution = (
|
|
439
|
+
read_lowres_dsm(srtm_path, startx, starty, endx, endy)
|
|
440
|
+
)
|
|
441
|
+
|
|
442
|
+
points_cloud_from_matches = pd.DataFrame(
|
|
443
|
+
data=pc_from_matches, columns=["x", "y", "z"]
|
|
444
|
+
)
|
|
445
|
+
|
|
446
|
+
# rasterize point cloud (superimposable image with lowres initial dem)
|
|
447
|
+
points_cloud_from_matches.attrs["attributes"] = {"number_of_pc": 0}
|
|
448
|
+
lowres_dsm = rasterization.simple_rasterization_dataset_wrapper(
|
|
449
|
+
points_cloud_from_matches,
|
|
450
|
+
resolution,
|
|
451
|
+
4326,
|
|
452
|
+
xstart=startx,
|
|
453
|
+
ystart=starty,
|
|
454
|
+
xsize=sizex,
|
|
455
|
+
ysize=sizey,
|
|
456
|
+
)
|
|
457
|
+
|
|
458
|
+
lowres_dsm_diff = lowres_initial_dem - lowres_dsm
|
|
459
|
+
origin = [
|
|
460
|
+
float(lowres_dsm_diff["x"][0].values),
|
|
461
|
+
float(lowres_dsm_diff["y"][0].values),
|
|
462
|
+
]
|
|
463
|
+
|
|
464
|
+
# fit initial dem and low res dsm from sift and deduce splines correction
|
|
465
|
+
splines = lowres_initial_dem_splines_fit(
|
|
466
|
+
lowres_dsm,
|
|
467
|
+
lowres_initial_dem,
|
|
468
|
+
origin,
|
|
469
|
+
time_direction_vector,
|
|
470
|
+
min_pts_per_time=min_pts_per_time,
|
|
471
|
+
min_pts_along_time_direction=min_pts_along_time_direction,
|
|
472
|
+
butterworth_filter_order=butterworth_filter_order,
|
|
473
|
+
butterworth_critical_frequency=butterworth_critical_frequency,
|
|
474
|
+
)
|
|
475
|
+
|
|
476
|
+
# save intermediate data
|
|
477
|
+
lowres_initial_dem_file = os.path.join(out_dir, "lowres_initial_dem.nc")
|
|
478
|
+
lowres_initial_dem.to_netcdf(lowres_initial_dem_file)
|
|
479
|
+
lowres_dsm_file = os.path.join(out_dir, "lowres_dsm.nc")
|
|
480
|
+
lowres_dsm.to_netcdf(lowres_dsm_file)
|
|
481
|
+
lowres_dsm_diff_file = os.path.join(out_dir, "lowres_dsm_diff.nc")
|
|
482
|
+
lowres_dsm_diff.to_netcdf(lowres_dsm_diff_file)
|
|
483
|
+
|
|
484
|
+
# use splines for correction introduced in rasterization
|
|
485
|
+
lowres_dsm_as_df = lowres_dsm.to_dataframe().reset_index()
|
|
486
|
+
|
|
487
|
+
distance_vector = project_coordinates_on_line(
|
|
488
|
+
lowres_dsm_as_df["x"],
|
|
489
|
+
lowres_dsm_as_df["y"],
|
|
490
|
+
origin,
|
|
491
|
+
time_direction_vector,
|
|
492
|
+
)
|
|
493
|
+
correction = splines(distance_vector)
|
|
494
|
+
|
|
495
|
+
lowres_dsm["hgt"] = lowres_dsm["hgt"] + correction.reshape(
|
|
496
|
+
lowres_dsm["hgt"].shape
|
|
497
|
+
)
|
|
498
|
+
new_lowres_dsm_file = os.path.join(out_dir, "new_lowres_dsm.nc")
|
|
499
|
+
lowres_dsm.to_netcdf(new_lowres_dsm_file)
|
|
500
|
+
new_lowres_dsm_diff = lowres_initial_dem - lowres_dsm
|
|
501
|
+
new_lowres_dsm_diff_file = os.path.join(out_dir, "new_lowres_dsm_diff.nc")
|
|
502
|
+
new_lowres_dsm_diff.to_netcdf(new_lowres_dsm_diff_file)
|
|
503
|
+
|
|
504
|
+
return {
|
|
505
|
+
"splines": splines,
|
|
506
|
+
"origin": origin,
|
|
507
|
+
"time_direction_vector": time_direction_vector,
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
|
|
511
|
+
def cars_devibrate( # pylint: disable=too-many-positional-arguments
|
|
512
|
+
used_conf,
|
|
513
|
+
srtm_path,
|
|
514
|
+
geoid_path,
|
|
515
|
+
min_pts_per_time: int = 100,
|
|
516
|
+
min_pts_along_time_direction: int = 100,
|
|
517
|
+
butterworth_filter_order: int = 3,
|
|
518
|
+
butterworth_critical_frequency: float = 0.05,
|
|
519
|
+
):
|
|
520
|
+
"""
|
|
521
|
+
Main fonction. Expects a dictionary from the CLI
|
|
522
|
+
or directly the input parameters.
|
|
523
|
+
"""
|
|
524
|
+
out_dir = os.path.dirname(used_conf)
|
|
525
|
+
|
|
526
|
+
with open(used_conf, "r", encoding="utf8") as jsonfile:
|
|
527
|
+
data = json.load(jsonfile)
|
|
528
|
+
|
|
529
|
+
pairing = data["inputs"]["pairing"]
|
|
530
|
+
assert len(pairing) == 1
|
|
531
|
+
|
|
532
|
+
sensor1 = data["inputs"]["sensors"][pairing[0][0]]["image"]
|
|
533
|
+
geomodel1 = data["inputs"]["sensors"][pairing[0][0]]["geomodel"]
|
|
534
|
+
sensor2 = data["inputs"]["sensors"][pairing[0][1]]["image"]
|
|
535
|
+
geomodel2 = data["inputs"]["sensors"][pairing[0][1]]["geomodel"]
|
|
536
|
+
|
|
537
|
+
matches = os.path.join(
|
|
538
|
+
out_dir,
|
|
539
|
+
"dump_dir",
|
|
540
|
+
"sparse_matching.sift",
|
|
541
|
+
"_".join(pairing[0]),
|
|
542
|
+
"filtered_matches.npy",
|
|
543
|
+
)
|
|
544
|
+
if not os.path.isfile(matches):
|
|
545
|
+
raise RuntimeError(
|
|
546
|
+
"Matches must be saved: Set applications.sparse_matching."
|
|
547
|
+
"sift.save_intermediate_data to true in CARS configuration file."
|
|
548
|
+
)
|
|
549
|
+
|
|
550
|
+
dsm_path = os.path.join(out_dir, "dsm", "dsm.tif")
|
|
551
|
+
if not os.path.isfile(dsm_path):
|
|
552
|
+
raise RuntimeError("DSM must be generated: set product level to `dsm`")
|
|
553
|
+
splines_path = os.path.join(out_dir, "splines.pck")
|
|
554
|
+
|
|
555
|
+
if os.path.exists(splines_path) is False:
|
|
556
|
+
splines_dict = compute_splines(
|
|
557
|
+
sensor1,
|
|
558
|
+
geomodel1,
|
|
559
|
+
sensor2,
|
|
560
|
+
geomodel2,
|
|
561
|
+
matches,
|
|
562
|
+
srtm_path,
|
|
563
|
+
geoid_path,
|
|
564
|
+
out_dir,
|
|
565
|
+
min_pts_per_time=min_pts_per_time,
|
|
566
|
+
min_pts_along_time_direction=min_pts_along_time_direction,
|
|
567
|
+
butterworth_filter_order=butterworth_filter_order,
|
|
568
|
+
butterworth_critical_frequency=butterworth_critical_frequency,
|
|
569
|
+
)
|
|
570
|
+
|
|
571
|
+
with open(splines_path, "wb") as writer:
|
|
572
|
+
pickle.dump(splines_dict, writer)
|
|
573
|
+
else:
|
|
574
|
+
with open(splines_path, "rb") as reader:
|
|
575
|
+
splines_dict = pickle.load(reader)
|
|
576
|
+
|
|
577
|
+
with rio.open(dsm_path) as src:
|
|
578
|
+
bounds = src.bounds
|
|
579
|
+
startx, starty = bounds[0], bounds[-1]
|
|
580
|
+
sizex, sizey = src.width, src.height
|
|
581
|
+
resolution = src.res[0]
|
|
582
|
+
|
|
583
|
+
x_values_1d = np.linspace(
|
|
584
|
+
startx + 0.5 * resolution,
|
|
585
|
+
startx + resolution * (sizex + 0.5),
|
|
586
|
+
sizex,
|
|
587
|
+
endpoint=False,
|
|
588
|
+
)
|
|
589
|
+
|
|
590
|
+
y_values_1d = np.linspace(
|
|
591
|
+
starty - 0.5 * resolution,
|
|
592
|
+
starty - resolution * (sizey + 0.5),
|
|
593
|
+
sizey,
|
|
594
|
+
endpoint=False,
|
|
595
|
+
)
|
|
596
|
+
|
|
597
|
+
x_values_2d, y_values_2d = np.meshgrid(x_values_1d, y_values_1d)
|
|
598
|
+
|
|
599
|
+
positions = None
|
|
600
|
+
if src.crs != "EPSG:4326":
|
|
601
|
+
transformer = pyproj.Transformer.from_crs(
|
|
602
|
+
src.crs, "EPSG:4326", always_xy=True
|
|
603
|
+
)
|
|
604
|
+
positions = np.array(
|
|
605
|
+
transformer.transform(x_values_2d, y_values_2d)
|
|
606
|
+
)
|
|
607
|
+
|
|
608
|
+
distance_vector = project_coordinates_on_line(
|
|
609
|
+
positions[0],
|
|
610
|
+
positions[1],
|
|
611
|
+
splines_dict["origin"],
|
|
612
|
+
splines_dict["time_direction_vector"],
|
|
613
|
+
)
|
|
614
|
+
|
|
615
|
+
bloclen = int(distance_vector.shape[0] / 2)
|
|
616
|
+
correction_1 = splines_dict["splines"](distance_vector[:bloclen, :])
|
|
617
|
+
correction_2 = splines_dict["splines"](distance_vector[bloclen:, :])
|
|
618
|
+
correction = np.vstack((correction_1, correction_2))
|
|
619
|
+
|
|
620
|
+
profile = src.profile
|
|
621
|
+
# User can apply correction with:
|
|
622
|
+
# otbcli_BandMath -il dsm.tif correction.tif \
|
|
623
|
+
# -exp "im1b1==-32768?-32768:im1b1+im2b1" -out corrected_dsm.tif
|
|
624
|
+
with rio.open(
|
|
625
|
+
os.path.join(out_dir, "correction.tif"), "w", **profile
|
|
626
|
+
) as dst:
|
|
627
|
+
dst.write(correction, 1)
|
|
628
|
+
|
|
629
|
+
|
|
630
|
+
def cli():
|
|
631
|
+
"""
|
|
632
|
+
Main cars-devibrate entrypoint (Command Line Interface)
|
|
633
|
+
"""
|
|
634
|
+
parser = argparse.ArgumentParser(
|
|
635
|
+
"cars-devibrate",
|
|
636
|
+
description="Devibrate a DSM produced from stereo images",
|
|
637
|
+
)
|
|
638
|
+
|
|
639
|
+
parser.add_argument(
|
|
640
|
+
"used_conf", type=str, help="CARS Used Configuration File"
|
|
641
|
+
)
|
|
642
|
+
|
|
643
|
+
parser.add_argument(
|
|
644
|
+
"srtm_path",
|
|
645
|
+
type=str,
|
|
646
|
+
help="SRTM path",
|
|
647
|
+
)
|
|
648
|
+
|
|
649
|
+
parser.add_argument(
|
|
650
|
+
"--geoid_path",
|
|
651
|
+
type=str,
|
|
652
|
+
help="Geoid path",
|
|
653
|
+
default=GEOID_DEFAULT,
|
|
654
|
+
)
|
|
655
|
+
|
|
656
|
+
parser.add_argument(
|
|
657
|
+
"--min_pts_per_time",
|
|
658
|
+
type=int,
|
|
659
|
+
help="minimum number of points for" "each measurement",
|
|
660
|
+
default=100,
|
|
661
|
+
)
|
|
662
|
+
|
|
663
|
+
parser.add_argument(
|
|
664
|
+
"--min_pts_along_time_direction",
|
|
665
|
+
type=int,
|
|
666
|
+
help="minimum number of points for" "time direction",
|
|
667
|
+
default=100,
|
|
668
|
+
)
|
|
669
|
+
|
|
670
|
+
parser.add_argument(
|
|
671
|
+
"--butterworth_filter_order",
|
|
672
|
+
type=int,
|
|
673
|
+
help="Order of the butterworth filter",
|
|
674
|
+
default=3,
|
|
675
|
+
)
|
|
676
|
+
|
|
677
|
+
parser.add_argument(
|
|
678
|
+
"--butterworth_critical_frequency",
|
|
679
|
+
type=float,
|
|
680
|
+
help=" The butterworth filter critical frequency",
|
|
681
|
+
default=0.05,
|
|
682
|
+
)
|
|
683
|
+
|
|
684
|
+
args = parser.parse_args()
|
|
685
|
+
cars_devibrate(**vars(args))
|
|
686
|
+
|
|
687
|
+
|
|
688
|
+
if __name__ == "__main__":
|
|
689
|
+
cli()
|