cars 1.0.0rc1__cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of cars might be problematic. Click here for more details.
- cars/__init__.py +74 -0
- cars/applications/__init__.py +37 -0
- cars/applications/application.py +117 -0
- cars/applications/application_constants.py +29 -0
- cars/applications/application_template.py +146 -0
- cars/applications/auxiliary_filling/__init__.py +29 -0
- cars/applications/auxiliary_filling/abstract_auxiliary_filling_app.py +104 -0
- cars/applications/auxiliary_filling/auxiliary_filling_algo.py +475 -0
- cars/applications/auxiliary_filling/auxiliary_filling_from_sensors_app.py +630 -0
- cars/applications/auxiliary_filling/auxiliary_filling_wrappers.py +90 -0
- cars/applications/dem_generation/__init__.py +30 -0
- cars/applications/dem_generation/abstract_dem_generation_app.py +116 -0
- cars/applications/dem_generation/bulldozer_config/base_config.yaml +42 -0
- cars/applications/dem_generation/bulldozer_dem_app.py +655 -0
- cars/applications/dem_generation/bulldozer_memory.py +55 -0
- cars/applications/dem_generation/dem_generation_algo.py +107 -0
- cars/applications/dem_generation/dem_generation_constants.py +32 -0
- cars/applications/dem_generation/dem_generation_wrappers.py +323 -0
- cars/applications/dense_match_filling/__init__.py +30 -0
- cars/applications/dense_match_filling/abstract_dense_match_filling_app.py +242 -0
- cars/applications/dense_match_filling/fill_disp_algo.py +113 -0
- cars/applications/dense_match_filling/fill_disp_constants.py +39 -0
- cars/applications/dense_match_filling/fill_disp_wrappers.py +83 -0
- cars/applications/dense_match_filling/zero_padding_app.py +302 -0
- cars/applications/dense_matching/__init__.py +30 -0
- cars/applications/dense_matching/abstract_dense_matching_app.py +261 -0
- cars/applications/dense_matching/census_mccnn_sgm_app.py +1460 -0
- cars/applications/dense_matching/cpp/__init__.py +0 -0
- cars/applications/dense_matching/cpp/dense_matching_cpp.cpython-312-i386-linux-gnu.so +0 -0
- cars/applications/dense_matching/cpp/dense_matching_cpp.py +94 -0
- cars/applications/dense_matching/cpp/includes/dense_matching.hpp +58 -0
- cars/applications/dense_matching/cpp/meson.build +9 -0
- cars/applications/dense_matching/cpp/src/bindings.cpp +13 -0
- cars/applications/dense_matching/cpp/src/dense_matching.cpp +207 -0
- cars/applications/dense_matching/dense_matching_algo.py +401 -0
- cars/applications/dense_matching/dense_matching_constants.py +89 -0
- cars/applications/dense_matching/dense_matching_wrappers.py +951 -0
- cars/applications/dense_matching/disparity_grid_algo.py +588 -0
- cars/applications/dense_matching/loaders/__init__.py +23 -0
- cars/applications/dense_matching/loaders/config_census_sgm_default.json +31 -0
- cars/applications/dense_matching/loaders/config_census_sgm_homogeneous.json +30 -0
- cars/applications/dense_matching/loaders/config_census_sgm_mountain_and_vegetation.json +30 -0
- cars/applications/dense_matching/loaders/config_census_sgm_shadow.json +30 -0
- cars/applications/dense_matching/loaders/config_census_sgm_sparse.json +36 -0
- cars/applications/dense_matching/loaders/config_census_sgm_urban.json +30 -0
- cars/applications/dense_matching/loaders/config_mapping.json +13 -0
- cars/applications/dense_matching/loaders/config_mccnn.json +28 -0
- cars/applications/dense_matching/loaders/global_land_cover_map.tif +0 -0
- cars/applications/dense_matching/loaders/pandora_loader.py +593 -0
- cars/applications/dsm_filling/__init__.py +32 -0
- cars/applications/dsm_filling/abstract_dsm_filling_app.py +101 -0
- cars/applications/dsm_filling/border_interpolation_app.py +270 -0
- cars/applications/dsm_filling/bulldozer_config/base_config.yaml +44 -0
- cars/applications/dsm_filling/bulldozer_filling_app.py +279 -0
- cars/applications/dsm_filling/exogenous_filling_app.py +333 -0
- cars/applications/grid_generation/__init__.py +30 -0
- cars/applications/grid_generation/abstract_grid_generation_app.py +142 -0
- cars/applications/grid_generation/epipolar_grid_generation_app.py +327 -0
- cars/applications/grid_generation/grid_correction_app.py +496 -0
- cars/applications/grid_generation/grid_generation_algo.py +388 -0
- cars/applications/grid_generation/grid_generation_constants.py +46 -0
- cars/applications/grid_generation/transform_grid.py +88 -0
- cars/applications/ground_truth_reprojection/__init__.py +30 -0
- cars/applications/ground_truth_reprojection/abstract_ground_truth_reprojection_app.py +137 -0
- cars/applications/ground_truth_reprojection/direct_localization_app.py +629 -0
- cars/applications/ground_truth_reprojection/ground_truth_reprojection_algo.py +275 -0
- cars/applications/point_cloud_outlier_removal/__init__.py +30 -0
- cars/applications/point_cloud_outlier_removal/abstract_outlier_removal_app.py +385 -0
- cars/applications/point_cloud_outlier_removal/outlier_removal_algo.py +392 -0
- cars/applications/point_cloud_outlier_removal/outlier_removal_constants.py +43 -0
- cars/applications/point_cloud_outlier_removal/small_components_app.py +527 -0
- cars/applications/point_cloud_outlier_removal/statistical_app.py +531 -0
- cars/applications/rasterization/__init__.py +30 -0
- cars/applications/rasterization/abstract_pc_rasterization_app.py +183 -0
- cars/applications/rasterization/rasterization_algo.py +534 -0
- cars/applications/rasterization/rasterization_constants.py +38 -0
- cars/applications/rasterization/rasterization_wrappers.py +634 -0
- cars/applications/rasterization/simple_gaussian_app.py +1152 -0
- cars/applications/resampling/__init__.py +28 -0
- cars/applications/resampling/abstract_resampling_app.py +187 -0
- cars/applications/resampling/bicubic_resampling_app.py +762 -0
- cars/applications/resampling/resampling_algo.py +614 -0
- cars/applications/resampling/resampling_constants.py +36 -0
- cars/applications/resampling/resampling_wrappers.py +309 -0
- cars/applications/sparse_matching/__init__.py +30 -0
- cars/applications/sparse_matching/abstract_sparse_matching_app.py +498 -0
- cars/applications/sparse_matching/sift_app.py +735 -0
- cars/applications/sparse_matching/sparse_matching_algo.py +360 -0
- cars/applications/sparse_matching/sparse_matching_constants.py +68 -0
- cars/applications/sparse_matching/sparse_matching_wrappers.py +238 -0
- cars/applications/triangulation/__init__.py +32 -0
- cars/applications/triangulation/abstract_triangulation_app.py +227 -0
- cars/applications/triangulation/line_of_sight_intersection_app.py +1243 -0
- cars/applications/triangulation/pc_transform.py +552 -0
- cars/applications/triangulation/triangulation_algo.py +371 -0
- cars/applications/triangulation/triangulation_constants.py +38 -0
- cars/applications/triangulation/triangulation_wrappers.py +259 -0
- cars/bundleadjustment.py +757 -0
- cars/cars.py +177 -0
- cars/conf/__init__.py +23 -0
- cars/conf/geoid/egm96.grd +0 -0
- cars/conf/geoid/egm96.grd.hdr +15 -0
- cars/conf/input_parameters.py +156 -0
- cars/conf/mask_cst.py +35 -0
- cars/core/__init__.py +23 -0
- cars/core/cars_logging.py +402 -0
- cars/core/constants.py +191 -0
- cars/core/constants_disparity.py +50 -0
- cars/core/datasets.py +140 -0
- cars/core/geometry/__init__.py +27 -0
- cars/core/geometry/abstract_geometry.py +1119 -0
- cars/core/geometry/shareloc_geometry.py +598 -0
- cars/core/inputs.py +568 -0
- cars/core/outputs.py +176 -0
- cars/core/preprocessing.py +722 -0
- cars/core/projection.py +843 -0
- cars/core/roi_tools.py +215 -0
- cars/core/tiling.py +774 -0
- cars/core/utils.py +164 -0
- cars/data_structures/__init__.py +23 -0
- cars/data_structures/cars_dataset.py +1541 -0
- cars/data_structures/cars_dict.py +74 -0
- cars/data_structures/corresponding_tiles_tools.py +186 -0
- cars/data_structures/dataframe_converter.py +185 -0
- cars/data_structures/format_transformation.py +297 -0
- cars/devibrate.py +689 -0
- cars/extractroi.py +264 -0
- cars/orchestrator/__init__.py +23 -0
- cars/orchestrator/achievement_tracker.py +125 -0
- cars/orchestrator/cluster/__init__.py +37 -0
- cars/orchestrator/cluster/abstract_cluster.py +244 -0
- cars/orchestrator/cluster/abstract_dask_cluster.py +375 -0
- cars/orchestrator/cluster/dask_cluster_tools.py +103 -0
- cars/orchestrator/cluster/dask_config/README.md +94 -0
- cars/orchestrator/cluster/dask_config/dask.yaml +21 -0
- cars/orchestrator/cluster/dask_config/distributed.yaml +70 -0
- cars/orchestrator/cluster/dask_config/jobqueue.yaml +26 -0
- cars/orchestrator/cluster/dask_config/reference_confs/dask-schema.yaml +137 -0
- cars/orchestrator/cluster/dask_config/reference_confs/dask.yaml +26 -0
- cars/orchestrator/cluster/dask_config/reference_confs/distributed-schema.yaml +1009 -0
- cars/orchestrator/cluster/dask_config/reference_confs/distributed.yaml +273 -0
- cars/orchestrator/cluster/dask_config/reference_confs/jobqueue.yaml +212 -0
- cars/orchestrator/cluster/dask_jobqueue_utils.py +204 -0
- cars/orchestrator/cluster/local_dask_cluster.py +116 -0
- cars/orchestrator/cluster/log_wrapper.py +1075 -0
- cars/orchestrator/cluster/mp_cluster/__init__.py +27 -0
- cars/orchestrator/cluster/mp_cluster/mp_factorizer.py +212 -0
- cars/orchestrator/cluster/mp_cluster/mp_objects.py +535 -0
- cars/orchestrator/cluster/mp_cluster/mp_tools.py +93 -0
- cars/orchestrator/cluster/mp_cluster/mp_wrapper.py +505 -0
- cars/orchestrator/cluster/mp_cluster/multiprocessing_cluster.py +873 -0
- cars/orchestrator/cluster/mp_cluster/multiprocessing_profiler.py +399 -0
- cars/orchestrator/cluster/pbs_dask_cluster.py +207 -0
- cars/orchestrator/cluster/sequential_cluster.py +139 -0
- cars/orchestrator/cluster/slurm_dask_cluster.py +234 -0
- cars/orchestrator/orchestrator.py +905 -0
- cars/orchestrator/orchestrator_constants.py +29 -0
- cars/orchestrator/registry/__init__.py +23 -0
- cars/orchestrator/registry/abstract_registry.py +143 -0
- cars/orchestrator/registry/compute_registry.py +106 -0
- cars/orchestrator/registry/id_generator.py +116 -0
- cars/orchestrator/registry/replacer_registry.py +213 -0
- cars/orchestrator/registry/saver_registry.py +363 -0
- cars/orchestrator/registry/unseen_registry.py +118 -0
- cars/orchestrator/tiles_profiler.py +279 -0
- cars/pipelines/__init__.py +26 -0
- cars/pipelines/conf_resolution/conf_final_resolution.yaml +5 -0
- cars/pipelines/conf_resolution/conf_first_resolution.yaml +2 -0
- cars/pipelines/conf_resolution/conf_intermediate_resolution.yaml +2 -0
- cars/pipelines/default/__init__.py +26 -0
- cars/pipelines/default/default_pipeline.py +786 -0
- cars/pipelines/parameters/__init__.py +0 -0
- cars/pipelines/parameters/advanced_parameters.py +417 -0
- cars/pipelines/parameters/advanced_parameters_constants.py +69 -0
- cars/pipelines/parameters/application_parameters.py +71 -0
- cars/pipelines/parameters/depth_map_inputs.py +0 -0
- cars/pipelines/parameters/dsm_inputs.py +918 -0
- cars/pipelines/parameters/dsm_inputs_constants.py +25 -0
- cars/pipelines/parameters/output_constants.py +52 -0
- cars/pipelines/parameters/output_parameters.py +454 -0
- cars/pipelines/parameters/sensor_inputs.py +842 -0
- cars/pipelines/parameters/sensor_inputs_constants.py +49 -0
- cars/pipelines/parameters/sensor_loaders/__init__.py +29 -0
- cars/pipelines/parameters/sensor_loaders/basic_classif_loader.py +86 -0
- cars/pipelines/parameters/sensor_loaders/basic_image_loader.py +98 -0
- cars/pipelines/parameters/sensor_loaders/pivot_classif_loader.py +90 -0
- cars/pipelines/parameters/sensor_loaders/pivot_image_loader.py +105 -0
- cars/pipelines/parameters/sensor_loaders/sensor_loader.py +93 -0
- cars/pipelines/parameters/sensor_loaders/sensor_loader_template.py +71 -0
- cars/pipelines/parameters/sensor_loaders/slurp_classif_loader.py +86 -0
- cars/pipelines/pipeline.py +119 -0
- cars/pipelines/pipeline_constants.py +31 -0
- cars/pipelines/pipeline_template.py +139 -0
- cars/pipelines/unit/__init__.py +26 -0
- cars/pipelines/unit/unit_pipeline.py +2850 -0
- cars/starter.py +167 -0
- cars-1.0.0rc1.dist-info/METADATA +292 -0
- cars-1.0.0rc1.dist-info/RECORD +200 -0
- cars-1.0.0rc1.dist-info/WHEEL +6 -0
- cars-1.0.0rc1.dist-info/entry_points.txt +8 -0
|
@@ -0,0 +1,1152 @@
|
|
|
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
|
+
# pylint: disable=too-many-lines
|
|
22
|
+
|
|
23
|
+
"""
|
|
24
|
+
this module contains the dense_matching application class.
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
import collections
|
|
28
|
+
|
|
29
|
+
# Third party imports
|
|
30
|
+
import copy
|
|
31
|
+
|
|
32
|
+
# Standard imports
|
|
33
|
+
import logging
|
|
34
|
+
import os
|
|
35
|
+
from typing import List
|
|
36
|
+
|
|
37
|
+
import numpy as np
|
|
38
|
+
import rasterio as rio
|
|
39
|
+
import xarray
|
|
40
|
+
from affine import Affine
|
|
41
|
+
from json_checker import Checker, Or
|
|
42
|
+
|
|
43
|
+
import cars.orchestrator.orchestrator as ocht
|
|
44
|
+
from cars.applications import application_constants
|
|
45
|
+
|
|
46
|
+
# CARS imports
|
|
47
|
+
from cars.applications.rasterization import (
|
|
48
|
+
rasterization_algo,
|
|
49
|
+
)
|
|
50
|
+
from cars.applications.rasterization import (
|
|
51
|
+
rasterization_constants as raster_cst,
|
|
52
|
+
)
|
|
53
|
+
from cars.applications.rasterization import (
|
|
54
|
+
rasterization_wrappers,
|
|
55
|
+
)
|
|
56
|
+
from cars.applications.rasterization.abstract_pc_rasterization_app import (
|
|
57
|
+
PointCloudRasterization,
|
|
58
|
+
)
|
|
59
|
+
from cars.applications.triangulation import pc_transform
|
|
60
|
+
from cars.core import constants as cst
|
|
61
|
+
from cars.core import projection, tiling
|
|
62
|
+
from cars.core.utils import safe_makedirs
|
|
63
|
+
from cars.data_structures import cars_dataset
|
|
64
|
+
|
|
65
|
+
# R0903 temporary disabled for error "Too few public methods"
|
|
66
|
+
# œgoing to be corrected by adding new methods as check_conf
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
class SimpleGaussian(
|
|
70
|
+
PointCloudRasterization, short_name="simple_gaussian"
|
|
71
|
+
): # pylint: disable=R0903
|
|
72
|
+
"""
|
|
73
|
+
PointCloudRasterisation
|
|
74
|
+
"""
|
|
75
|
+
|
|
76
|
+
# pylint: disable=too-many-instance-attributes
|
|
77
|
+
|
|
78
|
+
def __init__(self, conf=None):
|
|
79
|
+
"""
|
|
80
|
+
Init function of PointCloudRasterisation
|
|
81
|
+
|
|
82
|
+
:param conf: configuration for rasterization
|
|
83
|
+
:return: a application_to_use object
|
|
84
|
+
"""
|
|
85
|
+
|
|
86
|
+
super().__init__(conf=conf)
|
|
87
|
+
|
|
88
|
+
# check conf
|
|
89
|
+
|
|
90
|
+
# get rasterization parameter
|
|
91
|
+
self.used_method = self.used_config["method"]
|
|
92
|
+
self.dsm_radius = self.used_config["dsm_radius"]
|
|
93
|
+
self.sigma = self.used_config["sigma"]
|
|
94
|
+
self.grid_points_division_factor = self.used_config[
|
|
95
|
+
"grid_points_division_factor"
|
|
96
|
+
]
|
|
97
|
+
# get nodata values
|
|
98
|
+
self.dsm_no_data = self.used_config["dsm_no_data"]
|
|
99
|
+
self.texture_no_data = self.used_config["texture_no_data"]
|
|
100
|
+
self.color_dtype = self.used_config["texture_dtype"]
|
|
101
|
+
self.msk_no_data = self.used_config["msk_no_data"]
|
|
102
|
+
|
|
103
|
+
# Init orchestrator
|
|
104
|
+
self.orchestrator = None
|
|
105
|
+
|
|
106
|
+
def check_conf(self, conf):
|
|
107
|
+
"""
|
|
108
|
+
Check configuration
|
|
109
|
+
|
|
110
|
+
:param conf: configuration to check
|
|
111
|
+
:type conf: dict
|
|
112
|
+
|
|
113
|
+
:return: overloaded configuration
|
|
114
|
+
:rtype: dict
|
|
115
|
+
|
|
116
|
+
"""
|
|
117
|
+
|
|
118
|
+
# init conf
|
|
119
|
+
if conf is not None:
|
|
120
|
+
overloaded_conf = conf.copy()
|
|
121
|
+
else:
|
|
122
|
+
conf = {}
|
|
123
|
+
overloaded_conf = {}
|
|
124
|
+
|
|
125
|
+
# Overload conf
|
|
126
|
+
|
|
127
|
+
# get rasterization parameter
|
|
128
|
+
overloaded_conf["method"] = conf.get("method", "simple_gaussian")
|
|
129
|
+
overloaded_conf["dsm_radius"] = conf.get("dsm_radius", 1)
|
|
130
|
+
overloaded_conf["sigma"] = conf.get("sigma", None)
|
|
131
|
+
overloaded_conf["grid_points_division_factor"] = conf.get(
|
|
132
|
+
"grid_points_division_factor", None
|
|
133
|
+
)
|
|
134
|
+
|
|
135
|
+
# get nodata values
|
|
136
|
+
overloaded_conf["dsm_no_data"] = conf.get("dsm_no_data", -32768)
|
|
137
|
+
overloaded_conf["texture_no_data"] = conf.get("texture_no_data", None)
|
|
138
|
+
overloaded_conf["texture_dtype"] = conf.get("texture_dtype", None)
|
|
139
|
+
overloaded_conf["msk_no_data"] = conf.get("msk_no_data", 255)
|
|
140
|
+
|
|
141
|
+
overloaded_conf["save_intermediate_data"] = conf.get(
|
|
142
|
+
"save_intermediate_data", False
|
|
143
|
+
)
|
|
144
|
+
|
|
145
|
+
rasterization_schema = {
|
|
146
|
+
"method": str,
|
|
147
|
+
"dsm_radius": Or(float, int),
|
|
148
|
+
"sigma": Or(float, None),
|
|
149
|
+
"grid_points_division_factor": Or(None, int),
|
|
150
|
+
"dsm_no_data": int,
|
|
151
|
+
"msk_no_data": int,
|
|
152
|
+
"texture_no_data": Or(None, int),
|
|
153
|
+
"texture_dtype": Or(None, str),
|
|
154
|
+
"save_intermediate_data": bool,
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
# Check conf
|
|
158
|
+
checker = Checker(rasterization_schema)
|
|
159
|
+
checker.validate(overloaded_conf)
|
|
160
|
+
|
|
161
|
+
return overloaded_conf
|
|
162
|
+
|
|
163
|
+
def get_margins(self, resolution):
|
|
164
|
+
"""
|
|
165
|
+
Get the margin to use for terrain tiles
|
|
166
|
+
|
|
167
|
+
:param resolution: resolution of raster data (in target CRS unit)
|
|
168
|
+
:type resolution: float
|
|
169
|
+
|
|
170
|
+
:return: margin in meters or degrees
|
|
171
|
+
"""
|
|
172
|
+
|
|
173
|
+
margins = self.dsm_radius * resolution
|
|
174
|
+
return margins
|
|
175
|
+
|
|
176
|
+
def get_optimal_tile_size(
|
|
177
|
+
self,
|
|
178
|
+
max_ram_per_worker,
|
|
179
|
+
superposing_point_clouds=1,
|
|
180
|
+
point_cloud_resolution=0.5,
|
|
181
|
+
):
|
|
182
|
+
"""
|
|
183
|
+
Get the optimal tile size to use, depending on memory available
|
|
184
|
+
|
|
185
|
+
:param max_ram_per_worker: maximum ram available
|
|
186
|
+
:type max_ram_per_worker: int
|
|
187
|
+
:param superposing_point_clouds: number of point clouds superposing
|
|
188
|
+
:type superposing_point_clouds: int
|
|
189
|
+
:param point_cloud_resolution: resolution of point cloud
|
|
190
|
+
:type point_cloud_resolution: float
|
|
191
|
+
|
|
192
|
+
:return: optimal tile size in meter
|
|
193
|
+
:rtype: float
|
|
194
|
+
|
|
195
|
+
"""
|
|
196
|
+
|
|
197
|
+
tot = 7000 * superposing_point_clouds / point_cloud_resolution
|
|
198
|
+
|
|
199
|
+
import_ = 200 # MiB
|
|
200
|
+
tile_size = int(
|
|
201
|
+
np.sqrt(float(((max_ram_per_worker - import_) * 2**23)) / tot)
|
|
202
|
+
)
|
|
203
|
+
|
|
204
|
+
logging.info(
|
|
205
|
+
"Estimated optimal tile size for rasterization: {} meters".format(
|
|
206
|
+
tile_size
|
|
207
|
+
)
|
|
208
|
+
)
|
|
209
|
+
return tile_size
|
|
210
|
+
|
|
211
|
+
# pylint: disable=too-many-positional-arguments
|
|
212
|
+
def run( # noqa: C901 function is too complex
|
|
213
|
+
self,
|
|
214
|
+
point_clouds,
|
|
215
|
+
epsg,
|
|
216
|
+
output_crs,
|
|
217
|
+
resolution,
|
|
218
|
+
orchestrator=None,
|
|
219
|
+
dsm_file_name=None,
|
|
220
|
+
weights_file_name=None,
|
|
221
|
+
color_file_name=None,
|
|
222
|
+
classif_file_name=None,
|
|
223
|
+
performance_map_file_name=None,
|
|
224
|
+
ambiguity_file_name=None,
|
|
225
|
+
contributing_pair_file_name=None,
|
|
226
|
+
filling_file_name=None,
|
|
227
|
+
color_dtype=None,
|
|
228
|
+
dump_dir=None,
|
|
229
|
+
performance_map_classes=None,
|
|
230
|
+
phasing=None,
|
|
231
|
+
):
|
|
232
|
+
"""
|
|
233
|
+
Run PointCloudRasterisation application.
|
|
234
|
+
|
|
235
|
+
Creates a CarsDataset filled with dsm tiles.
|
|
236
|
+
|
|
237
|
+
:param point_clouds: merged point cloud or list of array point clouds
|
|
238
|
+
|
|
239
|
+
. CarsDataset contains:
|
|
240
|
+
|
|
241
|
+
- Z x W Delayed tiles. \
|
|
242
|
+
Each tile will be a future pandas DataFrame containing:
|
|
243
|
+
|
|
244
|
+
- data with keys "x", "y", "z", "corr_msk" \
|
|
245
|
+
optional: "texture", "mask", "data_valid", "z_inf", "z_sup"\
|
|
246
|
+
"coord_epi_geom_i", "coord_epi_geom_j", "idx_im_epi"
|
|
247
|
+
- attrs with keys "epsg", "ysize", "xsize", "xstart", "ystart"
|
|
248
|
+
|
|
249
|
+
- attributes containing "bounds", "ysize", "xsize", "epsg"
|
|
250
|
+
|
|
251
|
+
OR
|
|
252
|
+
|
|
253
|
+
|
|
254
|
+
Tuple(list of CarsDataset Arrays, bounds). With list of point
|
|
255
|
+
clouds:
|
|
256
|
+
list of CarsDataset of type array, with:
|
|
257
|
+
- data with keys x", "y", "z", "corr_msk", "z_inf", "z_sup"\
|
|
258
|
+
optional: "texture", "mask", "data_valid",\
|
|
259
|
+
"coord_epi_geom_i", "coord_epi_geom_j", "idx_im_epi"
|
|
260
|
+
|
|
261
|
+
:type point_clouds: CarsDataset filled with pandas.DataFrame
|
|
262
|
+
:param epsg: epsg of raster data
|
|
263
|
+
:type epsg: str
|
|
264
|
+
:param output_crs: output_crs of raster data
|
|
265
|
+
:type output_crs: str
|
|
266
|
+
:param resolution: resolution of raster data (in target CRS unit)
|
|
267
|
+
:type resolution: float
|
|
268
|
+
:param orchestrator: orchestrator used
|
|
269
|
+
:param dsm_file_name: path of dsm
|
|
270
|
+
:type dsm_file_name: str
|
|
271
|
+
:param weights_file_name: path of dsm weights
|
|
272
|
+
:type weights_file_name: str
|
|
273
|
+
:param color_file_name: path of color
|
|
274
|
+
:type color_file_name: str
|
|
275
|
+
:param classif_file_name: path of classification
|
|
276
|
+
:type classif_file_name: str
|
|
277
|
+
:param performance_map_file_name: path of performance map file
|
|
278
|
+
:type performance_map_file_name: str
|
|
279
|
+
:param ambiguity_file_name: path of ambiguity file
|
|
280
|
+
:type ambiguity_file_name: str
|
|
281
|
+
:param contributing_pair_file_name: path of contributing pair file
|
|
282
|
+
:type contributing_pair_file_name: str
|
|
283
|
+
:param filling_file_name: path of filling file
|
|
284
|
+
:type filling_file_name: str
|
|
285
|
+
:param color_dtype: output color image type
|
|
286
|
+
:type color_dtype: str (numpy type)
|
|
287
|
+
:param dump_dir: directory used for outputs with no associated filename
|
|
288
|
+
:type dump_dir: str
|
|
289
|
+
:param performance_map_classes: list for step defining border of class
|
|
290
|
+
:type performance_map_classes: list or None
|
|
291
|
+
:param phasing: if activated, we phase the dsm on this point
|
|
292
|
+
:type phasing: dict
|
|
293
|
+
|
|
294
|
+
:return: raster DSM. CarsDataset contains:
|
|
295
|
+
|
|
296
|
+
- Z x W Delayed tiles. \
|
|
297
|
+
Each tile will be a future xarray Dataset containing:
|
|
298
|
+
|
|
299
|
+
- data : with keys : "hgt", "img", "raster_msk",optional : \
|
|
300
|
+
"n_pts", "pts_in_cell", "hgt_mean", "hgt_stdev",\
|
|
301
|
+
"hgt_inf", "hgt_sup"
|
|
302
|
+
- attrs with keys: "epsg"
|
|
303
|
+
- attributes containing: None
|
|
304
|
+
|
|
305
|
+
:rtype : CarsDataset filled with xr.Dataset
|
|
306
|
+
"""
|
|
307
|
+
# only the saved layers will be saved
|
|
308
|
+
list_computed_layers = []
|
|
309
|
+
|
|
310
|
+
# Default orchestrator
|
|
311
|
+
if orchestrator is None:
|
|
312
|
+
# Create default sequential orchestrator for current application
|
|
313
|
+
# be awere, no out_json will be shared between orchestrators
|
|
314
|
+
# No files saved
|
|
315
|
+
self.orchestrator = ocht.Orchestrator(
|
|
316
|
+
orchestrator_conf={"mode": "sequential"}
|
|
317
|
+
)
|
|
318
|
+
else:
|
|
319
|
+
self.orchestrator = orchestrator
|
|
320
|
+
|
|
321
|
+
# Get if color and stats are saved
|
|
322
|
+
save_intermediate_data = self.used_config["save_intermediate_data"]
|
|
323
|
+
|
|
324
|
+
keep_dir = (
|
|
325
|
+
len(
|
|
326
|
+
list(
|
|
327
|
+
filter(
|
|
328
|
+
lambda x: x is not None,
|
|
329
|
+
[
|
|
330
|
+
weights_file_name,
|
|
331
|
+
color_file_name,
|
|
332
|
+
classif_file_name,
|
|
333
|
+
performance_map_file_name,
|
|
334
|
+
ambiguity_file_name,
|
|
335
|
+
contributing_pair_file_name,
|
|
336
|
+
filling_file_name,
|
|
337
|
+
],
|
|
338
|
+
)
|
|
339
|
+
)
|
|
340
|
+
)
|
|
341
|
+
> 0
|
|
342
|
+
)
|
|
343
|
+
|
|
344
|
+
if not self.color_dtype:
|
|
345
|
+
self.color_dtype = color_dtype
|
|
346
|
+
|
|
347
|
+
if self.texture_no_data is None:
|
|
348
|
+
if self.color_dtype is not None:
|
|
349
|
+
if "float" in self.color_dtype:
|
|
350
|
+
self.texture_no_data = np.finfo(self.color_dtype).max
|
|
351
|
+
else:
|
|
352
|
+
self.texture_no_data = np.iinfo(self.color_dtype).max
|
|
353
|
+
else:
|
|
354
|
+
self.texture_no_data = np.finfo("float32").max
|
|
355
|
+
|
|
356
|
+
# Setup dump directory
|
|
357
|
+
if dump_dir is not None:
|
|
358
|
+
out_dump_dir = dump_dir
|
|
359
|
+
safe_makedirs(dump_dir)
|
|
360
|
+
if not save_intermediate_data and not keep_dir:
|
|
361
|
+
self.orchestrator.add_to_clean(dump_dir)
|
|
362
|
+
else:
|
|
363
|
+
out_dump_dir = self.orchestrator.out_dir
|
|
364
|
+
|
|
365
|
+
# Check if input data is supported
|
|
366
|
+
if not (
|
|
367
|
+
isinstance(point_clouds, tuple)
|
|
368
|
+
and isinstance(point_clouds[0][0], cars_dataset.CarsDataset)
|
|
369
|
+
and point_clouds[0][0].dataset_type == "arrays"
|
|
370
|
+
):
|
|
371
|
+
message = (
|
|
372
|
+
"PointCloudRasterization application doesn't support "
|
|
373
|
+
"this input data "
|
|
374
|
+
"format : type : {}".format(type(point_clouds))
|
|
375
|
+
)
|
|
376
|
+
logging.error(message)
|
|
377
|
+
raise RuntimeError(message)
|
|
378
|
+
|
|
379
|
+
# Create CarsDataset
|
|
380
|
+
terrain_raster = cars_dataset.CarsDataset(
|
|
381
|
+
"arrays", name="rasterization"
|
|
382
|
+
)
|
|
383
|
+
|
|
384
|
+
paths_data = {}
|
|
385
|
+
|
|
386
|
+
bounds = point_clouds[1]
|
|
387
|
+
# tiling grid: all tiles from sources -> not replaceable.
|
|
388
|
+
# CarsDataset is only used for processing
|
|
389
|
+
nb_tiles = 0
|
|
390
|
+
for point_cld in point_clouds[0]:
|
|
391
|
+
nb_tiles += point_cld.shape[0] * point_cld.shape[1]
|
|
392
|
+
terrain_raster.tiling_grid = tiling.generate_tiling_grid(
|
|
393
|
+
0, 0, 1, nb_tiles, 1, 1
|
|
394
|
+
)
|
|
395
|
+
|
|
396
|
+
terrain_raster.generate_none_tiles()
|
|
397
|
+
|
|
398
|
+
if phasing is not None:
|
|
399
|
+
res = resolution
|
|
400
|
+
x_phase = phasing["point"][0]
|
|
401
|
+
y_phase = phasing["point"][1]
|
|
402
|
+
|
|
403
|
+
for index, value in enumerate(bounds):
|
|
404
|
+
if index in (0, 2):
|
|
405
|
+
bounds[index] = rasterization_wrappers.phased_dsm(
|
|
406
|
+
value, x_phase, res
|
|
407
|
+
)
|
|
408
|
+
else:
|
|
409
|
+
bounds[index] = rasterization_wrappers.phased_dsm(
|
|
410
|
+
value, y_phase, res
|
|
411
|
+
)
|
|
412
|
+
|
|
413
|
+
# Derive output image files parameters to pass to rasterio
|
|
414
|
+
_, _, xsize, ysize = tiling.roi_to_start_and_size(bounds, resolution)
|
|
415
|
+
logging.info("DSM output image size: {}x{} pixels".format(xsize, ysize))
|
|
416
|
+
|
|
417
|
+
try:
|
|
418
|
+
if isinstance(point_clouds, tuple):
|
|
419
|
+
source_pc_names = point_clouds[0][0].attributes[
|
|
420
|
+
"source_pc_names"
|
|
421
|
+
]
|
|
422
|
+
else:
|
|
423
|
+
source_pc_names = point_clouds.attributes["source_pc_names"]
|
|
424
|
+
except KeyError:
|
|
425
|
+
source_pc_names = None
|
|
426
|
+
|
|
427
|
+
# Save objects
|
|
428
|
+
|
|
429
|
+
if dsm_file_name is not None:
|
|
430
|
+
safe_makedirs(os.path.dirname(dsm_file_name))
|
|
431
|
+
|
|
432
|
+
out_dsm_file_name = dsm_file_name
|
|
433
|
+
|
|
434
|
+
if out_dsm_file_name is not None:
|
|
435
|
+
self.orchestrator.update_index(
|
|
436
|
+
{
|
|
437
|
+
"dsm": {
|
|
438
|
+
cst.INDEX_DSM_ALT: os.path.basename(out_dsm_file_name)
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
)
|
|
442
|
+
elif save_intermediate_data:
|
|
443
|
+
# File is not part of the official product, write it in dump_dir
|
|
444
|
+
out_dsm_file_name = os.path.join(out_dump_dir, "dsm.tif")
|
|
445
|
+
if out_dsm_file_name is not None:
|
|
446
|
+
list_computed_layers += ["dsm"]
|
|
447
|
+
self.orchestrator.add_to_save_lists(
|
|
448
|
+
out_dsm_file_name,
|
|
449
|
+
cst.RASTER_HGT,
|
|
450
|
+
terrain_raster,
|
|
451
|
+
dtype=np.float32,
|
|
452
|
+
nodata=self.dsm_no_data,
|
|
453
|
+
cars_ds_name="dsm",
|
|
454
|
+
)
|
|
455
|
+
paths_data[cst.RASTER_HGT] = out_dsm_file_name
|
|
456
|
+
|
|
457
|
+
out_weights_file_name = weights_file_name
|
|
458
|
+
if out_weights_file_name is not None:
|
|
459
|
+
# add contributing pair filename to index
|
|
460
|
+
self.orchestrator.update_index(
|
|
461
|
+
{
|
|
462
|
+
"dsm": {
|
|
463
|
+
cst.INDEX_DSM_WEIGHTS: os.path.basename(
|
|
464
|
+
out_weights_file_name
|
|
465
|
+
)
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
)
|
|
469
|
+
else:
|
|
470
|
+
# Always write weights.tif, but dump_dir is in the orchestrator
|
|
471
|
+
# clean list if save_intermediate_data is not activated
|
|
472
|
+
out_weights_file_name = os.path.join(out_dump_dir, "weights.tif")
|
|
473
|
+
if out_weights_file_name is not None:
|
|
474
|
+
list_computed_layers += ["weights"]
|
|
475
|
+
self.orchestrator.add_to_save_lists(
|
|
476
|
+
out_weights_file_name,
|
|
477
|
+
cst.RASTER_WEIGHTS_SUM,
|
|
478
|
+
terrain_raster,
|
|
479
|
+
dtype=np.float32,
|
|
480
|
+
nodata=0,
|
|
481
|
+
cars_ds_name="dsm_weights",
|
|
482
|
+
)
|
|
483
|
+
paths_data[cst.RASTER_WEIGHTS_SUM] = out_weights_file_name
|
|
484
|
+
|
|
485
|
+
out_clr_file_name = color_file_name
|
|
486
|
+
if out_clr_file_name is not None:
|
|
487
|
+
# add contributing pair filename to index
|
|
488
|
+
self.orchestrator.update_index(
|
|
489
|
+
{
|
|
490
|
+
"dsm": {
|
|
491
|
+
cst.INDEX_DSM_COLOR: os.path.basename(out_clr_file_name)
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
)
|
|
495
|
+
elif save_intermediate_data:
|
|
496
|
+
# File is not part of the official product, write it in dump_dir
|
|
497
|
+
out_clr_file_name = os.path.join(out_dump_dir, "image.tif")
|
|
498
|
+
if out_clr_file_name is not None:
|
|
499
|
+
list_computed_layers += ["texture"]
|
|
500
|
+
|
|
501
|
+
self.orchestrator.add_to_save_lists(
|
|
502
|
+
out_clr_file_name,
|
|
503
|
+
cst.RASTER_COLOR_IMG,
|
|
504
|
+
terrain_raster,
|
|
505
|
+
dtype=self.color_dtype,
|
|
506
|
+
nodata=self.texture_no_data,
|
|
507
|
+
cars_ds_name="texture",
|
|
508
|
+
)
|
|
509
|
+
paths_data[cst.RASTER_COLOR_IMG] = out_clr_file_name
|
|
510
|
+
|
|
511
|
+
out_classif_file_name = classif_file_name
|
|
512
|
+
if out_classif_file_name is not None:
|
|
513
|
+
# add contributing pair filename to index
|
|
514
|
+
self.orchestrator.update_index(
|
|
515
|
+
{
|
|
516
|
+
"dsm": {
|
|
517
|
+
cst.INDEX_DSM_CLASSIFICATION: os.path.basename(
|
|
518
|
+
out_classif_file_name
|
|
519
|
+
)
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
)
|
|
523
|
+
elif save_intermediate_data:
|
|
524
|
+
# File is not part of the official product, write it in dump_dir
|
|
525
|
+
out_classif_file_name = os.path.join(
|
|
526
|
+
out_dump_dir, "classification.tif"
|
|
527
|
+
)
|
|
528
|
+
|
|
529
|
+
if out_classif_file_name is not None:
|
|
530
|
+
list_computed_layers += ["classif"]
|
|
531
|
+
self.orchestrator.add_to_save_lists(
|
|
532
|
+
out_classif_file_name,
|
|
533
|
+
cst.RASTER_CLASSIF,
|
|
534
|
+
terrain_raster,
|
|
535
|
+
dtype=np.uint8,
|
|
536
|
+
nodata=self.msk_no_data,
|
|
537
|
+
cars_ds_name="dsm_classif",
|
|
538
|
+
optional_data=True,
|
|
539
|
+
)
|
|
540
|
+
paths_data[cst.RASTER_CLASSIF] = out_classif_file_name
|
|
541
|
+
|
|
542
|
+
out_performance_map = performance_map_file_name
|
|
543
|
+
|
|
544
|
+
# save raw as performance map if performance_map_classes is None
|
|
545
|
+
# save classified performance map if performance_map_classes is not None
|
|
546
|
+
out_performance_map_raw = None
|
|
547
|
+
if out_performance_map is not None:
|
|
548
|
+
# add contributing pair filename to index
|
|
549
|
+
self.orchestrator.update_index(
|
|
550
|
+
{
|
|
551
|
+
"dsm": {
|
|
552
|
+
cst.INDEX_DSM_PERFORMANCE_MAP: os.path.basename(
|
|
553
|
+
out_performance_map
|
|
554
|
+
)
|
|
555
|
+
}
|
|
556
|
+
}
|
|
557
|
+
)
|
|
558
|
+
if performance_map_classes is False:
|
|
559
|
+
# No classes, we return raw data
|
|
560
|
+
out_performance_map_raw = out_performance_map
|
|
561
|
+
out_performance_map = None
|
|
562
|
+
elif save_intermediate_data:
|
|
563
|
+
# File is not part of the official product, write it in dump_dir
|
|
564
|
+
out_performance_map = os.path.join(
|
|
565
|
+
out_dump_dir, "performance_map.tif"
|
|
566
|
+
)
|
|
567
|
+
out_performance_map_raw = os.path.join(
|
|
568
|
+
out_dump_dir, "performance_map_raw.tif"
|
|
569
|
+
)
|
|
570
|
+
|
|
571
|
+
if out_performance_map_raw is not None:
|
|
572
|
+
list_computed_layers += ["performance_map_raw"]
|
|
573
|
+
self.orchestrator.add_to_save_lists(
|
|
574
|
+
out_performance_map_raw,
|
|
575
|
+
cst.RASTER_PERFORMANCE_MAP_RAW,
|
|
576
|
+
terrain_raster,
|
|
577
|
+
dtype=np.float32,
|
|
578
|
+
nodata=self.msk_no_data,
|
|
579
|
+
cars_ds_name="performance_map_raw",
|
|
580
|
+
optional_data=True,
|
|
581
|
+
)
|
|
582
|
+
paths_data[cst.RASTER_PERFORMANCE_MAP_RAW] = out_performance_map_raw
|
|
583
|
+
|
|
584
|
+
if out_performance_map is not None:
|
|
585
|
+
# classified performance map exists
|
|
586
|
+
list_computed_layers += ["performance_map"]
|
|
587
|
+
self.orchestrator.add_to_save_lists(
|
|
588
|
+
out_performance_map,
|
|
589
|
+
cst.RASTER_PERFORMANCE_MAP,
|
|
590
|
+
terrain_raster,
|
|
591
|
+
dtype=np.uint8,
|
|
592
|
+
nodata=self.msk_no_data,
|
|
593
|
+
cars_ds_name="performance_map",
|
|
594
|
+
optional_data=True,
|
|
595
|
+
)
|
|
596
|
+
paths_data[cst.RASTER_PERFORMANCE_MAP] = out_performance_map
|
|
597
|
+
|
|
598
|
+
out_ambiguity = ambiguity_file_name
|
|
599
|
+
if out_ambiguity is not None:
|
|
600
|
+
# add contributing pair filename to index
|
|
601
|
+
self.orchestrator.update_index(
|
|
602
|
+
{
|
|
603
|
+
"dsm": {
|
|
604
|
+
cst.INDEX_DSM_AMBIGUITY: os.path.basename(out_ambiguity)
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
)
|
|
608
|
+
elif save_intermediate_data:
|
|
609
|
+
# File is not part of the official product, write it in dump_dir
|
|
610
|
+
out_ambiguity = os.path.join(out_dump_dir, "ambiguity.tif")
|
|
611
|
+
if out_ambiguity:
|
|
612
|
+
list_computed_layers += ["ambiguity"]
|
|
613
|
+
self.orchestrator.add_to_save_lists(
|
|
614
|
+
out_ambiguity,
|
|
615
|
+
cst.RASTER_AMBIGUITY,
|
|
616
|
+
terrain_raster,
|
|
617
|
+
dtype=np.float32,
|
|
618
|
+
nodata=self.msk_no_data,
|
|
619
|
+
cars_ds_name="ambiguity",
|
|
620
|
+
optional_data=True,
|
|
621
|
+
)
|
|
622
|
+
paths_data[cst.RASTER_AMBIGUITY] = out_ambiguity
|
|
623
|
+
|
|
624
|
+
out_source_pc = contributing_pair_file_name
|
|
625
|
+
if out_source_pc is not None:
|
|
626
|
+
# add contributing pair filename to index
|
|
627
|
+
self.orchestrator.update_index(
|
|
628
|
+
{
|
|
629
|
+
"dsm": {
|
|
630
|
+
cst.INDEX_DSM_CONTRIBUTING_PAIR: os.path.basename(
|
|
631
|
+
out_source_pc
|
|
632
|
+
)
|
|
633
|
+
}
|
|
634
|
+
}
|
|
635
|
+
)
|
|
636
|
+
elif save_intermediate_data:
|
|
637
|
+
# File is not part of the official product, write it in dump_dir
|
|
638
|
+
out_source_pc = os.path.join(out_dump_dir, "contributing_pair.tif")
|
|
639
|
+
if out_source_pc:
|
|
640
|
+
list_computed_layers += ["source_pc"]
|
|
641
|
+
self.orchestrator.add_to_save_lists(
|
|
642
|
+
out_source_pc,
|
|
643
|
+
cst.RASTER_SOURCE_PC,
|
|
644
|
+
terrain_raster,
|
|
645
|
+
dtype=np.uint8,
|
|
646
|
+
nodata=self.msk_no_data,
|
|
647
|
+
cars_ds_name="contributing_pair",
|
|
648
|
+
optional_data=True,
|
|
649
|
+
)
|
|
650
|
+
paths_data[cst.RASTER_SOURCE_PC] = out_source_pc
|
|
651
|
+
|
|
652
|
+
out_filling = filling_file_name
|
|
653
|
+
if out_filling is not None:
|
|
654
|
+
# add filling filename to index
|
|
655
|
+
self.orchestrator.update_index(
|
|
656
|
+
{"dsm": {cst.INDEX_DSM_FILLING: os.path.basename(out_filling)}}
|
|
657
|
+
)
|
|
658
|
+
elif save_intermediate_data:
|
|
659
|
+
# File is not part of the official product, write it in dump_dir
|
|
660
|
+
out_filling = os.path.join(out_dump_dir, "filling.tif")
|
|
661
|
+
if out_filling:
|
|
662
|
+
list_computed_layers += ["filling"]
|
|
663
|
+
self.orchestrator.add_to_save_lists(
|
|
664
|
+
out_filling,
|
|
665
|
+
cst.RASTER_FILLING,
|
|
666
|
+
terrain_raster,
|
|
667
|
+
dtype=np.uint8,
|
|
668
|
+
nodata=self.msk_no_data,
|
|
669
|
+
cars_ds_name="filling",
|
|
670
|
+
optional_data=True,
|
|
671
|
+
)
|
|
672
|
+
paths_data[cst.RASTER_FILLING] = out_filling
|
|
673
|
+
|
|
674
|
+
# TODO Check that intervals indeed exist!
|
|
675
|
+
if save_intermediate_data:
|
|
676
|
+
list_computed_layers += [cst.POINT_CLOUD_LAYER_SUP_OR_INF_ROOT]
|
|
677
|
+
out_dsm_inf_file_name = os.path.join(out_dump_dir, "dsm_inf.tif")
|
|
678
|
+
self.orchestrator.add_to_save_lists(
|
|
679
|
+
out_dsm_inf_file_name,
|
|
680
|
+
cst.RASTER_HGT_INF,
|
|
681
|
+
terrain_raster,
|
|
682
|
+
dtype=np.float32,
|
|
683
|
+
nodata=self.dsm_no_data,
|
|
684
|
+
cars_ds_name="dsm_inf",
|
|
685
|
+
optional_data=True,
|
|
686
|
+
)
|
|
687
|
+
out_dsm_sup_file_name = os.path.join(out_dump_dir, "dsm_sup.tif")
|
|
688
|
+
self.orchestrator.add_to_save_lists(
|
|
689
|
+
out_dsm_sup_file_name,
|
|
690
|
+
cst.RASTER_HGT_SUP,
|
|
691
|
+
terrain_raster,
|
|
692
|
+
dtype=np.float32,
|
|
693
|
+
nodata=self.dsm_no_data,
|
|
694
|
+
cars_ds_name="dsm_sup",
|
|
695
|
+
optional_data=True,
|
|
696
|
+
)
|
|
697
|
+
|
|
698
|
+
out_dsm_mean_file_name = os.path.join(out_dump_dir, "dsm_mean.tif")
|
|
699
|
+
out_dsm_std_file_name = os.path.join(out_dump_dir, "dsm_std.tif")
|
|
700
|
+
out_dsm_n_pts_file_name = os.path.join(
|
|
701
|
+
out_dump_dir, "dsm_n_pts.tif"
|
|
702
|
+
)
|
|
703
|
+
out_dsm_points_in_cell_file_name = os.path.join(
|
|
704
|
+
out_dump_dir, "dsm_pts_in_cell.tif"
|
|
705
|
+
)
|
|
706
|
+
self.orchestrator.add_to_save_lists(
|
|
707
|
+
out_dsm_mean_file_name,
|
|
708
|
+
cst.RASTER_HGT_MEAN,
|
|
709
|
+
terrain_raster,
|
|
710
|
+
dtype=np.float32,
|
|
711
|
+
nodata=self.dsm_no_data,
|
|
712
|
+
cars_ds_name="dsm_mean",
|
|
713
|
+
)
|
|
714
|
+
self.orchestrator.add_to_save_lists(
|
|
715
|
+
out_dsm_std_file_name,
|
|
716
|
+
cst.RASTER_HGT_STD_DEV,
|
|
717
|
+
terrain_raster,
|
|
718
|
+
dtype=np.float32,
|
|
719
|
+
nodata=self.dsm_no_data,
|
|
720
|
+
cars_ds_name="dsm_std",
|
|
721
|
+
)
|
|
722
|
+
self.orchestrator.add_to_save_lists(
|
|
723
|
+
out_dsm_n_pts_file_name,
|
|
724
|
+
cst.RASTER_NB_PTS,
|
|
725
|
+
terrain_raster,
|
|
726
|
+
dtype=np.uint16,
|
|
727
|
+
nodata=0,
|
|
728
|
+
cars_ds_name="dsm_n_pts",
|
|
729
|
+
)
|
|
730
|
+
self.orchestrator.add_to_save_lists(
|
|
731
|
+
out_dsm_points_in_cell_file_name,
|
|
732
|
+
cst.RASTER_NB_PTS_IN_CELL,
|
|
733
|
+
terrain_raster,
|
|
734
|
+
dtype=np.uint16,
|
|
735
|
+
nodata=0,
|
|
736
|
+
cars_ds_name="dsm_pts_in_cells",
|
|
737
|
+
)
|
|
738
|
+
|
|
739
|
+
out_dsm_inf_mean_file_name = os.path.join(
|
|
740
|
+
out_dump_dir, "dsm_inf_mean.tif"
|
|
741
|
+
)
|
|
742
|
+
out_dsm_inf_std_file_name = os.path.join(
|
|
743
|
+
out_dump_dir, "dsm_inf_std.tif"
|
|
744
|
+
)
|
|
745
|
+
|
|
746
|
+
self.orchestrator.add_to_save_lists(
|
|
747
|
+
out_dsm_inf_mean_file_name,
|
|
748
|
+
cst.RASTER_HGT_INF_MEAN,
|
|
749
|
+
terrain_raster,
|
|
750
|
+
dtype=np.float32,
|
|
751
|
+
nodata=self.dsm_no_data,
|
|
752
|
+
cars_ds_name="dsm_inf_mean",
|
|
753
|
+
optional_data=True,
|
|
754
|
+
)
|
|
755
|
+
|
|
756
|
+
self.orchestrator.add_to_save_lists(
|
|
757
|
+
out_dsm_inf_std_file_name,
|
|
758
|
+
cst.RASTER_HGT_INF_STD_DEV,
|
|
759
|
+
terrain_raster,
|
|
760
|
+
dtype=np.float32,
|
|
761
|
+
nodata=self.dsm_no_data,
|
|
762
|
+
cars_ds_name="dsm_inf_std",
|
|
763
|
+
optional_data=True,
|
|
764
|
+
)
|
|
765
|
+
|
|
766
|
+
out_dsm_sup_mean_file_name = os.path.join(
|
|
767
|
+
out_dump_dir, "dsm_sup_mean.tif"
|
|
768
|
+
)
|
|
769
|
+
out_dsm_sup_std_file_name = os.path.join(
|
|
770
|
+
out_dump_dir, "dsm_sup_std.tif"
|
|
771
|
+
)
|
|
772
|
+
|
|
773
|
+
self.orchestrator.add_to_save_lists(
|
|
774
|
+
out_dsm_sup_mean_file_name,
|
|
775
|
+
cst.RASTER_HGT_SUP_MEAN,
|
|
776
|
+
terrain_raster,
|
|
777
|
+
dtype=np.float32,
|
|
778
|
+
nodata=self.dsm_no_data,
|
|
779
|
+
cars_ds_name="dsm_sup_mean",
|
|
780
|
+
optional_data=True,
|
|
781
|
+
)
|
|
782
|
+
self.orchestrator.add_to_save_lists(
|
|
783
|
+
out_dsm_sup_std_file_name,
|
|
784
|
+
cst.RASTER_HGT_SUP_STD_DEV,
|
|
785
|
+
terrain_raster,
|
|
786
|
+
dtype=np.float32,
|
|
787
|
+
nodata=self.dsm_no_data,
|
|
788
|
+
cars_ds_name="dsm_sup_std",
|
|
789
|
+
optional_data=True,
|
|
790
|
+
)
|
|
791
|
+
|
|
792
|
+
# Get saving infos in order to save tiles when they are computed
|
|
793
|
+
[saving_info] = self.orchestrator.get_saving_infos([terrain_raster])
|
|
794
|
+
|
|
795
|
+
# Generate profile
|
|
796
|
+
geotransform = (
|
|
797
|
+
bounds[0],
|
|
798
|
+
resolution,
|
|
799
|
+
0.0,
|
|
800
|
+
bounds[3],
|
|
801
|
+
0.0,
|
|
802
|
+
-resolution,
|
|
803
|
+
)
|
|
804
|
+
|
|
805
|
+
transform = Affine.from_gdal(*geotransform)
|
|
806
|
+
raster_profile = collections.OrderedDict(
|
|
807
|
+
{
|
|
808
|
+
"height": ysize,
|
|
809
|
+
"width": xsize,
|
|
810
|
+
"driver": "GTiff",
|
|
811
|
+
"dtype": "float32",
|
|
812
|
+
"transform": transform,
|
|
813
|
+
"crs": output_crs.to_wkt(),
|
|
814
|
+
"tiled": True,
|
|
815
|
+
}
|
|
816
|
+
)
|
|
817
|
+
|
|
818
|
+
# Get number of tiles
|
|
819
|
+
logging.info(
|
|
820
|
+
"Number of tiles in cloud rasterization: "
|
|
821
|
+
"row: {} "
|
|
822
|
+
"col: {}".format(
|
|
823
|
+
terrain_raster.tiling_grid.shape[0],
|
|
824
|
+
terrain_raster.tiling_grid.shape[1],
|
|
825
|
+
)
|
|
826
|
+
)
|
|
827
|
+
|
|
828
|
+
# Add infos to orchestrator.out_json
|
|
829
|
+
updating_dict = {
|
|
830
|
+
application_constants.APPLICATION_TAG: {
|
|
831
|
+
raster_cst.RASTERIZATION_RUN_TAG: {
|
|
832
|
+
raster_cst.EPSG_TAG: epsg,
|
|
833
|
+
raster_cst.DSM_NO_DATA_TAG: float(self.dsm_no_data),
|
|
834
|
+
},
|
|
835
|
+
}
|
|
836
|
+
}
|
|
837
|
+
if self.texture_no_data is not None:
|
|
838
|
+
updating_dict[application_constants.APPLICATION_TAG][
|
|
839
|
+
raster_cst.RASTERIZATION_RUN_TAG
|
|
840
|
+
][raster_cst.TEXTURE_NO_DATA_TAG] = float(self.texture_no_data)
|
|
841
|
+
|
|
842
|
+
self.orchestrator.update_out_info(updating_dict)
|
|
843
|
+
|
|
844
|
+
# Add attributrs
|
|
845
|
+
terrain_raster.attributes["paths"] = paths_data
|
|
846
|
+
|
|
847
|
+
# Add final function to apply
|
|
848
|
+
terrain_raster.final_function = raster_final_function
|
|
849
|
+
ind_tile = 0
|
|
850
|
+
for point_cloud in point_clouds[0]:
|
|
851
|
+
for row_pc in range(point_cloud.shape[0]):
|
|
852
|
+
for col_pc in range(point_cloud.shape[1]):
|
|
853
|
+
# update saving infos for potential replacement
|
|
854
|
+
full_saving_info = ocht.update_saving_infos(
|
|
855
|
+
saving_info, row=0, col=ind_tile
|
|
856
|
+
)
|
|
857
|
+
if point_cloud[row_pc, col_pc] is not None:
|
|
858
|
+
# Delayed call to rasterization operations using all
|
|
859
|
+
# required point clouds
|
|
860
|
+
terrain_raster[
|
|
861
|
+
0, ind_tile
|
|
862
|
+
] = self.orchestrator.cluster.create_task(
|
|
863
|
+
rasterization_wrapper
|
|
864
|
+
)(
|
|
865
|
+
point_cloud[row_pc, col_pc],
|
|
866
|
+
resolution,
|
|
867
|
+
epsg,
|
|
868
|
+
raster_profile,
|
|
869
|
+
window=None,
|
|
870
|
+
terrain_region=None,
|
|
871
|
+
terrain_full_roi=bounds,
|
|
872
|
+
list_computed_layers=list_computed_layers,
|
|
873
|
+
saving_info=full_saving_info,
|
|
874
|
+
radius=self.dsm_radius,
|
|
875
|
+
sigma=self.sigma,
|
|
876
|
+
dsm_no_data=self.dsm_no_data,
|
|
877
|
+
texture_no_data=self.texture_no_data,
|
|
878
|
+
color_dtype=color_dtype,
|
|
879
|
+
msk_no_data=self.msk_no_data,
|
|
880
|
+
source_pc_names=source_pc_names,
|
|
881
|
+
performance_map_classes=performance_map_classes,
|
|
882
|
+
)
|
|
883
|
+
ind_tile += 1
|
|
884
|
+
|
|
885
|
+
# Sort tiles according to rank TODO remove or implement it ?
|
|
886
|
+
|
|
887
|
+
return terrain_raster
|
|
888
|
+
|
|
889
|
+
|
|
890
|
+
# pylint: disable=too-many-positional-arguments
|
|
891
|
+
def rasterization_wrapper( # noqa: C901
|
|
892
|
+
cloud,
|
|
893
|
+
resolution,
|
|
894
|
+
epsg,
|
|
895
|
+
profile,
|
|
896
|
+
window=None,
|
|
897
|
+
terrain_region=None,
|
|
898
|
+
terrain_full_roi=None,
|
|
899
|
+
list_computed_layers: List[str] = None,
|
|
900
|
+
saving_info=None,
|
|
901
|
+
sigma: float = None,
|
|
902
|
+
radius: int = 1,
|
|
903
|
+
dsm_no_data: int = np.nan,
|
|
904
|
+
texture_no_data: int = np.nan,
|
|
905
|
+
color_dtype: str = "float32",
|
|
906
|
+
msk_no_data: int = 255,
|
|
907
|
+
source_pc_names=None,
|
|
908
|
+
performance_map_classes=None,
|
|
909
|
+
):
|
|
910
|
+
"""
|
|
911
|
+
Wrapper for rasterization step :
|
|
912
|
+
- Convert a list of clouds to correct epsg
|
|
913
|
+
- Rasterize it with associated colors
|
|
914
|
+
|
|
915
|
+
if terrain_region is not provided: region is computed from point cloud,
|
|
916
|
+
with margin to use
|
|
917
|
+
|
|
918
|
+
:param cloud: combined cloud
|
|
919
|
+
:type cloud: pandas.DataFrame
|
|
920
|
+
:param terrain_region: terrain bounds
|
|
921
|
+
:param resolution: Produced DSM resolution (meter, degree [EPSG dependent])
|
|
922
|
+
:type resolution: float
|
|
923
|
+
:param epsg_code: epsg code for the CRS of the output DSM
|
|
924
|
+
:type epsg_code: int
|
|
925
|
+
:param window: Window considered
|
|
926
|
+
:type window: int
|
|
927
|
+
:param margin: margin in pixel to use
|
|
928
|
+
:type margin: int
|
|
929
|
+
:param profile: rasterio profile
|
|
930
|
+
:param list_computed_layers: list of computed output data
|
|
931
|
+
:type profile: dict
|
|
932
|
+
:param saving_info: information about CarsDataset ID.
|
|
933
|
+
:type saving_info: dict
|
|
934
|
+
:param sigma: sigma for gaussian interpolation.
|
|
935
|
+
(If None, set to resolution)
|
|
936
|
+
:param radius: Radius for hole filling.
|
|
937
|
+
:param dsm_no_data: no data value to use in the final raster
|
|
938
|
+
:param texture_no_data: no data value to use in the final colored raster
|
|
939
|
+
:param msk_no_data: no data value to use in the final mask image
|
|
940
|
+
:param source_pc_names: list of names of point cloud before merging :
|
|
941
|
+
name of sensors pair or name of point cloud file
|
|
942
|
+
:param performance_map_classes: list for step defining border of class
|
|
943
|
+
:type performance_map_classes: list or None
|
|
944
|
+
|
|
945
|
+
:return: digital surface model + projected colors
|
|
946
|
+
:rtype: xr.Dataset
|
|
947
|
+
"""
|
|
948
|
+
# update attributes
|
|
949
|
+
attributes = copy.deepcopy(cloud.attrs)
|
|
950
|
+
attributes.update(attributes.get("attributes", {}))
|
|
951
|
+
if "attributes" in attributes:
|
|
952
|
+
del attributes["attributes"]
|
|
953
|
+
if "saving_info" in attributes:
|
|
954
|
+
del attributes["saving_info"]
|
|
955
|
+
|
|
956
|
+
# convert back to correct epsg
|
|
957
|
+
# If the point cloud is not in the right epsg referential, it is converted
|
|
958
|
+
if isinstance(cloud, xarray.Dataset):
|
|
959
|
+
# Transform Dataset to Dataframe
|
|
960
|
+
cloud, cloud_epsg = pc_transform.depth_map_dataset_to_dataframe(
|
|
961
|
+
cloud, epsg
|
|
962
|
+
)
|
|
963
|
+
elif cloud is None:
|
|
964
|
+
logging.warning("Input cloud is None")
|
|
965
|
+
return None
|
|
966
|
+
else:
|
|
967
|
+
cloud_epsg = attributes.get("epsg")
|
|
968
|
+
|
|
969
|
+
if "number_of_pc" not in attributes:
|
|
970
|
+
if source_pc_names is not None:
|
|
971
|
+
attributes["number_of_pc"] = len(source_pc_names)
|
|
972
|
+
else:
|
|
973
|
+
attributes["number_of_pc"] = None
|
|
974
|
+
|
|
975
|
+
# update attributes
|
|
976
|
+
cloud.attrs = {}
|
|
977
|
+
cars_dataset.fill_dataframe(cloud, attributes=attributes)
|
|
978
|
+
|
|
979
|
+
if epsg != cloud_epsg:
|
|
980
|
+
projection.point_cloud_conversion_dataframe(cloud, cloud_epsg, epsg)
|
|
981
|
+
|
|
982
|
+
# filter cloud
|
|
983
|
+
if "mask" in cloud:
|
|
984
|
+
cloud = cloud[cloud["mask"] == 0]
|
|
985
|
+
|
|
986
|
+
if cloud.dropna(subset=["x", "y", "z"]).empty:
|
|
987
|
+
return None
|
|
988
|
+
|
|
989
|
+
# Compute start and size
|
|
990
|
+
if terrain_region is None:
|
|
991
|
+
# compute region from cloud
|
|
992
|
+
xmin = np.nanmin(cloud["x"])
|
|
993
|
+
xmax = np.nanmax(cloud["x"])
|
|
994
|
+
ymin = np.nanmin(cloud["y"])
|
|
995
|
+
ymax = np.nanmax(cloud["y"])
|
|
996
|
+
# Add margin to be sure every point is rasterized
|
|
997
|
+
terrain_region = [
|
|
998
|
+
xmin - radius * resolution,
|
|
999
|
+
ymin - radius * resolution,
|
|
1000
|
+
xmax + radius * resolution,
|
|
1001
|
+
ymax + radius * resolution,
|
|
1002
|
+
]
|
|
1003
|
+
|
|
1004
|
+
if terrain_full_roi is not None:
|
|
1005
|
+
# Modify start (used in tiling.roi_to_start_and_size) [0, 3]
|
|
1006
|
+
# to share the same global grid
|
|
1007
|
+
terrain_region[0] = (
|
|
1008
|
+
terrain_full_roi[0]
|
|
1009
|
+
+ np.round(
|
|
1010
|
+
(terrain_region[0] - terrain_full_roi[0]) / resolution
|
|
1011
|
+
)
|
|
1012
|
+
* resolution
|
|
1013
|
+
)
|
|
1014
|
+
terrain_region[3] = (
|
|
1015
|
+
terrain_full_roi[3]
|
|
1016
|
+
+ np.round(
|
|
1017
|
+
(terrain_region[3] - terrain_full_roi[3]) / resolution
|
|
1018
|
+
)
|
|
1019
|
+
* resolution
|
|
1020
|
+
)
|
|
1021
|
+
# Crop
|
|
1022
|
+
terrain_region = [
|
|
1023
|
+
max(terrain_full_roi[0], terrain_region[0]),
|
|
1024
|
+
max(terrain_full_roi[1], terrain_region[1]),
|
|
1025
|
+
min(terrain_full_roi[2], terrain_region[2]),
|
|
1026
|
+
min(terrain_full_roi[3], terrain_region[3]),
|
|
1027
|
+
]
|
|
1028
|
+
|
|
1029
|
+
if (
|
|
1030
|
+
terrain_region[0] > terrain_region[2]
|
|
1031
|
+
or terrain_region[1] > terrain_region[3]
|
|
1032
|
+
):
|
|
1033
|
+
return None
|
|
1034
|
+
|
|
1035
|
+
xstart, ystart, xsize, ysize = tiling.roi_to_start_and_size(
|
|
1036
|
+
terrain_region, resolution
|
|
1037
|
+
)
|
|
1038
|
+
|
|
1039
|
+
if xsize == 0 or ysize == 0:
|
|
1040
|
+
logging.warning("Tile is empty")
|
|
1041
|
+
return None
|
|
1042
|
+
|
|
1043
|
+
if window is None:
|
|
1044
|
+
transform = rio.Affine(*profile["transform"][0:6])
|
|
1045
|
+
row_pix_pos, col_pix_pos = rio.transform.AffineTransformer(
|
|
1046
|
+
transform
|
|
1047
|
+
).rowcol(xstart, ystart)
|
|
1048
|
+
window = [
|
|
1049
|
+
row_pix_pos,
|
|
1050
|
+
row_pix_pos + ysize,
|
|
1051
|
+
col_pix_pos,
|
|
1052
|
+
col_pix_pos + xsize,
|
|
1053
|
+
]
|
|
1054
|
+
|
|
1055
|
+
window = cars_dataset.window_array_to_dict(window)
|
|
1056
|
+
|
|
1057
|
+
# Call simple_rasterization
|
|
1058
|
+
raster = rasterization_algo.simple_rasterization_dataset_wrapper(
|
|
1059
|
+
cloud,
|
|
1060
|
+
resolution,
|
|
1061
|
+
epsg,
|
|
1062
|
+
xstart=xstart,
|
|
1063
|
+
ystart=ystart,
|
|
1064
|
+
xsize=xsize,
|
|
1065
|
+
ysize=ysize,
|
|
1066
|
+
sigma=sigma,
|
|
1067
|
+
radius=radius,
|
|
1068
|
+
dsm_no_data=dsm_no_data,
|
|
1069
|
+
texture_no_data=texture_no_data,
|
|
1070
|
+
msk_no_data=msk_no_data,
|
|
1071
|
+
list_computed_layers=list_computed_layers,
|
|
1072
|
+
source_pc_names=source_pc_names,
|
|
1073
|
+
performance_map_classes=performance_map_classes,
|
|
1074
|
+
cloud_global_id=attributes["cloud_id"],
|
|
1075
|
+
)
|
|
1076
|
+
|
|
1077
|
+
# Fill raster
|
|
1078
|
+
attributes = {
|
|
1079
|
+
"color_type": color_dtype,
|
|
1080
|
+
cst.CROPPED_DISPARITY_RANGE: (ocht.get_disparity_range_cropped(cloud)),
|
|
1081
|
+
}
|
|
1082
|
+
if raster is not None:
|
|
1083
|
+
cars_dataset.fill_dataset(
|
|
1084
|
+
raster,
|
|
1085
|
+
saving_info=saving_info,
|
|
1086
|
+
window=window,
|
|
1087
|
+
profile=profile,
|
|
1088
|
+
attributes=attributes,
|
|
1089
|
+
overlaps=None,
|
|
1090
|
+
)
|
|
1091
|
+
|
|
1092
|
+
return raster
|
|
1093
|
+
|
|
1094
|
+
|
|
1095
|
+
def raster_final_function(orchestrator, future_object):
|
|
1096
|
+
"""
|
|
1097
|
+
Apply function to current object, reading already rasterized data
|
|
1098
|
+
|
|
1099
|
+
:param orchestrator: orchestrator
|
|
1100
|
+
:param future_object: Dataset
|
|
1101
|
+
|
|
1102
|
+
:return: update object
|
|
1103
|
+
"""
|
|
1104
|
+
# Get data weights
|
|
1105
|
+
old_weights, _ = orchestrator.get_data(
|
|
1106
|
+
cst.RASTER_WEIGHTS_SUM, future_object
|
|
1107
|
+
)
|
|
1108
|
+
weights = future_object[cst.RASTER_WEIGHTS_SUM].values
|
|
1109
|
+
|
|
1110
|
+
future_object[cst.RASTER_WEIGHTS_SUM].values = np.reshape(
|
|
1111
|
+
rasterization_wrappers.update_weights(old_weights, weights),
|
|
1112
|
+
weights.shape,
|
|
1113
|
+
)
|
|
1114
|
+
|
|
1115
|
+
# Get color type
|
|
1116
|
+
color_type = future_object.attrs["attributes"]["color_type"]
|
|
1117
|
+
|
|
1118
|
+
# Get data dsm
|
|
1119
|
+
for tag in future_object.keys():
|
|
1120
|
+
|
|
1121
|
+
if tag != cst.RASTER_WEIGHTS_SUM:
|
|
1122
|
+
|
|
1123
|
+
if tag in [cst.RASTER_NB_PTS, cst.RASTER_NB_PTS_IN_CELL]:
|
|
1124
|
+
method = "sum"
|
|
1125
|
+
elif tag in [
|
|
1126
|
+
cst.RASTER_FILLING,
|
|
1127
|
+
cst.RASTER_CLASSIF,
|
|
1128
|
+
cst.RASTER_SOURCE_PC,
|
|
1129
|
+
]:
|
|
1130
|
+
method = "bool"
|
|
1131
|
+
else:
|
|
1132
|
+
method = "basic"
|
|
1133
|
+
|
|
1134
|
+
old_data, nodata_raster = orchestrator.get_data(tag, future_object)
|
|
1135
|
+
current_data = future_object[tag].values
|
|
1136
|
+
if tag == cst.RASTER_COLOR_IMG and np.issubdtype(
|
|
1137
|
+
color_type, np.integer
|
|
1138
|
+
):
|
|
1139
|
+
current_data = np.round(current_data).astype(color_type)
|
|
1140
|
+
future_object[tag].values = np.reshape(
|
|
1141
|
+
rasterization_wrappers.update_data(
|
|
1142
|
+
old_data,
|
|
1143
|
+
current_data,
|
|
1144
|
+
weights,
|
|
1145
|
+
old_weights,
|
|
1146
|
+
nodata_raster,
|
|
1147
|
+
method=method,
|
|
1148
|
+
),
|
|
1149
|
+
current_data.shape,
|
|
1150
|
+
)
|
|
1151
|
+
|
|
1152
|
+
return future_object
|