diff --git a/Data/Input/apTvUtExportBandMathX.txt b/Data/Input/apTvUtExportBandMathX.txt index baa6a64fc44bd2084a44a5b4ff0044f7b0e3fa21..774aca47ddf40b5931ef58ec124c5394bbb4a511 100644 --- a/Data/Input/apTvUtExportBandMathX.txt +++ b/Data/Input/apTvUtExportBandMathX.txt @@ -1,2 +1,3 @@ -#E cos(im1b1)+im2b1*im3b1-im3b2+ndvi(im3b3,im3b4) +#F val 1 +#E im1b1 + 2*val + im2b1 diff --git a/Modules/Applications/AppMathParserX/app/otbBandMathX.cxx b/Modules/Applications/AppMathParserX/app/otbBandMathX.cxx index 4ea775fdb56ca5db88271e0918e90a39da46dcb8..95a859ebbecede54dad37cc8cbc1587330bd6b91 100644 --- a/Modules/Applications/AppMathParserX/app/otbBandMathX.cxx +++ b/Modules/Applications/AppMathParserX/app/otbBandMathX.cxx @@ -246,54 +246,42 @@ private: void DoUpdateParameters() override { // check if input context should be used - bool useContext = this->ContextCheck(); + bool context_exists = this->ContextCheck(); // Check if the expression is correctly set - if (HasValue("il") && HasValue("exp")) - { - this->LiveCheck(useContext); - } + BandMathImageFilterType::Pointer math_filter = BandMathImageFilterType::New(); + math_filter->SetManyExpressions(false); + // first thing, load context if there is one + if (context_exists) + math_filter->ImportContext(GetParameterString("incontext")); + // Only one expression is allowed '-exp'>'-incontext' + if ( !HasValue("exp") ) + SetParameterString("exp", math_filter->GetExpression(0)); + if ( HasValue("il") && HasValue("exp") ) + { + math_filter->ClearExpression(); // remove expression set by context + math_filter->SetExpression(GetParameterString("exp")); //set expression + LiveCheck(math_filter); + } } + // Check if the given filename is valid bool ContextCheck(void) - { - bool useContext = false; + { + bool context_exists = false; if (IsParameterEnabled("incontext") && HasValue("incontext")) - { + { std::string contextPath = GetParameterString("incontext"); // check that file exists if (itksys::SystemTools::FileExists(contextPath,true)) - { - BandMathImageFilterType::Pointer dummyFilter = - BandMathImageFilterType::New(); - dummyFilter->SetManyExpressions(false); - try - { - dummyFilter->ImportContext(contextPath); - useContext = true; - } - catch(itk::ExceptionObject& err) - { - //trick to prevent unreferenced local variable warning on MSVC - (void)err; - // silent catch - useContext = false; - } - if (useContext) - { - // only set the first expression, 'ManyExpression' is disabled. - this->SetParameterString("exp",dummyFilter->GetExpression(0)); - } - } + { + context_exists = true; } - return useContext; } + return context_exists; + } - void LiveCheck(bool useContext=false) + void LiveCheck( BandMathImageFilterType::Pointer math_filter ) { - BandMathImageFilterType::Pointer dummyFilter = - BandMathImageFilterType::New(); - dummyFilter->SetManyExpressions(false); - std::vector<MultiChannelExtractorType::Pointer> extractors; FloatVectorImageListType::Pointer inList = GetParameterImageList("il"); for (unsigned int i = 0; i < inList->Size(); i++) @@ -314,19 +302,11 @@ private: { extract->SetChannel(j+1); } - dummyFilter->SetNthInput(i,extract->GetOutput()); - } - if (useContext) - { - dummyFilter->ImportContext(GetParameterString("incontext")); - } - else - { - dummyFilter->SetExpression(GetParameterString("exp")); + math_filter->SetNthInput(i,extract->GetOutput()); } try { - dummyFilter->UpdateOutputInformation(); + math_filter->UpdateOutputInformation(); SetParameterDescription("exp", "Valid expression"); } catch(itk::ExceptionObject& err) @@ -334,8 +314,15 @@ private: // Change the parameter description to be able to have the // parser errors in the tooltip SetParameterDescription("exp", err.GetDescription()); + // std::string error_string(err.GetDescription()); + // otbAppLogINFO("There was an error while parsing the expression given " + // "its input:" + error_string ); } + catch(...) + { + SetParameterDescription("exp", "Other exception catched"); } + } void DoExecute() override { @@ -352,11 +339,11 @@ private: if ( (!IsParameterEnabled("exp")) && (!IsParameterEnabled("incontext")) ) { - itkExceptionMacro("No expression set...; please set and enable at least one one expression"); + itkExceptionMacro("No expression set...; please set and enable at least one expression"); } - m_Filter = BandMathImageFilterType::New(); - m_Filter->SetManyExpressions(false); + BandMathImageFilterType::Pointer math_filter = BandMathImageFilterType::New(); + math_filter->SetManyExpressions(false); for (unsigned int i = 0; i < nbImages; i++) { @@ -367,31 +354,33 @@ private: << currentImage->GetNumberOfComponentsPerPixel() << " components"); - m_Filter->SetNthInput(i,currentImage); + math_filter->SetNthInput(i,currentImage); } - bool useContext = this->ContextCheck(); + bool context_exists = this->ContextCheck(); + // first thing, load context if there is one + if (context_exists) + { + std::string context_string = GetParameterString("incontext"); + math_filter->ImportContext(context_string); + otbAppLogINFO("Using Context: " << context_string + << " for variables (and expression if no parameter -exp has been given)." ); + } + // Only one expression is allowed '-exp'>'-incontext' + math_filter->ClearExpression(); // remove expression set by context std::string expStr = GetParameterString("exp"); - if (useContext) - { - otbAppLogINFO("Using input context: " << expStr ); - m_Filter->ImportContext(GetParameterString("incontext")); - } - else - { - otbAppLogINFO("Using expression: " << expStr ); - m_Filter->SetExpression(expStr); - } + otbAppLogINFO("Using expression: " << expStr ); + math_filter->SetExpression(expStr); if ( IsParameterEnabled("outcontext") && HasValue("outcontext") ) - m_Filter->ExportContext(GetParameterString("outcontext")); + math_filter->ExportContext(GetParameterString("outcontext")); // Set the output image - SetParameterOutputImage("out", m_Filter->GetOutput()); + SetParameterOutputImage("out", math_filter->GetOutput()); + RegisterPipeline(); } - BandMathImageFilterType::Pointer m_Filter; }; } // namespace Wrapper diff --git a/Modules/Applications/AppMathParserX/otb-module.cmake b/Modules/Applications/AppMathParserX/otb-module.cmake index 54b1fc571ac6aedc6cdfa26f7c1c4949ce4c3baa..eda0082072a811e8947f5962ccacd390f71e8af5 100644 --- a/Modules/Applications/AppMathParserX/otb-module.cmake +++ b/Modules/Applications/AppMathParserX/otb-module.cmake @@ -26,6 +26,9 @@ otb_module(OTBAppMathParserX OTBMathParserX OTBObjectList + TEST_DEPENDS + OTBTestKernel + DESCRIPTION "${DOCUMENTATION}" ) diff --git a/Modules/Applications/AppMathParserX/test/CMakeLists.txt b/Modules/Applications/AppMathParserX/test/CMakeLists.txt index b5d584699738fa153a1a7a5c93a80138bd52c904..65e464ffec60931c951646d2e77d2a31575c841f 100644 --- a/Modules/Applications/AppMathParserX/test/CMakeLists.txt +++ b/Modules/Applications/AppMathParserX/test/CMakeLists.txt @@ -20,15 +20,15 @@ otb_module_test() -#----------- BandMathX TESTS ---------------- -otb_test_application(NAME apTvUtBandMathX - APP BandMathX - OPTIONS -il ${INPUTDATA}/poupees_sub_c1.png - ${INPUTDATA}/poupees_sub_c2.png - ${INPUTDATA}/poupees_sub.png - -out ${TEMP}/apTvUtBandMathXOutput.tif - -incontext ${INPUTDATA}/apTvUtExportBandMathX.txt - VALID --compare-image ${NOTOL} - ${INPUTDATA}/apTvUtBandMathOutput.tif - ${TEMP}/apTvUtBandMathXOutput.tif) +set(OTBBandMathXAppTest +otbBandMathXAppTests.cxx +) +add_executable(OTBBandMathXAppTest ${OTBBandMathXAppTest}) +target_link_libraries(OTBBandMathXAppTest ${OTBAppMathParserX-Test_LIBRARIES}) +otb_module_target_label(OTBBandMathXAppTest) + +otb_add_test(NAME apTvUtBandMathX COMMAND OTBBandMathXAppTest + $<TARGET_FILE_DIR:otbapp_BandMathX> + ${INPUTDATA}/apTvUtExportBandMathX.txt + ) \ No newline at end of file diff --git a/Modules/Applications/AppMathParserX/test/otbBandMathXAppTests.cxx b/Modules/Applications/AppMathParserX/test/otbBandMathXAppTests.cxx new file mode 100644 index 0000000000000000000000000000000000000000..b2d425fc0632ff9f3c4cd85adb3572160bcfee87 --- /dev/null +++ b/Modules/Applications/AppMathParserX/test/otbBandMathXAppTests.cxx @@ -0,0 +1,175 @@ +/* + * Copyright (C) 2005-2019 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 "otbVectorImage.h" +#include "otbWrapperApplicationRegistry.h" +#include "otbWrapperTypes.h" +#include <string> + +typedef otb::VectorImage<unsigned char> VectorImageType; +typedef VectorImageType::PixelType PixelType; + +/* This function is creating and filling a vector image */ +VectorImageType::Pointer create_vector_image( int pxl_s , int nb_comp , unsigned char value ) +{ + VectorImageType::SizeType size; + size.Fill(pxl_s); + VectorImageType::IndexType index; + index.Fill(0); + VectorImageType::RegionType region; + region.SetSize(size); + region.SetIndex(index); + + VectorImageType::Pointer image = VectorImageType::New(); + + image->SetLargestPossibleRegion( region ); + image->SetBufferedRegion( region ); + image->SetRequestedRegion( region ); + image->SetNumberOfComponentsPerPixel(nb_comp); + image->Allocate(); + PixelType val(nb_comp); + val.Fill(value); + image->FillBuffer(val); + return image; +} + +#define dyn_cast( im_base , vect_im ) \ +{ \ + vect_im = dynamic_cast<otb::VectorImage<float> *>(im_base); \ + if( ! vect_im ) \ + { \ + std::cout<<"Not the right conversion, cannot retrieve the output"<<std::endl; \ + return EXIT_FAILURE ; \ + } \ +} + +int main(int , char * argv[] ) +{ + std::cout<<"Begin bandMathX Test"<<std::endl; + int return_val = 0; + auto img1 = create_vector_image(5,2,1); + auto img2 = create_vector_image(5,1,2); + VectorImageType::IndexType index; + index.Fill(3); // Center of the images + std::cout<<"Create application"<<std::endl; + otb::Wrapper::ApplicationRegistry::SetApplicationPath(argv[1]); + auto app = otb::Wrapper::ApplicationRegistry::CreateApplication("BandMathX"); + app->AddImageToParameterInputImageList("il", img1); + app->UpdateParameters(); + app->AddImageToParameterInputImageList("il", img2); + app->UpdateParameters(); + std::cout<<"Inputs are set"<<std::endl; + + // Case one: only expression + app->SetParameterString("exp", "im1b1+im2b1"); + app->UpdateParameters(); + app->SetParameterOutputImagePixelType("out", otb::Wrapper::ImagePixelType::ImagePixelType_uint8); + std::cout<<"Case one: parameter exp is set"<<std::endl; + app->Execute(); + auto output = app->GetParameterImageBase("out"); + output->Update(); + float im_val = 0; + // We need to be carefull as we are taking the direct output of the underlying + // filter in the application + otb::VectorImage<float> * output_int = nullptr; + dyn_cast( output , output_int ) + im_val = output_int->GetPixel(index).GetElement(0); + if ( im_val != 3 ) + { + std::cout<<"Wrong value in test, was expecting 3, got "<<im_val<<std::endl; + return_val++; + } + else + { + std::cout<<"Case one passed"<<std::endl; + } + + // Case two: expression and context + app->SetParameterString("exp", "im1b1+val-im2b1"); + app->UpdateParameters(); + std::cout<<"Case two: use context to define a constant"<<std::endl; + auto desc = app->GetParameterDescription("exp"); + if (desc.find("Following variables not allowed : val") == std::string::npos) + { + std::cout<<"Cannot find usual value in the parameter description."<<std::endl; + std::cout<<"The test was looking for \"Following variables not allowed : val\"" + <<" in the parameter description and got \""<<desc<<"\" instead."<<std::endl; + return_val++; + } + + app->SetParameterString("incontext",argv[2]); + // val is set in the context to 1 + app->UpdateParameters(); + desc = app->GetParameterDescription("exp"); + if (desc.find("Valid expression") == std::string::npos ) + { + std::cout<<"Cannot find usual value in the parameter description."<<std::endl; + std::cout<<"The test was looking for \"Valid expression\"" + <<" in the parameter description and got \""<<desc<<"\" instead."<<std::endl; + return_val++; + } + + app->Execute(); + output = app->GetParameterImageBase("out"); + output->Update(); + // We need to be carefull as we are taking the direct output of the underlying + // filter in the application + dyn_cast( output , output_int ) + im_val = output_int->GetPixel(index).GetElement(0); + if ( im_val != 0 ) + { + std::cout<<"Wrong value in test, was expecting 0, got "<<im_val<<std::endl; + return_val++; + } + else + { + std::cout<<"Case two passed"<<std::endl; + } + + // Case three: no expression and context + app->SetParameterString("exp", ""); + app->UpdateParameters(); + std::cout<<"Case three: no parameter exp"<<std::endl; + auto exp = app->GetParameterString("exp"); + if (exp.find("im1b1 + 2*val + im2b1") == std::string::npos ) + { + std::cout<<"The expression value is not set correctly."<<std::endl; + std::cout<<"The test was looking for \"im1b1 + 2*val + im2b1\"" + <<" in the parameter value and got \""<<exp<<"\" instead."<<std::endl; + return_val++; + } + app->Execute(); + output = app->GetParameterImageBase("out"); + output->Update(); + // We need to be carefull as we are taking the direct output of the underlying + // filter in the application + dyn_cast( output , output_int ) + im_val = output_int->GetPixel(index).GetElement(0); + if (im_val != 5 ) + { + std::cout<<"Wrong value in test, was expecting 5, got "<<im_val<<std::endl; + return_val++; + } + else + { + std::cout<<"Case three passed"<<std::endl; + } + return return_val; +} \ No newline at end of file diff --git a/Modules/Filtering/MathParserX/include/otbBandMathXImageFilter.h b/Modules/Filtering/MathParserX/include/otbBandMathXImageFilter.h index 9822cd6ab731a520109484ec9e4bd32947f65468..ce76dd8e44ded0f672a328bb72bcabdf6566dced 100644 --- a/Modules/Filtering/MathParserX/include/otbBandMathXImageFilter.h +++ b/Modules/Filtering/MathParserX/include/otbBandMathXImageFilter.h @@ -112,8 +112,8 @@ public: /** Set an expression to be parsed */ void SetExpression(const std::string& expression); - /** Return the nth expression to be parsed */ - std::string GetExpression(int) const; + /** Return the nth expression to be parsed*/ + std::string GetExpression(unsigned int IDExpression) const; /** Set a matrix (or a vector) */ void SetMatrix(const std::string& name, const std::string& definition); @@ -127,9 +127,15 @@ public: /** Import constants and expressions from a given filename */ void ImportContext(const std::string& filename); + /** Clear all previously set expression*/ + void ClearExpression(); /** Return the variable and constant names */ std::vector<std::string> GetVarNames() const; + bool GlobalStatsDetected() const + { + return !m_StatsVarDetected.empty(); + } protected : BandMathXImageFilter(); @@ -145,11 +151,6 @@ protected : private : - bool globalStatsDetected() const - { - return (m_StatsVarDetected.size()>0); - } - typedef struct { std::string name; ValueType value; diff --git a/Modules/Filtering/MathParserX/include/otbBandMathXImageFilter.hxx b/Modules/Filtering/MathParserX/include/otbBandMathXImageFilter.hxx index 6092926c7d22d861d0a88a811de26f9f537d016c..44cde5bed777f89ebfada6a1e1c24de73a2802d1 100644 --- a/Modules/Filtering/MathParserX/include/otbBandMathXImageFilter.hxx +++ b/Modules/Filtering/MathParserX/include/otbBandMathXImageFilter.hxx @@ -261,7 +261,13 @@ void BandMathXImageFilter<TImage> this->Modified(); } - +template< typename TImage > +void BandMathXImageFilter<TImage> +::ClearExpression() +{ + m_Expression.clear(); + this->Modified(); +} template< typename TImage > void BandMathXImageFilter<TImage> ::SetMatrix(const std::string& name, const std::string& definition) @@ -517,9 +523,11 @@ void BandMathXImageFilter<TImage> template< typename TImage > std::string BandMathXImageFilter<TImage> -::GetExpression(int IDExpression) const +::GetExpression(unsigned int IDExpression) const { - return m_Expression[IDExpression]; + if ( IDExpression < m_Expression.size() ) + return m_Expression[IDExpression]; + return ""; } @@ -913,7 +921,7 @@ void BandMathXImageFilter< TImage > CheckImageDimensions(); PrepareParsers(); - if (globalStatsDetected()) + if (GlobalStatsDetected()) PrepareParsersGlobStats(); OutputsDimensions();