cars 1.0.0rc2__cp312-cp312-win_amd64.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of cars might be problematic. Click here for more details.

Files changed (225) hide show
  1. cars/__init__.py +86 -0
  2. cars/applications/__init__.py +40 -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 +105 -0
  8. cars/applications/auxiliary_filling/auxiliary_filling_algo.py +475 -0
  9. cars/applications/auxiliary_filling/auxiliary_filling_from_sensors_app.py +632 -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 +641 -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 +1461 -0
  28. cars/applications/dense_matching/cpp/__init__.py +0 -0
  29. cars/applications/dense_matching/cpp/dense_matching_cpp.cp312-win_amd64.dll.a +0 -0
  30. cars/applications/dense_matching/cpp/dense_matching_cpp.cp312-win_amd64.pyd +0 -0
  31. cars/applications/dense_matching/cpp/dense_matching_cpp.py +94 -0
  32. cars/applications/dense_matching/cpp/includes/dense_matching.hpp +58 -0
  33. cars/applications/dense_matching/cpp/meson.build +9 -0
  34. cars/applications/dense_matching/cpp/src/bindings.cpp +13 -0
  35. cars/applications/dense_matching/cpp/src/dense_matching.cpp +207 -0
  36. cars/applications/dense_matching/dense_matching_algo.py +401 -0
  37. cars/applications/dense_matching/dense_matching_constants.py +89 -0
  38. cars/applications/dense_matching/dense_matching_wrappers.py +951 -0
  39. cars/applications/dense_matching/disparity_grid_algo.py +597 -0
  40. cars/applications/dense_matching/loaders/__init__.py +23 -0
  41. cars/applications/dense_matching/loaders/config_census_sgm_default.json +31 -0
  42. cars/applications/dense_matching/loaders/config_census_sgm_homogeneous.json +30 -0
  43. cars/applications/dense_matching/loaders/config_census_sgm_mountain_and_vegetation.json +30 -0
  44. cars/applications/dense_matching/loaders/config_census_sgm_shadow.json +30 -0
  45. cars/applications/dense_matching/loaders/config_census_sgm_sparse.json +36 -0
  46. cars/applications/dense_matching/loaders/config_census_sgm_urban.json +30 -0
  47. cars/applications/dense_matching/loaders/config_mapping.json +13 -0
  48. cars/applications/dense_matching/loaders/config_mccnn.json +28 -0
  49. cars/applications/dense_matching/loaders/global_land_cover_map.tif +0 -0
  50. cars/applications/dense_matching/loaders/pandora_loader.py +593 -0
  51. cars/applications/dsm_filling/__init__.py +32 -0
  52. cars/applications/dsm_filling/abstract_dsm_filling_app.py +101 -0
  53. cars/applications/dsm_filling/border_interpolation_app.py +278 -0
  54. cars/applications/dsm_filling/bulldozer_config/base_config.yaml +44 -0
  55. cars/applications/dsm_filling/bulldozer_filling_app.py +288 -0
  56. cars/applications/dsm_filling/exogenous_filling_app.py +341 -0
  57. cars/applications/dsm_merging/__init__.py +28 -0
  58. cars/applications/dsm_merging/abstract_dsm_merging_app.py +101 -0
  59. cars/applications/dsm_merging/weighted_fusion_app.py +639 -0
  60. cars/applications/grid_correction/__init__.py +30 -0
  61. cars/applications/grid_correction/abstract_grid_correction_app.py +103 -0
  62. cars/applications/grid_correction/grid_correction_app.py +557 -0
  63. cars/applications/grid_generation/__init__.py +30 -0
  64. cars/applications/grid_generation/abstract_grid_generation_app.py +142 -0
  65. cars/applications/grid_generation/epipolar_grid_generation_app.py +327 -0
  66. cars/applications/grid_generation/grid_generation_algo.py +388 -0
  67. cars/applications/grid_generation/grid_generation_constants.py +46 -0
  68. cars/applications/grid_generation/transform_grid.py +88 -0
  69. cars/applications/ground_truth_reprojection/__init__.py +30 -0
  70. cars/applications/ground_truth_reprojection/abstract_ground_truth_reprojection_app.py +137 -0
  71. cars/applications/ground_truth_reprojection/direct_localization_app.py +629 -0
  72. cars/applications/ground_truth_reprojection/ground_truth_reprojection_algo.py +275 -0
  73. cars/applications/point_cloud_outlier_removal/__init__.py +30 -0
  74. cars/applications/point_cloud_outlier_removal/abstract_outlier_removal_app.py +385 -0
  75. cars/applications/point_cloud_outlier_removal/outlier_removal_algo.py +392 -0
  76. cars/applications/point_cloud_outlier_removal/outlier_removal_constants.py +43 -0
  77. cars/applications/point_cloud_outlier_removal/small_components_app.py +522 -0
  78. cars/applications/point_cloud_outlier_removal/statistical_app.py +528 -0
  79. cars/applications/rasterization/__init__.py +30 -0
  80. cars/applications/rasterization/abstract_pc_rasterization_app.py +183 -0
  81. cars/applications/rasterization/rasterization_algo.py +534 -0
  82. cars/applications/rasterization/rasterization_constants.py +38 -0
  83. cars/applications/rasterization/rasterization_wrappers.py +639 -0
  84. cars/applications/rasterization/simple_gaussian_app.py +1152 -0
  85. cars/applications/resampling/__init__.py +28 -0
  86. cars/applications/resampling/abstract_resampling_app.py +187 -0
  87. cars/applications/resampling/bicubic_resampling_app.py +760 -0
  88. cars/applications/resampling/resampling_algo.py +590 -0
  89. cars/applications/resampling/resampling_constants.py +36 -0
  90. cars/applications/resampling/resampling_wrappers.py +309 -0
  91. cars/applications/sensors_subsampling/__init__.py +32 -0
  92. cars/applications/sensors_subsampling/abstract_subsampling_app.py +109 -0
  93. cars/applications/sensors_subsampling/rasterio_subsampling_app.py +420 -0
  94. cars/applications/sensors_subsampling/subsampling_algo.py +108 -0
  95. cars/applications/sparse_matching/__init__.py +30 -0
  96. cars/applications/sparse_matching/abstract_sparse_matching_app.py +599 -0
  97. cars/applications/sparse_matching/sift_app.py +724 -0
  98. cars/applications/sparse_matching/sparse_matching_algo.py +360 -0
  99. cars/applications/sparse_matching/sparse_matching_constants.py +66 -0
  100. cars/applications/sparse_matching/sparse_matching_wrappers.py +282 -0
  101. cars/applications/triangulation/__init__.py +32 -0
  102. cars/applications/triangulation/abstract_triangulation_app.py +227 -0
  103. cars/applications/triangulation/line_of_sight_intersection_app.py +1243 -0
  104. cars/applications/triangulation/pc_transform.py +552 -0
  105. cars/applications/triangulation/triangulation_algo.py +371 -0
  106. cars/applications/triangulation/triangulation_constants.py +38 -0
  107. cars/applications/triangulation/triangulation_wrappers.py +259 -0
  108. cars/bundleadjustment.py +750 -0
  109. cars/cars.py +179 -0
  110. cars/conf/__init__.py +23 -0
  111. cars/conf/geoid/egm96.grd +0 -0
  112. cars/conf/geoid/egm96.grd.hdr +15 -0
  113. cars/conf/input_parameters.py +156 -0
  114. cars/conf/mask_cst.py +35 -0
  115. cars/core/__init__.py +23 -0
  116. cars/core/cars_logging.py +402 -0
  117. cars/core/constants.py +191 -0
  118. cars/core/constants_disparity.py +50 -0
  119. cars/core/datasets.py +140 -0
  120. cars/core/geometry/__init__.py +27 -0
  121. cars/core/geometry/abstract_geometry.py +1119 -0
  122. cars/core/geometry/shareloc_geometry.py +598 -0
  123. cars/core/inputs.py +568 -0
  124. cars/core/outputs.py +176 -0
  125. cars/core/preprocessing.py +722 -0
  126. cars/core/projection.py +843 -0
  127. cars/core/roi_tools.py +215 -0
  128. cars/core/tiling.py +774 -0
  129. cars/core/utils.py +164 -0
  130. cars/data_structures/__init__.py +23 -0
  131. cars/data_structures/cars_dataset.py +1544 -0
  132. cars/data_structures/cars_dict.py +74 -0
  133. cars/data_structures/corresponding_tiles_tools.py +186 -0
  134. cars/data_structures/dataframe_converter.py +185 -0
  135. cars/data_structures/format_transformation.py +297 -0
  136. cars/devibrate.py +689 -0
  137. cars/extractroi.py +264 -0
  138. cars/orchestrator/__init__.py +23 -0
  139. cars/orchestrator/achievement_tracker.py +125 -0
  140. cars/orchestrator/cluster/__init__.py +37 -0
  141. cars/orchestrator/cluster/abstract_cluster.py +250 -0
  142. cars/orchestrator/cluster/abstract_dask_cluster.py +381 -0
  143. cars/orchestrator/cluster/dask_cluster_tools.py +103 -0
  144. cars/orchestrator/cluster/dask_config/README.md +94 -0
  145. cars/orchestrator/cluster/dask_config/dask.yaml +21 -0
  146. cars/orchestrator/cluster/dask_config/distributed.yaml +70 -0
  147. cars/orchestrator/cluster/dask_config/jobqueue.yaml +26 -0
  148. cars/orchestrator/cluster/dask_config/reference_confs/dask-schema.yaml +137 -0
  149. cars/orchestrator/cluster/dask_config/reference_confs/dask.yaml +26 -0
  150. cars/orchestrator/cluster/dask_config/reference_confs/distributed-schema.yaml +1009 -0
  151. cars/orchestrator/cluster/dask_config/reference_confs/distributed.yaml +273 -0
  152. cars/orchestrator/cluster/dask_config/reference_confs/jobqueue.yaml +212 -0
  153. cars/orchestrator/cluster/dask_jobqueue_utils.py +204 -0
  154. cars/orchestrator/cluster/local_dask_cluster.py +116 -0
  155. cars/orchestrator/cluster/log_wrapper.py +728 -0
  156. cars/orchestrator/cluster/mp_cluster/__init__.py +27 -0
  157. cars/orchestrator/cluster/mp_cluster/mp_factorizer.py +212 -0
  158. cars/orchestrator/cluster/mp_cluster/mp_objects.py +535 -0
  159. cars/orchestrator/cluster/mp_cluster/mp_tools.py +93 -0
  160. cars/orchestrator/cluster/mp_cluster/mp_wrapper.py +505 -0
  161. cars/orchestrator/cluster/mp_cluster/multiprocessing_cluster.py +986 -0
  162. cars/orchestrator/cluster/mp_cluster/multiprocessing_profiler.py +399 -0
  163. cars/orchestrator/cluster/pbs_dask_cluster.py +207 -0
  164. cars/orchestrator/cluster/sequential_cluster.py +139 -0
  165. cars/orchestrator/cluster/slurm_dask_cluster.py +234 -0
  166. cars/orchestrator/memory_tools.py +47 -0
  167. cars/orchestrator/orchestrator.py +755 -0
  168. cars/orchestrator/orchestrator_constants.py +29 -0
  169. cars/orchestrator/registry/__init__.py +23 -0
  170. cars/orchestrator/registry/abstract_registry.py +143 -0
  171. cars/orchestrator/registry/compute_registry.py +106 -0
  172. cars/orchestrator/registry/id_generator.py +116 -0
  173. cars/orchestrator/registry/replacer_registry.py +213 -0
  174. cars/orchestrator/registry/saver_registry.py +363 -0
  175. cars/orchestrator/registry/unseen_registry.py +118 -0
  176. cars/orchestrator/tiles_profiler.py +279 -0
  177. cars/pipelines/__init__.py +26 -0
  178. cars/pipelines/conf_resolution/conf_final_resolution.yaml +5 -0
  179. cars/pipelines/conf_resolution/conf_first_resolution.yaml +4 -0
  180. cars/pipelines/conf_resolution/conf_intermediate_resolution.yaml +2 -0
  181. cars/pipelines/default/__init__.py +26 -0
  182. cars/pipelines/default/default_pipeline.py +1088 -0
  183. cars/pipelines/filling/__init__.py +26 -0
  184. cars/pipelines/filling/filling.py +981 -0
  185. cars/pipelines/formatting/__init__.py +26 -0
  186. cars/pipelines/formatting/formatting.py +186 -0
  187. cars/pipelines/merging/__init__.py +26 -0
  188. cars/pipelines/merging/merging.py +439 -0
  189. cars/pipelines/parameters/__init__.py +0 -0
  190. cars/pipelines/parameters/advanced_parameters.py +256 -0
  191. cars/pipelines/parameters/advanced_parameters_constants.py +68 -0
  192. cars/pipelines/parameters/application_parameters.py +72 -0
  193. cars/pipelines/parameters/depth_map_inputs.py +0 -0
  194. cars/pipelines/parameters/dsm_inputs.py +349 -0
  195. cars/pipelines/parameters/dsm_inputs_constants.py +25 -0
  196. cars/pipelines/parameters/output_constants.py +52 -0
  197. cars/pipelines/parameters/output_parameters.py +438 -0
  198. cars/pipelines/parameters/sensor_inputs.py +859 -0
  199. cars/pipelines/parameters/sensor_inputs_constants.py +51 -0
  200. cars/pipelines/parameters/sensor_loaders/__init__.py +29 -0
  201. cars/pipelines/parameters/sensor_loaders/basic_classif_loader.py +86 -0
  202. cars/pipelines/parameters/sensor_loaders/basic_image_loader.py +98 -0
  203. cars/pipelines/parameters/sensor_loaders/pivot_classif_loader.py +90 -0
  204. cars/pipelines/parameters/sensor_loaders/pivot_image_loader.py +105 -0
  205. cars/pipelines/parameters/sensor_loaders/sensor_loader.py +93 -0
  206. cars/pipelines/parameters/sensor_loaders/sensor_loader_template.py +71 -0
  207. cars/pipelines/parameters/sensor_loaders/slurp_classif_loader.py +86 -0
  208. cars/pipelines/pipeline.py +119 -0
  209. cars/pipelines/pipeline_constants.py +38 -0
  210. cars/pipelines/pipeline_template.py +135 -0
  211. cars/pipelines/subsampling/__init__.py +26 -0
  212. cars/pipelines/subsampling/subsampling.py +358 -0
  213. cars/pipelines/surface_modeling/__init__.py +26 -0
  214. cars/pipelines/surface_modeling/surface_modeling.py +2098 -0
  215. cars/pipelines/tie_points/__init__.py +26 -0
  216. cars/pipelines/tie_points/tie_points.py +536 -0
  217. cars/starter.py +167 -0
  218. cars-1.0.0rc2.dist-info/DELVEWHEEL +2 -0
  219. cars-1.0.0rc2.dist-info/METADATA +289 -0
  220. cars-1.0.0rc2.dist-info/RECORD +225 -0
  221. cars-1.0.0rc2.dist-info/WHEEL +4 -0
  222. cars-1.0.0rc2.dist-info/entry_points.txt +8 -0
  223. cars.libs/libgcc_s_seh-1-b2494fcbd4d80cf2c98fdd5261f6d850.dll +0 -0
  224. cars.libs/libstdc++-6-e9b0d12ae0e9555bbae55e8dfd08c3f7.dll +0 -0
  225. cars.libs/libwinpthread-1-7882d1b093714ccdfaf4e0789a817792.dll +0 -0
@@ -0,0 +1,103 @@
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
+ this module contains the abstract grid generation application class.
23
+ """
24
+ import logging
25
+ from abc import ABCMeta, abstractmethod
26
+ from typing import Dict
27
+
28
+ from cars.applications.application import Application
29
+ from cars.applications.application_template import ApplicationTemplate
30
+
31
+
32
+ @Application.register("grid_correction")
33
+ class GridCorrection(ApplicationTemplate, metaclass=ABCMeta):
34
+ """
35
+ AbstractGridCorrection
36
+ """
37
+
38
+ available_applications: Dict = {}
39
+ default_application = "default"
40
+
41
+ def __new__(cls, conf=None): # pylint: disable=W0613
42
+ """
43
+ Return the required application
44
+ :raises:
45
+ - KeyError when the required application is not registered
46
+
47
+ :param conf: configuration for grid generation
48
+ :return: a application_to_use object
49
+ """
50
+
51
+ grid_method = cls.default_application
52
+
53
+ if bool(conf) is False or "method" not in conf:
54
+ logging.info(
55
+ "Grid correction method not specified, default "
56
+ " {} is used".format(grid_method)
57
+ )
58
+ else:
59
+ grid_method = conf.get("method", cls.default_application)
60
+
61
+ if grid_method not in cls.available_applications:
62
+ logging.error(
63
+ "No GridCorrection application named {} registered".format(
64
+ grid_method
65
+ )
66
+ )
67
+ raise KeyError(
68
+ "No GridCorrection application named {} registered".format(
69
+ grid_method
70
+ )
71
+ )
72
+
73
+ logging.info(
74
+ "The GridCorrection({}) application will be used".format(
75
+ grid_method
76
+ )
77
+ )
78
+
79
+ return super(GridCorrection, cls).__new__(
80
+ cls.available_applications[grid_method]
81
+ )
82
+
83
+ def __init_subclass__(cls, short_name, **kwargs): # pylint: disable=E0302
84
+ super().__init_subclass__(**kwargs)
85
+ cls.available_applications[short_name] = cls
86
+
87
+ def __init__(self, conf=None):
88
+ """
89
+ Init function of GridGeneration
90
+
91
+ :param conf: configuration
92
+ :return: an application_to_use object
93
+ """
94
+
95
+ super().__init__(conf=conf)
96
+
97
+ @abstractmethod
98
+ def run( # pylint: disable=too-many-positional-arguments
99
+ self,
100
+ ):
101
+ """
102
+ Run GridCorrection application
103
+ """
@@ -0,0 +1,557 @@
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
+ this module contains the epipolar grid correction application class.
23
+ """
24
+ # Standard imports
25
+ from __future__ import absolute_import
26
+
27
+ # Standard imports
28
+ import logging
29
+ import os
30
+
31
+ import numpy as np
32
+
33
+ # Third party imports
34
+ import rasterio as rio
35
+
36
+ # Third party imports
37
+ from json_checker import And, Checker
38
+ from scipy.interpolate import LinearNDInterpolator
39
+ from scipy.spatial import Delaunay # pylint: disable=E0611
40
+
41
+ import cars.orchestrator.orchestrator as ocht
42
+ from cars.applications import application_constants
43
+ from cars.applications.grid_correction.abstract_grid_correction_app import (
44
+ GridCorrection,
45
+ )
46
+ from cars.applications.grid_generation import (
47
+ grid_generation_algo,
48
+ )
49
+ from cars.applications.grid_generation import (
50
+ grid_generation_constants as grid_constants,
51
+ )
52
+
53
+ # CARS imports
54
+ from cars.core.utils import safe_makedirs
55
+ from cars.orchestrator.cluster.log_wrapper import cars_profile
56
+
57
+
58
+ class GridCorrectionApp(GridCorrection, short_name="default"):
59
+ """
60
+ EpipolarGridGeneration
61
+ """
62
+
63
+ def __init__(self, conf=None):
64
+ """
65
+ Init function of EpipolarGridGeneration
66
+
67
+ :param conf: configuration for grid generation
68
+ :return: a application_to_use object
69
+ """
70
+
71
+ super().__init__(conf=conf)
72
+
73
+ # check conf
74
+ self.used_method = self.used_config["method"]
75
+ self.nb_matches = self.used_config["nb_matches"]
76
+ self.save_intermediate_data = self.used_config["save_intermediate_data"]
77
+
78
+ def check_conf(self, conf):
79
+ """
80
+ Check configuration
81
+
82
+ :param conf: configuration to check
83
+ :type conf: dict
84
+
85
+ :return: overloaded configuration
86
+ :rtype: dict
87
+
88
+ """
89
+
90
+ # Init conf
91
+ if conf is not None:
92
+ overloaded_conf = conf.copy()
93
+ else:
94
+ conf = {}
95
+ overloaded_conf = {}
96
+
97
+ # Overload conf
98
+ overloaded_conf["method"] = conf.get("method", "epipolar")
99
+ overloaded_conf["nb_matches"] = conf.get("nb_matches", 90)
100
+ overloaded_conf["save_intermediate_data"] = conf.get(
101
+ "save_intermediate_data", False
102
+ )
103
+
104
+ grid_generation_schema = {
105
+ "method": str,
106
+ "nb_matches": And(int, lambda x: x > 0),
107
+ "save_intermediate_data": bool,
108
+ }
109
+
110
+ # Check conf
111
+ checker = Checker(grid_generation_schema)
112
+ checker.validate(overloaded_conf)
113
+
114
+ return overloaded_conf
115
+
116
+ def get_minimum_nb_matches(self):
117
+ """
118
+ Get the minimum number of matches required for grid correction
119
+ """
120
+
121
+ return self.nb_matches
122
+
123
+ @cars_profile(name="Correct grid")
124
+ def correct_grid(self, grid, grid_correction, pair_folder, save_grid=None):
125
+ """
126
+ Correct grid
127
+
128
+ :param grid: grid to correct
129
+ :type grid: dict
130
+ :param grid_correction: grid correction to apply
131
+ :type grid_correction: Tuple(np.ndarray, np.ndarray)
132
+ (coefsx_2d, coefsy_2d) , each of size (2,2)
133
+ :param pair_folder: directory where grids are saved: either in
134
+ pair_folder/tmp, or at the root of pair_folder if save_grid is True
135
+ :type pair_folder: str
136
+ :param save_grid: if True grids are saved in root of pair_folder,
137
+ instead of tmp
138
+ :type save_grid: bool
139
+ """
140
+
141
+ coefsx_2d, coefsy_2d = grid_correction
142
+
143
+ with rio.open(grid["path"]) as right_grid:
144
+ right_grid_row = right_grid.read(1)
145
+ right_grid_col = right_grid.read(2)
146
+
147
+ origin = grid["grid_origin"]
148
+ spacing = grid["grid_spacing"]
149
+
150
+ # Form 3D array with grid positions
151
+ x_values_1d = np.linspace(
152
+ origin[0],
153
+ origin[0] + right_grid_row.shape[0] * spacing[0],
154
+ right_grid_row.shape[0],
155
+ )
156
+ y_values_1d = np.linspace(
157
+ origin[1],
158
+ origin[1] + right_grid_row.shape[1] * spacing[1],
159
+ right_grid_row.shape[1],
160
+ )
161
+ x_values_2d, y_values_2d = np.meshgrid(y_values_1d, x_values_1d)
162
+
163
+ # Compute corresponding point in sensor geometry
164
+ # (grid encodes (x_sensor -
165
+ # x_epi,y_sensor - y__epi)
166
+
167
+ # Interpolate the regression model at grid position
168
+ correction_grid_x = np.polynomial.polynomial.polyval2d(
169
+ x_values_2d, y_values_2d, coefsx_2d
170
+ )
171
+ correction_grid_y = np.polynomial.polynomial.polyval2d(
172
+ x_values_2d, y_values_2d, coefsy_2d
173
+ )
174
+
175
+ # Compute corrected grid
176
+ corrected_grid_x = right_grid_row - correction_grid_x
177
+ corrected_grid_y = right_grid_col - correction_grid_y
178
+ corrected_right_grid = np.stack(
179
+ (corrected_grid_x, corrected_grid_y), axis=2
180
+ )
181
+
182
+ # create new grid dict
183
+ corrected_grid_right = grid.copy()
184
+
185
+ # Dump corrected grid
186
+ grid_origin = grid["grid_origin"]
187
+ grid_spacing = grid["grid_spacing"]
188
+
189
+ # Get save folder (permanent or temporay according to
190
+ # save_grid parameter)
191
+ if save_grid:
192
+ safe_makedirs(pair_folder)
193
+ save_folder = os.path.join(
194
+ pair_folder, "corrected_right_epi_grid.tif"
195
+ )
196
+ else:
197
+ safe_makedirs(os.path.join(pair_folder, "tmp"))
198
+ save_folder = os.path.join(
199
+ pair_folder, "tmp", "corrected_right_epi_grid.tif"
200
+ )
201
+
202
+ grid_generation_algo.write_grid(
203
+ corrected_right_grid, save_folder, grid_origin, grid_spacing
204
+ )
205
+
206
+ corrected_grid_right["path"] = save_folder
207
+
208
+ return corrected_grid_right
209
+
210
+ @cars_profile(name="Epi Grid Generation")
211
+ def run( # pylint: disable=too-many-positional-arguments
212
+ self,
213
+ matches,
214
+ grid_right,
215
+ save_matches=False,
216
+ minimum_nb_matches=100,
217
+ pair_folder="",
218
+ pair_key="pair_0",
219
+ orchestrator=None,
220
+ save_corrected_grid=None,
221
+ ):
222
+ """
223
+ Estimates grid correction, and correct matches
224
+
225
+ :param matches: matches
226
+ :type matches: np.ndarray
227
+ :param grid_right: grid to correct
228
+ :type grid_right: dict
229
+ :param save_matches: true is matches needs to be saved
230
+ :type save_matches: bool
231
+ :param minimum_nb_matches: minimum number of matches required
232
+ :type minimum_nb_matches: int
233
+ :param pair_folder: folder used for current pair
234
+ :type pair_folder: str
235
+
236
+ :return: grid_correction to apply, corrected_matches, info before,
237
+ info after
238
+ :rtype: Tuple(np.ndarray, np.ndarray) , np.ndarray, dict, dict
239
+ grid_correction is : (coefsx_2d, coefsy_2d) , each of size (2,2)
240
+
241
+ """
242
+
243
+ # Default orchestrator
244
+ if orchestrator is None:
245
+ # Create default sequential orchestrator for current application
246
+ # be awere, no out_json will be shared between orchestrators
247
+ # No files saved
248
+ cars_orchestrator = ocht.Orchestrator(
249
+ orchestrator_conf={"mode": "sequential"}
250
+ )
251
+ else:
252
+ cars_orchestrator = orchestrator
253
+
254
+ if matches.shape[0] < minimum_nb_matches:
255
+ logging.error(
256
+ "Insufficient amount of matches found"
257
+ ", can not safely estimate epipolar error correction"
258
+ )
259
+
260
+ raise ValueError(
261
+ f"Insufficient amount of matches found (< {minimum_nb_matches})"
262
+ ", can not safely estimate epipolar error correction"
263
+ )
264
+
265
+ # Get grids attributes
266
+ with rio.open(grid_right["path"]) as right_grid:
267
+ right_grid_row = right_grid.read(1)
268
+ right_grid_col = right_grid.read(2)
269
+
270
+ origin = grid_right["grid_origin"]
271
+ spacing = grid_right["grid_spacing"]
272
+
273
+ # Form 3D array with grid positions
274
+ x_values_1d = np.arange(
275
+ origin[0],
276
+ origin[0] + right_grid_row.shape[0] * spacing[0],
277
+ spacing[0],
278
+ )
279
+ y_values_1d = np.arange(
280
+ origin[1],
281
+ origin[1] + right_grid_row.shape[1] * spacing[1],
282
+ spacing[1],
283
+ )
284
+ x_values_2d, y_values_2d = np.meshgrid(y_values_1d, x_values_1d)
285
+
286
+ # Compute corresponding point in sensor geometry
287
+ # (grid encodes (x_sensor -
288
+ # x_epi,y_sensor - y__epi)
289
+
290
+ # Extract matches for convenience
291
+ matches_y1 = matches[:, 1]
292
+ matches_x2 = matches[:, 2]
293
+ matches_y2 = matches[:, 3]
294
+
295
+ # Map real matches to sensor geometry
296
+ points = np.column_stack((np.ravel(x_values_2d), np.ravel(y_values_2d)))
297
+
298
+ triangulation = Delaunay(points)
299
+
300
+ values = np.ravel(right_grid_row)
301
+
302
+ interpolator = LinearNDInterpolator(triangulation, values)
303
+ sensor_matches_raw_x = interpolator(matches_x2, matches_y2)
304
+
305
+ # Simulate matches that have no epipolar error (i.e. y2 == y1) and map
306
+ # them to sensor geometry
307
+ sensor_matches_perfect_x = interpolator(matches_x2, matches_y1)
308
+
309
+ values = np.ravel(right_grid_col)
310
+ interpolator = LinearNDInterpolator(triangulation, values)
311
+ sensor_matches_raw_y = interpolator(matches_x2, matches_y2)
312
+
313
+ sensor_matches_perfect_y = interpolator(matches_x2, matches_y1)
314
+
315
+ # Compute epipolar error in sensor geometry in both direction
316
+ epipolar_error_x = sensor_matches_perfect_x - sensor_matches_raw_x
317
+ epipolar_error_y = sensor_matches_perfect_y - sensor_matches_raw_y
318
+
319
+ # Output epipolar error stats for monitoring
320
+ mean_epipolar_error = [
321
+ np.mean(epipolar_error_x),
322
+ np.mean(epipolar_error_y),
323
+ ]
324
+ median_epipolar_error = [
325
+ np.median(epipolar_error_x),
326
+ np.median(epipolar_error_y),
327
+ ]
328
+ std_epipolar_error = [
329
+ np.std(epipolar_error_x),
330
+ np.std(epipolar_error_y),
331
+ ]
332
+ rms_epipolar_error = np.mean(
333
+ np.sqrt(
334
+ epipolar_error_x * epipolar_error_x
335
+ + epipolar_error_y * epipolar_error_y
336
+ )
337
+ )
338
+ rmsd_epipolar_error = np.std(
339
+ np.sqrt(
340
+ epipolar_error_x * epipolar_error_x
341
+ + epipolar_error_y * epipolar_error_y
342
+ )
343
+ )
344
+
345
+ in_stats = {
346
+ "mean_epipolar_error": mean_epipolar_error,
347
+ "median_epipolar_error": median_epipolar_error,
348
+ "std_epipolar_error": std_epipolar_error,
349
+ "rms_epipolar_error": rms_epipolar_error,
350
+ "rmsd_epipolar_error": rmsd_epipolar_error,
351
+ }
352
+
353
+ logging.debug(
354
+ "Epipolar error before correction: \n"
355
+ "x = {:.3f} +/- {:.3f} pixels \n"
356
+ "y = {:.3f} +/- {:.3f} pixels \n"
357
+ "rmse = {:.3f} +/- {:.3f} pixels \n"
358
+ "medianx = {:.3f} pixels \n"
359
+ "mediany = {:.3f} pixels".format(
360
+ mean_epipolar_error[0],
361
+ std_epipolar_error[0],
362
+ mean_epipolar_error[1],
363
+ std_epipolar_error[1],
364
+ rms_epipolar_error,
365
+ rmsd_epipolar_error,
366
+ median_epipolar_error[0],
367
+ median_epipolar_error[1],
368
+ )
369
+ )
370
+
371
+ # Perform bilinear regression for both component of epipolar error
372
+ nan_mask = np.logical_and(
373
+ ~np.isnan(epipolar_error_x), ~np.isnan(epipolar_error_y)
374
+ )
375
+ lstsq_input = np.array(
376
+ [
377
+ matches_x2[nan_mask] * 0 + 1,
378
+ matches_x2[nan_mask],
379
+ matches_y2[nan_mask],
380
+ ]
381
+ ).T
382
+ coefsx, residx, __, __ = np.linalg.lstsq(
383
+ lstsq_input, epipolar_error_x[nan_mask], rcond=None
384
+ )
385
+ coefsy, residy, __, __ = np.linalg.lstsq(
386
+ lstsq_input, epipolar_error_y[nan_mask], rcond=None
387
+ )
388
+
389
+ # Normalize residuals by number of matches
390
+ rmsex = np.sqrt(residx / matches.shape[0])
391
+ rmsey = np.sqrt(residy / matches.shape[1])
392
+
393
+ logging.debug(
394
+ "Root Mean Square Error of correction estimation:"
395
+ "rmsex={} pixels, rmsey={} pixels".format(rmsex, rmsey)
396
+ )
397
+
398
+ # Reshape coefs to 2D (expected by np.polynomial.polyval2d)
399
+ coefsx_2d = np.ndarray((2, 2))
400
+ coefsx_2d[0, 0] = coefsx[0]
401
+ coefsx_2d[1, 0] = coefsx[1]
402
+ coefsx_2d[0, 1] = coefsx[2]
403
+ coefsx_2d[1, 1] = 0.0
404
+
405
+ coefsy_2d = np.ndarray((2, 2))
406
+ coefsy_2d[0, 0] = coefsy[0]
407
+ coefsy_2d[1, 0] = coefsy[1]
408
+ coefsy_2d[0, 1] = coefsy[2]
409
+ coefsy_2d[1, 1] = 0.0
410
+
411
+ grid_correction = (coefsx_2d, coefsy_2d)
412
+
413
+ # Map corrected matches to sensor geometry
414
+ sensor_matches_corrected_x = (
415
+ sensor_matches_raw_x
416
+ + np.polynomial.polynomial.polyval2d(
417
+ matches_x2, matches_y2, coefsx_2d
418
+ )
419
+ )
420
+ sensor_matches_corrected_y = (
421
+ sensor_matches_raw_y
422
+ + np.polynomial.polynomial.polyval2d(
423
+ matches_x2, matches_y2, coefsy_2d
424
+ )
425
+ )
426
+
427
+ # Map corrected matches to epipolar geometry
428
+ points = np.column_stack(
429
+ (np.ravel(right_grid_row), np.ravel(right_grid_col))
430
+ )
431
+ triangulation = Delaunay(points)
432
+
433
+ values = np.ravel(x_values_2d)
434
+ interpolator = LinearNDInterpolator(triangulation, values)
435
+ epipolar_matches_corrected_x = interpolator(
436
+ sensor_matches_corrected_x, sensor_matches_corrected_y
437
+ )
438
+
439
+ values = np.ravel(y_values_2d)
440
+ interpolator = LinearNDInterpolator(triangulation, values)
441
+ epipolar_matches_corrected_y = interpolator(
442
+ sensor_matches_corrected_x, sensor_matches_corrected_y
443
+ )
444
+
445
+ corrected_matches = np.copy(matches)
446
+ corrected_matches[:, 2] = epipolar_matches_corrected_x
447
+ corrected_matches[:, 3] = epipolar_matches_corrected_y
448
+
449
+ # Compute epipolar error in sensor geometry in both direction after
450
+ # correction
451
+ corrected_epipolar_error_x = (
452
+ sensor_matches_perfect_x - sensor_matches_corrected_x
453
+ )
454
+ corrected_epipolar_error_y = (
455
+ sensor_matches_perfect_y - sensor_matches_corrected_y
456
+ )
457
+
458
+ # Output corrected epipolar error stats for monitoring
459
+ mean_corrected_epipolar_error = [
460
+ np.mean(corrected_epipolar_error_x),
461
+ np.mean(corrected_epipolar_error_y),
462
+ ]
463
+ median_corrected_epipolar_error = [
464
+ np.median(corrected_epipolar_error_x),
465
+ np.median(corrected_epipolar_error_y),
466
+ ]
467
+ std_corrected_epipolar_error = [
468
+ np.std(corrected_epipolar_error_x),
469
+ np.std(corrected_epipolar_error_y),
470
+ ]
471
+ rms_corrected_epipolar_error = np.mean(
472
+ np.sqrt(
473
+ corrected_epipolar_error_x * corrected_epipolar_error_x
474
+ + corrected_epipolar_error_y * corrected_epipolar_error_y
475
+ )
476
+ )
477
+ rmsd_corrected_epipolar_error = np.std(
478
+ np.sqrt(
479
+ corrected_epipolar_error_x * corrected_epipolar_error_x
480
+ + corrected_epipolar_error_y * corrected_epipolar_error_y
481
+ )
482
+ )
483
+
484
+ out_stats = {
485
+ "mean_epipolar_error": mean_corrected_epipolar_error,
486
+ "median_epipolar_error": median_corrected_epipolar_error,
487
+ "std_epipolar_error": std_corrected_epipolar_error,
488
+ "rms_epipolar_error": rms_corrected_epipolar_error,
489
+ "rmsd_epipolar_error": rmsd_corrected_epipolar_error,
490
+ }
491
+
492
+ logging.debug(
493
+ "Epipolar error after correction: \n"
494
+ "x = {:.3f} +/- {:.3f} pixels \n"
495
+ "y = {:.3f} +/- {:.3f} pixels \n"
496
+ "rmse = {:.3f} +/- {:.3f} pixels \n"
497
+ "medianx = {:.3f} pixels \n"
498
+ "mediany = {:.3f} pixels".format(
499
+ mean_corrected_epipolar_error[0],
500
+ std_corrected_epipolar_error[0],
501
+ mean_corrected_epipolar_error[1],
502
+ std_corrected_epipolar_error[1],
503
+ rms_corrected_epipolar_error,
504
+ rmsd_corrected_epipolar_error,
505
+ median_corrected_epipolar_error[0],
506
+ median_corrected_epipolar_error[1],
507
+ )
508
+ )
509
+
510
+ corrected_epipolar_error = (
511
+ corrected_matches[:, 1] - corrected_matches[:, 3]
512
+ )
513
+ logging.info(
514
+ "Epipolar error after correction: mean = {:.3f} pix., "
515
+ "standard deviation = {:.3f} pix., max = {:.3f} pix.".format(
516
+ np.mean(corrected_epipolar_error),
517
+ np.std(corrected_epipolar_error),
518
+ np.max(np.fabs(corrected_epipolar_error)),
519
+ )
520
+ )
521
+
522
+ # Export filtered matches
523
+ matches_array_path = None
524
+ current_out_dir = None
525
+ if save_matches:
526
+ logging.info("Writing matches file")
527
+ if pair_folder is None:
528
+ logging.error("Pair folder not provided")
529
+ else:
530
+ safe_makedirs(pair_folder)
531
+ current_out_dir = pair_folder
532
+ matches_array_path = os.path.join(
533
+ current_out_dir, "corrected_filtered_matches.npy"
534
+ )
535
+ np.save(matches_array_path, corrected_matches)
536
+
537
+ # Update orchestrator out_json
538
+ corrected_matches_infos = {
539
+ application_constants.APPLICATION_TAG: {
540
+ grid_constants.GRID_CORRECTION_TAG: {pair_key: {}}
541
+ }
542
+ }
543
+ cars_orchestrator.update_out_info(corrected_matches_infos)
544
+
545
+ corrected_grid_right = self.correct_grid(
546
+ grid_right,
547
+ grid_correction,
548
+ pair_folder,
549
+ save_corrected_grid,
550
+ )
551
+
552
+ return (
553
+ corrected_grid_right,
554
+ corrected_matches,
555
+ in_stats,
556
+ out_stats,
557
+ )
@@ -0,0 +1,30 @@
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
+ CARS grid_generation module init file
23
+ """
24
+ # flake8: noqa: F401
25
+
26
+ from cars.applications.grid_generation.abstract_grid_generation_app import (
27
+ GridGeneration,
28
+ )
29
+
30
+ from . import epipolar_grid_generation_app