Commit b5ff3162 authored by Cédric Traizet's avatar Cédric Traizet

Merge branch 'develop' into app_vector_regression

parents 181690b3 4ffb954d
Pipeline #2451 failed with stages
in 88 minutes and 10 seconds
......@@ -24,10 +24,10 @@
# NUMPY_FOUND - True if Numpy headers are found.
# NUMPY_INCLUDE_DIR - where to find numpy/arrayobject.h, etc.
EXEC_PROGRAM ("${PYTHON_EXECUTABLE}"
ARGS "${CMAKE_SOURCE_DIR}/CMake/otbTestNumpy.py"
execute_process(
COMMAND "${PYTHON_EXECUTABLE}" -c "import sys, numpy; sys.stdout.write(numpy.get_include())"
OUTPUT_VARIABLE NUMPY_INCLUDE_DIR
RETURN_VALUE NUMPY_NOT_FOUND)
RESULT_VARIABLE NUMPY_NOT_FOUND)
if( NUMPY_INCLUDE_DIR MATCHES "Traceback" )
# Did not successfully include numpy
......
......@@ -23,6 +23,8 @@
# OTB before including OTBModuleMacros. This is the preferred way to build an
# OTB module outside of the OTB source tree.
include(GenerateExportHeaderCustom)
macro(otb_module_test)
include(../otb-module.cmake) # Load module meta-data
set(${otb-module-test}_LIBRARIES "")
......
This diff is collapsed.
import numpy
print(numpy.get_include())
......@@ -22,6 +22,8 @@ cmake_minimum_required(VERSION 3.10.2)
foreach(p
CMP0072 # CMake 3.11
CMP0078 # CMake 3.13
CMP0086 # CMake 3.14
)
if(POLICY ${p})
cmake_policy(SET ${p} NEW)
......@@ -105,7 +107,7 @@ set ( Python_ADDITIONAL_VERSIONS "3;2" )
set ( PythonInterp_FIND_REQUIRED ${OTB_WRAP_PYTHON} )
set ( PythonLibs_FIND_REQUIRED ${OTB_WRAP_PYTHON} )
find_package( PythonInterp )
if ( OTB_WRAP_PYTHON AND ( ${PYTHON_VERSION_MAJOR} EQUAL 2 ) )
if ( OTB_WRAP_PYTHON AND ( "x${PYTHON_VERSION_MAJOR}" STREQUAL "x2" ) )
message (WARNING "Python3 not found. There is no longer support of \
wrapping in python2 in OTB, but it can still be used.")
endif()
......@@ -399,6 +401,8 @@ install(FILES ${OTB_BINARY_DIR}/CMakeFiles/OTBConfig.cmake
CMake/OTBStandaloneModuleMacros.cmake
CMake/OTBModuleExternal.cmake
CMake/UseOTB.cmake
CMake/GenerateExportHeaderCustom.cmake
CMake/exportheader.cmake.in
DESTINATION ${OTB_INSTALL_PACKAGE_DIR}
COMPONENT Development)
get_property(OTBTargets_MODULES GLOBAL PROPERTY OTBTargets_MODULES)
......
#E cos(im1b1)+im2b1*im3b1-im3b2+ndvi(im3b3,im3b4)
#F val 1
#E im1b1 + 2*val + im2b1
QGIS interface
==============
The QGIS-OTB plugin (requires QGIS > 3.0)
The QGIS-OTB plugin (requires QGIS > 3.2)
-----------------------------------------
With QGIS 3.0.2 or later, you will need to manually install the plugin.
Clone qgis-otb-plugin repository and set ``QGIS_PLUGINPATH``:
With QGIS < 3.8 you will need to manually install the plugin.
You can follow the instruction here: https://gitlab.orfeo-toolbox.org/orfeotoolbox/qgis-otb-plugin#otb-provider-for-qgis-processing
For Linux/Unix/MacOSX
^^^^^^^^^^^^^^^^^^^^^
::
mkdir $HOME/projects; cd $HOME/projects
git clone https://gitlab.orfeo-toolbox.org/orfeotoolbox/qgis-otb-plugin
export QGIS_PLUGINPATH=$HOME/projects/qgis-otb-plugin
For Windows
^^^^^^^^^^^
Clone qgis-otb-plugin repository to ``C:\qgis-plugins\qgis-otb-plugin``
::
git clone https://gitlab.orfeo-toolbox.org/orfeotoolbox/qgis-otb-plugin
Then set the ``QGIS_PLUGINPATH`` variable:
* System properties (``Windows Key + R -> sysdm.cpl`` )
* Select Advanced Tab -> Environment variables.
* Under "user variables for "
* Add or Edit variable ``QGIS_PLUGINPATH`` and set value to ``C:\qgis-plugins\qgis-otb-plugin``
With QGIS > 3.8, the plugin is in the QGIS core. So you just need install OTB and set the plugin up.
Download and Install OTB
^^^^^^^^^^^^^^^^^^^^^^^^
......@@ -59,27 +37,6 @@ You can see OTB under "Providers":
* Set OTB application folder. This is location of your OTB applications. ``<OTB_FOLDER>/lib/otb/applications``
* Click "ok" to save settings and close dialog. If settings are correct, you will have OTB algorithms loaded in Processing toolbox
Using the processing toolbox (for QGIS < 3.0)
---------------------------------------------
In older QGIS version (3.0 or before), OTB applications are available from QGIS.
Use them from the processing toolbox, which is accessible under `Processing
-> ToolBox`. Switch to “advanced interface” in the bottom of the
application widget and OTB applications will be there.
.. figure:: Art/QtImages/qgis-otb.png
Using a custom OTB
^^^^^^^^^^^^^^^^^^
If QGIS cannot find OTB, the “applications folder” and “binaries folder”
can be set from the settings found under Processing :math:`\rightarrow`
Settings :math:`\rightarrow` “service provider”.
.. figure:: Art/QtImages/qgis-otb-settings.png
On some versions of QGIS, if an existing OTB installation is found, the
textfield settings will not be shown. To use a custom OTB instead of the
existing one, you will need to replace the otbcli, otbgui and library
files in QGIS installation directly.
Troubleshoot
------------
As of QGIS 3.8 the otb plugin is in the core. It might get messy if you have a previously installed plugin. Try to remove the old plugin before launching QGIS.
......@@ -75,17 +75,15 @@ LearningApplicationBase<TInputValue,TOutputValue>
"Cluster possible values of a categorical variable into K <= cat clusters to find a "
"suboptimal split.");
//CVFolds
//CVFolds: only exposed for OPENCV 2 because it crashes in OpenCV 3
#ifndef OTB_OPENCV_3
AddParameter(ParameterType_Int, "classifier.dt.f", "K-fold cross-validations");
#ifdef OTB_OPENCV_3
// disable cross validation by default (crash in opencv 3.2)
SetParameterInt("classifier.dt.f",0);
#else
SetParameterInt("classifier.dt.f",10);
#endif
SetParameterDescription("classifier.dt.f",
"If cv_folds > 1, then it prunes a tree with K-fold cross-validation where K "
"is equal to cv_folds.");
#endif
//Use1seRule
AddParameter(ParameterType_Bool, "classifier.dt.r", "Set Use1seRule flag to false");
......@@ -118,7 +116,10 @@ LearningApplicationBase<TInputValue,TOutputValue>
classifier->SetMinSampleCount(GetParameterInt("classifier.dt.min"));
classifier->SetRegressionAccuracy(GetParameterFloat("classifier.dt.ra"));
classifier->SetMaxCategories(GetParameterInt("classifier.dt.cat"));
//CVFolds is only exposed for OPENCV 2 because it crashes in OpenCV 3
#ifndef OTB_OPENCV_3
classifier->SetCVFolds(GetParameterInt("classifier.dt.f"));
#endif
if (GetParameterInt("classifier.dt.r"))
{
classifier->SetUse1seRule(false);
......
......@@ -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
......
......@@ -26,6 +26,9 @@ otb_module(OTBAppMathParserX
OTBMathParserX
OTBObjectList
TEST_DEPENDS
OTBTestKernel
DESCRIPTION
"${DOCUMENTATION}"
)
......@@ -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
/*
* 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
......@@ -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;
......
......@@ -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();
......
......@@ -128,7 +128,7 @@ ossimString ossimOgcWktTranslator::fromOssimKwl(const ossimKeywordlist &kwl,
<< ": "
<< ( ossimUnitTypeLut::instance()->
getEntryString(units).c_str() )
<< endl;
<< std::endl;
break;
}
} // End of switch (units)
......@@ -491,11 +491,11 @@ ossimString ossimOgcWktTranslator::fromOssimKwl(const ossimKeywordlist &kwl,
}
else
{
cerr << "ossimOgcWktTranslator::fromOssimKwl:\n"
std::cerr << "ossimOgcWktTranslator::fromOssimKwl:\n"
<< "Projection translation for "
<< projType
<< " not supported "
<< endl;
<< std::endl;
}
if(pcsCodeVal >= EPSG_CODE_MAX)
......@@ -532,10 +532,10 @@ ossimString ossimOgcWktTranslator::fromOssimKwl(const ossimKeywordlist &kwl,
}
else
{
cerr << "ossimOgcWktTranslator::fromOssimKwl: Datum translation for "
std::cerr << "ossimOgcWktTranslator::fromOssimKwl: Datum translation for "
<< datumType
<<" not supported"
<< endl;
<< std::endl;
}
}
......@@ -700,7 +700,7 @@ bool ossimOgcWktTranslator::toOssimKwl( const ossimString& wktString,
{
ossimNotify(ossimNotifyLevel_DEBUG)
<< MODULE << "DEBUG:"
<< "\nossimProj = " << ossimProj << endl;
<< "\nossimProj = " << ossimProj << std::endl;
}