===========
Application
===========
.. role:: raw-html(raw)
:format: html
.. _application:
:raw-html:`
Application
`
**Overview**
An *application* is a main step of CARS 3D reconstruction framework.
It contains algorithm methods.
It takes *CarsDatasets* and configuration parameters as input and returns *CarsDatasets*.
It is composed of:
* an application factory concept that register all 3D step application
* an application template
* Some abstract applications (each one defined a main 3d step)
* Some subclass associated to each abstract application, containing specific algorithm
.. figure:: ../../images/application_concept.png
:align: center
:alt: Applications
**Example**
Let's take an example of `dense_matching` application to describe the main steps:
First, we can notice that `dense_matching` derives from `ApplicationTemplate` and is registered with the decorator:
.. sourcecode:: python
@Application.register("dense_matching")
class DenseMatching(ApplicationTemplate, metaclass=ABCMeta):
Then, algorithm is contain in a subclass register, by is `short_name`, of `dense_matching` application.
.. sourcecode:: python
class CensusMccnnSgm(
DenseMatching, short_name=["census_sgm"]
)
1. Init with parameters checking
To instantiate, need the *orchestrator* and a configuration file that contains algorirhm parameters.
.. sourcecode:: python
def __init__(self, orchestrator, conf_matching):
"""
Init function of DenseMatching
:param orchestrator: orchestrator used
:param conf_matching: configuration for matching
:return: a application_to_use object
"""
# orchestrator
self.orchestrator = orchestrator
# check conf
self.corr_config = None
if "corr_config" in conf_matching:
self.corr_config = conf_matching["corr_config"]
# For now, this is a path, transform it to dict
# later : integrated to input config
# TODO use loader to check and generate corr_config
self.corr_config = corr_conf.configure_correlator(self.corr_config)
self.correlator = None
if "correlator" in conf_matching:
self.correlator = conf_matching["correlator"]
else:
self.correlator = "pandora"
# check loader
# Saving files
if "save_intermediate_data" in conf_matching:
self.save_intermediate_data = conf_matching["save_intermediate_data"]
else:
self.save_intermediate_data = False
#
2. Run,Take *CarsDataset* as input and return new *CarsDatasets*.
.. sourcecode:: python
def run(
self,
epipolar_images_left,
epipolar_images_right,
pair_folder,
):
"""
Run Matching application.
Create left and right CarsDataset filled with xarray.Dataset ,
corresponding to epipolar disparities, on the same geometry
that epipolar_images_left and epipolar_images_right.
:param epipolar_images_left: tiled left epipolar
:type epipolar_images_left: CarsDataset
:param epipolar_images_right: tiled right epipolar
:type epipolar_images_right: CarsDataset
:param pair_folder: folder used for current pair
:type pair_folder: str
:return Disparity map
:rtype: CarsDataset
"""
2.1. Create empty *CarsDatasets*.
.. sourcecode:: python
if epipolar_images_left.dataset_type == "arrays":
# Create CarsDataset
# Epipolar_disparity
epipolar_disparity_map = cars_dataset.CarsDataset("arrays")
epipolar_disparity_map.create_empty_copy(epipolar_images_left)
# Update attributes to get epipolar info
epipolar_disparity_map.attributes.update(
epipolar_images_left.attributes
)
2.2 Declare to *Orchestrator* which products we want to save.
.. sourcecode:: python
# Save disparity maps
if self.save_intermediate_data:
self.orchestrator.add_to_save_lists(
os.path.join(pair_folder, "epi_disp.tif"),
cst_disp.MAP,
epipolar_disparity_map,
)
....
2.3 Ask to the *Orchestrator* ID for each *CarsDataset*
.. sourcecode:: python
# Get saving infos in order to save tiles when they are computed
[
saving_info
] = self.orchestrator.get_saving_infos(
[epipolar_disparity_map]
)
2.4 Tile by tile, algorithm step computation
1. Use `create_task` function of the cluster throughout the Orchestrator. Algorithm function is called. See juste above
2. `create_task` returns a `delayed` stored in previous created CarsDataset
.. sourcecode:: python
# Generate disparity maps
for col in range(epipolar_disparity_map.shape[1]):
for row in range(epipolar_disparity_map.shape[0]):
# Compute disparity
(
epipolar_disparity_map[row, col],
) = self.orchestrator.cluster.create_task(
compute_disparity
)(
epipolar_images[row, col],
self.corr_config,
saving_info=saving_info,
)
else:
logging.error(
"DenseMatching application doesn't "
"support this input data format"
)
return epipolar_disparity_map
3. For each tile, the core algorithm function is called.
1. Takes unique tile in input (not a whole *CarsDataset*) and returns a tile
2. Add the ID, given by *orchestrator*, to this tile
.. sourcecode:: python
def compute_disparity(
image_object: xr.Dataset,
corr_cfg: dict,
saving_info=None,
) -> Dict[str, xr.Dataset]:
"""
Compute disparity map from image objects.
This function will be run as a delayed task.
User must provide saving infos to save properly created datasets
:param left_image_object: tiled Left image
* dataset with :
- cst.EPI_IMAGE
- cst.EPI_MSK (if given)
- cst.EPI_TEXTURE (for left, if given)
:type left_image_object: xr.Dataset
* dataset with :
- cst.EPI_IMAGE
- cst.EPI_MSK (if given)
- cst.EPI_TEXTURE (for left, if given)
:param right_image_object: tiled Right image
:type right_image_object: xr.Dataset
:param corr_cfg: Correlator configuration
:type corr_cfg: dict
:returns: Disparity object
Returned objects are composed of :
* dataset with :
- cst_disp.MAP
- cst_disp.VALID
- cst.EPI_TEXTURE
"""
# Get disp_min and disp_max
disp_min = cars_dataset.get_attributes(left_image_object)["disp_min"]
disp_max = cars_dataset.get_attributes(left_image_object)["disp_max"]
# Compute disparity
disp = dense_matching_tools.compute_disparity(
left_image_object,
right_image_object,
corr_cfg,
disp_min,
disp_max,
mask1_ignored_by_corr=mask1_ignored_by_corr,
mask2_ignored_by_corr=mask2_ignored_by_corr,
)
# Fill with attributes
left_disp_dataset = disp[cst.STEREO_REF]
cars_dataset.fill_dataset(
left_disp_dataset,
saving_info=saving_info_left,
window=cars_dataset.get_window_dataset(left_image_object),
profile=cars_dataset.get_profile_rasterio(left_image_object),
attributes=None,
overlaps=None, # overlaps are removed
)
return disp_dataset
At the end of the application, we can obtain *CarsDatasets* filled with delayed, one per tile.