diff --git a/Documentation/Cookbook/rst/AdvancedUse.rst b/Documentation/Cookbook/rst/AdvancedUse.rst index 604380e57a96fd41d45411e905f6380256f6c21c..5ce7228d2f45f4db63d51ed917a8283c3a9e4d0e 100644 --- a/Documentation/Cookbook/rst/AdvancedUse.rst +++ b/Documentation/Cookbook/rst/AdvancedUse.rst @@ -315,6 +315,36 @@ The available syntax for boolean options are: - OFF, Off, off, false, False, 0 are available for setting a ’false’ boolean value +OGR DataSource options +^^^^^^^^^^^^^^^^^^^^^^^ + +We extended this process to OGR DataSource. There are three different type of +option : open, creation and layer creation. Those options come from the GDAL +API. In order to use them one just need to specify to which of this family +the option one want to use is from. + +For open option : + +:: + + &gdal:oo:<GDALKEY>=<VALUE> + + +For creation option : + +:: + + &gdal:co:<GDALKEY>=<VALUE> + + +For layer creation option : + +:: + + &gdal:lco:<GDALKEY>=<VALUE> + + + Examples ^^^^^^^^^^^^^^ diff --git a/Documentation/Cookbook/rst/Installation.rst b/Documentation/Cookbook/rst/Installation.rst index 6058b155e95676de923b69e2649dbd86d0a0ef90..4a18859fc78af28af4d2eedf2be2fc2cc93fc54f 100644 --- a/Documentation/Cookbook/rst/Installation.rst +++ b/Documentation/Cookbook/rst/Installation.rst @@ -3,9 +3,9 @@ Installation We provide different standalone binary packages for OTB-Applications: -- for Windows platform (7 or higher) +- for Windows platform (Seven or higher) -- for 64bit Linux distribution +- for 64 bits Linux distribution - for MacOS X @@ -21,17 +21,17 @@ You can get latest binary packages from our `Download page <https://www.orfeo-to Windows ------- -.. include:: Installation_Windows.txt +.. include:: Installation_Windows.rst -Linux x86_64 +Linux ------------ -.. include:: Installation_Linux.txt +.. include:: Installation_Linux.rst MacOS X ------- -.. include:: Installation_Macx.txt +.. include:: Installation_Macos.rst Other packages -------------- diff --git a/Documentation/Cookbook/rst/Installation_Linux.txt b/Documentation/Cookbook/rst/Installation_Linux.rst similarity index 77% rename from Documentation/Cookbook/rst/Installation_Linux.txt rename to Documentation/Cookbook/rst/Installation_Linux.rst index a236e906a5417653c12333a5967c9b6026d120f0..8f90e6cd8183744e0e6f6e98b81f5041b13a3493 100644 --- a/Documentation/Cookbook/rst/Installation_Linux.txt +++ b/Documentation/Cookbook/rst/Installation_Linux.rst @@ -91,9 +91,9 @@ OTB wrappings. If no compatible Python 2.x version is found a notification is generated during the installation process. If the installation completes without issue, information relating to your Python bindings will be provided. -You must have Python numpy bindings installed in your system. They can be installed locally +You must have Python NumPy bindings installed in your system. They can be installed locally without admin rights as follows: "pip install --user numpy". This is to give users the option -to select their own existing Python installation rather than the one dibstributed by the OTB package. +to select their own existing Python installation rather than the one distributed by the OTB package. By default, bindings for Python 2.7 will be enabled with the ``otbenv`` script. If you want to use bindings for Python 3.5, you can copy this script and modify: @@ -115,38 +115,21 @@ Notes: FAQ ~~~ -Q: I am getting an error message... +Q: Unable to import otbApplication library with Python3 +++++++++++++++++++++++++++++++++++ :: - Cannot mix incompatible Qt library (version 0x40806) with this library (version 0x40807) - Aborted + ImportError: libpython3.5m.so.rh-python35-1.0: cannot open shared object file: No such file or directory -A: This is due to a conflict with system Qt4 (usually seen on KDE) and Qt4 + gtk libs in OTB package. The fix you need is to remove those libs from package. +A: You need to add a symlink to libpython3.5m.so.rh-python35-1.0 to make it works. -.. parsed-literal:: - - cd /path/to/OTB-|release|-Linux64 - rm -f lib/libQt* && rm -fr lib/gtk - -Q: Monteverdi and Mapla applications look different from my other applications. -+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ - -A: In versions 6.0, Monteverdi, Mapla and otbapplication (otbgui\_\*) -use the system gtk theme. If you can't install GTK on your system you can use the -one distributed with the OTB package. Note that using system GTK is the preferred -way with the OTB standalone package as the distributed version of GTK do not -work on recent Linux distributions. - -To use the distributed GTK libraries you need to set the OTB_USE_LOCAL_GTK: - -:: - - export OTB_USE_LOCAL_GTK=1 +Here is the solution: -And now start ``monteverdi.sh`` or ``mapla.sh`` from OTB-6.0.0-Linux64 -To get back default behaviour, unset OTB_USE_LOCAL_GTK=1 or set OTB_USE_LOCAL_GTK=0 +- find the libpython3.5XX on your system : find /usr/lib -iname *libpython3.5* +(on Ubuntu 14.04, it is /usr/lib/x86_64-linux-gnu/libpython3.5m.so) +- create a symlink : ln -s path/to/lib/python3.5XX +path/to/lib/libpython3.5m.so.rh-python35-1.0 +- Try to import otbApplication again -In version 6.2 and older, the Linux binaries are built without GTK support to cut some -dependencies. +See this discussion on `OTB issue tracker <https://gitlab.orfeo-toolbox.org/orfeotoolbox/otb/issues/1540#note_67864>`_ diff --git a/Documentation/Cookbook/rst/Installation_Linux_xdk.txt b/Documentation/Cookbook/rst/Installation_Linux_xdk.rst similarity index 100% rename from Documentation/Cookbook/rst/Installation_Linux_xdk.txt rename to Documentation/Cookbook/rst/Installation_Linux_xdk.rst diff --git a/Documentation/Cookbook/rst/Installation_Macx.txt b/Documentation/Cookbook/rst/Installation_Macos.rst similarity index 100% rename from Documentation/Cookbook/rst/Installation_Macx.txt rename to Documentation/Cookbook/rst/Installation_Macos.rst diff --git a/Documentation/Cookbook/rst/Installation_Macx_xdk.txt b/Documentation/Cookbook/rst/Installation_Macos_xsk.rst similarity index 100% rename from Documentation/Cookbook/rst/Installation_Macx_xdk.txt rename to Documentation/Cookbook/rst/Installation_Macos_xsk.rst diff --git a/Documentation/Cookbook/rst/Installation_Windows.txt b/Documentation/Cookbook/rst/Installation_Windows.rst similarity index 100% rename from Documentation/Cookbook/rst/Installation_Windows.txt rename to Documentation/Cookbook/rst/Installation_Windows.rst diff --git a/Documentation/Cookbook/rst/Installation_Windows_xdk.txt b/Documentation/Cookbook/rst/Installation_Windows_xdk.rst similarity index 100% rename from Documentation/Cookbook/rst/Installation_Windows_xdk.txt rename to Documentation/Cookbook/rst/Installation_Windows_xdk.rst diff --git a/Modules/Adapters/GdalAdapters/include/otbOGRDataSourceWrapper.h b/Modules/Adapters/GdalAdapters/include/otbOGRDataSourceWrapper.h index eb6daff5f0510f2a9908ac88f3c5566df747ebd2..b06a173fd978e0d7237157fed617e3c90d9ad6f8 100644 --- a/Modules/Adapters/GdalAdapters/include/otbOGRDataSourceWrapper.h +++ b/Modules/Adapters/GdalAdapters/include/otbOGRDataSourceWrapper.h @@ -46,6 +46,7 @@ #include "otbOGRLayerWrapper.h" #include "otbOGRVersionProxy.h" +#include "otbOGRExtendedFilenameToOptions.h" class OGRLayer; class OGRSpatialReference; @@ -88,6 +89,7 @@ public: typedef itk::SmartPointer<const Self> ConstPointer; //@} + typedef OGRExtendedFilenameToOptions FileNameHelperType; /**\name Standard macros */ //@{ /** Default builder. @@ -162,7 +164,7 @@ public: * \note No condition is assumed on the non-nullity of \c source. * \see \c DataSource(GDALDataset *) */ - static Pointer New(ogr::version_proxy::GDALDatasetType * sourcemode, Modes::type mode = Modes::Read); + static Pointer New(ogr::version_proxy::GDALDatasetType * sourcemode, Modes::type mode = Modes::Read , const std::vector< std::string > & layerOptions = std::vector< std::string >() ); //@} /**\name Projection Reference property */ @@ -368,7 +370,7 @@ public: Layer CopyLayer( Layer & srcLayer, std::string const& newName, - char ** papszOptions = ITK_NULLPTR); + std::vector<std::string> const& papszOptions = std::vector<std::string>() ); //@} /**\name Layers access @@ -497,6 +499,10 @@ public: */ ogr::version_proxy::GDALDatasetType & ogr(); + void SetLayerCreationOptions( const std::vector< std::string > & options ); + void AddLayerCreationOptions( std::vector< std::string > options ); + const std::vector< std::string > & GetLayerCreationOptions() const ; + protected: /** Default constructor. * The actual \c GDALDataset is using the <em>in-memory</em> \c @@ -511,7 +517,7 @@ protected: /** Init constructor. * \post The newly constructed object owns the \c source parameter. */ - DataSource(ogr::version_proxy::GDALDatasetType * source, Modes::type mode); + DataSource(ogr::version_proxy::GDALDatasetType * source, Modes::type mode , const std::vector< std::string > & layerOption = std::vector< std::string >() ); /** Destructor. * \post The \c GDALDataset owned is released (if not null). */ @@ -549,6 +555,7 @@ private: private: ogr::version_proxy::GDALDatasetType *m_DataSource; + std::vector< std::string > m_LayerOptions; Modes::type m_OpenMode; int m_FirstModifiableLayerID; }; // end class DataSource diff --git a/Modules/Adapters/GdalAdapters/include/otbOGRExtendedFilenameToOptions.h b/Modules/Adapters/GdalAdapters/include/otbOGRExtendedFilenameToOptions.h new file mode 100644 index 0000000000000000000000000000000000000000..6ae631531fd5d2de3f6198851deace861b11b1ec --- /dev/null +++ b/Modules/Adapters/GdalAdapters/include/otbOGRExtendedFilenameToOptions.h @@ -0,0 +1,116 @@ +/* + * Copyright (C) 2005-2017 Centre National d'Etudes Spatiales (CNES) + * + * This file is part of Orfeo Toolbox + * + * https://www.orfeo-toolbox.org/ + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef otbOGRExtendedFilenameToOptions_h +#define otbOGRExtendedFilenameToOptions_h + +#include <unordered_map> +#include "otbExtendedFilenameHelper.h" + +namespace otb +{ + +/** \class OGRExtendedFilenameToOptions + * \brief This class aim at processing GDAL option that can be pass through + * extended filename. + * \ingroup OTBExtendedFilename + * \ingroup OTBGdalAdapters + * + */ +#include "OTBGdalAdaptersExport.h" + +class OTBGdalAdapters_EXPORT OGRExtendedFilenameToOptions : public ExtendedFilenameHelper +{ +public: + /** Standard class typedefs. */ + typedef OGRExtendedFilenameToOptions Self; + typedef itk::SmartPointer<Self> Pointer; + typedef itk::SmartPointer<const Self> ConstPointer; + typedef ExtendedFilenameHelper Superclass; + + typedef Superclass::OptionMapType OptionMapType; + typedef OptionMapType::const_iterator ConstMapIteratorType; + typedef std::vector<std::string> GDALOptionType; + + itkTypeMacro(OGRExtendedFilenameToOptions, otb::ExtendedFilenameHelper); + itkNewMacro(Self); + + + /** The creation option structure. */ + struct OpenOptionType + { + GDALOptionType gdalOptions; + // std::unordered_map< std::string , bool > availableOptions; + }; + + struct CreationOptionType + { + GDALOptionType gdalOptions; + // std::unordered_map< std::string , bool > availableOptions; + }; + + struct LayerOptionType + { + std::unordered_map< std::string , std::string > gdalOptions; + }; + + /** Set extended filename */ + void SetExtendedFileName(const char * extFname) override; + + /** Get the GDAL option for type operation */ + GDALOptionType GetGDALOptions( const std::string & type ) const ; + + /** Get the deffierent GDAL options*/ + GDALOptionType GetGDALOpenOptions() const ; + GDALOptionType GetGDALCreationOptions() const ; + GDALOptionType GetGDALLayerOptions() const ; + + bool SimpleFileNameIsSet() const; + bool HasGDALLayerOption() const; + + /** Set GDAL layer option through a vector of string */ + void SetGDALLayerOptions( const GDALOptionType & options ); + + /** Add GDAL layer option to existing one */ + void AddGDALLayerOptions( const GDALOptionType & options ); + + /** Constructor that return a pointer to an OGRExtendedFilename with + * GDAL layer option as options + */ + static Pointer GetGDALLayerOptionsHelper( const GDALOptionType & options ); + +protected: + OGRExtendedFilenameToOptions(); + OGRExtendedFilenameToOptions( const GDALOptionType & options ); + ~OGRExtendedFilenameToOptions() override {}; + +private: + OGRExtendedFilenameToOptions(const Self &) = delete ; + void operator =(const Self&) = delete ; + + OpenOptionType m_OpenOptions; + CreationOptionType m_CreationOptions; + LayerOptionType m_LayerOptions; + bool m_HasFileName; +}; + +} //end namespace otb + +#endif // otbOGRExtendedFilenameToOptions_h diff --git a/Modules/Adapters/GdalAdapters/include/otbOGRVersionProxy.h b/Modules/Adapters/GdalAdapters/include/otbOGRVersionProxy.h index 7fdc308413d2a912eba471b90fe74576e1ef5310..7cae3f6dba4cf94203bfcb7e3cd52f431a5309c9 100644 --- a/Modules/Adapters/GdalAdapters/include/otbOGRVersionProxy.h +++ b/Modules/Adapters/GdalAdapters/include/otbOGRVersionProxy.h @@ -97,7 +97,7 @@ OTBGdalAdapters_EXPORT bool IsOFTInteger64(OGRFieldType type); * \return NULL if file could not be open. */ OTBGdalAdapters_EXPORT - GDALDatasetType * Open(const char * filename, bool readOnly = true); + GDALDatasetType * Open(const char * filename, bool readOnly = true , std::vector< std::string > const & options = std::vector< std::string >() ); /** * This function closes a dataset. @@ -126,7 +126,7 @@ OTBGdalAdapters_EXPORT bool IsOFTInteger64(OGRFieldType type); * \return NULL if dataset could not be created. */ OTBGdalAdapters_EXPORT - GDALDatasetType * Create(GDALDriverType * driver, const char * name); + GDALDatasetType * Create(GDALDriverType * driver, const char * name , std::vector< std::string > const & options = std::vector< std::string >() ); /** diff --git a/Modules/Adapters/GdalAdapters/otb-module.cmake b/Modules/Adapters/GdalAdapters/otb-module.cmake index 637a63ed4226e96af21f3fb7b3a1136c6db100b0..c985c5fb0e477a9f35de9a629e506d865239ee5e 100644 --- a/Modules/Adapters/GdalAdapters/otb-module.cmake +++ b/Modules/Adapters/GdalAdapters/otb-module.cmake @@ -29,6 +29,9 @@ ENABLE_SHARED OTBGDAL OTBITK + TEST_DEPENDS + OTBTestKernel + DESCRIPTION "${DOCUMENTATION}" ) diff --git a/Modules/Adapters/GdalAdapters/src/CMakeLists.txt b/Modules/Adapters/GdalAdapters/src/CMakeLists.txt index 4ba50ff1e286f05842703510931bffec1d5f0f21..0f73c07a03e044b6c89d6a19b5df6fc6b9ae4851 100644 --- a/Modules/Adapters/GdalAdapters/src/CMakeLists.txt +++ b/Modules/Adapters/GdalAdapters/src/CMakeLists.txt @@ -29,6 +29,7 @@ set(OTBGdalAdapters_SRC otbGeometriesToGeometriesFilter.cxx otbOGRDataSourceWrapper.cxx otbOGRVersionProxy.cxx + otbOGRExtendedFilenameToOptions.cxx ) add_library(OTBGdalAdapters ${OTBGdalAdapters_SRC}) diff --git a/Modules/Adapters/GdalAdapters/src/otbOGRDataSourceWrapper.cxx b/Modules/Adapters/GdalAdapters/src/otbOGRDataSourceWrapper.cxx index ac43afbb4d837ce66ed40327bb4b8d1664626cc4..b8b5d4e94096611d5c8d42378157eea8332fec3b 100644 --- a/Modules/Adapters/GdalAdapters/src/otbOGRDataSourceWrapper.cxx +++ b/Modules/Adapters/GdalAdapters/src/otbOGRDataSourceWrapper.cxx @@ -41,6 +41,7 @@ /*===========================================================================*/ /*=======================[ construction/destruction ]========================*/ /*===========================================================================*/ + bool otb::ogr::DataSource::Clear() { Reset(ITK_NULLPTR); @@ -85,6 +86,7 @@ const ExtensionDriverAssociation k_ExtensionDriverMap[] = {".GPX", "GPX"}, {".SQLITE", "SQLite"}, {".KML", "KML"}, + {".CSV", "CSV"}, }; /**\ingroup GeometryInternals * \brief Returns the OGR driver name associated to a filename. @@ -117,22 +119,29 @@ char const* DeduceDriverName(std::string filename) otb::ogr::DataSource::DataSource() : m_DataSource(ITK_NULLPTR), + m_LayerOptions() , m_OpenMode(Modes::Update_LayerUpdate), m_FirstModifiableLayerID(0) { Drivers::Init(); - ogr::version_proxy::GDALDriverType * d = ogr::version_proxy::GetDriverByName("Memory"); + ogr::version_proxy::GDALDriverType * d = + ogr::version_proxy::GetDriverByName("Memory"); assert(d && "OGR Memory driver not found"); m_DataSource = ogr::version_proxy::Create(d,"in-memory"); if (!m_DataSource) { - itkExceptionMacro(<< "Failed to create OGRMemDataSource: " << CPLGetLastErrorMsg()); + itkExceptionMacro(<< "Failed to create OGRMemDataSource: " + << CPLGetLastErrorMsg()); } } -otb::ogr::DataSource::DataSource(otb::ogr::version_proxy::GDALDatasetType * source, Modes::type mode) -: m_DataSource(source), - m_OpenMode(mode), +otb::ogr::DataSource::DataSource( + otb::ogr::version_proxy::GDALDatasetType * source , + Modes::type mode , + const std::vector< std::string > & options /*NULL*/ ) +: m_DataSource(source) , + m_LayerOptions(options) , + m_OpenMode(mode) , m_FirstModifiableLayerID(0) { m_FirstModifiableLayerID = GetLayersCount(); @@ -140,9 +149,15 @@ otb::ogr::DataSource::DataSource(otb::ogr::version_proxy::GDALDatasetType * sour otb::ogr::DataSource::Pointer otb::ogr::DataSource::OpenDataSource(std::string const& datasourceName, Modes::type mode) { - bool update = (mode != Modes::Read); + FileNameHelperType::Pointer fileNameHelper = FileNameHelperType::New(); + fileNameHelper->SetExtendedFileName( datasourceName.c_str() ); + std::string simpleFileName = fileNameHelper->GetSimpleFileName(); - ogr::version_proxy::GDALDatasetType * source = ogr::version_proxy::Open(datasourceName.c_str(),!update); + bool update = (mode != Modes::Read); + ogr::version_proxy::GDALDatasetType * source = + ogr::version_proxy::Open( simpleFileName.c_str() , + !update , + fileNameHelper->GetGDALOpenOptions() ); if (!source) { // In read mode, this is a failure @@ -150,39 +165,51 @@ otb::ogr::DataSource::Pointer otb::ogr::DataSource::OpenDataSource(std::string c if (mode == Modes::Read) { itkGenericExceptionMacro(<< "Failed to open GDALDataset file " - << datasourceName<<" : " << CPLGetLastErrorMsg()); + << simpleFileName<<" : " << CPLGetLastErrorMsg()); } // Hand made factory based on file extension. - char const* driverName = DeduceDriverName(datasourceName); + char const* driverName = DeduceDriverName(simpleFileName); if (!driverName) { - itkGenericExceptionMacro(<< "No OGR driver known to OTB to create and handle a DataSource named <" - <<datasourceName<<">."); + itkGenericExceptionMacro(<< "No OGR driver known to OTB to create and " + "handle a DataSource named <" + <<simpleFileName<<">."); } - ogr::version_proxy::GDALDriverType * d = ogr::version_proxy::GetDriverByName(driverName); + ogr::version_proxy::GDALDriverType * d = + ogr::version_proxy::GetDriverByName( driverName ); if(!d) { - itkGenericExceptionMacro(<<"Could not create OGR driver "<<driverName<<", check your OGR configuration for available drivers."); + itkGenericExceptionMacro(<< "Could not create OGR driver " << driverName + << ", check your OGR configuration for available drivers." ); } - source = ogr::version_proxy::Create(d,datasourceName.c_str()); + source = ogr::version_proxy::Create( + d , + simpleFileName.c_str() , + fileNameHelper->GetGDALCreationOptions() ); if (!source) { - itkGenericExceptionMacro(<< "Failed to create GDALDataset <"<<datasourceName - <<"> (driver name: <" << driverName<<">: " << CPLGetLastErrorMsg()); + itkGenericExceptionMacro(<< "Failed to create GDALDataset <" + << simpleFileName << "> (driver name: <" << driverName + <<">: " << CPLGetLastErrorMsg()); } } - return otb::ogr::DataSource::New(source, mode); + return otb::ogr::DataSource::New( source , mode , fileNameHelper->GetGDALLayerOptions() ); } void DeleteDataSource(std::string const& datasourceName) { - bool ret = otb::ogr::version_proxy::Delete(datasourceName.c_str()); + otb::OGRExtendedFilenameToOptions::Pointer fileNameHelper = + otb::OGRExtendedFilenameToOptions::New(); + fileNameHelper->SetExtendedFileName( datasourceName.c_str() ); + std::string simpleFileName = fileNameHelper->GetSimpleFileName(); + + bool ret = otb::ogr::version_proxy::Delete(simpleFileName.c_str()); if (!ret) { - itkGenericExceptionMacro(<< "Deletion of data source " << datasourceName + itkGenericExceptionMacro(<< "Deletion of data source " << simpleFileName << " failed: " << CPLGetLastErrorMsg()); } } @@ -190,14 +217,18 @@ void DeleteDataSource(std::string const& datasourceName) otb::ogr::DataSource::Pointer otb::ogr::DataSource::New(std::string const& datasourceName, Modes::type mode) { + FileNameHelperType::Pointer fileNameHelper = FileNameHelperType::New(); + fileNameHelper->SetExtendedFileName( datasourceName.c_str() ); + std::string simpleFileName = fileNameHelper->GetSimpleFileName(); + if (mode < Modes::Read || mode >= Modes::MAX__) { - itkGenericExceptionMacro(<< "Wrong mode when opening " << datasourceName); + itkGenericExceptionMacro(<< "Wrong mode when opening " << simpleFileName ); } Drivers::Init(); - - ogr::version_proxy::GDALDatasetType * ds = ogr::version_proxy::Open(datasourceName.c_str(),true); + ogr::version_proxy::GDALDatasetType * ds = + ogr::version_proxy::Open( simpleFileName.c_str() , true ); bool ds_exists = (ds!=ITK_NULLPTR); @@ -214,9 +245,9 @@ otb::ogr::DataSource::New(std::string const& datasourceName, Modes::type mode) /*static*/ otb::ogr::DataSource::Pointer -otb::ogr::DataSource::New(otb::ogr::version_proxy::GDALDatasetType * source, Modes::type mode) +otb::ogr::DataSource::New(otb::ogr::version_proxy::GDALDatasetType * source , Modes::type mode , const std::vector< std::string > & layerOptions ) { - Pointer res = new DataSource(source, mode); + Pointer res = new DataSource( source , mode , layerOptions ); res->UnRegister(); return res; } @@ -262,13 +293,20 @@ otb::ogr::Layer otb::ogr::DataSource::CreateLayer( if (m_OpenMode == Modes::Read) { otb::ogr::Layer l = GetLayerChecked(name); // will throw if not existing - itkGenericOutputMacro(<< "Requesting layer creation in read-only GDALDataset. Returning the existing layer"); + itkGenericOutputMacro(<< "Requesting layer creation in read-only " + "GDALDataset. Returning the existing layer"); return l; } // Other mode : Check if the layer already exists. otb::ogr::Layer layer = GetLayer(name); // won't throw on failure + FileNameHelperType::Pointer layerOptionHelper = + FileNameHelperType::GetGDALLayerOptionsHelper( m_LayerOptions ); + layerOptionHelper->AddGDALLayerOptions( papszOptions ); + std::vector<std::string> layerOptions = + layerOptionHelper->GetGDALLayerOptions(); + switch (m_OpenMode) { case Modes::Update_LayerOverwrite: @@ -281,11 +319,15 @@ otb::ogr::Layer otb::ogr::DataSource::CreateLayer( // Then create it OGRLayer * ol = m_DataSource->CreateLayer( - name.c_str(), poSpatialRef, eGType, otb::ogr::StringListConverter(papszOptions).to_ogr()); + name.c_str() , + poSpatialRef , + eGType , + otb::ogr::StringListConverter( layerOptions ).to_ogr() ); + if (!ol) { itkGenericExceptionMacro(<< "Failed to create the layer <"<<name - << "> in the GDALDataset file <" << GetDatasetDescription() + << "> in the GDALDataset file <" << GetDatasetDescription() <<">: " << CPLGetLastErrorMsg()); } @@ -304,11 +346,15 @@ otb::ogr::Layer otb::ogr::DataSource::CreateLayer( { // Then create it OGRLayer * ol = m_DataSource->CreateLayer( - name.c_str(), poSpatialRef, eGType, otb::ogr::StringListConverter(papszOptions).to_ogr()); + name.c_str() , + poSpatialRef , + eGType , + otb::ogr::StringListConverter( layerOptions ).to_ogr() ); + if (!ol) { itkGenericExceptionMacro(<< "Failed to create the layer <"<<name - << "> in the GDALDataset file <" << GetDatasetDescription() + << "> in the GDALDataset file <" << GetDatasetDescription() <<">: " << CPLGetLastErrorMsg()); } @@ -328,11 +374,15 @@ otb::ogr::Layer otb::ogr::DataSource::CreateLayer( // Case where the layer does not exists OGRLayer * ol = m_DataSource->CreateLayer( - name.c_str(), poSpatialRef, eGType, otb::ogr::StringListConverter(papszOptions).to_ogr()); + name.c_str() , + poSpatialRef , + eGType , + otb::ogr::StringListConverter( layerOptions ).to_ogr() ); + if (!ol) { itkGenericExceptionMacro(<< "Failed to create the layer <"<<name - << "> in the GDALDataset file <" << GetDatasetDescription() + << "> in the GDALDataset file <" << GetDatasetDescription() <<">: " << CPLGetLastErrorMsg()); } @@ -353,7 +403,7 @@ otb::ogr::Layer otb::ogr::DataSource::CreateLayer( otb::ogr::Layer otb::ogr::DataSource::CopyLayer( Layer & srcLayer, std::string const& newName, - char ** papszOptions/* = NULL */) + std::vector<std::string> const& papszOptions/* = NULL */) { assert(m_DataSource && "Datasource not initialized"); @@ -364,23 +414,33 @@ otb::ogr::Layer otb::ogr::DataSource::CopyLayer( itkGenericExceptionMacro(<< "Invalid GDALDataset opening mode"); break; case Modes::Read: - itkGenericExceptionMacro(<< "GDALDataset is opened in Read mode : cannot create a layer"); + itkGenericExceptionMacro(<< "GDALDataset is opened in Read mode : " + "cannot create a layer"); break; default: break; } + + FileNameHelperType::Pointer layerOptionHelper = + FileNameHelperType::GetGDALLayerOptionsHelper( m_LayerOptions ); + layerOptionHelper->AddGDALLayerOptions( papszOptions ); + std::vector<std::string> layerOptions = + layerOptionHelper->GetGDALLayerOptions(); OGRLayer * l0 = &srcLayer.ogr(); - OGRLayer * ol = m_DataSource->CopyLayer(l0, newName.c_str(), papszOptions); + OGRLayer * ol = m_DataSource->CopyLayer( + l0 , + newName.c_str() , + otb::ogr::StringListConverter( layerOptions ).to_ogr() ); if (!ol) { itkGenericExceptionMacro(<< "Failed to copy the layer <" - << srcLayer.GetName() << "> into the new layer <" <<newName - << "> in the GDALDataset file <" << GetDatasetDescription() + << srcLayer.GetName() << "> into the new layer <" << newName + << "> in the GDALDataset file <" << GetDatasetDescription() <<">: " << CPLGetLastErrorMsg()); } const bool modifiable = true; - Layer l(ol, modifiable); + Layer l( ol , modifiable ); return l; } @@ -395,10 +455,12 @@ void otb::ogr::DataSource::DeleteLayer(size_t i) itkGenericExceptionMacro(<< "Invalid GDALDataset opening mode"); break; case Modes::Read: - itkGenericExceptionMacro(<< "GDALDataset is opened in Read mode : cannot delete a layer"); + itkGenericExceptionMacro(<< "GDALDataset is opened in Read mode : " + "cannot delete a layer"); break; case Modes::Update_LayerCreateOnly: - itkGenericExceptionMacro(<< "GDALDataset is opened in Update_LayerCreateOnly mode : cannot delete a layer"); + itkGenericExceptionMacro(<< "GDALDataset is opened in " + "Update_LayerCreateOnly mode : cannot delete a layer"); break; default: break; @@ -407,14 +469,16 @@ void otb::ogr::DataSource::DeleteLayer(size_t i) const int nb_layers = GetLayersCount(); if (int(i) >= nb_layers) { - itkExceptionMacro(<< "Cannot delete " << i << "th layer in the GDALDataset <" - << GetDatasetDescription() << "> as it contains only " << nb_layers << "layers."); + itkExceptionMacro(<< "Cannot delete " << i + << "th layer in the GDALDataset <" << GetDatasetDescription() + << "> as it contains only " << nb_layers << "layers."); } const OGRErr err = m_DataSource->DeleteLayer(int(i)); if (err != OGRERR_NONE) { - itkExceptionMacro(<< "Cannot delete " << i << "th layer in the GDALDataset <" - << GetDatasetDescription() << ">: " << CPLGetLastErrorMsg()); + itkExceptionMacro(<< "Cannot delete " << i + << "th layer in the GDALDataset <" << GetDatasetDescription() + << ">: " << CPLGetLastErrorMsg()); } } @@ -471,7 +535,7 @@ size_t otb::ogr::DataSource::GetLayerID(std::string const& name) const if (id < 0) { itkExceptionMacro( << "Cannot fetch any layer named <" << name - << "> in the GDALDataset <" << GetDatasetDescription() << ">: " + << "> in the GDALDataset <" << GetDatasetDescription() << ">: " << CPLGetLastErrorMsg()); } return 0; // keep compiler happy @@ -484,13 +548,15 @@ otb::ogr::Layer otb::ogr::DataSource::GetLayerChecked(size_t i) if (int(i) >= nb_layers) { itkExceptionMacro(<< "Cannot fetch " << i << "th layer in the GDALDataset <" - << GetDatasetDescription() << "> as it contains only " << nb_layers << "layers."); + << GetDatasetDescription() << "> as it contains only " << nb_layers + << "layers."); } OGRLayer * layer_ptr = m_DataSource->GetLayer(int(i)); if (!layer_ptr) { - itkExceptionMacro( << "Unexpected error: cannot fetch " << i << "th layer in the GDALDataset <" - << GetDatasetDescription() << ">: " << CPLGetLastErrorMsg()); + itkExceptionMacro( << "Unexpected error: cannot fetch " << i + << "th layer in the GDALDataset <" << GetDatasetDescription() + << ">: " << CPLGetLastErrorMsg()); } return otb::ogr::Layer(layer_ptr, IsLayerModifiable(i)); } @@ -540,8 +606,9 @@ otb::ogr::Layer otb::ogr::DataSource::ExecuteSQL( if (!layer_ptr) { #if defined(PREFER_EXCEPTION) - itkExceptionMacro( << "Unexpected error: cannot execute the SQL request <" << statement - << "> in the GDALDataset <" << GetDatasetDescription() << ">: " << CPLGetLastErrorMsg()); + itkExceptionMacro( << "Unexpected error: cannot execute the SQL request <" + << statement << "> in the GDALDataset <" << GetDatasetDescription() + << ">: " << CPLGetLastErrorMsg()); #else // Cannot use the deleter made for result sets obtained from // GDALDataset::ExecuteSQL because it checks for non-nullity.... @@ -552,7 +619,33 @@ otb::ogr::Layer otb::ogr::DataSource::ExecuteSQL( return otb::ogr::Layer(layer_ptr, *m_DataSource, modifiable); } +void +otb::ogr::DataSource:: +SetLayerCreationOptions( const std::vector< std::string > & options ) +{ + FileNameHelperType::Pointer helper = FileNameHelperType::New(); + helper->SetGDALLayerOptions( options ); + m_LayerOptions = helper->GetGDALLayerOptions(); + // perf : do we move code from helper->SetGDALLayerOptions in here? +} +void +otb::ogr::DataSource:: +AddLayerCreationOptions( std::vector< std::string > options ) +{ + FileNameHelperType::Pointer helper = FileNameHelperType::New(); + helper->SetGDALLayerOptions( m_LayerOptions ); + helper->AddGDALLayerOptions( options ); + m_LayerOptions = helper->GetGDALLayerOptions(); + // perf : do we move code from helper->AddGDALLayerOptions in here? +} + +const std::vector< std::string > & +otb::ogr::DataSource:: +GetLayerCreationOptions() const +{ + return m_LayerOptions; +} /*===========================================================================*/ /*===============================[ features ]================================*/ /*===========================================================================*/ @@ -591,7 +684,8 @@ OGREnvelope otb::ogr::DataSource::GetGlobalExtent(bool force/* = false */, std:: if(lit==this->end()) { - itkGenericExceptionMacro(<< "Cannot compute global extent because there are no layers in the DataSource"); + itkGenericExceptionMacro(<< "Cannot compute global extent because there " + "are no layers in the DataSource"); } const OGRSpatialReference * ref_srs = lit->GetSpatialRef(); @@ -695,16 +789,18 @@ void otb::ogr::DataSource::SyncToDisk() if(!ret) { itkExceptionMacro( << "Cannot flush the pending of the OGRDataSource <" - << GetDatasetDescription() << ">: " << CPLGetLastErrorMsg()); + << GetDatasetDescription() << ">: " << CPLGetLastErrorMsg()); } } std::string otb::ogr::DataSource::GetDatasetDescription() const { - std::vector<std::string> files = otb::ogr::version_proxy::GetFileListAsStringVector(m_DataSource); + std::vector<std::string> files = + otb::ogr::version_proxy::GetFileListAsStringVector( m_DataSource ); std::string description = ""; - for(std::vector<std::string>::const_iterator it = files.begin();it!=files.end();++it) + for( std::vector<std::string>::const_iterator it = files.begin() ; + it!=files.end() ; ++it ) description+=(*it)+", "; return description; diff --git a/Modules/Adapters/GdalAdapters/src/otbOGRExtendedFilenameToOptions.cxx b/Modules/Adapters/GdalAdapters/src/otbOGRExtendedFilenameToOptions.cxx new file mode 100644 index 0000000000000000000000000000000000000000..a34e70947138645f0d660570c6abdefedb7aa141 --- /dev/null +++ b/Modules/Adapters/GdalAdapters/src/otbOGRExtendedFilenameToOptions.cxx @@ -0,0 +1,178 @@ +/* + * Copyright (C) 2005-2017 Centre National d'Etudes Spatiales (CNES) + * + * This file is part of Orfeo Toolbox + * + * https://www.orfeo-toolbox.org/ + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "otbOGRExtendedFilenameToOptions.h" +#include "otb_boost_string_header.h" +#include "otb_boost_tokenizer_header.h" + +namespace otb +{ + +OGRExtendedFilenameToOptions:: +OGRExtendedFilenameToOptions(): +m_HasFileName(false) +{ +} + +OGRExtendedFilenameToOptions:: +OGRExtendedFilenameToOptions( const GDALOptionType & options ): +m_LayerOptions(), +m_HasFileName(false) +{ + this->SetGDALLayerOptions( options ); +} + +OGRExtendedFilenameToOptions::Pointer +OGRExtendedFilenameToOptions:: +GetGDALLayerOptionsHelper( const GDALOptionType & options ) +{ + Pointer res = new OGRExtendedFilenameToOptions( options ); + res->UnRegister(); + return res; +} + + +void +OGRExtendedFilenameToOptions:: +SetExtendedFileName(const char *extFname) +{ + Superclass::SetExtendedFileName(extFname); + m_HasFileName = true; + + const OptionMapType & map = GetOptionMap(); + + ConstMapIteratorType it; + for ( it=map.begin(); it != map.end(); it++ ) + { + std::vector<std::string> tmp; + boost::split(tmp, it->first, boost::is_any_of(":"), boost::token_compress_on); + + if (tmp.size()>2 && (tmp[0]=="gdal") ) + { + if ( tmp[1]=="oo" ) + { + m_OpenOptions.gdalOptions.push_back(tmp[2] + "=" +it->second); + } + else if ( tmp[1]=="co" ) + { + m_CreationOptions.gdalOptions.push_back(tmp[2] + "=" +it->second); + } + else if ( tmp[1]=="lco" ) + { + m_LayerOptions.gdalOptions[tmp[2]] = it->second; + } + else + { + // log a warning + } + } + + } +} + +OGRExtendedFilenameToOptions:: +GDALOptionType +OGRExtendedFilenameToOptions:: +GetGDALOptions( const std::string & type ) const +{ + if ( type == "layer" ) + return GetGDALLayerOptions(); + else if ( type == "creation" ) + return m_CreationOptions.gdalOptions; + else if ( type == "open" ) + return m_OpenOptions.gdalOptions; + else + { + // warn user : wrong option + return GDALOptionType(); + } +} + +void +OGRExtendedFilenameToOptions:: +SetGDALLayerOptions( const OGRExtendedFilenameToOptions::GDALOptionType & options ) +{ + std::vector<std::string> tmp; + for ( const auto & option : options ) + { + boost::split(tmp, option , boost::is_any_of(":"), boost::token_compress_on); + if ( tmp.size()<2 ) + boost::split(tmp, option , boost::is_any_of("="), boost::token_compress_on); + m_LayerOptions.gdalOptions[ tmp[0] ] = tmp[1] ; + } +} + +void +OGRExtendedFilenameToOptions:: +AddGDALLayerOptions( const OGRExtendedFilenameToOptions::GDALOptionType & options ) +{ + for ( const auto & option : options ) + { + std::vector<std::string> tmp; + boost::split(tmp, option , boost::is_any_of(":"), boost::token_compress_on); + if ( tmp.size()<2 ) + boost::split(tmp, option , boost::is_any_of("="), boost::token_compress_on); + m_LayerOptions.gdalOptions[ tmp[0] ] = tmp[1] ; + } +} + +bool +OGRExtendedFilenameToOptions:: +SimpleFileNameIsSet() const +{ + return m_HasFileName; +} + +bool +OGRExtendedFilenameToOptions:: +HasGDALLayerOption() const +{ + return ! m_LayerOptions.gdalOptions.empty() ; +} + +OGRExtendedFilenameToOptions:: +GDALOptionType +OGRExtendedFilenameToOptions:: +GetGDALLayerOptions() const +{ + GDALOptionType options; + for (const auto & option : m_LayerOptions.gdalOptions ) + { + options.push_back( option.first + "=" + option.second ); + } + return options; +} + +#define GetGDALOptionMacro( Type ) \ +OGRExtendedFilenameToOptions:: \ +GDALOptionType \ +OGRExtendedFilenameToOptions:: \ +GetGDAL##Type##Options() const \ +{ \ + return m_##Type##Options.gdalOptions; \ +} \ + +GetGDALOptionMacro( Open ) +GetGDALOptionMacro( Creation ) +// GetGDALOptionMacro( Layer ) + + + +} //end namespace otb diff --git a/Modules/Adapters/GdalAdapters/src/otbOGRVersionProxy.cxx b/Modules/Adapters/GdalAdapters/src/otbOGRVersionProxy.cxx index 22b7a612ca3f7f557eeed7abe2da3c2e1d047fe9..b616fe752698e68c3731b5ef6bf822802468a33c 100644 --- a/Modules/Adapters/GdalAdapters/src/otbOGRVersionProxy.cxx +++ b/Modules/Adapters/GdalAdapters/src/otbOGRVersionProxy.cxx @@ -19,6 +19,7 @@ */ #include "otbOGRVersionProxy.h" +#include "otbOGRHelpers.h" #include "itkMacro.h" @@ -60,12 +61,18 @@ OTBGdalAdapters_EXPORT bool IsOFTInteger64(OGRFieldType type) } -GDALDatasetType * Open(const char * filename, bool readOnly) +GDALDatasetType * Open(const char * filename, bool readOnly , std::vector< std::string > const & options ) { #if GDAL_VERSION_NUM<2000000 + (void)options; return OGRSFDriverRegistrar::Open(filename,!readOnly); #else - return (GDALDatasetType *)GDALOpenEx(filename, (readOnly? GDAL_OF_READONLY : GDAL_OF_UPDATE) | GDAL_OF_VECTOR,NULL,NULL,NULL); + return (GDALDatasetType *)GDALOpenEx( + filename, + (readOnly? GDAL_OF_READONLY : GDAL_OF_UPDATE) | GDAL_OF_VECTOR, + NULL, + otb::ogr::StringListConverter( options ).to_ogr(), + NULL); #endif } @@ -78,9 +85,10 @@ void Close(GDALDatasetType * dataset) #endif } -GDALDatasetType * Create(GDALDriverType * driver, const char * name) +GDALDatasetType * Create(GDALDriverType * driver, const char * name , std::vector< std::string > const & options ) { #if GDAL_VERSION_NUM<2000000 + (void)options; GDALDatasetType * ds = driver->CreateDataSource(name); if(ds) @@ -88,7 +96,12 @@ GDALDatasetType * Create(GDALDriverType * driver, const char * name) return ds; #else - return driver->Create(name,0,0,0,GDT_Unknown,NULL); + return driver->Create( name , + 0 , + 0 , + 0 , + GDT_Unknown , + otb::ogr::StringListConverter( options ).to_ogr() ); #endif } diff --git a/Modules/Adapters/GdalAdapters/test/CMakeLists.txt b/Modules/Adapters/GdalAdapters/test/CMakeLists.txt index 5a0de851487aa4484815aa06e6b244c57d23f480..bc03ae797ebe581884ef8b30c42e3f317c9db315 100644 --- a/Modules/Adapters/GdalAdapters/test/CMakeLists.txt +++ b/Modules/Adapters/GdalAdapters/test/CMakeLists.txt @@ -35,5 +35,49 @@ endif() add_executable(otbOGRTestsIO otbOGRDataSourceWrapperIO.cxx) target_link_libraries(otbOGRTestsIO ${OTBGdalAdapters-Test_LIBRARIES}) -add_test(NAME coTuOGRDataSourceWrapperIO +otb_add_test(NAME coTuOGRDataSourceWrapperIO COMMAND otbOGRTestsIO ${INPUTDATA}/ToulousePoints-examples.shp ) + +set(OTBOGRTests +otbOGRTestDriver.cxx +otbOGRExtendedFilenameToOptionsTest.cxx +otbOGRExtendedFilenameToOptionsGDALTest.cxx +) + +add_executable(otbOGRTestDriver ${OTBOGRTests}) +target_link_libraries(otbOGRTestDriver ${OTBGdalAdapters-Test_LIBRARIES}) +otb_module_target_label(otbOGRTestDriver) + +otb_add_test(NAME TvOGRExtendedFilename + COMMAND otbOGRTestDriver + --compare-ascii ${NOTOL} + ${BASELINE}/TvOGRExtendedFilename.txt + ${TEMP}/TvOGRExtendedFilenameTest.txt + otbOGRExtendedFileName + test.shp?&writegeom=ON&gdal:co:QUALITY=75&gdal:co:TILED=YES&gdal:co:BLOCKYSIZE=1024&gdal:lco:layeroption=OPTION&gdal:oo:openoption=OPTION + ${TEMP}/TvOGRExtendedFilenameTest.txt ) + +#Problem with error thrown by GDAL : unable to catch it with "CPLGetLastErrorMsg" +# otb_add_test(NAME TvOGRExtendedFilenameGDALOpen +# COMMAND otbOGRTestDriver +# otbOGRExtendedFileNameGDALOpen +# ${INPUTDATA}/ToulousePoints-examples.shp?&gdal:oo:openOption=OPTION +# ) + +otb_add_test(NAME TvOGRExtendedFilenameGDALCreate + COMMAND otbOGRTestDriver + otbOGRExtendedFileNameGDALCreate + test.shp?gdal:co:creationOption=OPTION + ) + +otb_add_test(NAME TvOGRExtendedFilenameGDALLayer + COMMAND otbOGRTestDriver + otbOGRExtendedFileNameGDALLayer + test.shp?&gdal:lco:layeroption=OPTION + ) + +otb_add_test(NAME TvOGRExtendedFilenameGDALLayerOption + COMMAND otbOGRTestDriver + otbOGRExtendedFileNameGDALLayerOption + test.shp + ) \ No newline at end of file diff --git a/Modules/Adapters/GdalAdapters/test/otbOGRExtendedFilenameToOptionsGDALTest.cxx b/Modules/Adapters/GdalAdapters/test/otbOGRExtendedFilenameToOptionsGDALTest.cxx new file mode 100644 index 0000000000000000000000000000000000000000..37368ea11836c6de38f616d14f368e51a611f702 --- /dev/null +++ b/Modules/Adapters/GdalAdapters/test/otbOGRExtendedFilenameToOptionsGDALTest.cxx @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2005-2017 Centre National d'Etudes Spatiales (CNES) + * + * This file is part of Orfeo Toolbox + * + * https://www.orfeo-toolbox.org/ + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "cpl_error.h" +#include "otbOGRExtendedFilenameToOptions.h" +#include "otbOGRDataSourceWrapper.h" +#include <iostream> +#include <fstream> + + +int otbOGRExtendedFileNameGDALOpen(int , char* argv[]) +{ + auto test = otb::ogr::DataSource::New( argv[1] ); + std::string error = CPLGetLastErrorMsg(); + return 0; +} + +int otbOGRExtendedFileNameGDALCreate(int , char* argv[]) +{ + auto test = otb::ogr::DataSource::New( argv[1] , otb::ogr::DataSource::Modes::Overwrite); + std::string error = CPLGetLastErrorMsg(); + if ( error.find( "does not support creation option creationOption" ) ) + return EXIT_SUCCESS; + return EXIT_FAILURE; +} + +int otbOGRExtendedFileNameGDALLayer(int , char* argv[]) +{ + auto test = otb::ogr::DataSource::New( argv[1] , otb::ogr::DataSource::Modes::Update_LayerOverwrite); + test->CreateLayer( "2layertest" , + ITK_NULLPTR , + wkbUnknown ); + std::string error = CPLGetLastErrorMsg(); + if ( error.find( "does not support layer creation option layeroption" ) ) + return EXIT_SUCCESS; + return EXIT_FAILURE; +} + +int otbOGRExtendedFileNameGDALLayerOption(int , char* argv[]) +{ + auto test = otb::ogr::DataSource::New( argv[1] , otb::ogr::DataSource::Modes::Update_LayerOverwrite); + std::vector<std::string> option { "vectorlayeroption=OPTION" }; + test->CreateLayer( "2layertest" , + ITK_NULLPTR , + wkbUnknown , + option ); + std::string error = CPLGetLastErrorMsg(); + if ( error.find( "does not support layer creation option vectorlayeroption" ) ) + return EXIT_SUCCESS; + return EXIT_FAILURE; +} + diff --git a/Modules/Adapters/GdalAdapters/test/otbOGRExtendedFilenameToOptionsTest.cxx b/Modules/Adapters/GdalAdapters/test/otbOGRExtendedFilenameToOptionsTest.cxx new file mode 100644 index 0000000000000000000000000000000000000000..63df6eeed09ed3c5b63ff8369dd95467bdaadb3c --- /dev/null +++ b/Modules/Adapters/GdalAdapters/test/otbOGRExtendedFilenameToOptionsTest.cxx @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2005-2017 Centre National d'Etudes Spatiales (CNES) + * + * This file is part of Orfeo Toolbox + * + * https://www.orfeo-toolbox.org/ + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "otbOGRExtendedFilenameToOptions.h" +#include <algorithm> +#include <iostream> +#include <fstream> + + +using namespace otb; + + +typedef OGRExtendedFilenameToOptions FilenameHelperType; + +int otbOGRExtendedFileName(int , char* argv[]) +{ + // Verify the number of parameters in the command line + const char * inputExtendedFilename = argv[1]; + const char * outputFilename = argv[2]; + + std::cout<< argv[1] <<" "<<argv[2]<<std::endl; + FilenameHelperType::Pointer helper = FilenameHelperType::New(); + + helper->SetExtendedFileName(inputExtendedFilename); + + std::ofstream file; + file.open(outputFilename); + + file << helper->SimpleFileNameIsSet() << std::endl; + file << helper->GetSimpleFileName() << std::endl; + + file << "Open option :"<<std::endl; + FilenameHelperType::GDALOptionType open = helper->GetGDALOpenOptions(); + for ( auto option : open ) + { + file<< option << std::endl; + } + + file << "Create option :"<<std::endl; + FilenameHelperType::GDALOptionType create = helper->GetGDALOptions("creation"); + for ( auto option : create ) + { + file<< option << std::endl; + } + + file << "Layer option :"<<std::endl; + FilenameHelperType::GDALOptionType layer = helper->GetGDALOptions("layer"); + for ( auto option : layer ) + { + file<< option << std::endl; + } + + file<< "End of classic helper."<<std::endl; + + layer.push_back("TOTO=first"); + FilenameHelperType::Pointer layerHelper = + FilenameHelperType::GetGDALLayerOptionsHelper ( layer ); + std::cout<< layerHelper->GetGDALLayerOptions()[0] <<std::endl; + FilenameHelperType::GDALOptionType newOptions; + // std::vector< std::string> newOptions; + newOptions.push_back("TOTO=second"); + newOptions.push_back("TiTi=option"); + layerHelper->AddGDALLayerOptions( newOptions ); + + file << layerHelper->SimpleFileNameIsSet() << std::endl; + file << layerHelper->HasGDALLayerOption() << std::endl; + file << "Layer option from layer helper:"<<std::endl; + FilenameHelperType::GDALOptionType latestOptions = layerHelper->GetGDALOptions("layer"); + // need to sort for dummy windows + std::sort( latestOptions.begin() , latestOptions.end() ); + for ( auto option : latestOptions ) + { + file<< option << std::endl; + } + + file.close(); + return EXIT_SUCCESS; +} + diff --git a/Modules/Adapters/GdalAdapters/test/otbOGRTestDriver.cxx b/Modules/Adapters/GdalAdapters/test/otbOGRTestDriver.cxx new file mode 100644 index 0000000000000000000000000000000000000000..dba04ea4d743cc89be61e25aa01978fd87882a5b --- /dev/null +++ b/Modules/Adapters/GdalAdapters/test/otbOGRTestDriver.cxx @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2005-2017 Centre National d'Etudes Spatiales (CNES) + * + * This file is part of Orfeo Toolbox + * + * https://www.orfeo-toolbox.org/ + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "otbTestMain.h" + +void RegisterTests() +{ + REGISTER_TEST(otbOGRExtendedFileName); + REGISTER_TEST(otbOGRExtendedFileNameGDALOpen); + REGISTER_TEST(otbOGRExtendedFileNameGDALCreate); + REGISTER_TEST(otbOGRExtendedFileNameGDALLayer); + REGISTER_TEST(otbOGRExtendedFileNameGDALLayerOption); +} diff --git a/Modules/Applications/AppClassification/app/CMakeLists.txt b/Modules/Applications/AppClassification/app/CMakeLists.txt index 3e1dbd85f5cd2ca409ddb9ef51389edf5e544976..d34c3842d0a6d9e39011b98df48af97bfb2ceb87 100644 --- a/Modules/Applications/AppClassification/app/CMakeLists.txt +++ b/Modules/Applications/AppClassification/app/CMakeLists.txt @@ -125,5 +125,10 @@ otb_create_application( SOURCES otbVectorClassifier.cxx LINK_LIBRARIES ${${otb-module}_LIBRARIES}) +otb_create_application( + NAME SampleAugmentation + SOURCES otbSampleAugmentation.cxx + LINK_LIBRARIES ${${otb-module}_LIBRARIES}) + # Mantis-1427 : temporary fix add_dependencies(${otb-module}-all otbapp_ImageEnvelope) diff --git a/Modules/Applications/AppClassification/app/otbSampleAugmentation.cxx b/Modules/Applications/AppClassification/app/otbSampleAugmentation.cxx new file mode 100644 index 0000000000000000000000000000000000000000..6bb7382856e619cce13f501acea90530cfd18f37 --- /dev/null +++ b/Modules/Applications/AppClassification/app/otbSampleAugmentation.cxx @@ -0,0 +1,270 @@ +/* + * Copyright (C) 2005-2017 Centre National d'Etudes Spatiales (CNES) + * + * This file is part of Orfeo Toolbox + * + * https://www.orfeo-toolbox.org/ + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "otbWrapperApplication.h" +#include "otbWrapperApplicationFactory.h" +#include "otbOGRDataSourceWrapper.h" +#include "otbSampleAugmentationFilter.h" + +namespace otb +{ +namespace Wrapper +{ + + +class SampleAugmentation : public Application +{ +public: + /** Standard class typedefs. */ + typedef SampleAugmentation Self; + typedef Application Superclass; + typedef itk::SmartPointer<Self> Pointer; + typedef itk::SmartPointer<const Self> ConstPointer; + + /** Standard macro */ + itkNewMacro(Self); + + itkTypeMacro(SampleAugmentation, otb::Application); + + /** Filters typedef */ + using FilterType = otb::SampleAugmentationFilter; + using SampleType = FilterType::SampleType; + using SampleVectorType = FilterType::SampleVectorType; + +private: + SampleAugmentation() {} + + void DoInit() + { + SetName("SampleAugmentation"); + SetDescription("Generates synthetic samples from a sample data file."); + + // Documentation + SetDocName("Sample Augmentation"); + SetDocLongDescription("The application takes a sample data file as " + "generated by the SampleExtraction application and " + "generates synthetic samples to increase the number of " + "available samples."); + SetDocLimitations("None"); + SetDocAuthors("OTB-Team"); + SetDocSeeAlso(" "); + + AddDocTag(Tags::Learning); + + AddParameter(ParameterType_InputFilename, "in", "Input samples"); + SetParameterDescription("in","Vector data file containing samples (OGR format)"); + + AddParameter(ParameterType_OutputFilename, "out", "Output samples"); + SetParameterDescription("out","Output vector data file storing new samples" + "(OGR format)."); + + AddParameter(ParameterType_ListView, "field", "Field Name"); + SetParameterDescription("field","Name of the field carrying the class name in the input vectors."); + SetListViewSingleSelectionMode("field",true); + + AddParameter(ParameterType_Int, "layer", "Layer Index"); + SetParameterDescription("layer", "Layer index to read in the input vector file."); + MandatoryOff("layer"); + SetDefaultParameterInt("layer",0); + + AddParameter(ParameterType_Int, "label", "Label of the class to be augmented"); + SetParameterDescription("label", "Label of the class of the input file for which " + "new samples will be generated."); + SetDefaultParameterInt("label",1); + + AddParameter(ParameterType_Int, "samples", "Number of generated samples"); + SetParameterDescription("samples", "Number of synthetic samples that will " + "be generated."); + SetDefaultParameterInt("samples",100); + + AddParameter(ParameterType_ListView, "exclude", "Field names for excluded features."); + SetParameterDescription("exclude", + "List of field names in the input vector data that will not be generated in the output file."); + + AddParameter(ParameterType_Choice, "strategy", "Augmentation strategy"); + + AddChoice("strategy.replicate","Replicate input samples"); + SetParameterDescription("strategy.replicate","The new samples are generated " + "by replicating input samples which are randomly " + "selected with replacement."); + + AddChoice("strategy.jitter","Jitter input samples"); + SetParameterDescription("strategy.jitter","The new samples are generated " + "by adding gaussian noise to input samples which are " + "randomly selected with replacement."); + AddParameter(ParameterType_Float, "strategy.jitter.stdfactor", + "Factor for dividing the standard deviation of each feature"); + SetParameterDescription("strategy.jitter.stdfactor", + "The noise added to the input samples will have the " + "standard deviation of the input features divided " + "by the value of this parameter. "); + SetDefaultParameterFloat("strategy.jitter.stdfactor",10); + + AddChoice("strategy.smote","Smote input samples"); + SetParameterDescription("strategy.smote","The new samples are generated " + "by using the SMOTE algorithm (http://dx.doi.org/10.1613/jair.953) " + "on input samples which are " + "randomly selected with replacement."); + AddParameter(ParameterType_Int, "strategy.smote.neighbors", + "Number of nearest neighbors."); + SetParameterDescription("strategy.smote.neighbors", + "Number of nearest neighbors to be used in the " + "SMOTE algorithm"); + SetDefaultParameterFloat("strategy.smote.neighbors", 5); + + AddRANDParameter("seed"); + MandatoryOff("seed"); + + // Doc example parameter settings + SetDocExampleParameterValue("in", "samples.sqlite"); + SetDocExampleParameterValue("field", "class"); + SetDocExampleParameterValue("label", "3"); + SetDocExampleParameterValue("samples", "100"); + SetDocExampleParameterValue("out","augmented_samples.sqlite"); + SetDocExampleParameterValue( "exclude", "OGC_FID name class originfid" ); + SetDocExampleParameterValue("strategy", "smote"); + SetDocExampleParameterValue("strategy.smote.neighbors", "5"); + + SetOfficialDocLink(); + } + + void DoUpdateParameters() + { + if ( HasValue("in") ) + { + std::string vectorFile = GetParameterString("in"); + ogr::DataSource::Pointer ogrDS = + ogr::DataSource::New(vectorFile, ogr::DataSource::Modes::Read); + ogr::Layer layer = ogrDS->GetLayer(this->GetParameterInt("layer")); + ogr::Feature feature = layer.ogr().GetNextFeature(); + + ClearChoices("exclude"); + ClearChoices("field"); + + for(int iField=0; iField<feature.ogr().GetFieldCount(); iField++) + { + std::string key, item = feature.ogr().GetFieldDefnRef(iField)->GetNameRef(); + key = item; + std::string::iterator end = std::remove_if(key.begin(),key.end(), + [](auto c){return !std::isalnum(c);}); + std::transform(key.begin(), end, key.begin(), tolower); + + OGRFieldType fieldType = feature.ogr().GetFieldDefnRef(iField)->GetType(); + + if(fieldType == OFTString || fieldType == OFTInteger || ogr::version_proxy::IsOFTInteger64(fieldType)) + { + std::string tmpKey="field."+key.substr(0, end - key.begin()); + AddChoice(tmpKey,item); + } + if( fieldType == OFTInteger || ogr::version_proxy::IsOFTInteger64( fieldType ) || fieldType == OFTReal ) + { + std::string tmpKey = "exclude." + key.substr( 0, static_cast<unsigned long>( end - key.begin() ) ); + AddChoice( tmpKey, item ); + } + } + } + } + + void DoExecute() + { + ogr::DataSource::Pointer vectors; + ogr::DataSource::Pointer output; + vectors = ogr::DataSource::New(this->GetParameterString("in")); + output = ogr::DataSource::New(this->GetParameterString("out"), + ogr::DataSource::Modes::Overwrite); + + // Retrieve the field name + std::vector<int> selectedCFieldIdx = GetSelectedItems("field"); + + if(selectedCFieldIdx.empty()) + { + otbAppLogFATAL(<<"No field has been selected for data labelling!"); + } + + std::vector<std::string> cFieldNames = GetChoiceNames("field"); + std::string fieldName = cFieldNames[selectedCFieldIdx.front()]; + + std::vector<std::string> excludedFields = + GetExcludedFields( GetChoiceNames( "exclude" ), + GetSelectedItems( "exclude" )); + for(const auto& ef : excludedFields) + otbAppLogINFO("Excluding feature " << ef << '\n'); + + int seed = std::time(nullptr); + if(IsParameterEnabled("seed")) seed = this->GetParameterInt("seed"); + + + FilterType::Pointer filter = FilterType::New(); + filter->SetInput(vectors); + filter->SetLayer(this->GetParameterInt("layer")); + filter->SetNumberOfSamples(this->GetParameterInt("samples")); + filter->SetOutputSamples(output); + filter->SetClassFieldName(fieldName); + filter->SetLabel(this->GetParameterInt("label")); + filter->SetExcludedFields(excludedFields); + filter->SetSeed(seed); + switch (this->GetParameterInt("strategy")) + { + // replicate + case 0: + { + otbAppLogINFO("Augmentation strategy : replicate"); + filter->SetStrategy(FilterType::Strategy::Replicate); + } + break; + // jitter + case 1: + { + otbAppLogINFO("Augmentation strategy : jitter"); + filter->SetStrategy(FilterType::Strategy::Jitter); + filter->SetStdFactor(this->GetParameterFloat("strategy.jitter.stdfactor")); + } + break; + case 2: + { + otbAppLogINFO("Augmentation strategy : smote"); + filter->SetStrategy(FilterType::Strategy::Smote); + filter->SetSmoteNeighbors(this->GetParameterInt("strategy.smote.neighbors")); + } + break; + } + filter->Update(); + output->SyncToDisk(); + } + + + std::vector<std::string> GetExcludedFields(const std::vector<std::string>& fieldNames, + const std::vector<int>& selectedIdx) + { + auto nbFeatures = static_cast<unsigned int>(selectedIdx.size()); + std::vector<std::string> result( nbFeatures ); + for( unsigned int i = 0; i < nbFeatures; ++i ) + { + result[i] = fieldNames[selectedIdx[i]]; + } + return result; + } + +}; + +} // end of namespace Wrapper +} // end of namespace otb + +OTB_APPLICATION_EXPORT(otb::Wrapper::SampleAugmentation) diff --git a/Modules/Applications/AppClassification/test/CMakeLists.txt b/Modules/Applications/AppClassification/test/CMakeLists.txt index fae1474266db959a03a6accd01b14b43f00f4ec0..e848e08ed8fca6c2f1bd840c6adc16816b716bc6 100644 --- a/Modules/Applications/AppClassification/test/CMakeLists.txt +++ b/Modules/Applications/AppClassification/test/CMakeLists.txt @@ -972,3 +972,39 @@ otb_test_application( ${OTBAPP_BASELINE_FILES}/apTvClMultiImageSamplingRate_out_3.csv ${TEMP}/apTvClMultiImageSamplingRate_out_3.csv ) + +#------------ SampleAgmentation TESTS ---------------- +otb_test_application(NAME apTvClSampleAugmentationReplicate + APP SampleAugmentation + OPTIONS -in ${INPUTDATA}/Classification/apTvClSampleExtractionOut.sqlite + -field class + -label 3 + -samples 100 + -out ${TEMP}/apTvClSampleAugmentationReplicate.sqlite + -exclude originfid + -strategy replicate + ) + +otb_test_application(NAME apTvClSampleAugmentationJitter + APP SampleAugmentation + OPTIONS -in ${INPUTDATA}/Classification/apTvClSampleExtractionOut.sqlite + -field class + -label 3 + -samples 100 + -out ${TEMP}/apTvClSampleAugmentationJitter.sqlite + -exclude originfid + -strategy jitter + -strategy.jitter.stdfactor 10 + ) + +otb_test_application(NAME apTvClSampleAugmentationSmote + APP SampleAugmentation + OPTIONS -in ${INPUTDATA}/Classification/apTvClSampleExtractionOut.sqlite + -field class + -label 3 + -samples 100 + -out ${TEMP}/apTvClSampleAugmentationSmote.sqlite + -exclude originfid + -strategy smote + -strategy.smote.neighbors 5 + ) diff --git a/Modules/Applications/AppImageUtils/app/otbCompareImages.cxx b/Modules/Applications/AppImageUtils/app/otbCompareImages.cxx index fab3a3d9096e3d71ad39c26c182dfe8e41424271..cbdcb90795e11c3400a1bbeb9ab02c4b612f98db 100644 --- a/Modules/Applications/AppImageUtils/app/otbCompareImages.cxx +++ b/Modules/Applications/AppImageUtils/app/otbCompareImages.cxx @@ -172,9 +172,12 @@ private: void DoExecute() override { // Init filters - m_ExtractRefFilter = ExtractROIMonoFilterType::New(); - m_ExtractMeasFilter = ExtractROIMonoFilterType::New(); - m_CompareFilter = StreamingCompareImageFilterType::New(); + ExtractROIMonoFilterType::Pointer extractRefFilter = + ExtractROIMonoFilterType::New(); + ExtractROIMonoFilterType::Pointer extractMeasFilter = + ExtractROIMonoFilterType::New(); + StreamingCompareImageFilterType::Pointer compareFilter = + StreamingCompareImageFilterType::New(); // Get input image pointers FloatVectorImageType::Pointer refIm = this->GetParameterImage("ref.in"); @@ -200,41 +203,37 @@ private: otbAppLogFATAL( << "ROI is not contained in the images regions"); } - m_ExtractRefFilter->SetInput( refIm ); - m_ExtractMeasFilter->SetInput( measIm ); + extractRefFilter->SetInput( refIm ); + extractMeasFilter->SetInput( measIm ); - m_ExtractRefFilter->SetExtractionRegion(region); - m_ExtractMeasFilter->SetExtractionRegion(region); + extractRefFilter->SetExtractionRegion(region); + extractMeasFilter->SetExtractionRegion(region); // Set channels to extract otbAppLogINFO( << "reference image channel "<<this->GetParameterInt("ref.channel")<<" is compared with measured image channel "<<this->GetParameterInt("meas.channel")); - m_ExtractRefFilter->SetChannel( this->GetParameterInt("ref.channel") ); - m_ExtractMeasFilter->SetChannel( this->GetParameterInt("meas.channel") ); + extractRefFilter->SetChannel( this->GetParameterInt("ref.channel") ); + extractMeasFilter->SetChannel( this->GetParameterInt("meas.channel") ); // Compute comparison - m_CompareFilter->SetInput1(m_ExtractRefFilter->GetOutput()); - m_CompareFilter->SetInput2(m_ExtractMeasFilter->GetOutput()); - m_CompareFilter->SetPhysicalSpaceCheck(false); - m_CompareFilter->GetStreamer()->SetAutomaticAdaptativeStreaming(GetParameterInt("ram")); - AddProcess(m_CompareFilter->GetStreamer(), "Comparing..."); - m_CompareFilter->Update(); + compareFilter->SetInput1(extractRefFilter->GetOutput()); + compareFilter->SetInput2(extractMeasFilter->GetOutput()); + compareFilter->SetPhysicalSpaceCheck(false); + compareFilter->GetStreamer()->SetAutomaticAdaptativeStreaming(GetParameterInt("ram")); + AddProcess(compareFilter->GetStreamer(), "Comparing..."); + compareFilter->Update(); // Show result - otbAppLogINFO( << "MSE: " << m_CompareFilter->GetMSE() ); - otbAppLogINFO( << "MAE: " << m_CompareFilter->GetMAE() ); - otbAppLogINFO( << "PSNR: " << m_CompareFilter->GetPSNR() ); - otbAppLogINFO( << "Number of Pixel different: " << m_CompareFilter->GetDiffCount() ); - - SetParameterFloat( "mse",m_CompareFilter->GetMSE()); - SetParameterFloat( "mae",m_CompareFilter->GetMAE()); - SetParameterFloat( "psnr",m_CompareFilter->GetPSNR()); - SetParameterFloat( "count",m_CompareFilter->GetDiffCount()); + otbAppLogINFO( << "MSE: " << compareFilter->GetMSE() ); + otbAppLogINFO( << "MAE: " << compareFilter->GetMAE() ); + otbAppLogINFO( << "PSNR: " << compareFilter->GetPSNR() ); + otbAppLogINFO( << "Number of Pixel different: " << compareFilter->GetDiffCount() ); + + SetParameterFloat( "mse",compareFilter->GetMSE()); + SetParameterFloat( "mae",compareFilter->GetMAE()); + SetParameterFloat( "psnr",compareFilter->GetPSNR()); + SetParameterFloat( "count",compareFilter->GetDiffCount()); + RegisterPipeline(); } - - - ExtractROIMonoFilterType::Pointer m_ExtractRefFilter; - ExtractROIMonoFilterType::Pointer m_ExtractMeasFilter; - StreamingCompareImageFilterType::Pointer m_CompareFilter; }; } diff --git a/Modules/Applications/AppImageUtils/app/otbConcatenateImages.cxx b/Modules/Applications/AppImageUtils/app/otbConcatenateImages.cxx index 7e3459d154b7e919115d129c5084259b51792130..0c9d52659cf8864c387b70388470f51b9114db59 100644 --- a/Modules/Applications/AppImageUtils/app/otbConcatenateImages.cxx +++ b/Modules/Applications/AppImageUtils/app/otbConcatenateImages.cxx @@ -72,10 +72,6 @@ private: AddDocTag("Concatenation"); AddDocTag("Multi-channel"); - m_Concatener = ListConcatenerFilterType::New(); - m_ExtractorList = ExtractROIFilterListType::New(); - m_ImageList = ImageListType::New(); - AddParameter(ParameterType_InputImageList, "il", "Input images list"); SetParameterDescription("il", "The list of images to concatenate, must have the same size."); @@ -94,15 +90,16 @@ private: void DoUpdateParameters() override { // Nothing to do here for the parameters : all are independent - - // Reinitialize the object - m_Concatener = ListConcatenerFilterType::New(); - m_ImageList = ImageListType::New(); - m_ExtractorList = ExtractROIFilterListType::New(); } void DoExecute() override { + ListConcatenerFilterType::Pointer m_Concatener = + ListConcatenerFilterType::New(); + ExtractROIFilterListType::Pointer m_ExtractorList = + ExtractROIFilterListType::New(); + ImageListType::Pointer m_ImageList = + ImageListType::New(); // Get the input image list FloatVectorImageListType::Pointer inList = this->GetParameterImageList("il"); @@ -140,12 +137,8 @@ private: m_Concatener->SetInput( m_ImageList ); SetParameterOutputImage("out", m_Concatener->GetOutput()); + RegisterPipeline(); } - - - ListConcatenerFilterType::Pointer m_Concatener; - ExtractROIFilterListType::Pointer m_ExtractorList; - ImageListType::Pointer m_ImageList; }; } diff --git a/Modules/Applications/AppImageUtils/app/otbExtractROI.cxx b/Modules/Applications/AppImageUtils/app/otbExtractROI.cxx index fd773fe9cad85082d67e2ccd85480823e85c7e44..66f2886edc0c2b5d47acca8bef211319d023726e 100644 --- a/Modules/Applications/AppImageUtils/app/otbExtractROI.cxx +++ b/Modules/Applications/AppImageUtils/app/otbExtractROI.cxx @@ -828,23 +828,21 @@ private: this->CropRegionOfInterest(); - m_ExtractROIFilter = ExtractROIFilterType::New(); - m_ExtractROIFilter->SetInput(inImage); - m_ExtractROIFilter->SetStartX(GetParameterInt("startx")); - m_ExtractROIFilter->SetStartY(GetParameterInt("starty")); - m_ExtractROIFilter->SetSizeX(GetParameterInt("sizex")); - m_ExtractROIFilter->SetSizeY(GetParameterInt("sizey")); + ExtractROIFilterType::Pointer extractROIFilter = ExtractROIFilterType::New(); + extractROIFilter->SetInput(inImage); + extractROIFilter->SetStartX(GetParameterInt("startx")); + extractROIFilter->SetStartY(GetParameterInt("starty")); + extractROIFilter->SetSizeX(GetParameterInt("sizex")); + extractROIFilter->SetSizeY(GetParameterInt("sizey")); for (unsigned int idx = 0; idx < GetSelectedItems("cl").size(); ++idx) { - m_ExtractROIFilter->SetChannel(GetSelectedItems("cl")[idx] + 1 ); + extractROIFilter->SetChannel(GetSelectedItems("cl")[idx] + 1 ); } - SetParameterOutputImage("out", m_ExtractROIFilter->GetOutput()); + SetParameterOutputImage("out", extractROIFilter->GetOutput()); + RegisterPipeline(); } - - ExtractROIFilterType::Pointer m_ExtractROIFilter; - }; } diff --git a/Modules/Applications/AppImageUtils/app/otbQuicklook.cxx b/Modules/Applications/AppImageUtils/app/otbQuicklook.cxx index 978740d95ecd1eba022252b3be4bb6818359d026..f31b64075bec5a3d90dcb6e8c04d2685a555ecde 100644 --- a/Modules/Applications/AppImageUtils/app/otbQuicklook.cxx +++ b/Modules/Applications/AppImageUtils/app/otbQuicklook.cxx @@ -207,27 +207,29 @@ bool CropRegionOfInterest() { InputImageType::Pointer inImage = GetParameterImage("in"); - m_ExtractROIFilter = ExtractROIFilterType::New(); - m_ResamplingFilter = ShrinkImageFilterType::New(); + ExtractROIFilterType::Pointer extractROIFilter = + ExtractROIFilterType::New(); + ShrinkImageFilterType::Pointer resamplingFilter = + ShrinkImageFilterType::New(); // The image on which the quicklook will be generated - // Will eventually be the m_ExtractROIFilter output + // Will eventually be the extractROIFilter output if (HasUserValue("rox") || HasUserValue("roy") || HasUserValue("rsx") || HasUserValue("rsy") || (GetSelectedItems("cl").size() > 0)) { - m_ExtractROIFilter->SetInput(inImage); - m_ExtractROIFilter->SetStartX(GetParameterInt("rox")); - m_ExtractROIFilter->SetStartY(GetParameterInt("roy")); - m_ExtractROIFilter->SetSizeX(GetParameterInt("rsx")); - m_ExtractROIFilter->SetSizeY(GetParameterInt("rsy")); + extractROIFilter->SetInput(inImage); + extractROIFilter->SetStartX(GetParameterInt("rox")); + extractROIFilter->SetStartY(GetParameterInt("roy")); + extractROIFilter->SetSizeX(GetParameterInt("rsx")); + extractROIFilter->SetSizeY(GetParameterInt("rsy")); if ((GetSelectedItems("cl").size() > 0)) { for (unsigned int idx = 0; idx < GetSelectedItems("cl").size(); ++idx) { - m_ExtractROIFilter->SetChannel(GetSelectedItems("cl")[idx] + 1 ); + extractROIFilter->SetChannel(GetSelectedItems("cl")[idx] + 1 ); } } else @@ -235,14 +237,14 @@ bool CropRegionOfInterest() unsigned int nbComponents = inImage->GetNumberOfComponentsPerPixel(); for (unsigned int idx = 0; idx < nbComponents; ++idx) { - m_ExtractROIFilter->SetChannel(idx + 1); + extractROIFilter->SetChannel(idx + 1); } } - m_ResamplingFilter->SetInput( m_ExtractROIFilter->GetOutput() ); + resamplingFilter->SetInput( extractROIFilter->GetOutput() ); } else { - m_ResamplingFilter->SetInput(inImage); + resamplingFilter->SetInput(inImage); } unsigned int Ratio = static_cast<unsigned int>(GetParameterInt("sr")); @@ -278,15 +280,13 @@ bool CropRegionOfInterest() } otbAppLogINFO( << "Ratio used: "<<Ratio << "."); - m_ResamplingFilter->SetShrinkFactor( Ratio ); - m_ResamplingFilter->Update(); + resamplingFilter->SetShrinkFactor( Ratio ); + resamplingFilter->Update(); - SetParameterOutputImage("out", m_ResamplingFilter->GetOutput()); + SetParameterOutputImage("out", resamplingFilter->GetOutput()); + RegisterPipeline(); } - ExtractROIFilterType::Pointer m_ExtractROIFilter; - ShrinkImageFilterType::Pointer m_ResamplingFilter; - }; } diff --git a/Modules/Applications/AppImageUtils/app/otbRescale.cxx b/Modules/Applications/AppImageUtils/app/otbRescale.cxx index a94e8440ac341c8a4964734145096686c3b89dcd..ad0992f5048a89db4af581e732dc1e44859e806c 100644 --- a/Modules/Applications/AppImageUtils/app/otbRescale.cxx +++ b/Modules/Applications/AppImageUtils/app/otbRescale.cxx @@ -100,24 +100,25 @@ private: FloatVectorImageType::Pointer inImage = GetParameterImage("in"); otbAppLogDEBUG( << "Starting Min/Max computation" ) + + MinMaxFilterType::Pointer minMaxFilter = MinMaxFilterType::New(); + minMaxFilter->SetInput( inImage ); + minMaxFilter->GetStreamer()->SetAutomaticAdaptativeStreaming(GetParameterInt("ram")); - m_MinMaxFilter = MinMaxFilterType::New(); - m_MinMaxFilter->SetInput( inImage ); - m_MinMaxFilter->GetStreamer()->SetAutomaticAdaptativeStreaming(GetParameterInt("ram")); + AddProcess(minMaxFilter->GetStreamer(), "Min/Max computing"); + minMaxFilter->Update(); - AddProcess(m_MinMaxFilter->GetStreamer(), "Min/Max computing"); - m_MinMaxFilter->Update(); - - otbAppLogDEBUG( << "Min/Max computation done : min=" << m_MinMaxFilter->GetMinimum() - << " max=" << m_MinMaxFilter->GetMaximum() ) + otbAppLogDEBUG( << "Min/Max computation done : min=" << minMaxFilter->GetMinimum() + << " max=" << minMaxFilter->GetMaximum() ) FloatVectorImageType::PixelType inMin, inMax; - m_RescaleFilter = RescaleImageFilterType::New(); - m_RescaleFilter->SetInput( inImage ); - m_RescaleFilter->SetAutomaticInputMinMaxComputation(false); - m_RescaleFilter->SetInputMinimum( m_MinMaxFilter->GetMinimum() ); - m_RescaleFilter->SetInputMaximum( m_MinMaxFilter->GetMaximum() ); + RescaleImageFilterType::Pointer rescaleFilter = + RescaleImageFilterType::New(); + rescaleFilter->SetInput( inImage ); + rescaleFilter->SetAutomaticInputMinMaxComputation(false); + rescaleFilter->SetInputMinimum( minMaxFilter->GetMinimum() ); + rescaleFilter->SetInputMaximum( minMaxFilter->GetMaximum() ); FloatVectorImageType::PixelType outMin, outMax; outMin.SetSize( inImage->GetNumberOfComponentsPerPixel() ); @@ -125,15 +126,13 @@ private: outMin.Fill( GetParameterFloat("outmin") ); outMax.Fill( GetParameterFloat("outmax") ); - m_RescaleFilter->SetOutputMinimum( outMin ); - m_RescaleFilter->SetOutputMaximum( outMax ); - m_RescaleFilter->UpdateOutputInformation(); + rescaleFilter->SetOutputMinimum( outMin ); + rescaleFilter->SetOutputMaximum( outMax ); + rescaleFilter->UpdateOutputInformation(); - SetParameterOutputImage("out", m_RescaleFilter->GetOutput()); + SetParameterOutputImage("out", rescaleFilter->GetOutput()); + RegisterPipeline(); } - - RescaleImageFilterType::Pointer m_RescaleFilter; - MinMaxFilterType::Pointer m_MinMaxFilter; }; } diff --git a/Modules/Applications/AppImageUtils/app/otbSplitImage.cxx b/Modules/Applications/AppImageUtils/app/otbSplitImage.cxx index fba6fb7580027a544875ca533104d631593e0a05..3f73fba77a07e69fd3bd4a58f502dc4c285d90ea 100644 --- a/Modules/Applications/AppImageUtils/app/otbSplitImage.cxx +++ b/Modules/Applications/AppImageUtils/app/otbSplitImage.cxx @@ -103,13 +103,13 @@ private: ext = itksys::SystemTools::GetFilenameExtension(ofname); // Set the extract filter input image - m_Filter = FilterType::New(); - m_Filter->SetInput(inImage); + FilterType::Pointer filter = FilterType::New(); + filter->SetInput(inImage); for (unsigned int i = 0; i < inImage->GetNumberOfComponentsPerPixel(); ++i) { // Set the channel to extract - m_Filter->SetChannel(i+1); + filter->SetChannel(i+1); // build the current output filename std::ostringstream oss; @@ -129,7 +129,7 @@ private: // Set the filename of the current output image paramOut->SetFileName(oss.str()); otbAppLogINFO(<< "File: "<<paramOut->GetFileName() << " will be written."); - paramOut->SetValue(m_Filter->GetOutput()); + paramOut->SetValue(filter->GetOutput()); paramOut->SetPixelType(this->GetParameterOutputImagePixelType("out")); // Add the current level to be written paramOut->InitializeWriters(); @@ -140,9 +140,8 @@ private: // Disable the output Image parameter to avoid writing // the last image (Application::ExecuteAndWriteOutput method) DisableParameter("out"); + RegisterPipeline(); } - - FilterType::Pointer m_Filter; }; } } diff --git a/Modules/Applications/AppImageUtils/app/otbTileFusion.cxx b/Modules/Applications/AppImageUtils/app/otbTileFusion.cxx index fd53f599b9ab23dd9f12548e539c84e14cc9cdf8..7ba75ba1a3007513cc3d864b0287a2aa340ea451 100644 --- a/Modules/Applications/AppImageUtils/app/otbTileFusion.cxx +++ b/Modules/Applications/AppImageUtils/app/otbTileFusion.cxx @@ -95,23 +95,22 @@ private: itkExceptionMacro("No input Image set..."); } - m_FusionFilter = TileFilterType::New(); + TileFilterType::Pointer fusionFilter = TileFilterType::New(); TileFilterType::SizeType layout; layout[0] = this->GetParameterInt("cols"); layout[1] = this->GetParameterInt("rows"); - m_FusionFilter->SetLayout(layout); + fusionFilter->SetLayout(layout); for (unsigned int i=0; i<(layout[0]*layout[1]); i++) { - m_FusionFilter->SetInput(i,tileList->GetNthElement(i)); + fusionFilter->SetInput(i,tileList->GetNthElement(i)); } - SetParameterOutputImage("out", m_FusionFilter->GetOutput()); + SetParameterOutputImage("out", fusionFilter->GetOutput()); + RegisterPipeline(); } - TileFilterType::Pointer m_FusionFilter; - }; } diff --git a/Modules/Applications/AppSegmentation/app/otbLSMSSegmentation.cxx b/Modules/Applications/AppSegmentation/app/otbLSMSSegmentation.cxx index 1d1fe5e17c187f1a1f92a72ccea9287990fd576e..290b393e841bbfef53018353985f4912a29f14e9 100644 --- a/Modules/Applications/AppSegmentation/app/otbLSMSSegmentation.cxx +++ b/Modules/Applications/AppSegmentation/app/otbLSMSSegmentation.cxx @@ -88,13 +88,14 @@ public: LabelImageType, AffineFunctorType> LabelShiftFilterType; - LSMSSegmentation(): m_FinalReader(),m_ImportGeoInformationFilter(),m_FilesToRemoveAfterExecute(),m_TmpDirCleanup(false){} + LSMSSegmentation(): //m_FinalReader(),m_ImportGeoInformationFilter(), + m_FilesToRemoveAfterExecute(),m_TmpDirCleanup(false){} ~LSMSSegmentation() override{} private: - LabelImageReaderType::Pointer m_FinalReader; - ImportGeoInformationImageFilterType::Pointer m_ImportGeoInformationFilter; + // LabelImageReaderType::Pointer m_FinalReader; + // ImportGeoInformationImageFilterType::Pointer m_ImportGeoInformationFilter; std::vector<std::string> m_FilesToRemoveAfterExecute; bool m_TmpDirCleanup; @@ -708,20 +709,23 @@ private: otbAppLogINFO(<<"Elapsed time: "<<(double)(toc - tic) / CLOCKS_PER_SEC<<" seconds"); // Final writing - m_FinalReader = LabelImageReaderType::New(); - m_FinalReader->SetFileName(vrtfile); + LabelImageReaderType::Pointer finalReader = LabelImageReaderType::New(); + finalReader->SetFileName(vrtfile); - m_ImportGeoInformationFilter = ImportGeoInformationImageFilterType::New(); - m_ImportGeoInformationFilter->SetInput(m_FinalReader->GetOutput()); - m_ImportGeoInformationFilter->SetSource(imageIn); + ImportGeoInformationImageFilterType::Pointer + importGeoInformationFilter = + ImportGeoInformationImageFilterType::New(); + importGeoInformationFilter->SetInput(finalReader->GetOutput()); + importGeoInformationFilter->SetSource(imageIn); - SetParameterOutputImage("out",m_ImportGeoInformationFilter->GetOutput()); + SetParameterOutputImage("out",importGeoInformationFilter->GetOutput()); + RegisterPipeline(); } void AfterExecuteAndWriteOutputs() override { // Release input files - m_FinalReader = ITK_NULLPTR; + // finalReader = ITK_NULLPTR; if(GetParameterInt("cleanup")) { diff --git a/Modules/Applications/AppSegmentation/app/otbMeanShiftSmoothing.cxx b/Modules/Applications/AppSegmentation/app/otbMeanShiftSmoothing.cxx index 3b8dca5976ca3ca7c8ee1fba11e4efa1b42e8563..1d30963c546efd88ebc84ec613144f2463a6ade8 100644 --- a/Modules/Applications/AppSegmentation/app/otbMeanShiftSmoothing.cxx +++ b/Modules/Applications/AppSegmentation/app/otbMeanShiftSmoothing.cxx @@ -181,21 +181,21 @@ private: { FloatVectorImageType* input = GetParameterImage("in"); - m_Filter = MSFilterType::New(); + MSFilterType::Pointer filter = MSFilterType::New(); - m_Filter->SetInput(input); + filter->SetInput(input); - m_Filter->SetSpatialBandwidth(GetParameterInt("spatialr")); - m_Filter->SetRangeBandwidth(GetParameterFloat("ranger")); - m_Filter->SetThreshold(GetParameterFloat("thres")); - m_Filter->SetMaxIterationNumber(GetParameterInt("maxiter")); - m_Filter->SetRangeBandwidthRamp(GetParameterFloat("rangeramp")); - m_Filter->SetModeSearch(GetParameterInt("modesearch")); + filter->SetSpatialBandwidth(GetParameterInt("spatialr")); + filter->SetRangeBandwidth(GetParameterFloat("ranger")); + filter->SetThreshold(GetParameterFloat("thres")); + filter->SetMaxIterationNumber(GetParameterInt("maxiter")); + filter->SetRangeBandwidthRamp(GetParameterFloat("rangeramp")); + filter->SetModeSearch(GetParameterInt("modesearch")); //Compute the margin used to ensure exact results (tile wise smoothing) //This margin is valid for the default uniform kernel used by the //MeanShiftSmoothing filter (bandwidth equal to radius in this case) - const unsigned int margin = (m_Filter->GetMaxIterationNumber() * m_Filter->GetSpatialBandwidth()) + 1; + const unsigned int margin = (filter->GetMaxIterationNumber() * filter->GetSpatialBandwidth()) + 1; otbAppLogINFO(<<"Margin of " << margin << " pixels applied to each tile to stabilized mean shift filtering." << std::endl); @@ -204,19 +204,18 @@ private: otbAppLogWARNING(<<"Margin value exceed the input image size." << std::endl); } - SetParameterOutputImage("fout", m_Filter->GetOutput()); + SetParameterOutputImage("fout", filter->GetOutput()); if (IsParameterEnabled("foutpos") && HasValue("foutpos")) { - SetParameterOutputImage("foutpos", m_Filter->GetSpatialOutput()); + SetParameterOutputImage("foutpos", filter->GetSpatialOutput()); } if(!GetParameterInt("modesearch")) { otbAppLogINFO(<<"Mode Search is disabled." << std::endl); } + RegisterPipeline(); } - MSFilterType::Pointer m_Filter; - }; diff --git a/Modules/IO/ExtendedFilename/include/otbExtendedFilenameHelper.h b/Modules/Core/Common/include/otbExtendedFilenameHelper.h similarity index 94% rename from Modules/IO/ExtendedFilename/include/otbExtendedFilenameHelper.h rename to Modules/Core/Common/include/otbExtendedFilenameHelper.h index 72123839c76f922c32d63296abd6c3559f92f705..549ad2451d85745f196db750ceea2ba3a9156c12 100644 --- a/Modules/IO/ExtendedFilename/include/otbExtendedFilenameHelper.h +++ b/Modules/Core/Common/include/otbExtendedFilenameHelper.h @@ -23,6 +23,7 @@ #include "itkObject.h" #include "itkObjectFactory.h" +#include "OTBCommonExport.h" namespace otb { @@ -33,9 +34,10 @@ namespace otb * \sa ImageFileReader * * \ingroup OTBExtendedFilename + * \ingroup OTBCommon */ -class ITK_EXPORT ExtendedFilenameHelper : public itk::Object +class OTBCommon_EXPORT ExtendedFilenameHelper : public itk::Object { public: /** Standard class typedefs. */ @@ -55,7 +57,7 @@ public: itkGetStringMacro(ExtendedFileName); itkGetStringMacro(SimpleFileName); - struct GenericBandRange : std::pair<int,int> + struct OTBCommon_EXPORT GenericBandRange : std::pair<int,int> { GenericBandRange() {} diff --git a/Modules/Core/Common/include/otbUtils.h b/Modules/Core/Common/include/otbUtils.h index da2ecc286ed4abf8e175a700b69f9e14a8483fb4..ec8ed5475928066f74af913491931433b1a4d6da 100644 --- a/Modules/Core/Common/include/otbUtils.h +++ b/Modules/Core/Common/include/otbUtils.h @@ -51,6 +51,9 @@ namespace Utils /** Function that prints nothing (useful to disable libsvm logs)*/ void OTBCommon_EXPORT PrintNothing(const char *s); + extern OTBCommon_EXPORT bool const TrueConstant; + + extern OTBCommon_EXPORT bool const FalseConstant; } } // namespace otb diff --git a/Modules/Core/Common/otb-module.cmake b/Modules/Core/Common/otb-module.cmake index 97ba992dd7ca4a88b6ce1ac6475678f27eb1368b..b3276d69422b566872290eba6a5d817cb2b45be1 100644 --- a/Modules/Core/Common/otb-module.cmake +++ b/Modules/Core/Common/otb-module.cmake @@ -27,6 +27,8 @@ ENABLE_SHARED OTBITK #Add dependency to OTBGDAL as GDAL module need to set OTB_USE_GDAL_20 before configuring otbConfigure.h OTBGDAL + #Add dependency for extended filename helper class + OTBBoostAdapters TEST_DEPENDS OTBImageBase diff --git a/Modules/Core/Common/src/CMakeLists.txt b/Modules/Core/Common/src/CMakeLists.txt index ce5d20761e59a5e57cf7f3b909317dc1c072f7aa..1aee98f792eca87f73212b1892b60e4571698bf2 100644 --- a/Modules/Core/Common/src/CMakeLists.txt +++ b/Modules/Core/Common/src/CMakeLists.txt @@ -29,6 +29,7 @@ set(OTBCommon_SRC otbWriterWatcherBase.cxx otbStopwatch.cxx otbStringToHTML.cxx + otbExtendedFilenameHelper.cxx otbLogger.cxx ) diff --git a/Modules/IO/ExtendedFilename/src/otbExtendedFilenameHelper.cxx b/Modules/Core/Common/src/otbExtendedFilenameHelper.cxx similarity index 100% rename from Modules/IO/ExtendedFilename/src/otbExtendedFilenameHelper.cxx rename to Modules/Core/Common/src/otbExtendedFilenameHelper.cxx diff --git a/Modules/Core/Common/src/otbUtils.cxx b/Modules/Core/Common/src/otbUtils.cxx index f837789ba6ee406bb6739f527dc2ef103f780f9a..7bdf9edb7121801353360e316d8c15ac4836fe6b 100644 --- a/Modules/Core/Common/src/otbUtils.cxx +++ b/Modules/Core/Common/src/otbUtils.cxx @@ -41,6 +41,10 @@ void PrintNothing(const char * /* s */) { } +bool const TrueConstant = true; + +bool const FalseConstant = false; + } } diff --git a/Modules/Core/ObjectList/include/otbDataObjectListInterface.h b/Modules/Core/ObjectList/include/otbDataObjectListInterface.h new file mode 100644 index 0000000000000000000000000000000000000000..c1a1ac722fd9398613bac3953252dcc1b8c2af0b --- /dev/null +++ b/Modules/Core/ObjectList/include/otbDataObjectListInterface.h @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2005-2017 Centre National d'Etudes Spatiales (CNES) + * + * This file is part of Orfeo Toolbox + * + * https://www.orfeo-toolbox.org/ + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef otbDataObjectListInterface_h +#define otbDataObjectListInterface_h + +#include "itkDataObject.h" +#include <boost/noncopyable.hpp> + +namespace otb +{ + +class DataObjectListInterface : private boost::noncopyable +{ +/** \class DataObjectListInterface + * \brief This non template class is an interface that wrapp ObjectList + * + * New method that do not need the template parameter of ObjectList + * should be declare here. + * + * \ingroup OTBObjectList + */ +public: + /** + Get the nth element of the list as a DataObject *. + */ + virtual itk::DataObject * GetNthDataObject(unsigned int index) const = 0; + + virtual std::size_t Size(void) const = 0; + +protected: + DataObjectListInterface() = default ; + virtual ~DataObjectListInterface() = default ; +}; + +} // end of otb namespace + +#endif diff --git a/Modules/Core/ObjectList/include/otbObjectList.h b/Modules/Core/ObjectList/include/otbObjectList.h index 6df4f167c879a5ffd349ef2ec587e0e6ad26fb3a..80464782d4af1ccf21c9f93b13f406d4017831e6 100644 --- a/Modules/Core/ObjectList/include/otbObjectList.h +++ b/Modules/Core/ObjectList/include/otbObjectList.h @@ -23,6 +23,7 @@ #include <vector> #include "itkDataObject.h" +#include "otbDataObjectListInterface.h" #include "itkObjectFactory.h" namespace otb @@ -36,7 +37,7 @@ namespace otb * \ingroup OTBObjectList */ template <class TObject> -class ITK_EXPORT ObjectList : public itk::DataObject +class ITK_EXPORT ObjectList : public itk::DataObject , public DataObjectListInterface { public: /** Standard typedefs */ @@ -71,7 +72,7 @@ public: * Get the number of elements in the vector. * \return The number of elements in the vector. */ - InternalContainerSizeType Size(void) const; + InternalContainerSizeType Size(void) const override; /** * Resize the maximal list capacity. * \param size The new maximal size of the list. @@ -99,6 +100,11 @@ public: * \return The pointer to the nth element of the list. */ ObjectPointerType GetNthElement(unsigned int index) const; + /** + * Get the nth element of the list as a DataObject *. + * \param index The index of the object to get. + */ + Superclass * GetNthDataObject(unsigned int index) const override; /** * Return the first element of the list. * \return The first element of the list. @@ -462,7 +468,7 @@ public: friend class ConstIterator; friend class ReverseIterator; /** typedef of the internal iterator */ - typedef typename InternalContainerType::reverse_iterator InternalReverseConstIteratorType; + typedef typename InternalContainerType::const_reverse_iterator InternalReverseConstIteratorType; /** Constructor */ ReverseConstIterator() {}; /** Constructor with iternal iterator parameter */ diff --git a/Modules/Core/ObjectList/include/otbObjectList.txx b/Modules/Core/ObjectList/include/otbObjectList.txx index 26044b1432f2023d2c34ecbdb57afa8374bab3e3..84f600c6fd2480db7c8dfedca12e923fa602f7da 100644 --- a/Modules/Core/ObjectList/include/otbObjectList.txx +++ b/Modules/Core/ObjectList/include/otbObjectList.txx @@ -162,6 +162,16 @@ ObjectList<TObject> } return m_InternalContainer[index]; } + +template <class TObject> +typename ObjectList<TObject>::Superclass * +ObjectList<TObject> +::GetNthDataObject(unsigned int index) const +{ + + return dynamic_cast<itk::DataObject *> ( GetNthElement(index).GetPointer() ); +} + /** * Return the first element of the list. * \return The first element of the list. @@ -236,7 +246,11 @@ typename ObjectList<TObject>::ReverseIterator ObjectList<TObject> ::Insert(ReverseIterator position, ObjectPointerType element) { - ReverseIterator iter(m_InternalContainer.insert(position.GetIter(), element)); + ReverseIterator iter( + InternalContainerType::reverse_iterator( + m_InternalContainer.insert(position.GetIter().base(), element) + ) + ); this->Modified(); return iter; } diff --git a/Modules/Core/Streaming/include/otbStreamingImageVirtualWriter.h b/Modules/Core/Streaming/include/otbStreamingImageVirtualWriter.h index 998ba4c287f98991973e8c15d96448124ad21058..29fafc6bf528190377325ff63be8fb2e5b67664b 100644 --- a/Modules/Core/Streaming/include/otbStreamingImageVirtualWriter.h +++ b/Modules/Core/Streaming/include/otbStreamingImageVirtualWriter.h @@ -24,6 +24,7 @@ #include "itkMacro.h" #include "itkImageToImageFilter.h" #include "otbStreamingManager.h" +#include "itkFastMutexLock.h" namespace otb { @@ -142,6 +143,11 @@ public: * This filter does not produce an output */ void Update() override; + /** This override doesn't return a const ref on the actual boolean */ + const bool & GetAbortGenerateData() const override; + + void SetAbortGenerateData(const bool val) override; + protected: StreamingImageVirtualWriter(); @@ -186,6 +192,9 @@ private: bool m_IsObserving; unsigned long m_ObserverID; + + /** Lock to ensure thread-safety (added for the AbortGenerateData flag) */ + itk::SimpleFastMutexLock m_Lock; }; } // end namespace otb diff --git a/Modules/Core/Streaming/include/otbStreamingImageVirtualWriter.txx b/Modules/Core/Streaming/include/otbStreamingImageVirtualWriter.txx index 008b6c0eaafe7d39dfc3febf6889be7471d1cf0c..ec0ae5ae6d760b11ca6238deb582174752bb7f21 100644 --- a/Modules/Core/Streaming/include/otbStreamingImageVirtualWriter.txx +++ b/Modules/Core/Streaming/include/otbStreamingImageVirtualWriter.txx @@ -32,6 +32,7 @@ #include "otbTileDimensionTiledStreamingManager.h" #include "otbRAMDrivenTiledStreamingManager.h" #include "otbRAMDrivenAdaptativeStreamingManager.h" +#include "otbUtils.h" namespace otb { @@ -258,6 +259,13 @@ StreamingImageVirtualWriter<TInputImage> { this->UpdateProgress(1.0); } + else + { + itk::ProcessAborted e(__FILE__, __LINE__); + e.SetLocation(ITK_LOCATION); + e.SetDescription("Image streaming has been aborted"); + throw e; + } // Notify end event observers this->InvokeEvent(itk::EndEvent()); @@ -285,6 +293,27 @@ StreamingImageVirtualWriter<TInputImage> this->ReleaseInputs(); } +template <class TInputImage> +const bool & +StreamingImageVirtualWriter<TInputImage> +::GetAbortGenerateData() const +{ + m_Lock.Lock(); + bool ret = Superclass::GetAbortGenerateData(); + m_Lock.Unlock(); + if (ret) return otb::Utils::TrueConstant; + return otb::Utils::FalseConstant; +} + +template <class TInputImage> +void +StreamingImageVirtualWriter<TInputImage> +::SetAbortGenerateData(bool val) +{ + m_Lock.Lock(); + Superclass::SetAbortGenerateData(val); + m_Lock.Unlock(); +} } // end namespace otb diff --git a/Modules/Core/Streaming/include/otbStreamingManager.h b/Modules/Core/Streaming/include/otbStreamingManager.h index cd57e83f9e7ddeda81282d35af4d048a4d837faa..0bffd7e95779c755c41e2a94a276a72360845e1d 100644 --- a/Modules/Core/Streaming/include/otbStreamingManager.h +++ b/Modules/Core/Streaming/include/otbStreamingManager.h @@ -67,6 +67,7 @@ public: typedef typename ImageType::InternalPixelType PixelType; typedef otb::PipelineMemoryPrintCalculator::MemoryPrintType MemoryPrintType; + typedef itk::ImageRegionSplitterBase AbstractSplitterType; /** Type macro */ itkTypeMacro(StreamingManager, itk::LightObject); @@ -74,6 +75,8 @@ public: /** Dimension of input image. */ itkStaticConstMacro(ImageDimension, unsigned int, ImageType::ImageDimension); + const AbstractSplitterType * GetSplitter() const; + /** Actually computes the stream divisions, according to the specified streaming mode, * eventually using the input parameter to estimate memory consumption */ virtual void PrepareStreaming(itk::DataObject * input, const RegionType ®ion) = 0; @@ -106,7 +109,6 @@ protected: RegionType m_Region; /** The splitter used to compute the different strips */ - typedef itk::ImageRegionSplitterBase AbstractSplitterType; typedef typename AbstractSplitterType::Pointer AbstractSplitterPointerType; AbstractSplitterPointerType m_Splitter; diff --git a/Modules/Core/Streaming/include/otbStreamingManager.txx b/Modules/Core/Streaming/include/otbStreamingManager.txx index 1d76ff1fe498007c5b9bb60fcc7fa80e040733b2..24e487a4e55998ee7d28b1be1a58cbda21ee8768 100644 --- a/Modules/Core/Streaming/include/otbStreamingManager.txx +++ b/Modules/Core/Streaming/include/otbStreamingManager.txx @@ -40,6 +40,13 @@ StreamingManager<TImage>::~StreamingManager() { } +template <class TImage> +const typename StreamingManager<TImage>::AbstractSplitterType * +StreamingManager<TImage>::GetSplitter() const +{ + return m_Splitter; +} + template <class TImage> typename StreamingManager<TImage>::MemoryPrintType StreamingManager<TImage>::GetActualAvailableRAMInBytes(MemoryPrintType availableRAMInMB) diff --git a/Modules/Feature/Edge/include/otbLineSegmentDetector.txx b/Modules/Feature/Edge/include/otbLineSegmentDetector.txx index c06344e1831c17e0f9c07448a40f7ceb1b154155..8ff969c22c23d5558295af53589c62a5d8168e38 100644 --- a/Modules/Feature/Edge/include/otbLineSegmentDetector.txx +++ b/Modules/Feature/Edge/include/otbLineSegmentDetector.txx @@ -792,8 +792,14 @@ LineSegmentDetector<TInputImage, TPrecision> l = (static_cast<double>((*it)[0]) - x) * dx + (static_cast<double>((*it)[1]) - y) * dy; w = -(static_cast<double>((*it)[0]) - x) * dy + (static_cast<double>((*it)[1]) - y) * dx; - if (l < l_min) l_min = l; if (l > l_max) l_max = l; - if (w < w_min) w_min = w; if (w > w_max) w_max = w; + if (l < l_min) + l_min = l; + if (l > l_max) + l_max = l; + if (w < w_min) + w_min = w; + if (w > w_max) + w_max = w; sum_l[static_cast < int > (vcl_floor(l) + 0.5) + Diagonal] += static_cast<MagnitudePixelType>(weight); sum_w[static_cast < int > (vcl_floor(w) + 0.5) + Diagonal] += static_cast<MagnitudePixelType>(weight); diff --git a/Modules/Feature/Edge/include/otbLocalHoughFilter.txx b/Modules/Feature/Edge/include/otbLocalHoughFilter.txx index a3b0d6e6be609527145600c5024aa0f8ffec19a3..b6a21b20f2e078f308d1af840690944d5096bc1c 100644 --- a/Modules/Feature/Edge/include/otbLocalHoughFilter.txx +++ b/Modules/Feature/Edge/include/otbLocalHoughFilter.txx @@ -229,7 +229,11 @@ LocalHoughFilter<TInputImage> // Get the list of LineSpatialObject lines // --------------------------------------- + #if !defined(ITK_LEGACY_REMOVE) lines = houghFilter->GetLines(m_NumberOfLines); + #else + lines = houghFilter->GetLines(); + #endif LineIterator itLines = lines.begin(); diff --git a/Modules/Filtering/Path/include/otbClosePathFunctor.h b/Modules/Filtering/Path/include/otbClosePathFunctor.h index 90924a2b427f09eedc3289678f17115078c5cd1f..d880196c9f4b9d4992fd538fc8c988f74369fcdc 100644 --- a/Modules/Filtering/Path/include/otbClosePathFunctor.h +++ b/Modules/Filtering/Path/include/otbClosePathFunctor.h @@ -59,6 +59,9 @@ public: if(input->GetVertexList()->Size()>0) { + //Initialization of lastVertex to GetVertexList + lastVertex = input->GetVertexList()->Begin().Value(); + for (VertexListConstIteratorType vertexIt = input->GetVertexList()->Begin(); vertexIt != input->GetVertexList()->End(); ++vertexIt) diff --git a/Modules/IO/ExtendedFilename/otb-module.cmake b/Modules/IO/ExtendedFilename/otb-module.cmake index cf194a8edde1a755df8dd5343ffaeefbb56b65c9..1e46b70a5bfefcc72cda05d38d35e428fba5d6f5 100644 --- a/Modules/IO/ExtendedFilename/otb-module.cmake +++ b/Modules/IO/ExtendedFilename/otb-module.cmake @@ -26,8 +26,8 @@ product by skipping either geographic or sensor-model information.") otb_module(OTBExtendedFilename DEPENDS + OTBCommon OTBIOGDAL - OTBBoostAdapters OTBITK TEST_DEPENDS diff --git a/Modules/IO/ExtendedFilename/src/CMakeLists.txt b/Modules/IO/ExtendedFilename/src/CMakeLists.txt index 033f0db2d93dfe762ed49684cdbf8cba0eed68b8..35ec302ad43392100078862ecc954abce105eeaa 100644 --- a/Modules/IO/ExtendedFilename/src/CMakeLists.txt +++ b/Modules/IO/ExtendedFilename/src/CMakeLists.txt @@ -20,15 +20,13 @@ set(OTBExtendedFilename_SRC otbExtendedFilenameToReaderOptions.cxx - otbExtendedFilenameHelper.cxx otbExtendedFilenameToWriterOptions.cxx ) add_library(OTBExtendedFilename ${OTBExtendedFilename_SRC}) target_link_libraries(OTBExtendedFilename + ${OTBCommon_LIBRARIES} ${OTBIOGDAL_LIBRARIES} - ${OTBBoost_LIBRARIES} - ) otb_module_target(OTBExtendedFilename) diff --git a/Modules/IO/ImageIO/include/otbImageFileWriter.h b/Modules/IO/ImageIO/include/otbImageFileWriter.h index 2851c8b03d3372f6353bba967aeaf0f99a1c9ba5..169e946c7579a241c7bed42424c561101d608912 100644 --- a/Modules/IO/ImageIO/include/otbImageFileWriter.h +++ b/Modules/IO/ImageIO/include/otbImageFileWriter.h @@ -25,6 +25,7 @@ #include "itkProcessObject.h" #include "otbStreamingManager.h" #include "otbExtendedFilenameToWriterOptions.h" +#include "itkFastMutexLock.h" namespace otb { @@ -199,6 +200,11 @@ public: itkGetObjectMacro(ImageIO, otb::ImageIOBase); itkGetConstObjectMacro(ImageIO, otb::ImageIOBase); + /** This override doesn't return a const ref on the actual boolean */ + const bool & GetAbortGenerateData() const override; + + void SetAbortGenerateData(const bool val) override; + protected: ImageFileWriter(); ~ImageFileWriter() override; @@ -207,6 +213,9 @@ protected: /** Does the real work. */ void GenerateData(void) override; + /** Prepare the streaming and write the output information on disk */ + void GenerateOutputInformation(void) override; + private: ImageFileWriter(const ImageFileWriter &); //purposely not implemented void operator =(const ImageFileWriter&); //purposely not implemented @@ -270,6 +279,9 @@ private: * This variable can be the number of components in m_ImageIO or the * number of components in the m_BandList (if used) */ unsigned int m_IOComponents; + + /** Lock to ensure thread-safety (added for the AbortGenerateData flag) */ + itk::SimpleFastMutexLock m_Lock; }; } // end namespace otb diff --git a/Modules/IO/ImageIO/include/otbImageFileWriter.txx b/Modules/IO/ImageIO/include/otbImageFileWriter.txx index 677ea22b4bab59d04121735bd58990ca0b516ec4..6cfff64f8ae1df38f26344110537bb1c47d0336c 100644 --- a/Modules/IO/ImageIO/include/otbImageFileWriter.txx +++ b/Modules/IO/ImageIO/include/otbImageFileWriter.txx @@ -48,6 +48,7 @@ #include "otb_boost_tokenizer_header.h" #include "otbStringUtils.h" +#include "otbUtils.h" namespace otb { @@ -262,13 +263,11 @@ ImageFileWriter<TInputImage> return static_cast<const InputImageType*>(this->ProcessObject::GetInput(0)); } -/** - * Update method : update output information of input and write to file - */ +/** Prepare everything and call m_ImageIO.WriteInformation() */ template<class TInputImage> void ImageFileWriter<TInputImage> -::Update() +::GenerateOutputInformation(void) { // Update output information on input image InputImagePointer inputPtr = @@ -397,14 +396,6 @@ ImageFileWriter<TInputImage> } } - this->SetAbortGenerateData(0); - this->SetProgress(0.0); - - /** - * Tell all Observers that the filter is starting - */ - this->InvokeEvent(itk::StartEvent()); - /** Prepare ImageIO : create ImageFactory */ if (m_FileName == "") @@ -476,7 +467,6 @@ ImageFileWriter<TInputImage> /** * Grab the input */ - inputPtr->UpdateOutputInformation(); InputImageRegionType inputRegion = inputPtr->GetLargestPossibleRegion(); /** Parse region size modes */ @@ -540,12 +530,6 @@ ImageFileWriter<TInputImage> const auto firstSplitSize = m_StreamingManager->GetSplit(0).GetSize(); otbLogMacro(Info,<<"File "<<m_FileName<<" will be written in "<<m_NumberOfDivisions<<" blocks of "<<firstSplitSize[0]<<"x"<<firstSplitSize[1]<<" pixels"); - /** - * Loop over the number of pieces, execute the upstream pipeline on each - * piece, and copy the results into the output image. - */ - InputImageRegionType streamRegion; - // // Setup the ImageIO with information from inputPtr // @@ -585,12 +569,33 @@ ImageFileWriter<TInputImage> m_ImageIO->SetFileName(m_FileName.c_str()); m_ImageIO->WriteImageInformation(); +} +/** + * Update method : update output information of input and write to file + */ +template<class TInputImage> +void +ImageFileWriter<TInputImage> +::Update() +{ + this->UpdateOutputInformation(); + + this->SetAbortGenerateData(0); + this->SetProgress(0.0); + + /** + * Tell all Observers that the filter is starting + */ + this->InvokeEvent(itk::StartEvent()); + this->UpdateProgress(0); m_CurrentDivision = 0; m_DivisionProgress = 0; // Get the source process object + InputImagePointer inputPtr = + const_cast<InputImageType *>(this->GetInput()); itk::ProcessObject* source = inputPtr->GetSource(); m_IsObserving = false; m_ObserverID = 0; @@ -612,6 +617,12 @@ ImageFileWriter<TInputImage> otbLogMacro(Warning,<< "Could not get the source process object. Progress report might be buggy"); } + /** + * Loop over the number of pieces, execute the upstream pipeline on each + * piece, and copy the results into the output image. + */ + InputImageRegionType streamRegion; + for (m_CurrentDivision = 0; m_CurrentDivision < m_NumberOfDivisions && !this->GetAbortGenerateData(); m_CurrentDivision++, m_DivisionProgress = 0, this->UpdateFilterProgress()) @@ -645,6 +656,13 @@ ImageFileWriter<TInputImage> { this->UpdateProgress(1.0); } + else + { + itk::ProcessAborted e(__FILE__, __LINE__); + e.SetLocation(ITK_LOCATION); + e.SetDescription("Image writing has been aborted"); + throw e; + } // Notify end event observers this->InvokeEvent(itk::EndEvent()); @@ -833,6 +851,29 @@ ImageFileWriter<TInputImage> return this->m_FilenameHelper->GetSimpleFileName(); } +template <class TInputImage> +const bool & +ImageFileWriter<TInputImage> +::GetAbortGenerateData() const +{ + m_Lock.Lock(); + // protected read here + bool ret = Superclass::GetAbortGenerateData(); + m_Lock.Unlock(); + if (ret) return otb::Utils::TrueConstant; + return otb::Utils::FalseConstant; +} + +template <class TInputImage> +void +ImageFileWriter<TInputImage> +::SetAbortGenerateData(bool val) +{ + m_Lock.Lock(); + Superclass::SetAbortGenerateData(val); + m_Lock.Unlock(); +} + } // end namespace otb #endif diff --git a/Modules/IO/ImageIO/include/otbImageIOFactory.h b/Modules/IO/ImageIO/include/otbImageIOFactory.h index 287c149792eada50c4605aaa1213d6e621c4807a..591999ac6ef4aa91d5017f171fad14df516bb1d7 100644 --- a/Modules/IO/ImageIO/include/otbImageIOFactory.h +++ b/Modules/IO/ImageIO/include/otbImageIOFactory.h @@ -23,6 +23,7 @@ #include "itkObject.h" #include "otbImageIOBase.h" +#include "OTBImageIOExport.h" namespace otb { @@ -31,7 +32,7 @@ namespace otb * * \ingroup OTBImageIO */ -class ITK_EXPORT ImageIOFactory : public itk::Object +class OTBImageIO_EXPORT ImageIOFactory : public itk::Object { public: /** Standard class typedefs. */ diff --git a/Modules/IO/ImageIO/include/otbMultiImageFileWriter.h b/Modules/IO/ImageIO/include/otbMultiImageFileWriter.h new file mode 100644 index 0000000000000000000000000000000000000000..5d2cf24f80ddbcf858d00becb3fbbd3489e5e9f9 --- /dev/null +++ b/Modules/IO/ImageIO/include/otbMultiImageFileWriter.h @@ -0,0 +1,288 @@ +/* + * Copyright (C) CS SI + * + * This file is part of Orfeo Toolbox + * + * https://www.orfeo-toolbox.org/ + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef otbMultiImageFileWriter_h +#define otbMultiImageFileWriter_h + +#include "otbImageFileWriter.h" +#include "otbImage.h" +#include "itkImageBase.h" +#include "itkProcessObject.h" +#include "itkImageIOBase.h" +#include "OTBImageIOExport.h" + +#include <boost/shared_ptr.hpp> + +namespace otb +{ + +/** \class MultiImageFileWriter + * \brief Streams a pipeline with multiple outputs. + * It writes each output to a file. Inputs + * are connected to the writer using the AddInputImage method. + * The type of streaming can be chosen. Each output may have a different size. + * When the user gives a number of lines per strip or a tile size, the value + * is interpreted on the first input to deduce the number of streams. This + * number of streams is then used to split the other inputs. + */ +class OTBImageIO_EXPORT MultiImageFileWriter: public itk::ProcessObject +{ +public: + /** Standard class typedefs. */ + typedef MultiImageFileWriter Self; + typedef itk::ProcessObject Superclass; + typedef itk::SmartPointer<Self> Pointer; + typedef itk::SmartPointer<const Self> ConstPointer; + + itkNewMacro(Self); + + itkTypeMacro(MultiImageFileWriter, itk::ProcessObject); + + /** Public typedefs */ + typedef itk::ImageBase<2> ImageBaseType; + typedef ImageBaseType::RegionType RegionType; + typedef ImageBaseType::IndexType IndexType; + typedef ImageBaseType::IndexValueType IndexValueType; + typedef ImageBaseType::SizeType SizeType; + typedef ImageBaseType::SizeValueType SizeValueType; + + /** Set the streaming mode to 'stripped' and configure the number of strips + * which will be used to stream the image */ + void SetNumberOfDivisionsStrippedStreaming(unsigned int nbDivisions); + + /** Set the streaming mode to 'tiled' and configure the number of tiles + * which will be used to stream the image */ + void SetNumberOfDivisionsTiledStreaming(unsigned int nbDivisions); + + /** Set the streaming mode to 'stripped' and configure the number of strips + * which will be used to stream the image with respect to a number of line + * per strip */ + void SetNumberOfLinesStrippedStreaming(unsigned int nbLinesPerStrip); + + /** Set the streaming mode to 'stripped' and configure the number of MB + * available. The actual number of divisions is computed automatically + * by estimating the memory consumption of the pipeline. + * Setting the availableRAM parameter to 0 means that the available RAM + * is set from the CMake configuration option. + * The bias parameter is a multiplier applied on the estimated memory size + * of the pipeline and can be used to fine tune the potential gap between + * estimated memory and actual memory used, which can happen because of + * composite filters for example */ + void SetAutomaticStrippedStreaming(unsigned int availableRAM = 0, double bias = 1.0); + + /** Set the streaming mode to 'tiled' and configure the dimension of the tiles + * in pixels for each dimension (square tiles will be generated) */ + void SetTileDimensionTiledStreaming(unsigned int tileDimension); + + /** Set the streaming mode to 'tiled' and configure the number of MB + * available. The actual number of divisions is computed automatically + * by estimating the memory consumption of the pipeline. + * Tiles will be square. + * Setting the availableRAM parameter to 0 means that the available RAM + * is set from the CMake configuration option + * The bias parameter is a multiplier applied on the estimated memory size + * of the pipeline and can be used to fine tune the potential gap between + * estimated memory and actual memory used, which can happen because of + * composite filters for example */ + void SetAutomaticTiledStreaming(unsigned int availableRAM = 0, double bias = 1.0); + + /** Set the streaming mode to 'adaptative' and configure the number of MB + * available. The actual number of divisions is computed automatically + * by estimating the memory consumption of the pipeline. + * Tiles will try to match the input file tile scheme. + * Setting the availableRAM parameter to 0 means that the available RAM + * is set from the CMake configuration option */ + void SetAutomaticAdaptativeStreaming(unsigned int availableRAM = 0, double bias = 1.0); + + virtual void UpdateOutputData(itk::DataObject * itkNotUsed(output)); + + /** Connect a new input to the multi-writer. Only the input pointer is + * required. If the filename list is empty, + * streaming will occur without writing. It the filename list contains more + * than one element, then the output will be divided into this number of + * granule files. The resolution factor specifies the ratio between the height of this image and the + * height of a reference image. The number of lines per strip class parameter will be modified according to this factor, so + * that images with different resolutions can be streamed together. For example, use 1.0 for a 10m pixels image, 0.5 for a 20m + * pixels image, and specify the number of lines per strip according to the 10m pixels image. + * You may specify top and bottom margins that will be removed from the input image, according to its largest possible region. + */ + template <class TImage> + void AddInputImage(const TImage* inputPtr, const std::string & fileName) + { + Sink<TImage> * sink = new Sink<TImage>(inputPtr, fileName); + m_SinkList.push_back(SinkBase::Pointer(sink)); + unsigned int size = m_SinkList.size(); + this->SetNthInput(size - 1, const_cast<itk::DataObject*>(dynamic_cast<const itk::DataObject*>(inputPtr))); + } + + /** Add a new ImageFileWriter to the multi-writer. This is an alternative method + * when you already have an instanciated writer. + */ + template <class TWriter> + void AddInputWriter(const TWriter* writer) + { + Sink<typename TWriter::InputImageType > * sink = + new Sink<typename TWriter::InputImageType >(writer); + m_SinkList.push_back(SinkBase::Pointer(sink)); + unsigned int size = m_SinkList.size(); + this->SetNthInput(size - 1, const_cast<itk::DataObject*>(dynamic_cast<const itk::DataObject*>(writer->GetInput()))); + } + + virtual void UpdateOutputInformation(); + + virtual void Update() + { + this->UpdateOutputInformation(); + this->UpdateOutputData(NULL); + } + +protected: + /** SetInput is changed to protected. Use AddInputImage to connect the + * pipeline to the writer + */ + virtual void SetInput(const itk::ProcessObject::DataObjectIdentifierType & key, itk::DataObject* image) + { this->Superclass::SetInput(key, image); } + + /** SetNthInput is changed to protected. Use AddInputImage to connect the + * pipeline to the writer + */ + virtual void SetNthInput(itk::ProcessObject::DataObjectPointerArraySizeType i, itk::DataObject* image) + { this->Superclass::SetNthInput(i, image); } + + MultiImageFileWriter(); + virtual ~MultiImageFileWriter() {} + + /** GenerateData calls the Write method for each connected input */ + virtual void GenerateData(void); + + /** GenerateInputRequestedRegion can predict approximate input regions + * based on the requested region of the fake output. Only usefull for + * pipeline memory print estimation + */ + virtual void GenerateInputRequestedRegion(); + + /** Computes the number of divisions */ + virtual void InitializeStreaming(); + + /** Goes up the pipeline starting at imagePtr, resetting all requested regions + * to a null region. This may be usefull when mixing inputs of different + * resolutions. */ + void ResetAllRequestedRegions(ImageBaseType* imagePtr); + + /** Returns the current stream region of the given input */ + virtual RegionType GetStreamRegion(int inputIndex); + + void operator =(const MultiImageFileWriter&); //purposely not implemented + + void ObserveSourceFilterProgress(itk::Object* object, const itk::EventObject & event) + { + if (typeid(event) != typeid(itk::ProgressEvent)) + { + return; + } + + itk::ProcessObject* processObject = dynamic_cast<itk::ProcessObject*>(object); + if (processObject) + { + m_DivisionProgress = processObject->GetProgress(); + } + + this->UpdateFilterProgress(); + } + + void UpdateFilterProgress() + { + this->UpdateProgress((m_DivisionProgress + m_CurrentDivision) / m_NumberOfDivisions); + } + +private: + typedef otb::Image<unsigned char, 2> FakeOutputType; + typedef StreamingManager<FakeOutputType> StreamingManagerType; + /** Streaming manager used for the N inputs */ + StreamingManagerType::Pointer m_StreamingManager; + + //Division parameters + unsigned int m_NumberOfDivisions; + unsigned int m_CurrentDivision; + float m_DivisionProgress; + + bool m_IsObserving; + unsigned long m_ObserverID; + + /** Internal base wrapper class to handle each ImageFileWriter */ + class SinkBase + { + public: + SinkBase() {} + SinkBase(ImageBaseType::ConstPointer inputImage) : + m_InputImage(inputImage) + {} + virtual ~SinkBase() {} + virtual ImageBaseType::ConstPointer GetInput() const { return m_InputImage; } + virtual ImageBaseType::Pointer GetInput() { return const_cast<ImageBaseType*>(m_InputImage.GetPointer()); } + virtual void WriteImageInformation() = 0; + virtual void Write(const RegionType & streamRegion) = 0; + virtual bool CanStreamWrite() = 0; + typedef boost::shared_ptr<SinkBase> Pointer; + protected: + /** The image on which streaming is performed */ + ImageBaseType::ConstPointer m_InputImage; + }; + + /** \class Sink + * Wrapper class for each ImageFileWriter + */ + template <class TImage> + class Sink : public SinkBase + { + public: + Sink() {} + Sink(typename TImage::ConstPointer inputImage, + const std::string & filename); + Sink(typename otb::ImageFileWriter<TImage>::ConstPointer writer); + + virtual ~Sink() {} + + virtual void WriteImageInformation(); + virtual void Write(const RegionType & streamRegion); + virtual bool CanStreamWrite(); + typedef boost::shared_ptr<Sink> Pointer; + private: + /** Actual writer for this image */ + typename otb::ImageFileWriter<TImage>::Pointer m_Writer; + + /** An ImageIO used to actually write data to a file */ + otb::ImageIOBase::Pointer m_ImageIO; + }; + + /** The list of inputs and their associated parameters, built using AddInput */ + typedef std::vector<boost::shared_ptr<SinkBase> > SinkListType; + SinkListType m_SinkList; + + std::vector<RegionType> m_StreamRegionList; +}; + +} // end of namespace otb + +#ifndef OTB_MANUAL_INSTANTIATION +#include "otbMultiImageFileWriter.txx" +#endif + +#endif // otbMultiImageFileWriter_h diff --git a/Modules/IO/ImageIO/include/otbMultiImageFileWriter.txx b/Modules/IO/ImageIO/include/otbMultiImageFileWriter.txx new file mode 100644 index 0000000000000000000000000000000000000000..5e6d6304dc52aa3dc95a497b45264ff2cc15d277 --- /dev/null +++ b/Modules/IO/ImageIO/include/otbMultiImageFileWriter.txx @@ -0,0 +1,89 @@ +/* + * Copyright (C) CS SI + * + * This file is part of Orfeo Toolbox + * + * https://www.orfeo-toolbox.org/ + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef otbMultiImageFileWriter_txx +#define otbMultiImageFileWriter_txx + +#include "otbMultiImageFileWriter.h" +#include "otbImageIOFactory.h" +#include "otbMacro.h" + +namespace otb +{ + +template <class TImage> +MultiImageFileWriter::Sink<TImage> +::Sink(typename TImage::ConstPointer inputImage, + const std::string & fileName): + SinkBase(dynamic_cast<const ImageBaseType*>(inputImage.GetPointer())), + m_Writer(otb::ImageFileWriter<TImage>::New()), + m_ImageIO(NULL) +{ + m_Writer->SetFileName(fileName); + m_Writer->SetInput(inputImage); +} + +template <class TImage> +MultiImageFileWriter::Sink<TImage> +::Sink(typename otb::ImageFileWriter<TImage>::ConstPointer writer): + SinkBase(dynamic_cast<const ImageBaseType*>(writer->GetInput()->GetPointer())), + m_Writer(writer), + m_ImageIO(NULL) +{ +} + +template <class TImage> +bool +MultiImageFileWriter::Sink<TImage> +::CanStreamWrite() +{ + if (m_ImageIO.IsNull()) + return false; + return m_ImageIO->CanStreamWrite(); +} + +template <class TImage> +void +MultiImageFileWriter::Sink<TImage> +::WriteImageInformation() +{ + m_Writer->UpdateOutputInformation(); + m_ImageIO = m_Writer->GetImageIO(); +} + +template <class TImage> +void +MultiImageFileWriter::Sink<TImage> +::Write(const RegionType & streamRegion) +{ + // Write the image stream + itk::ImageIORegion ioRegion(TImage::ImageDimension); + for (unsigned int i = 0; i < TImage::ImageDimension; ++i) + { + ioRegion.SetSize(i, streamRegion.GetSize(i)); + ioRegion.SetIndex(i, streamRegion.GetIndex(i)); + } + m_ImageIO->SetIORegion(ioRegion); + m_Writer->UpdateOutputData(nullptr); +} + +} // end of namespace otb + +#endif // otbMultiImageFileWriter_txx diff --git a/Modules/IO/ImageIO/otb-module.cmake b/Modules/IO/ImageIO/otb-module.cmake index 552d431d5a41aba8c23ba62004eac3e60a905c80..d592ba8b086549b5a132f64f0867282e9b7a4de8 100644 --- a/Modules/IO/ImageIO/otb-module.cmake +++ b/Modules/IO/ImageIO/otb-module.cmake @@ -22,6 +22,7 @@ set(DOCUMENTATION "This module contains classes related to the reading and the writing of remote sensing images.") otb_module(OTBImageIO + ENABLE_SHARED DEPENDS OTBBoostAdapters OTBCommon diff --git a/Modules/IO/ImageIO/src/CMakeLists.txt b/Modules/IO/ImageIO/src/CMakeLists.txt index 2d580c6541b14e92fb19e500276f5146103257e0..e604942a7cd4b8473e595bd457dfbdac05523938 100644 --- a/Modules/IO/ImageIO/src/CMakeLists.txt +++ b/Modules/IO/ImageIO/src/CMakeLists.txt @@ -20,6 +20,7 @@ set(OTBImageIO_SRC otbImageIOFactory.cxx + otbMultiImageFileWriter.cxx ) add_library(OTBImageIO ${OTBImageIO_SRC}) diff --git a/Modules/IO/ImageIO/src/otbMultiImageFileWriter.cxx b/Modules/IO/ImageIO/src/otbMultiImageFileWriter.cxx new file mode 100644 index 0000000000000000000000000000000000000000..a3f32a8d36bd404a271c2fd58d61b94488faf981 --- /dev/null +++ b/Modules/IO/ImageIO/src/otbMultiImageFileWriter.cxx @@ -0,0 +1,443 @@ +/* + * Copyright (C) CS SI + * + * This file is part of Orfeo Toolbox + * + * https://www.orfeo-toolbox.org/ + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "otbMultiImageFileWriter.h" +#include "otbImageIOFactory.h" + +namespace otb +{ + +MultiImageFileWriter +::MultiImageFileWriter() : + m_NumberOfDivisions(0), + m_CurrentDivision(0), + m_DivisionProgress(0.0), + m_IsObserving(true), + m_ObserverID(0) +{ + // By default, we use tiled streaming, with automatic tile size + // We don't set any parameter, so the memory size is retrieved from the OTB configuration options + this->SetAutomaticAdaptativeStreaming(); + // add a fake output to drive memory estimation + this->SetNthOutput(0, FakeOutputType::New()); +} + +void +MultiImageFileWriter +::SetNumberOfDivisionsStrippedStreaming(unsigned int nbDivisions) +{ + typedef NumberOfDivisionsStrippedStreamingManager<FakeOutputType> NumberOfDivisionsStrippedStreamingManagerType; + NumberOfDivisionsStrippedStreamingManagerType::Pointer streamingManager = + NumberOfDivisionsStrippedStreamingManagerType::New(); + streamingManager->SetNumberOfDivisions(nbDivisions); + + m_StreamingManager = streamingManager; +} + +void +MultiImageFileWriter +::SetNumberOfDivisionsTiledStreaming(unsigned int nbDivisions) +{ + typedef NumberOfDivisionsTiledStreamingManager<FakeOutputType> NumberOfDivisionsTiledStreamingManagerType; + NumberOfDivisionsTiledStreamingManagerType::Pointer streamingManager = + NumberOfDivisionsTiledStreamingManagerType::New(); + streamingManager->SetNumberOfDivisions(nbDivisions); + + m_StreamingManager = streamingManager; +} + +void +MultiImageFileWriter +::SetNumberOfLinesStrippedStreaming(unsigned int nbLinesPerStrip) +{ + typedef NumberOfLinesStrippedStreamingManager<FakeOutputType> NumberOfLinesStrippedStreamingManagerType; + NumberOfLinesStrippedStreamingManagerType::Pointer streamingManager = + NumberOfLinesStrippedStreamingManagerType::New(); + streamingManager->SetNumberOfLinesPerStrip(nbLinesPerStrip); + + m_StreamingManager = streamingManager; +} + +void +MultiImageFileWriter +::SetAutomaticStrippedStreaming(unsigned int availableRAM, double bias) +{ + typedef RAMDrivenStrippedStreamingManager<FakeOutputType> RAMDrivenStrippedStreamingManagerType; + RAMDrivenStrippedStreamingManagerType::Pointer streamingManager = + RAMDrivenStrippedStreamingManagerType::New(); + streamingManager->SetAvailableRAMInMB(availableRAM); + streamingManager->SetBias(bias); + + m_StreamingManager = streamingManager; +} + +void +MultiImageFileWriter +::SetTileDimensionTiledStreaming(unsigned int tileDimension) +{ + typedef TileDimensionTiledStreamingManager<FakeOutputType> TileDimensionTiledStreamingManagerType; + TileDimensionTiledStreamingManagerType::Pointer streamingManager = + TileDimensionTiledStreamingManagerType::New(); + streamingManager->SetTileDimension(tileDimension); + + m_StreamingManager = streamingManager; +} + +void +MultiImageFileWriter +::SetAutomaticTiledStreaming(unsigned int availableRAM, double bias) +{ + typedef RAMDrivenTiledStreamingManager<FakeOutputType> RAMDrivenTiledStreamingManagerType; + RAMDrivenTiledStreamingManagerType::Pointer streamingManager = + RAMDrivenTiledStreamingManagerType::New(); + streamingManager->SetAvailableRAMInMB(availableRAM); + streamingManager->SetBias(bias); + m_StreamingManager = streamingManager; +} + +void +MultiImageFileWriter +::SetAutomaticAdaptativeStreaming(unsigned int availableRAM, double bias) +{ + typedef RAMDrivenAdaptativeStreamingManager<FakeOutputType> RAMDrivenAdaptativeStreamingManagerType; + RAMDrivenAdaptativeStreamingManagerType::Pointer streamingManager = + RAMDrivenAdaptativeStreamingManagerType::New(); + streamingManager->SetAvailableRAMInMB(availableRAM); + streamingManager->SetBias(bias); + m_StreamingManager = streamingManager; +} + +void +MultiImageFileWriter +::InitializeStreaming() +{ +// const ImageBaseType* inputPtr = this->GetInput(0); + if(m_SinkList.size() == 0) + itkExceptionMacro("At least one input must be connected to the writer\n"); + const ImageBaseType* inputPtr = m_SinkList[0]->GetInput(); + if(!inputPtr) + itkExceptionMacro("At least one input must be connected to the writer\n"); + + /** Control if the ImageIO is CanStreamWrite */ + m_NumberOfDivisions = 1; + bool canStream = true; + bool isBuffered = true; + for (unsigned int inputIndex = 0; inputIndex < m_SinkList.size(); ++inputIndex) + { + if (!m_SinkList[inputIndex]->CanStreamWrite()) + { + canStream = false; + } + if (m_SinkList[inputIndex]->GetInput()->GetBufferedRegion() != + m_SinkList[inputIndex]->GetInput()->GetLargestPossibleRegion()) + { + isBuffered = false; + } + } + if (canStream == false) + { + otbWarningMacro( + << "One of the selected ImageIO does not support streaming."); + this->SetNumberOfDivisionsStrippedStreaming(m_NumberOfDivisions); + } + + /** Compare the buffered region with the inputRegion which is the largest + * possible region or a user defined region through extended filename + * Not sure that if this modification is needed */ + else if (isBuffered) + { + otbMsgDevMacro(<< "Buffered region is the largest possible region, there is" + " no need for streaming."); + this->SetNumberOfDivisionsStrippedStreaming(m_NumberOfDivisions); + } + else + { + /** + * Determine of number of pieces to divide the input. This will be the + * first estimated on the fake output, which has the same size as the + * first input. Then there is a check that each input can be split into + * this number of pieces. + */ + FakeOutputType * fakeOut = static_cast<FakeOutputType *>( + this->itk::ProcessObject::GetOutput(0)); + RegionType region = fakeOut->GetLargestPossibleRegion(); + m_StreamingManager->PrepareStreaming(fakeOut, region); + m_NumberOfDivisions = m_StreamingManager->GetNumberOfSplits(); + // Check this number of division is compatible with all inputs + bool nbDivValid = false; + while ( (!nbDivValid) && 1 < m_NumberOfDivisions) + { + unsigned int smallestNbDiv = m_NumberOfDivisions; + for (unsigned int i = 0; i < m_SinkList.size(); ++i) + { + unsigned int div = m_StreamingManager->GetSplitter()->GetNumberOfSplits( + m_SinkList[i]->GetInput()->GetLargestPossibleRegion(), + m_NumberOfDivisions); + smallestNbDiv = std::min(div, smallestNbDiv); + } + if (smallestNbDiv == m_NumberOfDivisions) + { + nbDivValid = true; + } + else + { + m_NumberOfDivisions = smallestNbDiv; + } + } + if (m_NumberOfDivisions == 1) + { + otbWarningMacro("Can't find a common split scheme between all inputs, streaming disabled\n"); + } + otbMsgDebugMacro(<< "Number Of Stream Divisions : " << m_NumberOfDivisions); + } +} + +void +MultiImageFileWriter +::ResetAllRequestedRegions(ImageBaseType* imagePtr) +{ + RegionType nullRegion = imagePtr->GetLargestPossibleRegion(); + nullRegion.SetSize(0, 0); + nullRegion.SetSize(1, 0); + + imagePtr->SetRequestedRegion(nullRegion); + if(imagePtr->GetSource()) + { + itk::ProcessObject::DataObjectPointerArray inputs = imagePtr->GetSource()->GetInputs(); + for( itk::ProcessObject::DataObjectPointerArray::iterator + it = inputs.begin(); + it != inputs.end(); + ++it ) + { + ImageBaseType * inputImagePtr = dynamic_cast<ImageBaseType*>(it->GetPointer()); + if(inputImagePtr != NULL) + { + ResetAllRequestedRegions(inputImagePtr); + } + } + } +} + +void +MultiImageFileWriter +::UpdateOutputInformation() +{ + for(unsigned int inputIndex = 0; inputIndex < m_SinkList.size(); ++inputIndex) + { + m_SinkList[inputIndex]->WriteImageInformation(); + } + this->GenerateOutputInformation(); +} + +void +MultiImageFileWriter +::UpdateOutputData(itk::DataObject * itkNotUsed(output)) +{ + /** + * prevent chasing our tail + */ + if (this->m_Updating) + { + return; + } + + // Initialize streaming + this->InitializeStreaming(); + + this->SetAbortGenerateData(0); + this->SetProgress(0.0); + this->m_Updating = true; + + /** + * Tell all Observers that the filter is starting + */ + this->InvokeEvent(itk::StartEvent()); + + this->UpdateProgress(0); + m_CurrentDivision = 0; + m_DivisionProgress = 0; + + /** Loop over the number of pieces, set and propagate requested regions then + * update pipeline upstream + */ + int numInputs = m_SinkList.size(); //this->GetNumberOfInputs(); + m_StreamRegionList.resize(numInputs); + itkDebugMacro( "Number of streaming divisions: " << m_NumberOfDivisions); + + // Add observer only to first input + if(numInputs > 0) + { + m_IsObserving = false; + m_ObserverID = 0; + + typedef itk::MemberCommand<Self> CommandType; + typedef CommandType::Pointer CommandPointerType; + + CommandPointerType command = CommandType::New(); + command->SetCallbackFunction(this, &Self::ObserveSourceFilterProgress); + + itk::ProcessObject* src = this->GetInput(0)->GetSource(); + m_ObserverID = src->AddObserver(itk::ProgressEvent(), command); + m_IsObserving = true; + } + + for (m_CurrentDivision = 0; m_CurrentDivision < m_NumberOfDivisions && !this->GetAbortGenerateData(); + m_CurrentDivision++, m_DivisionProgress = 0, this->UpdateFilterProgress()) + { + // Update all stream regions + for(int inputIndex = 0; inputIndex < numInputs; ++inputIndex) + { + m_StreamRegionList[inputIndex] = GetStreamRegion(inputIndex); + } + + // NOTE : this reset was probably designed to work with the next section + // Where the final requested region is the "union" between the computed + // requested region and the current requested region. + + // Reset requested regions for all images + for(int inputIndex = 0; inputIndex < numInputs; ++inputIndex) + { + ResetAllRequestedRegions(m_SinkList[inputIndex]->GetInput()); + } + + for(int inputIndex = 0; inputIndex < numInputs; ++inputIndex) + { + ImageBaseType::Pointer inputPtr = m_SinkList[inputIndex]->GetInput(); + RegionType inputRequestedRegion = m_StreamRegionList[inputIndex]; + const RegionType & currentInputRequestedRegion = inputPtr->GetRequestedRegion(); + if( currentInputRequestedRegion != inputPtr->GetLargestPossibleRegion() + && currentInputRequestedRegion.GetNumberOfPixels() != 0) + { + IndexType startIndex = currentInputRequestedRegion.GetIndex(); + IndexType lastIndex = currentInputRequestedRegion.GetUpperIndex(); + startIndex[0] = std::min(startIndex[0], inputRequestedRegion.GetIndex(0)); + startIndex[1] = std::min(startIndex[1], inputRequestedRegion.GetIndex(1)); + lastIndex[0] = std::max(lastIndex[0], inputRequestedRegion.GetUpperIndex()[0]); + lastIndex[1] = std::max(lastIndex[1], inputRequestedRegion.GetUpperIndex()[1]); + inputRequestedRegion.SetIndex(startIndex); + inputRequestedRegion.SetUpperIndex(lastIndex); + } + + inputPtr->SetRequestedRegion(inputRequestedRegion); + inputPtr->PropagateRequestedRegion(); + } + + /** Call GenerateData to write streams to files if needed */ + this->GenerateData(); + } + + /** + * If we ended due to aborting, push the progress up to 1.0 (since + * it probably didn't end there) + */ + if (!this->GetAbortGenerateData()) + { + this->UpdateProgress(1.0); + } + + // Notify end event observers + this->InvokeEvent(itk::EndEvent()); + + if (m_IsObserving) + { + ImageBaseType::Pointer inputPtr = m_SinkList[0]->GetInput(); // const_cast<ImageBaseType *>(this->GetInput(0)); + itk::ProcessObject* source = inputPtr->GetSource(); + m_IsObserving = false; + source->RemoveObserver(m_ObserverID); + } + + /** + * Release any inputs if marked for release + */ + this->ReleaseInputs(); + + // Mark that we are no longer updating the data in this filter + this->m_Updating = false; +} + + +void +MultiImageFileWriter +::GenerateInputRequestedRegion() +{ + Superclass::GenerateInputRequestedRegion(); + + // Approximate conversion of output requested region into each input, + // but this is only to have a consistent pipeline memory estimation. + int numInputs = m_SinkList.size(); //this->GetNumberOfInputs(); + + FakeOutputType* fakeOut = static_cast<FakeOutputType *>( + this->itk::ProcessObject::GetOutput(0)); + + if (numInputs) + { + RegionType refLargest = fakeOut->GetLargestPossibleRegion(); + RegionType refRequest = fakeOut->GetRequestedRegion(); + IndexType idxLargest = refLargest.GetIndex(); + SizeType sizeLargest = refLargest.GetSize(); + IndexType idxRequest = refRequest.GetIndex(); + SizeType sizeRequest = refRequest.GetSize(); + for (int i = 0; i < numInputs; ++i) + { + ImageBaseType* inputPtr = m_SinkList[i]->GetInput(); + if(!inputPtr) + { + return; + } + RegionType region = inputPtr->GetLargestPossibleRegion(); + IndexType idx = region.GetIndex(); + SizeType size = region.GetSize(); + idx[0] += size[0] * (idxRequest[0] - idxLargest[0]) / sizeLargest[0]; + idx[1] += size[1] * (idxRequest[1] - idxLargest[1]) / sizeLargest[1]; + size[0] *= sizeRequest[0] / sizeLargest[0]; + size[1] *= sizeRequest[1] / sizeLargest[1]; + region.SetIndex(idx); + region.SetSize(size); + inputPtr->SetRequestedRegion(region); + } + } +} + +void +MultiImageFileWriter +::GenerateData() +{ + int numInputs = m_SinkList.size(); + for(int inputIndex = 0; inputIndex < numInputs; ++inputIndex) + { + m_SinkList[inputIndex]->Write(m_StreamRegionList[inputIndex]); + } +} + +MultiImageFileWriter::RegionType +MultiImageFileWriter +::GetStreamRegion(int inputIndex) +{ + const SinkBase::Pointer sink = m_SinkList[inputIndex]; + RegionType region = sink->GetInput()->GetLargestPossibleRegion(); + + m_StreamingManager->GetSplitter()->GetSplit( + m_CurrentDivision, + m_NumberOfDivisions, + region); + return region; +} + +} // end of namespace otb diff --git a/Modules/IO/ImageIO/test/CMakeLists.txt b/Modules/IO/ImageIO/test/CMakeLists.txt index aac49fcdeab48d245227689b5aeb6461f6d6dd07..110b25410e8bfd8f663a505fa202b4399d213609 100644 --- a/Modules/IO/ImageIO/test/CMakeLists.txt +++ b/Modules/IO/ImageIO/test/CMakeLists.txt @@ -76,6 +76,7 @@ otbImageIOFactoryNew.cxx otbCompareWritingComplexImage.cxx otbImageFileReaderOptBandTest.cxx otbImageFileWriterOptBandTest.cxx +otbMultiImageFileWriterTest.cxx ) add_executable(otbImageIOTestDriver ${OTBImageIOTests}) @@ -1339,3 +1340,31 @@ otb_add_test(NAME ioTvImageIOToWriterOptions_OptBandReorgTest COMMAND otbImageIO ${TEMP}/QB_Toulouse_Ortho_XS_WriterOptBandReorg.tif?bands=2,:,-3,2:-1 4 ) + +otb_add_test(NAME ioTvMultiImageFileWriter_SameSize + COMMAND otbImageIOTestDriver + --compare-n-images ${EPSILON_9} 2 + ${INPUTDATA}/GomaAvant.png + ${TEMP}/ioTvMultiImageFileWriter_SameSize1.tif + ${INPUTDATA}/GomaApres.png + ${TEMP}/ioTvMultiImageFileWriter_SameSize2.tif + otbMultiImageFileWriterTest + ${INPUTDATA}/GomaAvant.png + ${INPUTDATA}/GomaApres.png + ${TEMP}/ioTvMultiImageFileWriter_SameSize1.tif + ${TEMP}/ioTvMultiImageFileWriter_SameSize2.tif + 50) + +otb_add_test(NAME ioTvMultiImageFileWriter_DiffSize + COMMAND otbImageIOTestDriver + --compare-n-images ${EPSILON_9} 2 + ${INPUTDATA}/GomaAvant.png + ${TEMP}/ioTvMultiImageFileWriter_DiffSize1.tif + ${INPUTDATA}/QB_Toulouse_Ortho_PAN.tif + ${TEMP}/ioTvMultiImageFileWriter_DiffSize2.tif + otbMultiImageFileWriterTest + ${INPUTDATA}/GomaAvant.png + ${INPUTDATA}/QB_Toulouse_Ortho_PAN.tif + ${TEMP}/ioTvMultiImageFileWriter_DiffSize1.tif + ${TEMP}/ioTvMultiImageFileWriter_DiffSize2.tif + 25) diff --git a/Modules/IO/ImageIO/test/otbImageIOTestDriver.cxx b/Modules/IO/ImageIO/test/otbImageIOTestDriver.cxx index fb2aa583aa73653ece1ffb9f523ae6caaec71d84..4a7ff9409a53d34ec7d404c6f6d56cc57c8917bf 100644 --- a/Modules/IO/ImageIO/test/otbImageIOTestDriver.cxx +++ b/Modules/IO/ImageIO/test/otbImageIOTestDriver.cxx @@ -154,4 +154,5 @@ void RegisterTests() REGISTER_TEST(otbCompareWritingComplexImageTest); REGISTER_TEST(otbImageFileReaderOptBandTest); REGISTER_TEST(otbImageFileWriterOptBandTest); + REGISTER_TEST(otbMultiImageFileWriterTest); } diff --git a/Modules/IO/ImageIO/test/otbMultiImageFileWriterTest.cxx b/Modules/IO/ImageIO/test/otbMultiImageFileWriterTest.cxx new file mode 100644 index 0000000000000000000000000000000000000000..77d6ede243b19c34694ff18a74959457c060a0d0 --- /dev/null +++ b/Modules/IO/ImageIO/test/otbMultiImageFileWriterTest.cxx @@ -0,0 +1,65 @@ +/* + * Copyright (C) CS SI + * + * This file is part of Orfeo Toolbox + * + * https://www.orfeo-toolbox.org/ + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "otbMultiImageFileWriter.h" +#include "otbImage.h" +#include "otbImageFileReader.h" + +int otbMultiImageFileWriterTest(int argc, char* argv[]) +{ + typedef unsigned short PixelType1; + typedef otb::Image<PixelType1, 2> ImageType1; + typedef otb::ImageFileReader<ImageType1> ReaderType1; + + typedef double PixelType2; + typedef otb::Image<PixelType2, 2> ImageType2; + typedef otb::ImageFileReader<ImageType2> ReaderType2; + + typedef otb::MultiImageFileWriter WriterType; + + if (argc < 6) + { + std::cout << "Usage: " << argv[0] << " inputImageFileName1 inputImageFileName2 outputImageFileName1 outputImageFileName2 numberOfLinesPerStrip\n"; + return EXIT_FAILURE; + } + + const char * inputImageFileName1 = argv[1]; + const char * inputImageFileName2 = argv[2]; + const std::string outputImageFileName1 = argv[3]; + const std::string outputImageFileName2 = argv[4]; + const int numberOfLinesPerStrip = atoi(argv[5]); + + ReaderType1::Pointer reader1 = ReaderType1::New(); + reader1->SetFileName( inputImageFileName1 ); + + ReaderType2::Pointer reader2 = ReaderType2::New(); + reader2->SetFileName( inputImageFileName2 ); + + WriterType::Pointer writer = WriterType::New(); + writer->AddInputImage( reader1->GetOutput(), outputImageFileName1); + writer->AddInputImage( reader2->GetOutput(), outputImageFileName2); + writer->SetNumberOfLinesStrippedStreaming( numberOfLinesPerStrip ); + + writer->Update(); + + std::cout << writer << std::endl; + + return EXIT_SUCCESS; +} diff --git a/Modules/Learning/Sampling/include/otbSampleAugmentation.h b/Modules/Learning/Sampling/include/otbSampleAugmentation.h new file mode 100644 index 0000000000000000000000000000000000000000..8196b3e9da759e330f6a652a13377b9f609e0d57 --- /dev/null +++ b/Modules/Learning/Sampling/include/otbSampleAugmentation.h @@ -0,0 +1,235 @@ +/* + * Copyright (C) 2005-2017 Centre National d'Etudes Spatiales (CNES) + * + * This file is part of Orfeo Toolbox + * + * https://www.orfeo-toolbox.org/ + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef otbSampleAugmentation_h +#define otbSampleAugmentation_h + +#ifdef _OPENMP + # include <omp.h> +#endif + +#include <vector> +#include <algorithm> +#include <random> +#include <ctime> +#include <cassert> +#include <iostream> + +namespace otb +{ + +namespace sampleAugmentation +{ +using SampleType = std::vector<double>; +using SampleVectorType = std::vector<SampleType>; + +/** +Estimate standard deviations of the components in one pass using +Welford's algorithm +*/ +SampleType EstimateStds(const SampleVectorType& samples) +{ + const auto nbSamples = samples.size(); + const auto nbComponents = samples[0].size(); + SampleType stds(nbComponents, 0.0); + SampleType means(nbComponents, 0.0); + for(size_t i=0; i<nbSamples; ++i) + { + auto norm_factor = 1.0/(i+1); +#ifdef _OPENMP +#pragma omp parallel for +#endif + for(size_t j=0; j<nbComponents; ++j) + { + const auto mu = means[j]; + const auto x = samples[i][j]; + auto muNew = mu+(x-mu)*norm_factor; + stds[j] += (x-mu)*(x-muNew); + means[j] = muNew; + } + } +#ifdef _OPENMP +#pragma omp parallel for +#endif + for(size_t j=0; j<nbComponents; ++j) + { + stds[j] = std::sqrt(stds[j]/nbSamples); + } + return stds; +} + +/** Create new samples by replicating input samples. We loop through +* the input samples and add them to the new data set until nbSamples +* are added. The elements of newSamples are removed before proceeding. +*/ +void ReplicateSamples(const SampleVectorType& inSamples, + const size_t nbSamples, + SampleVectorType& newSamples) +{ + newSamples.resize(nbSamples); + size_t imod{0}; +#ifdef _OPENMP +#pragma omp parallel for +#endif + for(size_t i=0; i<nbSamples; ++i) + { + if (imod == inSamples.size()) imod = 0; + newSamples[i] = inSamples[imod++]; + } + +} + +/** Create new samples by adding noise to existing samples. Gaussian +* noise is added to randomly selected samples. The standard deviation +* of the noise added to each component is the same as the one of the +* input variables divided by stdFactor (defaults to 10). The +* elements of newSamples are removed before proceeding. +*/ +void JitterSamples(const SampleVectorType& inSamples, + const size_t nbSamples, + SampleVectorType& newSamples, + float stdFactor=10, + const int seed = std::time(nullptr)) +{ + newSamples.resize(nbSamples); + const auto nbComponents = inSamples[0].size(); + std::random_device rd; + std::mt19937 gen(rd()); + // The input samples are selected randomly with replacement + std::srand(seed); + // We use one gaussian distribution per component since they may + // have different stds + auto stds = EstimateStds(inSamples); + std::vector<std::normal_distribution<double>> gaussDis(nbComponents); +#ifdef _OPENMP +#pragma omp parallel for +#endif + for(size_t i=0; i<nbComponents; ++i) + gaussDis[i] = std::normal_distribution<double>{0.0, stds[i]/stdFactor}; + + for(size_t i=0; i<nbSamples; ++i) + { + newSamples[i] = inSamples[std::rand()%inSamples.size()]; +#ifdef _OPENMP +#pragma omp parallel for +#endif + for(size_t j=0; j<nbComponents; ++j) + newSamples[i][j] += gaussDis[j](gen); + } +} + + +struct NeighborType +{ + size_t index; + double distance; +}; + +struct NeighborSorter +{ + constexpr bool operator ()(const NeighborType& a, const NeighborType& b) const + { + return b.distance > a.distance; + } +}; + +double ComputeSquareDistance(const SampleType& x, const SampleType& y) +{ + assert(x.size()==y.size()); + double dist{0}; + for(size_t i=0; i<x.size(); ++i) + { + dist += (x[i]-y[i])*(x[i]-y[i]); + } + return dist/(x.size()*x.size()); +} + +using NNIndicesType = std::vector<NeighborType>; +using NNVectorType = std::vector<NNIndicesType>; +/** Returns the indices of the nearest neighbors for each input sample +*/ +void FindKNNIndices(const SampleVectorType& inSamples, + const size_t nbNeighbors, + NNVectorType& nnVector) +{ + const auto nbSamples = inSamples.size(); + nnVector.resize(nbSamples); + #ifdef _OPENMP + #pragma omp parallel for + #endif + for(size_t sampleIdx=0; sampleIdx<nbSamples; ++sampleIdx) + { + NNIndicesType nns; + for(size_t neighborIdx=0; neighborIdx<nbSamples; ++neighborIdx) + { + if(sampleIdx!=neighborIdx) + nns.push_back({neighborIdx, ComputeSquareDistance(inSamples[sampleIdx], + inSamples[neighborIdx])}); + } + std::partial_sort(nns.begin(), nns.begin()+nbNeighbors, nns.end(), NeighborSorter{}); + nns.resize(nbNeighbors); + nnVector[sampleIdx] = std::move(nns); + } +} + +/** Generate the new sample in the line linking s1 and s2 +*/ +SampleType SmoteCombine(const SampleType& s1, const SampleType& s2, double position) +{ + auto result = s1; + for(size_t i=0; i<s1.size(); ++i) + result[i] = s1[i]+(s2[i]-s1[i])*position; + return result; +} + +/** Create new samples using the SMOTE algorithm +Chawla, N. V., Bowyer, K. W., Hall, L. O., & Kegelmeyer, W. P., Smote: +synthetic minority over-sampling technique, Journal of artificial +intelligence research, 16(), 321–357 (2002). +http://dx.doi.org/10.1613/jair.953 +*/ +void Smote(const SampleVectorType& inSamples, + const size_t nbSamples, + SampleVectorType& newSamples, + const int nbNeighbors, + const int seed = std::time(nullptr)) +{ + newSamples.resize(nbSamples); + NNVectorType nnVector; + FindKNNIndices(inSamples, nbNeighbors, nnVector); + // The input samples are selected randomly with replacement + std::srand(seed); + #ifdef _OPENMP + #pragma omp parallel for + #endif + for(size_t i=0; i<nbSamples; ++i) + { + const auto sampleIdx = std::rand()%(inSamples.size()); + const auto sample = inSamples[sampleIdx]; + const auto neighborIdx = nnVector[sampleIdx][std::rand()%nbNeighbors].index; + const auto neighbor = inSamples[neighborIdx]; + newSamples[i] = SmoteCombine(sample, neighbor, std::rand()/double{RAND_MAX}); + } +} + +}//end namespaces sampleAugmentation +}//end namespace otb + +#endif diff --git a/Modules/Learning/Sampling/include/otbSampleAugmentationFilter.h b/Modules/Learning/Sampling/include/otbSampleAugmentationFilter.h new file mode 100644 index 0000000000000000000000000000000000000000..63b8af0a7a95dabb33795817a4a4b302d495b832 --- /dev/null +++ b/Modules/Learning/Sampling/include/otbSampleAugmentationFilter.h @@ -0,0 +1,175 @@ +/* + * Copyright (C) 2005-2017 Centre National d'Etudes Spatiales (CNES) + * + * This file is part of Orfeo Toolbox + * + * https://www.orfeo-toolbox.org/ + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef otbSampleAugmentationFilter_h +#define otbSampleAugmentationFilter_h + +#include "itkProcessObject.h" +#include "otbOGRDataSourceWrapper.h" +#include "otbSampleAugmentation.h" +#include "OTBSamplingExport.h" + +namespace otb +{ + + +/** + * \class SampleAugmentationFilter + * + * \brief Filter to generate synthetic samples from existing ones + * + * This class generates synthetic samples from existing ones either by + * replication, jitter (adding gaussian noise to the features of + * existing samples) or SMOTE (linear combination of pairs + * neighbouring samples of the same class. + * + * \ingroup OTBSampling + */ + +class OTBSampling_EXPORT SampleAugmentationFilter : + public itk::ProcessObject +{ +public: + + /** typedef for the classes standards. */ + typedef SampleAugmentationFilter Self; + typedef itk::ProcessObject Superclass; + typedef itk::SmartPointer<Self> Pointer; + typedef itk::SmartPointer<const Self> ConstPointer; + + /** Method for management of the object factory. */ + itkNewMacro(Self); + + /** Return the name of the class. */ + itkTypeMacro(SampleAugmentationFilter, ProcessObject); + + typedef ogr::DataSource OGRDataSourceType; + typedef typename OGRDataSourceType::Pointer OGRDataSourcePointerType; + typedef ogr::Layer OGRLayerType; + + typedef itk::ProcessObject::DataObjectPointerArraySizeType DataObjectPointerArraySizeType; + + using SampleType = sampleAugmentation::SampleType; + using SampleVectorType = sampleAugmentation::SampleVectorType; + + enum class Strategy { Replicate, Jitter, Smote }; + + /** Set/Get the input OGRDataSource of this process object. */ + using Superclass::SetInput; + virtual void SetInput(const OGRDataSourceType* ds); + const OGRDataSourceType* GetInput(unsigned int idx); + + virtual void SetOutputSamples(ogr::DataSource* data); + + /** Set the Field Name in which labels will be written. (default is "class") + * A field "ClassFieldName" of type integer is created in the output memory layer. + */ + itkSetMacro(ClassFieldName, std::string); + /** + * Return the Field name in which labels have been written. + */ + itkGetMacro(ClassFieldName, std::string); + + + itkSetMacro(Layer, size_t); + itkGetMacro(Layer, size_t); + itkSetMacro(Label, int); + itkGetMacro(Label, int); + void SetStrategy(Strategy s) + { + m_Strategy = s; + } + Strategy GetStrategy() const + { + return m_Strategy; + } + itkSetMacro(NumberOfSamples, int); + itkGetMacro(NumberOfSamples, int); + void SetExcludedFields(const std::vector<std::string>& ef) + { + m_ExcludedFields = ef; + } + std::vector<std::string> GetExcludedFields() const + { + return m_ExcludedFields; + } + itkSetMacro(StdFactor, double); + itkGetMacro(StdFactor, double); + itkSetMacro(SmoteNeighbors, size_t); + itkGetMacro(SmoteNeighbors, size_t); + itkSetMacro(Seed, int); + itkGetMacro(Seed, int); +/** + * Get the output \c ogr::DataSource which is a "memory" datasource. + */ + const OGRDataSourceType * GetOutput(); + +protected: + SampleAugmentationFilter(); + ~SampleAugmentationFilter() ITK_OVERRIDE {} + + /** Generate Data method*/ + void GenerateData() ITK_OVERRIDE; + + /** DataObject pointer */ + typedef itk::DataObject::Pointer DataObjectPointer; + + DataObjectPointer MakeOutput(DataObjectPointerArraySizeType idx) ITK_OVERRIDE; + using Superclass::MakeOutput; + + + SampleVectorType ExtractSamples(const ogr::DataSource::Pointer vectors, + size_t layerName, + const std::string& classField, const int label, + const std::vector<std::string>& excludedFields = {}); + + void SampleToOGRFeatures(const ogr::DataSource::Pointer& vectors, + ogr::DataSource* output, + const SampleVectorType& samples, + const size_t layerName, + const std::string& classField, int label, + const std::vector<std::string>& excludedFields = {}); + + std::set<size_t> GetExcludedFieldsIds(const std::vector<std::string>& excludedFields, + const ogr::Layer& inputLayer); + bool IsNumericField(const ogr::Feature& feature, const int idx); + + ogr::Feature SelectTemplateFeature(const ogr::Layer& inputLayer, + const std::string& classField, int label); +private: + SampleAugmentationFilter(const Self &); //purposely not implemented + void operator =(const Self&); //purposely not implemented + + std::string m_ClassFieldName; + size_t m_Layer; + int m_Label; + std::vector<std::string> m_ExcludedFields; + Strategy m_Strategy; + int m_NumberOfSamples; + double m_StdFactor; + size_t m_SmoteNeighbors; + int m_Seed; + +}; + + +} // end namespace otb + +#endif diff --git a/Modules/Learning/Sampling/include/otbSamplingRateCalculator.h b/Modules/Learning/Sampling/include/otbSamplingRateCalculator.h index e16e8a9a48447e453cf2c70e69b4c881eb1c19fb..c16a758a5d82b51578de112e59963470f3625f41 100644 --- a/Modules/Learning/Sampling/include/otbSamplingRateCalculator.h +++ b/Modules/Learning/Sampling/include/otbSamplingRateCalculator.h @@ -24,6 +24,7 @@ #include "itkImageRegion.h" #include "itkVectorContainer.h" #include <set> +#include "OTBSamplingExport.h" namespace otb { @@ -36,7 +37,7 @@ namespace otb * \ingroup OTBSampling */ -class ITK_EXPORT SamplingRateCalculator +class OTBSampling_EXPORT SamplingRateCalculator : public itk::Object { public: @@ -49,7 +50,7 @@ public: /** typdefs **/ typedef std::map<std::string, unsigned long> ClassCountMapType; typedef ClassCountMapType::const_iterator constItMapType; - typedef struct Triplet + typedef struct OTBSampling_EXPORT Triplet { unsigned long Required; unsigned long Tot; diff --git a/Modules/Learning/Sampling/include/otbSamplingRateCalculatorList.h b/Modules/Learning/Sampling/include/otbSamplingRateCalculatorList.h index 7bc78c188d8e0be07a8da44b22547bb4a8138f15..ad9d86a54edded0d444b6a9b8463f775be5107f7 100644 --- a/Modules/Learning/Sampling/include/otbSamplingRateCalculatorList.h +++ b/Modules/Learning/Sampling/include/otbSamplingRateCalculatorList.h @@ -23,6 +23,7 @@ #include "otbSamplingRateCalculator.h" #include "otbObjectList.h" +#include "OTBSamplingExport.h" namespace otb { @@ -37,7 +38,7 @@ namespace otb * * \ingroup OTBSampling */ -class ITK_EXPORT SamplingRateCalculatorList +class OTBSampling_EXPORT SamplingRateCalculatorList : public ObjectList<SamplingRateCalculator> { public: diff --git a/Modules/Learning/Sampling/otb-module.cmake b/Modules/Learning/Sampling/otb-module.cmake index 4eb65c39829be9317c80589eac55b93f934db40e..2b8ca50f8a4579e401b3d83fb04212f1a63b3cc0 100644 --- a/Modules/Learning/Sampling/otb-module.cmake +++ b/Modules/Learning/Sampling/otb-module.cmake @@ -21,6 +21,7 @@ set(DOCUMENTATION "This module contains OTB sampling framework.") otb_module(OTBSampling + ENABLE_SHARED DEPENDS OTBCommon OTBConversion diff --git a/Modules/Learning/Sampling/src/CMakeLists.txt b/Modules/Learning/Sampling/src/CMakeLists.txt index 8504c688f4827bd93006e71961bb5255b4cfb809..bef592365baf129ebdfd02742ca5d76830d11b06 100644 --- a/Modules/Learning/Sampling/src/CMakeLists.txt +++ b/Modules/Learning/Sampling/src/CMakeLists.txt @@ -21,7 +21,8 @@ set(OTBSampling_SRC otbSamplingRateCalculator.cxx otbSamplingRateCalculatorList.cxx -) + otbSampleAugmentationFilter.cxx + ) add_library(OTBSampling ${OTBSampling_SRC}) target_link_libraries(OTBSampling diff --git a/Modules/Learning/Sampling/src/otbSampleAugmentationFilter.cxx b/Modules/Learning/Sampling/src/otbSampleAugmentationFilter.cxx new file mode 100644 index 0000000000000000000000000000000000000000..5778de0d9324b7a022cab8569c3b25a48260cfc8 --- /dev/null +++ b/Modules/Learning/Sampling/src/otbSampleAugmentationFilter.cxx @@ -0,0 +1,269 @@ +/* + * Copyright (C) 2005-2017 Centre National d'Etudes Spatiales (CNES) + * + * This file is part of Orfeo Toolbox + * + * https://www.orfeo-toolbox.org/ + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "otbSampleAugmentationFilter.h" +#include "stdint.h" //needed for uintptr_t + +namespace otb +{ + +SampleAugmentationFilter +::SampleAugmentationFilter() : m_ClassFieldName{"class"}, m_Layer{0}, m_Label{1}, + m_Strategy{SampleAugmentationFilter::Strategy::Replicate}, + m_NumberOfSamples{100}, m_StdFactor{10.0}, + m_SmoteNeighbors{5}, m_Seed{0} +{ + this->SetNumberOfRequiredInputs(1); + this->SetNumberOfRequiredOutputs(1); + this->ProcessObject::SetNthOutput(0, this->MakeOutput(0) ); +} + + +typename SampleAugmentationFilter::DataObjectPointer +SampleAugmentationFilter +::MakeOutput(DataObjectPointerArraySizeType itkNotUsed(idx)) +{ + return static_cast< DataObjectPointer >(OGRDataSourceType::New().GetPointer()); +} + +const typename SampleAugmentationFilter::OGRDataSourceType * +SampleAugmentationFilter +::GetOutput() +{ + return static_cast< const OGRDataSourceType *>( + this->ProcessObject::GetOutput(0)); +} + +void +SampleAugmentationFilter +::SetInput(const otb::ogr::DataSource* ds) +{ + this->Superclass::SetNthInput(0, const_cast<otb::ogr::DataSource *>(ds)); +} + +const typename SampleAugmentationFilter::OGRDataSourceType * +SampleAugmentationFilter +::GetInput(unsigned int idx) +{ + return static_cast<const OGRDataSourceType *> + (this->itk::ProcessObject::GetInput(idx)); +} + +void +SampleAugmentationFilter +::SetOutputSamples(ogr::DataSource* data) +{ + this->SetNthOutput(0,data); +} + + +void +SampleAugmentationFilter +::GenerateData(void) +{ + + OGRDataSourcePointerType inputDS = dynamic_cast<OGRDataSourceType*>(this->itk::ProcessObject::GetInput(0)); + auto outputDS = static_cast<ogr::DataSource *>(this->itk::ProcessObject::GetOutput(0)); + auto inSamples = this->ExtractSamples(inputDS, m_Layer, + m_ClassFieldName, + m_Label, + m_ExcludedFields); + SampleVectorType newSamples; + switch (m_Strategy) + { + case Strategy::Replicate: + { + sampleAugmentation::ReplicateSamples(inSamples, m_NumberOfSamples, + newSamples); + } + break; + case Strategy::Jitter: + { + sampleAugmentation::JitterSamples(inSamples, m_NumberOfSamples, + newSamples, + m_StdFactor, + m_Seed); + } + break; + case Strategy::Smote: + { + sampleAugmentation::Smote(inSamples, m_NumberOfSamples, + newSamples, + m_SmoteNeighbors, + m_Seed); + } + break; + } + this->SampleToOGRFeatures(inputDS, outputDS, newSamples, m_Layer, + m_ClassFieldName, + m_Label, + m_ExcludedFields); + + + // this->SetNthOutput(0,outputDS); +} + +/** Extracts the samples of a single class from the vector data to a +* vector and excludes some unwanted features. +*/ +SampleAugmentationFilter::SampleVectorType +SampleAugmentationFilter +::ExtractSamples(const ogr::DataSource::Pointer vectors, + size_t layerName, + const std::string& classField, const int label, + const std::vector<std::string>& excludedFields) +{ + ogr::Layer layer = vectors->GetLayer(layerName); + auto featureIt = layer.begin(); + if(featureIt==layer.end()) + { + itkExceptionMacro("Layer " << layerName << " of input sample file is empty.\n"); + } + int cFieldIndex = (*featureIt).ogr().GetFieldIndex( classField.c_str() ); + if( cFieldIndex < 0 ) + { + itkExceptionMacro( "The field name for class label (" << classField + << ") has not been found in the vector file " ); + } + + auto numberOfFields = (*featureIt).ogr().GetFieldCount(); + auto excludedIds = this->GetExcludedFieldsIds(excludedFields, layer); + SampleVectorType samples; + int sampleCount{0}; + while( featureIt!=layer.end() ) + { + // Retrieve all the features for each field in the ogr layer. + if((*featureIt).ogr().GetFieldAsInteger(classField.c_str()) == label) + { + + SampleType mv; + for(auto idx=0; idx<numberOfFields; ++idx) + { + if(excludedIds.find(idx) == excludedIds.cend() && + this->IsNumericField((*featureIt), idx)) + mv.push_back((*featureIt).ogr().GetFieldAsDouble(idx)); + } + samples.push_back(mv); + ++sampleCount; + } + ++featureIt; + } + if(sampleCount==0) + { + itkExceptionMacro("Could not find any samples in layer " << layerName << + " with label " << label << '\n'); + } + return samples; +} + +void +SampleAugmentationFilter +::SampleToOGRFeatures(const ogr::DataSource::Pointer& vectors, + ogr::DataSource* output, + const SampleAugmentationFilter::SampleVectorType& samples, + const size_t layerName, + const std::string& classField, int label, + const std::vector<std::string>& excludedFields) +{ + + auto inputLayer = vectors->GetLayer(layerName); + auto excludedIds = this->GetExcludedFieldsIds(excludedFields, inputLayer); + + OGRSpatialReference * oSRS = nullptr; + if (inputLayer.GetSpatialRef()) + { + oSRS = inputLayer.GetSpatialRef()->Clone(); + } + OGRFeatureDefn &layerDefn = inputLayer.GetLayerDefn(); + + auto outputLayer = output->CreateLayer(inputLayer.GetName(), oSRS, + inputLayer.GetGeomType()); + for (int k=0 ; k < layerDefn.GetFieldCount() ; k++) + { + OGRFieldDefn originDefn(layerDefn.GetFieldDefn(k)); + ogr::FieldDefn fieldDefn(originDefn); + outputLayer.CreateField(fieldDefn); + } + + auto featureCount = outputLayer.GetFeatureCount(false); + auto templateFeature = this->SelectTemplateFeature(inputLayer, classField, label); + for(const auto& sample : samples) + { + ogr::Feature dstFeature(outputLayer.GetLayerDefn()); + dstFeature.SetFrom( templateFeature, TRUE ); + dstFeature.SetFID(++featureCount); + auto sampleFieldCounter = 0; + for (int k=0 ; k < layerDefn.GetFieldCount() ; k++) + { + if(excludedIds.find(k) == excludedIds.cend() && + this->IsNumericField(dstFeature, k)) + { + dstFeature.ogr().SetField(k, sample[sampleFieldCounter++]); + } + } + outputLayer.CreateFeature( dstFeature ); + } +} + +std::set<size_t> +SampleAugmentationFilter +::GetExcludedFieldsIds(const std::vector<std::string>& excludedFields, + const ogr::Layer& inputLayer) +{ + auto feature = *(inputLayer).begin(); + std::set<size_t> excludedIds; + if( excludedFields.size() != 0) + { + for(const auto& fieldName : excludedFields) + { + auto idx = feature.ogr().GetFieldIndex( fieldName.c_str() ); + excludedIds.insert(idx); + } + } + return excludedIds; +} + +bool +SampleAugmentationFilter +::IsNumericField(const ogr::Feature& feature, + const int idx) +{ + OGRFieldType fieldType = feature.ogr().GetFieldDefnRef(idx)->GetType(); + return (fieldType == OFTInteger + || ogr::version_proxy::IsOFTInteger64( fieldType ) + || fieldType == OFTReal); +} + +ogr::Feature +SampleAugmentationFilter +::SelectTemplateFeature(const ogr::Layer& inputLayer, + const std::string& classField, int label) +{ + auto wh = std::find_if(inputLayer.begin(), inputLayer.end(), + [&](auto& featureIt) + { + return featureIt.ogr().GetFieldAsInteger(classField.c_str()) == + label; + }); + return (wh == inputLayer.end()) ? *(inputLayer.begin()) : *wh; + +} +} // end namespace otb + diff --git a/Modules/MPI/MPITiffWriter/include/otbSimpleParallelTiffWriter.h b/Modules/MPI/MPITiffWriter/include/otbSimpleParallelTiffWriter.h index f008e22bbb46c34eb77b2c8fafb464a68f5bb4dd..a7c413064a46a9afdd5f07ae2ca614ebc37b09d2 100644 --- a/Modules/MPI/MPITiffWriter/include/otbSimpleParallelTiffWriter.h +++ b/Modules/MPI/MPITiffWriter/include/otbSimpleParallelTiffWriter.h @@ -31,6 +31,7 @@ #include "itkImageFileWriter.h" #include "itkObjectFactoryBase.h" +#include "itkFastMutexLock.h" #include "itkImageRegionMultidimensionalSplitter.h" #include "otbImageIOFactory.h" @@ -252,6 +253,11 @@ public: itkSetMacro(TiffTiledMode, bool); itkGetMacro(TiffTiledMode, bool); + /** This override doesn't return a const ref on the actual boolean */ + const bool & GetAbortGenerateData() const override; + + void SetAbortGenerateData(bool val) override; + protected: SimpleParallelTiffWriter(); virtual ~SimpleParallelTiffWriter(); @@ -325,6 +331,9 @@ private: bool m_Verbose; bool m_VirtualMode; bool m_TiffTiledMode; + + /** Lock to ensure thread-safety (added for the AbortGenerateData flag) */ + itk::SimpleFastMutexLock m_Lock; }; diff --git a/Modules/MPI/MPITiffWriter/include/otbSimpleParallelTiffWriter.txx b/Modules/MPI/MPITiffWriter/include/otbSimpleParallelTiffWriter.txx index 9a3790c872e8f11dc6f0dc65ef0c565e26794cde..4292ad0f8460c3811e541e25a793711f59caec43 100644 --- a/Modules/MPI/MPITiffWriter/include/otbSimpleParallelTiffWriter.txx +++ b/Modules/MPI/MPITiffWriter/include/otbSimpleParallelTiffWriter.txx @@ -23,6 +23,7 @@ #include "otbSimpleParallelTiffWriter.h" #include "otbStopwatch.h" +#include "otbUtils.h" using std::vector; @@ -729,6 +730,16 @@ SimpleParallelTiffWriter<TInputImage> } } + // abort case + if (this->GetAbortGenerateData()) + { + itk::ProcessAborted e(__FILE__, __LINE__); + e.SetLocation(ITK_LOCATION); + e.SetDescription("Image writing has been aborted"); + throw e; + otb::MPIConfig::Instance()->abort(EXIT_FAILURE); + } + // Clean up close_raster(output_raster); output_raster = NULL; @@ -837,5 +848,27 @@ SimpleParallelTiffWriter<TInputImage> return this->m_FilenameHelper->GetSimpleFileName(); } +template <class TInputImage> +const bool & +SimpleParallelTiffWriter<TInputImage> +::GetAbortGenerateData() const +{ + m_Lock.Lock(); + bool ret = Superclass::GetAbortGenerateData(); + m_Lock.Unlock(); + if (ret) return otb::Utils::TrueConstant; + return otb::Utils::FalseConstant; +} + +template <class TInputImage> +void +SimpleParallelTiffWriter<TInputImage> +::SetAbortGenerateData(bool val) +{ + m_Lock.Lock(); + Superclass::SetAbortGenerateData(val); + m_Lock.Unlock(); +} + } #endif diff --git a/Modules/MPI/MPIVrtWriter/include/otbMPIVrtWriter.h b/Modules/MPI/MPIVrtWriter/include/otbMPIVrtWriter.h index ecec379e6d0cdaab2a6b39817b61ccd1a533c524..1800dc62ae4a170c70f0039906c79f2b2093e40d 100644 --- a/Modules/MPI/MPIVrtWriter/include/otbMPIVrtWriter.h +++ b/Modules/MPI/MPIVrtWriter/include/otbMPIVrtWriter.h @@ -47,232 +47,102 @@ namespace otb { /** - *\brief Write image data to multiple files with MPI processus and add a VRT file. + * \class MPIVrtWriter * - * The image is divided into several pieces. - * Each pieces is distributed to a MPI processus. - * Each MPI processus write their pieces into a separate - * file. - * The master processus writes a VRT file (optional). + * \brief Write each tile of an image into a separate Tiff file and join them in a VRT * - *\param img Image - *\param output Output Filename - *\param availableRAM Available memory for streaming - *\param writeVRTFile Activate the VRT file writing + * \ingroup OTBMPIVrtWriter */ -template <typename TImage> void WriteMPI(TImage *img, const std::string &output, unsigned int availableRAM = 0, bool writeVRTFile=true) +template <typename TImage> +class MPIVrtWriter: public itk::ProcessObject { - typename otb::MPIConfig::Pointer mpiConfig = otb::MPIConfig::Instance(); - - unsigned int myRank = mpiConfig->GetMyRank(); - unsigned int nbProcs = mpiConfig->GetNbProcs(); - - typedef otb::ImageFileWriter<TImage> WriterType; - typedef otb::NumberOfDivisionsTiledStreamingManager<TImage> StreamingManagerType; - typedef itk::RegionOfInterestImageFilter<TImage, TImage> ExtractFilterType; - - // First, update infomration from image to write - img->UpdateOutputInformation(); - - // Configure streaming manager - typename StreamingManagerType::Pointer streamingManager = StreamingManagerType::New(); - streamingManager->SetNumberOfDivisions(nbProcs); - streamingManager->PrepareStreaming(img,img->GetLargestPossibleRegion()); - unsigned int numberOfSplits = streamingManager->GetNumberOfSplits(); - - // This vector will hold all regions to write for current rank - std::vector<typename TImage::RegionType> regionsToWrite; - - // Handle both cases when there are much more (resp. less) region to - // write than NbProcs - if(myRank < numberOfSplits) - { - unsigned int splitIdx = myRank; - while(splitIdx < numberOfSplits) - { - typename TImage::RegionType currentRegion = streamingManager->GetSplit(splitIdx); - regionsToWrite.push_back(currentRegion); - splitIdx+=nbProcs; - } - } - - // Output prefix - std::string extension = itksys::SystemTools::GetFilenameExtension(output); - if (extension != ".vrt") - { - - // TODO: Maybe remove this - if (extension == "") - { - // Missing extension - mpiConfig->logInfo("Filename has no extension. Adding <.vrt> extension."); - } - else - { - // Bad extension - mpiConfig->logError("Filename must have .vrt extension!"); - mpiConfig->abort(EXIT_FAILURE); - } - } - std::vector<std::string> joins; - joins.push_back(itksys::SystemTools::GetFilenamePath(output).append("/")); - joins.push_back(itksys::SystemTools::GetFilenameWithoutExtension(output)); - std::string prefix = itksys::SystemTools::JoinPath(joins); - +public: + /** Standard class typedefs. */ + typedef MPIVrtWriter Self; + typedef itk::ProcessObject Superclass; + typedef itk::SmartPointer<Self> Pointer; + typedef itk::SmartPointer<const Self> ConstPointer; - // Data type - std::string dataTypeStr = "Float32"; - GDALImageIO::Pointer gdalImageIO; + typedef TImage InputImageType; - // Now write all the regions - for(typename std::vector<typename TImage::RegionType>::const_iterator it = regionsToWrite.begin(); - it!=regionsToWrite.end();++it) - { - typename ExtractFilterType::Pointer extractFilter = ExtractFilterType::New(); - extractFilter->SetInput(img); - extractFilter->SetRegionOfInterest(*it); - // Writer - // Output Filename - std::stringstream ss; - ss<<prefix<<"_"<<it->GetIndex()[0]<<"_"<<it->GetIndex()[1]<<"_"<<it->GetSize()[0]<<"_"<<it->GetSize()[1]<<".tif"; - typename WriterType::Pointer writer = WriterType::New(); - writer->SetFileName(ss.str()); - writer->SetInput(extractFilter->GetOutput()); - // Datatype - gdalImageIO = dynamic_cast<GDALImageIO *>(writer->GetImageIO()); - if(gdalImageIO.IsNotNull()) - { - dataTypeStr = gdalImageIO->GetGdalPixelTypeAsString(); - } + /** Method for creation through the object factory. */ + itkNewMacro(Self); - if (!availableRAM) - { - writer->SetNumberOfDivisionsTiledStreaming(0); - } - else - { - writer->SetAutomaticAdaptativeStreaming(availableRAM); - } + /** Run-time type information (and related methods). */ + itkTypeMacro(MPIVrtWriter, itk::ProcessObject); - // Pipeline execution - try - { - writer->Update(); - } - catch (itk::ExceptionObject& err) - { - std::stringstream message; - message << "ExceptionObject caught: " << err << std::endl; - mpiConfig->logError(message.str()); - mpiConfig->abort(EXIT_FAILURE); - } - } + using Superclass::SetInput; + virtual void SetInput(const InputImageType *input); - // MPI process synchronization - mpiConfig->barrier(); + /** Get writer only input */ + const InputImageType* GetInput(); - // Write VRT file - try - { - if (writeVRTFile && (myRank == 0)) - { - // VRT Filename - std::stringstream vrtfOut; - vrtfOut<< prefix << ".vrt"; + /** Does the real work. */ + virtual void Update() override; - // Data type - GDALDataType dataType; - dataType = GDALGetDataTypeByName(dataTypeStr.c_str()); + /** SimpleParallelTiffWriter Methods */ + virtual void SetFileName(const char* extendedFileName); + virtual void SetFileName(std::string extendedFileName); + virtual const char* GetFileName () const; - int imageSizeY = img->GetLargestPossibleRegion().GetSize()[1]; - int imageSizeX = img->GetLargestPossibleRegion().GetSize()[0]; - const unsigned int nbBands = img->GetNumberOfComponentsPerPixel(); + /** Specify the region to write. If left NULL, then the whole image + * is written. */ + void SetIORegion(const itk::ImageIORegion& region); + itkGetConstReferenceMacro(IORegion, itk::ImageIORegion); - // Get VRT driver - GDALAllRegister(); - GDALDriver *driver = GetGDALDriverManager()->GetDriverByName("VRT"); - if (driver == NULL) { - mpiConfig->logError("Error opening VRT driver."); - mpiConfig->abort(EXIT_FAILURE); - } + itkSetMacro(WriteVRT, bool); + itkGetMacro(WriteVRT, bool); - // Create output raster - GDALDataset *VRTOutput = driver->Create(vrtfOut.str().c_str(), - imageSizeX, - imageSizeY, - 0, - dataType, - NULL); // No options - if (VRTOutput == NULL) { - mpiConfig->logError("driver->Create call failed.\n"); - mpiConfig->abort(EXIT_FAILURE); - } + itkSetMacro(AvailableRAM, unsigned int); + itkGetMacro(AvailableRAM, unsigned int); - // Set GeoTransform - double gt[6]; - gt[0] = img->GetOrigin()[0] - 0.5*img->GetSignedSpacing()[0]; - gt[1] = img->GetSignedSpacing()[0]; - gt[2] = 0.0; - gt[3] = img->GetOrigin()[1] - 0.5*img->GetSignedSpacing()[1]; - gt[4] = 0.0; - gt[5] = img->GetSignedSpacing()[1]; - VRTOutput->SetGeoTransform(gt); +protected: + MPIVrtWriter(); + virtual ~MPIVrtWriter(); + void PrintSelf(std::ostream& os, itk::Indent indent) const override; - // Set projection - OGRSpatialReference out_sr; - char *wkt = NULL; - out_sr.SetFromUserInput(img->GetProjectionRef().c_str()); - out_sr.exportToWkt(&wkt); - VRTOutput->SetProjection(wkt); +private: + MPIVrtWriter(const MPIVrtWriter &) = delete; + void operator =(const MPIVrtWriter&) = delete; - for(unsigned int band = 1; band<=nbBands;++band) - { - VRTOutput->AddBand(dataType, NULL); + unsigned int m_AvailableRAM; - typename TImage::RegionType currentRegion; - for(unsigned int id=0; id < numberOfSplits; ++id) - { - currentRegion = streamingManager->GetSplit(id); - int tileSizeX = currentRegion.GetSize()[0]; - int tileSizeY = currentRegion.GetSize()[1]; - int tileIndexX = currentRegion.GetIndex()[0]; - int tileIndexY = currentRegion.GetIndex()[1]; - std::stringstream tileFileName; - tileFileName <<prefix<<"_"<<tileIndexX<<"_"<<tileIndexY<<"_"<<tileSizeX<<"_"<<tileSizeY<<".tif"; - std::cout<<tileFileName.str()<<std::endl; + itk::ImageIORegion m_IORegion; - GDALDataset *dataset = (GDALDataset*) GDALOpen(tileFileName.str().c_str(), GA_ReadOnly); + std::string m_Filename; - VRTSourcedRasterBand *VRTBand = dynamic_cast<VRTSourcedRasterBand*> (VRTOutput->GetRasterBand(band)); - VRTBand->AddSimpleSource(dataset->GetRasterBand(band), - 0, //xOffSrc - 0, //yOffSrc - tileSizeX, //xSizeSrc - tileSizeY, //ySizeSrc - tileIndexX, //xOffDest - tileIndexY, //yOffDest - tileSizeX, //xSizeDest - tileSizeY, //ySizeDest - "near", - VRT_NODATA_UNSET); - } + bool m_WriteVRT; - } - - // Close - CPLFree(wkt); - GDALClose(VRTOutput); - } - } - catch (itk::ExceptionObject& err) - { - std::stringstream message; - message << "ExceptionObject caught: " << err << std::endl; - mpiConfig->logError(message.str()); - mpiConfig->abort(EXIT_FAILURE); - } +}; +/** + *\brief Write image data to multiple files with MPI processus and add a VRT file. + * + * The image is divided into several pieces. + * Each pieces is distributed to a MPI processus. + * Each MPI processus write their pieces into a separate + * file. + * The master processus writes a VRT file (optional). + * + *\param img Image + *\param output Output Filename + *\param availableRAM Available memory for streaming + *\param writeVRTFile Activate the VRT file writing + */ +template <typename TImage> void WriteMPI(TImage *img, const std::string &output, unsigned int availableRAM = 0, bool writeVRTFile=true) +{ + typename MPIVrtWriter<TImage>::Pointer writer = MPIVrtWriter<TImage>::New(); + writer->SetInput(img); + writer->SetFileName(output); + writer->SetAvailableRAM(availableRAM); + writer->SetWriteVRT(writeVRTFile); + writer->Update(); } } // End namespace otb + +#ifndef OTB_MANUAL_INSTANTIATION +#include "otbMPIVrtWriter.txx" +#endif + #endif //__otbMPIVrtWriter_h diff --git a/Modules/MPI/MPIVrtWriter/include/otbMPIVrtWriter.txx b/Modules/MPI/MPIVrtWriter/include/otbMPIVrtWriter.txx new file mode 100644 index 0000000000000000000000000000000000000000..b57518ed73d613b37e33b86c59ba732ed0e44cc2 --- /dev/null +++ b/Modules/MPI/MPIVrtWriter/include/otbMPIVrtWriter.txx @@ -0,0 +1,327 @@ +/* + * Copyright (C) 2005-2017 Centre National d'Etudes Spatiales (CNES) + * + * This file is part of Orfeo Toolbox + * + * https://www.orfeo-toolbox.org/ + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef otbMPIVrtWriter_txx +#define otbMPIVrtWriter_txx + +#include "otbMPIVrtWriter.h" +#include "otbMacro.h" + +namespace otb +{ + +template <typename TImage> +MPIVrtWriter<TImage>::MPIVrtWriter() + : m_AvailableRAM(0) + , m_IORegion() + , m_Filename("") + , m_WriteVRT(true) +{ +} + +template <typename TImage> +MPIVrtWriter<TImage>::~MPIVrtWriter() +{ +} + +template <typename TImage> +void +MPIVrtWriter<TImage>::SetInput(const InputImageType *input) +{ + this->ProcessObject::SetNthInput(0,const_cast<InputImageType*>(input)); +} + +template <typename TImage> +const TImage* +MPIVrtWriter<TImage>::GetInput() +{ + if (this->GetNumberOfInputs() < 1) + { + return 0; + } + return static_cast<const InputImageType*>(this->ProcessObject::GetInput(0)); +} + +template <typename TImage> +void +MPIVrtWriter<TImage>::SetFileName(const char* extendedFileName) +{ + if (m_Filename.compare(extendedFileName) != 0 ) + { + m_Filename = std::string(extendedFileName); + this->Modified(); + } +} + +template <typename TImage> +void +MPIVrtWriter<TImage>::SetFileName(std::string extendedFileName) +{ + this->SetFileName(extendedFileName.c_str()); +} + +template <typename TImage> +const char* +MPIVrtWriter<TImage>::GetFileName () const +{ + return m_Filename.c_str(); +} + +template <typename TImage> +void +MPIVrtWriter<TImage>::SetIORegion(const itk::ImageIORegion& region) +{ + if (m_IORegion != region) + { + m_IORegion = region; + this->Modified(); + } +} + +template <typename TImage> +void +MPIVrtWriter<TImage>::PrintSelf(std::ostream& os, itk::Indent indent) const +{ + Superclass::PrintSelf(os, indent); + os << indent << "File Name: "<< m_Filename << std::endl; + os << indent << "Available RAM: "<< m_AvailableRAM << std::endl; + os << indent << "Write VRT: "<< m_WriteVRT << std::endl; +} + +template <typename TImage> +void +MPIVrtWriter<TImage>::Update() +{ + typename otb::MPIConfig::Pointer mpiConfig = otb::MPIConfig::Instance(); + + unsigned int myRank = mpiConfig->GetMyRank(); + unsigned int nbProcs = mpiConfig->GetNbProcs(); + + typedef otb::ImageFileWriter<TImage> WriterType; + typedef otb::NumberOfDivisionsTiledStreamingManager<TImage> StreamingManagerType; + typedef itk::RegionOfInterestImageFilter<TImage, TImage> ExtractFilterType; + + // First, update infomration from image to write + UpdateOutputInformation(); + InputImageType* img = const_cast<InputImageType*>(GetInput()); + std::string output = GetFileName(); + + // Configure streaming manager + typename StreamingManagerType::Pointer streamingManager = StreamingManagerType::New(); + streamingManager->SetNumberOfDivisions(nbProcs); + streamingManager->PrepareStreaming(img,img->GetLargestPossibleRegion()); + unsigned int numberOfSplits = streamingManager->GetNumberOfSplits(); + + // This vector will hold all regions to write for current rank + std::vector<typename TImage::RegionType> regionsToWrite; + + // Handle both cases when there are much more (resp. less) region to + // write than NbProcs + if(myRank < numberOfSplits) + { + unsigned int splitIdx = myRank; + while(splitIdx < numberOfSplits) + { + typename TImage::RegionType currentRegion = streamingManager->GetSplit(splitIdx); + regionsToWrite.push_back(currentRegion); + splitIdx+=nbProcs; + } + } + + // Output prefix + std::string extension = itksys::SystemTools::GetFilenameExtension(output); + if (extension != ".vrt") + { + + // TODO: Maybe remove this + if (extension == "") + { + // Missing extension + mpiConfig->logInfo("Filename has no extension. Adding <.vrt> extension."); + } + else + { + // Bad extension + mpiConfig->logError("Filename must have .vrt extension!"); + mpiConfig->abort(EXIT_FAILURE); + } + } + std::vector<std::string> joins; + itksys::SystemTools::SplitPath(output, joins); + joins.back() = itksys::SystemTools::GetFilenameWithoutExtension(output); + std::string prefix = itksys::SystemTools::JoinPath(joins); + + // Data type + std::string dataTypeStr = "Float32"; + GDALImageIO::Pointer gdalImageIO; + + // Now write all the regions + for(typename std::vector<typename TImage::RegionType>::const_iterator it = regionsToWrite.begin(); + it!=regionsToWrite.end();++it) + { + typename ExtractFilterType::Pointer extractFilter = ExtractFilterType::New(); + extractFilter->SetInput(img); + extractFilter->SetRegionOfInterest(*it); + // Writer + // Output Filename + std::stringstream ss; + ss<<prefix<<"_"<<it->GetIndex()[0]<<"_"<<it->GetIndex()[1]<<"_"<<it->GetSize()[0]<<"_"<<it->GetSize()[1]<<".tif"; + typename WriterType::Pointer writer = WriterType::New(); + writer->SetFileName(ss.str()); + writer->SetInput(extractFilter->GetOutput()); + // Datatype + gdalImageIO = dynamic_cast<GDALImageIO *>(writer->GetImageIO()); + if(gdalImageIO.IsNotNull()) + { + dataTypeStr = gdalImageIO->GetGdalPixelTypeAsString(); + } + + if (!m_AvailableRAM) + { + writer->SetNumberOfDivisionsTiledStreaming(0); + } + else + { + writer->SetAutomaticAdaptativeStreaming(m_AvailableRAM); + } + + // Pipeline execution + try + { + writer->Update(); + } + catch (itk::ExceptionObject& err) + { + std::stringstream message; + message << "ExceptionObject caught: " << err << std::endl; + mpiConfig->logError(message.str()); + mpiConfig->abort(EXIT_FAILURE); + } + } + + // MPI process synchronization + mpiConfig->barrier(); + + // Write VRT file + try + { + if (m_WriteVRT && (myRank == 0)) + { + // VRT Filename + std::stringstream vrtfOut; + vrtfOut<< prefix << ".vrt"; + + // Data type + GDALDataType dataType; + dataType = GDALGetDataTypeByName(dataTypeStr.c_str()); + + int imageSizeY = img->GetLargestPossibleRegion().GetSize()[1]; + int imageSizeX = img->GetLargestPossibleRegion().GetSize()[0]; + const unsigned int nbBands = img->GetNumberOfComponentsPerPixel(); + + // Get VRT driver + GDALAllRegister(); + GDALDriver *driver = GetGDALDriverManager()->GetDriverByName("VRT"); + if (driver == NULL) { + mpiConfig->logError("Error opening VRT driver."); + mpiConfig->abort(EXIT_FAILURE); + } + + // Create output raster + GDALDataset *VRTOutput = driver->Create(vrtfOut.str().c_str(), + imageSizeX, + imageSizeY, + 0, + dataType, + NULL); // No options + if (VRTOutput == NULL) { + mpiConfig->logError("driver->Create call failed.\n"); + mpiConfig->abort(EXIT_FAILURE); + } + + // Set GeoTransform + double gt[6]; + gt[0] = img->GetOrigin()[0] - 0.5*img->GetSignedSpacing()[0]; + gt[1] = img->GetSignedSpacing()[0]; + gt[2] = 0.0; + gt[3] = img->GetOrigin()[1] - 0.5*img->GetSignedSpacing()[1]; + gt[4] = 0.0; + gt[5] = img->GetSignedSpacing()[1]; + VRTOutput->SetGeoTransform(gt); + + // Set projection + OGRSpatialReference out_sr; + char *wkt = NULL; + out_sr.SetFromUserInput(img->GetProjectionRef().c_str()); + out_sr.exportToWkt(&wkt); + VRTOutput->SetProjection(wkt); + + for(unsigned int band = 1; band<=nbBands;++band) + { + VRTOutput->AddBand(dataType, NULL); + + typename TImage::RegionType currentRegion; + for(unsigned int id=0; id < numberOfSplits; ++id) + { + currentRegion = streamingManager->GetSplit(id); + int tileSizeX = currentRegion.GetSize()[0]; + int tileSizeY = currentRegion.GetSize()[1]; + int tileIndexX = currentRegion.GetIndex()[0]; + int tileIndexY = currentRegion.GetIndex()[1]; + std::stringstream tileFileName; + tileFileName <<prefix<<"_"<<tileIndexX<<"_"<<tileIndexY<<"_"<<tileSizeX<<"_"<<tileSizeY<<".tif"; + otbDebugMacro(<<tileFileName.str()); + + GDALDataset *dataset = (GDALDataset*) GDALOpen(tileFileName.str().c_str(), GA_ReadOnly); + + VRTSourcedRasterBand *VRTBand = dynamic_cast<VRTSourcedRasterBand*> (VRTOutput->GetRasterBand(band)); + VRTBand->AddSimpleSource(dataset->GetRasterBand(band), + 0, //xOffSrc + 0, //yOffSrc + tileSizeX, //xSizeSrc + tileSizeY, //ySizeSrc + tileIndexX, //xOffDest + tileIndexY, //yOffDest + tileSizeX, //xSizeDest + tileSizeY, //ySizeDest + "near", + VRT_NODATA_UNSET); + } + + } + + // Close + CPLFree(wkt); + GDALClose(VRTOutput); + } + } + catch (itk::ExceptionObject& err) + { + std::stringstream message; + message << "ExceptionObject caught: " << err << std::endl; + mpiConfig->logError(message.str()); + mpiConfig->abort(EXIT_FAILURE); + } +} + + +} // end of namespace otb + +#endif diff --git a/Modules/MPI/MPIVrtWriter/otb-module.cmake b/Modules/MPI/MPIVrtWriter/otb-module.cmake index 0e95d97f7cbe02b27ba37be548a0106b3d70ea9e..f98ab957d207a9bf02f6f13c27002ad054582897 100644 --- a/Modules/MPI/MPIVrtWriter/otb-module.cmake +++ b/Modules/MPI/MPIVrtWriter/otb-module.cmake @@ -22,8 +22,9 @@ set(DOCUMENTATION "Provides a template function for MPI writing to a VRT file") otb_module(OTBMPIVrtWriter DEPENDS - OTBMPIConfig - OTBPanSharpening + OTBCommon + OTBMPIConfig +# OTBPanSharpening OTBProjection OTBInterpolation OTBTestKernel diff --git a/Modules/Visualization/Monteverdi/src/mvdMainWindow.cxx b/Modules/Visualization/Monteverdi/src/mvdMainWindow.cxx index 6b1d92cf582d251cb2a3c6a7936f8842d73ee0a4..9420452e321f19bb13b9ba6521c5b79ae9415280 100644 --- a/Modules/Visualization/Monteverdi/src/mvdMainWindow.cxx +++ b/Modules/Visualization/Monteverdi/src/mvdMainWindow.cxx @@ -1431,11 +1431,11 @@ MainWindow if( !( *it )->IsClosable() ) { - assert( !( *it )->GetApplication().IsNull() ); + assert( ( *it )->GetModel()->GetApplication() ); // qDebug() << "OTB-application:" << ( *it )->GetApplication()->GetDocName(); - names.push_back( ( *it )->GetApplication()->GetDocName() ); + names.push_back( ( *it )->GetModel()->GetApplication()->GetDocName() ); } } diff --git a/Modules/Visualization/MonteverdiGui/include/mvdQtWidgetView.h b/Modules/Visualization/MonteverdiGui/include/mvdQtWidgetView.h index c55664e2dcbf8871e6510a930f564b1372afd8e9..08ead75886afef2b4b57f26e5359f1df37895dda 100644 --- a/Modules/Visualization/MonteverdiGui/include/mvdQtWidgetView.h +++ b/Modules/Visualization/MonteverdiGui/include/mvdQtWidgetView.h @@ -37,16 +37,10 @@ #include <QtGui> #include <QShortcut> -// -// System includes (sorted by alphabetic order) - -// -// ITK includes (sorted by alphabetic order) - // // OTB includes (sorted by alphabetic order) #ifndef Q_MOC_RUN // See: https://bugreports.qt-project.org/browse/QTBUG-22829 //tag=QT4-boost-compatibility -#include "otbWrapperApplication.h" +#include "otbWrapperQtWidgetView.h" #include "otbWrapperQtWidgetModel.h" #endif //tag=QT4-boost-compatibility #include "OTBMonteverdiGUIExport.h" @@ -57,18 +51,8 @@ #include "mvdTypes.h" #endif //tag=QT4-boost-compatibility -/*****************************************************************************/ -/* PRE-DECLARATION SECTION */ - -// -// External classes pre-declaration. -namespace -{ -} - namespace mvd { - namespace Wrapper { @@ -84,16 +68,12 @@ namespace Wrapper */ class OTBMonteverdiGUI_EXPORT QtWidgetView : - public QWidget + public otb::Wrapper::QtWidgetView { /*-[ QOBJECT SECTION ]-----------------------------------------------------*/ Q_OBJECT - Q_PROPERTY( bool isClosable - READ IsClosable - WRITE SetClosable ); - /*-[ PUBLIC SECTION ]------------------------------------------------------*/ // @@ -112,29 +92,6 @@ public: /** \brief Destructor. */ ~QtWidgetView() override; - /** \brief Gui Creation. */ - void CreateGui(); - - /** \brief Model Accessor */ - inline otb::Wrapper::QtWidgetModel* GetModel() - { - return m_Model; - } - - /** - * \return The OTB-application pointer of this view. - */ - otb::Wrapper::Application::ConstPointer GetApplication() const - { - return otb::ConstCast< otb::Wrapper::Application >( - m_Application - ); - } - - /** - */ - inline bool IsClosable() const; - /*-[ PUBLIC SLOTS SECTION ]------------------------------------------------*/ // @@ -146,12 +103,9 @@ public slots: // // Signals. signals: - void QuitSignal(); void OTBApplicationOutputImageChanged( const QString &, const QString &); - void ExecuteAndWriteOutput(); - void ExecutionDone( int nbOutputs ); /*-[ PROTECTED SECTION ]---------------------------------------------------*/ @@ -160,14 +114,16 @@ signals: // Protected methods. protected: - // - // QWidget overloads. + QWidget* CreateInputWidgets() override; - void closeEvent( QCloseEvent * event ) override; +protected slots: -// -// Protected attributes. -protected: + /** extend the behaviour of base class OnExecButtonClicked */ + void OnExecButtonClicked(); + + /** modify the behaviour of base class OnExceptionRaised + */ + void OnExceptionRaised( QString what ); /*-[ PRIVATE SECTION ]-----------------------------------------------------*/ @@ -178,12 +134,6 @@ private: QtWidgetView(const QtWidgetView&); //purposely not implemented void operator=(const QtWidgetView&); //purposely not implemented - QWidget* CreateFooter(); - - QWidget* CreateInputWidgets(); - - QWidget* CreateDoc(); - /** */ void SetupParameterWidgets( QWidget* widget ); @@ -192,21 +142,6 @@ private: */ void SetupFileSelectionWidget( QWidget * ); -// -// Private attributes. - -private: - - otb::Wrapper::Application::Pointer m_Application; - otb::Wrapper::QtWidgetModel* m_Model; - - QPushButton* m_ExecButton; - QPushButton* m_QuitButton; - QShortcut* m_QuitShortcut; - QLabel* m_Message; - - bool m_IsClosable : 1; - /*-[ PRIVATE SLOTS SECTION ]-----------------------------------------------*/ // @@ -218,30 +153,6 @@ private slots: // image filename{s} set by the user in this OTB application (if any). void OnApplicationExecutionDone( int ); - void UpdateMessageAfterApplicationReady(bool val); - - void UpdateMessageAfterExecution(int status); - - /** - */ - void OnExecButtonClicked(); - - /** - */ - inline void OnProgressReportBegin(); - - /** - */ - inline void OnProgressReportEnd( int status ); - - /** - */ - void OnExceptionRaised( QString what ); - - /** - */ - inline void SetClosable( bool ); - /** */ inline void OnFileSelectionWidgetAdded0( QWidget * ); @@ -264,47 +175,6 @@ namespace mvd namespace Wrapper { -/*****************************************************************************/ -inline -bool -QtWidgetView -::IsClosable() const -{ - return m_IsClosable; -} - -/*****************************************************************************/ -inline -void -QtWidgetView -::SetClosable( bool enabled ) -{ - m_IsClosable = enabled; - - setEnabled( true ); - - if( m_QuitButton!=NULL ) - m_QuitButton->setEnabled( m_IsClosable ); -} - -/*******************************************************************************/ -inline -void -QtWidgetView -::OnProgressReportBegin() -{ - SetClosable( false ); -} - -/*******************************************************************************/ -inline -void -QtWidgetView -::OnProgressReportEnd( int ) -{ - SetClosable( true ); -} - /*******************************************************************************/ inline void diff --git a/Modules/Visualization/MonteverdiGui/src/mvdApplicationLauncher.cxx b/Modules/Visualization/MonteverdiGui/src/mvdApplicationLauncher.cxx index 7d571ff73bccdfba11819ccd01eafeb6e258439d..b3a28a432f839bb8e67921f440449a9ff9bee71e 100644 --- a/Modules/Visualization/MonteverdiGui/src/mvdApplicationLauncher.cxx +++ b/Modules/Visualization/MonteverdiGui/src/mvdApplicationLauncher.cxx @@ -217,13 +217,13 @@ ApplicationLauncher ApplicationLauncher::NewOtbApplicationWidget( appName, isStandalone ); assert( appWidget!=NULL ); - assert( appWidget->GetApplication() ); + assert( appWidget->GetModel()->GetApplication() ); QMainWindow * mainWindow = new QMainWindow( p, flags ); mainWindow->setWindowTitle( QString( "%1 (OTB-" OTB_VERSION_STRING ")" ) - .arg( appWidget->GetApplication()->GetDocName() ) + .arg( appWidget->GetModel()->GetApplication()->GetDocName() ) ); mainWindow->setWindowIcon( QIcon( ":/otb_small.png" ) ); @@ -251,11 +251,11 @@ ApplicationLauncher ); assert( appWidget!=NULL ); - assert( appWidget->GetApplication() ); + assert( appWidget->GetModel()->GetApplication() ); appWidget->setWindowTitle( QString( "%1 (OTB-" OTB_VERSION_STRING ")" ) - .arg( appWidget->GetApplication()->GetDocName() ) + .arg( appWidget->GetModel()->GetApplication()->GetDocName() ) ); appWidget->setWindowIcon( QIcon( ":/icons/process" ) ); diff --git a/Modules/Visualization/MonteverdiGui/src/mvdQtWidgetView.cxx b/Modules/Visualization/MonteverdiGui/src/mvdQtWidgetView.cxx index 3c33015a5bde59b586340e7fc5155ed3166c4c84..252c6043797f53d43bfa40cbf5c8dde1abc3421c 100644 --- a/Modules/Visualization/MonteverdiGui/src/mvdQtWidgetView.cxx +++ b/Modules/Visualization/MonteverdiGui/src/mvdQtWidgetView.cxx @@ -23,28 +23,16 @@ /*****************************************************************************/ /* INCLUDE SECTION */ -// -// Qt includes (sorted by alphabetic order) -//// Must be included before system/custom includes. - // // System includes (sorted by alphabetic order) #include <functional> -// -// ITK includes (sorted by alphabetic order) - // // OTB includes (sorted by alphabetic order) #include "otbWrapperApplicationHtmlDocGenerator.h" -// #include "otbWrapperComplexOutputImageParameter.h" #include "otbWrapperOutputFilenameParameter.h" -// #include "otbWrapperOutputImageParameter.h" #include "otbWrapperOutputVectorDataParameter.h" -// #include "otbWrapperQtWidgetOutputImageParameter.h" -// #include "otbWrapperQtWidgetProgressReport.h" #include "otbWrapperQtWidgetSimpleProgressReport.h" -// #include "otbWrapperTypes.h" // // Monteverdi includes (sorted by alphabetic order) @@ -140,208 +128,43 @@ QtWidgetView ::QtWidgetView( const otb::Wrapper::Application::Pointer & otbApp, QWidget* p, Qt::WindowFlags flags ) : - QWidget( p, flags ), - m_Application( otbApp ), - m_Model( NULL ), - m_ExecButton( NULL ), - m_QuitButton( NULL ), - m_Message( NULL ), - m_IsClosable( true ) + otb::Wrapper::QtWidgetView( otbApp, p, flags ) { setObjectName( QtWidgetView::OBJECT_NAME ); - m_Model = new otb::Wrapper::QtWidgetModel( otbApp ); - m_QuitShortcut = new QShortcut(QKeySequence("Ctrl+Q"), this); - - QObject::connect( - m_Model, SIGNAL( SetProgressReportBegin() ), - this, SLOT( OnProgressReportBegin() ) - ); - - QObject::connect( - m_Model, SIGNAL( SetProgressReportDone( int ) ), - this, SLOT( OnProgressReportEnd( int ) ) - ); - - QObject::connect( - m_Model, SIGNAL( ExceptionRaised( QString ) ), - this, SLOT( OnExceptionRaised( QString ) ) - ); -} - -/*******************************************************************************/ -QtWidgetView -::~QtWidgetView() -{ - // m_Application is smart-pointed and will be automatically deleted. - - delete m_Model; - m_Model = NULL; -} - -/*******************************************************************************/ -void -QtWidgetView -::CreateGui() -{ - // Create a VBoxLayout with the header, the input widgets, and the footer - QVBoxLayout *mainLayout = new QVBoxLayout(); - QTabWidget *tab = new QTabWidget(); - tab->addTab(CreateInputWidgets(), tr("Parameters")); - - //otb::Wrapper::QtWidgetProgressReport* prog = new otb::Wrapper::QtWidgetProgressReport(m_Model); - //prog->SetApplication(m_Application); - //tab->addTab(prog, "Progress"); - tab->addTab(CreateDoc(), tr("Documentation")); - mainLayout->addWidget(tab); - - QTextEdit *log = new QTextEdit(); - connect( m_Model->GetLogOutput(), SIGNAL(NewContentLog(QString)), log, SLOT(append(QString) ) ); - tab->addTab(log, tr("Logs")); - - m_Message = new QLabel("<center><font color=\"#FF0000\">"+tr("Select parameters")+"</font></center>"); - connect( - m_Model, - SIGNAL( SetApplicationReady( bool ) ), - this, SLOT( UpdateMessageAfterApplicationReady( bool ) ) - ); - connect( - m_Model, - SIGNAL(SetProgressReportDone(int)), - this, SLOT(UpdateMessageAfterExecution(int)) ); - mainLayout->addWidget(m_Message); - - otb::Wrapper::QtWidgetSimpleProgressReport* progressReport = - new otb::Wrapper::QtWidgetSimpleProgressReport(m_Model); - progressReport->SetApplication(m_Application); - - QWidget* footer = CreateFooter(); - - QHBoxLayout *footLayout = new QHBoxLayout; - footLayout->addWidget(progressReport); - footLayout->addWidget(footer); - mainLayout->addLayout(footLayout); - - footLayout->setAlignment(footer, Qt::AlignBottom); - - QGroupBox *mainGroup = new QGroupBox(); - mainGroup->setLayout(mainLayout); - - QVBoxLayout *finalLayout = new QVBoxLayout(); - finalLayout->addWidget(mainGroup); - - // Make the final layout to the widget - this->setLayout(finalLayout); -} - -/*******************************************************************************/ -QWidget* -QtWidgetView -::CreateInputWidgets() -{ - QScrollArea *scrollArea = new QScrollArea; - - QWidget * widget = - otb::Wrapper::QtWidgetParameterFactory::CreateQtWidget( - m_Model->GetApplication()->GetParameterList(), - m_Model - ); - - scrollArea->setWidget( widget ); - scrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded); - scrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); - scrollArea->setWidgetResizable(true); + m_IconPathDone = std::string("<img src=\":/icons/done\" width=\"16\" height=\"16\" />"); + m_IconPathFailed = std::string("<img src=\":/icons/failed\" width=\"16\" height=\"16\" />"); // // need to be connected to the end of a process QObject::connect( - m_Model, + GetModel(), SIGNAL( SetProgressReportDone( int ) ), // to: this, SLOT ( OnApplicationExecutionDone( int ) ) ); - - SetupParameterWidgets( widget ); - - return scrollArea; } /*******************************************************************************/ -QWidget* QtWidgetView -::CreateFooter() +::~QtWidgetView() { - // an HLayout with two buttons : Execute and Quit - QGroupBox *footerGroup = new QGroupBox; - QHBoxLayout *footerLayout = new QHBoxLayout; - - footerGroup->setFixedHeight(40); - footerGroup->setContentsMargins(0, 0, 0, 0); - footerLayout->setContentsMargins(5, 5, 5, 5); - - m_ExecButton = new QPushButton(footerGroup); - m_ExecButton->setDefault(true); - m_ExecButton->setEnabled(false); - m_ExecButton->setText(QObject::tr("Execute")); - connect( - m_Model, SIGNAL( SetApplicationReady( bool ) ), - m_ExecButton, SLOT( setEnabled( bool ) ) - ); - - QObject::connect( - m_ExecButton, SIGNAL( clicked() ), - // to: - this, SLOT( OnExecButtonClicked() ) - ); - QObject::connect( - this, SIGNAL( ExecuteAndWriteOutput() ), - // to: - m_Model, SLOT( ExecuteAndWriteOutputSlot() ) - ); - - m_QuitButton = new QPushButton(footerGroup); - m_QuitButton->setText(QObject::tr("Quit")); - connect( - m_QuitButton, - SIGNAL( clicked() ), - // to: - this, - SLOT( close() ) - ); - - // Add Ctrl-Q shortcut to quit - connect( m_QuitShortcut, SIGNAL(activated()), this, SLOT(close()) ); - - - // Put the buttons on the right - footerLayout->addStretch(); - footerLayout->addWidget(m_ExecButton); - footerLayout->addWidget(m_QuitButton); - - footerGroup->setLayout(footerLayout); - - return footerGroup; } /*******************************************************************************/ QWidget* QtWidgetView -::CreateDoc() +::CreateInputWidgets() { - QTextEdit *text = new QTextEdit; - text->setReadOnly(true); - - QTextDocument * doc = new QTextDocument(); + QWidget * widget = otb::Wrapper::QtWidgetView::CreateInputWidgets(); - std::string docContain; - otb::Wrapper::ApplicationHtmlDocGenerator::GenerateDoc( m_Application, docContain); + otb::Wrapper::QtWidgetParameterBase *paramWidget = + widget->findChild<otb::Wrapper::QtWidgetParameterBase*>(); - doc->setHtml(docContain.c_str()); + SetupParameterWidgets(paramWidget); - text->setDocument(doc); - - return text; + return widget; } /*******************************************************************************/ @@ -369,11 +192,11 @@ QtWidgetView SetupWidget( widget, - OutputImageInitializer( m_Application->GetName() ) + OutputImageInitializer( GetModel()->GetApplication()->GetName() ) ); SetupWidget( widget, - ComplexOutputImageInitializer( m_Application->GetName() ) + ComplexOutputImageInitializer( GetModel()->GetApplication()->GetName() ) ); SetupWidget( widget, OutputVectorDataInitializer() ); @@ -394,36 +217,6 @@ QtWidgetView initialize( qobject_cast< FileSelectionInitializer::argument_type >( widget ) ); } -/*******************************************************************************/ -void -QtWidgetView -::closeEvent( QCloseEvent * e ) -{ - assert( e!=NULL ); - - if( !IsClosable() ) - { - assert( !m_Application.IsNull() ); - - QMessageBox::warning( - this, - tr( "Warning!" ), - tr( "OTB-Application '%1' cannot be closed while running!") - .arg( m_Application->GetDocName() ) - ); - - e->ignore(); - - return; - } - - QWidget::closeEvent( e ); - - emit QuitSignal(); - - deleteLater(); -} - /*******************************************************************************/ /* SLOTS */ /*******************************************************************************/ @@ -431,226 +224,180 @@ void QtWidgetView ::OnExecButtonClicked() { - assert( m_Model!=NULL ); - assert( m_Model->GetApplication()!=NULL ); - - - assert( I18nCoreApplication::Instance()!=NULL ); - - // - // Get layer-stack, if any. - StackedLayerModel * layerStack = - I18nCoreApplication::Instance()->GetModel< StackedLayerModel >(); - - otb::Wrapper::Application::Pointer otbApp( m_Model->GetApplication() ); - - // - // Check output parameters of OTB-application. - StringVector paramKeys( otbApp->GetParametersKeys() ); - QStringList filenames1; - - KeyLayerAccumulator::KeyLayerPairList layers; - QStringList filenames2; - - for( StringVector::const_iterator it( paramKeys.begin() ); - it!=paramKeys.end(); - ++it ) + if ( !IsRunning() ) { - if( otbApp->IsParameterEnabled( *it, true ) && - otbApp->HasValue( *it ) ) + assert( GetModel()!=NULL ); + assert( GetModel()->GetApplication()!=NULL ); + + + assert( I18nCoreApplication::Instance()!=NULL ); + + // + // Get layer-stack, if any. + StackedLayerModel * layerStack = + I18nCoreApplication::Instance()->GetModel< StackedLayerModel >(); + + otb::Wrapper::Application::Pointer otbApp( GetModel()->GetApplication() ); + + // + // Check output parameters of OTB-application. + StringVector paramKeys( otbApp->GetParametersKeys() ); + QStringList filenames1; + + KeyLayerAccumulator::KeyLayerPairList layers; + QStringList filenames2; + + for( StringVector::const_iterator it( paramKeys.begin() ); + it!=paramKeys.end(); + ++it ) { - otb::Wrapper::Parameter::Pointer param( otbApp->GetParameterByKey( *it ) ); - assert( !param.IsNull() ); - - // qDebug() - // << it->c_str() << ": type" << otbApp->GetParameterType( *it ); - - // const char* filename = NULL; - std::string filename; - - switch( otbApp->GetParameterType( *it ) ) - { - case otb::Wrapper::ParameterType_OutputFilename: - filename = - otb::DynamicCast< otb::Wrapper::OutputFilenameParameter >( param ) - ->GetValue(); - break; - // - // FILENAME. - // - // IMAGE. - case otb::Wrapper::ParameterType_OutputImage: - filename = - otb::DynamicCast< otb::Wrapper::OutputImageParameter >( param ) - ->GetFileName(); - break; - // - // VECTOR-DATA. - case otb::Wrapper::ParameterType_OutputVectorData: - filename = - otb::DynamicCast< otb::Wrapper::OutputVectorDataParameter >( param ) - ->GetFileName(); - break; - // - // COMPLEX IMAGE. - case otb::Wrapper::ParameterType_ComplexOutputImage: - filename = - otb::DynamicCast< otb::Wrapper::ComplexOutputImageParameter >( param ) - ->GetFileName(); - break; - // - // NONE. - default: - break; - } - - if( QFileInfo( filename.c_str() ).exists() ) - filenames1.push_back( filename.c_str() ); - - if( layerStack!=NULL ) - { - KeyLayerAccumulator accumulator( - std::for_each( - layerStack->Begin(), - layerStack->End(), KeyLayerAccumulator( filename, layers ) - ) - ); - - if( accumulator.GetCount()>0 ) - filenames2.push_back( filename.c_str() ); - } - } - } - - { - QString message; - - if( filenames1.size()==1 ) - { - // qDebug() - // << it->c_str() << ":" << QString( filename.c_str() ); - - message = - tr( "Are you sure you want to overwrite file '%1'?" ) - .arg( filenames1.front() ); - } - else if( filenames1.size()>1 ) - { - message = - tr( "Following files will be overwritten. Are you sure you want to continue?\n- %1" ) - .arg( filenames1.join( "\n- " ) ); - } - - if( !message.isEmpty() ) + if( otbApp->IsParameterEnabled( *it, true ) && + otbApp->HasValue( *it ) ) + { + otb::Wrapper::Parameter::Pointer param( otbApp->GetParameterByKey( *it ) ); + assert( !param.IsNull() ); + + // qDebug() + // << it->c_str() << ": type" << otbApp->GetParameterType( *it ); + + // const char* filename = NULL; + std::string filename; + + switch( otbApp->GetParameterType( *it ) ) { - QMessageBox::StandardButton button = - QMessageBox::question( - this, - PROJECT_NAME, - message, - QMessageBox::Yes | QMessageBox::No, - QMessageBox::No - ); - - if( button==QMessageBox::No ) - return; + case otb::Wrapper::ParameterType_OutputFilename: + filename = + otb::DynamicCast< otb::Wrapper::OutputFilenameParameter >( param ) + ->GetValue(); + break; + // + // FILENAME. + // + // IMAGE. + case otb::Wrapper::ParameterType_OutputImage: + filename = + otb::DynamicCast< otb::Wrapper::OutputImageParameter >( param ) + ->GetFileName(); + break; + // + // VECTOR-DATA. + case otb::Wrapper::ParameterType_OutputVectorData: + filename = + otb::DynamicCast< otb::Wrapper::OutputVectorDataParameter >( param ) + ->GetFileName(); + break; + // + // COMPLEX IMAGE. + case otb::Wrapper::ParameterType_ComplexOutputImage: + filename = + otb::DynamicCast< otb::Wrapper::ComplexOutputImageParameter >( param ) + ->GetFileName(); + break; + // + // NONE. + default: + break; } - } - - { - QString message; - - if( filenames2.size()==1 ) + + if( QFileInfo( filename.c_str() ).exists() ) + filenames1.push_back( filename.c_str() ); + + if( layerStack!=NULL ) { - // qDebug() - // << it->c_str() << ":" << QString( filename.c_str() ); - - message = - tr( "File '%1' is being viewed in " PROJECT_NAME " and will be concurrently overwritten by running this %2. File will be removed from layer-stack before running %2 and reloaded after.\n\nDo you want to continue?" ) - .arg( filenames2.front() ) - .arg( otbApp->GetDocName() ); + KeyLayerAccumulator accumulator( + std::for_each( + layerStack->Begin(), + layerStack->End(), KeyLayerAccumulator( filename, layers ) + ) + ); + + if( accumulator.GetCount()>0 ) + filenames2.push_back( filename.c_str() ); } - else if( filenames2.size()>1 ) + } + } + { - message = - tr( "Following files are being viewed in " PROJECT_NAME " and will be concurrently overwritter by running %2. Files will be removed from layer-stack before running %2. Do you want to continue?\n- %1" ) - .arg( filenames2.join( "\n- " ) ) - .arg( otbApp->GetDocName() ); + QString message; + + if( filenames1.size()==1 ) + { + // qDebug() + // << it->c_str() << ":" << QString( filename.c_str() ); + + message = + tr( "Are you sure you want to overwrite file '%1'?" ) + .arg( filenames1.front() ); + } + else if( filenames1.size()>1 ) + { + message = + tr( "Following files will be overwritten. Are you sure you want to continue?\n- %1" ) + .arg( filenames1.join( "\n- " ) ); + } + + if( !message.isEmpty() ) + { + QMessageBox::StandardButton button = + QMessageBox::question( + this, + PROJECT_NAME, + message, + QMessageBox::Yes | QMessageBox::No, + QMessageBox::No + ); + + if( button==QMessageBox::No ) + return; + } } - - if( !message.isEmpty() ) + { - QMessageBox::StandardButton button = - QMessageBox::question( - this, - PROJECT_NAME, - message, - QMessageBox::Yes | QMessageBox::No, - QMessageBox::No - ); - - if( button==QMessageBox::No ) - return; - - while( !layers.empty() ) + QString message; + + if( filenames2.size()==1 ) { - layerStack->Delete( layers.front().first ); - - layers.pop_front(); + // qDebug() + // << it->c_str() << ":" << QString( filename.c_str() ); + + message = + tr( "File '%1' is being viewed in " PROJECT_NAME " and will be concurrently overwritten by running this %2. File will be removed from layer-stack before running %2 and reloaded after.\n\nDo you want to continue?" ) + .arg( filenames2.front() ) + .arg( otbApp->GetDocName() ); + } + else if( filenames2.size()>1 ) + { + message = + tr( "Following files are being viewed in " PROJECT_NAME " and will be concurrently overwritter by running %2. Files will be removed from layer-stack before running %2. Do you want to continue?\n- %1" ) + .arg( filenames2.join( "\n- " ) ) + .arg( otbApp->GetDocName() ); + } + + if( !message.isEmpty() ) + { + QMessageBox::StandardButton button = + QMessageBox::question( + this, + PROJECT_NAME, + message, + QMessageBox::Yes | QMessageBox::No, + QMessageBox::No + ); + + if( button==QMessageBox::No ) + return; + + while( !layers.empty() ) + { + layerStack->Delete( layers.front().first ); + + layers.pop_front(); + } } } } - - /* U N S A F E - // BUGFIX: Mantis-750 - // - // Remove files which will be overwritten in order to use - // file-existence to check whether to emit the OutputImageChanged - // signal (see ::OnApplicationExecutionDone()). - for( FileInfoVector::const_iterator it( fileInfos.begin() ); - it!=fileInfos.end(); - ++it ) - { - qDebug() << "Removing:" << it->filePath(); - - it->dir().remove( it->fileName() ); - } - */ - - emit ExecuteAndWriteOutput(); - - m_Message->setText("<center><font color=\"#FF0000\">"+tr("Running")+"</font></center>"); -} - -/******************************************************************************/ -void -QtWidgetView -::UpdateMessageAfterExecution(int status) -{ - if (status >= 0) - { - m_Message->setText("<center>" - "<img src=\":/icons/done\" width=\"16\" height=\"16\" />" - "<font color=\"#00A000\">"+tr("Done")+"</font></center>"); - } - else - { - m_Message->setText("<center>" - "<img src=\":/icons/failed\" width=\"16\" height=\"16\" />" - "<font color=\"#FF0000\">"+tr("Failed")+"</font></center>"); - } -} - -/*******************************************************************************/ -void -QtWidgetView -::UpdateMessageAfterApplicationReady( bool val ) -{ - if(val == true) - m_Message->setText("<center><font color=\"#00A000\">"+tr("Ready to run")+"</font></center>"); - else - m_Message->setText("<center><font color=\"#FF0000\">"+tr("Select parameters")+"</font></center>"); + otb::Wrapper::QtWidgetView::OnExecButtonClicked(); } /*******************************************************************************/ @@ -675,7 +422,7 @@ void QtWidgetView ::OnApplicationExecutionDone( int status ) { - otb::Wrapper::Application::Pointer otbApp( m_Model->GetApplication() ); + otb::Wrapper::Application::Pointer otbApp( GetModel()->GetApplication() ); if( status!=0 ) { @@ -765,6 +512,5 @@ QtWidgetView emit ExecutionDone( status ); } -} - -} +} // end of namespace Wrapper +} // end of namespace mvd diff --git a/Modules/Wrappers/ApplicationEngine/include/otbWrapperApplication.h b/Modules/Wrappers/ApplicationEngine/include/otbWrapperApplication.h index f840418c3cf41522683483cebe35446206104ddb..4748ab0c5ddc71a9a1ffff72b8ba89585d86bc8d 100644 --- a/Modules/Wrappers/ApplicationEngine/include/otbWrapperApplication.h +++ b/Modules/Wrappers/ApplicationEngine/include/otbWrapperApplication.h @@ -22,6 +22,7 @@ #define otbWrapperApplication_h #include <string> +#include <set> #include "otbWrapperTypes.h" #include "otbWrapperTags.h" #include "otbWrapperParameterGroup.h" @@ -154,6 +155,9 @@ public: */ int ExecuteAndWriteOutput(); + /** Request the application to stop its processing */ + void Stop(); + /* Get the internal application parameters * * WARNING: this method may disappear from the API */ @@ -914,6 +918,27 @@ public: */ void SetParameterImageBase(const std::string & key, ImageBaseType* img, unsigned int idx = 0); + /** + Register all ProcessObject that are linked to parameters : + \li ParameterType_OutputImage + \li ParameterType_OutputVectorData + + Those ProcessObjects are stored in the m_Filters set and are deleted at the + end of ExecuteAndWriteOutput (if there are only held by the set) + This method can be called just before the end of a DoExecute in a derived + class of Application. + */ + void RegisterPipeline(); + + /** + Register all DataObject that are reachable from : + \li ParameterType_OutputImage + \li ParameterType_OutputVectorData + + Once registered, the methode ReleaseData is called on each one of them. + */ + void FreeRessources(); + protected: /** Constructor */ Application(); @@ -1048,6 +1073,8 @@ private: * implementation does nothing */ virtual void AfterExecuteAndWriteOutputs(); + virtual void DoFreeRessources(){}; + Application(const Application &); //purposely not implemented void operator =(const Application&); //purposely not implemented @@ -1059,6 +1086,8 @@ private: itk::ProcessObject::Pointer m_ProgressSource; std::string m_ProgressSourceDescription; + std::set<itk::ProcessObject::Pointer> m_Filters; + /** Long name of the application (that can be displayed...) */ std::string m_DocName; /** Long and precise application description . */ diff --git a/Modules/Wrappers/ApplicationEngine/include/otbWrapperInputImageParameter.h b/Modules/Wrappers/ApplicationEngine/include/otbWrapperInputImageParameter.h index 722dbeead7ab40c868ec1fc0732292a9f6ab6b55..29b9ac2866266b6439baf95154f147ea44241e67 100644 --- a/Modules/Wrappers/ApplicationEngine/include/otbWrapperInputImageParameter.h +++ b/Modules/Wrappers/ApplicationEngine/include/otbWrapperInputImageParameter.h @@ -109,7 +109,6 @@ public: void ClearValue() override; - protected: /** Constructor */ InputImageParameter(); diff --git a/Modules/Wrappers/ApplicationEngine/include/otbWrapperOutputImageParameter.h b/Modules/Wrappers/ApplicationEngine/include/otbWrapperOutputImageParameter.h index b9bc451c3b3b9a59437ac306cf48929e9459d734..eac7df4cadad4e1c8376918f53401439748a6094 100644 --- a/Modules/Wrappers/ApplicationEngine/include/otbWrapperOutputImageParameter.h +++ b/Modules/Wrappers/ApplicationEngine/include/otbWrapperOutputImageParameter.h @@ -117,14 +117,8 @@ protected: /** Destructor */ ~OutputImageParameter() override; - template <class TInputVectorImageType> - void SwitchVectorImageWrite(); - - template <class TInputVectorImageType> - void SwitchRGBImageWrite(); - - template <class TInputVectorImageType> - void SwitchRGBAImageWrite(); + template <class TInput> + int SwitchInput(TInput *img); //FloatVectorImageType::Pointer m_Image; ImageBaseType::Pointer m_Image; @@ -132,46 +126,28 @@ protected: ImagePixelType m_PixelType; ImagePixelType m_DefaultPixelType; - typedef otb::ImageFileWriter<UInt8VectorImageType> VectorUInt8WriterType; - typedef otb::ImageFileWriter<Int16VectorImageType> VectorInt16WriterType; - typedef otb::ImageFileWriter<UInt16VectorImageType> VectorUInt16WriterType; - typedef otb::ImageFileWriter<Int32VectorImageType> VectorInt32WriterType; - typedef otb::ImageFileWriter<UInt32VectorImageType> VectorUInt32WriterType; - typedef otb::ImageFileWriter<FloatVectorImageType> VectorFloatWriterType; - typedef otb::ImageFileWriter<DoubleVectorImageType> VectorDoubleWriterType; - - typedef otb::ImageFileWriter<UInt8RGBAImageType> RGBAUInt8WriterType; - typedef otb::ImageFileWriter<UInt8RGBImageType> RGBUInt8WriterType; - - typedef otb::ImageFileWriter<ComplexInt16VectorImageType> ComplexVectorInt16WriterType; - typedef otb::ImageFileWriter<ComplexInt32VectorImageType> ComplexVectorInt32WriterType; - typedef otb::ImageFileWriter<ComplexFloatVectorImageType> ComplexVectorFloatWriterType; - typedef otb::ImageFileWriter<ComplexDoubleVectorImageType> ComplexVectorDoubleWriterType; - - VectorUInt8WriterType::Pointer m_VectorUInt8Writer; - VectorInt16WriterType::Pointer m_VectorInt16Writer; - VectorUInt16WriterType::Pointer m_VectorUInt16Writer; - VectorInt32WriterType::Pointer m_VectorInt32Writer; - VectorUInt32WriterType::Pointer m_VectorUInt32Writer; - VectorFloatWriterType::Pointer m_VectorFloatWriter; - VectorDoubleWriterType::Pointer m_VectorDoubleWriter; - - RGBUInt8WriterType::Pointer m_RGBUInt8Writer; - RGBAUInt8WriterType::Pointer m_RGBAUInt8Writer; - - ComplexVectorInt16WriterType::Pointer m_ComplexVectorInt16Writer; - ComplexVectorInt32WriterType::Pointer m_ComplexVectorInt32Writer; - ComplexVectorFloatWriterType::Pointer m_ComplexVectorFloatWriter; - ComplexVectorDoubleWriterType::Pointer m_ComplexVectorDoubleWriter; - private: OutputImageParameter(const Parameter &); //purposely not implemented void operator =(const Parameter&); //purposely not implemented unsigned int m_RAMValue; + itk::ProcessObject::Pointer m_Caster; + + itk::ProcessObject::Pointer m_Writer; + }; // End class OutputImage Parameter +// Declare specialisation for UInt8RGBAImageType +template <> +int +OutputImageParameter::SwitchInput(UInt8RGBAImageType *img); + +// Declare specialisation for UInt8RGBImageType +template <> +int +OutputImageParameter::SwitchInput(UInt8RGBImageType *img); + } // End namespace Wrapper } // End namespace otb diff --git a/Modules/Wrappers/ApplicationEngine/src/otbWrapperApplication.cxx b/Modules/Wrappers/ApplicationEngine/src/otbWrapperApplication.cxx index 5f20a11ace33f79f904a2810a7c19f5571d4142f..31f46f3a019e6fe335ebcae5535b4b6284bc2f4a 100644 --- a/Modules/Wrappers/ApplicationEngine/src/otbWrapperApplication.cxx +++ b/Modules/Wrappers/ApplicationEngine/src/otbWrapperApplication.cxx @@ -45,6 +45,8 @@ #include "otbWrapperTypes.h" #include <exception> #include "itkMacro.h" +#include <stack> +#include <set> namespace otb { @@ -380,6 +382,213 @@ void Application::UpdateParameters() void Application::AfterExecuteAndWriteOutputs() {} +void +Application::RegisterPipeline() +{ + std::stack< itk::DataObject * > dataStack; + std::set< itk::DataObject * > inputData; + std::vector<std::string> paramList = GetParametersKeys(true); + // Get both end of the pipeline + for ( auto const & key : paramList ) + { + if ( GetParameterType(key) == ParameterType_OutputImage ) + { + Parameter* param = GetParameterByKey(key); + OutputImageParameter * outP = + dynamic_cast< OutputImageParameter * >( param ); + itk::ImageBase< 2 > * outData = outP->GetValue(); + if ( outData ) + dataStack.push(outData); + } + else if ( GetParameterType(key) == ParameterType_OutputVectorData ) + { + Parameter* param = GetParameterByKey(key); + OutputVectorDataParameter * outP = + dynamic_cast< OutputVectorDataParameter * >( param ); + VectorDataType * outData = outP->GetValue(); + if ( outData ) + dataStack.push(outData); + } + else if ( GetParameterType(key) == ParameterType_InputImage ) + { + Parameter* param = GetParameterByKey(key); + InputImageParameter * inP = + dynamic_cast< InputImageParameter * >( param ); + if ( !inP->HasValue() ) + continue; + ImageBaseType * inData = inP->GetImage< ImageBaseType >(); + if ( inData && !inputData.count(inData) ) + inputData.insert(inData); + } + else if ( GetParameterType(key) == ParameterType_InputImageList ) + { + Parameter * param = GetParameterByKey(key); + InputImageListParameter * inP = + dynamic_cast< InputImageListParameter * > ( param ); + if ( !inP->HasValue() ) + continue; + const FloatVectorImageListType * list = inP->GetImageList(); + for ( auto it = list->Begin() ; it != list->End() ; ++it ) + { + FloatVectorImageType * inData = it.Get().GetPointer(); + if ( inData && !inputData.count(inData) ) + inputData.insert(inData); + } + } + else if ( GetParameterType(key) == ParameterType_InputVectorData ) + { + Parameter * param = GetParameterByKey(key); + InputVectorDataParameter * inP = + dynamic_cast< InputVectorDataParameter * > ( param ); + if ( !inP->HasValue() ) + continue; + VectorDataType * inData = inP->GetVectorData(); + if ( inData && !inputData.count(inData) ) + inputData.insert(inData); + } + else if ( GetParameterType(key) == ParameterType_InputVectorDataList ) + { + Parameter * param = GetParameterByKey(key); + InputVectorDataListParameter * inP = + dynamic_cast< InputVectorDataListParameter * > ( param ); + if ( !inP->HasValue() ) + continue; + VectorDataListType * list = inP->GetVectorDataList(); + for ( auto it = list->Begin() ; it != list->End() ; ++it ) + { + VectorDataType * inData = it.Get().GetPointer(); + if ( inData && !inputData.count(inData) ) + inputData.insert(inData); + } + } + } + + // DFS + while ( !dataStack.empty() ) + { + itk::DataObject * current = dataStack.top(); + dataStack.pop(); + // whether current = null or is an input data it has no source + if ( !current || inputData.count( current ) ) + continue; + // if current is a list push every of its members in datastack + if ( dynamic_cast< DataObjectListInterface *> (current) ) + { + DataObjectListInterface * list = + dynamic_cast< DataObjectListInterface *> (current); + int length = list->Size(); + for ( int i = 0 ; i < length ; i++ ) + { + itk::DataObject * newData = list->GetNthDataObject(i); + if ( !current || inputData.count( current ) ) + continue; + dataStack.push( newData ); + continue; + } + } + // Finally get the current's process object source + itk::ProcessObject * process = (current->GetSource()).GetPointer(); + if ( !process || m_Filters.find( process ) != m_Filters.end() ) + continue; + m_Filters.insert( process ); + std::vector< itk::DataObject::Pointer > inputs = process->GetInputs(); + // Push back all source's inputs in datastack + for ( auto const & it : inputs ) + { + if ( inputData.count( it.GetPointer() ) ) + continue; + dataStack.push( it.GetPointer() ); + } + } +} + +void Application::FreeRessources() +{ + std::set< itk::DataObject * > dataSetToRelease; // do not release output + std::set< itk::DataObject * > dataSet; + std::vector<std::string> paramList = GetParametersKeys(true); + // Get the end of the pipeline + for ( const auto & key : paramList ) + { + if ( GetParameterType(key) == ParameterType_OutputImage ) + { + Parameter* param = GetParameterByKey(key); + OutputImageParameter * outP = dynamic_cast<OutputImageParameter*>(param); + itk::ImageBase<2> * outData = outP->GetValue(); + if ( outData ) + dataSet.insert(outData); + } + else if ( GetParameterType(key) == ParameterType_OutputVectorData ) + { + Parameter* param = GetParameterByKey(key); + OutputVectorDataParameter * outP = dynamic_cast<OutputVectorDataParameter*>(param); + Wrapper::VectorDataType * outData = outP->GetValue(); + if ( outData ) + dataSet.insert(outData); + } + else + continue; + } + // initialize DFS + std::stack< itk::ProcessObject * > processStack; + for ( auto data : dataSet ) + { + auto process = (data->GetSource()).GetPointer(); + if ( process ) + processStack.push( process ); + } + // DFS + while ( !processStack.empty() ) + { + itk::ProcessObject * current = processStack.top(); + processStack.pop(); + // if null continue + if ( !current ) + continue; + // Get all inputs + auto inputVector = current->GetInputs(); + for ( auto data : inputVector ) + { + // If input is null or already in the set continue + if ( !data.GetPointer() || dataSet.count( data.GetPointer() ) ) + continue; + // If input is a list + if ( dynamic_cast< DataObjectListInterface *> (data.GetPointer()) ) + { + DataObjectListInterface * list = + dynamic_cast< DataObjectListInterface *> (data.GetPointer()); + int length = list->Size(); + for ( int i = 0 ; i < length ; i++ ) + { + itk::DataObject * newData = list->GetNthDataObject(i); + if ( !newData || dataSet.count( newData ) ) + continue; + dataSet.insert( newData ); + dataSetToRelease.insert( newData ); + itk::ProcessObject * process = newData->GetSource().GetPointer(); + if ( process ) + processStack.push( process ); + } + } + else + { + dataSet.insert( data.GetPointer() ); + dataSetToRelease.insert( data.GetPointer() ); + itk::ProcessObject * process = data->GetSource().GetPointer(); + if ( process ) + processStack.push( process ); + } + } + } + // Release data + for ( auto data : dataSetToRelease ) + { + data->ReleaseData(); + } + // Call override method + DoFreeRessources(); +} + int Application::Execute() { @@ -477,7 +686,6 @@ int Application::ExecuteAndWriteOutput() if(outputParam!=ITK_NULLPTR) { - outputParam->InitializeWriters(); std::string checkReturn = outputParam->CheckFileName(true); if (!checkReturn.empty()) { @@ -487,6 +695,7 @@ int Application::ExecuteAndWriteOutput() { outputParam->SetRAMValue(ram); } + outputParam->InitializeWriters(); std::ostringstream progressId; progressId << "Writing " << outputParam->GetFileName() << "..."; AddProcess(outputParam->GetWriter(), progressId.str()); @@ -526,7 +735,6 @@ int Application::ExecuteAndWriteOutput() outputParam->Write(); } } - //xml writer parameter else if (m_HaveOutXML && GetParameterType(key) == ParameterType_OutputProcessXML && IsParameterEnabled(key) && HasValue(key) ) @@ -542,11 +750,19 @@ int Application::ExecuteAndWriteOutput() } this->AfterExecuteAndWriteOutputs(); - m_Chrono.Stop(); + + FreeRessources(); + m_Filters.clear(); return status; } +void +Application::Stop() +{ + m_ProgressSource->SetAbortGenerateData(true); +} + /* Enable the use of an optional parameter. Returns the previous state */ void Application::EnableParameter(std::string paramKey) { diff --git a/Modules/Wrappers/ApplicationEngine/src/otbWrapperComplexOutputImageParameter.cxx b/Modules/Wrappers/ApplicationEngine/src/otbWrapperComplexOutputImageParameter.cxx index b97171cb71d9b270f07437cf22fa5116bc0b0d7c..4218836ed7eb3091f1cf1a451c165d9bb77a040a 100644 --- a/Modules/Wrappers/ApplicationEngine/src/otbWrapperComplexOutputImageParameter.cxx +++ b/Modules/Wrappers/ApplicationEngine/src/otbWrapperComplexOutputImageParameter.cxx @@ -259,20 +259,22 @@ ComplexOutputImageParameter::GetWriter() case ComplexImagePixelType_int16: { writer = m_ComplexVectorInt16Writer; + break; } case ComplexImagePixelType_int32: { writer = m_ComplexVectorInt32Writer; + break; } case ComplexImagePixelType_float: { writer = m_ComplexVectorFloatWriter; - break; + break; } case ComplexImagePixelType_double: { writer = m_ComplexVectorDoubleWriter; - break; + break; } } return writer; diff --git a/Modules/Wrappers/ApplicationEngine/src/otbWrapperMetaDataHelper.cxx b/Modules/Wrappers/ApplicationEngine/src/otbWrapperMetaDataHelper.cxx index 4cf7c602e1f247a6c9836a50cea9587218b9e989..02ddfea8985acb5b67f29cbbf344139b5bcacc3f 100644 --- a/Modules/Wrappers/ApplicationEngine/src/otbWrapperMetaDataHelper.cxx +++ b/Modules/Wrappers/ApplicationEngine/src/otbWrapperMetaDataHelper.cxx @@ -30,7 +30,7 @@ namespace MetaDataHelper MDType GetType(const std::string &val) { - MDType ret; + MDType ret(MDType::String); otb::MetaDataKey::KeyType kt = otb::MetaDataKey::GetKeyType(val); switch (kt) { @@ -88,7 +88,7 @@ GetInt( const itk::MetaDataDictionary &dict, const std::string &key) { - unsigned int ret; + unsigned int ret=0; itk::ExposeMetaData<unsigned int>(dict, key, ret); return ret; } @@ -107,7 +107,7 @@ GetDouble( const itk::MetaDataDictionary &dict, const std::string &key) { - double ret; + double ret = 0.0; itk::ExposeMetaData<double>(dict, key, ret); return ret; } diff --git a/Modules/Wrappers/ApplicationEngine/src/otbWrapperOutputImageParameter.cxx b/Modules/Wrappers/ApplicationEngine/src/otbWrapperOutputImageParameter.cxx index 09396447765610f1b1f2dd903943b9a2cc116922..9e6e33efdac2c1223830dad9db754e7110777817 100644 --- a/Modules/Wrappers/ApplicationEngine/src/otbWrapperOutputImageParameter.cxx +++ b/Modules/Wrappers/ApplicationEngine/src/otbWrapperOutputImageParameter.cxx @@ -42,7 +42,9 @@ namespace Wrapper OutputImageParameter::OutputImageParameter() : m_PixelType(ImagePixelType_float), m_DefaultPixelType(ImagePixelType_float), - m_RAMValue(0) + m_RAMValue(0), + m_Caster(nullptr), + m_Writer(nullptr) { this->SetName("Output Image"); this->SetKey("out"); @@ -149,35 +151,102 @@ OutputImageParameter::ConvertStringToPixelType(const std::string &value, ImagePi void OutputImageParameter::InitializeWriters() { - m_VectorUInt8Writer = VectorUInt8WriterType::New(); - m_VectorInt16Writer = VectorInt16WriterType::New(); - m_VectorUInt16Writer = VectorUInt16WriterType::New(); - m_VectorInt32Writer = VectorInt32WriterType::New(); - m_VectorUInt32Writer = VectorUInt32WriterType::New(); - m_VectorFloatWriter = VectorFloatWriterType::New(); - m_VectorDoubleWriter = VectorDoubleWriterType::New(); - - m_RGBUInt8Writer = RGBUInt8WriterType::New(); - m_RGBAUInt8Writer = RGBAUInt8WriterType::New(); - - m_ComplexVectorInt16Writer = ComplexVectorInt16WriterType::New(); - m_ComplexVectorInt32Writer = ComplexVectorInt32WriterType::New(); - m_ComplexVectorFloatWriter = ComplexVectorFloatWriterType::New(); - m_ComplexVectorDoubleWriter = ComplexVectorDoubleWriterType::New(); + ImageBaseType* imgBase = m_Image.GetPointer(); + // Guess the image type + std::string className(m_Image->GetNameOfClass()); + if (className == "VectorImage") + { + UInt8VectorImageType* imgUInt8 = dynamic_cast<UInt8VectorImageType*>(imgBase); + if (imgUInt8 && SwitchInput(imgUInt8)) return; + + Int16VectorImageType* imgInt16 = dynamic_cast<Int16VectorImageType*>(imgBase); + if (imgInt16 && SwitchInput(imgInt16)) return; + + UInt16VectorImageType* imgUInt16 = dynamic_cast<UInt16VectorImageType*>(imgBase); + if (imgUInt16 && SwitchInput(imgUInt16)) return; + + Int32VectorImageType* imgInt32 = dynamic_cast<Int32VectorImageType*>(imgBase); + if (imgInt32 && SwitchInput(imgInt32)) return; + + UInt32VectorImageType* imgUInt32 = dynamic_cast<UInt32VectorImageType*>(imgBase); + if (imgUInt32 && SwitchInput(imgUInt32)) return; + + FloatVectorImageType* imgFloat = dynamic_cast<FloatVectorImageType*>(imgBase); + if (imgFloat && SwitchInput(imgFloat)) return; + + DoubleVectorImageType* imgDouble = dynamic_cast<DoubleVectorImageType*>(imgBase); + if (imgDouble && SwitchInput(imgDouble)) return; + + ComplexInt16VectorImageType* imgCInt16 = dynamic_cast<ComplexInt16VectorImageType*>(imgBase); + if (imgCInt16 && SwitchInput(imgCInt16)) return; + + ComplexInt32VectorImageType* imgCInt32 = dynamic_cast<ComplexInt32VectorImageType*>(imgBase); + if (imgCInt32 && SwitchInput(imgCInt32)) return; + + ComplexFloatVectorImageType* imgCFloat = dynamic_cast<ComplexFloatVectorImageType*>(imgBase); + if (imgCFloat && SwitchInput(imgCFloat)) return; + + ComplexDoubleVectorImageType* imgCDouble = dynamic_cast<ComplexDoubleVectorImageType*>(imgBase); + if (imgCDouble && SwitchInput(imgCDouble)) return; + } + else + { + UInt8ImageType* imgUInt8 = dynamic_cast<UInt8ImageType*>(imgBase); + if (imgUInt8 && SwitchInput(imgUInt8)) return; + + Int16ImageType* imgInt16 = dynamic_cast<Int16ImageType*>(imgBase); + if (imgInt16 && SwitchInput(imgInt16)) return; + + UInt16ImageType* imgUInt16 = dynamic_cast<UInt16ImageType*>(imgBase); + if (imgUInt16 && SwitchInput(imgUInt16)) return; + + Int32ImageType* imgInt32 = dynamic_cast<Int32ImageType*>(imgBase); + if (imgInt32 && SwitchInput(imgInt32)) return; + + UInt32ImageType* imgUInt32 = dynamic_cast<UInt32ImageType*>(imgBase); + if (imgUInt32 && SwitchInput(imgUInt32)) return; + + FloatImageType* imgFloat = dynamic_cast<FloatImageType*>(imgBase); + if (imgFloat && SwitchInput(imgFloat)) return; + + DoubleImageType* imgDouble = dynamic_cast<DoubleImageType*>(imgBase); + if (imgDouble && SwitchInput(imgDouble)) return; + + ComplexInt16ImageType* imgCInt16 = dynamic_cast<ComplexInt16ImageType*>(imgBase); + if (imgCInt16 && SwitchInput(imgCInt16)) return; + + ComplexInt32ImageType* imgCInt32 = dynamic_cast<ComplexInt32ImageType*>(imgBase); + if (imgCInt32 && SwitchInput(imgCInt32)) return; + + ComplexFloatImageType* imgCFloat = dynamic_cast<ComplexFloatImageType*>(imgBase); + if (imgCFloat && SwitchInput(imgCFloat)) return; + + ComplexDoubleImageType* imgCDouble = dynamic_cast<ComplexDoubleImageType*>(imgBase); + if (imgCDouble && SwitchInput(imgCDouble)) return; + + UInt8RGBImageType* imgRGB = dynamic_cast<UInt8RGBImageType*>(imgBase); + if (imgRGB && SwitchInput(imgRGB)) return; + + UInt8RGBAImageType* imgRGBA = dynamic_cast<UInt8RGBAImageType*>(imgBase); + if (imgRGBA && SwitchInput(imgRGBA)) return; + } + + itkExceptionMacro("Unknown image type"); } template <typename TInput, typename TOutput> -void -ClampAndWriteVectorImage( itk::ImageBase<2> * in , - otb::ImageFileWriter<TOutput> * writer , +std::pair<itk::ProcessObject::Pointer,itk::ProcessObject::Pointer> +ClampAndWriteVectorImage( TInput * in , const std::string & filename , const unsigned int & ramValue ) { + std::pair<itk::ProcessObject::Pointer,itk::ProcessObject::Pointer> ret; typedef ClampImageFilter < TInput , TOutput > ClampFilterType; typename ClampFilterType::Pointer clampFilter ( ClampFilterType::New() ); - clampFilter->SetInput( dynamic_cast<TInput*>(in)); + clampFilter->SetInput( in); + ret.first = clampFilter.GetPointer(); bool useStandardWriter = true; @@ -194,8 +263,14 @@ ClampAndWriteVectorImage( itk::ImageBase<2> * in , if(extension == ".vrt") { - // Use the WriteMPI function - WriteMPI(clampFilter->GetOutput(),filename,ramValue); + // Use the MPIVrtWriter + typedef otb::MPIVrtWriter<TOutput> VRTWriterType; + + typename VRTWriterType::Pointer vrtWriter = VRTWriterType::New(); + vrtWriter->SetInput(clampFilter->GetOutput()); + vrtWriter->SetFileName(filename); + vrtWriter->SetAvailableRAM(ramValue); + ret.second = vrtWriter.GetPointer(); } #ifdef OTB_USE_SPTW else if (extension == ".tif") @@ -206,9 +281,8 @@ ClampAndWriteVectorImage( itk::ImageBase<2> * in , typename SPTWriterType::Pointer sptWriter = SPTWriterType::New(); sptWriter->SetFileName(filename); sptWriter->SetInput(clampFilter->GetOutput()); - sptWriter->SetAutomaticAdaptativeStreaming(ramValue); sptWriter->GetStreamingManager()->SetDefaultRAM(ramValue); - sptWriter->Update(); + ret.second = sptWriter.GetPointer(); } #endif @@ -223,114 +297,110 @@ ClampAndWriteVectorImage( itk::ImageBase<2> * in , if(useStandardWriter) { + typename otb::ImageFileWriter<TOutput>::Pointer writer = + otb::ImageFileWriter<TOutput>::New(); writer->SetFileName( filename ); writer->SetInput(clampFilter->GetOutput()); writer->GetStreamingManager()->SetDefaultRAM(ramValue); - writer->Update(); + ret.second = writer.GetPointer(); } + + return ret; } template <class TInput> -void -OutputImageParameter::SwitchVectorImageWrite() - { +int +OutputImageParameter::SwitchInput(TInput *img) +{ + if (! img) return 0; + + std::pair<itk::ProcessObject::Pointer,itk::ProcessObject::Pointer> ret; switch(m_PixelType ) { case ImagePixelType_uint8: { - ClampAndWriteVectorImage< TInput , UInt8VectorImageType > ( - m_Image , - m_VectorUInt8Writer , + ret = ClampAndWriteVectorImage< TInput , UInt8VectorImageType > ( + img , m_FileName , m_RAMValue ); break; } case ImagePixelType_int16: { - ClampAndWriteVectorImage< TInput , Int16VectorImageType > ( - m_Image , - m_VectorInt16Writer , + ret = ClampAndWriteVectorImage< TInput , Int16VectorImageType > ( + img , m_FileName , m_RAMValue ); break; } case ImagePixelType_uint16: { - ClampAndWriteVectorImage< TInput , UInt16VectorImageType > ( - m_Image , - m_VectorUInt16Writer , + ret = ClampAndWriteVectorImage< TInput , UInt16VectorImageType > ( + img , m_FileName , m_RAMValue ); break; } case ImagePixelType_int32: { - ClampAndWriteVectorImage< TInput , Int32VectorImageType > ( - m_Image , - m_VectorInt32Writer , + ret = ClampAndWriteVectorImage< TInput , Int32VectorImageType > ( + img , m_FileName , m_RAMValue ); break; } case ImagePixelType_uint32: { - ClampAndWriteVectorImage< TInput , UInt32VectorImageType > ( - m_Image , - m_VectorUInt32Writer , + ret = ClampAndWriteVectorImage< TInput , UInt32VectorImageType > ( + img , m_FileName , m_RAMValue ); break; } case ImagePixelType_float: { - ClampAndWriteVectorImage< TInput , FloatVectorImageType > ( - m_Image , - m_VectorFloatWriter , + ret = ClampAndWriteVectorImage< TInput , FloatVectorImageType > ( + img , m_FileName , m_RAMValue ); break; } case ImagePixelType_double: { - ClampAndWriteVectorImage< TInput , DoubleVectorImageType > ( - m_Image , - m_VectorDoubleWriter , + ret = ClampAndWriteVectorImage< TInput , DoubleVectorImageType > ( + img , m_FileName , m_RAMValue ); break; } case ImagePixelType_cint16: { - ClampAndWriteVectorImage < TInput , ComplexInt16VectorImageType > ( - m_Image , - m_ComplexVectorInt16Writer , + ret = ClampAndWriteVectorImage < TInput , ComplexInt16VectorImageType > ( + img , m_FileName , m_RAMValue ); break; } case ImagePixelType_cint32: { - ClampAndWriteVectorImage < TInput , ComplexInt32VectorImageType > ( - m_Image , - m_ComplexVectorInt32Writer , + ret = ClampAndWriteVectorImage < TInput , ComplexInt32VectorImageType > ( + img , m_FileName , m_RAMValue ); break; } case ImagePixelType_cfloat: { - ClampAndWriteVectorImage < TInput , ComplexFloatVectorImageType > ( - m_Image , - m_ComplexVectorFloatWriter , + ret = ClampAndWriteVectorImage < TInput , ComplexFloatVectorImageType > ( + img , m_FileName , m_RAMValue ); break; } case ImagePixelType_cdouble: { - ClampAndWriteVectorImage < TInput , ComplexDoubleVectorImageType > ( - m_Image , - m_ComplexVectorDoubleWriter , + ret = ClampAndWriteVectorImage < TInput , ComplexDoubleVectorImageType > ( + img , m_FileName , m_RAMValue ); break; @@ -338,256 +408,27 @@ OutputImageParameter::SwitchVectorImageWrite() default: break; } - } - -template <class TInputRGBAImageType> -void -OutputImageParameter::SwitchRGBAImageWrite() - { - if( m_PixelType == ImagePixelType_uint8 ) - { - m_RGBAUInt8Writer->SetFileName( this->GetFileName() ); - m_RGBAUInt8Writer->SetInput(dynamic_cast<UInt8RGBAImageType*>(m_Image.GetPointer()) ); - m_RGBAUInt8Writer->GetStreamingManager()->SetDefaultRAM(m_RAMValue); - m_RGBAUInt8Writer->Update(); - } - else - itkExceptionMacro("Unknown PixelType for RGBA Image. Only uint8 is supported."); - } - -template <class TInputRGBImageType> -void -OutputImageParameter::SwitchRGBImageWrite() - { - if( m_PixelType == ImagePixelType_uint8 ) - { - m_RGBUInt8Writer->SetFileName( this->GetFileName() ); - m_RGBUInt8Writer->SetInput(dynamic_cast<UInt8RGBImageType*>(m_Image.GetPointer()) ); - m_RGBUInt8Writer->GetStreamingManager()->SetDefaultRAM(m_RAMValue); - m_RGBUInt8Writer->Update(); - } - else - itkExceptionMacro("Unknown PixelType for RGB Image. Only uint8 is supported."); - } + // Save the caster and writer + m_Caster = ret.first; + m_Writer = ret.second; + return 1; +} void OutputImageParameter::Write() { - m_Image->UpdateOutputInformation(); + m_Writer->Update(); - if (dynamic_cast<UInt8ImageType*>(m_Image.GetPointer())) - { - SwitchVectorImageWrite<UInt8ImageType>(); - } - else if (dynamic_cast<Int16ImageType*>(m_Image.GetPointer())) - { - SwitchVectorImageWrite<Int16ImageType>(); - } - else if (dynamic_cast<UInt16ImageType*>(m_Image.GetPointer())) - { - SwitchVectorImageWrite<UInt16ImageType>(); - } - else if (dynamic_cast<Int32ImageType*>(m_Image.GetPointer())) - { - SwitchVectorImageWrite<Int32ImageType>(); - } - else if (dynamic_cast<UInt32ImageType*>(m_Image.GetPointer())) - { - SwitchVectorImageWrite<UInt32ImageType>(); - } - else if (dynamic_cast<FloatImageType*>(m_Image.GetPointer())) - { - SwitchVectorImageWrite<FloatImageType>(); - } - else if (dynamic_cast<DoubleImageType*>(m_Image.GetPointer())) - { - SwitchVectorImageWrite<DoubleImageType>(); - } - else if (dynamic_cast<ComplexInt16ImageType*>(m_Image.GetPointer()) ) - { - SwitchVectorImageWrite<ComplexInt16ImageType>(); - } - else if (dynamic_cast<ComplexInt32ImageType*>(m_Image.GetPointer()) ) - { - SwitchVectorImageWrite<ComplexInt32ImageType>(); - } - else if (dynamic_cast<ComplexFloatImageType*>(m_Image.GetPointer()) ) - { - SwitchVectorImageWrite<ComplexFloatImageType>(); - } - else if (dynamic_cast<ComplexDoubleImageType*>(m_Image.GetPointer()) ) - { - SwitchVectorImageWrite<ComplexDoubleImageType>(); - } - else if (dynamic_cast<UInt8VectorImageType*>(m_Image.GetPointer())) - { - SwitchVectorImageWrite<UInt8VectorImageType>(); - } - else if (dynamic_cast<Int16VectorImageType*>(m_Image.GetPointer())) - { - SwitchVectorImageWrite<Int16VectorImageType>(); - } - else if (dynamic_cast<UInt16VectorImageType*>(m_Image.GetPointer())) - { - SwitchVectorImageWrite<UInt16VectorImageType>(); - } - else if (dynamic_cast<Int32VectorImageType*>(m_Image.GetPointer())) - { - SwitchVectorImageWrite<Int32VectorImageType>(); - } - else if (dynamic_cast<UInt32VectorImageType*>(m_Image.GetPointer())) - { - SwitchVectorImageWrite<UInt32VectorImageType>(); - } - else if (dynamic_cast<FloatVectorImageType*>(m_Image.GetPointer())) - { - SwitchVectorImageWrite<FloatVectorImageType>(); - } - else if (dynamic_cast<DoubleVectorImageType*>(m_Image.GetPointer())) - { - SwitchVectorImageWrite<DoubleVectorImageType>(); - } - else if (dynamic_cast<ComplexInt16VectorImageType*>(m_Image.GetPointer())) - { - SwitchVectorImageWrite<ComplexInt16VectorImageType>(); - } - else if (dynamic_cast<ComplexInt32VectorImageType*>(m_Image.GetPointer())) - { - SwitchVectorImageWrite<ComplexInt32VectorImageType>(); - } - else if (dynamic_cast<ComplexFloatVectorImageType*>(m_Image.GetPointer())) - { - SwitchVectorImageWrite<ComplexFloatVectorImageType>(); - } - else if (dynamic_cast<ComplexDoubleVectorImageType*>(m_Image.GetPointer())) - { - SwitchVectorImageWrite<ComplexDoubleVectorImageType>(); - } - else if (dynamic_cast<UInt8RGBImageType*>(m_Image.GetPointer())) - { - SwitchRGBImageWrite<UInt8RGBImageType>(); - } - else if (dynamic_cast<UInt8RGBAImageType*>(m_Image.GetPointer())) - { - SwitchRGBAImageWrite<UInt8RGBAImageType>(); - } - else - { - itkExceptionMacro("Unknown image type"); - } - } + // Clean internal filters + m_Caster = nullptr; + m_Writer = nullptr; +} itk::ProcessObject* OutputImageParameter::GetWriter() { - int type = 1; - // 0 : image - // 1 : VectorImage - // 2 : RGBAImage - // 3 : RGBImage - itk::ProcessObject* writer = ITK_NULLPTR; - - if (dynamic_cast<UInt8RGBAImageType*> (m_Image.GetPointer())) - { - type = 2; - writer = m_RGBAUInt8Writer; - itkWarningMacro("UInt8RGBAImageType will be saved in UInt8 format."); - return writer; - } - else if (dynamic_cast<UInt8RGBImageType*> (m_Image.GetPointer())) - { - type = 3; - writer = m_RGBUInt8Writer; - itkWarningMacro("UInt8RGBImageType will be saved in UInt8 format."); - return writer; - } - - switch (GetPixelType()) - { - case ImagePixelType_uint8: - { - switch(type) - { - case 1: - writer = m_VectorUInt8Writer; - break; - case 2: - writer = m_RGBAUInt8Writer; - break; - default: - writer = m_RGBUInt8Writer; - break; - } - break; - } - case ImagePixelType_int16: - { - if (type == 1) - writer = m_VectorInt16Writer; - break; - } - case ImagePixelType_uint16: - { - if (type == 1) - writer = m_VectorUInt16Writer; - break; - } - case ImagePixelType_int32: - { - if (type == 1) - writer = m_VectorInt32Writer; - break; - } - case ImagePixelType_uint32: - { - if (type == 1) - writer = m_VectorUInt32Writer; - break; - } - case ImagePixelType_float: - { - if (type == 1) - writer = m_VectorFloatWriter; - break; - } - case ImagePixelType_double: - { - if (type == 1) - writer = m_VectorDoubleWriter; - break; - } - case ImagePixelType_cint16: - { - if( type == 1 ) - writer = m_ComplexVectorInt16Writer; - break; - } - case ImagePixelType_cint32: - { - if( type == 1 ) - writer = m_ComplexVectorInt32Writer; - break; - } - case ImagePixelType_cfloat: - { - if( type == 1 ) - writer = m_ComplexVectorFloatWriter; - break; - } - case ImagePixelType_cdouble: - { - if( type == 1 ) - writer = m_ComplexVectorDoubleWriter; - break; - } - } - if (ITK_NULLPTR == writer) - { - itkExceptionMacro("Unknown Writer type."); - } - - return writer; + return m_Writer; } ImageBaseType* @@ -651,5 +492,45 @@ OutputImageParameter::CheckFileName(bool fixMissingExtension) return ret; } +// Specialization for UInt8RGBAImageType +template <> +int +OutputImageParameter::SwitchInput(UInt8RGBAImageType *img) +{ + if (! img) return 0; + if( m_PixelType == ImagePixelType_uint8 ) + { + typename otb::ImageFileWriter<UInt8RGBAImageType>::Pointer writer = + otb::ImageFileWriter<UInt8RGBAImageType>::New(); + writer->SetFileName( this->GetFileName() ); + writer->SetInput(img); + writer->GetStreamingManager()->SetDefaultRAM(m_RAMValue); + m_Writer = writer.GetPointer(); + } + else + itkExceptionMacro("Unknown PixelType for RGBA Image. Only uint8 is supported."); + return 1; +} + +// Specialization for UInt8RGBImageType +template <> +int +OutputImageParameter::SwitchInput(UInt8RGBImageType *img) +{ + if (! img) return 0; + if( m_PixelType == ImagePixelType_uint8 ) + { + typename otb::ImageFileWriter<UInt8RGBImageType>::Pointer writer = + otb::ImageFileWriter<UInt8RGBImageType>::New(); + writer->SetFileName( this->GetFileName() ); + writer->SetInput(img); + writer->GetStreamingManager()->SetDefaultRAM(m_RAMValue); + m_Writer = writer.GetPointer(); + } + else + itkExceptionMacro("Unknown PixelType for RGB Image. Only uint8 is supported."); + return 1; +} + } } diff --git a/Modules/Wrappers/ApplicationEngine/test/CMakeLists.txt b/Modules/Wrappers/ApplicationEngine/test/CMakeLists.txt index 856191df14551aabfe7ea8982d7a70a59aa6ea56..e92b69dc0324ee9debb1e80104b846a545fa3c4d 100644 --- a/Modules/Wrappers/ApplicationEngine/test/CMakeLists.txt +++ b/Modules/Wrappers/ApplicationEngine/test/CMakeLists.txt @@ -190,6 +190,7 @@ otb_add_test(NAME owTuDocExampleStructureNew COMMAND otbApplicationEngineTestDri otbWrapperDocExampleStructureNew ) +# Warning this test require otbapp_Smoothing and otbapp_ConcatenateImages to be built otb_add_test(NAME owTvApplicationMemoryConnectTest COMMAND otbApplicationEngineTestDriver otbApplicationMemoryConnectTest $<TARGET_FILE_DIR:otbapp_Smoothing> ${INPUTDATA}/poupees.tif diff --git a/Modules/Wrappers/CommandLine/src/otbApplicationLauncherCommandLine.cxx b/Modules/Wrappers/CommandLine/src/otbApplicationLauncherCommandLine.cxx index 4c5bd8a93f43780e7d4827b260ae92e73ca9fde3..72f8b84d95c656070dbe0748572a1e140e2d78b7 100644 --- a/Modules/Wrappers/CommandLine/src/otbApplicationLauncherCommandLine.cxx +++ b/Modules/Wrappers/CommandLine/src/otbApplicationLauncherCommandLine.cxx @@ -270,7 +270,7 @@ int main(int argc, char* argv[]) if (argc < 2) { ShowUsage(argv); - return false; + return EXIT_FAILURE; } std::vector<std::string> vexp; @@ -302,7 +302,7 @@ int main(int argc, char* argv[]) if (vexp.empty()) { ShowUsage(argv); - return false; + return EXIT_FAILURE; } bool success = launcher->Load(vexp) && launcher->ExecuteAndWriteOutput(); diff --git a/Modules/Wrappers/QtWidget/include/otbWrapperQtWidgetModel.h b/Modules/Wrappers/QtWidget/include/otbWrapperQtWidgetModel.h index 2fefeda56e012736bd3f0d13ca782ddf39aa5b38..09d040a4dcd35f5ef17f1e820c30f8d8f55f3471 100644 --- a/Modules/Wrappers/QtWidget/include/otbWrapperQtWidgetModel.h +++ b/Modules/Wrappers/QtWidget/include/otbWrapperQtWidgetModel.h @@ -68,6 +68,13 @@ signals: */ void ExceptionRaised( QString what ); +public slots: + /** Ask the running application to stop */ + void Stop() + { + m_Application->Stop(); + } + protected: void run() override; @@ -139,6 +146,8 @@ signals: void UpdateGui(); + void Stop(); + protected slots: /** * \brief Slot called when execution is requested (e.g. by diff --git a/Modules/Wrappers/QtWidget/include/otbWrapperQtWidgetView.h b/Modules/Wrappers/QtWidget/include/otbWrapperQtWidgetView.h index 5a004af2dc56df5e5a94d8510df22b60d7e822a2..0f1ac8a3455cd5487c7af6b2dfd45b3c25069984 100644 --- a/Modules/Wrappers/QtWidget/include/otbWrapperQtWidgetView.h +++ b/Modules/Wrappers/QtWidget/include/otbWrapperQtWidgetView.h @@ -22,75 +22,244 @@ #define otbWrapperQtWidgetView_h #include <QtGui> -#include <QObject> #include <QShortcut> + +// +// OTB includes (sorted by alphabetic order) #ifndef Q_MOC_RUN // See: https://bugreports.qt-project.org/browse/QTBUG-22829 //tag=QT4-boost-compatibility +#include "otbWrapperApplication.h" #include "otbWrapperQtWidgetModel.h" #endif //tag=QT4-boost-compatibility +#include "OTBQtWidgetExport.h" namespace otb { namespace Wrapper { -/** \class QtWidgetView - * \brief +/*****************************************************************************/ +/* CLASS DEFINITION SECTION */ + +/** + * \class QtWidgetView * * \ingroup OTBQtWidget + * + * \brief WIP. */ -class OTBQtWidget_EXPORT QtWidgetView : public QWidget + +class OTBQtWidget_EXPORT QtWidgetView : + public QWidget { + /*-[ QOBJECT SECTION ]-----------------------------------------------------*/ + Q_OBJECT + + Q_PROPERTY( bool isClosable + READ IsClosable + WRITE SetClosable ); + + /*-[ PUBLIC SECTION ]------------------------------------------------------*/ + +// +// Public methods. public: - QtWidgetView(Application* app); + + /** + */ + static char const * const OBJECT_NAME; + + /** \brief Constructor. */ + QtWidgetView( const otb::Wrapper::Application::Pointer & otbApp, + QWidget* p =0, + Qt::WindowFlags flags =0 ); + + /** \brief Destructor. */ ~QtWidgetView() override; + /** \brief Gui Creation. */ void CreateGui(); - QtWidgetModel* GetModel() + /** \brief Model Accessor */ + inline otb::Wrapper::QtWidgetModel* GetModel() { return m_Model; } + /** + * \return The OTB-application pointer of this view. + */ + //~ otb::Wrapper::Application::ConstPointer GetApplication() const + //~ { + //~ return otb::ConstCast< otb::Wrapper::Application >( + //~ m_Application + //~ ); + //~ } + + /** + */ + inline bool IsClosable() const; + + /*-[ PUBLIC SLOTS SECTION ]------------------------------------------------*/ + +// +// Public SLOTS. public slots: - void CloseSlot(); - void UnhandledException(QString message); - void OnExceptionRaised(QString message); -private slots: - void UpdateMessageAfterExecuteClicked(); - void UpdateMessageAfterExecution(int status); - void UpdateMessageAfterApplicationReady(bool val); + /*-[ SIGNALS SECTION ]-----------------------------------------------------*/ +// +// Signals. signals: void QuitSignal(); + void ExecuteAndWriteOutput(); + void Stop(); + /*-[ PROTECTED SECTION ]---------------------------------------------------*/ + +// +// Protected methods. +protected: + + bool IsRunning(); + + virtual QWidget* CreateInputWidgets(); + + // + // QWidget overloads. + + void closeEvent( QCloseEvent * event ) override; + +// +// Protected attributes. +protected: + + /** Html section for 'Done' icon */ + std::string m_IconPathDone; + + /** Html section for 'Failed' icon */ + std::string m_IconPathFailed; + +protected slots: + + /** + */ + void OnExecButtonClicked(); + + void UnhandledException(QString message); + void OnExceptionRaised( QString what ); + + /*-[ PRIVATE SECTION ]-----------------------------------------------------*/ + +// +// Private methods. private: + QtWidgetView(const QtWidgetView&); //purposely not implemented void operator=(const QtWidgetView&); //purposely not implemented QWidget* CreateFooter(); - QWidget* CreateInputWidgets(); - QWidget* CreateDoc(); - Application::Pointer m_Application; - QtWidgetModel* m_Model; +// +// Private attributes. - QTextEdit *m_LogText; - QTabWidget *m_TabWidget; +private: + + otb::Wrapper::QtWidgetModel* m_Model; QPushButton* m_ExecButton; QPushButton* m_QuitButton; QShortcut* m_QuitShortcut; QLabel* m_Message; + QTextEdit *m_LogText; + QTabWidget *m_TabWidget; + + bool m_IsClosable : 1; + + bool m_IsRunning; + + /*-[ PRIVATE SLOTS SECTION ]-----------------------------------------------*/ + +// +// Slots. +private slots: + void UpdateMessageAfterExecution(int status); + void UpdateMessageAfterApplicationReady(bool val); + + /** + */ + inline void OnProgressReportBegin(); + + /** + */ + inline void OnProgressReportEnd( int status ); + + /** + */ + inline void SetClosable( bool ); + }; +} // end namespace 'Wrapper' +} // end namespace 'otb' + +/*****************************************************************************/ +/* INLINE SECTION */ + +namespace otb +{ + +namespace Wrapper +{ + +/*****************************************************************************/ +inline +bool +QtWidgetView +::IsClosable() const +{ + return m_IsClosable; } + +/*****************************************************************************/ +inline +void +QtWidgetView +::SetClosable( bool enabled ) +{ + m_IsClosable = enabled; + + setEnabled( true ); + + if( m_QuitButton!=NULL ) + m_QuitButton->setEnabled( m_IsClosable ); } +/*******************************************************************************/ +inline +void +QtWidgetView +::OnProgressReportBegin() +{ + SetClosable( false ); +} + +/*******************************************************************************/ +inline +void +QtWidgetView +::OnProgressReportEnd( int ) +{ + SetClosable( true ); +} + +} // end namespace 'Wrapper' + +} // end namespace 'otb' + #endif diff --git a/Modules/Wrappers/QtWidget/src/otbWrapperQtWidgetModel.cxx b/Modules/Wrappers/QtWidget/src/otbWrapperQtWidgetModel.cxx index a0d61138d72e424fd282b89c1daeb9da67bb9fe6..d19102fa8fe5bd21479caf4724941c7da5fd130a 100644 --- a/Modules/Wrappers/QtWidget/src/otbWrapperQtWidgetModel.cxx +++ b/Modules/Wrappers/QtWidget/src/otbWrapperQtWidgetModel.cxx @@ -200,7 +200,7 @@ QtWidgetModel } // launch the output image writing - AppliThread * taskAppli = new AppliThread( m_Application ); + AppliThread *taskAppli = new AppliThread( m_Application ); QObject::connect( taskAppli, @@ -225,10 +225,19 @@ QtWidgetModel SLOT( deleteLater() ) ); + QObject::connect( + this, + SIGNAL( Stop() ), + taskAppli, + SLOT( Stop() ) + ); + // Tell the Progress Reporter to begin emit SetProgressReportBegin(); taskAppli->Execute(); + + emit SetApplicationReady(true); } void @@ -322,6 +331,10 @@ AppliThread m_Application->GetLogger()->Fatal(string("Cannot open image ") + err.m_Filename + string(". ") + err.GetDescription() + string("\n")); emit ExceptionRaised( err.what() ); } + catch(itk::ProcessAborted& /*err*/) + { + m_Application->GetLogger()->Info("Processing aborted\n"); + } catch(itk::ExceptionObject& err) { m_Application->GetLogger()->Debug("Caught itk::ExceptionObject during application execution:\n"); diff --git a/Modules/Wrappers/QtWidget/src/otbWrapperQtWidgetView.cxx b/Modules/Wrappers/QtWidget/src/otbWrapperQtWidgetView.cxx index 39c9d80bd4f84283c9878a6c985404ad9e37dfaa..f689d59d81183f5c54173b29afdd2307403c1bbe 100644 --- a/Modules/Wrappers/QtWidget/src/otbWrapperQtWidgetView.cxx +++ b/Modules/Wrappers/QtWidget/src/otbWrapperQtWidgetView.cxx @@ -20,31 +20,77 @@ #include "otbWrapperQtWidgetView.h" +#include <functional> + #include "otbWrapperQtWidgetParameterGroup.h" #include "otbWrapperQtWidgetParameterFactory.h" -#include "otbWrapperQtWidgetProgressReport.h" -#include "otbWrapperOutputImageParameter.h" -#include "otbWrapperChoiceParameter.h" -#include "otbWrapperQtWidgetSimpleProgressReport.h" #include "otbWrapperApplicationHtmlDocGenerator.h" - -#include "itksys/SystemTools.hxx" - +#include "otbWrapperOutputFilenameParameter.h" +#include "otbWrapperOutputVectorDataParameter.h" +#include "otbWrapperQtWidgetSimpleProgressReport.h" namespace otb { namespace Wrapper { - -QtWidgetView::QtWidgetView(Application* app) +/* + TRANSLATOR mvd::Wrapper::QtWidgetView + + Necessary for lupdate to be aware of C++ namespaces. + + Context comment for translator. +*/ + +/*****************************************************************************/ +/* CONSTANTS */ + +char const * const +QtWidgetView +::OBJECT_NAME = "otb::Wrapper::QtWidgetView"; + +/*****************************************************************************/ +/* CLASS IMPLEMENTATION SECTION */ +/*****************************************************************************/ +QtWidgetView::QtWidgetView( const otb::Wrapper::Application::Pointer & otbApp, + QWidget* p, + Qt::WindowFlags flags ) : + QWidget( p, flags ), + m_IconPathDone(""), + m_IconPathFailed(""), + m_Model( NULL ), + m_ExecButton( NULL ), + m_QuitButton( NULL ), + m_Message( NULL ), + m_LogText( NULL ), + m_TabWidget( NULL ), + m_IsClosable( true ), + m_IsRunning(false) { - m_Model = new QtWidgetModel(app); - m_Application = app; + setObjectName( QtWidgetView::OBJECT_NAME ); + + m_Model = new otb::Wrapper::QtWidgetModel( otbApp ); + m_QuitShortcut = new QShortcut(QKeySequence("Ctrl+Q"), this); + + QObject::connect( + m_Model, SIGNAL( SetProgressReportBegin() ), + this, SLOT( OnProgressReportBegin() ) + ); + + QObject::connect( + m_Model, SIGNAL( SetProgressReportDone( int ) ), + this, SLOT( OnProgressReportEnd( int ) ) + ); + + QObject::connect( + m_Model, SIGNAL( ExceptionRaised( QString ) ), + this, SLOT( OnExceptionRaised( QString ) ) + ); } QtWidgetView::~QtWidgetView() { delete m_Model; + m_Model = NULL; } void QtWidgetView::CreateGui() @@ -53,71 +99,74 @@ void QtWidgetView::CreateGui() QVBoxLayout *mainLayout = new QVBoxLayout(); m_TabWidget = new QTabWidget(); - m_TabWidget->addTab(CreateInputWidgets(), "Parameters"); + m_TabWidget->addTab(CreateInputWidgets(), tr("Parameters")); m_LogText = new QTextEdit(); connect( m_Model->GetLogOutput(), SIGNAL(NewContentLog(QString)), m_LogText, SLOT(append(QString) ) ); - m_TabWidget->addTab(m_LogText, "Logs"); - QtWidgetProgressReport* prog = new QtWidgetProgressReport(m_Model); - prog->SetApplication(m_Application); - m_TabWidget->addTab(prog, "Progress"); - m_TabWidget->addTab(CreateDoc(), "Documentation"); + m_TabWidget->addTab(m_LogText, tr("Logs")); + m_TabWidget->addTab(CreateDoc(), tr("Documentation")); mainLayout->addWidget(m_TabWidget); - m_Message = new QLabel("<center><font color=\"#FF0000\">Select parameters</font></center>"); - connect( m_Model, SIGNAL(SetApplicationReady(bool)), this, SLOT(UpdateMessageAfterApplicationReady(bool)) ); + m_Message = new QLabel("<center><font color=\"#FF0000\">"+tr("Select parameters")+"</font></center>"); + connect( m_Model, SIGNAL(SetApplicationReady(bool)), this, SLOT( UpdateMessageAfterApplicationReady(bool)) ); connect( m_Model, SIGNAL(SetProgressReportDone(int)), this, SLOT(UpdateMessageAfterExecution(int)) ); mainLayout->addWidget(m_Message); - QtWidgetSimpleProgressReport * progressReport = new QtWidgetSimpleProgressReport(m_Model); - progressReport->SetApplication(m_Application); + otb::Wrapper::QtWidgetSimpleProgressReport * progressReport = new otb::Wrapper::QtWidgetSimpleProgressReport(m_Model); + progressReport->SetApplication(m_Model->GetApplication()); + QWidget* footer = CreateFooter(); + QHBoxLayout *footLayout = new QHBoxLayout; footLayout->addWidget(progressReport); - footLayout->addWidget(CreateFooter()); + footLayout->addWidget(footer); mainLayout->addLayout(footLayout); + footLayout->setAlignment(footer, Qt::AlignBottom); + QGroupBox *mainGroup = new QGroupBox(); mainGroup->setLayout(mainLayout); QVBoxLayout *finalLayout = new QVBoxLayout(); finalLayout->addWidget(mainGroup); - connect( m_Model, SIGNAL(ExceptionRaised(QString)), this, SLOT(OnExceptionRaised(QString)) ); - // Make the final layout to the widget this->setLayout(finalLayout); } -void QtWidgetView::UpdateMessageAfterExecuteClicked() -{ - m_Message->setText("<center><font color=\"#FF0000\">Running</font></center>"); -} - void QtWidgetView::UpdateMessageAfterExecution(int status) { if (status >= 0) { - m_Message->setText("<center><font color=\"#00A000\">Done</font></center>"); + m_Message->setText("<center>"+QString(m_IconPathDone.c_str())+ + "<font color=\"#00A000\">"+tr("Done")+"</font></center>"); } else { - m_Message->setText("<center><font color=\"#FF0000\">Failed</font></center>"); + m_Message->setText("<center>"+QString(m_IconPathFailed.c_str())+ + "<font color=\"#FF0000\">"+tr("Failed")+"</font></center>"); } + m_ExecButton->setText(QObject::tr("Execute")); + m_IsRunning = false; } void QtWidgetView::UpdateMessageAfterApplicationReady( bool val ) { - if(val == true) - m_Message->setText("<center><font color=\"#00A000\">Ready to run</font></center>"); - else - m_Message->setText("<center><font color=\"#FF0000\">Select parameters</font></center>"); + if(!m_IsRunning) + { + if(val == true) + m_Message->setText("<center><font color=\"#00A000\">"+tr("Ready to run")+"</font></center>"); + else + m_Message->setText("<center><font color=\"#FF0000\">"+tr("Select parameters")+"</font></center>"); + } } QWidget* QtWidgetView::CreateInputWidgets() { QScrollArea *scrollArea = new QScrollArea; - // Put the main group inside a scroll area - scrollArea->setWidget(QtWidgetParameterFactory::CreateQtWidget(m_Model->GetApplication()->GetParameterList(), m_Model)); + + scrollArea->setWidget( otb::Wrapper::QtWidgetParameterFactory::CreateQtWidget( + m_Model->GetApplication()->GetParameterList(), + m_Model)); scrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded); scrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); scrollArea->setWidgetResizable(true); @@ -131,7 +180,7 @@ QWidget* QtWidgetView::CreateFooter() // an HLayout with two buttons : Execute and Quit QGroupBox *footerGroup = new QGroupBox; QHBoxLayout *footerLayout = new QHBoxLayout; - + footerGroup->setFixedHeight(40); footerGroup->setContentsMargins(0, 0, 0, 0); footerLayout->setContentsMargins(5, 5, 5, 5); @@ -140,17 +189,17 @@ QWidget* QtWidgetView::CreateFooter() m_ExecButton->setDefault(true); m_ExecButton->setEnabled(false); m_ExecButton->setText(QObject::tr("Execute")); - connect( m_ExecButton, SIGNAL(clicked()), m_Model, SLOT(ExecuteAndWriteOutputSlot() ) ); - connect( m_Model, SIGNAL(SetApplicationReady(bool)), m_ExecButton, SLOT(setEnabled(bool)) ); - connect( m_ExecButton, SIGNAL(clicked()), this, SLOT(UpdateMessageAfterExecuteClicked() ) ); + connect( m_Model, SIGNAL( SetApplicationReady( bool ) ), m_ExecButton, SLOT( setEnabled( bool ) )); + QObject::connect( m_ExecButton, SIGNAL( clicked() ), this, SLOT( OnExecButtonClicked() )); + QObject::connect( this, SIGNAL( ExecuteAndWriteOutput() ), m_Model, SLOT( ExecuteAndWriteOutputSlot() )); + QObject::connect( this, SIGNAL( Stop() ), m_Model, SIGNAL( Stop() )); m_QuitButton = new QPushButton(footerGroup); m_QuitButton->setText(QObject::tr("Quit")); - connect( m_QuitButton, SIGNAL(clicked()), this, SLOT(CloseSlot()) ); + connect(m_QuitButton, SIGNAL( clicked() ), this, SLOT( close() )); // Add Ctrl-Q shortcut to quit - m_QuitShortcut = new QShortcut(QKeySequence("Ctrl+Q"), this); - connect( m_QuitShortcut, SIGNAL(activated()), this, SLOT(CloseSlot()) ); + connect( m_QuitShortcut, SIGNAL(activated()), this, SLOT(close()) ); // Put the buttons on the right footerLayout->addStretch(); @@ -170,7 +219,7 @@ QWidget* QtWidgetView::CreateDoc() QTextDocument * doc = new QTextDocument(); std::string docContain; - ApplicationHtmlDocGenerator::GenerateDoc( m_Application, docContain, true); + otb::Wrapper::ApplicationHtmlDocGenerator::GenerateDoc( GetModel()->GetApplication(), docContain); doc->setHtml(docContain.c_str()); @@ -179,13 +228,49 @@ QWidget* QtWidgetView::CreateDoc() return text; } -void QtWidgetView::CloseSlot() +void QtWidgetView::closeEvent( QCloseEvent * e ) { - // Close the widget - this->close(); + assert( e!=NULL ); + + if( !IsClosable() ) + { + assert( GetModel()->GetApplication() ); + + QMessageBox::warning( + this, + tr( "Warning!" ), + tr( "OTB-Application '%1' cannot be closed while running!") + .arg( GetModel()->GetApplication()->GetDocName() ) + ); + + e->ignore(); + + return; + } + + QWidget::closeEvent( e ); - // Emit a signal to close any widget that this gui belonging to emit QuitSignal(); + + deleteLater(); +} + +void +QtWidgetView +::OnExecButtonClicked() +{ + if (m_IsRunning) + { + m_Message->setText("<center><font color=\"#FF0000\">"+tr("Cancelling")+"...</font></center>"); + emit Stop(); + } + else + { + m_IsRunning = true; + m_Message->setText("<center><font color=\"#FF0000\">"+tr("Running")+"</font></center>"); + m_ExecButton->setText(QObject::tr("Cancel")); + emit ExecuteAndWriteOutput(); + } } void QtWidgetView::UnhandledException(QString message) @@ -194,10 +279,16 @@ void QtWidgetView::UnhandledException(QString message) m_LogText->append(message); } -void QtWidgetView::OnExceptionRaised(QString /*message*/) +void QtWidgetView::OnExceptionRaised( QString /*message*/) { m_TabWidget->setCurrentIndex(1); } +bool QtWidgetView::IsRunning() +{ + return m_IsRunning; } -} + +} // end of namespace Wrapper +} // end of namespace otb + diff --git a/Modules/Wrappers/SWIG/src/otbApplication.i b/Modules/Wrappers/SWIG/src/otbApplication.i index c8b97c12e217b970a514dea5c1a248d6a89bb863..665b689fd17300b423490b77bace6c518edc5164 100644 --- a/Modules/Wrappers/SWIG/src/otbApplication.i +++ b/Modules/Wrappers/SWIG/src/otbApplication.i @@ -287,6 +287,8 @@ public: std::string GetProgressDescription() const; + void FreeRessources(); + itkSetStringMacro(DocName); itkGetStringMacro(DocName); itkSetStringMacro(DocLongDescription); diff --git a/Packaging/README.md b/Packaging/README.md index e845566c06f9bf557d35e8292c37cf05d97a9769..df83d58b21e469b8a3723b04c7efc6702ae7c32c 100644 --- a/Packaging/README.md +++ b/Packaging/README.md @@ -1 +1 @@ -README +OTB recipes to build standalone binary packages for Windows/Linux/Mac OS X diff --git a/Packaging/installer_files.cmake b/Packaging/installer_files.cmake index 941f7f8cdfbf8caa17e8f52e3553a65bd25b6ab9..b864b74b7eacbfff0d65cb5ffbf94a26f1a7deff 100644 --- a/Packaging/installer_files.cmake +++ b/Packaging/installer_files.cmake @@ -22,11 +22,11 @@ macro(installer_files) #configure README from rst docs set(RSTDOC_DIR "${PACKAGE_OTB_SRC_DIR}/Documentation/Cookbook/rst") if(APPLE) - set(README_FILE ${RSTDOC_DIR}/Installation_Macx.txt) + set(README_FILE ${RSTDOC_DIR}/Installation_Macos.rst) elseif(LINUX) #not osx - set(README_FILE ${RSTDOC_DIR}/Installation_Linux.txt) + set(README_FILE ${RSTDOC_DIR}/Installation_Linux.rst) elseif(WIN32) #windows - set(README_FILE ${RSTDOC_DIR}/Installation_Windows.txt) + set(README_FILE ${RSTDOC_DIR}/Installation_Windows.rst) endif() configure_file("${README_FILE}" ${CMAKE_BINARY_DIR}/README ) install(FILES ${CMAKE_BINARY_DIR}/README DESTINATION ${PKG_STAGE_DIR} )