cars 1.0.0rc3__cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.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.
- cars/__init__.py +74 -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 +46 -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.cpython-313-x86_64-linux-gnu.so +0 -0
- cars/applications/dense_matching/cpp/dense_matching_cpp.py +94 -0
- cars/applications/dense_matching/cpp/includes/dense_matching.hpp +58 -0
- cars/applications/dense_matching/cpp/meson.build +9 -0
- cars/applications/dense_matching/cpp/src/bindings.cpp +13 -0
- cars/applications/dense_matching/cpp/src/dense_matching.cpp +207 -0
- cars/applications/dense_matching/dense_matching_algo.py +401 -0
- cars/applications/dense_matching/dense_matching_constants.py +89 -0
- cars/applications/dense_matching/dense_matching_wrappers.py +951 -0
- cars/applications/dense_matching/disparity_grid_algo.py +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 +1130 -0
- cars/core/geometry/shareloc_geometry.py +604 -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 +1095 -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 +190 -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 +435 -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.0rc3.dist-info/METADATA +289 -0
- cars-1.0.0rc3.dist-info/RECORD +220 -0
- cars-1.0.0rc3.dist-info/WHEEL +6 -0
- cars-1.0.0rc3.dist-info/entry_points.txt +8 -0
|
@@ -0,0 +1,951 @@
|
|
|
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
|
+
This module is responsible for the dense matching algorithms:
|
|
22
|
+
- thus it creates a disparity map from a pair of images
|
|
23
|
+
"""
|
|
24
|
+
# pylint: disable=C0302
|
|
25
|
+
|
|
26
|
+
# Standard imports
|
|
27
|
+
import logging
|
|
28
|
+
import math
|
|
29
|
+
import warnings
|
|
30
|
+
from typing import Dict
|
|
31
|
+
|
|
32
|
+
import numpy as np
|
|
33
|
+
import xarray as xr
|
|
34
|
+
|
|
35
|
+
# Third party imports
|
|
36
|
+
from pandora import constants as p_cst
|
|
37
|
+
from scipy.ndimage import generic_filter
|
|
38
|
+
|
|
39
|
+
from cars.applications.dense_match_filling import fill_disp_wrappers
|
|
40
|
+
|
|
41
|
+
# CARS imports
|
|
42
|
+
from cars.applications.dense_matching import (
|
|
43
|
+
dense_matching_constants as dense_match_cst,
|
|
44
|
+
)
|
|
45
|
+
from cars.conf import mask_cst as msk_cst
|
|
46
|
+
from cars.core import constants as cst
|
|
47
|
+
from cars.core import constants_disparity as cst_disp
|
|
48
|
+
|
|
49
|
+
from .cpp import dense_matching_cpp
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def get_margins(margin, disp_min, disp_max):
|
|
53
|
+
"""
|
|
54
|
+
Get margins for the dense matching steps
|
|
55
|
+
|
|
56
|
+
:param margin: margins object
|
|
57
|
+
:type margin: Margins
|
|
58
|
+
:param disp_min: Minimum disparity
|
|
59
|
+
:type disp_min: int
|
|
60
|
+
:param disp_max: Maximum disparity
|
|
61
|
+
:type disp_max: int
|
|
62
|
+
:return: margins of the matching algorithm used
|
|
63
|
+
"""
|
|
64
|
+
|
|
65
|
+
corner = ["left", "up", "right", "down"]
|
|
66
|
+
col = np.arange(len(corner))
|
|
67
|
+
|
|
68
|
+
left_margins = [
|
|
69
|
+
margin.left + disp_max,
|
|
70
|
+
margin.up,
|
|
71
|
+
margin.right - disp_min,
|
|
72
|
+
margin.down,
|
|
73
|
+
]
|
|
74
|
+
right_margins = [
|
|
75
|
+
margin.left - disp_min,
|
|
76
|
+
margin.up,
|
|
77
|
+
margin.right + disp_max,
|
|
78
|
+
margin.down,
|
|
79
|
+
]
|
|
80
|
+
same_margins = [
|
|
81
|
+
max(left, right)
|
|
82
|
+
for left, right in zip(left_margins, right_margins) # noqa: B905
|
|
83
|
+
]
|
|
84
|
+
|
|
85
|
+
margins = xr.Dataset(
|
|
86
|
+
{
|
|
87
|
+
"left_margin": (
|
|
88
|
+
["col"],
|
|
89
|
+
same_margins,
|
|
90
|
+
)
|
|
91
|
+
},
|
|
92
|
+
coords={"col": col},
|
|
93
|
+
)
|
|
94
|
+
margins["right_margin"] = xr.DataArray(same_margins, dims=["col"])
|
|
95
|
+
|
|
96
|
+
margins.attrs["disp_min"] = disp_min
|
|
97
|
+
margins.attrs["disp_max"] = disp_max
|
|
98
|
+
|
|
99
|
+
return margins
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
def get_masks_from_pandora(
|
|
103
|
+
disp: xr.Dataset,
|
|
104
|
+
compute_disparity_masks: bool,
|
|
105
|
+
filter_incomplete_disparity_range: bool,
|
|
106
|
+
) -> Dict[str, np.ndarray]:
|
|
107
|
+
"""
|
|
108
|
+
Get masks dictionary from the disparity map in output of pandora.
|
|
109
|
+
|
|
110
|
+
:param disp: disparity map (pandora output)
|
|
111
|
+
:param compute_disparity_masks: compute_disparity_masks activation status
|
|
112
|
+
:return: masks dictionary
|
|
113
|
+
"""
|
|
114
|
+
masks = {}
|
|
115
|
+
|
|
116
|
+
# Retrieve validity mask from pandora
|
|
117
|
+
# Invalid pixels in validity mask are:
|
|
118
|
+
# * Bit 0: Edge of the reference image or nodata in reference image
|
|
119
|
+
# * Bit 1: Disparity interval to explore is missing or nodata in the
|
|
120
|
+
# secondary image
|
|
121
|
+
# * Bit 6: Pixel is masked on the mask of the reference image
|
|
122
|
+
# * Bit 7: Disparity to explore is masked on the mask of the secondary
|
|
123
|
+
# image
|
|
124
|
+
# * Bit 8: Pixel located in an occlusion region
|
|
125
|
+
# * Bit 9: Fake match
|
|
126
|
+
validity_mask_cropped = disp.validity_mask.values
|
|
127
|
+
# Mask initialization to false (all is invalid)
|
|
128
|
+
masks[cst_disp.VALID] = np.full(validity_mask_cropped.shape, False)
|
|
129
|
+
invalid_value = p_cst.PANDORA_MSK_PIXEL_INVALID
|
|
130
|
+
if filter_incomplete_disparity_range:
|
|
131
|
+
invalid_value |= (
|
|
132
|
+
p_cst.PANDORA_MSK_PIXEL_INCOMPLETE_VARIABLE_DISPARITY_RANGE
|
|
133
|
+
)
|
|
134
|
+
|
|
135
|
+
# Identify valid points
|
|
136
|
+
masks[cst_disp.VALID][
|
|
137
|
+
np.where((validity_mask_cropped & invalid_value) == 0)
|
|
138
|
+
] = True
|
|
139
|
+
|
|
140
|
+
# With compute_disparity_masks, produce one mask for each invalid flag in
|
|
141
|
+
if compute_disparity_masks:
|
|
142
|
+
msk_table = dense_match_cst.MASK_HASH_TABLE
|
|
143
|
+
for key, val in msk_table.items():
|
|
144
|
+
masks[key] = np.full(validity_mask_cropped.shape, False)
|
|
145
|
+
masks[key][np.where((validity_mask_cropped & val) == 0)] = True
|
|
146
|
+
|
|
147
|
+
# Build final mask with 255 for valid points and 0 for invalid points
|
|
148
|
+
# The mask is used by rasterize method (non zero are valid points)
|
|
149
|
+
for key, mask in masks.items():
|
|
150
|
+
final_msk = np.ndarray(mask.shape, dtype=np.int16)
|
|
151
|
+
final_msk[mask] = 255
|
|
152
|
+
final_msk[np.equal(mask, False)] = 0
|
|
153
|
+
masks[key] = final_msk
|
|
154
|
+
|
|
155
|
+
return masks
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
def add_disparity_grids(
|
|
159
|
+
output_dataset: xr.Dataset,
|
|
160
|
+
disp_min_grid: np.ndarray = None,
|
|
161
|
+
disp_max_grid: np.ndarray = None,
|
|
162
|
+
):
|
|
163
|
+
"""
|
|
164
|
+
Add disparity min and max grids to dataset
|
|
165
|
+
|
|
166
|
+
:param output_dataset: output dataset
|
|
167
|
+
:param disp_min_grid: dense disparity map grid min
|
|
168
|
+
:param disp_max_grid: dense disparity map grid max
|
|
169
|
+
|
|
170
|
+
"""
|
|
171
|
+
if disp_min_grid is not None:
|
|
172
|
+
output_dataset[cst_disp.EPI_DISP_MIN_GRID] = xr.DataArray(
|
|
173
|
+
disp_min_grid,
|
|
174
|
+
dims=[cst.ROW, cst.COL],
|
|
175
|
+
)
|
|
176
|
+
|
|
177
|
+
# Add color mask
|
|
178
|
+
if disp_max_grid is not None:
|
|
179
|
+
output_dataset[cst_disp.EPI_DISP_MAX_GRID] = xr.DataArray(
|
|
180
|
+
disp_max_grid,
|
|
181
|
+
dims=[cst.ROW, cst.COL],
|
|
182
|
+
)
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
def add_texture(
|
|
186
|
+
output_dataset: xr.Dataset,
|
|
187
|
+
texture: np.ndarray = None,
|
|
188
|
+
color_type=None,
|
|
189
|
+
band_im: list = None,
|
|
190
|
+
texture_bands: list = None,
|
|
191
|
+
):
|
|
192
|
+
"""
|
|
193
|
+
Add image and image mask to dataset
|
|
194
|
+
|
|
195
|
+
:param output_dataset: output dataset
|
|
196
|
+
:param image: image array
|
|
197
|
+
:param image_type: data type of pixels
|
|
198
|
+
:param band_im: list of band names
|
|
199
|
+
|
|
200
|
+
"""
|
|
201
|
+
if texture_bands:
|
|
202
|
+
output_dataset.coords[cst.BAND_IM] = band_im[texture_bands]
|
|
203
|
+
output_dataset[cst.EPI_TEXTURE] = xr.DataArray(
|
|
204
|
+
texture[texture_bands],
|
|
205
|
+
dims=[cst.BAND_IM, cst.ROW, cst.COL],
|
|
206
|
+
)
|
|
207
|
+
|
|
208
|
+
if color_type is not None:
|
|
209
|
+
# Add input image type
|
|
210
|
+
output_dataset[cst.EPI_TEXTURE].attrs["color_type"] = color_type
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
def add_classification(
|
|
214
|
+
output_dataset: xr.Dataset,
|
|
215
|
+
classif: np.ndarray = None,
|
|
216
|
+
band_classif: list = None,
|
|
217
|
+
):
|
|
218
|
+
"""
|
|
219
|
+
Add classification to dataset
|
|
220
|
+
|
|
221
|
+
:param output_dataset: output dataset
|
|
222
|
+
:param classif: classif array
|
|
223
|
+
:param band_im: list of band names
|
|
224
|
+
|
|
225
|
+
"""
|
|
226
|
+
if classif is not None:
|
|
227
|
+
if (
|
|
228
|
+
band_classif is not None
|
|
229
|
+
and cst.BAND_CLASSIF not in output_dataset.dims
|
|
230
|
+
):
|
|
231
|
+
output_dataset.coords[cst.BAND_CLASSIF] = band_classif
|
|
232
|
+
output_dataset[cst.EPI_CLASSIFICATION] = xr.DataArray(
|
|
233
|
+
classif,
|
|
234
|
+
dims=[cst.BAND_CLASSIF, cst.ROW, cst.COL],
|
|
235
|
+
)
|
|
236
|
+
|
|
237
|
+
|
|
238
|
+
def compute_cropped_roi(
|
|
239
|
+
current_margins, margins_to_keep, tile_roi, nb_rows, nb_cols
|
|
240
|
+
):
|
|
241
|
+
"""
|
|
242
|
+
Compute cropped roi, with associated margins and
|
|
243
|
+
|
|
244
|
+
:param current_margins: current dataset margins
|
|
245
|
+
:type current_margins: list[int]
|
|
246
|
+
:param margins_to_keep: margin to keep
|
|
247
|
+
:type margins_to_keep: int
|
|
248
|
+
:param nb_rows: number of current rows
|
|
249
|
+
:type margins_to_keep: int
|
|
250
|
+
:param tile_roi: roi without margin of tile
|
|
251
|
+
:type tile_roi: list
|
|
252
|
+
:param nb_cols: number of current cols
|
|
253
|
+
:type nb_cols: int
|
|
254
|
+
|
|
255
|
+
:return: (borders to use as roi, new dataset roi with margin,
|
|
256
|
+
margin associated to roi)
|
|
257
|
+
:rtype: tuple(list, list, list)
|
|
258
|
+
"""
|
|
259
|
+
|
|
260
|
+
def cast_int_list(current_list):
|
|
261
|
+
"""
|
|
262
|
+
Apply int cast to list
|
|
263
|
+
"""
|
|
264
|
+
new_list = []
|
|
265
|
+
for obj in current_list:
|
|
266
|
+
new_list.append(int(obj))
|
|
267
|
+
return new_list
|
|
268
|
+
|
|
269
|
+
new_margin_neg = list(
|
|
270
|
+
np.clip(np.array(current_margins[0:2]), -margins_to_keep, 0)
|
|
271
|
+
)
|
|
272
|
+
new_margin_pos = list(
|
|
273
|
+
np.clip(np.array(current_margins[2:4]), 0, margins_to_keep)
|
|
274
|
+
)
|
|
275
|
+
new_margin = list(np.array(new_margin_neg + new_margin_pos).astype(int))
|
|
276
|
+
|
|
277
|
+
new_roi = list(np.array(tile_roi) + np.array(new_margin))
|
|
278
|
+
|
|
279
|
+
ref_roi = [
|
|
280
|
+
int(-current_margins[0] + new_margin[0]),
|
|
281
|
+
int(-current_margins[1] + new_margin[1]),
|
|
282
|
+
int(nb_cols - current_margins[2] + new_margin[2]),
|
|
283
|
+
int(nb_rows - current_margins[3] + new_margin[3]),
|
|
284
|
+
]
|
|
285
|
+
|
|
286
|
+
return (
|
|
287
|
+
cast_int_list(ref_roi),
|
|
288
|
+
cast_int_list(new_roi),
|
|
289
|
+
cast_int_list(new_margin),
|
|
290
|
+
)
|
|
291
|
+
|
|
292
|
+
|
|
293
|
+
# pylint: disable=too-many-positional-arguments
|
|
294
|
+
def create_disp_dataset( # noqa: C901
|
|
295
|
+
disp: xr.Dataset,
|
|
296
|
+
ref_dataset: xr.Dataset,
|
|
297
|
+
secondary_dataset: xr.Dataset,
|
|
298
|
+
compute_disparity_masks: bool = False,
|
|
299
|
+
disp_min_grid=None,
|
|
300
|
+
disp_max_grid=None,
|
|
301
|
+
cropped_range=None,
|
|
302
|
+
margins_to_keep=0,
|
|
303
|
+
classification_fusion_margin=-1,
|
|
304
|
+
texture_bands=None,
|
|
305
|
+
filter_incomplete_disparity_range=True,
|
|
306
|
+
) -> xr.Dataset:
|
|
307
|
+
"""
|
|
308
|
+
Create the disparity dataset.
|
|
309
|
+
|
|
310
|
+
:param disp: disparity map (result of pandora)
|
|
311
|
+
:param ref_dataset: reference dataset for the considered disparity map
|
|
312
|
+
:param secondary_dataset: secondary dataset for the considered
|
|
313
|
+
disparity map
|
|
314
|
+
:param compute_disparity_masks: compute_disparity_masks activation status
|
|
315
|
+
:param disp_min_grid: disparity min grid
|
|
316
|
+
:param disp_max_grid: disparity max grid
|
|
317
|
+
:param cropped_range: true if disparity range was cropped
|
|
318
|
+
:type cropped_range: numpy array
|
|
319
|
+
:param margins_to_keep: margin to keep after dense matching
|
|
320
|
+
:type margins_to_keep: int
|
|
321
|
+
|
|
322
|
+
:return: disparity dataset as used in cars
|
|
323
|
+
"""
|
|
324
|
+
|
|
325
|
+
# Crop disparity to ROI
|
|
326
|
+
ref_roi, new_roi, new_margin = compute_cropped_roi(
|
|
327
|
+
ref_dataset.attrs[cst.EPI_MARGINS],
|
|
328
|
+
margins_to_keep,
|
|
329
|
+
ref_dataset.attrs[cst.ROI],
|
|
330
|
+
ref_dataset.sizes[cst.ROW],
|
|
331
|
+
ref_dataset.sizes[cst.COL],
|
|
332
|
+
)
|
|
333
|
+
|
|
334
|
+
# Crop datasets
|
|
335
|
+
disp = disp.isel(
|
|
336
|
+
row=slice(ref_roi[1], ref_roi[3]), col=slice(ref_roi[0], ref_roi[2])
|
|
337
|
+
)
|
|
338
|
+
ref_dataset = ref_dataset.isel(
|
|
339
|
+
row=slice(ref_roi[1], ref_roi[3]), col=slice(ref_roi[0], ref_roi[2])
|
|
340
|
+
)
|
|
341
|
+
secondary_dataset = secondary_dataset.isel(
|
|
342
|
+
row=slice(ref_roi[1], ref_roi[3]), col=slice(ref_roi[0], ref_roi[2])
|
|
343
|
+
)
|
|
344
|
+
# Crop disparity min max grids
|
|
345
|
+
if disp_min_grid is not None:
|
|
346
|
+
disp_min_grid = disp_min_grid[
|
|
347
|
+
ref_roi[1] : ref_roi[3], ref_roi[0] : ref_roi[2]
|
|
348
|
+
]
|
|
349
|
+
if disp_max_grid is not None:
|
|
350
|
+
disp_max_grid = disp_max_grid[
|
|
351
|
+
ref_roi[1] : ref_roi[3], ref_roi[0] : ref_roi[2]
|
|
352
|
+
]
|
|
353
|
+
if cropped_range is not None:
|
|
354
|
+
cropped_range = cropped_range[
|
|
355
|
+
ref_roi[1] : ref_roi[3], ref_roi[0] : ref_roi[2]
|
|
356
|
+
]
|
|
357
|
+
|
|
358
|
+
# Retrieve disparity values
|
|
359
|
+
disp_map = disp.disparity_map.values
|
|
360
|
+
|
|
361
|
+
# Transform image to texture
|
|
362
|
+
epi_image = ref_dataset[cst.EPI_IMAGE].values
|
|
363
|
+
band_im = ref_dataset.coords[cst.BAND_IM]
|
|
364
|
+
image_type = ref_dataset.attrs.get("image_type", "float32")
|
|
365
|
+
if isinstance(image_type, list):
|
|
366
|
+
if texture_bands is not None:
|
|
367
|
+
image_type = image_type[texture_bands[0]]
|
|
368
|
+
else:
|
|
369
|
+
image_type = image_type[0]
|
|
370
|
+
# Cast image
|
|
371
|
+
if np.issubdtype(image_type, np.floating):
|
|
372
|
+
min_value_clr = np.finfo(image_type).min
|
|
373
|
+
max_value_clr = np.finfo(image_type).max
|
|
374
|
+
else:
|
|
375
|
+
min_value_clr = np.iinfo(image_type).min
|
|
376
|
+
max_value_clr = np.iinfo(image_type).max
|
|
377
|
+
epi_image = np.clip(epi_image, min_value_clr, max_value_clr).astype(
|
|
378
|
+
image_type
|
|
379
|
+
)
|
|
380
|
+
|
|
381
|
+
# Retrieve original mask of panchromatic image
|
|
382
|
+
epi_msk = None
|
|
383
|
+
if cst.EPI_MSK in ref_dataset:
|
|
384
|
+
epi_msk = ref_dataset[cst.EPI_MSK].values
|
|
385
|
+
|
|
386
|
+
epi_msk_right = None
|
|
387
|
+
if cst.EPI_MSK in secondary_dataset:
|
|
388
|
+
epi_msk_right = secondary_dataset[cst.EPI_MSK].values
|
|
389
|
+
|
|
390
|
+
# Retrieve masks from pandora
|
|
391
|
+
pandora_masks = get_masks_from_pandora(
|
|
392
|
+
disp, compute_disparity_masks, filter_incomplete_disparity_range
|
|
393
|
+
)
|
|
394
|
+
pandora_masks[cst_disp.VALID][np.isnan(disp_map)] = 0
|
|
395
|
+
|
|
396
|
+
# retrieve classif
|
|
397
|
+
left_classif = None
|
|
398
|
+
left_band_classif = None
|
|
399
|
+
if cst.EPI_CLASSIFICATION in ref_dataset:
|
|
400
|
+
left_classif = ref_dataset[cst.EPI_CLASSIFICATION].values
|
|
401
|
+
left_band_classif = ref_dataset.coords[cst.BAND_CLASSIF].values
|
|
402
|
+
|
|
403
|
+
# mask left classif outside right sensor
|
|
404
|
+
if epi_msk_right is not None:
|
|
405
|
+
left_classif = mask_left_classif_from_right_mask( # here
|
|
406
|
+
left_classif,
|
|
407
|
+
epi_msk_right == msk_cst.NO_DATA_IN_EPIPOLAR_RECTIFICATION,
|
|
408
|
+
np.floor(disp_min_grid).astype(np.int16),
|
|
409
|
+
np.ceil(disp_max_grid).astype(np.int16),
|
|
410
|
+
classification_fusion_margin,
|
|
411
|
+
)
|
|
412
|
+
|
|
413
|
+
left_from_right_classif = None
|
|
414
|
+
right_band_classif = None
|
|
415
|
+
if cst.EPI_CLASSIFICATION in secondary_dataset:
|
|
416
|
+
right_classif = secondary_dataset[cst.EPI_CLASSIFICATION].values
|
|
417
|
+
right_band_classif = secondary_dataset.coords[cst.BAND_CLASSIF].values
|
|
418
|
+
|
|
419
|
+
left_from_right_classif = estimate_right_classif_on_left(
|
|
420
|
+
right_classif,
|
|
421
|
+
disp_map,
|
|
422
|
+
pandora_masks[cst_disp.VALID],
|
|
423
|
+
int(np.floor(np.min(disp_min_grid))),
|
|
424
|
+
int(np.ceil(np.max(disp_max_grid))),
|
|
425
|
+
classification_fusion_margin,
|
|
426
|
+
)
|
|
427
|
+
|
|
428
|
+
# mask outside left sensor
|
|
429
|
+
left_mask = (
|
|
430
|
+
ref_dataset[cst.EPI_MSK].values
|
|
431
|
+
== msk_cst.NO_DATA_IN_EPIPOLAR_RECTIFICATION
|
|
432
|
+
)
|
|
433
|
+
left_mask_stacked = np.repeat(
|
|
434
|
+
np.expand_dims(left_mask, axis=0),
|
|
435
|
+
left_from_right_classif.shape[0],
|
|
436
|
+
axis=0,
|
|
437
|
+
)
|
|
438
|
+
# pylint: disable=unsupported-assignment-operation
|
|
439
|
+
left_from_right_classif[left_mask_stacked] = 0
|
|
440
|
+
# Merge right classif
|
|
441
|
+
classif, band_classif = merge_classif_left_right(
|
|
442
|
+
left_classif,
|
|
443
|
+
left_band_classif,
|
|
444
|
+
left_from_right_classif,
|
|
445
|
+
right_band_classif,
|
|
446
|
+
)
|
|
447
|
+
|
|
448
|
+
# Fill disparity array with 0 value for invalid points
|
|
449
|
+
disp_map[pandora_masks[cst_disp.VALID] == 0] = np.nan
|
|
450
|
+
|
|
451
|
+
# Build output dataset
|
|
452
|
+
row = np.array(range(new_roi[1], new_roi[3]))
|
|
453
|
+
col = np.array(range(new_roi[0], new_roi[2]))
|
|
454
|
+
|
|
455
|
+
disp_ds = xr.Dataset(
|
|
456
|
+
{
|
|
457
|
+
cst_disp.MAP: ([cst.ROW, cst.COL], np.copy(disp_map)),
|
|
458
|
+
cst_disp.VALID: (
|
|
459
|
+
[cst.ROW, cst.COL],
|
|
460
|
+
pandora_masks[cst_disp.VALID].astype("uint8"),
|
|
461
|
+
),
|
|
462
|
+
},
|
|
463
|
+
coords={cst.ROW: row, cst.COL: col},
|
|
464
|
+
)
|
|
465
|
+
|
|
466
|
+
# add left texture
|
|
467
|
+
add_texture(
|
|
468
|
+
disp_ds,
|
|
469
|
+
texture=epi_image,
|
|
470
|
+
color_type=image_type,
|
|
471
|
+
band_im=band_im,
|
|
472
|
+
texture_bands=texture_bands,
|
|
473
|
+
)
|
|
474
|
+
|
|
475
|
+
# add original mask
|
|
476
|
+
if epi_msk is not None:
|
|
477
|
+
disp_ds[cst.EPI_MSK] = xr.DataArray(
|
|
478
|
+
epi_msk.astype("uint8"), dims=[cst.ROW, cst.COL]
|
|
479
|
+
)
|
|
480
|
+
|
|
481
|
+
# add confidence
|
|
482
|
+
if "confidence_measure" in disp:
|
|
483
|
+
add_confidence(disp_ds, disp)
|
|
484
|
+
|
|
485
|
+
# add classif
|
|
486
|
+
add_classification(disp_ds, classif=classif, band_classif=band_classif)
|
|
487
|
+
|
|
488
|
+
# Add disparity grids
|
|
489
|
+
add_disparity_grids(
|
|
490
|
+
disp_ds, disp_min_grid=disp_min_grid, disp_max_grid=disp_max_grid
|
|
491
|
+
)
|
|
492
|
+
|
|
493
|
+
# Add filling infos
|
|
494
|
+
if cropped_range is not None:
|
|
495
|
+
disp_ds = add_crop_info(disp_ds, cropped_range)
|
|
496
|
+
|
|
497
|
+
if compute_disparity_masks:
|
|
498
|
+
for key, val in pandora_masks.items():
|
|
499
|
+
disp_ds[key] = xr.DataArray(np.copy(val), dims=[cst.ROW, cst.COL])
|
|
500
|
+
|
|
501
|
+
disp_ds.attrs[cst.ROI] = ref_dataset.attrs[cst.ROI]
|
|
502
|
+
disp_ds.attrs[cst.ROI_WITH_MARGINS] = new_roi
|
|
503
|
+
disp_ds.attrs[cst.EPI_MARGINS] = new_margin
|
|
504
|
+
|
|
505
|
+
disp_ds.attrs[cst.EPI_FULL_SIZE] = ref_dataset.attrs[cst.EPI_FULL_SIZE]
|
|
506
|
+
|
|
507
|
+
return disp_ds
|
|
508
|
+
|
|
509
|
+
|
|
510
|
+
def add_crop_info(disp_ds, cropped_range):
|
|
511
|
+
"""
|
|
512
|
+
Add crop info
|
|
513
|
+
|
|
514
|
+
:param disp: disp xarray
|
|
515
|
+
:param cropped_range: was cropped range, bool
|
|
516
|
+
|
|
517
|
+
:return updated dataset
|
|
518
|
+
"""
|
|
519
|
+
|
|
520
|
+
disp_ds = fill_disp_wrappers.add_empty_filling_band(
|
|
521
|
+
disp_ds, ["cropped_disp_range"]
|
|
522
|
+
)
|
|
523
|
+
fill_disp_wrappers.update_filling(
|
|
524
|
+
disp_ds, cropped_range, "cropped_disp_range"
|
|
525
|
+
)
|
|
526
|
+
return disp_ds
|
|
527
|
+
|
|
528
|
+
|
|
529
|
+
# pylint: disable=too-many-positional-arguments
|
|
530
|
+
def estimate_right_classif_on_left(
|
|
531
|
+
right_classif,
|
|
532
|
+
disp_map,
|
|
533
|
+
disp_mask,
|
|
534
|
+
disp_min,
|
|
535
|
+
disp_max,
|
|
536
|
+
classifiation_fusion_margin,
|
|
537
|
+
):
|
|
538
|
+
"""
|
|
539
|
+
Estimate right classif on left image
|
|
540
|
+
|
|
541
|
+
:param right_classif: right classification
|
|
542
|
+
:type right_classif: np ndarray
|
|
543
|
+
:param disp_map: disparity map
|
|
544
|
+
:type disp_map: np ndarray
|
|
545
|
+
:param disp_mask: disparity mask
|
|
546
|
+
:type disp_mask: np ndarray
|
|
547
|
+
:param disp_min: disparity min
|
|
548
|
+
:type disp_min: int
|
|
549
|
+
:param disp_max: disparity max
|
|
550
|
+
:type disp_max: int
|
|
551
|
+
|
|
552
|
+
:return: right classif on left image
|
|
553
|
+
:rtype: np nadarray
|
|
554
|
+
"""
|
|
555
|
+
return dense_matching_cpp.estimate_right_classif_on_left(
|
|
556
|
+
right_classif,
|
|
557
|
+
disp_map,
|
|
558
|
+
disp_mask,
|
|
559
|
+
disp_min,
|
|
560
|
+
disp_max,
|
|
561
|
+
classifiation_fusion_margin,
|
|
562
|
+
)
|
|
563
|
+
|
|
564
|
+
|
|
565
|
+
def mask_left_classif_from_right_mask(
|
|
566
|
+
left_classif,
|
|
567
|
+
right_mask,
|
|
568
|
+
disp_min,
|
|
569
|
+
disp_max,
|
|
570
|
+
classifiation_fusion_margin,
|
|
571
|
+
):
|
|
572
|
+
"""
|
|
573
|
+
Mask left classif with right mask.
|
|
574
|
+
|
|
575
|
+
:param left_classif: right classification
|
|
576
|
+
:type left_classif: np ndarray
|
|
577
|
+
:param right_mask: right mask
|
|
578
|
+
:type right_mask: np ndarray
|
|
579
|
+
:param disp_min: disparity min
|
|
580
|
+
:type disp_min: np.array type int
|
|
581
|
+
:param disp_max: disparity max
|
|
582
|
+
:type disp_max: np.array type int
|
|
583
|
+
|
|
584
|
+
:return: masked left classif
|
|
585
|
+
:rtype: np nadarray
|
|
586
|
+
"""
|
|
587
|
+
return dense_matching_cpp.mask_left_classif_from_right_mask(
|
|
588
|
+
left_classif,
|
|
589
|
+
right_mask,
|
|
590
|
+
disp_min,
|
|
591
|
+
disp_max,
|
|
592
|
+
classifiation_fusion_margin,
|
|
593
|
+
)
|
|
594
|
+
|
|
595
|
+
|
|
596
|
+
def merge_classif_left_right(
|
|
597
|
+
left_classif, left_band_classif, right_classif, right_band_classif
|
|
598
|
+
):
|
|
599
|
+
"""
|
|
600
|
+
Merge left and right classif
|
|
601
|
+
|
|
602
|
+
:param left_classif: left classif
|
|
603
|
+
:type left_classif: np nadarray
|
|
604
|
+
:param left_band_classif: list of tag
|
|
605
|
+
:type left_band_classif: list
|
|
606
|
+
:param right_classif: left classif
|
|
607
|
+
:type right_classif: np nadarray
|
|
608
|
+
:param right_band_classif: list of tag
|
|
609
|
+
:type right_band_classif: list
|
|
610
|
+
|
|
611
|
+
:return: merged classif, merged tag list
|
|
612
|
+
:rtype: np ndarray, list
|
|
613
|
+
|
|
614
|
+
"""
|
|
615
|
+
|
|
616
|
+
classif = None
|
|
617
|
+
band_classif = None
|
|
618
|
+
|
|
619
|
+
if left_classif is None and right_classif is not None:
|
|
620
|
+
classif = right_classif
|
|
621
|
+
band_classif = right_band_classif
|
|
622
|
+
|
|
623
|
+
elif left_classif is not None and right_classif is None:
|
|
624
|
+
classif = left_classif
|
|
625
|
+
band_classif = left_band_classif
|
|
626
|
+
|
|
627
|
+
elif left_classif is not None and right_classif is not None:
|
|
628
|
+
list_classif = []
|
|
629
|
+
band_classif = []
|
|
630
|
+
seen_tag = []
|
|
631
|
+
|
|
632
|
+
# search in left
|
|
633
|
+
for ind_left, tag_left in enumerate(left_band_classif):
|
|
634
|
+
seen_tag.append(tag_left)
|
|
635
|
+
band_classif.append(tag_left)
|
|
636
|
+
if tag_left in right_band_classif:
|
|
637
|
+
# merge left and right
|
|
638
|
+
ind_right = list(right_band_classif).index(tag_left)
|
|
639
|
+
list_classif.append(
|
|
640
|
+
np.logical_or(
|
|
641
|
+
left_classif[ind_left, :, :],
|
|
642
|
+
right_classif[ind_right, :, :],
|
|
643
|
+
)
|
|
644
|
+
)
|
|
645
|
+
else:
|
|
646
|
+
list_classif.append(left_classif[ind_left, :, :])
|
|
647
|
+
|
|
648
|
+
# search in right not already seen
|
|
649
|
+
for ind_right, tag_right in enumerate(right_band_classif):
|
|
650
|
+
if tag_right not in seen_tag:
|
|
651
|
+
band_classif.append(tag_right)
|
|
652
|
+
list_classif.append(right_classif[ind_right, :, :])
|
|
653
|
+
|
|
654
|
+
# Stack classif
|
|
655
|
+
classif = np.stack(list_classif, axis=0)
|
|
656
|
+
|
|
657
|
+
return classif, band_classif
|
|
658
|
+
|
|
659
|
+
|
|
660
|
+
def add_confidence(
|
|
661
|
+
output_dataset: xr.Dataset,
|
|
662
|
+
disp: xr.Dataset,
|
|
663
|
+
):
|
|
664
|
+
"""
|
|
665
|
+
Add confidences to dataset
|
|
666
|
+
|
|
667
|
+
:param output_dataset: output dataset
|
|
668
|
+
:param disp: disp xarray
|
|
669
|
+
|
|
670
|
+
"""
|
|
671
|
+
confidence_measure_indicator_list = np.array(
|
|
672
|
+
disp.confidence_measure.indicator
|
|
673
|
+
)
|
|
674
|
+
for key in confidence_measure_indicator_list:
|
|
675
|
+
if key == "confidence_from_ambiguity.cars_1":
|
|
676
|
+
confidence_idx = list(disp.confidence_measure.indicator).index(key)
|
|
677
|
+
|
|
678
|
+
output_dataset["ambiguity"] = xr.DataArray(
|
|
679
|
+
1
|
|
680
|
+
- np.copy(
|
|
681
|
+
disp.confidence_measure.data[
|
|
682
|
+
:,
|
|
683
|
+
:,
|
|
684
|
+
confidence_idx,
|
|
685
|
+
]
|
|
686
|
+
),
|
|
687
|
+
dims=[cst.ROW, cst.COL],
|
|
688
|
+
)
|
|
689
|
+
else:
|
|
690
|
+
confidence_idx = list(disp.confidence_measure.indicator).index(key)
|
|
691
|
+
output_dataset[str(key)] = xr.DataArray(
|
|
692
|
+
np.copy(
|
|
693
|
+
disp.confidence_measure.data[
|
|
694
|
+
:,
|
|
695
|
+
:,
|
|
696
|
+
confidence_idx,
|
|
697
|
+
]
|
|
698
|
+
),
|
|
699
|
+
dims=[cst.ROW, cst.COL],
|
|
700
|
+
)
|
|
701
|
+
|
|
702
|
+
|
|
703
|
+
def to_safe_disp_grid(grid_disp_min, grid_disp_max):
|
|
704
|
+
"""
|
|
705
|
+
Generate safe grids, with min < max for each point
|
|
706
|
+
|
|
707
|
+
:param grid_disp_min: min disp grid
|
|
708
|
+
:param grid_disp_max: max disp grid
|
|
709
|
+
|
|
710
|
+
:return: grid_disp_min, grid_disp_max
|
|
711
|
+
"""
|
|
712
|
+
|
|
713
|
+
stacked_disp_range = np.dstack([grid_disp_min, grid_disp_max])
|
|
714
|
+
grid_disp_min = np.nanmin(stacked_disp_range, axis=2)
|
|
715
|
+
grid_disp_max = np.nanmax(stacked_disp_range, axis=2)
|
|
716
|
+
|
|
717
|
+
# convert nan
|
|
718
|
+
grid_disp_min[np.isnan(grid_disp_min)] = 0
|
|
719
|
+
grid_disp_max[np.isnan(grid_disp_max)] = 0
|
|
720
|
+
|
|
721
|
+
if (grid_disp_min > grid_disp_max).any():
|
|
722
|
+
raise RuntimeError("grid min > max")
|
|
723
|
+
|
|
724
|
+
return grid_disp_min, grid_disp_max
|
|
725
|
+
|
|
726
|
+
|
|
727
|
+
def estimate_right_grid_disp(disp_min_grid, disp_max_grid):
|
|
728
|
+
"""
|
|
729
|
+
Estimate right grid min and max.
|
|
730
|
+
Correspond to the range of pixels that can be correlated
|
|
731
|
+
from left -> right.
|
|
732
|
+
If no left pixels can be associated to right, use global values
|
|
733
|
+
|
|
734
|
+
:param disp_min_grid: left disp min grid
|
|
735
|
+
:type disp_min_grid: numpy ndarray
|
|
736
|
+
:param disp_max_grid: left disp max grid
|
|
737
|
+
:type disp_max_grid: numpy ndarray
|
|
738
|
+
|
|
739
|
+
:return: disp_min_right_grid, disp_max_right_grid
|
|
740
|
+
:rtype: numpy ndarray, numpy ndarray
|
|
741
|
+
"""
|
|
742
|
+
float_types = [np.float16, np.float32, np.float64, np.float128]
|
|
743
|
+
int_types = [
|
|
744
|
+
int,
|
|
745
|
+
np.int8,
|
|
746
|
+
np.uint8,
|
|
747
|
+
np.int16,
|
|
748
|
+
np.uint16,
|
|
749
|
+
np.int32,
|
|
750
|
+
np.uint32,
|
|
751
|
+
np.int64,
|
|
752
|
+
np.uint64,
|
|
753
|
+
]
|
|
754
|
+
if disp_min_grid.dtype in float_types:
|
|
755
|
+
return dense_matching_cpp.estimate_right_grid_disp_float(
|
|
756
|
+
disp_min_grid, disp_max_grid
|
|
757
|
+
)
|
|
758
|
+
if disp_min_grid.dtype in int_types:
|
|
759
|
+
return dense_matching_cpp.estimate_right_grid_disp_int(
|
|
760
|
+
disp_min_grid, disp_max_grid
|
|
761
|
+
)
|
|
762
|
+
|
|
763
|
+
raise TypeError(
|
|
764
|
+
"estimate_right_grid_disp does not support"
|
|
765
|
+
f"{disp_min_grid.dtype} as an input type"
|
|
766
|
+
)
|
|
767
|
+
|
|
768
|
+
|
|
769
|
+
# pylint: disable=too-many-positional-arguments
|
|
770
|
+
def optimal_tile_size_pandora_plugin_libsgm(
|
|
771
|
+
disp_min: int,
|
|
772
|
+
disp_max: int,
|
|
773
|
+
min_tile_size: int,
|
|
774
|
+
max_tile_size: int,
|
|
775
|
+
max_ram_per_worker: int,
|
|
776
|
+
tile_size_rounding: int = 50,
|
|
777
|
+
margin: int = 0,
|
|
778
|
+
) -> int:
|
|
779
|
+
"""
|
|
780
|
+
Compute optimal tile size according to estimated memory usage
|
|
781
|
+
(pandora_plugin_libsgm)
|
|
782
|
+
Returned optimal tile size will be at least equal to tile_size_rounding.
|
|
783
|
+
|
|
784
|
+
:param disp_min: Minimum disparity to explore
|
|
785
|
+
:param disp_max: Maximum disparity to explore
|
|
786
|
+
:param min_tile_size: Minimal tile size
|
|
787
|
+
:param max_tile_size: Maximal tile size
|
|
788
|
+
:param max_ram_per_worker: amount of RAM allocated per worker
|
|
789
|
+
:param tile_size_rounding: Optimal tile size will be aligned to multiples\
|
|
790
|
+
of tile_size_rounding
|
|
791
|
+
:param margin: margin to remove to the computed tile size
|
|
792
|
+
(as a percent of the computed tile size)
|
|
793
|
+
:returns: Optimal tile size according to benchmarked memory usage
|
|
794
|
+
"""
|
|
795
|
+
|
|
796
|
+
memory = max_ram_per_worker
|
|
797
|
+
disp = max(3, abs(disp_max - disp_min))
|
|
798
|
+
|
|
799
|
+
image = 32 * 2
|
|
800
|
+
disp_ref = 32
|
|
801
|
+
validity_mask_ref = 16
|
|
802
|
+
confidence = 32
|
|
803
|
+
cv_ = disp * 32
|
|
804
|
+
nan_ = disp * 8
|
|
805
|
+
cv_uint = disp * 8
|
|
806
|
+
penal = 8 * 32 * 2
|
|
807
|
+
img_crop = 32 * 2
|
|
808
|
+
|
|
809
|
+
tot = image + disp_ref + validity_mask_ref
|
|
810
|
+
tot += confidence + 2 * cv_ + nan_ + cv_uint + penal + img_crop
|
|
811
|
+
import_ = 200 # MiB
|
|
812
|
+
|
|
813
|
+
row_or_col = float(((memory - import_) * 2**23)) / tot
|
|
814
|
+
|
|
815
|
+
if row_or_col <= 0:
|
|
816
|
+
logging.warning(
|
|
817
|
+
"Optimal tile size is null, "
|
|
818
|
+
"forcing it to {} pixels".format(tile_size_rounding)
|
|
819
|
+
)
|
|
820
|
+
tile_size = tile_size_rounding
|
|
821
|
+
else:
|
|
822
|
+
# nb_pixels = tile_size x (tile_size + disp)
|
|
823
|
+
# hence tile_size not equal to sqrt(nb_pixels)
|
|
824
|
+
# but sqrt(nb_pixels + (disp/2)**2) - disp/2
|
|
825
|
+
tile_size = np.sqrt(row_or_col + (disp / 2) ** 2) - disp / 2
|
|
826
|
+
tile_size = (1.0 - margin / 100.0) * tile_size
|
|
827
|
+
|
|
828
|
+
if math.isinf(tile_size):
|
|
829
|
+
logging.warning("Tile size infinite")
|
|
830
|
+
|
|
831
|
+
if tile_size > max_tile_size:
|
|
832
|
+
tile_size = max_tile_size
|
|
833
|
+
elif tile_size < min_tile_size:
|
|
834
|
+
tile_size = min_tile_size
|
|
835
|
+
|
|
836
|
+
tile_size = tile_size_rounding * int(tile_size / tile_size_rounding)
|
|
837
|
+
|
|
838
|
+
return tile_size
|
|
839
|
+
|
|
840
|
+
|
|
841
|
+
def get_max_disp_from_opt_tile_size(
|
|
842
|
+
opt_epipolar_tile_size, max_ram_per_worker, margin=0, used_disparity_range=0
|
|
843
|
+
):
|
|
844
|
+
"""
|
|
845
|
+
Compute max range possible depending on max ram per worker
|
|
846
|
+
Return max range usable
|
|
847
|
+
|
|
848
|
+
:param opt_epipolar_tile_size: used tile size
|
|
849
|
+
:param max_ram_per_worker: amount of RAM allocated per worker
|
|
850
|
+
:param tile_size_rounding: Optimal tile size will be aligned to multiples\
|
|
851
|
+
of tile_size_rounding
|
|
852
|
+
:param margin: margin to remove to the computed tile size
|
|
853
|
+
(as a percent of the computed tile size)
|
|
854
|
+
:returns: max disp range to use
|
|
855
|
+
"""
|
|
856
|
+
|
|
857
|
+
import_ = 200
|
|
858
|
+
memory = max_ram_per_worker
|
|
859
|
+
|
|
860
|
+
# not depending on disp
|
|
861
|
+
image = 32 * 2
|
|
862
|
+
disp_ref = 32
|
|
863
|
+
validity_mask_ref = 16
|
|
864
|
+
confidence = 32
|
|
865
|
+
penal = 8 * 32 * 2
|
|
866
|
+
img_crop = 32 * 2
|
|
867
|
+
# depending on disp : data * disp
|
|
868
|
+
cv_ = 32
|
|
869
|
+
nan_ = 8
|
|
870
|
+
cv_uint = 8
|
|
871
|
+
|
|
872
|
+
row = opt_epipolar_tile_size / (1.0 - margin / 100.0)
|
|
873
|
+
col = row + used_disparity_range
|
|
874
|
+
row_or_col = row * col
|
|
875
|
+
tot = float(((memory - import_) * 2**23)) / row_or_col
|
|
876
|
+
|
|
877
|
+
disp_tot = tot - (
|
|
878
|
+
image + disp_ref + validity_mask_ref + confidence + penal + img_crop
|
|
879
|
+
)
|
|
880
|
+
# disp tot = disp x (2 x cv_ + nan_ + cv uint)
|
|
881
|
+
max_range = int(disp_tot / (2 * cv_ + nan_ + cv_uint) + 1)
|
|
882
|
+
|
|
883
|
+
return max_range
|
|
884
|
+
|
|
885
|
+
|
|
886
|
+
def nan_ratio_func(window):
|
|
887
|
+
""" "
|
|
888
|
+
Calculate the number of nan in the window
|
|
889
|
+
|
|
890
|
+
:param window: the window in the image
|
|
891
|
+
"""
|
|
892
|
+
|
|
893
|
+
total_pixels = window.size
|
|
894
|
+
nan_count = np.isnan(window).sum()
|
|
895
|
+
return nan_count / total_pixels
|
|
896
|
+
|
|
897
|
+
|
|
898
|
+
def confidence_filtering(
|
|
899
|
+
dataset,
|
|
900
|
+
requested_confidence,
|
|
901
|
+
conf_filtering,
|
|
902
|
+
):
|
|
903
|
+
"""
|
|
904
|
+
Filter the disparity map by using the confidence
|
|
905
|
+
|
|
906
|
+
:param dataset: the epipolar disparity map dataset
|
|
907
|
+
:type dataset: cars dataset
|
|
908
|
+
:param disp_map: the disparity map
|
|
909
|
+
:type disp_map: numpy darray
|
|
910
|
+
:param requested_confidence: the confidence to use
|
|
911
|
+
:type requested_confidence: list
|
|
912
|
+
:param conf_filtering: the confidence_filtering parameters
|
|
913
|
+
:type conf_filtering: dict
|
|
914
|
+
"""
|
|
915
|
+
|
|
916
|
+
data_risk_inf = dataset[requested_confidence[0]].values
|
|
917
|
+
data_risk_sup = dataset[requested_confidence[1]].values
|
|
918
|
+
risk_range = data_risk_sup - data_risk_inf
|
|
919
|
+
|
|
920
|
+
data_bounds_inf = dataset[requested_confidence[2]].values
|
|
921
|
+
data_bounds_sup = dataset[requested_confidence[3]].values
|
|
922
|
+
bounds_range = data_bounds_sup - data_bounds_inf
|
|
923
|
+
|
|
924
|
+
disp_min = dataset["disp_min_grid"].values
|
|
925
|
+
disp_max = dataset["disp_max_grid"].values
|
|
926
|
+
|
|
927
|
+
risk_ratio = risk_range / (disp_max - disp_min)
|
|
928
|
+
bounds_ratio = bounds_range / (disp_max - disp_min)
|
|
929
|
+
|
|
930
|
+
bounds_mask = (bounds_ratio > conf_filtering["bounds_ratio_threshold"]) & (
|
|
931
|
+
bounds_range > conf_filtering["bounds_range_threshold"]
|
|
932
|
+
)
|
|
933
|
+
risk_mask = (risk_ratio > conf_filtering["risk_ratio_threshold"]) & (
|
|
934
|
+
risk_range > conf_filtering["risk_range_threshold"]
|
|
935
|
+
)
|
|
936
|
+
mask = bounds_mask | risk_mask
|
|
937
|
+
dataset["disp"].values[mask] = np.nan
|
|
938
|
+
dataset["disp_msk"].values[mask] = 0
|
|
939
|
+
|
|
940
|
+
with warnings.catch_warnings():
|
|
941
|
+
warnings.simplefilter("ignore", category=RuntimeWarning)
|
|
942
|
+
nan_ratio = generic_filter(
|
|
943
|
+
dataset["disp"], nan_ratio_func, size=conf_filtering["win_nanratio"]
|
|
944
|
+
)
|
|
945
|
+
|
|
946
|
+
mask = (nan_ratio > conf_filtering["nan_threshold"]) & (
|
|
947
|
+
(bounds_range > conf_filtering["bounds_range_threshold"])
|
|
948
|
+
| (risk_range > conf_filtering["risk_range_threshold"])
|
|
949
|
+
)
|
|
950
|
+
dataset["disp"].values[mask] = np.nan
|
|
951
|
+
dataset["disp_msk"].values[mask] = 0
|