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.

Files changed (202) hide show
  1. cars/__init__.py +74 -0
  2. cars/applications/__init__.py +37 -0
  3. cars/applications/application.py +117 -0
  4. cars/applications/application_constants.py +29 -0
  5. cars/applications/application_template.py +146 -0
  6. cars/applications/auxiliary_filling/__init__.py +29 -0
  7. cars/applications/auxiliary_filling/abstract_auxiliary_filling_app.py +104 -0
  8. cars/applications/auxiliary_filling/auxiliary_filling_algo.py +475 -0
  9. cars/applications/auxiliary_filling/auxiliary_filling_from_sensors_app.py +630 -0
  10. cars/applications/auxiliary_filling/auxiliary_filling_wrappers.py +90 -0
  11. cars/applications/dem_generation/__init__.py +30 -0
  12. cars/applications/dem_generation/abstract_dem_generation_app.py +116 -0
  13. cars/applications/dem_generation/bulldozer_config/base_config.yaml +42 -0
  14. cars/applications/dem_generation/bulldozer_dem_app.py +655 -0
  15. cars/applications/dem_generation/bulldozer_memory.py +55 -0
  16. cars/applications/dem_generation/dem_generation_algo.py +107 -0
  17. cars/applications/dem_generation/dem_generation_constants.py +32 -0
  18. cars/applications/dem_generation/dem_generation_wrappers.py +323 -0
  19. cars/applications/dense_match_filling/__init__.py +30 -0
  20. cars/applications/dense_match_filling/abstract_dense_match_filling_app.py +242 -0
  21. cars/applications/dense_match_filling/fill_disp_algo.py +113 -0
  22. cars/applications/dense_match_filling/fill_disp_constants.py +39 -0
  23. cars/applications/dense_match_filling/fill_disp_wrappers.py +83 -0
  24. cars/applications/dense_match_filling/zero_padding_app.py +302 -0
  25. cars/applications/dense_matching/__init__.py +30 -0
  26. cars/applications/dense_matching/abstract_dense_matching_app.py +261 -0
  27. cars/applications/dense_matching/census_mccnn_sgm_app.py +1460 -0
  28. cars/applications/dense_matching/cpp/__init__.py +0 -0
  29. cars/applications/dense_matching/cpp/dense_matching_cpp.cpython-313-i386-linux-musl.so +0 -0
  30. cars/applications/dense_matching/cpp/dense_matching_cpp.py +94 -0
  31. cars/applications/dense_matching/cpp/includes/dense_matching.hpp +58 -0
  32. cars/applications/dense_matching/cpp/meson.build +9 -0
  33. cars/applications/dense_matching/cpp/src/bindings.cpp +13 -0
  34. cars/applications/dense_matching/cpp/src/dense_matching.cpp +207 -0
  35. cars/applications/dense_matching/dense_matching_algo.py +401 -0
  36. cars/applications/dense_matching/dense_matching_constants.py +89 -0
  37. cars/applications/dense_matching/dense_matching_wrappers.py +951 -0
  38. cars/applications/dense_matching/disparity_grid_algo.py +588 -0
  39. cars/applications/dense_matching/loaders/__init__.py +23 -0
  40. cars/applications/dense_matching/loaders/config_census_sgm_default.json +31 -0
  41. cars/applications/dense_matching/loaders/config_census_sgm_homogeneous.json +30 -0
  42. cars/applications/dense_matching/loaders/config_census_sgm_mountain_and_vegetation.json +30 -0
  43. cars/applications/dense_matching/loaders/config_census_sgm_shadow.json +30 -0
  44. cars/applications/dense_matching/loaders/config_census_sgm_sparse.json +36 -0
  45. cars/applications/dense_matching/loaders/config_census_sgm_urban.json +30 -0
  46. cars/applications/dense_matching/loaders/config_mapping.json +13 -0
  47. cars/applications/dense_matching/loaders/config_mccnn.json +28 -0
  48. cars/applications/dense_matching/loaders/global_land_cover_map.tif +0 -0
  49. cars/applications/dense_matching/loaders/pandora_loader.py +593 -0
  50. cars/applications/dsm_filling/__init__.py +32 -0
  51. cars/applications/dsm_filling/abstract_dsm_filling_app.py +101 -0
  52. cars/applications/dsm_filling/border_interpolation_app.py +270 -0
  53. cars/applications/dsm_filling/bulldozer_config/base_config.yaml +44 -0
  54. cars/applications/dsm_filling/bulldozer_filling_app.py +279 -0
  55. cars/applications/dsm_filling/exogenous_filling_app.py +333 -0
  56. cars/applications/grid_generation/__init__.py +30 -0
  57. cars/applications/grid_generation/abstract_grid_generation_app.py +142 -0
  58. cars/applications/grid_generation/epipolar_grid_generation_app.py +327 -0
  59. cars/applications/grid_generation/grid_correction_app.py +496 -0
  60. cars/applications/grid_generation/grid_generation_algo.py +388 -0
  61. cars/applications/grid_generation/grid_generation_constants.py +46 -0
  62. cars/applications/grid_generation/transform_grid.py +88 -0
  63. cars/applications/ground_truth_reprojection/__init__.py +30 -0
  64. cars/applications/ground_truth_reprojection/abstract_ground_truth_reprojection_app.py +137 -0
  65. cars/applications/ground_truth_reprojection/direct_localization_app.py +629 -0
  66. cars/applications/ground_truth_reprojection/ground_truth_reprojection_algo.py +275 -0
  67. cars/applications/point_cloud_outlier_removal/__init__.py +30 -0
  68. cars/applications/point_cloud_outlier_removal/abstract_outlier_removal_app.py +385 -0
  69. cars/applications/point_cloud_outlier_removal/outlier_removal_algo.py +392 -0
  70. cars/applications/point_cloud_outlier_removal/outlier_removal_constants.py +43 -0
  71. cars/applications/point_cloud_outlier_removal/small_components_app.py +527 -0
  72. cars/applications/point_cloud_outlier_removal/statistical_app.py +531 -0
  73. cars/applications/rasterization/__init__.py +30 -0
  74. cars/applications/rasterization/abstract_pc_rasterization_app.py +183 -0
  75. cars/applications/rasterization/rasterization_algo.py +534 -0
  76. cars/applications/rasterization/rasterization_constants.py +38 -0
  77. cars/applications/rasterization/rasterization_wrappers.py +634 -0
  78. cars/applications/rasterization/simple_gaussian_app.py +1152 -0
  79. cars/applications/resampling/__init__.py +28 -0
  80. cars/applications/resampling/abstract_resampling_app.py +187 -0
  81. cars/applications/resampling/bicubic_resampling_app.py +762 -0
  82. cars/applications/resampling/resampling_algo.py +614 -0
  83. cars/applications/resampling/resampling_constants.py +36 -0
  84. cars/applications/resampling/resampling_wrappers.py +309 -0
  85. cars/applications/sparse_matching/__init__.py +30 -0
  86. cars/applications/sparse_matching/abstract_sparse_matching_app.py +498 -0
  87. cars/applications/sparse_matching/sift_app.py +735 -0
  88. cars/applications/sparse_matching/sparse_matching_algo.py +360 -0
  89. cars/applications/sparse_matching/sparse_matching_constants.py +68 -0
  90. cars/applications/sparse_matching/sparse_matching_wrappers.py +238 -0
  91. cars/applications/triangulation/__init__.py +32 -0
  92. cars/applications/triangulation/abstract_triangulation_app.py +227 -0
  93. cars/applications/triangulation/line_of_sight_intersection_app.py +1243 -0
  94. cars/applications/triangulation/pc_transform.py +552 -0
  95. cars/applications/triangulation/triangulation_algo.py +371 -0
  96. cars/applications/triangulation/triangulation_constants.py +38 -0
  97. cars/applications/triangulation/triangulation_wrappers.py +259 -0
  98. cars/bundleadjustment.py +757 -0
  99. cars/cars.py +177 -0
  100. cars/conf/__init__.py +23 -0
  101. cars/conf/geoid/egm96.grd +0 -0
  102. cars/conf/geoid/egm96.grd.hdr +15 -0
  103. cars/conf/input_parameters.py +156 -0
  104. cars/conf/mask_cst.py +35 -0
  105. cars/core/__init__.py +23 -0
  106. cars/core/cars_logging.py +402 -0
  107. cars/core/constants.py +191 -0
  108. cars/core/constants_disparity.py +50 -0
  109. cars/core/datasets.py +140 -0
  110. cars/core/geometry/__init__.py +27 -0
  111. cars/core/geometry/abstract_geometry.py +1119 -0
  112. cars/core/geometry/shareloc_geometry.py +598 -0
  113. cars/core/inputs.py +568 -0
  114. cars/core/outputs.py +176 -0
  115. cars/core/preprocessing.py +722 -0
  116. cars/core/projection.py +843 -0
  117. cars/core/roi_tools.py +215 -0
  118. cars/core/tiling.py +774 -0
  119. cars/core/utils.py +164 -0
  120. cars/data_structures/__init__.py +23 -0
  121. cars/data_structures/cars_dataset.py +1541 -0
  122. cars/data_structures/cars_dict.py +74 -0
  123. cars/data_structures/corresponding_tiles_tools.py +186 -0
  124. cars/data_structures/dataframe_converter.py +185 -0
  125. cars/data_structures/format_transformation.py +297 -0
  126. cars/devibrate.py +689 -0
  127. cars/extractroi.py +264 -0
  128. cars/orchestrator/__init__.py +23 -0
  129. cars/orchestrator/achievement_tracker.py +125 -0
  130. cars/orchestrator/cluster/__init__.py +37 -0
  131. cars/orchestrator/cluster/abstract_cluster.py +244 -0
  132. cars/orchestrator/cluster/abstract_dask_cluster.py +375 -0
  133. cars/orchestrator/cluster/dask_cluster_tools.py +103 -0
  134. cars/orchestrator/cluster/dask_config/README.md +94 -0
  135. cars/orchestrator/cluster/dask_config/dask.yaml +21 -0
  136. cars/orchestrator/cluster/dask_config/distributed.yaml +70 -0
  137. cars/orchestrator/cluster/dask_config/jobqueue.yaml +26 -0
  138. cars/orchestrator/cluster/dask_config/reference_confs/dask-schema.yaml +137 -0
  139. cars/orchestrator/cluster/dask_config/reference_confs/dask.yaml +26 -0
  140. cars/orchestrator/cluster/dask_config/reference_confs/distributed-schema.yaml +1009 -0
  141. cars/orchestrator/cluster/dask_config/reference_confs/distributed.yaml +273 -0
  142. cars/orchestrator/cluster/dask_config/reference_confs/jobqueue.yaml +212 -0
  143. cars/orchestrator/cluster/dask_jobqueue_utils.py +204 -0
  144. cars/orchestrator/cluster/local_dask_cluster.py +116 -0
  145. cars/orchestrator/cluster/log_wrapper.py +1075 -0
  146. cars/orchestrator/cluster/mp_cluster/__init__.py +27 -0
  147. cars/orchestrator/cluster/mp_cluster/mp_factorizer.py +212 -0
  148. cars/orchestrator/cluster/mp_cluster/mp_objects.py +535 -0
  149. cars/orchestrator/cluster/mp_cluster/mp_tools.py +93 -0
  150. cars/orchestrator/cluster/mp_cluster/mp_wrapper.py +505 -0
  151. cars/orchestrator/cluster/mp_cluster/multiprocessing_cluster.py +873 -0
  152. cars/orchestrator/cluster/mp_cluster/multiprocessing_profiler.py +399 -0
  153. cars/orchestrator/cluster/pbs_dask_cluster.py +207 -0
  154. cars/orchestrator/cluster/sequential_cluster.py +139 -0
  155. cars/orchestrator/cluster/slurm_dask_cluster.py +234 -0
  156. cars/orchestrator/orchestrator.py +905 -0
  157. cars/orchestrator/orchestrator_constants.py +29 -0
  158. cars/orchestrator/registry/__init__.py +23 -0
  159. cars/orchestrator/registry/abstract_registry.py +143 -0
  160. cars/orchestrator/registry/compute_registry.py +106 -0
  161. cars/orchestrator/registry/id_generator.py +116 -0
  162. cars/orchestrator/registry/replacer_registry.py +213 -0
  163. cars/orchestrator/registry/saver_registry.py +363 -0
  164. cars/orchestrator/registry/unseen_registry.py +118 -0
  165. cars/orchestrator/tiles_profiler.py +279 -0
  166. cars/pipelines/__init__.py +26 -0
  167. cars/pipelines/conf_resolution/conf_final_resolution.yaml +5 -0
  168. cars/pipelines/conf_resolution/conf_first_resolution.yaml +2 -0
  169. cars/pipelines/conf_resolution/conf_intermediate_resolution.yaml +2 -0
  170. cars/pipelines/default/__init__.py +26 -0
  171. cars/pipelines/default/default_pipeline.py +786 -0
  172. cars/pipelines/parameters/__init__.py +0 -0
  173. cars/pipelines/parameters/advanced_parameters.py +417 -0
  174. cars/pipelines/parameters/advanced_parameters_constants.py +69 -0
  175. cars/pipelines/parameters/application_parameters.py +71 -0
  176. cars/pipelines/parameters/depth_map_inputs.py +0 -0
  177. cars/pipelines/parameters/dsm_inputs.py +918 -0
  178. cars/pipelines/parameters/dsm_inputs_constants.py +25 -0
  179. cars/pipelines/parameters/output_constants.py +52 -0
  180. cars/pipelines/parameters/output_parameters.py +454 -0
  181. cars/pipelines/parameters/sensor_inputs.py +842 -0
  182. cars/pipelines/parameters/sensor_inputs_constants.py +49 -0
  183. cars/pipelines/parameters/sensor_loaders/__init__.py +29 -0
  184. cars/pipelines/parameters/sensor_loaders/basic_classif_loader.py +86 -0
  185. cars/pipelines/parameters/sensor_loaders/basic_image_loader.py +98 -0
  186. cars/pipelines/parameters/sensor_loaders/pivot_classif_loader.py +90 -0
  187. cars/pipelines/parameters/sensor_loaders/pivot_image_loader.py +105 -0
  188. cars/pipelines/parameters/sensor_loaders/sensor_loader.py +93 -0
  189. cars/pipelines/parameters/sensor_loaders/sensor_loader_template.py +71 -0
  190. cars/pipelines/parameters/sensor_loaders/slurp_classif_loader.py +86 -0
  191. cars/pipelines/pipeline.py +119 -0
  192. cars/pipelines/pipeline_constants.py +31 -0
  193. cars/pipelines/pipeline_template.py +139 -0
  194. cars/pipelines/unit/__init__.py +26 -0
  195. cars/pipelines/unit/unit_pipeline.py +2850 -0
  196. cars/starter.py +167 -0
  197. cars-1.0.0rc1.dist-info/METADATA +292 -0
  198. cars-1.0.0rc1.dist-info/RECORD +202 -0
  199. cars-1.0.0rc1.dist-info/WHEEL +5 -0
  200. cars-1.0.0rc1.dist-info/entry_points.txt +8 -0
  201. cars.libs/libgcc_s-1257a076.so.1 +0 -0
  202. cars.libs/libstdc++-0530927c.so.6.0.32 +0 -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
+ )