cars 1.0.0a1__cp312-cp312-win_amd64.whl → 1.0.0a2__cp312-cp312-win_amd64.whl

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

Potentially problematic release.


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

Files changed (49) hide show
  1. cars/__init__.py +4 -4
  2. cars/applications/dem_generation/dem_generation_wrappers.py +5 -1
  3. cars/applications/dem_generation/dichotomic_generation_app.py +21 -6
  4. cars/applications/dem_generation/rasterization_app.py +70 -27
  5. cars/applications/dense_match_filling/abstract_dense_match_filling_app.py +4 -0
  6. cars/applications/dense_match_filling/cpp/dense_match_filling_cpp.cp312-win_amd64.dll.a +0 -0
  7. cars/applications/dense_match_filling/cpp/dense_match_filling_cpp.cp312-win_amd64.pyd +0 -0
  8. cars/applications/dense_match_filling/fill_disp_algo.py +41 -12
  9. cars/applications/dense_match_filling/plane_app.py +11 -0
  10. cars/applications/dense_match_filling/zero_padding_app.py +11 -1
  11. cars/applications/dense_matching/census_mccnn_sgm_app.py +262 -545
  12. cars/applications/dense_matching/cpp/dense_matching_cpp.cp312-win_amd64.dll.a +0 -0
  13. cars/applications/dense_matching/cpp/dense_matching_cpp.cp312-win_amd64.pyd +0 -0
  14. cars/applications/dense_matching/dense_matching_algo.py +59 -11
  15. cars/applications/dense_matching/dense_matching_wrappers.py +51 -31
  16. cars/applications/dense_matching/disparity_grid_algo.py +572 -0
  17. cars/applications/grid_generation/grid_correction_app.py +0 -53
  18. cars/applications/grid_generation/transform_grid.py +5 -5
  19. cars/applications/point_cloud_fusion/pc_fusion_algo.py +17 -11
  20. cars/applications/point_cloud_fusion/pc_fusion_wrappers.py +3 -4
  21. cars/applications/rasterization/rasterization_algo.py +20 -27
  22. cars/applications/rasterization/rasterization_wrappers.py +6 -5
  23. cars/applications/rasterization/simple_gaussian_app.py +2 -14
  24. cars/applications/sparse_matching/sparse_matching_wrappers.py +0 -49
  25. cars/applications/triangulation/line_of_sight_intersection_app.py +1 -1
  26. cars/applications/triangulation/triangulation_wrappers.py +2 -1
  27. cars/bundleadjustment.py +51 -11
  28. cars/cars.py +15 -5
  29. cars/core/constants.py +1 -1
  30. cars/core/geometry/abstract_geometry.py +54 -11
  31. cars/core/geometry/shareloc_geometry.py +59 -14
  32. cars/orchestrator/registry/saver_registry.py +0 -78
  33. cars/pipelines/default/default_pipeline.py +23 -26
  34. cars/pipelines/parameters/depth_map_inputs.py +22 -67
  35. cars/pipelines/parameters/dsm_inputs.py +16 -29
  36. cars/pipelines/parameters/sensor_inputs.py +20 -21
  37. cars/pipelines/parameters/sensor_loaders/basic_sensor_loader.py +3 -3
  38. cars/pipelines/parameters/sensor_loaders/pivot_sensor_loader.py +2 -2
  39. cars/pipelines/parameters/sensor_loaders/sensor_loader.py +4 -6
  40. cars/pipelines/parameters/sensor_loaders/sensor_loader_template.py +2 -2
  41. cars/pipelines/pipeline.py +8 -8
  42. cars/pipelines/unit/unit_pipeline.py +103 -196
  43. cars/starter.py +20 -1
  44. cars-1.0.0a2.dist-info/DELVEWHEEL +2 -0
  45. {cars-1.0.0a1.dist-info → cars-1.0.0a2.dist-info}/METADATA +3 -2
  46. {cars-1.0.0a1.dist-info → cars-1.0.0a2.dist-info}/RECORD +48 -47
  47. cars-1.0.0a1.dist-info/DELVEWHEEL +0 -2
  48. {cars-1.0.0a1.dist-info → cars-1.0.0a2.dist-info}/WHEEL +0 -0
  49. {cars-1.0.0a1.dist-info → cars-1.0.0a2.dist-info}/entry_points.txt +0 -0
@@ -26,25 +26,21 @@ import collections
26
26
 
27
27
  # Standard imports
28
28
  import copy
29
- import itertools
30
29
  import logging
31
30
  import math
32
31
  import os
33
32
  from typing import Dict, Tuple
34
33
 
35
34
  # Third party imports
36
- import affine
37
35
  import numpy as np
38
- import pandas
39
36
  import pandora
40
- import rasterio
41
37
  import xarray as xr
42
38
  from affine import Affine
43
39
  from json_checker import And, Checker, Or
44
40
  from pandora.check_configuration import check_pipeline_section
45
41
  from pandora.img_tools import add_global_disparity
46
42
  from pandora.state_machine import PandoraMachine
47
- from scipy.ndimage import generic_filter
43
+ from scipy.ndimage import maximum_filter, minimum_filter
48
44
 
49
45
  import cars.applications.dense_matching.dense_matching_constants as dm_cst
50
46
  import cars.orchestrator.orchestrator as ocht
@@ -56,8 +52,9 @@ from cars.applications.dense_matching import (
56
52
  from cars.applications.dense_matching.abstract_dense_matching_app import (
57
53
  DenseMatching,
58
54
  )
59
- from cars.applications.dense_matching.dense_matching_algo import (
60
- LinearInterpNearestExtrap,
55
+ from cars.applications.dense_matching.disparity_grid_algo import (
56
+ generate_disp_range_const_tile_wrapper,
57
+ generate_disp_range_from_dem_wrapper,
61
58
  )
62
59
  from cars.applications.dense_matching.loaders.pandora_loader import (
63
60
  PandoraLoader,
@@ -66,8 +63,7 @@ from cars.applications.dense_matching.loaders.pandora_loader import (
66
63
  # CARS imports
67
64
  from cars.core import constants as cst
68
65
  from cars.core import constants_disparity as cst_disp
69
- from cars.core import inputs, projection
70
- from cars.core.projection import point_cloud_conversion
66
+ from cars.core import inputs, tiling
71
67
  from cars.core.utils import safe_makedirs
72
68
  from cars.data_structures import cars_dataset, format_transformation
73
69
  from cars.orchestrator.cluster.log_wrapper import cars_profile
@@ -126,6 +122,9 @@ class CensusMccnnSgm(
126
122
  self.disp_range_propagation_filter_size = self.used_config[
127
123
  "disp_range_propagation_filter_size"
128
124
  ]
125
+ self.epi_disp_grid_tile_size = self.used_config[
126
+ "epi_disp_grid_tile_size"
127
+ ]
129
128
  self.use_cross_validation = self.used_config["use_cross_validation"]
130
129
  self.denoise_disparity_map = self.used_config["denoise_disparity_map"]
131
130
  self.required_bands = self.used_config["required_bands"]
@@ -133,7 +132,11 @@ class CensusMccnnSgm(
133
132
 
134
133
  # Saving files
135
134
  self.save_intermediate_data = self.used_config["save_intermediate_data"]
135
+
136
136
  self.confidence_filtering = self.used_config["confidence_filtering"]
137
+ self.threshold_disp_range_to_borders = self.used_config[
138
+ "threshold_disp_range_to_borders"
139
+ ]
137
140
 
138
141
  # init orchestrator
139
142
  self.orchestrator = None
@@ -232,13 +235,19 @@ class CensusMccnnSgm(
232
235
  "use_global_disp_range", False
233
236
  )
234
237
  overloaded_conf["local_disp_grid_step"] = conf.get(
235
- "local_disp_grid_step", 30
238
+ "local_disp_grid_step", 10
236
239
  )
237
240
  overloaded_conf["disp_range_propagation_filter_size"] = conf.get(
238
- "disp_range_propagation_filter_size", 300
241
+ "disp_range_propagation_filter_size", 50
242
+ )
243
+ overloaded_conf["epi_disp_grid_tile_size"] = conf.get(
244
+ "epi_disp_grid_tile_size", 800
239
245
  )
240
246
  overloaded_conf["required_bands"] = conf.get("required_bands", ["b0"])
241
247
  overloaded_conf["used_band"] = conf.get("used_band", "b0")
248
+ overloaded_conf["threshold_disp_range_to_borders"] = conf.get(
249
+ "threshold_disp_range_to_borders", False
250
+ )
242
251
 
243
252
  # Saving files
244
253
  overloaded_conf["save_intermediate_data"] = conf.get(
@@ -360,11 +369,13 @@ class CensusMccnnSgm(
360
369
  "disp_range_propagation_filter_size": And(
361
370
  Or(int, float), lambda x: x >= 0
362
371
  ),
372
+ "epi_disp_grid_tile_size": int,
363
373
  "required_bands": [str],
364
374
  "used_band": str,
365
375
  "loader_conf": Or(dict, collections.OrderedDict, str, None),
366
376
  "loader": str,
367
377
  "confidence_filtering": dict,
378
+ "threshold_disp_range_to_borders": bool,
368
379
  }
369
380
 
370
381
  # Check conf
@@ -416,33 +427,41 @@ class CensusMccnnSgm(
416
427
  overloaded_conf["confidence_filtering"]["activated"] = overloaded_conf[
417
428
  "confidence_filtering"
418
429
  ].get("activated", True)
419
- overloaded_conf["confidence_filtering"]["upper_bound"] = (
420
- overloaded_conf["confidence_filtering"].get("upper_bound", 5)
430
+ overloaded_conf["confidence_filtering"]["bounds_ratio_threshold"] = (
431
+ overloaded_conf["confidence_filtering"].get(
432
+ "bounds_ratio_threshold", 0.2
433
+ )
434
+ )
435
+ overloaded_conf["confidence_filtering"]["risk_ratio_threshold"] = (
436
+ overloaded_conf["confidence_filtering"].get(
437
+ "risk_ratio_threshold", 0.8
438
+ )
421
439
  )
422
- overloaded_conf["confidence_filtering"]["lower_bound"] = (
423
- overloaded_conf["confidence_filtering"].get("lower_bound", -30)
440
+ overloaded_conf["confidence_filtering"]["bounds_range_threshold"] = (
441
+ overloaded_conf["confidence_filtering"].get(
442
+ "bounds_range_threshold", 4
443
+ )
444
+ )
445
+ overloaded_conf["confidence_filtering"]["risk_range_threshold"] = (
446
+ overloaded_conf["confidence_filtering"].get(
447
+ "risk_range_threshold", 12
448
+ )
424
449
  )
425
- overloaded_conf["confidence_filtering"]["risk_max"] = overloaded_conf[
426
- "confidence_filtering"
427
- ].get("risk_max", 60)
428
450
  overloaded_conf["confidence_filtering"]["nan_threshold"] = (
429
- overloaded_conf["confidence_filtering"].get("nan_threshold", 0.1)
451
+ overloaded_conf["confidence_filtering"].get("nan_threshold", 0.2)
430
452
  )
431
453
  overloaded_conf["confidence_filtering"]["win_nanratio"] = (
432
454
  overloaded_conf["confidence_filtering"].get("win_nanratio", 20)
433
455
  )
434
- overloaded_conf["confidence_filtering"]["win_mean_risk_max"] = (
435
- overloaded_conf["confidence_filtering"].get("win_mean_risk_max", 7)
436
- )
437
456
 
438
457
  confidence_filtering_schema = {
439
458
  "activated": bool,
440
- "upper_bound": int,
441
- "lower_bound": int,
442
- "risk_max": int,
459
+ "bounds_ratio_threshold": float,
460
+ "risk_ratio_threshold": float,
461
+ "bounds_range_threshold": int,
462
+ "risk_range_threshold": int,
443
463
  "nan_threshold": float,
444
464
  "win_nanratio": int,
445
- "win_mean_risk_max": int,
446
465
  }
447
466
 
448
467
  checker_confidence_filtering_schema = Checker(
@@ -464,12 +483,16 @@ class CensusMccnnSgm(
464
483
 
465
484
  """
466
485
 
467
- disp_min_grid_arr = disp_range_grid[0, 0]["disp_min_grid"].values
468
- disp_max_grid_arr = disp_range_grid[0, 0]["disp_max_grid"].values
469
- step_row = disp_range_grid.attributes["step_row"]
470
- step_col = disp_range_grid.attributes["step_col"]
471
- row_range = disp_range_grid.attributes["row_range"]
472
- col_range = disp_range_grid.attributes["col_range"]
486
+ disp_min_grid_arr, _ = inputs.rasterio_read_as_array(
487
+ disp_range_grid["grid_min_path"]
488
+ )
489
+ disp_max_grid_arr, _ = inputs.rasterio_read_as_array(
490
+ disp_range_grid["grid_max_path"]
491
+ )
492
+ step_row = disp_range_grid["step_row"]
493
+ step_col = disp_range_grid["step_col"]
494
+ row_range = disp_range_grid["row_range"]
495
+ col_range = disp_range_grid["col_range"]
473
496
 
474
497
  # get disp_to_alt_ratio
475
498
  disp_to_alt_ratio = grid_left["disp_to_alt_ratio"]
@@ -585,19 +608,24 @@ class CensusMccnnSgm(
585
608
 
586
609
  """
587
610
 
588
- disp_min_grids = disp_range_grid[0, 0][dm_cst.DISP_MIN_GRID].values
589
- disp_max_grids = disp_range_grid[0, 0][dm_cst.DISP_MAX_GRID].values
611
+ disp_min_grids, _ = inputs.rasterio_read_as_array(
612
+ disp_range_grid["grid_min_path"]
613
+ )
614
+ disp_max_grids, _ = inputs.rasterio_read_as_array(
615
+ disp_range_grid["grid_max_path"]
616
+ )
590
617
 
591
618
  # use max tile size as overlap for min and max:
592
619
  # max Point to point diff is less than diff of tile
593
620
 
594
621
  # use filter of size max_epi_tile_size
595
622
  overlap = 3 * int(self.max_epi_tile_size / self.local_disp_grid_step)
596
- disp_min_grids = generic_filter(
597
- disp_min_grids, np.nanmin, [overlap, overlap], mode="nearest"
623
+
624
+ disp_min_grids = minimum_filter(
625
+ disp_min_grids, size=[overlap, overlap], mode="nearest"
598
626
  )
599
- disp_max_grids = generic_filter(
600
- disp_max_grids, np.nanmax, [overlap, overlap], mode="nearest"
627
+ disp_max_grids = maximum_filter(
628
+ disp_max_grids, size=[overlap, overlap], mode="nearest"
601
629
  )
602
630
 
603
631
  # Worst cases scenario:
@@ -692,7 +720,7 @@ class CensusMccnnSgm(
692
720
  dem_min=None,
693
721
  dem_max=None,
694
722
  pair_folder=None,
695
- loc_inverse_orchestrator=None,
723
+ orchestrator=None,
696
724
  ):
697
725
  """
698
726
  Generate disparity grids min and max, with given step
@@ -724,18 +752,24 @@ class CensusMccnnSgm(
724
752
  :type dem_max: str
725
753
  :param pair_folder: folder used for current pair
726
754
  :type pair_folder: str
727
- :param loc_inverse_orchestrator: orchestrator to perform inverse locs
728
- :type loc_inverse_orchestrator: Orchestrator
755
+ :param orchestrator: orchestrator
756
+ :type orchestrator: Orchestrator
729
757
 
730
758
 
731
759
  :return disparity grid range, containing grid min and max
732
760
  :rtype: CarsDataset
733
761
  """
734
762
 
735
- # Create sequential orchestrator for savings
736
- grid_orchestrator = ocht.Orchestrator(
737
- orchestrator_conf={"mode": "sequential"}
738
- )
763
+ # Default orchestrator
764
+ if orchestrator is None:
765
+ # Create default sequential orchestrator for current application
766
+ # be aware, no out_json will be shared between orchestrators
767
+ # No files saved
768
+ self.orchestrator = ocht.Orchestrator(
769
+ orchestrator_conf={"mode": "sequential"}
770
+ )
771
+ else:
772
+ self.orchestrator = orchestrator
739
773
 
740
774
  epi_size_row = grid_right["epipolar_size_y"]
741
775
  epi_size_col = grid_right["epipolar_size_x"]
@@ -751,46 +785,50 @@ class CensusMccnnSgm(
751
785
  0, epi_size_col, nb_cols, retstep=True
752
786
  )
753
787
 
754
- grid_min = np.empty((len(row_range), len(col_range)))
755
- grid_max = np.empty((len(row_range), len(col_range)))
756
-
757
788
  # Create CarsDataset
758
789
  grid_disp_range = cars_dataset.CarsDataset(
759
790
  "arrays", name="grid_disp_range_unknown_pair"
760
791
  )
761
- # Only one tile
762
- grid_disp_range.tiling_grid = np.array(
763
- [[[0, epi_size_row, 0, epi_size_col]]]
792
+ global_infos_cars_ds = cars_dataset.CarsDataset("dict")
793
+
794
+ # Generate profile
795
+ geotransform = (
796
+ epi_size_row,
797
+ step_row,
798
+ 0.0,
799
+ epi_size_col,
800
+ 0.0,
801
+ step_col,
764
802
  )
765
803
 
766
- grid_attributes = {
767
- "step_row": step_row,
768
- "step_col": step_col,
769
- "row_range": row_range,
770
- "col_range": col_range,
771
- }
772
- grid_disp_range.attributes = grid_attributes.copy()
804
+ transform = Affine.from_gdal(*geotransform)
805
+ raster_profile = collections.OrderedDict(
806
+ {
807
+ "height": nb_rows,
808
+ "width": nb_cols,
809
+ "driver": "GTiff",
810
+ "dtype": "float32",
811
+ "transform": transform,
812
+ "tiled": True,
813
+ }
814
+ )
773
815
 
774
816
  # saving infos
775
817
  # disp grids
818
+
776
819
  if self.save_intermediate_data:
777
- safe_makedirs(pair_folder)
778
820
  grid_min_path = os.path.join(pair_folder, "disp_min_grid.tif")
779
- grid_orchestrator.add_to_save_lists(
780
- grid_min_path,
781
- dm_cst.DISP_MIN_GRID,
782
- grid_disp_range,
783
- dtype=np.float32,
784
- cars_ds_name="disp_min_grid",
785
- )
786
821
  grid_max_path = os.path.join(pair_folder, "disp_max_grid.tif")
787
- grid_orchestrator.add_to_save_lists(
788
- grid_max_path,
789
- dm_cst.DISP_MAX_GRID,
790
- grid_disp_range,
791
- dtype=np.float32,
792
- cars_ds_name="disp_max_grid",
793
- )
822
+ safe_makedirs(pair_folder)
823
+ else:
824
+ if pair_folder is None:
825
+ tmp_folder = os.path.join(self.orchestrator.out_dir, "tmp")
826
+ else:
827
+ tmp_folder = os.path.join(pair_folder, "tmp")
828
+ safe_makedirs(tmp_folder)
829
+ self.orchestrator.add_to_clean(tmp_folder)
830
+ grid_min_path = os.path.join(tmp_folder, "disp_min_grid.tif")
831
+ grid_max_path = os.path.join(tmp_folder, "disp_max_grid.tif")
794
832
 
795
833
  if None not in (dmin, dmax):
796
834
  # use global disparity range
@@ -800,446 +838,169 @@ class CensusMccnnSgm(
800
838
  ):
801
839
  raise RuntimeError("Mix between local and global mode")
802
840
 
803
- grid_min[:, :] = dmin
804
- grid_max[:, :] = dmax
841
+ # Only one tile
842
+ grid_disp_range.tiling_grid = np.array([[[0, nb_rows, 0, nb_cols]]])
805
843
 
806
844
  elif None not in (dem_min, dem_max, dem_median) or None not in (
807
845
  altitude_delta_min,
808
846
  altitude_delta_max,
809
847
  ):
810
- # use local disparity
811
-
812
- # Get associated alti mean / min / max values
813
- dem_median_shape = inputs.rasterio_get_size(dem_median)
814
- dem_median_width, dem_median_height = dem_median_shape
815
-
816
- min_row = 0
817
- min_col = 0
818
- max_row = dem_median_height
819
- max_col = dem_median_width
820
-
821
- # get epsg
822
- terrain_epsg = inputs.rasterio_get_epsg(dem_median)
823
-
824
- # Get epipolar position of all dem mean
825
- transform = inputs.rasterio_get_transform(dem_median)
826
-
827
- if None not in (dem_min, dem_max, dem_median):
828
- dem_min_shape = inputs.rasterio_get_size(dem_min)
829
- dem_epsg = inputs.rasterio_get_epsg(dem_min)
830
-
831
- if dem_median_shape != dem_min_shape:
832
- # dem min max are the same shape
833
- # dem median is not , hence we crop it
834
-
835
- # get terrain bounds dem min
836
- dem_min_bounds = inputs.rasterio_get_bounds(dem_min)
837
-
838
- # find roi in dem_mean
839
- roi_points = np.array(
840
- [
841
- [dem_min_bounds[0], dem_min_bounds[1]],
842
- [dem_min_bounds[0], dem_min_bounds[3]],
843
- [dem_min_bounds[2], dem_min_bounds[1]],
844
- [dem_min_bounds[2], dem_min_bounds[3]],
845
- ]
846
- )
847
-
848
- # Transform points to terrain_epsg (dem min is in 4326)
849
- roi_points_terrain = point_cloud_conversion(
850
- roi_points,
851
- dem_epsg,
852
- terrain_epsg,
853
- )
854
848
 
855
- # Get pixel roi in dem mean
856
- pixel_roi_dem_mean = inputs.rasterio_get_pixel_points(
857
- dem_median, roi_points_terrain
858
- )
859
- roi_lower_row = np.floor(np.min(pixel_roi_dem_mean[:, 0]))
860
- roi_upper_row = np.ceil(np.max(pixel_roi_dem_mean[:, 0]))
861
- roi_lower_col = np.floor(np.min(pixel_roi_dem_mean[:, 1]))
862
- roi_upper_col = np.ceil(np.max(pixel_roi_dem_mean[:, 1]))
863
-
864
- min_row = int(max(0, roi_lower_row))
865
- max_row = int(
866
- min(
867
- dem_median_height, # number of rows
868
- roi_upper_row,
869
- )
870
- )
871
- min_col = int(max(0, roi_lower_col))
872
- max_col = int(
873
- min(
874
- dem_median_width, # number of columns
875
- roi_upper_col,
876
- )
877
- )
849
+ # Generate multiple tiles
850
+ grid_tile_size = self.epi_disp_grid_tile_size
851
+ grid_disp_range.tiling_grid = tiling.generate_tiling_grid(
852
+ 0,
853
+ 0,
854
+ nb_rows,
855
+ nb_cols,
856
+ grid_tile_size,
857
+ grid_tile_size,
858
+ )
878
859
 
879
- elif (
880
- None not in (altitude_delta_min, altitude_delta_max)
881
- and geom_plugin_with_dem_and_geoid.plugin_name
882
- == "SharelocGeometry"
883
- ):
884
- roi_points_terrain = np.array(
885
- [
886
- [
887
- geom_plugin_with_dem_and_geoid.roi_shareloc[1],
888
- geom_plugin_with_dem_and_geoid.roi_shareloc[0],
889
- ],
890
- [
891
- geom_plugin_with_dem_and_geoid.roi_shareloc[1],
892
- geom_plugin_with_dem_and_geoid.roi_shareloc[2],
893
- ],
894
- [
895
- geom_plugin_with_dem_and_geoid.roi_shareloc[3],
896
- geom_plugin_with_dem_and_geoid.roi_shareloc[0],
897
- ],
898
- [
899
- geom_plugin_with_dem_and_geoid.roi_shareloc[3],
900
- geom_plugin_with_dem_and_geoid.roi_shareloc[2],
901
- ],
902
- ]
903
- )
860
+ # add tiling of global_infos_cars_ds
861
+ global_infos_cars_ds.tiling_grid = grid_disp_range.tiling_grid
862
+ self.orchestrator.add_to_replace_lists(
863
+ global_infos_cars_ds,
864
+ cars_ds_name="global infos",
865
+ )
904
866
 
905
- pixel_roi_dem_mean = inputs.rasterio_get_pixel_points(
906
- dem_median, roi_points_terrain
907
- )
867
+ self.orchestrator.add_to_save_lists(
868
+ grid_min_path,
869
+ dm_cst.DISP_MIN_GRID,
870
+ grid_disp_range,
871
+ dtype=np.float32,
872
+ nodata=0,
873
+ cars_ds_name="disp_min_grid",
874
+ )
908
875
 
909
- roi_lower_row = np.floor(np.min(pixel_roi_dem_mean[:, 0]))
910
- roi_upper_row = np.ceil(np.max(pixel_roi_dem_mean[:, 0]))
911
- roi_lower_col = np.floor(np.min(pixel_roi_dem_mean[:, 1]))
912
- roi_upper_col = np.ceil(np.max(pixel_roi_dem_mean[:, 1]))
876
+ self.orchestrator.add_to_save_lists(
877
+ grid_max_path,
878
+ dm_cst.DISP_MAX_GRID,
879
+ grid_disp_range,
880
+ dtype=np.float32,
881
+ nodata=0,
882
+ cars_ds_name="disp_max_grid",
883
+ )
884
+ [saving_info] = ( # pylint: disable=unbalanced-tuple-unpacking
885
+ self.orchestrator.get_saving_infos([grid_disp_range])
886
+ )
887
+ [saving_info_global_infos] = self.orchestrator.get_saving_infos(
888
+ [global_infos_cars_ds]
889
+ )
913
890
 
914
- min_row = int(max(0, roi_lower_row))
915
- max_row = int(min(dem_median_height, roi_upper_row))
916
- min_col = int(max(0, roi_lower_col))
917
- max_col = int(min(dem_median_width, roi_upper_col))
891
+ # Generate grids on dict format
892
+ grid_disp_range_dict = {
893
+ "grid_min_path": grid_min_path,
894
+ "grid_max_path": grid_max_path,
895
+ "global_min": None,
896
+ "global_max": None,
897
+ "step_row": step_row,
898
+ "step_col": step_col,
899
+ "row_range": row_range,
900
+ "col_range": col_range,
901
+ }
918
902
 
919
- # compute terrain positions to use (all dem min and max)
920
- row_indexes = range(min_row, max_row)
921
- col_indexes = range(min_col, max_col)
922
- transformer = rasterio.transform.AffineTransformer(transform)
903
+ if None not in (dmin, dmax):
904
+ # use global disparity range
905
+ if None not in (dem_min, dem_max) or None not in (
906
+ altitude_delta_min,
907
+ altitude_delta_max,
908
+ ):
909
+ raise RuntimeError("Mix between local and global mode")
923
910
 
924
- indexes = np.array(
925
- list(itertools.product(row_indexes, col_indexes))
911
+ saving_info_global_infos_full = ocht.update_saving_infos(
912
+ saving_info_global_infos, row=0, col=0
926
913
  )
927
-
928
- row = indexes[:, 0]
929
- col = indexes[:, 1]
930
- terrain_positions = []
931
- x_mean, y_mean = transformer.xy(row, col)
932
- terrain_positions = np.transpose(np.array([x_mean, y_mean]))
933
-
934
- # dem mean in terrain_epsg
935
- x_mean = terrain_positions[:, 0]
936
- y_mean = terrain_positions[:, 1]
937
-
938
- dem_median_list = inputs.rasterio_get_values(
939
- dem_median, x_mean, y_mean, point_cloud_conversion
914
+ saving_info_full = ocht.update_saving_infos(
915
+ saving_info, row=0, col=0
940
916
  )
941
917
 
942
- nan_mask = ~np.isnan(dem_median_list)
943
-
944
- # transform to lon lat
945
- terrain_position_lon_lat = projection.point_cloud_conversion(
946
- terrain_positions, terrain_epsg, 4326
918
+ (
919
+ grid_disp_range[0, 0],
920
+ global_infos_cars_ds[0, 0],
921
+ ) = self.orchestrator.cluster.create_task(
922
+ generate_disp_range_const_tile_wrapper, nout=2
923
+ )(
924
+ row_range,
925
+ col_range,
926
+ dmin,
927
+ dmax,
928
+ raster_profile,
929
+ saving_info_full,
930
+ saving_info_global_infos_full,
947
931
  )
948
- lon_mean = terrain_position_lon_lat[:, 0]
949
- lat_mean = terrain_position_lon_lat[:, 1]
950
-
951
- if None not in (dem_min, dem_max, dem_median):
952
- # dem min and max are in 4326
953
- dem_min_list = inputs.rasterio_get_values(
954
- dem_min, lon_mean, lat_mean, point_cloud_conversion
955
- )
956
- dem_max_list = inputs.rasterio_get_values(
957
- dem_max, lon_mean, lat_mean, point_cloud_conversion
958
- )
959
- nan_mask = (
960
- nan_mask & ~np.isnan(dem_min_list) & ~np.isnan(dem_max_list)
961
- )
962
- else:
963
- dem_min_list = dem_median_list - altitude_delta_min
964
- dem_max_list = dem_median_list + altitude_delta_max
965
-
966
- # filter nan value from input points
967
- lon_mean = lon_mean[nan_mask]
968
- lat_mean = lat_mean[nan_mask]
969
- dem_median_list = dem_median_list[nan_mask]
970
- dem_min_list = dem_min_list[nan_mask]
971
- dem_max_list = dem_max_list[nan_mask]
972
-
973
- # if shareloc is used, perform inverse locs sequentially
974
- if geom_plugin_with_dem_and_geoid.plugin_name == "SharelocGeometry":
975
-
976
- # sensors physical positions
977
- (
978
- ind_cols_sensor,
979
- ind_rows_sensor,
980
- _,
981
- ) = geom_plugin_with_dem_and_geoid.inverse_loc(
982
- sensor_image_right["image"]["main_file"],
983
- sensor_image_right["geomodel"],
984
- lat_mean,
985
- lon_mean,
986
- z_coord=dem_median_list,
987
- )
988
-
989
- # else (if libgeo is used) perform inverse locs in parallel
990
- else:
991
-
992
- num_points = len(dem_median_list)
993
-
994
- if loc_inverse_orchestrator is None:
995
- loc_inverse_orchestrator = grid_orchestrator
996
-
997
- num_workers = loc_inverse_orchestrator.get_conf().get(
998
- "nb_workers", 1
999
- )
1000
932
 
1001
- loc_inverse_dataset = cars_dataset.CarsDataset(
1002
- "points", name="loc_inverse"
1003
- )
1004
- step = int(np.ceil(num_points / num_workers))
1005
- # Create a grid with num_workers elements
1006
- loc_inverse_dataset.create_grid(1, num_workers, 1, 1, 0, 0)
933
+ elif None not in (dem_min, dem_max, dem_median) or None not in (
934
+ altitude_delta_min,
935
+ altitude_delta_max,
936
+ ):
1007
937
 
1008
- # Get saving info in order to save tiles when they are computed
1009
- [saving_info] = loc_inverse_orchestrator.get_saving_infos(
1010
- [loc_inverse_dataset]
938
+ # use filter to propagate min and max
939
+ filter_overlap = (
940
+ 2
941
+ * int(
942
+ self.disp_range_propagation_filter_size
943
+ / self.local_disp_grid_step
1011
944
  )
945
+ + 1
946
+ )
1012
947
 
1013
- for task_id in range(0, num_workers):
1014
- first_elem = task_id * step
1015
- last_elem = min((task_id + 1) * step, num_points)
948
+ for col in range(grid_disp_range.shape[1]):
949
+ for row in range(grid_disp_range.shape[0]):
950
+ # update saving infos for potential replacement
1016
951
  full_saving_info = ocht.update_saving_infos(
1017
- saving_info, row=task_id, col=0
952
+ saving_info, row=row, col=col
953
+ )
954
+ saving_info_global_infos_full = ocht.update_saving_infos(
955
+ saving_info_global_infos, row=row, col=col
1018
956
  )
1019
- loc_inverse_dataset[
1020
- task_id, 0
1021
- ] = loc_inverse_orchestrator.cluster.create_task(
1022
- loc_inverse_wrapper
957
+ array_window = grid_disp_range.get_window_as_dict(row, col)
958
+ (
959
+ grid_disp_range[row, col],
960
+ global_infos_cars_ds[row, col],
961
+ ) = self.orchestrator.cluster.create_task(
962
+ generate_disp_range_from_dem_wrapper, nout=2
1023
963
  )(
964
+ array_window,
965
+ row_range,
966
+ col_range,
967
+ sensor_image_right,
968
+ grid_right,
1024
969
  geom_plugin_with_dem_and_geoid,
1025
- sensor_image_right["image"]["main_file"],
1026
- sensor_image_right["geomodel"],
1027
- lat_mean[first_elem:last_elem],
1028
- lon_mean[first_elem:last_elem],
1029
- dem_median_list[first_elem:last_elem],
970
+ dem_median,
971
+ dem_min,
972
+ dem_max,
973
+ altitude_delta_min,
974
+ altitude_delta_max,
975
+ raster_profile,
1030
976
  full_saving_info,
977
+ saving_info_global_infos_full,
978
+ filter_overlap,
979
+ disp_to_alt_ratio,
980
+ disp_min_threshold=self.disp_min_threshold,
981
+ disp_max_threshold=self.disp_max_threshold,
1031
982
  )
1032
983
 
1033
- loc_inverse_orchestrator.add_to_replace_lists(
1034
- loc_inverse_dataset
1035
- )
1036
-
1037
- loc_inverse_orchestrator.compute_futures(
1038
- only_remaining_delayed=[
1039
- tile[0] for tile in loc_inverse_dataset.tiles
1040
- ]
1041
- )
1042
-
1043
- ind_cols_sensor = []
1044
- ind_rows_sensor = []
1045
-
1046
- for tile in loc_inverse_dataset.tiles:
1047
- ind_cols_sensor += list(tile[0]["col"])
1048
- ind_rows_sensor += list(tile[0]["row"])
1049
-
1050
- # Generate epipolar disp grids
1051
- # Get epipolar positions
1052
- (epipolar_positions_row, epipolar_positions_col) = np.meshgrid(
1053
- col_range, row_range
1054
- )
1055
- epipolar_positions = np.stack(
1056
- [epipolar_positions_row, epipolar_positions_col], axis=2
1057
- )
1058
-
1059
- # Get sensor position
1060
- sensors_positions = (
1061
- geom_plugin_with_dem_and_geoid.sensor_position_from_grid(
1062
- grid_right,
1063
- np.reshape(
1064
- epipolar_positions,
1065
- (
1066
- epipolar_positions.shape[0]
1067
- * epipolar_positions.shape[1],
1068
- 2,
1069
- ),
1070
- ),
1071
- )
1072
- )
1073
-
1074
- # compute reverse matrix
1075
- transform_sensor = rasterio.Affine(
1076
- *np.abs(
1077
- inputs.rasterio_get_transform(
1078
- sensor_image_right["image"]["main_file"]
1079
- )
1080
- )
1081
- )
1082
-
1083
- trans_inv = ~transform_sensor
1084
- # Transform to positive values
1085
- trans_inv = np.array(trans_inv)
1086
- trans_inv = np.reshape(trans_inv, (3, 3))
1087
- if trans_inv[0, 0] < 0:
1088
- trans_inv[0, :] *= -1
1089
- if trans_inv[1, 1] < 0:
1090
- trans_inv[1, :] *= -1
1091
- trans_inv = affine.Affine(*list(trans_inv.flatten()))
1092
-
1093
- # Transform physical position to index
1094
- index_positions = np.empty(sensors_positions.shape)
1095
- for row_point in range(index_positions.shape[0]):
1096
- row_geo, col_geo = sensors_positions[row_point, :]
1097
- col, row = trans_inv * (row_geo, col_geo)
1098
- index_positions[row_point, :] = (row, col)
1099
-
1100
- ind_rows_sensor_grid = index_positions[:, 0] - 0.5
1101
- ind_cols_sensor_grid = index_positions[:, 1] - 0.5
1102
-
1103
- # Interpolate disparity
1104
- disp_min_points = (
1105
- -(dem_max_list - dem_median_list) / disp_to_alt_ratio
1106
- )
1107
- disp_max_points = (
1108
- -(dem_min_list - dem_median_list) / disp_to_alt_ratio
1109
- )
1110
-
1111
- interp_min_linear = LinearInterpNearestExtrap(
1112
- list(zip(ind_rows_sensor, ind_cols_sensor)), # noqa: B905
1113
- disp_min_points,
1114
- )
1115
- interp_max_linear = LinearInterpNearestExtrap(
1116
- list(zip(ind_rows_sensor, ind_cols_sensor)), # noqa: B905
1117
- disp_max_points,
1118
- )
1119
-
1120
- grid_min = np.reshape(
1121
- interp_min_linear(ind_rows_sensor_grid, ind_cols_sensor_grid),
1122
- (
1123
- epipolar_positions.shape[0],
1124
- epipolar_positions.shape[1],
1125
- ),
1126
- )
1127
-
1128
- grid_max = np.reshape(
1129
- interp_max_linear(ind_rows_sensor_grid, ind_cols_sensor_grid),
1130
- (
1131
- epipolar_positions.shape[0],
1132
- epipolar_positions.shape[1],
1133
- ),
1134
- )
1135
-
1136
- else:
1137
- raise RuntimeError(
1138
- "Not a global or local mode for disparity range estimation"
1139
- )
1140
-
1141
- # Add margin
1142
- diff = grid_max - grid_min
1143
- logging.info("Max grid max - grid min : {} disp ".format(np.max(diff)))
1144
-
1145
- if self.disp_min_threshold is not None:
1146
- if np.any(grid_min < self.disp_min_threshold):
1147
- logging.warning(
1148
- "Override disp_min with disp_min_threshold {}".format(
1149
- self.disp_min_threshold
984
+ # Compute grid
985
+ self.orchestrator.breakpoint()
986
+
987
+ # Fill global infos
988
+ mins, maxs = [], []
989
+ for row in range(global_infos_cars_ds.shape[0]):
990
+ for col in range(global_infos_cars_ds.shape[1]):
991
+ try:
992
+ dict_data = global_infos_cars_ds[row, col].data
993
+ mins.append(dict_data["global_min"])
994
+ maxs.append(dict_data["global_max"])
995
+ except Exception:
996
+ logging.info(
997
+ "Tile {} {} not computed in epi disp range"
998
+ " generation".format(row, col)
1150
999
  )
1151
- )
1152
- grid_min[grid_min < self.disp_min_threshold] = (
1153
- self.disp_min_threshold
1154
- )
1155
- if self.disp_max_threshold is not None:
1156
- if np.any(grid_max > self.disp_max_threshold):
1157
- logging.warning(
1158
- "Override disp_max with disp_max_threshold {}".format(
1159
- self.disp_max_threshold
1160
- )
1161
- )
1162
- grid_max[grid_max > self.disp_max_threshold] = (
1163
- self.disp_max_threshold
1164
- )
1000
+ grid_disp_range_dict["global_min"] = np.floor(np.nanmin(mins))
1001
+ grid_disp_range_dict["global_max"] = np.ceil(np.nanmax(maxs))
1165
1002
 
1166
- # use filter to propagate min and max
1167
- overlap = (
1168
- 2
1169
- * int(
1170
- self.disp_range_propagation_filter_size
1171
- / self.local_disp_grid_step
1172
- )
1173
- + 1
1174
- )
1175
-
1176
- grid_min = generic_filter(
1177
- grid_min, np.min, [overlap, overlap], mode="nearest"
1178
- )
1179
- grid_max = generic_filter(
1180
- grid_max, np.max, [overlap, overlap], mode="nearest"
1181
- )
1182
-
1183
- # Generate dataset
1184
- # min and max are reversed
1185
- disp_range_tile = xr.Dataset(
1186
- data_vars={
1187
- dm_cst.DISP_MIN_GRID: (["row", "col"], grid_min),
1188
- dm_cst.DISP_MAX_GRID: (["row", "col"], grid_max),
1189
- },
1190
- coords={
1191
- "row": np.arange(0, grid_min.shape[0]),
1192
- "col": np.arange(0, grid_min.shape[1]),
1193
- },
1194
- )
1195
-
1196
- # Save
1197
- [saving_info] = ( # pylint: disable=unbalanced-tuple-unpacking
1198
- grid_orchestrator.get_saving_infos([grid_disp_range])
1199
- )
1200
- saving_info = ocht.update_saving_infos(saving_info, row=0, col=0)
1201
- # Generate profile
1202
- # Generate profile
1203
- geotransform = (
1204
- epi_size_row,
1205
- step_row,
1206
- 0.0,
1207
- epi_size_col,
1208
- 0.0,
1209
- step_col,
1210
- )
1211
-
1212
- transform = Affine.from_gdal(*geotransform)
1213
- raster_profile = collections.OrderedDict(
1214
- {
1215
- "height": nb_rows,
1216
- "width": nb_cols,
1217
- "driver": "GTiff",
1218
- "dtype": "float32",
1219
- "transform": transform,
1220
- "tiled": True,
1221
- }
1222
- )
1223
- cars_dataset.fill_dataset(
1224
- disp_range_tile,
1225
- saving_info=saving_info,
1226
- window=None,
1227
- profile=raster_profile,
1228
- attributes=None,
1229
- overlaps=None,
1230
- )
1231
- grid_disp_range[0, 0] = disp_range_tile
1232
-
1233
- if self.save_intermediate_data:
1234
- grid_orchestrator.breakpoint()
1235
-
1236
- if np.any(diff < 0):
1237
- logging.error("grid min > grid max in {}".format(pair_folder))
1238
- raise RuntimeError("grid min > grid max in {}".format(pair_folder))
1239
-
1240
- # cleanup
1241
- grid_orchestrator.cleanup()
1242
- return grid_disp_range
1003
+ return grid_disp_range_dict
1243
1004
 
1244
1005
  def run(
1245
1006
  self,
@@ -1435,16 +1196,8 @@ class CensusMccnnSgm(
1435
1196
  application_constants.APPLICATION_TAG: {
1436
1197
  dm_cst.DENSE_MATCHING_RUN_TAG: {
1437
1198
  pair_key: {
1438
- "global_disp_min": np.nanmin(
1439
- disp_range_grid[0, 0][
1440
- dm_cst.DISP_MIN_GRID
1441
- ].values
1442
- ),
1443
- "global_disp_max": np.nanmax(
1444
- disp_range_grid[0, 0][
1445
- dm_cst.DISP_MAX_GRID
1446
- ].values
1447
- ),
1199
+ "global_disp_min": disp_range_grid["global_min"],
1200
+ "global_disp_max": disp_range_grid["global_max"],
1448
1201
  },
1449
1202
  },
1450
1203
  }
@@ -1521,6 +1274,9 @@ class CensusMccnnSgm(
1521
1274
  ),
1522
1275
  texture_bands=texture_bands,
1523
1276
  conf_filtering=self.confidence_filtering,
1277
+ threshold_disp_range_to_borders=(
1278
+ self.threshold_disp_range_to_borders
1279
+ ),
1524
1280
  )
1525
1281
 
1526
1282
  else:
@@ -1545,6 +1301,7 @@ def compute_disparity_wrapper(
1545
1301
  classification_fusion_margin=-1,
1546
1302
  texture_bands=None,
1547
1303
  conf_filtering=None,
1304
+ threshold_disp_range_to_borders=False,
1548
1305
  ) -> Dict[str, Tuple[xr.Dataset, xr.Dataset]]:
1549
1306
  """
1550
1307
  Compute disparity maps from image objects.
@@ -1604,15 +1361,17 @@ def compute_disparity_wrapper(
1604
1361
  (
1605
1362
  disp_min_grid,
1606
1363
  disp_max_grid,
1607
- ) = dm_algo.compute_disparity_grid(disp_range_grid, left_image_object)
1608
-
1609
- global_disp_min = np.floor(
1610
- np.nanmin(disp_range_grid[0, 0]["disp_min_grid"].data)
1611
- )
1612
- global_disp_max = np.ceil(
1613
- np.nanmax(disp_range_grid[0, 0]["disp_max_grid"].data)
1364
+ ) = dm_algo.compute_disparity_grid(
1365
+ disp_range_grid,
1366
+ left_image_object,
1367
+ right_image_object,
1368
+ used_band,
1369
+ threshold_disp_range_to_borders,
1614
1370
  )
1615
1371
 
1372
+ global_disp_min = disp_range_grid["global_min"]
1373
+ global_disp_max = disp_range_grid["global_max"]
1374
+
1616
1375
  # add global disparity in case of ambiguity normalization
1617
1376
  left_image_object = add_global_disparity(
1618
1377
  left_image_object, global_disp_min, global_disp_max
@@ -1664,7 +1423,9 @@ def compute_disparity_wrapper(
1664
1423
 
1665
1424
  # Filtering by using the confidence
1666
1425
  requested_confidence = [
1426
+ "confidence_from_risk_min.cars_2",
1667
1427
  "confidence_from_risk_max.cars_2",
1428
+ "confidence_from_interval_bounds_inf.cars_3",
1668
1429
  "confidence_from_interval_bounds_sup.cars_3",
1669
1430
  ]
1670
1431
 
@@ -1690,47 +1451,3 @@ def compute_disparity_wrapper(
1690
1451
  )
1691
1452
 
1692
1453
  return disp_dataset
1693
-
1694
-
1695
- def loc_inverse_wrapper(
1696
- geom_plugin,
1697
- image,
1698
- geomodel,
1699
- latitudes,
1700
- longitudes,
1701
- altitudes,
1702
- saving_info=None,
1703
- ) -> pandas.DataFrame:
1704
- """
1705
- Perform inverse localizations on input coordinates
1706
- This function will be run as a delayed task.
1707
-
1708
- :param geom_plugin: geometry plugin used to perform localizations
1709
- :type geom_plugin: SharelocGeometry
1710
- :param image: input image path
1711
- :type image: str
1712
- :param geomodel: input geometric model
1713
- :type geomodel: str
1714
- :param latitudes: input latitude coordinates
1715
- :type latitudes: np.array
1716
- :param longitudes: input longitudes coordinates
1717
- :type longitudes: np.array
1718
- :param altitudes: input latitude coordinates
1719
- :type altitudes: np.array
1720
- :param saving_info: saving info for cars orchestrator
1721
- :type saving_info: dict
1722
-
1723
- """
1724
- col, row, _ = geom_plugin.inverse_loc(
1725
- image,
1726
- geomodel,
1727
- latitudes,
1728
- longitudes,
1729
- z_coord=altitudes,
1730
- )
1731
- output = pandas.DataFrame({"col": col, "row": row}, copy=False)
1732
- cars_dataset.fill_dataframe(
1733
- output, saving_info=saving_info, attributes=None
1734
- )
1735
-
1736
- return output