Skip to content
GitLab
Projects Groups Topics Snippets
  • /
  • Help
    • Help
    • Support
    • Community forum
    • Submit feedback
    • Contribute to GitLab
  • Sign in
  • otb otb
  • Project information
    • Project information
    • Activity
    • Labels
    • Members
  • Repository
    • Repository
    • Files
    • Commits
    • Branches
    • Tags
    • Contributor statistics
    • Graph
    • Compare revisions
  • Issues 207
    • Issues 207
    • List
    • Boards
    • Service Desk
    • Milestones
  • Merge requests 12
    • Merge requests 12
  • CI/CD
    • CI/CD
    • Pipelines
    • Jobs
    • Schedules
  • Deployments
    • Deployments
    • Environments
    • Releases
  • Packages and registries
    • Packages and registries
    • Container Registry
  • Monitor
    • Monitor
    • Incidents
  • Analytics
    • Analytics
    • Value stream
    • CI/CD
    • Repository
  • Wiki
    • Wiki
  • Snippets
    • Snippets
  • Activity
  • Graph
  • Create a new issue
  • Jobs
  • Commits
  • Issue Boards
Collapse sidebar
  • Main Repositories
  • otbotb
  • Wiki
  • Migration guide OTBv8

Migration guide OTBv8 · Changes

Page history
Create Migration guide OTBv8 authored Aug 13, 2021 by Julien Osman's avatar Julien Osman
Hide whitespace changes
Inline Side-by-side
Migration-guide-OTBv8.md 0 → 100644
View page @ 014a5ec8
This guide documents the changes required to migrate a code base which uses OTB v7 to use OTB v8.
[[_TOC_]]
## Context
OSSIM is used for geometric **sensor modelling** and **metadata parsing**. It
has been a dependency of the OTB since it's beginning. Then adapter
classes have been added, to hide OSSIM headers from OTB public
API. With the version 8 of OTB, it is time to remove this dependency, whose
development cycle is difficult to follow. Until OTB v7, only a
small portion of OSSIM was used anyway.
The two main work for this version 8 have been:
- A complete refactoring of the metadata management
- A re-implementation of the sensor model framework.
A new DEM Handler was also developed.
## The new ImageMetadata object
### Presentation
To replace OSSIM's MetadataDictionary, a new *ImageMetadata* object is
introduced. It inherits from *ImageMetadataBase*.
```mermaid
classDiagram
class ImageMetadataBase
ImageMetadataBase : - NumericKeys
ImageMetadataBase : - StringKeys
ImageMetadataBase : - LUT1DKeys
ImageMetadataBase : - LUT2DKeys
ImageMetadataBase : - TimeKeys
ImageMetadataBase : - GeometryKeys
ImageMetadataBase : - ExtraKeys
ImageMetadataBase : - [] operator(key)
ImageMetadataBase : - Add(key, value)
ImageMetadataBase : - Remove(key)
ImageMetadataBase : - Has(key)
ImageMetadataBase : - ToKeywordList(kwl)
ImageMetadataBase : - FromKeywordList(kwl)
ImageMetadataBase : - Fuse(imd)
class ImageMetadata
ImageMetadata : - Bands
ImageMetadata : - slice(start, end)
ImageMetadata : - append(imd)
ImageMetadata : - compact()
ImageMetadata : - Merge(imd)
ImageMetadata : - AppendToKeywordLists(kwlv)
ImageMetadata : - AppendToBandKeywordLists(kwlv)
ImageMetadata : - FromKeywordLists(kwlv)
ImageMetadataBase <|-- ImageMetadata
ImageMetadata "1" *-- "0..*" ImageMetadataBase
```
*ImageMetadataBase* encapsulates seven std::map to store seven
different kind of metadata:
- Numeric metadata for the metadata that can be stored as a double
- String metadata for the metadata that can be stored as a std::string
- LUT 1D metadata the metadata that can be stored as a one
dimension table
- LUT 2D metadata for the metadata that can be stored as two
dimensions table
- Time metadata for the metadata that can be stored as a *time* object
- Geometry metadata for the metadata that represent a model
- Extra metadata for non generic metadata stored as std::string
The keys of the maps are described in the file otbMetaDataKey. This
file also defines the *time* object.
The *ImageMetadataBase* class also provides 4 methods:
- the *[] operator* for a read-only access the metadata from the key
- the *Add* method to set a metadata value
- the *Remove* method to delete a metadata value
- the *Has* method to test if a key has a value
The *ImageMetadata* class is used to store the metadata. It contains a
std::vector *Bands* that contains one *ImageMetadataBase* for each
band of the product. The metadata that are common to all the bands are
stored in the *ImageMetadata* object itself. It also provides some
useful methods:
- the *slice* method to access the metadata of a range of bands
- the *append* method to concatenate two *ImageMetadata* objects
- the *compact* method to put to the top level the metadata that are
common to all the bands
- the *Merge* method to merge with another *ImageMetadata*
### Migration example
Before, OSSIM worked with KeywordLists that transit through the pipeline with ITK's MetadataDictionary object.
```cpp
ImageKeywordlist kwl = inputImg->GetImageKeywordlist();
if (kwl.GetSize() > 0)
outputImg->SetImageKeywordlist(kwl);
```
After, those objects are not used anymore for the metadata, replaced by the ImageMetadata class.
```cpp
ImageMetadata imd = inputImg->GetImageMetadata();
if (imd != nullptr)
outputImg->SetImageMetadata(imd);
```
One can simply access the data in the ImageMetadata using its public functions:
```cpp
ImageMetadata imd = inputImg->GetImageMetadata();
if (imd != nullptr && imd.Has(MDNum::NumberOfLines))
{
int nLines = imd[MDNum::NumberOfLines];
imd[MDStr::BandName] = "NIR";
}
```
## Metadata parsing
### Presentation
There is a new workflow to replace the current function
``ReadGeometryFromImage()``:
![image](https://gitlab.orfeo-toolbox.org/orfeotoolbox/otb/-/wikis/uploads/1c06ec00864af716582714cfe7563345/image.png)
- The GDAL input/output capabilities are encapsulated in the
GDALImageIO class, which derivates from ImageIO. This class is in
charge of fetching the metadata from the product (supplier
capabilities inherited from the class MetadataSupplierInterface)
and storing them in memory as a keywordlist. It is also in charge
of writing the metadata to the product (storage capabilities
inherited from the class MetadataStorageInterface).
- An ImageMetadataInterface (IMI) is then called to parse the
metadata. There is one IMI per sensor. We use a classical Factory
to find which one can parse the metadata of a product. The IMI's
*parse* method will pick the metadata from the ImageIO and fill an
*ImageMetadata* object.
- Some metadata are not read by GDAL. To parse those metadata, the
IMI can call other suppliers, depending on the file format:
* to parse XML files, XMLMetadataSupplier uses GDAL's XML parsing
mechanism ("ReadXMLToList" method from the "GDALMDReaderBase"
class) to convert the XML file into a GDAL ListString, which is a
succession of 'key=value' pairs.
* to parse text files, TextMetadataSupplier tries to parse
'key=value' pairs.
Other suppliers can be added if needed. Those classes (including
GDALImageIO) all implement the method *GetMetadataValue* which
returns the value of the metadata from a given key. The base class
also implements the methods *GetAs* and *GetAsVector* which are
used by the IMI.
- The IMI finds the relevant metadata in the different Metadata
Suppliers and use the *Add()* method of the *ImageMetadata* object
to store the metadata. If the parsing returns successfully, the
generated ImageMetadata is given to the *ImageCommon* that
propagate through the pipeline.
## DEM Handler
### Presentation
Before the refactoring, `otb::DEMHandler` class was an adapter class for
OSSIM DEMs. It provided an API to retrieve height from DEM and/or geoid using OSSIM, and was used to set up DEM and geoids to be used by OSSIM internally (which is used in OSSIM RPCs for example).
The `otb::DEMHandler` class is a singleton, a reference to the `otb::DEMHandler` object can be obtained via the `GetInstance()` method. This is a change of API, the Ossim `otb::DEMHandler` returned a ITK smart pointer to the singleton object. This was not a very good design, and a singleton object does not need to use the ITK memory management system.
The new approach is based on `RasterIO` from GDAL. A 2x2 window is extracted around the point of interest, and the height, above ellipsoid or above mean sea level. The following methods are provided:
* GetHeightAboveEllipsoid(lon, lat):
* SRTM and geoid both available: dem_value + geoid_offset
* No SRTM but geoid available: default height above ellipsoid + geoid_offset
* SRTM available, but no geoid: dem_value
* No SRTM and no geoid available: default height above ellipsoid
* GetHeightAboveMSL(lon, lat):
* SRTM and geoid both available: dem_value
* No SRTM but geoid available: 0
* SRTM available, but no geoid: dem_value
* No SRTM and no geoid available: 0
Several DEM tiles can be provided at the same time, using the `OpenDEMDirectory` method. All raster from the input directory will be opened by GDAL. A mosaic of all DEM tiles is then created as a virtual dataset (vrt).
Geoid are managed with the `GDALOpenVerticalShiftGrid` and `GDALApplyVerticalShiftGrid` function from GDAL API. The former opens a 1D raster grid as a GDAL Datasource, and the latter creates a new datasource from the raster grid (geoid) and a raster (the DEM). Vertical datums (shifts from the reference ellipsoid) are applied on the fly.
All raster that can be opened by gdal can be used as a geoid. In Ossim it was common to use the `egm96.grd` file as geoid, this file cannot be opened by GDAL. However it is still possible to use it by using the following `egm96.grd.hdr` file :
```
ENVI
samples = 1441
lines = 721
bands = 1
header offset = 24
file type = ENVI Standard
data type = 4
interleave = bsq
sensor type = Unknown
byte order = 1
wavelength units = Unknown
map info = {Geographic Lat/Lon, 1, 1,-0.125, 90.125, 0.25, 0.25,WGS-84}
coordinate system string = {GEOGCS["GCS_WGS_1984",DATUM["D_WGS_1984",SPHEROID["WGS_1984",6378137,298.257223563]],PRIMEM["Greenwich",0],UNIT["Degree",0.017453292519943295]]}
band names = {
Band 1}
```
With this file attached, GDAL will be able to read the `egm96.grd` file as a ENVI dataset
### Migration example
We introduced a slight change in the DEMHandler manipulation. Before, we would manipulate a smart pointer:
```cpp
otb::DEMHandler::Pointer demHandler = otb::DEMHandler::Instance();
```
After, we manipulate the handle to the singletron:
```cpp
otb::DEMHandler & demHandler = otb::DEMHandler::GetInstance();
OR
auto & demHandler = otb::DEMHandler::GetInstance();
```
## The new Sensor Model mechanism
### Presentation
The class ```otb::SensorTransformBase``` is the new base class for
sensor models. It inherits from ```otb::Transform```, which inherits
from ```itk::Transform```. It is templated over the data type, and
input and output dimentions. All sensor model classes should inherit
from it, and implement the methods:
- ```SetMetadataModel``` that takes a boost::any object representing
the model;
- ```IsValidSensorModel``` that returns ```true``` if the model is
correctly set;
- ```TransformPoint``` that process the transformation.
#### RPC sensor model
```mermaid
classDiagram
class Transform~TScalarType, NInputDimensions, NOutputDimensions~{
-TransformPoint(Point)* Point
}
class SensorTransformBase~TScalarType, NInputDimensions, NOutputDimensions~{
-SetMetadataModel(boost_any)* bool
-IsValidSensorModel()* bool
}
class RPCTransformBase~TScalarType, NInputDimensions, NOutputDimensions~{
-SetMetadataModel(boost_any) bool
-IsValidSensorModel() bool
#Projection_RPCParam m_RPCParam
#GDALRPCTransformer m_Transformer
}
class RPCForwardTransform~TScalarType, NInputDimensions, NOutputDimensions~{
-TransformPoint(Point) Point
}
class RPCInverseTransform~TScalarType, NInputDimensions, NOutputDimensions~{
-TransformPoint(Point) Point
}
Transform <|-- SensorTransformBase
SensorTransformBase <|-- RPCTransformBase
RPCTransformBase <|-- RPCForwardTransform
RPCTransformBase <|-- RPCInverseTransform
```
The RPC model is stored in the ```otb::ImageMetadata``` object, using the
key ```MDGeom::RPC```. The classes ```otb::RPCTransformBase```,
```otb::RPCForwardTransform``` and ```otb::RPCInverseTransform``` are used to
perform forward and inverse transformation using this model.
The abstract class ```otb::RPCTransformBase``` contains the implementation
of the ```SetMetadataModel``` method, which receives the RPC
description from the ```otb::ImageMetadata``` and instantiates an
```otb::GDALRPCTransformer```.
The classes ```otb::RPCForwardTransform``` and ```otb::RPCInverseTransform```
each implement there version of the ```TransformPoint``` method which
uses the ```otb::GDALRPCTransformer```.
#### SAR sensor model
```mermaid
classDiagram
class Transform~TScalarType, NInputDimensions, NOutputDimensions~{
-TransformPoint(Point)* Point
}
class SensorTransformBase~TScalarType, NInputDimensions, NOutputDimensions~{
-SetMetadataModel(boost_any)* bool
-IsValidSensorModel()* bool
}
class SarTransformBase~TScalarType, NInputDimensions, NOutputDimensions~{
-SetMetadataModel(boost_any) bool
-IsValidSensorModel() bool
#SARParam m_SarParam
#SarSensorModel m_Transformer
}
class SarForwardTransform~TScalarType, NInputDimensions, NOutputDimensions~{
-TransformPoint(Point) Point
}
class SarInverseTransform~TScalarType, NInputDimensions, NOutputDimensions~{
-TransformPoint(Point) Point
}
Transform <|-- SensorTransformBase
SensorTransformBase <|-- SarTransformBase
SarTransformBase <|-- SarForwardTransform
SarTransformBase <|-- SarInverseTransform
```
The SAR model works on the same pattern than the RPC model.
### Migration example
Before, the sensor model relying on OSSIM would be instantiated like this:
```cpp
typedef otb::ForwardSensorModel<double, InputSpaceDimension, InputSpaceDimension> ForwardSensorModelType;
typename ForwardSensorModelType::Pointer sensorModel = ForwardSensorModelType::New();
sensorModel->SetImageGeometry(m_InputKeywordList);
if (sensorModel->IsValidSensorModel())
[...]
```
After, the new sensor model framework uses a simple factory that needs a pointer to the ImageMetadata object, and the transform direction:
```cpp
auto sensorModel = otb::SensorTransformFactory::GetInstance().CreateTransform
<double, InputSpaceDimension, OutputSpaceDimension>(*imd, TransformDirection::FORWARD);
if (sensorModel != nullptr)
[...]
```
\ No newline at end of file
Clone repository
  • Deprecated Info
  • Help for release actions
  • How to contribute to QGIS related to OTB processing provider
  • How to deprecate
  • List of publications mentioning OTB
  • Migration guide OTBv8
  • OTB Continuous Integration platform
  • OTB Users Day 2018
  • PSC meetings
  • Remote Modules
  • Remove OSSIM
  • Home
  • uploads
    • d7dcea8f9f7385fc40a5cc8328f02520
      • filterRefactoring