diff --git a/Modules/Applications/AppSegmentation/app/CMakeLists.txt b/Modules/Applications/AppSegmentation/app/CMakeLists.txt index 097d9971f16a23b266e64e28469fca2444e15f0f..f55ae3d7f91d607359b5024a17f89136428edf76 100644 --- a/Modules/Applications/AppSegmentation/app/CMakeLists.txt +++ b/Modules/Applications/AppSegmentation/app/CMakeLists.txt @@ -75,3 +75,8 @@ otb_create_application( NAME MeanShiftSmoothing SOURCES otbMeanShiftSmoothing.cxx LINK_LIBRARIES ${${otb-module}_LIBRARIES}) + +otb_create_application( + NAME LargeScaleMeanShift + SOURCES otbLargeScaleMeanShift.cxx + LINK_LIBRARIES ${${otb-module}_LIBRARIES}) diff --git a/Modules/Applications/AppSegmentation/app/otbLargeScaleMeanShift.cxx b/Modules/Applications/AppSegmentation/app/otbLargeScaleMeanShift.cxx new file mode 100644 index 0000000000000000000000000000000000000000..d0dfadb1a535ce0c3acafecb650258c3c4d855e1 --- /dev/null +++ b/Modules/Applications/AppSegmentation/app/otbLargeScaleMeanShift.cxx @@ -0,0 +1,233 @@ +/* + * 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 "otbWrapperCompositeApplication.h" +#include "otbWrapperApplicationFactory.h" + +namespace otb +{ +namespace Wrapper +{ + +/** + * \class LargeScaleMeanShift + * + * \brief All-in-one application for the LSMS framework + * + * This application gathers the 4 steps of the large-scale MeanShift + * segmentation framework. + * + */ +class LargeScaleMeanShift : public CompositeApplication +{ +public: + /** Standard class typedefs. */ + typedef LargeScaleMeanShift Self; + typedef CompositeApplication Superclass; + typedef itk::SmartPointer<Self> Pointer; + typedef itk::SmartPointer<const Self> ConstPointer; + +/** Standard macro */ + itkNewMacro(Self); + + itkTypeMacro(LargeScaleMeanShift, otb::CompositeApplication); + +private: + void DoInit() ITK_OVERRIDE + { + SetName("LargeScaleMeanShift"); + SetDescription("Large-scale segmentation using MeanShift"); + + // Documentation + SetDocName("Large-Scale MeanShift"); + SetDocLongDescription("This application chains together the 4 steps of the " + "MeanShit framework, that is the MeanShiftSmoothing [1], the " + "LSMSSegmentation [2], the LSMSSmallRegionsMerging [3] and the " + "LSMSVectorization [4].\n\n" + "This application can be a preliminary step for an object-based analysis.\n\n" + "It generates a vector data file containing the regions extracted with " + "the MeanShift algorithm. The spatial and range radius parameters allow " + "to adapt the sensitivity of the algorithm depending on the image dynamic " + "and resolution. There is a step to remove small regions whose size " + "(in pixels) is less than the given 'minsize' parameter. These regions " + "are merged to a similar neighbor region. In the output vectors, there " + "are additional fields to describe each region. In particular the mean " + "and standard deviation (for each band) is computed for each region " + "using the input image as support. If an optional 'imfield' image is " + "given, it will be used as support image instead." + ); + SetDocLimitations("None"); + SetDocAuthors("OTB-Team"); + SetDocSeeAlso("[1] MeanShiftSmoothing\n" + "[2] LSMSSegmentation\n" + "[3] LSMSSmallRegionsMerging\n" + "[4] LSMSVectorization"); + + AddDocTag(Tags::Segmentation); + AddDocTag("LSMS"); + + ClearApplications(); + AddApplication("MeanShiftSmoothing", "smoothing", "Smoothing step"); + AddApplication("LSMSSegmentation", "segmentation", "Segmentation step"); + AddApplication("LSMSSmallRegionsMerging", "merging", "Small region merging step"); + AddApplication("LSMSVectorization", "vectorization", "Vectorization step"); + + ShareParameter("in","smoothing.in"); + ShareParameter("spatialr","smoothing.spatialr"); + ShareParameter("ranger","smoothing.ranger"); + ShareParameter("minsize","merging.minsize"); + + ShareParameter("tilesizex","segmentation.tilesizex"); + ShareParameter("tilesizey","segmentation.tilesizey"); + + AddParameter(ParameterType_Choice, "mode","Output mode"); + SetParameterDescription("mode", "Type of segmented output"); + + AddChoice("mode.vector", "Segmentation as vector output"); + SetParameterDescription("mode.vector","In this mode, the application will " + "produce a vector file or database and compute field values for each " + "region"); + + AddParameter(ParameterType_InputImage, "mode.vector.imfield", "Support image for field computation"); + SetParameterDescription( "mode.vector.imfield", "This is an optional support image " + "that can be used to compute field values in each region. Otherwise, the " + "input image is used as support." ); + MandatoryOff("mode.vector.imfield"); + + ShareParameter("mode.vector.out","vectorization.out"); + + AddChoice("mode.raster", "Standard segmentation with labeled raster output"); + SetParameterDescription("mode.raster","In this mode, the application will produce a standard labeled raster."); + + ShareParameter("mode.raster.out","merging.out", + "The output raster image", + "It corresponds to the output of the small region merging step."); + + AddParameter( ParameterType_Empty, "cleanup", "Temporary files cleaning" ); + EnableParameter( "cleanup" ); + SetParameterDescription( "cleanup", + "If activated, the application will try to clean all temporary files it created" ); + MandatoryOff( "cleanup" ); + + // Setup RAM + ShareParameter("ram","smoothing.ram"); + Connect("merging.ram","smoothing.ram"); + Connect("vectorization.ram","smoothing.ram"); + + Connect("merging.tilesizex","segmentation.tilesizex"); + Connect("merging.tilesizey","segmentation.tilesizey"); + Connect("vectorization.tilesizex","segmentation.tilesizex"); + Connect("vectorization.tilesizey","segmentation.tilesizey"); + + // TODO : this is not exactly true, we used to choose the smoothed image instead + Connect("merging.in","smoothing.in"); + + // Setup constant parameters + GetInternalApplication("smoothing")->SetParameterString("foutpos","foo"); + GetInternalApplication("smoothing")->EnableParameter("foutpos"); + + // Doc example parameter settings + SetDocExampleParameterValue("in", "QB_1_ortho.tif"); + SetDocExampleParameterValue("spatialr", "4"); + SetDocExampleParameterValue("ranger", "80"); + SetDocExampleParameterValue("minsize", "16"); + SetDocExampleParameterValue("mode.vector.out", "regions.shp"); + + SetOfficialDocLink(); + } + + void DoUpdateParameters() ITK_OVERRIDE + {} + + void DoExecute() ITK_OVERRIDE + { + bool isVector(GetParameterString("mode") == "vector"); + std::string outPath(isVector ? + GetParameterString("mode.vector.out"): + GetParameterString("mode.raster.out")); + std::vector<std::string> tmpFilenames; + tmpFilenames.push_back(outPath+std::string("_labelmap.tif")); + tmpFilenames.push_back(outPath+std::string("_labelmap.geom")); + ExecuteInternal("smoothing"); + // in-memory connexion here (saves 1 additional update for foutpos) + GetInternalApplication("segmentation")->SetParameterInputImage("in", + GetInternalApplication("smoothing")->GetParameterOutputImage("fout")); + GetInternalApplication("segmentation")->SetParameterInputImage("inpos", + GetInternalApplication("smoothing")->GetParameterOutputImage("foutpos")); + // temporary file output here + GetInternalApplication("segmentation")->SetParameterString("out", + tmpFilenames[0]); + // take half of previous radii + GetInternalApplication("segmentation")->SetParameterFloat("spatialr", + 0.5 * (double)GetInternalApplication("smoothing")->GetParameterInt("spatialr")); + GetInternalApplication("segmentation")->SetParameterFloat("ranger", + 0.5 * GetInternalApplication("smoothing")->GetParameterFloat("ranger")); + GetInternalApplication("segmentation")->ExecuteAndWriteOutput(); + + GetInternalApplication("merging")->SetParameterString("inseg", + tmpFilenames[0]); + EnableParameter("mode.raster.out"); + if (isVector) + { + tmpFilenames.push_back(outPath+std::string("_labelmap_merged.tif")); + tmpFilenames.push_back(outPath+std::string("_labelmap_merged.geom")); + GetInternalApplication("merging")->SetParameterString("out", + tmpFilenames[2]); + GetInternalApplication("merging")->ExecuteAndWriteOutput(); + if (IsParameterEnabled("mode.vector.imfield") && + HasValue("mode.vector.imfield")) + { + GetInternalApplication("vectorization")->SetParameterString("in", + GetParameterString("mode.vector.imfield")); + } + else + { + GetInternalApplication("vectorization")->SetParameterString("in", + GetParameterString("in")); + } + GetInternalApplication("vectorization")->SetParameterString("inseg", + tmpFilenames[2]); + ExecuteInternal("vectorization"); + } + else + { + GetInternalApplication("merging")->ExecuteAndWriteOutput(); + } + DisableParameter("mode.raster.out"); + + if( IsParameterEnabled( "cleanup" ) ) + { + otbAppLogINFO( <<"Final clean-up ..." ); + for (unsigned int i=0 ; i<tmpFilenames.size() ; ++i) + { + if(itksys::SystemTools::FileExists(tmpFilenames[i].c_str())) + { + itksys::SystemTools::RemoveFile(tmpFilenames[i].c_str()); + } + } + } + } + +}; + +} // end of namespace Wrapper +} // end of namespace otb + +OTB_APPLICATION_EXPORT(otb::Wrapper::LargeScaleMeanShift) diff --git a/Modules/Applications/AppSegmentation/test/CMakeLists.txt b/Modules/Applications/AppSegmentation/test/CMakeLists.txt index 090a2562ef000e91787672ced1a327d2cf4cd9bb..e04c3bbc1e328924e605ab1fd77fce88dcf800dc 100644 --- a/Modules/Applications/AppSegmentation/test/CMakeLists.txt +++ b/Modules/Applications/AppSegmentation/test/CMakeLists.txt @@ -293,3 +293,18 @@ otb_test_application(NAME apTvSeHooverCompareSegmentationTest --ignore-lines-with 2 outgt: outms: ) +#----------- LargeScaleMeanShift TESTS ---------------- +otb_test_application(NAME apTvSeLargeScaleMeanShiftTest + APP LargeScaleMeanShift + OPTIONS -in ${EXAMPLEDATA}/QB_1_ortho.tif + -spatialr 3 + -ranger 80 + -minsize 16 + -tilesizex 100 + -tilesizey 100 + -mode vector + -mode.vector.out ${TEMP}/apTvSeLargeScaleMeanShiftTestOut.shp + VALID --compare-ogr ${NOTOL} + ${BASELINE_FILES}/apTvSeLargeScaleMeanShiftTestOut.shp + ${TEMP}/apTvSeLargeScaleMeanShiftTestOut.shp + ) diff --git a/Modules/Core/Common/include/otbUnaryFunctorImageFilter.h b/Modules/Core/Common/include/otbUnaryFunctorImageFilter.h index 798d8fe6c5aaa75994a24a3718ca84e529be51a4..2217de58baf54d756792382afc2c6080ccda5ddd 100644 --- a/Modules/Core/Common/include/otbUnaryFunctorImageFilter.h +++ b/Modules/Core/Common/include/otbUnaryFunctorImageFilter.h @@ -32,7 +32,7 @@ namespace otb * Add the capability to change the number of channel when operation on * VectorImage compared to the itk::UnaryFunctorImageFilter * - * The number of channel is provided by the functor: TFunction::OutputSize. If + * The number of channel is provided by the functor: TFunction::GetOutputSize. If * this number is lower or equal to zero, the behavior of the itk::UnaryFunctorImageFilter * remains unchanged. * diff --git a/Modules/Feature/Descriptors/test/otbImageToFastSIFTKeyPointSetFilterOutputInterestPointAscii.cxx b/Modules/Feature/Descriptors/test/otbImageToFastSIFTKeyPointSetFilterOutputInterestPointAscii.cxx index 4a2a416e5f9903b969816ecc22b52a059c419d90..d5f0f32747e42ebc0a7eb6a119a1d12628b34f2b 100644 --- a/Modules/Feature/Descriptors/test/otbImageToFastSIFTKeyPointSetFilterOutputInterestPointAscii.cxx +++ b/Modules/Feature/Descriptors/test/otbImageToFastSIFTKeyPointSetFilterOutputInterestPointAscii.cxx @@ -36,9 +36,9 @@ bool CMP(std::vector<float> a, std::vector<float> b) return lexicographical_compare(a.begin(), a.begin() + 2, b.begin(), b.begin() + 2); } -int otbImageToFastSIFTKeyPointSetFilterOutputInterestPointAscii(int itkNotUsed(argc), char * argv[]) +int +otbImageToFastSIFTKeyPointSetFilterOutputInterestPointAscii( int itkNotUsed( argc ), char * argv[] ) { - const char * infname = argv[1]; const char * outfname = argv[2]; @@ -59,7 +59,7 @@ int otbImageToFastSIFTKeyPointSetFilterOutputInterestPointAscii(int itkNotUsed(a typedef PointSetType::PointDataContainer PointDataContainerType; typedef PointDataContainerType::Iterator PointDataIteratorType; - typedef std::vector<float> siftDataVector; + typedef std::vector< RealType > siftDataVector; typedef std::vector<siftDataVector> ImageDataType; //Kind of PointSet with vectors // Instantiating object @@ -76,10 +76,29 @@ int otbImageToFastSIFTKeyPointSetFilterOutputInterestPointAscii(int itkNotUsed(a PointsIteratorType pIt = filter->GetOutput()->GetPoints()->Begin(); PointDataIteratorType pDataIt = filter->GetOutput()->GetPointData()->Begin(); + assert( + filter->GetOutput()->GetPoints()->Size() == + filter->GetOutput()->GetPointData()->Size() ); + std::ofstream outfile(outfname); outfile << "Number of scales: " << scales << std::endl; - outfile << "Number of SIFT key points: " << filter->GetOutput()->GetNumberOfPoints() << std::endl; + + outfile << "Number of SIFT key points: " + << filter->GetOutput()->GetNumberOfPoints() + << std::endl; + + outfile << "Number of points: " + << filter->GetOutput()->GetPoints()->Size() + << std::endl; + + outfile << "Number of points data: " + << filter->GetOutput()->GetPointData()->Size() + << std::endl; + + if( filter->GetOutput()->GetPoints()->Size() != + filter->GetOutput()->GetPointData()->Size() ) + return EXIT_FAILURE; // Copy the PointSet to std::vector< std::vector > while (pIt != filter->GetOutput()->GetPoints()->End()) diff --git a/Modules/Filtering/ImageManipulation/include/otbBinaryFunctorImageFilter.h b/Modules/Filtering/ImageManipulation/include/otbBinaryFunctorImageFilter.h new file mode 100644 index 0000000000000000000000000000000000000000..e0e246aee5f7e787c0d4b994874bd42f75c01d06 --- /dev/null +++ b/Modules/Filtering/ImageManipulation/include/otbBinaryFunctorImageFilter.h @@ -0,0 +1,84 @@ +/*========================================================================= + + Program: ORFEO Toolbox + Language: C++ + Date: $Date$ + Version: $Revision$ + + + Copyright (c) Centre National d'Etudes Spatiales. All rights reserved. + See OTBCopyright.txt for details. + + + This software is distributed WITHOUT ANY WARRANTY; without even + the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + PURPOSE. See the above copyright notices for more information. + +=========================================================================*/ +#ifndef otbBinaryFunctorImageFilter_h +#define otbBinaryFunctorImageFilter_h + +#include "itkBinaryFunctorImageFilter.h" + +namespace otb +{ +/** + * \class BinaryFunctorImageFilter + * \brief Implements pixel-wise generic operation on two images. + * + * Add the capability to change the number of channel when operation on + * VectorImage compared to the itk::BinaryFunctorImageFilter + * + * The number of channel is provided by the functor: TFunction::GetOutputSize. If + * this number is lower or equal to zero, the behavior of the itk::BinaryFunctorImageFilter + * remains unchanged. + * + * \sa itk::BinaryFunctorImageFilter + * + * \ingroup OTBImageManipulation + */ +template <class TInputImage1, class TInputImage2, class TOutputImage, class TFunction> +class ITK_EXPORT BinaryFunctorImageFilter : public itk::BinaryFunctorImageFilter<TInputImage1, TInputImage2, TOutputImage, TFunction> +{ +public: + /** Standard class typedefs. */ + typedef BinaryFunctorImageFilter Self; + typedef itk::BinaryFunctorImageFilter<TInputImage1, TInputImage2, TOutputImage, TFunction> Superclass; + typedef itk::SmartPointer<Self> Pointer; + typedef itk::SmartPointer<const Self> ConstPointer; + + /** Method for creation through the object factory. */ + itkNewMacro(Self); + + /** Run-time type information (and related methods). */ + itkTypeMacro(BinaryFunctorImageFilter, itk::BinaryFunctorImageFilter); + +protected: + BinaryFunctorImageFilter() {}; + ~BinaryFunctorImageFilter() ITK_OVERRIDE {} + + /** BinaryFunctorImageFilter can produce an image which has a different number of bands + * than its input image. As such, BinaryFunctorImageFilter + * needs to provide an implementation for + * GenerateOutputInformation() in order to inform the pipeline + * execution model. The original documentation of this method is + * below. + * + * \sa ProcessObject::GenerateOutputInformaton() */ + void GenerateOutputInformation() ITK_OVERRIDE + { + Superclass::GenerateOutputInformation(); + typename Superclass::OutputImagePointer outputPtr = this->GetOutput(); + outputPtr->SetNumberOfComponentsPerPixel( // propagate vector length info + this->GetFunctor().GetOutputSize()); + } + +private: + BinaryFunctorImageFilter(const Self &); //purposely not implemented + void operator =(const Self&); //purposely not implemented + +}; + +} // end namespace otb + +#endif diff --git a/Modules/Filtering/ImageManipulation/include/otbTernaryFunctorImageFilter.h b/Modules/Filtering/ImageManipulation/include/otbTernaryFunctorImageFilter.h new file mode 100644 index 0000000000000000000000000000000000000000..3e6d3f540505708893faa290c511633e55d4b462 --- /dev/null +++ b/Modules/Filtering/ImageManipulation/include/otbTernaryFunctorImageFilter.h @@ -0,0 +1,84 @@ +/*========================================================================= + + Program: ORFEO Toolbox + Language: C++ + Date: $Date$ + Version: $Revision$ + + + Copyright (c) Centre National d'Etudes Spatiales. All rights reserved. + See OTBCopyright.txt for details. + + + This software is distributed WITHOUT ANY WARRANTY; without even + the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + PURPOSE. See the above copyright notices for more information. + +=========================================================================*/ +#ifndef otbTernaryFunctorImageFilter_h +#define otbTernaryFunctorImageFilter_h + +#include "itkTernaryFunctorImageFilter.h" + +namespace otb +{ +/** + * \class TernaryFunctorImageFilter + * \brief Implements pixel-wise generic operation on three images. + * + * Add the capability to change the number of channel when operation on + * VectorImage compared to the itk::TernaryFunctorImageFilter + * + * The number of channel is provided by the functor: TFunction::GetOutputSize. If + * this number is lower or equal to zero, the behavior of the itk::TernaryFunctorImageFilter + * remains unchanged. + * + * \sa itk::TernaryFunctorImageFilter + * + * \ingroup OTBImageManipulation + */ +template <class TInputImage1, class TInputImage2, class TInputImage3, class TOutputImage, class TFunction> +class ITK_EXPORT TernaryFunctorImageFilter : public itk::TernaryFunctorImageFilter<TInputImage1, TInputImage2, TInputImage3, TOutputImage, TFunction> +{ +public: + /** Standard class typedefs. */ + typedef TernaryFunctorImageFilter Self; + typedef itk::TernaryFunctorImageFilter<TInputImage1, TInputImage2, TInputImage3, TOutputImage, TFunction> Superclass; + typedef itk::SmartPointer<Self> Pointer; + typedef itk::SmartPointer<const Self> ConstPointer; + + /** Method for creation through the object factory. */ + itkNewMacro(Self); + + /** Run-time type information (and related methods). */ + itkTypeMacro(TernaryFunctorImageFilter, itk::TernaryFunctorImageFilter); + +protected: + TernaryFunctorImageFilter() {}; + ~TernaryFunctorImageFilter() ITK_OVERRIDE {} + + /** TernaryFunctorImageFilter can produce an image which has a different number of bands + * than its input image. As such, TernaryFunctorImageFilter + * needs to provide an implementation for + * GenerateOutputInformation() in order to inform the pipeline + * execution model. The original documentation of this method is + * below. + * + * \sa ProcessObject::GenerateOutputInformaton() */ + void GenerateOutputInformation() ITK_OVERRIDE + { + Superclass::GenerateOutputInformation(); + typename Superclass::OutputImagePointer outputPtr = this->GetOutput(); + outputPtr->SetNumberOfComponentsPerPixel( // propagate vector length info + this->GetFunctor().GetOutputSize()); + } + +private: + TernaryFunctorImageFilter(const Self &); //purposely not implemented + void operator =(const Self&); //purposely not implemented + +}; + +} // end namespace otb + +#endif diff --git a/Modules/Wrappers/ApplicationEngine/include/otbWrapperApplication.h b/Modules/Wrappers/ApplicationEngine/include/otbWrapperApplication.h index 3d944f931ca7f268d8b0b07389f97bf5cf289af6..aa0cdd076ad93109e8acd054fc920c4a3892304e 100644 --- a/Modules/Wrappers/ApplicationEngine/include/otbWrapperApplication.h +++ b/Modules/Wrappers/ApplicationEngine/include/otbWrapperApplication.h @@ -167,7 +167,7 @@ public: /* Get the internal application parameter specified * * WARNING: this method may disappear from the API */ - const Parameter* GetParameterByKey(std::string parameter) const; + const Parameter* GetParameterByKey(std::string parameter, bool follow=true) const; /* Returns the description of a parameter */ std::string GetParameterName(std::string paramKey); @@ -284,8 +284,21 @@ public: void SetParameterEmpty(std::string parameter, bool value, bool hasUserValueFlag = true); + /** Checks if the application is ready to be executed. It checks that there + * is no parameter missing + */ bool IsApplicationReady(); + /** Checks if a parameter 'key' is missing. + * + * A parameter is missing when all the following conditions are true : + * - the parameter is mandatory + * - the parameter has Role_Input + * - the parameter is not a group + * - the parameter has no value + * - the parameter ancestors are mandatory or enabled. + */ + bool IsParameterMissing(const std::string &key) const; /* Set an default integer value, must used in the * DoInit when setting a value by default @@ -695,7 +708,10 @@ public: */ ComplexImagePixelType GetParameterComplexOutputImagePixelType(std::string parameter); - otb::Logger* GetLogger(); + otb::Logger* GetLogger() const; + + /** Sets the logger instance of the application (use with caution) */ + void SetLogger(otb::Logger *logger); itk::ProcessObject* GetProgressSource() const; diff --git a/Modules/Wrappers/ApplicationEngine/include/otbWrapperCompositeApplication.h b/Modules/Wrappers/ApplicationEngine/include/otbWrapperCompositeApplication.h index 3dae3b16346ec255054ddf14c6d5cfc711fe7215..859e6a81e733f238b81e51a80bf8bf665d72d653 100644 --- a/Modules/Wrappers/ApplicationEngine/include/otbWrapperCompositeApplication.h +++ b/Modules/Wrappers/ApplicationEngine/include/otbWrapperCompositeApplication.h @@ -149,10 +149,6 @@ private: InternalAppContainer m_AppContainer; - itk::StdStreamLogOutput::Pointer m_LogOutput; - - std::ostringstream m_Oss; - AddProcessCommandType::Pointer m_AddProcessCommand; }; diff --git a/Modules/Wrappers/ApplicationEngine/src/otbWrapperApplication.cxx b/Modules/Wrappers/ApplicationEngine/src/otbWrapperApplication.cxx index 7a7b45bb34e6b7b329b8ce174bd916d9a2f24c4d..178a8d55608661aa4253607f272cd54b5bddb6ad 100644 --- a/Modules/Wrappers/ApplicationEngine/src/otbWrapperApplication.cxx +++ b/Modules/Wrappers/ApplicationEngine/src/otbWrapperApplication.cxx @@ -36,6 +36,7 @@ #include "otbWrapperInputProcessXMLParameter.h" #include "otbWrapperRAMParameter.h" #include "otbWrapperProxyParameter.h" +#include "otbWrapperParameterKey.h" #include "otbWrapperAddProcessToWatchEvent.h" @@ -74,11 +75,19 @@ Application::~Application() { } -otb::Logger* Application::GetLogger() +otb::Logger* Application::GetLogger() const { return m_Logger; } +void Application::SetLogger(otb::Logger *logger) +{ + if (m_Logger != logger) + { + m_Logger = logger; + } +} + std::vector<std::string> Application::GetParametersKeys(bool recursive) { @@ -297,11 +306,11 @@ void Application::SetParameterUserValue(std::string paramKey, bool value) GetParameterByKey(paramKey)->SetUserValue(value); } -const Parameter* Application::GetParameterByKey(std::string name) const +const Parameter* Application::GetParameterByKey(std::string name, bool follow) const { // GetParameterList is non const... Application* _this = const_cast<Application*>(this); - return _this->GetParameterByKey(name); + return _this->GetParameterByKey(name,follow); } void Application::Init() @@ -1599,57 +1608,50 @@ Application::IsApplicationReady() it != paramList.end(); ++it) { - // Check all Input Parameters with Input Role - if (GetParameterByKey(*it)->GetRole() == Role_Input) + // Check all parameters + if (IsParameterMissing(*it)) { - // When a parameter is mandatory : - // return false when does not have value and: - // - The param is root - // - The param is not root and belonging to a Mandatory Group - // which is activated - if ( !this->HasValue(*it) && IsMandatory(*it) ) - { - if( GetParameterByKey(*it)->IsRoot() ) - { - otbDebugMacro("MISSING : "<< (*it).c_str() << " ( Is Root)"); - return false; - } - else - { - // check if the parameter is linked to a root parameter with a chain of active parameters - Parameter* currentParam = GetParameterByKey(*it)->GetRoot(); - if (currentParam->IsRoot()) - { - otbDebugMacro("MISSING : "<< (*it).c_str() << " ( Is Level 1)"); - return false; - } - - int level = 1; - - while (!currentParam->IsRoot()) - { - if (!currentParam->GetActive()) - { - // the missing parameter is not on an active branch : we can ignore it - break; - } - currentParam = currentParam->GetRoot(); - - level++; + ready = false; + break; + } + } + return ready; +} - if (currentParam->IsRoot()) - { - // the missing parameter is on an active branch : we need it - otbDebugMacro("MISSING : "<< (*it).c_str() << " ( Is Level "<< level<<")"); - return false; - } - } - } +bool +Application::IsParameterMissing(const std::string &key) const +{ + bool ret(false); + const Parameter* param = GetParameterByKey(key); + if (param->GetRole() == Role_Input && + GetParameterType(key) != ParameterType_Group && + param->GetMandatory() && + !param->HasValue()) + { + ret = true; + ParameterKey paramKey(key); + std::vector<std::string> split = paramKey.Split(); + std::string currentRoot(key); + unsigned int level = 1; + while (level < split.size()) + { + currentRoot.resize(currentRoot.find_last_of(".")); + param = GetParameterByKey(currentRoot); + if (!param->GetActive() && !param->GetMandatory()) + { + // the missing parameter is not on an active branch : we can ignore it + ret = false; + break; } + level++; + } + if (ret) + { + // the missing parameter is on an active branch : we need it + otbDebugMacro("MISSING : "<< key << " (Level "<< split.size()<<")"); } } - - return ready; + return ret; } void diff --git a/Modules/Wrappers/ApplicationEngine/src/otbWrapperChoiceParameter.cxx b/Modules/Wrappers/ApplicationEngine/src/otbWrapperChoiceParameter.cxx index 1ebb88d529cb5648416a466f98dc4a1dec8ec527..2f64625f58a08987b4db3c4940c8d1b978d2e3f6 100644 --- a/Modules/Wrappers/ApplicationEngine/src/otbWrapperChoiceParameter.cxx +++ b/Modules/Wrappers/ApplicationEngine/src/otbWrapperChoiceParameter.cxx @@ -44,7 +44,7 @@ ChoiceParameter::AddChoice( std::string choicekey, std::string choiceName ) choice.m_AssociatedParameter->SetName(choiceName); choice.m_AssociatedParameter->SetRoot(this); choice.m_AssociatedParameter->SetKey(choicekey); - + choice.m_AssociatedParameter->SetMandatory(false); m_ChoiceList.push_back(choice); // check if the new choice matches the m_CurrentChoice : if so the group should be active. diff --git a/Modules/Wrappers/ApplicationEngine/src/otbWrapperCompositeApplication.cxx b/Modules/Wrappers/ApplicationEngine/src/otbWrapperCompositeApplication.cxx index 741bcb03974feef556c01fd1be194be907d3bf40..db216594f54992949895a4158aadc5e3137f15eb 100644 --- a/Modules/Wrappers/ApplicationEngine/src/otbWrapperCompositeApplication.cxx +++ b/Modules/Wrappers/ApplicationEngine/src/otbWrapperCompositeApplication.cxx @@ -32,8 +32,6 @@ namespace Wrapper CompositeApplication::CompositeApplication() { - m_LogOutput = itk::StdStreamLogOutput::New(); - m_LogOutput->SetStream(m_Oss); m_AddProcessCommand = AddProcessCommandType::New(); m_AddProcessCommand->SetCallbackFunction(this, &CompositeApplication::LinkWatchers); } @@ -65,8 +63,7 @@ CompositeApplication container.App = ApplicationRegistry::CreateApplication(appType); container.Desc = desc; // Setup logger - container.App->GetLogger()->AddLogOutput(m_LogOutput); - container.App->GetLogger()->SetTimeStampFormat(itk::LoggerBase::HUMANREADABLE); + container.App->SetLogger(this->GetLogger()); container.App->AddObserver(AddProcessToWatchEvent(), m_AddProcessCommand.GetPointer()); m_AppContainer[key] = container; return true; @@ -179,40 +176,14 @@ CompositeApplication ::ExecuteInternal(std::string key) { otbAppLogINFO(<< GetInternalAppDescription(key) <<"..."); - try - { - GetInternalApplication(key)->Execute(); - } - catch(...) - { - this->GetLogger()->Write( itk::LoggerBase::FATAL, std::string("\n") + m_Oss.str() ); - throw; - } - if(!m_Oss.str().empty()) - { - otbAppLogINFO(<< "\n" << m_Oss.str()); - m_Oss.str(std::string("")); - } + GetInternalApplication(key)->Execute(); } void CompositeApplication ::UpdateInternalParameters(std::string key) { - try - { - GetInternalApplication(key)->UpdateParameters(); - } - catch(...) - { - this->GetLogger()->Write( itk::LoggerBase::FATAL, std::string("\n") + m_Oss.str() ); - throw; - } - if(!m_Oss.str().empty()) - { - otbAppLogINFO(<< "\n" << m_Oss.str()); - m_Oss.str(std::string("")); - } + GetInternalApplication(key)->UpdateParameters(); } } // end namespace Wrapper diff --git a/Modules/Wrappers/CommandLine/src/otbWrapperCommandLineLauncher.cxx b/Modules/Wrappers/CommandLine/src/otbWrapperCommandLineLauncher.cxx index 686d4d58cf65f248024accc8ad306cda13d73e4a..736ad395b5255f46e4ff29d725bbeb64f38f5e36 100644 --- a/Modules/Wrappers/CommandLine/src/otbWrapperCommandLineLauncher.cxx +++ b/Modules/Wrappers/CommandLine/src/otbWrapperCommandLineLauncher.cxx @@ -582,99 +582,23 @@ CommandLineLauncher::ParamResultType CommandLineLauncher::LoadParameters() for (unsigned int i = 0; i < appKeyList.size(); i++) { const std::string paramKey(appKeyList[i]); - Parameter::Pointer param = m_Application->GetParameterByKey(paramKey); ParameterType type = m_Application->GetParameterType(paramKey); - const bool paramExists(m_Parser->IsAttributExists(std::string("-").append(paramKey), m_VExpression)); - std::vector<std::string> values; - - // When a parameter is mandatory : - // it must be set if : - // - The param is root - // - The param is not root and belonging to a Mandatory Group - // which is activated - bool mustBeSet = false; - const bool hasValue = m_Application->HasValue(paramKey); - - //skip if mandatory parameters are missing because we have it already in XML - if(!paramInXMLExists) - { - if( param->GetMandatory() == true && param->GetRole() != Role_Output && type != ParameterType_Group) - { - // check if the parameter is linked to a root parameter with a chain of active parameters - if( param->IsRoot() || param->GetRoot()->IsRoot()) - { - // the parameter is a root or inside a group at root level - mustBeSet = true; - } - else - { - Parameter* currentParam = param->GetRoot(); - while (!currentParam->IsRoot()) - { - if (!currentParam->GetActive()) - { - // the missing parameter is not on an active branch : we can ignore it - break; - } - currentParam = currentParam->GetRoot(); - - if (currentParam->IsRoot()) - { - // the missing parameter is on an active branch : we need it - mustBeSet = true; - } - } - } - } - } - - if( mustBeSet ) - { - if (!paramExists) - { - // If key doesn't exist and parameter hasn't default value set... - if (!hasValue) - { - std::cerr << "ERROR: Missing mandatory parameter -" << paramKey << "." << std::endl; - return MISSINGMANDATORYPARAMETER; - } - } - else - { - values = m_Parser->GetAttribut(std::string("-").append(paramKey), m_VExpression); - if (values.size() == 0 && !m_Application->HasValue(paramKey)) - { - std::cerr << "ERROR: Missing mandatory parameter -" << paramKey << "." << std::endl; - return MISSINGPARAMETERVALUE; - } - } - } - // Check if non mandatory parameter have values - else + if (m_Application->IsParameterMissing(paramKey)) { - if( paramExists ) - { - values = m_Parser->GetAttribut(std::string("-").append(paramKey), m_VExpression); - if (values.size() == 0) - { - std::cerr << "ERROR: Missing non-mandatory parameter -" << paramKey << "." << std::endl; - return MISSINGPARAMETERVALUE; - } - } + std::cerr << "ERROR: Missing mandatory parameter -" << paramKey << "." << std::endl; + return MISSINGMANDATORYPARAMETER; } // Check output paths validity - if (hasValue) + if (m_Application->HasValue(paramKey) && + type == ParameterType_OutputFilename) { - if (type == ParameterType_OutputFilename) + std::string filename = m_Application->GetParameterString(paramKey); + itksys::String path = itksys::SystemTools::GetFilenamePath(filename); + if (path!="" && !itksys::SystemTools::FileIsDirectory(path.c_str())) { - std::string filename = m_Application->GetParameterString(paramKey); - itksys::String path = itksys::SystemTools::GetFilenamePath(filename); - if (path!="" && !itksys::SystemTools::FileIsDirectory(path.c_str())) - { - std::cerr <<"ERROR: Directory doesn't exist : "<< path.c_str() << std::endl; - return WRONGPARAMETERVALUE; - } + std::cerr <<"ERROR: Directory doesn't exist : "<< path.c_str() << std::endl; + return WRONGPARAMETERVALUE; } } } @@ -813,47 +737,7 @@ std::string CommandLineLauncher::DisplayParameterHelp(const Parameter::Pointer & std::ostringstream oss; - // When a parameter is mandatory : - // it must be set if : - // - The param is root - // - The param is not root and belonging to a Mandatory Group - // which is activated - bool isMissing = false; - if (!m_Parser->IsAttributExists(std::string("-").append(paramKey), m_VExpression)) - { - if (!m_Application->HasValue(paramKey)) - { - if( param->GetMandatory() && param->GetRole() != Role_Output ) - { - if( param->IsRoot() || param->GetRoot()->IsRoot()) - { - // the parameter is a root or inside a group at root level - isMissing = true; - } - else - { - Parameter* currentParam = param->GetRoot(); - while (!currentParam->IsRoot()) - { - if (!currentParam->GetActive()) - { - // the missing parameter is not on an active branch : we can ignore it - break; - } - currentParam = currentParam->GetRoot(); - - if (currentParam->IsRoot()) - { - // the missing parameter is on an active branch : we need it - isMissing = true; - } - } - } - } - } - } - - if( isMissing ) + if( m_Application->IsParameterMissing(paramKey) ) { oss << "MISSING "; }