cars 1.0.0a1__cp313-cp313-win_amd64.whl → 1.0.0a2__cp313-cp313-win_amd64.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of cars might be problematic. Click here for more details.
- cars/__init__.py +4 -4
- cars/applications/dem_generation/dem_generation_wrappers.py +5 -1
- cars/applications/dem_generation/dichotomic_generation_app.py +21 -6
- cars/applications/dem_generation/rasterization_app.py +70 -27
- cars/applications/dense_match_filling/abstract_dense_match_filling_app.py +4 -0
- cars/applications/dense_match_filling/cpp/dense_match_filling_cpp.cp313-win_amd64.dll.a +0 -0
- cars/applications/dense_match_filling/cpp/dense_match_filling_cpp.cp313-win_amd64.pyd +0 -0
- cars/applications/dense_match_filling/fill_disp_algo.py +41 -12
- cars/applications/dense_match_filling/plane_app.py +11 -0
- cars/applications/dense_match_filling/zero_padding_app.py +11 -1
- cars/applications/dense_matching/census_mccnn_sgm_app.py +262 -545
- cars/applications/dense_matching/cpp/dense_matching_cpp.cp313-win_amd64.dll.a +0 -0
- cars/applications/dense_matching/cpp/dense_matching_cpp.cp313-win_amd64.pyd +0 -0
- cars/applications/dense_matching/dense_matching_algo.py +59 -11
- cars/applications/dense_matching/dense_matching_wrappers.py +51 -31
- cars/applications/dense_matching/disparity_grid_algo.py +572 -0
- cars/applications/grid_generation/grid_correction_app.py +0 -53
- cars/applications/grid_generation/transform_grid.py +5 -5
- cars/applications/point_cloud_fusion/pc_fusion_algo.py +17 -11
- cars/applications/point_cloud_fusion/pc_fusion_wrappers.py +3 -4
- cars/applications/rasterization/rasterization_algo.py +20 -27
- cars/applications/rasterization/rasterization_wrappers.py +6 -5
- cars/applications/rasterization/simple_gaussian_app.py +2 -14
- cars/applications/sparse_matching/sparse_matching_wrappers.py +0 -49
- cars/applications/triangulation/line_of_sight_intersection_app.py +1 -1
- cars/applications/triangulation/triangulation_wrappers.py +2 -1
- cars/bundleadjustment.py +51 -11
- cars/cars.py +15 -5
- cars/core/constants.py +1 -1
- cars/core/geometry/abstract_geometry.py +54 -11
- cars/core/geometry/shareloc_geometry.py +59 -14
- cars/orchestrator/registry/saver_registry.py +0 -78
- cars/pipelines/default/default_pipeline.py +23 -26
- cars/pipelines/parameters/depth_map_inputs.py +22 -67
- cars/pipelines/parameters/dsm_inputs.py +16 -29
- cars/pipelines/parameters/sensor_inputs.py +20 -21
- cars/pipelines/parameters/sensor_loaders/basic_sensor_loader.py +3 -3
- cars/pipelines/parameters/sensor_loaders/pivot_sensor_loader.py +2 -2
- cars/pipelines/parameters/sensor_loaders/sensor_loader.py +4 -6
- cars/pipelines/parameters/sensor_loaders/sensor_loader_template.py +2 -2
- cars/pipelines/pipeline.py +8 -8
- cars/pipelines/unit/unit_pipeline.py +103 -196
- cars/starter.py +20 -1
- cars-1.0.0a2.dist-info/DELVEWHEEL +2 -0
- {cars-1.0.0a1.dist-info → cars-1.0.0a2.dist-info}/METADATA +3 -2
- {cars-1.0.0a1.dist-info → cars-1.0.0a2.dist-info}/RECORD +48 -47
- cars-1.0.0a1.dist-info/DELVEWHEEL +0 -2
- {cars-1.0.0a1.dist-info → cars-1.0.0a2.dist-info}/WHEEL +0 -0
- {cars-1.0.0a1.dist-info → cars-1.0.0a2.dist-info}/entry_points.txt +0 -0
|
@@ -0,0 +1,572 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
# coding: utf8
|
|
3
|
+
#
|
|
4
|
+
# Copyright (c) 2020 Centre National d'Etudes Spatiales (CNES).
|
|
5
|
+
#
|
|
6
|
+
# This file is part of CARS
|
|
7
|
+
# (see https://github.com/CNES/cars).
|
|
8
|
+
#
|
|
9
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
10
|
+
# you may not use this file except in compliance with the License.
|
|
11
|
+
# You may obtain a copy of the License at
|
|
12
|
+
#
|
|
13
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
14
|
+
#
|
|
15
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
16
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
17
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
18
|
+
# See the License for the specific language governing permissions and
|
|
19
|
+
# limitations under the License.
|
|
20
|
+
#
|
|
21
|
+
"""
|
|
22
|
+
this module contains the wrapper used in disparity grid computation.
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
# Standard imports
|
|
26
|
+
import itertools
|
|
27
|
+
import logging
|
|
28
|
+
|
|
29
|
+
# Third party imports
|
|
30
|
+
import affine
|
|
31
|
+
import numpy as np
|
|
32
|
+
import rasterio
|
|
33
|
+
import xarray as xr
|
|
34
|
+
from scipy.ndimage import maximum_filter, minimum_filter
|
|
35
|
+
from shareloc.proj_utils import transform_physical_point_to_index
|
|
36
|
+
|
|
37
|
+
# CARS imports
|
|
38
|
+
import cars.applications.dense_matching.dense_matching_constants as dm_cst
|
|
39
|
+
import cars.orchestrator.orchestrator as ocht
|
|
40
|
+
from cars.applications.dense_matching.dense_matching_algo import (
|
|
41
|
+
LinearInterpNearestExtrap,
|
|
42
|
+
)
|
|
43
|
+
from cars.core import inputs, projection
|
|
44
|
+
from cars.core.projection import point_cloud_conversion
|
|
45
|
+
from cars.data_structures import cars_dataset, cars_dict
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def generate_disp_grids_dataset(
|
|
49
|
+
grid_min,
|
|
50
|
+
grid_max,
|
|
51
|
+
saving_info,
|
|
52
|
+
raster_profile,
|
|
53
|
+
window=None,
|
|
54
|
+
row_coords=None,
|
|
55
|
+
col_coords=None,
|
|
56
|
+
):
|
|
57
|
+
"""
|
|
58
|
+
Generate disparity grids xarray dataset
|
|
59
|
+
|
|
60
|
+
:param grid_min: disp grid min
|
|
61
|
+
:type grid_min: np.ndarray
|
|
62
|
+
:param grid_max: disp grid max
|
|
63
|
+
:type grid_max: np.ndarray
|
|
64
|
+
:param saving_info: saving infos
|
|
65
|
+
:type saving_info: dict
|
|
66
|
+
:param raster_profile: raster_profile
|
|
67
|
+
:type raster_profile: dict
|
|
68
|
+
:param row_coords: row cooordinates
|
|
69
|
+
:type row_coords: np.ndarray, optional
|
|
70
|
+
:param col_coords: col coordinates
|
|
71
|
+
:type col_coords: np.ndarray, optional
|
|
72
|
+
|
|
73
|
+
:return: disp range dataset
|
|
74
|
+
:rtype: xarray.Dataset
|
|
75
|
+
"""
|
|
76
|
+
|
|
77
|
+
if row_coords is None:
|
|
78
|
+
row_coords = np.arange(0, grid_min.shape[0])
|
|
79
|
+
|
|
80
|
+
if col_coords is None:
|
|
81
|
+
col_coords = np.arange(0, grid_min.shape[1])
|
|
82
|
+
|
|
83
|
+
disp_range_tile = xr.Dataset(
|
|
84
|
+
data_vars={
|
|
85
|
+
dm_cst.DISP_MIN_GRID: (["row", "col"], grid_min),
|
|
86
|
+
dm_cst.DISP_MAX_GRID: (["row", "col"], grid_max),
|
|
87
|
+
},
|
|
88
|
+
coords={
|
|
89
|
+
"row": row_coords,
|
|
90
|
+
"col": col_coords,
|
|
91
|
+
},
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
cars_dataset.fill_dataset(
|
|
95
|
+
disp_range_tile,
|
|
96
|
+
saving_info=saving_info,
|
|
97
|
+
window=window,
|
|
98
|
+
profile=raster_profile,
|
|
99
|
+
attributes=None,
|
|
100
|
+
overlaps=None,
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
return disp_range_tile
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def generate_disp_range_const_tile_wrapper(
|
|
107
|
+
row_range,
|
|
108
|
+
col_range,
|
|
109
|
+
dmin,
|
|
110
|
+
dmax,
|
|
111
|
+
raster_profile,
|
|
112
|
+
saving_info,
|
|
113
|
+
saving_info_global_infos,
|
|
114
|
+
):
|
|
115
|
+
"""
|
|
116
|
+
Generate disparity range dataset from constant dmin and dmax
|
|
117
|
+
|
|
118
|
+
:param row_range: Row range
|
|
119
|
+
:type row_range: list
|
|
120
|
+
:param col_range: Column range.
|
|
121
|
+
:type col_range: list
|
|
122
|
+
:param dmin: disparity minimum.
|
|
123
|
+
:type dmin: float
|
|
124
|
+
:param dmax: disparity maximum.
|
|
125
|
+
:type dmax: float
|
|
126
|
+
:param raster_profile: The raster profile.
|
|
127
|
+
:type raster_profile: dict
|
|
128
|
+
:param saving_info: The disp range grid saving information.
|
|
129
|
+
:type saving_info: dict
|
|
130
|
+
:param saving_info_global_infos: Global info saving infos.
|
|
131
|
+
:type saving_info_global_infos: dict
|
|
132
|
+
|
|
133
|
+
:return: Disparity range grid
|
|
134
|
+
:rtype: dict
|
|
135
|
+
"""
|
|
136
|
+
grid_min = np.empty((len(row_range), len(col_range)))
|
|
137
|
+
grid_max = np.empty((len(row_range), len(col_range)))
|
|
138
|
+
grid_min[:, :] = dmin
|
|
139
|
+
grid_max[:, :] = dmax
|
|
140
|
+
|
|
141
|
+
mono_tile_saving_info = ocht.update_saving_infos(saving_info, row=0, col=0)
|
|
142
|
+
disp_range = generate_disp_grids_dataset(
|
|
143
|
+
grid_min, grid_max, mono_tile_saving_info, raster_profile, window=None
|
|
144
|
+
)
|
|
145
|
+
|
|
146
|
+
# Generate infos on global min and max
|
|
147
|
+
global_infos = cars_dict.CarsDict(
|
|
148
|
+
{"global_min": np.nanmin(dmin), "global_max": np.nanmin(dmax)}
|
|
149
|
+
)
|
|
150
|
+
cars_dataset.fill_dict(global_infos, saving_info=saving_info_global_infos)
|
|
151
|
+
|
|
152
|
+
return disp_range, global_infos
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
def generate_disp_range_from_dem_wrapper(
|
|
156
|
+
epipolar_grid_array_window,
|
|
157
|
+
full_epi_row_range,
|
|
158
|
+
full_epi_col_range,
|
|
159
|
+
sensor_image_right,
|
|
160
|
+
grid_right,
|
|
161
|
+
geom_plugin_with_dem_and_geoid,
|
|
162
|
+
dem_median,
|
|
163
|
+
dem_min,
|
|
164
|
+
dem_max,
|
|
165
|
+
altitude_delta_min,
|
|
166
|
+
altitude_delta_max,
|
|
167
|
+
raster_profile,
|
|
168
|
+
saving_info,
|
|
169
|
+
saving_info_global_infos,
|
|
170
|
+
filter_overlap,
|
|
171
|
+
disp_to_alt_ratio,
|
|
172
|
+
disp_min_threshold=None,
|
|
173
|
+
disp_max_threshold=None,
|
|
174
|
+
):
|
|
175
|
+
"""
|
|
176
|
+
Generate disparity range dataset from dems
|
|
177
|
+
|
|
178
|
+
:param epipolar_grid_array_window: The window of the epipolar grid array.
|
|
179
|
+
:type epipolar_grid_array_window: dict
|
|
180
|
+
:param full_epi_row_range: The full range of rows in the epipolar grid.
|
|
181
|
+
:type full_epi_row_range: list
|
|
182
|
+
:param full_epi_col_range: The full range of columns in the epipolar grid.
|
|
183
|
+
:type full_epi_col_range: list
|
|
184
|
+
:param sensor_image_right: The right sensor image.
|
|
185
|
+
:type sensor_image_right: dict
|
|
186
|
+
:param grid_right: The right epipolar grid.
|
|
187
|
+
:type grid_right: dict
|
|
188
|
+
:param geom_plugin_with_dem_and_geoid: The geometry plugin with DEM.
|
|
189
|
+
:type geom_plugin_with_dem_and_geoid: object
|
|
190
|
+
:param dem_median: Path of dem median.
|
|
191
|
+
:type dem_median: str
|
|
192
|
+
:param dem_min: Path of dem min.
|
|
193
|
+
:type dem_min: str
|
|
194
|
+
:param dem_max: Path of dem max.
|
|
195
|
+
:type dem_max: srt
|
|
196
|
+
:param altitude_delta_min: The minimum altitude delta.
|
|
197
|
+
:type altitude_delta_min: float
|
|
198
|
+
:param altitude_delta_max: The maximum altitude delta.
|
|
199
|
+
:type altitude_delta_max: float
|
|
200
|
+
:param raster_profile: The raster profile.
|
|
201
|
+
:type raster_profile: dict
|
|
202
|
+
:param saving_info: The disp range grid saving information.
|
|
203
|
+
:type saving_info: dict
|
|
204
|
+
:param saving_info_global_infos: Global info saving infos.
|
|
205
|
+
:type saving_info_global_infos: dict
|
|
206
|
+
:param filter_overlap: The overlap to use for filtering.
|
|
207
|
+
:type filter_overlap: int
|
|
208
|
+
:param disp_to_alt_ratio: disparity to altitude ratio
|
|
209
|
+
:type disp_to_alt_ratio: float
|
|
210
|
+
:param disp_min_threshold: The minimum disparity threshold.
|
|
211
|
+
:type disp_min_threshold: float, optional
|
|
212
|
+
:param disp_max_threshold: The maximum disparity threshold.
|
|
213
|
+
:type disp_max_threshold: float, optional
|
|
214
|
+
|
|
215
|
+
:return: Disparity range grid
|
|
216
|
+
:rtype: dict
|
|
217
|
+
"""
|
|
218
|
+
|
|
219
|
+
# Geometry plugin
|
|
220
|
+
geo_plugin = geom_plugin_with_dem_and_geoid
|
|
221
|
+
|
|
222
|
+
# get epsg
|
|
223
|
+
terrain_epsg = inputs.rasterio_get_epsg(dem_median)
|
|
224
|
+
|
|
225
|
+
# Get epipolar position of all dem mean
|
|
226
|
+
transform_dem_median = inputs.rasterio_get_transform(dem_median)
|
|
227
|
+
|
|
228
|
+
# use local disparity
|
|
229
|
+
|
|
230
|
+
# Get associated alti mean / min / max values
|
|
231
|
+
dem_median_shape = inputs.rasterio_get_size(dem_median)
|
|
232
|
+
dem_median_width, dem_median_height = dem_median_shape
|
|
233
|
+
|
|
234
|
+
# get corresponding window from epipolar_array_window
|
|
235
|
+
epi_grid_margin = filter_overlap + 1
|
|
236
|
+
epi_grid_row_min = epipolar_grid_array_window["row_min"]
|
|
237
|
+
epi_grid_row_max = epipolar_grid_array_window["row_max"]
|
|
238
|
+
epi_grid_col_min = epipolar_grid_array_window["col_min"]
|
|
239
|
+
epi_grid_col_max = epipolar_grid_array_window["col_max"]
|
|
240
|
+
|
|
241
|
+
def clip(value, min_value, max_value):
|
|
242
|
+
"""
|
|
243
|
+
Clip a value inside bounds
|
|
244
|
+
"""
|
|
245
|
+
return int(max(min_value, min(value, max_value)))
|
|
246
|
+
|
|
247
|
+
# Epi grid tile coordinate to use, with and without margins
|
|
248
|
+
epi_grid_row_min_with_margin = clip(
|
|
249
|
+
epi_grid_row_min - epi_grid_margin, 0, len(full_epi_row_range)
|
|
250
|
+
)
|
|
251
|
+
epi_grid_row_max_with_margin = clip(
|
|
252
|
+
epi_grid_row_max + epi_grid_margin, 0, len(full_epi_row_range)
|
|
253
|
+
)
|
|
254
|
+
epi_grid_col_min_with_margin = clip(
|
|
255
|
+
epi_grid_col_min - epi_grid_margin, 0, len(full_epi_col_range)
|
|
256
|
+
)
|
|
257
|
+
epi_grid_col_max_with_margin = clip(
|
|
258
|
+
epi_grid_col_max + epi_grid_margin, 0, len(full_epi_col_range)
|
|
259
|
+
)
|
|
260
|
+
|
|
261
|
+
# range to use for epipolar interpolation
|
|
262
|
+
row_range_with_margin = full_epi_row_range[
|
|
263
|
+
epi_grid_row_min_with_margin:epi_grid_row_max_with_margin
|
|
264
|
+
]
|
|
265
|
+
row_range_no_margin = full_epi_row_range[epi_grid_row_min:epi_grid_row_max]
|
|
266
|
+
col_range_with_margin = full_epi_col_range[
|
|
267
|
+
epi_grid_col_min_with_margin:epi_grid_col_max_with_margin
|
|
268
|
+
]
|
|
269
|
+
col_range_no_margin = full_epi_col_range[epi_grid_col_min:epi_grid_col_max]
|
|
270
|
+
|
|
271
|
+
# Loc on dem median
|
|
272
|
+
epi_bbox = [
|
|
273
|
+
(np.min(col_range_with_margin), np.min(row_range_with_margin)),
|
|
274
|
+
(np.min(col_range_with_margin), np.max(row_range_with_margin)),
|
|
275
|
+
(np.max(col_range_with_margin), np.min(row_range_with_margin)),
|
|
276
|
+
(np.max(col_range_with_margin), np.max(row_range_with_margin)),
|
|
277
|
+
]
|
|
278
|
+
sensor_bbox = geo_plugin.sensor_position_from_grid(grid_right, epi_bbox)
|
|
279
|
+
transform_sensor = inputs.rasterio_get_transform(
|
|
280
|
+
sensor_image_right["image"]["main_file"]
|
|
281
|
+
)
|
|
282
|
+
row_sensor_bbox, col_sensor_bbox = transform_physical_point_to_index(
|
|
283
|
+
~transform_sensor, sensor_bbox[:, 1], sensor_bbox[:, 0]
|
|
284
|
+
)
|
|
285
|
+
|
|
286
|
+
terrain_bbox = geo_plugin.direct_loc(
|
|
287
|
+
sensor_image_right["image"]["main_file"],
|
|
288
|
+
sensor_image_right["geomodel"],
|
|
289
|
+
col_sensor_bbox,
|
|
290
|
+
row_sensor_bbox,
|
|
291
|
+
)
|
|
292
|
+
|
|
293
|
+
# reshape terrain bbox
|
|
294
|
+
terrain_bbox = terrain_bbox[0:2].T
|
|
295
|
+
terrain_bbox[:, [1, 0]] = terrain_bbox[:, [0, 1]]
|
|
296
|
+
|
|
297
|
+
# get pixel location on dem median
|
|
298
|
+
pixel_roi_dem_mean = inputs.rasterio_get_pixel_points(
|
|
299
|
+
dem_median, terrain_bbox
|
|
300
|
+
)
|
|
301
|
+
|
|
302
|
+
# Add margins (for interpolation) and clip
|
|
303
|
+
dem_margin = 10 # arbitrary
|
|
304
|
+
roi_lower_row = np.floor(np.min(pixel_roi_dem_mean[:, 0])) - dem_margin
|
|
305
|
+
roi_upper_row = np.ceil(np.max(pixel_roi_dem_mean[:, 0])) + dem_margin
|
|
306
|
+
roi_lower_col = np.floor(np.min(pixel_roi_dem_mean[:, 1])) - dem_margin
|
|
307
|
+
roi_upper_col = np.ceil(np.max(pixel_roi_dem_mean[:, 1])) + dem_margin
|
|
308
|
+
|
|
309
|
+
min_row = clip(roi_lower_row, 0, dem_median_height)
|
|
310
|
+
max_row = clip(roi_upper_row, 0, dem_median_height)
|
|
311
|
+
min_col = clip(roi_lower_col, 0, dem_median_width)
|
|
312
|
+
max_col = clip(roi_upper_col, 0, dem_median_width)
|
|
313
|
+
|
|
314
|
+
# compute terrain positions to use (all dem min and max)
|
|
315
|
+
row_indexes = range(min_row, max_row)
|
|
316
|
+
col_indexes = range(min_col, max_col)
|
|
317
|
+
transformer = rasterio.transform.AffineTransformer(transform_dem_median)
|
|
318
|
+
|
|
319
|
+
indexes = np.array(list(itertools.product(row_indexes, col_indexes)))
|
|
320
|
+
|
|
321
|
+
row = indexes[:, 0]
|
|
322
|
+
col = indexes[:, 1]
|
|
323
|
+
x_mean, y_mean = transformer.xy(row, col)
|
|
324
|
+
terrain_positions = np.transpose(np.array([x_mean, y_mean]))
|
|
325
|
+
|
|
326
|
+
# dem mean in terrain_epsg
|
|
327
|
+
x_mean = terrain_positions[:, 0]
|
|
328
|
+
y_mean = terrain_positions[:, 1]
|
|
329
|
+
|
|
330
|
+
dem_median_list = inputs.rasterio_get_values(
|
|
331
|
+
dem_median, x_mean, y_mean, point_cloud_conversion
|
|
332
|
+
)
|
|
333
|
+
|
|
334
|
+
nan_mask = ~np.isnan(dem_median_list)
|
|
335
|
+
|
|
336
|
+
# transform to lon lat
|
|
337
|
+
terrain_position_lon_lat = projection.point_cloud_conversion(
|
|
338
|
+
terrain_positions, terrain_epsg, 4326
|
|
339
|
+
)
|
|
340
|
+
lon_mean = terrain_position_lon_lat[:, 0]
|
|
341
|
+
lat_mean = terrain_position_lon_lat[:, 1]
|
|
342
|
+
|
|
343
|
+
if None not in (dem_min, dem_max, dem_median):
|
|
344
|
+
# dem min and max are in 4326
|
|
345
|
+
dem_min_list = inputs.rasterio_get_values(
|
|
346
|
+
dem_min, lon_mean, lat_mean, point_cloud_conversion
|
|
347
|
+
)
|
|
348
|
+
dem_max_list = inputs.rasterio_get_values(
|
|
349
|
+
dem_max, lon_mean, lat_mean, point_cloud_conversion
|
|
350
|
+
)
|
|
351
|
+
nan_mask = nan_mask & ~np.isnan(dem_min_list) & ~np.isnan(dem_max_list)
|
|
352
|
+
else:
|
|
353
|
+
dem_min_list = dem_median_list - altitude_delta_min
|
|
354
|
+
dem_max_list = dem_median_list + altitude_delta_max
|
|
355
|
+
|
|
356
|
+
# filter nan value from input points
|
|
357
|
+
lon_mean = lon_mean[nan_mask]
|
|
358
|
+
lat_mean = lat_mean[nan_mask]
|
|
359
|
+
dem_median_list = dem_median_list[nan_mask]
|
|
360
|
+
dem_min_list = dem_min_list[nan_mask]
|
|
361
|
+
dem_max_list = dem_max_list[nan_mask]
|
|
362
|
+
|
|
363
|
+
# sensors physical positions
|
|
364
|
+
(
|
|
365
|
+
ind_cols_sensor,
|
|
366
|
+
ind_rows_sensor,
|
|
367
|
+
_,
|
|
368
|
+
) = geom_plugin_with_dem_and_geoid.inverse_loc(
|
|
369
|
+
sensor_image_right["image"]["main_file"],
|
|
370
|
+
sensor_image_right["geomodel"],
|
|
371
|
+
lat_mean,
|
|
372
|
+
lon_mean,
|
|
373
|
+
z_coord=dem_median_list,
|
|
374
|
+
)
|
|
375
|
+
|
|
376
|
+
# Generate epipolar disp grids
|
|
377
|
+
# Get epipolar positions
|
|
378
|
+
(epipolar_positions_row, epipolar_positions_col) = np.meshgrid(
|
|
379
|
+
col_range_with_margin,
|
|
380
|
+
row_range_with_margin,
|
|
381
|
+
)
|
|
382
|
+
epipolar_positions = np.stack(
|
|
383
|
+
[epipolar_positions_row, epipolar_positions_col], axis=2
|
|
384
|
+
)
|
|
385
|
+
|
|
386
|
+
# Get sensor position
|
|
387
|
+
sensors_positions = (
|
|
388
|
+
geom_plugin_with_dem_and_geoid.sensor_position_from_grid(
|
|
389
|
+
grid_right,
|
|
390
|
+
np.reshape(
|
|
391
|
+
epipolar_positions,
|
|
392
|
+
(
|
|
393
|
+
epipolar_positions.shape[0] * epipolar_positions.shape[1],
|
|
394
|
+
2,
|
|
395
|
+
),
|
|
396
|
+
),
|
|
397
|
+
)
|
|
398
|
+
)
|
|
399
|
+
|
|
400
|
+
# compute reverse matrix
|
|
401
|
+
transform_sensor = rasterio.Affine(
|
|
402
|
+
*np.abs(
|
|
403
|
+
inputs.rasterio_get_transform(
|
|
404
|
+
sensor_image_right["image"]["main_file"]
|
|
405
|
+
)
|
|
406
|
+
)
|
|
407
|
+
)
|
|
408
|
+
|
|
409
|
+
trans_inv = ~transform_sensor
|
|
410
|
+
# Transform to positive values
|
|
411
|
+
trans_inv = np.array(trans_inv)
|
|
412
|
+
trans_inv = np.reshape(trans_inv, (3, 3))
|
|
413
|
+
if trans_inv[0, 0] < 0:
|
|
414
|
+
trans_inv[0, :] *= -1
|
|
415
|
+
if trans_inv[1, 1] < 0:
|
|
416
|
+
trans_inv[1, :] *= -1
|
|
417
|
+
trans_inv = affine.Affine(*list(trans_inv.flatten()))
|
|
418
|
+
|
|
419
|
+
# Transform physical position to index
|
|
420
|
+
index_positions = np.empty(sensors_positions.shape)
|
|
421
|
+
for row_point in range(index_positions.shape[0]):
|
|
422
|
+
row_geo, col_geo = sensors_positions[row_point, :]
|
|
423
|
+
col, row = trans_inv * (row_geo, col_geo)
|
|
424
|
+
index_positions[row_point, :] = (row, col)
|
|
425
|
+
|
|
426
|
+
ind_rows_sensor_grid = index_positions[:, 0] - 0.5
|
|
427
|
+
ind_cols_sensor_grid = index_positions[:, 1] - 0.5
|
|
428
|
+
|
|
429
|
+
if len(ind_rows_sensor) < 5:
|
|
430
|
+
# QH6214 needs at least 4 points for interpolation
|
|
431
|
+
|
|
432
|
+
grid_min = np.empty(
|
|
433
|
+
(len(row_range_no_margin), len(col_range_no_margin))
|
|
434
|
+
)
|
|
435
|
+
grid_max = np.empty(
|
|
436
|
+
(len(row_range_no_margin), len(col_range_no_margin))
|
|
437
|
+
)
|
|
438
|
+
grid_min[:, :] = 0
|
|
439
|
+
grid_max[:, :] = 0
|
|
440
|
+
|
|
441
|
+
disp_range = generate_disp_grids_dataset(
|
|
442
|
+
grid_min,
|
|
443
|
+
grid_max,
|
|
444
|
+
saving_info,
|
|
445
|
+
raster_profile,
|
|
446
|
+
window=epipolar_grid_array_window,
|
|
447
|
+
row_coords=row_range_no_margin,
|
|
448
|
+
col_coords=col_range_no_margin,
|
|
449
|
+
)
|
|
450
|
+
|
|
451
|
+
# Generate infos on global min and max
|
|
452
|
+
global_infos = cars_dict.CarsDict(
|
|
453
|
+
{
|
|
454
|
+
"global_min": 0,
|
|
455
|
+
"global_max": 0,
|
|
456
|
+
}
|
|
457
|
+
)
|
|
458
|
+
cars_dataset.fill_dict(
|
|
459
|
+
global_infos, saving_info=saving_info_global_infos
|
|
460
|
+
)
|
|
461
|
+
|
|
462
|
+
return disp_range, global_infos
|
|
463
|
+
|
|
464
|
+
# Interpolate disparity
|
|
465
|
+
disp_min_points = -(dem_max_list - dem_median_list) / disp_to_alt_ratio
|
|
466
|
+
disp_max_points = -(dem_min_list - dem_median_list) / disp_to_alt_ratio
|
|
467
|
+
|
|
468
|
+
interp_min_linear = LinearInterpNearestExtrap(
|
|
469
|
+
list(zip(ind_rows_sensor, ind_cols_sensor)), # noqa: B905
|
|
470
|
+
disp_min_points,
|
|
471
|
+
)
|
|
472
|
+
interp_max_linear = LinearInterpNearestExtrap(
|
|
473
|
+
list(zip(ind_rows_sensor, ind_cols_sensor)), # noqa: B905
|
|
474
|
+
disp_max_points,
|
|
475
|
+
)
|
|
476
|
+
|
|
477
|
+
grid_min = np.reshape(
|
|
478
|
+
interp_min_linear(ind_rows_sensor_grid, ind_cols_sensor_grid),
|
|
479
|
+
(
|
|
480
|
+
epipolar_positions.shape[0],
|
|
481
|
+
epipolar_positions.shape[1],
|
|
482
|
+
),
|
|
483
|
+
)
|
|
484
|
+
|
|
485
|
+
grid_max = np.reshape(
|
|
486
|
+
interp_max_linear(ind_rows_sensor_grid, ind_cols_sensor_grid),
|
|
487
|
+
(
|
|
488
|
+
epipolar_positions.shape[0],
|
|
489
|
+
epipolar_positions.shape[1],
|
|
490
|
+
),
|
|
491
|
+
)
|
|
492
|
+
|
|
493
|
+
# Add margin
|
|
494
|
+
diff = grid_max - grid_min
|
|
495
|
+
logging.info("Max grid max - grid min : {} disp ".format(np.max(diff)))
|
|
496
|
+
|
|
497
|
+
if disp_min_threshold is not None:
|
|
498
|
+
if np.any(grid_min < disp_min_threshold):
|
|
499
|
+
logging.warning(
|
|
500
|
+
"Override disp_min with disp_min_threshold {}".format(
|
|
501
|
+
disp_min_threshold
|
|
502
|
+
)
|
|
503
|
+
)
|
|
504
|
+
grid_min[grid_min < disp_min_threshold] = disp_min_threshold
|
|
505
|
+
if disp_max_threshold is not None:
|
|
506
|
+
if np.any(grid_max > disp_max_threshold):
|
|
507
|
+
logging.warning(
|
|
508
|
+
"Override disp_max with disp_max_threshold {}".format(
|
|
509
|
+
disp_max_threshold
|
|
510
|
+
)
|
|
511
|
+
)
|
|
512
|
+
grid_max[grid_max > disp_max_threshold] = disp_max_threshold
|
|
513
|
+
|
|
514
|
+
# generate footprint
|
|
515
|
+
footprint_mask = create_circular_mask(filter_overlap, filter_overlap)
|
|
516
|
+
grid_min = minimum_filter(
|
|
517
|
+
grid_min, footprint=footprint_mask, mode="nearest"
|
|
518
|
+
)
|
|
519
|
+
grid_max = maximum_filter(
|
|
520
|
+
grid_max, footprint=footprint_mask, mode="nearest"
|
|
521
|
+
)
|
|
522
|
+
|
|
523
|
+
# Create xarray dataset
|
|
524
|
+
disp_range = generate_disp_grids_dataset(
|
|
525
|
+
grid_min,
|
|
526
|
+
grid_max,
|
|
527
|
+
saving_info,
|
|
528
|
+
raster_profile,
|
|
529
|
+
window=epipolar_grid_array_window,
|
|
530
|
+
row_coords=row_range_with_margin,
|
|
531
|
+
col_coords=col_range_with_margin,
|
|
532
|
+
)
|
|
533
|
+
|
|
534
|
+
# crop epipolar grid from margin added for propagation filter
|
|
535
|
+
disp_range = disp_range.sel(
|
|
536
|
+
row=list(row_range_no_margin), col=list(col_range_no_margin)
|
|
537
|
+
)
|
|
538
|
+
|
|
539
|
+
# Generate infos on global min and max
|
|
540
|
+
global_infos = cars_dict.CarsDict(
|
|
541
|
+
{
|
|
542
|
+
"global_min": np.floor(np.nanmin(grid_min)),
|
|
543
|
+
"global_max": np.ceil(np.nanmax(grid_max)),
|
|
544
|
+
}
|
|
545
|
+
)
|
|
546
|
+
cars_dataset.fill_dict(global_infos, saving_info=saving_info_global_infos)
|
|
547
|
+
|
|
548
|
+
return disp_range, global_infos
|
|
549
|
+
|
|
550
|
+
|
|
551
|
+
def create_circular_mask(height, width):
|
|
552
|
+
"""
|
|
553
|
+
Create a circular mask for footprint around pixel
|
|
554
|
+
|
|
555
|
+
:param height: height of footprint
|
|
556
|
+
:type height: int
|
|
557
|
+
:param width: width of footprint
|
|
558
|
+
:type width: int
|
|
559
|
+
|
|
560
|
+
:return: mask representing circular footprint
|
|
561
|
+
:rtype: np.ndarray
|
|
562
|
+
"""
|
|
563
|
+
center = (int(width / 2), int(height / 2))
|
|
564
|
+
radius = min(center[0], center[1], width - center[0], height - center[1])
|
|
565
|
+
|
|
566
|
+
y_grid, x_grid = np.ogrid[:height, :width]
|
|
567
|
+
dist_from_center = np.sqrt(
|
|
568
|
+
(x_grid - center[0]) ** 2 + (y_grid - center[1]) ** 2
|
|
569
|
+
)
|
|
570
|
+
|
|
571
|
+
mask = dist_from_center <= radius
|
|
572
|
+
return mask.astype(bool)
|
|
@@ -31,7 +31,6 @@ import os
|
|
|
31
31
|
|
|
32
32
|
# Third party imports
|
|
33
33
|
import numpy as np
|
|
34
|
-
import pandas
|
|
35
34
|
import rasterio as rio
|
|
36
35
|
from scipy.interpolate import LinearNDInterpolator
|
|
37
36
|
from scipy.spatial import Delaunay # pylint: disable=E0611
|
|
@@ -45,7 +44,6 @@ from cars.applications.grid_generation import (
|
|
|
45
44
|
from cars.core.utils import safe_makedirs
|
|
46
45
|
|
|
47
46
|
# CARS imports
|
|
48
|
-
from cars.data_structures import cars_dataset
|
|
49
47
|
from cars.orchestrator.cluster.log_wrapper import cars_profile
|
|
50
48
|
|
|
51
49
|
|
|
@@ -171,7 +169,6 @@ def correct_grid(grid, grid_correction, pair_folder, save_grid=None):
|
|
|
171
169
|
def estimate_right_grid_correction(
|
|
172
170
|
matches,
|
|
173
171
|
grid_right,
|
|
174
|
-
initial_cars_ds=None,
|
|
175
172
|
save_matches=False,
|
|
176
173
|
minimum_nb_matches=100,
|
|
177
174
|
pair_folder="",
|
|
@@ -480,13 +477,6 @@ def estimate_right_grid_correction(
|
|
|
480
477
|
)
|
|
481
478
|
np.save(matches_array_path, corrected_matches)
|
|
482
479
|
|
|
483
|
-
# Create CarsDataset containing corrected matches, with same tiling as input
|
|
484
|
-
corrected_matches_cars_ds = None
|
|
485
|
-
if initial_cars_ds is not None:
|
|
486
|
-
corrected_matches_cars_ds = create_matches_cars_ds(
|
|
487
|
-
corrected_matches, initial_cars_ds
|
|
488
|
-
)
|
|
489
|
-
|
|
490
480
|
# Update orchestrator out_json
|
|
491
481
|
corrected_matches_infos = {
|
|
492
482
|
application_constants.APPLICATION_TAG: {
|
|
@@ -498,49 +488,6 @@ def estimate_right_grid_correction(
|
|
|
498
488
|
return (
|
|
499
489
|
grid_correction,
|
|
500
490
|
corrected_matches,
|
|
501
|
-
corrected_matches_cars_ds,
|
|
502
491
|
in_stats,
|
|
503
492
|
out_stats,
|
|
504
493
|
)
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
def create_matches_cars_ds(corrected_matches, initial_cars_ds):
|
|
508
|
-
"""
|
|
509
|
-
Create CarsDataset representing matches, from numpy matches.
|
|
510
|
-
Matches are split into tiles, stored in pandas DataFrames
|
|
511
|
-
|
|
512
|
-
Right CarsDataset is filled with Nones
|
|
513
|
-
|
|
514
|
-
:param corrected_matches: matches
|
|
515
|
-
:type corrected_matches: numpy array
|
|
516
|
-
:param initial_cars_ds: cars dataset to use tiling from
|
|
517
|
-
:type initial_cars_ds: CarsDataset
|
|
518
|
-
|
|
519
|
-
:return new_matches_cars_ds
|
|
520
|
-
:rtype: CarsDataset
|
|
521
|
-
"""
|
|
522
|
-
|
|
523
|
-
# initialize CarsDataset
|
|
524
|
-
new_matches_cars_ds = cars_dataset.CarsDataset("points")
|
|
525
|
-
new_matches_cars_ds.create_empty_copy(initial_cars_ds)
|
|
526
|
-
new_matches_cars_ds.attributes = initial_cars_ds.attributes
|
|
527
|
-
|
|
528
|
-
for row in range(new_matches_cars_ds.shape[0]):
|
|
529
|
-
for col in range(new_matches_cars_ds.shape[1]):
|
|
530
|
-
[
|
|
531
|
-
row_min,
|
|
532
|
-
row_max,
|
|
533
|
-
col_min,
|
|
534
|
-
col_max,
|
|
535
|
-
] = new_matches_cars_ds.tiling_grid[row, col, :]
|
|
536
|
-
|
|
537
|
-
# Get corresponding matches
|
|
538
|
-
tile_matches = corrected_matches[corrected_matches[:, 1] > row_min]
|
|
539
|
-
tile_matches = tile_matches[tile_matches[:, 1] < row_max]
|
|
540
|
-
tile_matches = tile_matches[tile_matches[:, 0] > col_min]
|
|
541
|
-
tile_matches = tile_matches[tile_matches[:, 0] < col_max]
|
|
542
|
-
|
|
543
|
-
# Create pandas DataFrame
|
|
544
|
-
new_matches_cars_ds[row, col] = pandas.DataFrame(tile_matches)
|
|
545
|
-
|
|
546
|
-
return new_matches_cars_ds
|
|
@@ -41,9 +41,9 @@ def transform_grid_func(grid, resolution, right=False):
|
|
|
41
41
|
for key, value in grid.items():
|
|
42
42
|
if right:
|
|
43
43
|
if key not in ("grid_origin", "grid_spacing"):
|
|
44
|
-
|
|
44
|
+
scale(key, value, grid, resolution)
|
|
45
45
|
else:
|
|
46
|
-
|
|
46
|
+
scale(key, value, grid, resolution)
|
|
47
47
|
|
|
48
48
|
# we need to charge the data to override it
|
|
49
49
|
with rasterio.open(grid["path"]) as src:
|
|
@@ -59,9 +59,9 @@ def transform_grid_func(grid, resolution, right=False):
|
|
|
59
59
|
return grid
|
|
60
60
|
|
|
61
61
|
|
|
62
|
-
def
|
|
62
|
+
def scale(key, value, grid, resolution):
|
|
63
63
|
"""
|
|
64
|
-
|
|
64
|
+
Scale attributes by the resolution
|
|
65
65
|
"""
|
|
66
66
|
|
|
67
67
|
if key == "grid_origin":
|
|
@@ -71,7 +71,7 @@ def divide(key, value, grid, resolution):
|
|
|
71
71
|
for i, _ in enumerate(value):
|
|
72
72
|
grid[key][i] = np.floor(value[i] / resolution)
|
|
73
73
|
elif key == "disp_to_alt_ratio":
|
|
74
|
-
grid[key] = value
|
|
74
|
+
grid[key] = value * resolution
|
|
75
75
|
elif key == "epipolar_size_x":
|
|
76
76
|
grid[key] = np.floor(value / resolution)
|
|
77
77
|
elif key == "epipolar_size_y":
|