cars 1.0.0rc1__cp312-cp312-manylinux_2_17_i686.manylinux2014_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-312-i386-linux-gnu.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 +200 -0
- cars-1.0.0rc1.dist-info/WHEEL +6 -0
- cars-1.0.0rc1.dist-info/entry_points.txt +8 -0
|
@@ -0,0 +1,496 @@
|
|
|
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
|
+
"""
|
|
22
|
+
Grids module:
|
|
23
|
+
contains functions used for epipolar grid correction
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
# Standard imports
|
|
27
|
+
from __future__ import absolute_import
|
|
28
|
+
|
|
29
|
+
import logging
|
|
30
|
+
import os
|
|
31
|
+
|
|
32
|
+
# Third party imports
|
|
33
|
+
import numpy as np
|
|
34
|
+
import rasterio as rio
|
|
35
|
+
from scipy.interpolate import LinearNDInterpolator
|
|
36
|
+
from scipy.spatial import Delaunay # pylint: disable=E0611
|
|
37
|
+
|
|
38
|
+
import cars.orchestrator.orchestrator as ocht
|
|
39
|
+
from cars.applications import application_constants
|
|
40
|
+
from cars.applications.grid_generation import (
|
|
41
|
+
grid_generation_algo,
|
|
42
|
+
)
|
|
43
|
+
from cars.applications.grid_generation import (
|
|
44
|
+
grid_generation_constants as grid_constants,
|
|
45
|
+
)
|
|
46
|
+
from cars.core.utils import safe_makedirs
|
|
47
|
+
|
|
48
|
+
# CARS imports
|
|
49
|
+
from cars.orchestrator.cluster.log_wrapper import cars_profile
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
@cars_profile(name="Correct grid from 1d")
|
|
53
|
+
def correct_grid_from_1d(
|
|
54
|
+
grid, grid_correction_coef, save_grid=False, pair_folder=None
|
|
55
|
+
):
|
|
56
|
+
"""
|
|
57
|
+
Correct grid from correction given in 1d
|
|
58
|
+
|
|
59
|
+
:param grid: grid to correct
|
|
60
|
+
:type grid: CarsDataset
|
|
61
|
+
:param grid_correction_coef: grid correction to apply
|
|
62
|
+
:type grid_correction_coef: list(float), size 6
|
|
63
|
+
:param save_grid: if True grids are saved in root or pair_folder
|
|
64
|
+
:type save_grid: bool
|
|
65
|
+
:param pair_folder: directory where grids are saved
|
|
66
|
+
:type pair_folder: str
|
|
67
|
+
"""
|
|
68
|
+
|
|
69
|
+
coefs_x = grid_correction_coef[:3]
|
|
70
|
+
coefs_x.append(0.0)
|
|
71
|
+
coefs_y = grid_correction_coef[3:6]
|
|
72
|
+
coefs_y.append(0.0)
|
|
73
|
+
grid_correction_coef = (
|
|
74
|
+
np.array(coefs_x).reshape((2, 2)),
|
|
75
|
+
np.array(coefs_y).reshape((2, 2)),
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
# Correct grid right with provided epipolar a priori
|
|
79
|
+
corrected_grid_right = correct_grid(
|
|
80
|
+
grid, grid_correction_coef, pair_folder, save_grid
|
|
81
|
+
)
|
|
82
|
+
|
|
83
|
+
return corrected_grid_right
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
@cars_profile(name="Correct grid")
|
|
87
|
+
def correct_grid(grid, grid_correction, pair_folder, save_grid=None):
|
|
88
|
+
"""
|
|
89
|
+
Correct grid
|
|
90
|
+
|
|
91
|
+
:param grid: grid to correct
|
|
92
|
+
:type grid: dict
|
|
93
|
+
:param grid_correction: grid correction to apply
|
|
94
|
+
:type grid_correction: Tuple(np.ndarray, np.ndarray)
|
|
95
|
+
(coefsx_2d, coefsy_2d) , each of size (2,2)
|
|
96
|
+
:param pair_folder: directory where grids are saved: either in
|
|
97
|
+
pair_folder/tmp, or at the root of pair_folder if save_grid is True
|
|
98
|
+
:type pair_folder: str
|
|
99
|
+
:param save_grid: if True grids are saved in root of pair_folder,
|
|
100
|
+
instead of tmp
|
|
101
|
+
:type save_grid: bool
|
|
102
|
+
"""
|
|
103
|
+
|
|
104
|
+
coefsx_2d, coefsy_2d = grid_correction
|
|
105
|
+
|
|
106
|
+
with rio.open(grid["path"]) as right_grid:
|
|
107
|
+
right_grid_row = right_grid.read(1)
|
|
108
|
+
right_grid_col = right_grid.read(2)
|
|
109
|
+
|
|
110
|
+
origin = grid["grid_origin"]
|
|
111
|
+
spacing = grid["grid_spacing"]
|
|
112
|
+
|
|
113
|
+
# Form 3D array with grid positions
|
|
114
|
+
x_values_1d = np.linspace(
|
|
115
|
+
origin[0],
|
|
116
|
+
origin[0] + right_grid_row.shape[0] * spacing[0],
|
|
117
|
+
right_grid_row.shape[0],
|
|
118
|
+
)
|
|
119
|
+
y_values_1d = np.linspace(
|
|
120
|
+
origin[1],
|
|
121
|
+
origin[1] + right_grid_row.shape[1] * spacing[1],
|
|
122
|
+
right_grid_row.shape[1],
|
|
123
|
+
)
|
|
124
|
+
x_values_2d, y_values_2d = np.meshgrid(y_values_1d, x_values_1d)
|
|
125
|
+
|
|
126
|
+
# Compute corresponding point in sensor geometry (grid encodes (x_sensor -
|
|
127
|
+
# x_epi,y_sensor - y__epi)
|
|
128
|
+
|
|
129
|
+
# Interpolate the regression model at grid position
|
|
130
|
+
correction_grid_x = np.polynomial.polynomial.polyval2d(
|
|
131
|
+
x_values_2d, y_values_2d, coefsx_2d
|
|
132
|
+
)
|
|
133
|
+
correction_grid_y = np.polynomial.polynomial.polyval2d(
|
|
134
|
+
x_values_2d, y_values_2d, coefsy_2d
|
|
135
|
+
)
|
|
136
|
+
|
|
137
|
+
# Compute corrected grid
|
|
138
|
+
corrected_grid_x = right_grid_row - correction_grid_x
|
|
139
|
+
corrected_grid_y = right_grid_col - correction_grid_y
|
|
140
|
+
corrected_right_grid = np.stack(
|
|
141
|
+
(corrected_grid_x, corrected_grid_y), axis=2
|
|
142
|
+
)
|
|
143
|
+
|
|
144
|
+
# create new grid dict
|
|
145
|
+
corrected_grid_right = grid.copy()
|
|
146
|
+
|
|
147
|
+
# Dump corrected grid
|
|
148
|
+
grid_origin = grid["grid_origin"]
|
|
149
|
+
grid_spacing = grid["grid_spacing"]
|
|
150
|
+
|
|
151
|
+
# Get save folder (permanent or temporay according to save_grid parameter)
|
|
152
|
+
if save_grid:
|
|
153
|
+
safe_makedirs(pair_folder)
|
|
154
|
+
save_folder = os.path.join(pair_folder, "corrected_right_epi_grid.tif")
|
|
155
|
+
else:
|
|
156
|
+
safe_makedirs(os.path.join(pair_folder, "tmp"))
|
|
157
|
+
save_folder = os.path.join(
|
|
158
|
+
pair_folder, "tmp", "corrected_right_epi_grid.tif"
|
|
159
|
+
)
|
|
160
|
+
|
|
161
|
+
grid_generation_algo.write_grid(
|
|
162
|
+
corrected_right_grid, save_folder, grid_origin, grid_spacing
|
|
163
|
+
)
|
|
164
|
+
|
|
165
|
+
corrected_grid_right["path"] = save_folder
|
|
166
|
+
|
|
167
|
+
return corrected_grid_right
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
# pylint: disable=too-many-positional-arguments
|
|
171
|
+
@cars_profile(name="Grid correction estimation")
|
|
172
|
+
def estimate_right_grid_correction(
|
|
173
|
+
matches,
|
|
174
|
+
grid_right,
|
|
175
|
+
save_matches=False,
|
|
176
|
+
minimum_nb_matches=100,
|
|
177
|
+
pair_folder="",
|
|
178
|
+
pair_key="pair_0",
|
|
179
|
+
orchestrator=None,
|
|
180
|
+
):
|
|
181
|
+
"""
|
|
182
|
+
Estimates grid correction, and correct matches
|
|
183
|
+
|
|
184
|
+
:param matches: matches
|
|
185
|
+
:type matches: np.ndarray
|
|
186
|
+
:param grid_right: grid to correct
|
|
187
|
+
:type grid_right: dict
|
|
188
|
+
:param save_matches: true is matches needs to be saved
|
|
189
|
+
:type save_matches: bool
|
|
190
|
+
:param minimum_nb_matches: minimum number of matches required
|
|
191
|
+
:type minimum_nb_matches: int
|
|
192
|
+
:param pair_folder: folder used for current pair
|
|
193
|
+
:type pair_folder: str
|
|
194
|
+
|
|
195
|
+
:return: grid_correction to apply, corrected_matches, info before,
|
|
196
|
+
info after
|
|
197
|
+
:rtype: Tuple(np.ndarray, np.ndarray) , np.ndarray, dict, dict
|
|
198
|
+
grid_correction is : (coefsx_2d, coefsy_2d) , each of size (2,2)
|
|
199
|
+
|
|
200
|
+
"""
|
|
201
|
+
|
|
202
|
+
# Default orchestrator
|
|
203
|
+
if orchestrator is None:
|
|
204
|
+
# Create default sequential orchestrator for current application
|
|
205
|
+
# be awere, no out_json will be shared between orchestrators
|
|
206
|
+
# No files saved
|
|
207
|
+
cars_orchestrator = ocht.Orchestrator(
|
|
208
|
+
orchestrator_conf={"mode": "sequential"}
|
|
209
|
+
)
|
|
210
|
+
else:
|
|
211
|
+
cars_orchestrator = orchestrator
|
|
212
|
+
|
|
213
|
+
if matches.shape[0] < minimum_nb_matches:
|
|
214
|
+
logging.error(
|
|
215
|
+
"Insufficient amount of matches found"
|
|
216
|
+
", can not safely estimate epipolar error correction"
|
|
217
|
+
)
|
|
218
|
+
|
|
219
|
+
raise ValueError(
|
|
220
|
+
f"Insufficient amount of matches found (< {minimum_nb_matches})"
|
|
221
|
+
", can not safely estimate epipolar error correction"
|
|
222
|
+
)
|
|
223
|
+
|
|
224
|
+
# Get grids attributes
|
|
225
|
+
with rio.open(grid_right["path"]) as right_grid:
|
|
226
|
+
right_grid_row = right_grid.read(1)
|
|
227
|
+
right_grid_col = right_grid.read(2)
|
|
228
|
+
|
|
229
|
+
origin = grid_right["grid_origin"]
|
|
230
|
+
spacing = grid_right["grid_spacing"]
|
|
231
|
+
|
|
232
|
+
# Form 3D array with grid positions
|
|
233
|
+
x_values_1d = np.arange(
|
|
234
|
+
origin[0],
|
|
235
|
+
origin[0] + right_grid_row.shape[0] * spacing[0],
|
|
236
|
+
spacing[0],
|
|
237
|
+
)
|
|
238
|
+
y_values_1d = np.arange(
|
|
239
|
+
origin[1],
|
|
240
|
+
origin[1] + right_grid_row.shape[1] * spacing[1],
|
|
241
|
+
spacing[1],
|
|
242
|
+
)
|
|
243
|
+
x_values_2d, y_values_2d = np.meshgrid(y_values_1d, x_values_1d)
|
|
244
|
+
|
|
245
|
+
# Compute corresponding point in sensor geometry (grid encodes (x_sensor -
|
|
246
|
+
# x_epi,y_sensor - y__epi)
|
|
247
|
+
|
|
248
|
+
# Extract matches for convenience
|
|
249
|
+
matches_y1 = matches[:, 1]
|
|
250
|
+
matches_x2 = matches[:, 2]
|
|
251
|
+
matches_y2 = matches[:, 3]
|
|
252
|
+
|
|
253
|
+
# Map real matches to sensor geometry
|
|
254
|
+
points = np.column_stack((np.ravel(x_values_2d), np.ravel(y_values_2d)))
|
|
255
|
+
|
|
256
|
+
triangulation = Delaunay(points)
|
|
257
|
+
|
|
258
|
+
values = np.ravel(right_grid_row)
|
|
259
|
+
|
|
260
|
+
interpolator = LinearNDInterpolator(triangulation, values)
|
|
261
|
+
sensor_matches_raw_x = interpolator(matches_x2, matches_y2)
|
|
262
|
+
|
|
263
|
+
# Simulate matches that have no epipolar error (i.e. y2 == y1) and map
|
|
264
|
+
# them to sensor geometry
|
|
265
|
+
sensor_matches_perfect_x = interpolator(matches_x2, matches_y1)
|
|
266
|
+
|
|
267
|
+
values = np.ravel(right_grid_col)
|
|
268
|
+
interpolator = LinearNDInterpolator(triangulation, values)
|
|
269
|
+
sensor_matches_raw_y = interpolator(matches_x2, matches_y2)
|
|
270
|
+
|
|
271
|
+
sensor_matches_perfect_y = interpolator(matches_x2, matches_y1)
|
|
272
|
+
|
|
273
|
+
# Compute epipolar error in sensor geometry in both direction
|
|
274
|
+
epipolar_error_x = sensor_matches_perfect_x - sensor_matches_raw_x
|
|
275
|
+
epipolar_error_y = sensor_matches_perfect_y - sensor_matches_raw_y
|
|
276
|
+
|
|
277
|
+
# Output epipolar error stats for monitoring
|
|
278
|
+
mean_epipolar_error = [np.mean(epipolar_error_x), np.mean(epipolar_error_y)]
|
|
279
|
+
median_epipolar_error = [
|
|
280
|
+
np.median(epipolar_error_x),
|
|
281
|
+
np.median(epipolar_error_y),
|
|
282
|
+
]
|
|
283
|
+
std_epipolar_error = [np.std(epipolar_error_x), np.std(epipolar_error_y)]
|
|
284
|
+
rms_epipolar_error = np.mean(
|
|
285
|
+
np.sqrt(
|
|
286
|
+
epipolar_error_x * epipolar_error_x
|
|
287
|
+
+ epipolar_error_y * epipolar_error_y
|
|
288
|
+
)
|
|
289
|
+
)
|
|
290
|
+
rmsd_epipolar_error = np.std(
|
|
291
|
+
np.sqrt(
|
|
292
|
+
epipolar_error_x * epipolar_error_x
|
|
293
|
+
+ epipolar_error_y * epipolar_error_y
|
|
294
|
+
)
|
|
295
|
+
)
|
|
296
|
+
|
|
297
|
+
in_stats = {
|
|
298
|
+
"mean_epipolar_error": mean_epipolar_error,
|
|
299
|
+
"median_epipolar_error": median_epipolar_error,
|
|
300
|
+
"std_epipolar_error": std_epipolar_error,
|
|
301
|
+
"rms_epipolar_error": rms_epipolar_error,
|
|
302
|
+
"rmsd_epipolar_error": rmsd_epipolar_error,
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
logging.debug(
|
|
306
|
+
"Epipolar error before correction: \n"
|
|
307
|
+
"x = {:.3f} +/- {:.3f} pixels \n"
|
|
308
|
+
"y = {:.3f} +/- {:.3f} pixels \n"
|
|
309
|
+
"rmse = {:.3f} +/- {:.3f} pixels \n"
|
|
310
|
+
"medianx = {:.3f} pixels \n"
|
|
311
|
+
"mediany = {:.3f} pixels".format(
|
|
312
|
+
mean_epipolar_error[0],
|
|
313
|
+
std_epipolar_error[0],
|
|
314
|
+
mean_epipolar_error[1],
|
|
315
|
+
std_epipolar_error[1],
|
|
316
|
+
rms_epipolar_error,
|
|
317
|
+
rmsd_epipolar_error,
|
|
318
|
+
median_epipolar_error[0],
|
|
319
|
+
median_epipolar_error[1],
|
|
320
|
+
)
|
|
321
|
+
)
|
|
322
|
+
|
|
323
|
+
# Perform bilinear regression for both component of epipolar error
|
|
324
|
+
nan_mask = np.logical_and(
|
|
325
|
+
~np.isnan(epipolar_error_x), ~np.isnan(epipolar_error_y)
|
|
326
|
+
)
|
|
327
|
+
lstsq_input = np.array(
|
|
328
|
+
[
|
|
329
|
+
matches_x2[nan_mask] * 0 + 1,
|
|
330
|
+
matches_x2[nan_mask],
|
|
331
|
+
matches_y2[nan_mask],
|
|
332
|
+
]
|
|
333
|
+
).T
|
|
334
|
+
coefsx, residx, __, __ = np.linalg.lstsq(
|
|
335
|
+
lstsq_input, epipolar_error_x[nan_mask], rcond=None
|
|
336
|
+
)
|
|
337
|
+
coefsy, residy, __, __ = np.linalg.lstsq(
|
|
338
|
+
lstsq_input, epipolar_error_y[nan_mask], rcond=None
|
|
339
|
+
)
|
|
340
|
+
|
|
341
|
+
# Normalize residuals by number of matches
|
|
342
|
+
rmsex = np.sqrt(residx / matches.shape[0])
|
|
343
|
+
rmsey = np.sqrt(residy / matches.shape[1])
|
|
344
|
+
|
|
345
|
+
logging.debug(
|
|
346
|
+
"Root Mean Square Error of correction estimation:"
|
|
347
|
+
"rmsex={} pixels, rmsey={} pixels".format(rmsex, rmsey)
|
|
348
|
+
)
|
|
349
|
+
|
|
350
|
+
# Reshape coefs to 2D (expected by np.polynomial.polyval2d)
|
|
351
|
+
coefsx_2d = np.ndarray((2, 2))
|
|
352
|
+
coefsx_2d[0, 0] = coefsx[0]
|
|
353
|
+
coefsx_2d[1, 0] = coefsx[1]
|
|
354
|
+
coefsx_2d[0, 1] = coefsx[2]
|
|
355
|
+
coefsx_2d[1, 1] = 0.0
|
|
356
|
+
|
|
357
|
+
coefsy_2d = np.ndarray((2, 2))
|
|
358
|
+
coefsy_2d[0, 0] = coefsy[0]
|
|
359
|
+
coefsy_2d[1, 0] = coefsy[1]
|
|
360
|
+
coefsy_2d[0, 1] = coefsy[2]
|
|
361
|
+
coefsy_2d[1, 1] = 0.0
|
|
362
|
+
|
|
363
|
+
grid_correction = (coefsx_2d, coefsy_2d)
|
|
364
|
+
|
|
365
|
+
# Map corrected matches to sensor geometry
|
|
366
|
+
sensor_matches_corrected_x = (
|
|
367
|
+
sensor_matches_raw_x
|
|
368
|
+
+ np.polynomial.polynomial.polyval2d(matches_x2, matches_y2, coefsx_2d)
|
|
369
|
+
)
|
|
370
|
+
sensor_matches_corrected_y = (
|
|
371
|
+
sensor_matches_raw_y
|
|
372
|
+
+ np.polynomial.polynomial.polyval2d(matches_x2, matches_y2, coefsy_2d)
|
|
373
|
+
)
|
|
374
|
+
|
|
375
|
+
# Map corrected matches to epipolar geometry
|
|
376
|
+
points = np.column_stack(
|
|
377
|
+
(np.ravel(right_grid_row), np.ravel(right_grid_col))
|
|
378
|
+
)
|
|
379
|
+
triangulation = Delaunay(points)
|
|
380
|
+
|
|
381
|
+
values = np.ravel(x_values_2d)
|
|
382
|
+
interpolator = LinearNDInterpolator(triangulation, values)
|
|
383
|
+
epipolar_matches_corrected_x = interpolator(
|
|
384
|
+
sensor_matches_corrected_x, sensor_matches_corrected_y
|
|
385
|
+
)
|
|
386
|
+
|
|
387
|
+
values = np.ravel(y_values_2d)
|
|
388
|
+
interpolator = LinearNDInterpolator(triangulation, values)
|
|
389
|
+
epipolar_matches_corrected_y = interpolator(
|
|
390
|
+
sensor_matches_corrected_x, sensor_matches_corrected_y
|
|
391
|
+
)
|
|
392
|
+
|
|
393
|
+
corrected_matches = np.copy(matches)
|
|
394
|
+
corrected_matches[:, 2] = epipolar_matches_corrected_x
|
|
395
|
+
corrected_matches[:, 3] = epipolar_matches_corrected_y
|
|
396
|
+
|
|
397
|
+
# Compute epipolar error in sensor geometry in both direction after
|
|
398
|
+
# correction
|
|
399
|
+
corrected_epipolar_error_x = (
|
|
400
|
+
sensor_matches_perfect_x - sensor_matches_corrected_x
|
|
401
|
+
)
|
|
402
|
+
corrected_epipolar_error_y = (
|
|
403
|
+
sensor_matches_perfect_y - sensor_matches_corrected_y
|
|
404
|
+
)
|
|
405
|
+
|
|
406
|
+
# Output corrected epipolar error stats for monitoring
|
|
407
|
+
mean_corrected_epipolar_error = [
|
|
408
|
+
np.mean(corrected_epipolar_error_x),
|
|
409
|
+
np.mean(corrected_epipolar_error_y),
|
|
410
|
+
]
|
|
411
|
+
median_corrected_epipolar_error = [
|
|
412
|
+
np.median(corrected_epipolar_error_x),
|
|
413
|
+
np.median(corrected_epipolar_error_y),
|
|
414
|
+
]
|
|
415
|
+
std_corrected_epipolar_error = [
|
|
416
|
+
np.std(corrected_epipolar_error_x),
|
|
417
|
+
np.std(corrected_epipolar_error_y),
|
|
418
|
+
]
|
|
419
|
+
rms_corrected_epipolar_error = np.mean(
|
|
420
|
+
np.sqrt(
|
|
421
|
+
corrected_epipolar_error_x * corrected_epipolar_error_x
|
|
422
|
+
+ corrected_epipolar_error_y * corrected_epipolar_error_y
|
|
423
|
+
)
|
|
424
|
+
)
|
|
425
|
+
rmsd_corrected_epipolar_error = np.std(
|
|
426
|
+
np.sqrt(
|
|
427
|
+
corrected_epipolar_error_x * corrected_epipolar_error_x
|
|
428
|
+
+ corrected_epipolar_error_y * corrected_epipolar_error_y
|
|
429
|
+
)
|
|
430
|
+
)
|
|
431
|
+
|
|
432
|
+
out_stats = {
|
|
433
|
+
"mean_epipolar_error": mean_corrected_epipolar_error,
|
|
434
|
+
"median_epipolar_error": median_corrected_epipolar_error,
|
|
435
|
+
"std_epipolar_error": std_corrected_epipolar_error,
|
|
436
|
+
"rms_epipolar_error": rms_corrected_epipolar_error,
|
|
437
|
+
"rmsd_epipolar_error": rmsd_corrected_epipolar_error,
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
logging.debug(
|
|
441
|
+
"Epipolar error after correction: \n"
|
|
442
|
+
"x = {:.3f} +/- {:.3f} pixels \n"
|
|
443
|
+
"y = {:.3f} +/- {:.3f} pixels \n"
|
|
444
|
+
"rmse = {:.3f} +/- {:.3f} pixels \n"
|
|
445
|
+
"medianx = {:.3f} pixels \n"
|
|
446
|
+
"mediany = {:.3f} pixels".format(
|
|
447
|
+
mean_corrected_epipolar_error[0],
|
|
448
|
+
std_corrected_epipolar_error[0],
|
|
449
|
+
mean_corrected_epipolar_error[1],
|
|
450
|
+
std_corrected_epipolar_error[1],
|
|
451
|
+
rms_corrected_epipolar_error,
|
|
452
|
+
rmsd_corrected_epipolar_error,
|
|
453
|
+
median_corrected_epipolar_error[0],
|
|
454
|
+
median_corrected_epipolar_error[1],
|
|
455
|
+
)
|
|
456
|
+
)
|
|
457
|
+
|
|
458
|
+
corrected_epipolar_error = corrected_matches[:, 1] - corrected_matches[:, 3]
|
|
459
|
+
logging.info(
|
|
460
|
+
"Epipolar error after correction: mean = {:.3f} pix., "
|
|
461
|
+
"standard deviation = {:.3f} pix., max = {:.3f} pix.".format(
|
|
462
|
+
np.mean(corrected_epipolar_error),
|
|
463
|
+
np.std(corrected_epipolar_error),
|
|
464
|
+
np.max(np.fabs(corrected_epipolar_error)),
|
|
465
|
+
)
|
|
466
|
+
)
|
|
467
|
+
|
|
468
|
+
# Export filtered matches
|
|
469
|
+
matches_array_path = None
|
|
470
|
+
current_out_dir = None
|
|
471
|
+
if save_matches:
|
|
472
|
+
logging.info("Writing matches file")
|
|
473
|
+
if pair_folder is None:
|
|
474
|
+
logging.error("Pair folder not provided")
|
|
475
|
+
else:
|
|
476
|
+
safe_makedirs(pair_folder)
|
|
477
|
+
current_out_dir = pair_folder
|
|
478
|
+
matches_array_path = os.path.join(
|
|
479
|
+
current_out_dir, "corrected_filtered_matches.npy"
|
|
480
|
+
)
|
|
481
|
+
np.save(matches_array_path, corrected_matches)
|
|
482
|
+
|
|
483
|
+
# Update orchestrator out_json
|
|
484
|
+
corrected_matches_infos = {
|
|
485
|
+
application_constants.APPLICATION_TAG: {
|
|
486
|
+
grid_constants.GRID_CORRECTION_TAG: {pair_key: {}}
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
cars_orchestrator.update_out_info(corrected_matches_infos)
|
|
490
|
+
|
|
491
|
+
return (
|
|
492
|
+
grid_correction,
|
|
493
|
+
corrected_matches,
|
|
494
|
+
in_stats,
|
|
495
|
+
out_stats,
|
|
496
|
+
)
|