cars 1.0.0rc2__cp312-cp312-win_amd64.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of cars might be problematic. Click here for more details.
- cars/__init__.py +86 -0
- cars/applications/__init__.py +40 -0
- cars/applications/application.py +117 -0
- cars/applications/application_constants.py +29 -0
- cars/applications/application_template.py +146 -0
- cars/applications/auxiliary_filling/__init__.py +29 -0
- cars/applications/auxiliary_filling/abstract_auxiliary_filling_app.py +105 -0
- cars/applications/auxiliary_filling/auxiliary_filling_algo.py +475 -0
- cars/applications/auxiliary_filling/auxiliary_filling_from_sensors_app.py +632 -0
- cars/applications/auxiliary_filling/auxiliary_filling_wrappers.py +90 -0
- cars/applications/dem_generation/__init__.py +30 -0
- cars/applications/dem_generation/abstract_dem_generation_app.py +116 -0
- cars/applications/dem_generation/bulldozer_config/base_config.yaml +42 -0
- cars/applications/dem_generation/bulldozer_dem_app.py +641 -0
- cars/applications/dem_generation/bulldozer_memory.py +55 -0
- cars/applications/dem_generation/dem_generation_algo.py +107 -0
- cars/applications/dem_generation/dem_generation_constants.py +32 -0
- cars/applications/dem_generation/dem_generation_wrappers.py +323 -0
- cars/applications/dense_match_filling/__init__.py +30 -0
- cars/applications/dense_match_filling/abstract_dense_match_filling_app.py +242 -0
- cars/applications/dense_match_filling/fill_disp_algo.py +113 -0
- cars/applications/dense_match_filling/fill_disp_constants.py +39 -0
- cars/applications/dense_match_filling/fill_disp_wrappers.py +83 -0
- cars/applications/dense_match_filling/zero_padding_app.py +302 -0
- cars/applications/dense_matching/__init__.py +30 -0
- cars/applications/dense_matching/abstract_dense_matching_app.py +261 -0
- cars/applications/dense_matching/census_mccnn_sgm_app.py +1461 -0
- cars/applications/dense_matching/cpp/__init__.py +0 -0
- cars/applications/dense_matching/cpp/dense_matching_cpp.cp312-win_amd64.dll.a +0 -0
- cars/applications/dense_matching/cpp/dense_matching_cpp.cp312-win_amd64.pyd +0 -0
- cars/applications/dense_matching/cpp/dense_matching_cpp.py +94 -0
- cars/applications/dense_matching/cpp/includes/dense_matching.hpp +58 -0
- cars/applications/dense_matching/cpp/meson.build +9 -0
- cars/applications/dense_matching/cpp/src/bindings.cpp +13 -0
- cars/applications/dense_matching/cpp/src/dense_matching.cpp +207 -0
- cars/applications/dense_matching/dense_matching_algo.py +401 -0
- cars/applications/dense_matching/dense_matching_constants.py +89 -0
- cars/applications/dense_matching/dense_matching_wrappers.py +951 -0
- cars/applications/dense_matching/disparity_grid_algo.py +597 -0
- cars/applications/dense_matching/loaders/__init__.py +23 -0
- cars/applications/dense_matching/loaders/config_census_sgm_default.json +31 -0
- cars/applications/dense_matching/loaders/config_census_sgm_homogeneous.json +30 -0
- cars/applications/dense_matching/loaders/config_census_sgm_mountain_and_vegetation.json +30 -0
- cars/applications/dense_matching/loaders/config_census_sgm_shadow.json +30 -0
- cars/applications/dense_matching/loaders/config_census_sgm_sparse.json +36 -0
- cars/applications/dense_matching/loaders/config_census_sgm_urban.json +30 -0
- cars/applications/dense_matching/loaders/config_mapping.json +13 -0
- cars/applications/dense_matching/loaders/config_mccnn.json +28 -0
- cars/applications/dense_matching/loaders/global_land_cover_map.tif +0 -0
- cars/applications/dense_matching/loaders/pandora_loader.py +593 -0
- cars/applications/dsm_filling/__init__.py +32 -0
- cars/applications/dsm_filling/abstract_dsm_filling_app.py +101 -0
- cars/applications/dsm_filling/border_interpolation_app.py +278 -0
- cars/applications/dsm_filling/bulldozer_config/base_config.yaml +44 -0
- cars/applications/dsm_filling/bulldozer_filling_app.py +288 -0
- cars/applications/dsm_filling/exogenous_filling_app.py +341 -0
- cars/applications/dsm_merging/__init__.py +28 -0
- cars/applications/dsm_merging/abstract_dsm_merging_app.py +101 -0
- cars/applications/dsm_merging/weighted_fusion_app.py +639 -0
- cars/applications/grid_correction/__init__.py +30 -0
- cars/applications/grid_correction/abstract_grid_correction_app.py +103 -0
- cars/applications/grid_correction/grid_correction_app.py +557 -0
- cars/applications/grid_generation/__init__.py +30 -0
- cars/applications/grid_generation/abstract_grid_generation_app.py +142 -0
- cars/applications/grid_generation/epipolar_grid_generation_app.py +327 -0
- cars/applications/grid_generation/grid_generation_algo.py +388 -0
- cars/applications/grid_generation/grid_generation_constants.py +46 -0
- cars/applications/grid_generation/transform_grid.py +88 -0
- cars/applications/ground_truth_reprojection/__init__.py +30 -0
- cars/applications/ground_truth_reprojection/abstract_ground_truth_reprojection_app.py +137 -0
- cars/applications/ground_truth_reprojection/direct_localization_app.py +629 -0
- cars/applications/ground_truth_reprojection/ground_truth_reprojection_algo.py +275 -0
- cars/applications/point_cloud_outlier_removal/__init__.py +30 -0
- cars/applications/point_cloud_outlier_removal/abstract_outlier_removal_app.py +385 -0
- cars/applications/point_cloud_outlier_removal/outlier_removal_algo.py +392 -0
- cars/applications/point_cloud_outlier_removal/outlier_removal_constants.py +43 -0
- cars/applications/point_cloud_outlier_removal/small_components_app.py +522 -0
- cars/applications/point_cloud_outlier_removal/statistical_app.py +528 -0
- cars/applications/rasterization/__init__.py +30 -0
- cars/applications/rasterization/abstract_pc_rasterization_app.py +183 -0
- cars/applications/rasterization/rasterization_algo.py +534 -0
- cars/applications/rasterization/rasterization_constants.py +38 -0
- cars/applications/rasterization/rasterization_wrappers.py +639 -0
- cars/applications/rasterization/simple_gaussian_app.py +1152 -0
- cars/applications/resampling/__init__.py +28 -0
- cars/applications/resampling/abstract_resampling_app.py +187 -0
- cars/applications/resampling/bicubic_resampling_app.py +760 -0
- cars/applications/resampling/resampling_algo.py +590 -0
- cars/applications/resampling/resampling_constants.py +36 -0
- cars/applications/resampling/resampling_wrappers.py +309 -0
- cars/applications/sensors_subsampling/__init__.py +32 -0
- cars/applications/sensors_subsampling/abstract_subsampling_app.py +109 -0
- cars/applications/sensors_subsampling/rasterio_subsampling_app.py +420 -0
- cars/applications/sensors_subsampling/subsampling_algo.py +108 -0
- cars/applications/sparse_matching/__init__.py +30 -0
- cars/applications/sparse_matching/abstract_sparse_matching_app.py +599 -0
- cars/applications/sparse_matching/sift_app.py +724 -0
- cars/applications/sparse_matching/sparse_matching_algo.py +360 -0
- cars/applications/sparse_matching/sparse_matching_constants.py +66 -0
- cars/applications/sparse_matching/sparse_matching_wrappers.py +282 -0
- cars/applications/triangulation/__init__.py +32 -0
- cars/applications/triangulation/abstract_triangulation_app.py +227 -0
- cars/applications/triangulation/line_of_sight_intersection_app.py +1243 -0
- cars/applications/triangulation/pc_transform.py +552 -0
- cars/applications/triangulation/triangulation_algo.py +371 -0
- cars/applications/triangulation/triangulation_constants.py +38 -0
- cars/applications/triangulation/triangulation_wrappers.py +259 -0
- cars/bundleadjustment.py +750 -0
- cars/cars.py +179 -0
- cars/conf/__init__.py +23 -0
- cars/conf/geoid/egm96.grd +0 -0
- cars/conf/geoid/egm96.grd.hdr +15 -0
- cars/conf/input_parameters.py +156 -0
- cars/conf/mask_cst.py +35 -0
- cars/core/__init__.py +23 -0
- cars/core/cars_logging.py +402 -0
- cars/core/constants.py +191 -0
- cars/core/constants_disparity.py +50 -0
- cars/core/datasets.py +140 -0
- cars/core/geometry/__init__.py +27 -0
- cars/core/geometry/abstract_geometry.py +1119 -0
- cars/core/geometry/shareloc_geometry.py +598 -0
- cars/core/inputs.py +568 -0
- cars/core/outputs.py +176 -0
- cars/core/preprocessing.py +722 -0
- cars/core/projection.py +843 -0
- cars/core/roi_tools.py +215 -0
- cars/core/tiling.py +774 -0
- cars/core/utils.py +164 -0
- cars/data_structures/__init__.py +23 -0
- cars/data_structures/cars_dataset.py +1544 -0
- cars/data_structures/cars_dict.py +74 -0
- cars/data_structures/corresponding_tiles_tools.py +186 -0
- cars/data_structures/dataframe_converter.py +185 -0
- cars/data_structures/format_transformation.py +297 -0
- cars/devibrate.py +689 -0
- cars/extractroi.py +264 -0
- cars/orchestrator/__init__.py +23 -0
- cars/orchestrator/achievement_tracker.py +125 -0
- cars/orchestrator/cluster/__init__.py +37 -0
- cars/orchestrator/cluster/abstract_cluster.py +250 -0
- cars/orchestrator/cluster/abstract_dask_cluster.py +381 -0
- cars/orchestrator/cluster/dask_cluster_tools.py +103 -0
- cars/orchestrator/cluster/dask_config/README.md +94 -0
- cars/orchestrator/cluster/dask_config/dask.yaml +21 -0
- cars/orchestrator/cluster/dask_config/distributed.yaml +70 -0
- cars/orchestrator/cluster/dask_config/jobqueue.yaml +26 -0
- cars/orchestrator/cluster/dask_config/reference_confs/dask-schema.yaml +137 -0
- cars/orchestrator/cluster/dask_config/reference_confs/dask.yaml +26 -0
- cars/orchestrator/cluster/dask_config/reference_confs/distributed-schema.yaml +1009 -0
- cars/orchestrator/cluster/dask_config/reference_confs/distributed.yaml +273 -0
- cars/orchestrator/cluster/dask_config/reference_confs/jobqueue.yaml +212 -0
- cars/orchestrator/cluster/dask_jobqueue_utils.py +204 -0
- cars/orchestrator/cluster/local_dask_cluster.py +116 -0
- cars/orchestrator/cluster/log_wrapper.py +728 -0
- cars/orchestrator/cluster/mp_cluster/__init__.py +27 -0
- cars/orchestrator/cluster/mp_cluster/mp_factorizer.py +212 -0
- cars/orchestrator/cluster/mp_cluster/mp_objects.py +535 -0
- cars/orchestrator/cluster/mp_cluster/mp_tools.py +93 -0
- cars/orchestrator/cluster/mp_cluster/mp_wrapper.py +505 -0
- cars/orchestrator/cluster/mp_cluster/multiprocessing_cluster.py +986 -0
- cars/orchestrator/cluster/mp_cluster/multiprocessing_profiler.py +399 -0
- cars/orchestrator/cluster/pbs_dask_cluster.py +207 -0
- cars/orchestrator/cluster/sequential_cluster.py +139 -0
- cars/orchestrator/cluster/slurm_dask_cluster.py +234 -0
- cars/orchestrator/memory_tools.py +47 -0
- cars/orchestrator/orchestrator.py +755 -0
- cars/orchestrator/orchestrator_constants.py +29 -0
- cars/orchestrator/registry/__init__.py +23 -0
- cars/orchestrator/registry/abstract_registry.py +143 -0
- cars/orchestrator/registry/compute_registry.py +106 -0
- cars/orchestrator/registry/id_generator.py +116 -0
- cars/orchestrator/registry/replacer_registry.py +213 -0
- cars/orchestrator/registry/saver_registry.py +363 -0
- cars/orchestrator/registry/unseen_registry.py +118 -0
- cars/orchestrator/tiles_profiler.py +279 -0
- cars/pipelines/__init__.py +26 -0
- cars/pipelines/conf_resolution/conf_final_resolution.yaml +5 -0
- cars/pipelines/conf_resolution/conf_first_resolution.yaml +4 -0
- cars/pipelines/conf_resolution/conf_intermediate_resolution.yaml +2 -0
- cars/pipelines/default/__init__.py +26 -0
- cars/pipelines/default/default_pipeline.py +1088 -0
- cars/pipelines/filling/__init__.py +26 -0
- cars/pipelines/filling/filling.py +981 -0
- cars/pipelines/formatting/__init__.py +26 -0
- cars/pipelines/formatting/formatting.py +186 -0
- cars/pipelines/merging/__init__.py +26 -0
- cars/pipelines/merging/merging.py +439 -0
- cars/pipelines/parameters/__init__.py +0 -0
- cars/pipelines/parameters/advanced_parameters.py +256 -0
- cars/pipelines/parameters/advanced_parameters_constants.py +68 -0
- cars/pipelines/parameters/application_parameters.py +72 -0
- cars/pipelines/parameters/depth_map_inputs.py +0 -0
- cars/pipelines/parameters/dsm_inputs.py +349 -0
- cars/pipelines/parameters/dsm_inputs_constants.py +25 -0
- cars/pipelines/parameters/output_constants.py +52 -0
- cars/pipelines/parameters/output_parameters.py +438 -0
- cars/pipelines/parameters/sensor_inputs.py +859 -0
- cars/pipelines/parameters/sensor_inputs_constants.py +51 -0
- cars/pipelines/parameters/sensor_loaders/__init__.py +29 -0
- cars/pipelines/parameters/sensor_loaders/basic_classif_loader.py +86 -0
- cars/pipelines/parameters/sensor_loaders/basic_image_loader.py +98 -0
- cars/pipelines/parameters/sensor_loaders/pivot_classif_loader.py +90 -0
- cars/pipelines/parameters/sensor_loaders/pivot_image_loader.py +105 -0
- cars/pipelines/parameters/sensor_loaders/sensor_loader.py +93 -0
- cars/pipelines/parameters/sensor_loaders/sensor_loader_template.py +71 -0
- cars/pipelines/parameters/sensor_loaders/slurp_classif_loader.py +86 -0
- cars/pipelines/pipeline.py +119 -0
- cars/pipelines/pipeline_constants.py +38 -0
- cars/pipelines/pipeline_template.py +135 -0
- cars/pipelines/subsampling/__init__.py +26 -0
- cars/pipelines/subsampling/subsampling.py +358 -0
- cars/pipelines/surface_modeling/__init__.py +26 -0
- cars/pipelines/surface_modeling/surface_modeling.py +2098 -0
- cars/pipelines/tie_points/__init__.py +26 -0
- cars/pipelines/tie_points/tie_points.py +536 -0
- cars/starter.py +167 -0
- cars-1.0.0rc2.dist-info/DELVEWHEEL +2 -0
- cars-1.0.0rc2.dist-info/METADATA +289 -0
- cars-1.0.0rc2.dist-info/RECORD +225 -0
- cars-1.0.0rc2.dist-info/WHEEL +4 -0
- cars-1.0.0rc2.dist-info/entry_points.txt +8 -0
- cars.libs/libgcc_s_seh-1-b2494fcbd4d80cf2c98fdd5261f6d850.dll +0 -0
- cars.libs/libstdc++-6-e9b0d12ae0e9555bbae55e8dfd08c3f7.dll +0 -0
- cars.libs/libwinpthread-1-7882d1b093714ccdfaf4e0789a817792.dll +0 -0
cars/core/tiling.py
ADDED
|
@@ -0,0 +1,774 @@
|
|
|
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
|
+
Tiling module:
|
|
23
|
+
contains functions related to regions and tiles management
|
|
24
|
+
"""
|
|
25
|
+
# pylint: disable=too-many-lines
|
|
26
|
+
|
|
27
|
+
import logging
|
|
28
|
+
|
|
29
|
+
# Standard imports
|
|
30
|
+
import math
|
|
31
|
+
from typing import Dict, List, Tuple
|
|
32
|
+
|
|
33
|
+
# Third party imports
|
|
34
|
+
import numpy as np
|
|
35
|
+
from pyproj import CRS
|
|
36
|
+
from scipy.ndimage import generic_filter
|
|
37
|
+
from scipy.spatial import Delaunay # pylint: disable=no-name-in-module
|
|
38
|
+
from scipy.spatial import cKDTree # pylint: disable=no-name-in-module
|
|
39
|
+
from scipy.spatial import tsearch # pylint: disable=no-name-in-module
|
|
40
|
+
from shapely.geometry import box, mapping
|
|
41
|
+
from shapely.geometry.multipolygon import MultiPolygon
|
|
42
|
+
|
|
43
|
+
from cars.orchestrator.cluster.log_wrapper import cars_profile
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def grid( # pylint: disable=too-many-positional-arguments
|
|
47
|
+
xmin: float, ymin: float, xmax: float, ymax: float, xsplit: int, ysplit: int
|
|
48
|
+
) -> np.ndarray:
|
|
49
|
+
"""
|
|
50
|
+
Generate grid of positions by splitting [xmin, xmax]x[ymin, ymax]
|
|
51
|
+
in splits of xsplit x ysplit size
|
|
52
|
+
|
|
53
|
+
:param xmin : xmin of the bounding box of the region to split
|
|
54
|
+
:param ymin : ymin of the bounding box of the region to split
|
|
55
|
+
:param xmax : xmax of the bounding box of the region to split
|
|
56
|
+
:param ymax : ymax of the bounding box of the region to split
|
|
57
|
+
:param xsplit: width of splits
|
|
58
|
+
:param ysplit: height of splits
|
|
59
|
+
|
|
60
|
+
:return: The output ndarray grid with nb_ysplits splits in first direction
|
|
61
|
+
and nb_xsplits in second direction for 2 dimensions 0:x, 1:y
|
|
62
|
+
:rtype: numpy array
|
|
63
|
+
"""
|
|
64
|
+
nb_xsplits = math.ceil((xmax - xmin) / xsplit)
|
|
65
|
+
nb_ysplits = math.ceil((ymax - ymin) / ysplit)
|
|
66
|
+
|
|
67
|
+
out_grid = np.ndarray(
|
|
68
|
+
shape=(nb_ysplits + 1, nb_xsplits + 1, 2), dtype=float
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
for i in range(0, nb_xsplits + 1):
|
|
72
|
+
for j in range(0, nb_ysplits + 1):
|
|
73
|
+
out_grid[j, i, 0] = min(xmax, xmin + i * xsplit)
|
|
74
|
+
out_grid[j, i, 1] = min(ymax, ymin + j * ysplit)
|
|
75
|
+
|
|
76
|
+
return out_grid
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
@cars_profile(name="Transform four layers to two layers grid", interval=0.5)
|
|
80
|
+
def transform_four_layers_to_two_layers_grid(tiling_grid, terrain=False):
|
|
81
|
+
"""
|
|
82
|
+
Transform a 4 layer grid: (N, M, 4) containing
|
|
83
|
+
[rowmin, rowmax, colmin, colmax] when epipolar
|
|
84
|
+
and [xmin, xmax, ymin, ymax]
|
|
85
|
+
with x = col and y = row
|
|
86
|
+
into a 2 layer grid: (N+1, M+1, 2) containing x and y
|
|
87
|
+
defined like : grid[j, i, 0] = min(xmax, xmin + i * xsplit)
|
|
88
|
+
and grid[j, i, 1] = min(ymax, ymin + j * ysplit)
|
|
89
|
+
|
|
90
|
+
:param tiling_grid: tiling grid
|
|
91
|
+
:type tiling_grid: np.ndarray
|
|
92
|
+
|
|
93
|
+
:return: 2D grid
|
|
94
|
+
:rtype: np.ndarray
|
|
95
|
+
"""
|
|
96
|
+
|
|
97
|
+
if terrain is False:
|
|
98
|
+
tiling_grid_ = tiling_grid.copy()
|
|
99
|
+
tiling_grid_[:, :, [0, 1, 2, 3]] = tiling_grid_[:, :, [2, 3, 0, 1]]
|
|
100
|
+
else:
|
|
101
|
+
tiling_grid_ = tiling_grid.transpose(1, 0, 2).copy()
|
|
102
|
+
|
|
103
|
+
arr = np.ndarray(
|
|
104
|
+
shape=(tiling_grid_.shape[0] + 1, tiling_grid_.shape[1] + 1, 2),
|
|
105
|
+
dtype=float,
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
# Fill x
|
|
109
|
+
arr[0:-1, 0:-1, 0] = tiling_grid_[:, :, 0]
|
|
110
|
+
arr[0:-1, -1, 0] = tiling_grid_[:, -1, 1]
|
|
111
|
+
arr[-1, :, 0] = arr[0, :, 0] # All rows are identical
|
|
112
|
+
|
|
113
|
+
# Fill y
|
|
114
|
+
arr[0:-1, 0:-1, 1] = tiling_grid_[:, :, 2]
|
|
115
|
+
arr[-1, 0:-1, 1] = tiling_grid_[-1, :, 3]
|
|
116
|
+
arr[:, -1, 1] = arr[:, 0, 1] # All cols are identical
|
|
117
|
+
|
|
118
|
+
return arr
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
@cars_profile(name="Transform disp range grid to two layers", interval=0.5)
|
|
122
|
+
def transform_disp_range_grid_to_two_layers(disp_min_grid, disp_max_grid):
|
|
123
|
+
"""
|
|
124
|
+
Transform tiling disp min and max to N+1 M+1 array corresponding
|
|
125
|
+
to N+1, M+1 , 2 tiling grid
|
|
126
|
+
|
|
127
|
+
:param disp_min_grid: disp min tiling
|
|
128
|
+
:type disp_min_grid: np ndarray
|
|
129
|
+
:param disp_max_grid: disp max tiling
|
|
130
|
+
:type disp_max_grid: np ndarray
|
|
131
|
+
|
|
132
|
+
:return: disp_min_grid, disp_max_grid
|
|
133
|
+
:rtype: np ndarray , np ndarray
|
|
134
|
+
"""
|
|
135
|
+
|
|
136
|
+
# Create a 2xN+1, 2xM+1 matrix to apply filter on it
|
|
137
|
+
nb_row = 2 * disp_min_grid.shape[0] + 1
|
|
138
|
+
nb_col = 2 * disp_min_grid.shape[1] + 1
|
|
139
|
+
|
|
140
|
+
disp_min = np.full((nb_row, nb_col), np.nan)
|
|
141
|
+
disp_max = np.full((nb_row, nb_col), np.nan)
|
|
142
|
+
|
|
143
|
+
disp_min[1::2, 1::2] = disp_min_grid
|
|
144
|
+
disp_max[1::2, 1::2] = disp_max_grid
|
|
145
|
+
|
|
146
|
+
# Apply filter min and max:
|
|
147
|
+
# as each cell represent a node of 4 tiles from a regular grid
|
|
148
|
+
# we want for each node to
|
|
149
|
+
# represent the min and max of 4 cells
|
|
150
|
+
|
|
151
|
+
disp_min = generic_filter(disp_min, np.nanmin, [3, 3])
|
|
152
|
+
disp_max = generic_filter(disp_max, np.nanmax, [3, 3])
|
|
153
|
+
|
|
154
|
+
# eliminate odd indexes
|
|
155
|
+
disp_min = disp_min[::2, ::2]
|
|
156
|
+
disp_max = disp_max[::2, ::2]
|
|
157
|
+
|
|
158
|
+
return disp_min, disp_max
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
def generate_tiling_grid( # pylint: disable=too-many-positional-arguments
|
|
162
|
+
row_min: float,
|
|
163
|
+
col_min: float,
|
|
164
|
+
row_max: float,
|
|
165
|
+
col_max: float,
|
|
166
|
+
row_split: int,
|
|
167
|
+
col_split: int,
|
|
168
|
+
) -> np.ndarray:
|
|
169
|
+
"""
|
|
170
|
+
Generate grid of positions by splitting [row_min, row_max] x
|
|
171
|
+
[col_min, col_max]
|
|
172
|
+
in splits of row_split x col_split size
|
|
173
|
+
|
|
174
|
+
:param row_min : row_min of the bounding box of the region to split
|
|
175
|
+
:param col_min : col_min of the bounding box of the region to split
|
|
176
|
+
:param row_max : row_max of the bounding box of the region to split
|
|
177
|
+
:param col_max : col_max of the bounding box of the region to split
|
|
178
|
+
:param row_split: height of splits
|
|
179
|
+
:param col_split: width of splits
|
|
180
|
+
|
|
181
|
+
:return: The output ndarray grid with nb_row_split splits in first direction
|
|
182
|
+
and nb_col_split in second direction for 2 dimensions 0:y, 1:x
|
|
183
|
+
[row, col, :] containing [row_min, row_max, col_min, col_max]
|
|
184
|
+
:rtype: numpy array
|
|
185
|
+
"""
|
|
186
|
+
|
|
187
|
+
nb_col_split = math.ceil((col_max - col_min) / col_split)
|
|
188
|
+
nb_row_split = math.ceil((row_max - row_min) / row_split)
|
|
189
|
+
|
|
190
|
+
out_grid = np.ndarray(shape=(nb_row_split, nb_col_split, 4), dtype=float)
|
|
191
|
+
|
|
192
|
+
for row in range(0, nb_row_split):
|
|
193
|
+
for col in range(0, nb_col_split):
|
|
194
|
+
out_grid[row, col, 0] = min(row_max, row_min + row * row_split)
|
|
195
|
+
out_grid[row, col, 1] = min(
|
|
196
|
+
row_max, row_min + (row + 1) * row_split
|
|
197
|
+
)
|
|
198
|
+
out_grid[row, col, 2] = min(col_max, col_min + col * col_split)
|
|
199
|
+
out_grid[row, col, 3] = min(
|
|
200
|
+
col_max, col_min + (col + 1) * col_split
|
|
201
|
+
)
|
|
202
|
+
|
|
203
|
+
return out_grid
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
def split(
|
|
207
|
+
xmin, ymin, xmax, ymax, xsplit, ysplit
|
|
208
|
+
): # pylint: disable=too-many-positional-arguments
|
|
209
|
+
"""
|
|
210
|
+
Split a region defined by [xmin, xmax] x [ymin, ymax]
|
|
211
|
+
in splits of xsplit x ysplit size
|
|
212
|
+
|
|
213
|
+
:param xmin : xmin of the bounding box of the region to split
|
|
214
|
+
:type xmin: float
|
|
215
|
+
:param ymin : ymin of the bounding box of the region to split
|
|
216
|
+
:type ymin: float
|
|
217
|
+
:param xmax : xmax of the bounding box of the region to split
|
|
218
|
+
:type xmax: float
|
|
219
|
+
:param ymax : ymax of the bounding box of the region to split
|
|
220
|
+
:type ymax: float
|
|
221
|
+
:param xsplit: width of splits
|
|
222
|
+
:type xsplit: int
|
|
223
|
+
:param ysplit: height of splits
|
|
224
|
+
:type ysplit: int
|
|
225
|
+
|
|
226
|
+
:return: A list of splits represented
|
|
227
|
+
by arrays of 4 elements [xmin, ymin, xmax, ymax]
|
|
228
|
+
:rtype: list of 4 float
|
|
229
|
+
"""
|
|
230
|
+
nb_xsplits = math.ceil((xmax - xmin) / xsplit)
|
|
231
|
+
nb_ysplits = math.ceil((ymax - ymin) / ysplit)
|
|
232
|
+
|
|
233
|
+
terrain_regions = []
|
|
234
|
+
|
|
235
|
+
for i in range(0, nb_xsplits):
|
|
236
|
+
for j in range(0, nb_ysplits):
|
|
237
|
+
region = [
|
|
238
|
+
xmin + i * xsplit,
|
|
239
|
+
ymin + j * ysplit,
|
|
240
|
+
xmin + (i + 1) * xsplit,
|
|
241
|
+
ymin + (j + 1) * ysplit,
|
|
242
|
+
]
|
|
243
|
+
|
|
244
|
+
# Crop to largest region
|
|
245
|
+
region = crop(region, [xmin, ymin, xmax, ymax])
|
|
246
|
+
|
|
247
|
+
terrain_regions.append(region)
|
|
248
|
+
|
|
249
|
+
return terrain_regions
|
|
250
|
+
|
|
251
|
+
|
|
252
|
+
def crop(region1, region2):
|
|
253
|
+
"""
|
|
254
|
+
Crop a region by another one
|
|
255
|
+
|
|
256
|
+
:param region1: The region to crop as an array [xmin, ymin, xmax, ymax]
|
|
257
|
+
:type region1: list of four float
|
|
258
|
+
:param region2: The region used for cropping
|
|
259
|
+
as an array [xmin, ymin, xmax, ymax]
|
|
260
|
+
:type region2: list of four float
|
|
261
|
+
|
|
262
|
+
:return: The cropped regiona as an array [xmin, ymin, xmax, ymax].
|
|
263
|
+
If region1 is outside region2, might result in inconsistent region
|
|
264
|
+
:rtype: list of four float
|
|
265
|
+
"""
|
|
266
|
+
out = region1[:]
|
|
267
|
+
out[0] = min(region2[2], max(region2[0], region1[0]))
|
|
268
|
+
out[2] = min(region2[2], max(region2[0], region1[2]))
|
|
269
|
+
out[1] = min(region2[3], max(region2[1], region1[1]))
|
|
270
|
+
out[3] = min(region2[3], max(region2[1], region1[3]))
|
|
271
|
+
|
|
272
|
+
return out
|
|
273
|
+
|
|
274
|
+
|
|
275
|
+
def pad(region, margins):
|
|
276
|
+
"""
|
|
277
|
+
Pad region according to a margin
|
|
278
|
+
|
|
279
|
+
:param region: The region to pad
|
|
280
|
+
:type region: list of four floats
|
|
281
|
+
:param margins: Margin to add
|
|
282
|
+
:type margins: list of four floats
|
|
283
|
+
:return: padded region
|
|
284
|
+
:rtype: list of four float
|
|
285
|
+
"""
|
|
286
|
+
out = region[:]
|
|
287
|
+
out[0] -= margins[0]
|
|
288
|
+
out[1] -= margins[1]
|
|
289
|
+
out[2] += margins[2]
|
|
290
|
+
out[3] += margins[3]
|
|
291
|
+
|
|
292
|
+
return out
|
|
293
|
+
|
|
294
|
+
|
|
295
|
+
def empty(region):
|
|
296
|
+
"""
|
|
297
|
+
Check if a region is empty or inconsistent
|
|
298
|
+
|
|
299
|
+
:param region: region as an array [xmin, ymin, xmax, ymax]
|
|
300
|
+
:type region: list of four float
|
|
301
|
+
:return: True if the region is considered empty (no pixels inside),
|
|
302
|
+
False otherwise
|
|
303
|
+
:rtype: bool"""
|
|
304
|
+
return region[0] >= region[2] or region[1] >= region[3]
|
|
305
|
+
|
|
306
|
+
|
|
307
|
+
def union(regions):
|
|
308
|
+
"""
|
|
309
|
+
Returns union of all regions
|
|
310
|
+
|
|
311
|
+
:param regions: list of region as an array [xmin, ymin, xmax, ymax]
|
|
312
|
+
:type regions: list of list of four float
|
|
313
|
+
:return: xmin, ymin, xmax, ymax
|
|
314
|
+
:rtype: list of 4 float
|
|
315
|
+
"""
|
|
316
|
+
|
|
317
|
+
xmin = min((r[0] for r in regions))
|
|
318
|
+
xmax = max((r[2] for r in regions))
|
|
319
|
+
ymin = min((r[1] for r in regions))
|
|
320
|
+
ymax = max((r[3] for r in regions))
|
|
321
|
+
|
|
322
|
+
return xmin, ymin, xmax, ymax
|
|
323
|
+
|
|
324
|
+
|
|
325
|
+
def list_tiles(region, largest_region, tile_size, margin=1):
|
|
326
|
+
"""
|
|
327
|
+
Given a region, cut largest_region into tiles of size tile_size
|
|
328
|
+
and return tiles that intersect region within margin pixels.
|
|
329
|
+
|
|
330
|
+
:param region: The region to list intersecting tiles
|
|
331
|
+
:type region: list of four float
|
|
332
|
+
:param largest_region: The region to split
|
|
333
|
+
:type largest_region: list of four float
|
|
334
|
+
:param tile_size: Width of tiles for splitting (squared tiles)
|
|
335
|
+
:type tile_size: int
|
|
336
|
+
:param margin: Also include margin neighboring tiles
|
|
337
|
+
:type margin: int
|
|
338
|
+
:return: A list of tiles as dicts containing idx and idy
|
|
339
|
+
:rtype: list of dict
|
|
340
|
+
"""
|
|
341
|
+
# Find tile indices covered by region
|
|
342
|
+
min_tile_idx_x = int(math.floor(region[0] / tile_size))
|
|
343
|
+
max_tile_idx_x = int(math.ceil(region[2] / tile_size))
|
|
344
|
+
min_tile_idx_y = int(math.floor(region[1] / tile_size))
|
|
345
|
+
max_tile_idx_y = int(math.ceil(region[3] / tile_size))
|
|
346
|
+
|
|
347
|
+
# Include additional tiles
|
|
348
|
+
min_tile_idx_x -= margin
|
|
349
|
+
min_tile_idx_y -= margin
|
|
350
|
+
max_tile_idx_x += margin
|
|
351
|
+
max_tile_idx_y += margin
|
|
352
|
+
|
|
353
|
+
out = []
|
|
354
|
+
|
|
355
|
+
# Loop on tile idx
|
|
356
|
+
for tile_idx_x in range(min_tile_idx_x, max_tile_idx_x):
|
|
357
|
+
for tile_idx_y in range(min_tile_idx_y, max_tile_idx_y):
|
|
358
|
+
# Derive tile coordinates
|
|
359
|
+
tile = [
|
|
360
|
+
tile_idx_x * tile_size,
|
|
361
|
+
tile_idx_y * tile_size,
|
|
362
|
+
(tile_idx_x + 1) * tile_size,
|
|
363
|
+
(tile_idx_y + 1) * tile_size,
|
|
364
|
+
]
|
|
365
|
+
|
|
366
|
+
# Crop to largest region
|
|
367
|
+
tile = crop(tile, largest_region)
|
|
368
|
+
|
|
369
|
+
# Check if tile is empty
|
|
370
|
+
if not empty(tile):
|
|
371
|
+
out.append({"idx": tile_idx_x, "idy": tile_idx_y, "tile": tile})
|
|
372
|
+
|
|
373
|
+
return out
|
|
374
|
+
|
|
375
|
+
|
|
376
|
+
def roi_to_start_and_size(region, resolution):
|
|
377
|
+
"""
|
|
378
|
+
Convert roi as array of [xmin, ymin, xmax, ymax]
|
|
379
|
+
to xmin, ymin, xsize, ysize given a resolution
|
|
380
|
+
|
|
381
|
+
Beware that a negative spacing is considered for y axis,
|
|
382
|
+
and thus returned ystart is in fact ymax
|
|
383
|
+
|
|
384
|
+
:param region: The region to convert
|
|
385
|
+
:type region: list of four float
|
|
386
|
+
:param resolution: The resolution to use to determine sizes
|
|
387
|
+
:type resolution: float
|
|
388
|
+
:return: xstart, ystart, xsize, ysize tuple
|
|
389
|
+
:rtype: list of two float + two int
|
|
390
|
+
"""
|
|
391
|
+
xstart = region[0]
|
|
392
|
+
ystart = region[3]
|
|
393
|
+
xsize = int(np.round((region[2] - region[0]) / resolution))
|
|
394
|
+
ysize = int(np.round((region[3] - region[1]) / resolution))
|
|
395
|
+
|
|
396
|
+
return xstart, ystart, xsize, ysize
|
|
397
|
+
|
|
398
|
+
|
|
399
|
+
def snap_to_grid(xmin, ymin, xmax, ymax, resolution):
|
|
400
|
+
"""
|
|
401
|
+
Given a roi as xmin, ymin, xmax, ymax, snap values to entire step
|
|
402
|
+
of resolution
|
|
403
|
+
|
|
404
|
+
:param xmin: xmin of the roi
|
|
405
|
+
:type xmin: float
|
|
406
|
+
:param ymin: ymin of the roi
|
|
407
|
+
:type ymin: float
|
|
408
|
+
:param xmax: xmax of the roi
|
|
409
|
+
:type xmax: float
|
|
410
|
+
:param ymax: ymax of the roi
|
|
411
|
+
:type ymax: float
|
|
412
|
+
:param resolution: size of cells for snapping
|
|
413
|
+
:type resolution: float
|
|
414
|
+
:return: xmin, ymin, xmax, ymax snapped tuple
|
|
415
|
+
:rtype: list of four float
|
|
416
|
+
"""
|
|
417
|
+
xmin = math.floor(xmin / resolution) * resolution
|
|
418
|
+
xmax = math.ceil(xmax / resolution) * resolution
|
|
419
|
+
ymin = math.floor(ymin / resolution) * resolution
|
|
420
|
+
ymax = math.ceil(ymax / resolution) * resolution
|
|
421
|
+
|
|
422
|
+
return xmin, ymin, xmax, ymax
|
|
423
|
+
|
|
424
|
+
|
|
425
|
+
def filter_simplices_on_the_edges(
|
|
426
|
+
original_grid_shape: Tuple, tri: Delaunay, simplices: np.ndarray
|
|
427
|
+
):
|
|
428
|
+
"""
|
|
429
|
+
Filter simplices on the edges which allows to cut triangles out of the
|
|
430
|
+
concave Delaunay triangulation.
|
|
431
|
+
|
|
432
|
+
:param original_grid_shape: shape of the original grid (almost regular) used
|
|
433
|
+
to create delaunay triangulation
|
|
434
|
+
:param tri: Delaunay triangulation
|
|
435
|
+
:param simplices: Selected simplices to filter: set -1 if selected simplex
|
|
436
|
+
is on the edges
|
|
437
|
+
"""
|
|
438
|
+
|
|
439
|
+
# Filter simplices on the edges
|
|
440
|
+
edges = np.zeros((4, *original_grid_shape))
|
|
441
|
+
|
|
442
|
+
# left, bottom, right, top
|
|
443
|
+
edges[0, :, 0] = 1
|
|
444
|
+
edges[1, -1, :] = 1
|
|
445
|
+
edges[2, :, -1] = 1
|
|
446
|
+
edges[3, 0, :] = 1
|
|
447
|
+
|
|
448
|
+
for idx in range(edges.shape[0]):
|
|
449
|
+
edges_ravel = np.ravel(edges[idx, :, :])
|
|
450
|
+
# simplices filtered if all points are on an edge
|
|
451
|
+
edges_simplices = np.sum(edges_ravel[tri.simplices], axis=1) == 3
|
|
452
|
+
simplices[edges_simplices[simplices]] = -1
|
|
453
|
+
|
|
454
|
+
|
|
455
|
+
def terrain_grid_to_epipolar(
|
|
456
|
+
terrain_tiling_grid,
|
|
457
|
+
epipolar_tiling_grid,
|
|
458
|
+
epipolar_grid_min,
|
|
459
|
+
epipolar_grid_max,
|
|
460
|
+
epsg,
|
|
461
|
+
):
|
|
462
|
+
"""
|
|
463
|
+
Transform terrain grid to epipolar region
|
|
464
|
+
"""
|
|
465
|
+
|
|
466
|
+
terrain_regions_grid = transform_four_layers_to_two_layers_grid(
|
|
467
|
+
terrain_tiling_grid, terrain=True
|
|
468
|
+
)
|
|
469
|
+
epipolar_regions_grid = transform_four_layers_to_two_layers_grid(
|
|
470
|
+
epipolar_tiling_grid
|
|
471
|
+
)
|
|
472
|
+
|
|
473
|
+
epipolar_regions_grid_shape = np.shape(epipolar_regions_grid)[:2]
|
|
474
|
+
epipolar_regions_grid_flat = epipolar_regions_grid.reshape(
|
|
475
|
+
-1, epipolar_regions_grid.shape[-1]
|
|
476
|
+
)
|
|
477
|
+
|
|
478
|
+
# in the following code a factor is used to increase the precision
|
|
479
|
+
spatial_ref = CRS.from_epsg(epsg)
|
|
480
|
+
if spatial_ref.is_geographic:
|
|
481
|
+
precision_factor = 1000.0
|
|
482
|
+
else:
|
|
483
|
+
precision_factor = 1.0
|
|
484
|
+
|
|
485
|
+
# Build delaunay_triangulation
|
|
486
|
+
tri_min = Delaunay(epipolar_grid_min * precision_factor)
|
|
487
|
+
tri_max = Delaunay(epipolar_grid_max * precision_factor)
|
|
488
|
+
|
|
489
|
+
# Build kdtrees
|
|
490
|
+
tree_min = cKDTree(epipolar_grid_min * precision_factor)
|
|
491
|
+
tree_max = cKDTree(epipolar_grid_max * precision_factor)
|
|
492
|
+
|
|
493
|
+
# Look-up terrain_regions_grid with Delaunay
|
|
494
|
+
s_min = tsearch(tri_min, terrain_regions_grid * precision_factor)
|
|
495
|
+
s_max = tsearch(tri_max, terrain_regions_grid * precision_factor)
|
|
496
|
+
|
|
497
|
+
# Filter simplices on the edges
|
|
498
|
+
filter_simplices_on_the_edges(epipolar_regions_grid_shape, tri_min, s_min)
|
|
499
|
+
filter_simplices_on_the_edges(epipolar_regions_grid_shape, tri_max, s_max)
|
|
500
|
+
|
|
501
|
+
points_disp_min = epipolar_regions_grid_flat[tri_min.simplices[s_min]]
|
|
502
|
+
|
|
503
|
+
points_disp_max = epipolar_regions_grid_flat[tri_max.simplices[s_max]]
|
|
504
|
+
|
|
505
|
+
nn_disp_min = epipolar_regions_grid_flat[
|
|
506
|
+
tree_min.query(terrain_regions_grid * precision_factor)[1]
|
|
507
|
+
]
|
|
508
|
+
|
|
509
|
+
nn_disp_max = epipolar_regions_grid_flat[
|
|
510
|
+
tree_max.query(terrain_regions_grid * precision_factor)[1]
|
|
511
|
+
]
|
|
512
|
+
|
|
513
|
+
points_disp_min_min = np.min(points_disp_min, axis=2)
|
|
514
|
+
points_disp_min_max = np.max(points_disp_min, axis=2)
|
|
515
|
+
points_disp_max_min = np.min(points_disp_max, axis=2)
|
|
516
|
+
points_disp_max_max = np.max(points_disp_max, axis=2)
|
|
517
|
+
|
|
518
|
+
# Use either Delaunay search or NN search
|
|
519
|
+
# if delaunay search fails (point outside triangles)
|
|
520
|
+
points_disp_min_min = np.where(
|
|
521
|
+
np.stack((s_min, s_min), axis=-1) != -1,
|
|
522
|
+
points_disp_min_min,
|
|
523
|
+
nn_disp_min,
|
|
524
|
+
)
|
|
525
|
+
|
|
526
|
+
points_disp_min_max = np.where(
|
|
527
|
+
np.stack((s_min, s_min), axis=-1) != -1,
|
|
528
|
+
points_disp_min_max,
|
|
529
|
+
nn_disp_min,
|
|
530
|
+
)
|
|
531
|
+
|
|
532
|
+
points_disp_max_min = np.where(
|
|
533
|
+
np.stack((s_max, s_max), axis=-1) != -1,
|
|
534
|
+
points_disp_max_min,
|
|
535
|
+
nn_disp_max,
|
|
536
|
+
)
|
|
537
|
+
|
|
538
|
+
points_disp_max_max = np.where(
|
|
539
|
+
np.stack((s_max, s_max), axis=-1) != -1,
|
|
540
|
+
points_disp_max_max,
|
|
541
|
+
nn_disp_max,
|
|
542
|
+
)
|
|
543
|
+
|
|
544
|
+
points = np.stack(
|
|
545
|
+
(
|
|
546
|
+
points_disp_min_min,
|
|
547
|
+
points_disp_min_max,
|
|
548
|
+
points_disp_max_min,
|
|
549
|
+
points_disp_max_max,
|
|
550
|
+
),
|
|
551
|
+
axis=0,
|
|
552
|
+
)
|
|
553
|
+
|
|
554
|
+
points_min = np.min(points, axis=0)
|
|
555
|
+
points_max = np.max(points, axis=0)
|
|
556
|
+
|
|
557
|
+
return points_min, points_max
|
|
558
|
+
|
|
559
|
+
|
|
560
|
+
def region_hash_string(region: Tuple):
|
|
561
|
+
"""
|
|
562
|
+
This lambda will allow to derive a key
|
|
563
|
+
to index region in the previous dictionary
|
|
564
|
+
|
|
565
|
+
:param region: region to hash
|
|
566
|
+
"""
|
|
567
|
+
return "{}_{}_{}_{}".format(region[0], region[1], region[2], region[3])
|
|
568
|
+
|
|
569
|
+
|
|
570
|
+
# pylint: disable=too-many-positional-arguments
|
|
571
|
+
def get_corresponding_tiles_row_col(
|
|
572
|
+
terrain_tiling_grid: np.ndarray,
|
|
573
|
+
row: int,
|
|
574
|
+
col: int,
|
|
575
|
+
list_point_clouds: list,
|
|
576
|
+
list_epipolar_points_min: list,
|
|
577
|
+
list_epipolar_points_max: list,
|
|
578
|
+
) -> Tuple[List, List, List]:
|
|
579
|
+
"""
|
|
580
|
+
This function allows to get required point cloud for each
|
|
581
|
+
terrain region.
|
|
582
|
+
|
|
583
|
+
:param terrain_tiling_grid: terrain grid positions
|
|
584
|
+
:param row: row
|
|
585
|
+
:param col: column
|
|
586
|
+
epipolar input tiles where keys are image pairs index and values are
|
|
587
|
+
epipolar_points_min, epipolar_points_max, largest_epipolar_region,
|
|
588
|
+
opt_epipolar_tile_size
|
|
589
|
+
|
|
590
|
+
:return: Terrain regions
|
|
591
|
+
Corresponding tiles selected from delayed_point_clouds with
|
|
592
|
+
associated id
|
|
593
|
+
Terrain regions "rank" allowing to sorting tiles for dask
|
|
594
|
+
processing
|
|
595
|
+
"""
|
|
596
|
+
|
|
597
|
+
logging.debug(
|
|
598
|
+
"Processing tile located at {},{} in tile grid".format(row, col)
|
|
599
|
+
)
|
|
600
|
+
|
|
601
|
+
# Terrain grid [row, j, :] = [xmin, xmax, ymin, ymax]
|
|
602
|
+
# terrain region = [xmin, ymin, xmax, ymax]
|
|
603
|
+
terrain_region = [
|
|
604
|
+
terrain_tiling_grid[row, col, 0],
|
|
605
|
+
terrain_tiling_grid[row, col, 2],
|
|
606
|
+
terrain_tiling_grid[row, col, 1],
|
|
607
|
+
terrain_tiling_grid[row, col, 3],
|
|
608
|
+
]
|
|
609
|
+
|
|
610
|
+
# reverse convention as row and col correspond to new format
|
|
611
|
+
# Former format is transposed
|
|
612
|
+
row, col = col, row
|
|
613
|
+
|
|
614
|
+
logging.debug("Corresponding terrain region: {}".format(terrain_region))
|
|
615
|
+
|
|
616
|
+
# This list will hold the required points clouds for this terrain tile
|
|
617
|
+
required_point_clouds = []
|
|
618
|
+
|
|
619
|
+
# This list contains indexes of tiles (debug purpose)
|
|
620
|
+
list_indexes = []
|
|
621
|
+
|
|
622
|
+
# For each stereo configuration
|
|
623
|
+
for pc_id, (
|
|
624
|
+
point_cloud,
|
|
625
|
+
epipolar_points_min,
|
|
626
|
+
epipolar_points_max,
|
|
627
|
+
) in enumerate(
|
|
628
|
+
zip( # noqa: B905
|
|
629
|
+
list_point_clouds,
|
|
630
|
+
list_epipolar_points_min,
|
|
631
|
+
list_epipolar_points_max,
|
|
632
|
+
)
|
|
633
|
+
):
|
|
634
|
+
largest_epipolar_region = point_cloud.attributes[
|
|
635
|
+
"largest_epipolar_region"
|
|
636
|
+
]
|
|
637
|
+
opt_epipolar_tile_size = point_cloud.attributes[
|
|
638
|
+
"opt_epipolar_tile_size"
|
|
639
|
+
]
|
|
640
|
+
|
|
641
|
+
tile_min = np.minimum(
|
|
642
|
+
np.minimum(
|
|
643
|
+
np.minimum(
|
|
644
|
+
epipolar_points_min[row, col],
|
|
645
|
+
epipolar_points_min[row + 1, col],
|
|
646
|
+
),
|
|
647
|
+
np.minimum(
|
|
648
|
+
epipolar_points_min[row + 1, col + 1],
|
|
649
|
+
epipolar_points_min[row, col + 1],
|
|
650
|
+
),
|
|
651
|
+
),
|
|
652
|
+
np.minimum(
|
|
653
|
+
np.minimum(
|
|
654
|
+
epipolar_points_max[row, col],
|
|
655
|
+
epipolar_points_max[row + 1, col],
|
|
656
|
+
),
|
|
657
|
+
np.minimum(
|
|
658
|
+
epipolar_points_max[row + 1, col + 1],
|
|
659
|
+
epipolar_points_max[row, col + 1],
|
|
660
|
+
),
|
|
661
|
+
),
|
|
662
|
+
)
|
|
663
|
+
|
|
664
|
+
tile_max = np.maximum(
|
|
665
|
+
np.maximum(
|
|
666
|
+
np.maximum(
|
|
667
|
+
epipolar_points_min[row, col],
|
|
668
|
+
epipolar_points_min[row + 1, col],
|
|
669
|
+
),
|
|
670
|
+
np.maximum(
|
|
671
|
+
epipolar_points_min[row + 1, col + 1],
|
|
672
|
+
epipolar_points_min[row, col + 1],
|
|
673
|
+
),
|
|
674
|
+
),
|
|
675
|
+
np.maximum(
|
|
676
|
+
np.maximum(
|
|
677
|
+
epipolar_points_max[row, col],
|
|
678
|
+
epipolar_points_max[row + 1, col],
|
|
679
|
+
),
|
|
680
|
+
np.maximum(
|
|
681
|
+
epipolar_points_max[row + 1, col + 1],
|
|
682
|
+
epipolar_points_max[row, col + 1],
|
|
683
|
+
),
|
|
684
|
+
),
|
|
685
|
+
)
|
|
686
|
+
|
|
687
|
+
# Bounding region of corresponding cell
|
|
688
|
+
epipolar_region_minx = tile_min[0]
|
|
689
|
+
epipolar_region_miny = tile_min[1]
|
|
690
|
+
epipolar_region_maxx = tile_max[0]
|
|
691
|
+
epipolar_region_maxy = tile_max[1]
|
|
692
|
+
|
|
693
|
+
epipolar_region = [
|
|
694
|
+
epipolar_region_minx,
|
|
695
|
+
epipolar_region_miny,
|
|
696
|
+
epipolar_region_maxx,
|
|
697
|
+
epipolar_region_maxy,
|
|
698
|
+
]
|
|
699
|
+
|
|
700
|
+
# Crop epipolar region to largest region
|
|
701
|
+
epipolar_region = crop(epipolar_region, largest_epipolar_region)
|
|
702
|
+
|
|
703
|
+
logging.debug(
|
|
704
|
+
"Corresponding epipolar region: {}".format(epipolar_region)
|
|
705
|
+
)
|
|
706
|
+
|
|
707
|
+
# Check if the epipolar region contains any pixels to process
|
|
708
|
+
if empty(epipolar_region):
|
|
709
|
+
logging.debug(
|
|
710
|
+
"Skipping terrain region "
|
|
711
|
+
"because corresponding epipolar region is empty"
|
|
712
|
+
)
|
|
713
|
+
else:
|
|
714
|
+
# Loop on all epipolar tiles covered by epipolar region
|
|
715
|
+
for epipolar_tile in list_tiles(
|
|
716
|
+
epipolar_region,
|
|
717
|
+
largest_epipolar_region,
|
|
718
|
+
opt_epipolar_tile_size,
|
|
719
|
+
):
|
|
720
|
+
id_x = epipolar_tile["idx"]
|
|
721
|
+
id_y = epipolar_tile["idy"]
|
|
722
|
+
|
|
723
|
+
epi_grid_shape = point_cloud.tiling_grid.shape
|
|
724
|
+
|
|
725
|
+
if (
|
|
726
|
+
0 <= id_x < epi_grid_shape[1]
|
|
727
|
+
and 0 <= id_y < epi_grid_shape[0]
|
|
728
|
+
):
|
|
729
|
+
required_point_clouds.append(
|
|
730
|
+
(point_cloud[id_y, id_x], pc_id)
|
|
731
|
+
)
|
|
732
|
+
list_indexes.append([id_y, id_x])
|
|
733
|
+
|
|
734
|
+
rank = col * col + row * row
|
|
735
|
+
|
|
736
|
+
return (
|
|
737
|
+
terrain_region,
|
|
738
|
+
required_point_clouds,
|
|
739
|
+
rank,
|
|
740
|
+
list_indexes,
|
|
741
|
+
)
|
|
742
|
+
|
|
743
|
+
|
|
744
|
+
def get_paired_regions_as_geodict(
|
|
745
|
+
terrain_regions: List, epipolar_regions: List
|
|
746
|
+
) -> Tuple[Dict, Dict]:
|
|
747
|
+
"""
|
|
748
|
+
Get paired regions (terrain/epipolar) as "geodictionnaries": these
|
|
749
|
+
objects can be dumped into geojson files to be visualized.
|
|
750
|
+
|
|
751
|
+
:param terrain_regions: terrain region respecting cars tiling
|
|
752
|
+
:param epipolar_regions: corresponding epipolar regions
|
|
753
|
+
|
|
754
|
+
:return: Terrain dictionary and Epipolar dictionary containing
|
|
755
|
+
respectively Terrain tiles in terrain projection and Epipolar tiles
|
|
756
|
+
in epipolar projection
|
|
757
|
+
"""
|
|
758
|
+
|
|
759
|
+
ter_geodict = {"type": "FeatureCollection", "features": []}
|
|
760
|
+
epi_geodict = {"type": "FeatureCollection", "features": []}
|
|
761
|
+
|
|
762
|
+
for idx, (ter, epi_list) in enumerate(
|
|
763
|
+
zip(terrain_regions, epipolar_regions) # noqa: B905
|
|
764
|
+
):
|
|
765
|
+
feature = {}
|
|
766
|
+
feature["type"] = "Feature"
|
|
767
|
+
feature["properties"] = {"id": idx, "nb_epi": len(epi_list)}
|
|
768
|
+
feature["geometry"] = mapping(box(*ter))
|
|
769
|
+
ter_geodict["features"].append(feature.copy())
|
|
770
|
+
feature["geometry"] = mapping(MultiPolygon(box(*x) for x in epi_list))
|
|
771
|
+
|
|
772
|
+
epi_geodict["features"].append(feature.copy())
|
|
773
|
+
|
|
774
|
+
return ter_geodict, epi_geodict
|