diff --git a/CMake/FindOpenCV.cmake b/CMake/FindOpenCV.cmake index 4f665c7290de7e18282460bebc7b197e423e6ecd..9a6ced7b9f3a6ce3938a1c50d3d7dd7b6f9d8a38 100755 --- a/CMake/FindOpenCV.cmake +++ b/CMake/FindOpenCV.cmake @@ -1,64 +1,108 @@ -find_path( -cv_INCLUDE_DIR -opencv/cv.h -DOC "The directory where opencv/cv.h is installed") - -find_path( -opencv2_INCLUDE_DIR -opencv2/opencv.hpp -DOC "The directory where opencv2/opencv.hpp is installed") - -set(opencv_core_NAMES opencv_core) -set(opencv_ml_NAMES opencv_ml) - -if ( cv_INCLUDE_DIR AND opencv2_INCLUDE_DIR ) - set(OPENCV_INCLUDE_DIRS "${cv_INCLUDE_DIR};${opencv2_INCLUDE_DIR}") - list(REMOVE_DUPLICATES OPENCV_INCLUDE_DIRS) - - if(NOT OpenCV_VERSION) - file(READ "${opencv2_INCLUDE_DIR}/opencv2/core/version.hpp" _header_content) - string(REGEX REPLACE ".*# *define +CV_VERSION_EPOCH +([0-9]+).*" "\\1" - OpenCV_VERSION_MAJOR ${_header_content}) - string(REGEX REPLACE ".*# *define +CV_VERSION_MAJOR +([0-9]+).*" "\\1" - OpenCV_VERSION_MINOR ${_header_content}) - string(REGEX REPLACE ".*# *define +CV_VERSION_MINOR +([0-9]+).*" "\\1" - OpenCV_VERSION_PATCH ${_header_content}) - string(REGEX REPLACE ".*# *define +CV_VERSION_REVISION +([0-9]+).*" "\\1" - OpenCV_VERSION_TWEAK ${_header_content}) - set(OpenCV_VERSION - "${OpenCV_VERSION_MAJOR}.${OpenCV_VERSION_MINOR}.${OpenCV_VERSION_PATCH}") - endif() - - if(WIN32) - list(APPEND opencv_core_NAMES - "opencv_core${OpenCV_VERSION_MAJOR}${OpenCV_VERSION_MINOR}${OpenCV_VERSION_PATCH}") - list(APPEND opencv_ml_NAMES - "opencv_ml${OpenCV_VERSION_MAJOR}${OpenCV_VERSION_MINOR}${OpenCV_VERSION_PATCH}") - endif() -endif() - -# Prefer the static library. -find_library( -OPENCV_core_LIBRARY -NAMES ${opencv_core_NAMES} -DOC "Path to opencv_core library") - -find_library( -OPENCV_ml_LIBRARY -NAMES ${opencv_ml_NAMES} -DOC "Path to opencv_ml library") - -set(OpenCV_FOUND FALSE) -if ( OPENCV_core_LIBRARY AND OPENCV_ml_LIBRARY ) - set(OPENCV_LIBRARIES "${OPENCV_core_LIBRARY};${OPENCV_ml_LIBRARY}") -endif () - -if( OPENCV_INCLUDE_DIRS AND OPENCV_LIBRARIES ) - set(OpenCV_FOUND TRUE) - set(OPENCV_VERSION ${OpenCV_VERSION}) #for compatility -endif() - -include(${CMAKE_ROOT}/Modules/FindPackageHandleStandardArgs.cmake) -FIND_PACKAGE_HANDLE_STANDARD_ARGS(OPENCV - REQUIRED_VARS OPENCV_core_LIBRARY OPENCV_INCLUDE_DIRS - VERSION_VAR OpenCV_VERSION) +set(OPENCV_SEARCH_PATH) + +if(OpenCV_DIR) + get_filename_component(OPENCV_SEARCH_PATH "${OpenCV_DIR}" PATH) + if(OPENCV_SEARCH_PATH) + get_filename_component(OPENCV_SEARCH_PATH "${OPENCV_SEARCH_PATH}" PATH) + endif() + if(OPENCV_SEARCH_PATH) + find_path( + opencv_INCLUDE_DIR + opencv/cv.h + PATHS "${OPENCV_SEARCH_PATH}" + PATH_SUFFIXES "include" + DOC "The directory where opencv/cv.h is installed") + endif() +endif() + +if(NOT opencv_INCLUDE_DIR) + find_path( + opencv_INCLUDE_DIR + opencv/cv.h + PATHS "${OpenCV_DIR}" + PATH_SUFFIXES "include" + DOC "The directory where opencv/cv.h is installed") +endif() + +if(NOT EXISTS ${opencv_INCLUDE_DIR}/opencv2/opencv.hpp) + message(FATAL_ERROR "${opencv_INCLUDE_DIR}/opencv2/opencv.hpp does not exists. " + "Make sure you have opencv 2.3 or higher. We had searched in ${OPENCV_SEARCH_PATHS}") +endif() + +set(opencv_core_NAMES opencv_core) +set(opencv_ml_NAMES opencv_ml) + +if ( opencv_INCLUDE_DIR ) + + set(OPENCV_INCLUDE_DIRS "${opencv_INCLUDE_DIR}") + + if(NOT OpenCV_VERSION) + file(READ "${opencv_INCLUDE_DIR}/opencv2/core/version.hpp" _header_content) + + string(REGEX MATCH ".*# *define +CV_VERSION_EPOCH +([0-9]+).*" matched ${_header_content}) + if( matched) + string(REGEX REPLACE ".*# *define +CV_VERSION_EPOCH +([0-9]+).*" "\\1" + OpenCV_VERSION_MAJOR ${_header_content}) + string(REGEX REPLACE ".*# *define +CV_VERSION_MAJOR +([0-9]+).*" "\\1" + OpenCV_VERSION_MINOR ${_header_content}) + string(REGEX REPLACE ".*# *define +CV_VERSION_MINOR +([0-9]+).*" "\\1" + OpenCV_VERSION_PATCH ${_header_content}) + string(REGEX REPLACE ".*# *define +CV_VERSION_REVISION +([0-9]+).*" "\\1" + OpenCV_VERSION_TWEAK ${_header_content}) + else() + #for opencv 2.3.x + string(REGEX REPLACE ".*# *define +CV_MAJOR_VERSION +([0-9]+).*" "\\1" + OpenCV_VERSION_MAJOR ${_header_content}) + string(REGEX REPLACE ".*# *define +CV_MINOR_VERSION +([0-9]+).*" "\\1" + OpenCV_VERSION_MINOR ${_header_content}) + string(REGEX REPLACE ".*# *define +CV_SUBMINOR_VERSION +([0-9]+).*" "\\1" + OpenCV_VERSION_PATCH ${_header_content}) + set(OpenCV_VERSION_TWEAK) + endif() + set(OpenCV_VERSION + "${OpenCV_VERSION_MAJOR}.${OpenCV_VERSION_MINOR}.${OpenCV_VERSION_PATCH}") + endif() + + if(WIN32) + list(APPEND opencv_core_NAMES + "opencv_core${OpenCV_VERSION_MAJOR}${OpenCV_VERSION_MINOR}${OpenCV_VERSION_PATCH}") + list(APPEND opencv_ml_NAMES + "opencv_ml${OpenCV_VERSION_MAJOR}${OpenCV_VERSION_MINOR}${OpenCV_VERSION_PATCH}") + endif() +endif() + +if(NOT OPENCV_SEARCH_PATH) + get_filename_component(OPENCV_SEARCH_PATH "${opencv_INCLUDE_DIR}" PATH) +endif() + +# Prefer the static library. +find_library( + OPENCV_core_LIBRARY + NAMES ${opencv_core_NAMES} + PATHS ${OPENCV_SEARCH_PATH} + PATH_SUFFIXES "lib" "lib64" "lib/x86_64-linux-gnu" + NO_DEFAULT_PATH + DOC "Path to opencv_core library") + +find_library( + OPENCV_ml_LIBRARY + NAMES ${opencv_ml_NAMES} + PATHS ${OPENCV_SEARCH_PATH} + PATH_SUFFIXES "lib" "lib64" "lib/x86_64-linux-gnu" + NO_DEFAULT_PATH + DOC "Path to opencv_ml library") + +set(OpenCV_FOUND FALSE) +if ( OPENCV_core_LIBRARY AND OPENCV_ml_LIBRARY ) + set(OPENCV_LIBRARIES "${OPENCV_core_LIBRARY};${OPENCV_ml_LIBRARY}") +endif () + +if( OPENCV_INCLUDE_DIRS AND OPENCV_LIBRARIES ) + set(OpenCV_FOUND TRUE) + set(OPENCV_VERSION ${OpenCV_VERSION}) #for compatility +endif() + +include(${CMAKE_ROOT}/Modules/FindPackageHandleStandardArgs.cmake) +FIND_PACKAGE_HANDLE_STANDARD_ARGS(OpenCV + REQUIRED_VARS OPENCV_core_LIBRARY OPENCV_INCLUDE_DIRS + VERSION_VAR OpenCV_VERSION) diff --git a/CMake/OTBSetStandardCompilerFlags.cmake b/CMake/OTBSetStandardCompilerFlags.cmake index 28c40e694dd37652c28dd6b07325e00f5b2d02ee..129eeb075d3cd674d761967c0873d67bdfebbd4c 100644 --- a/CMake/OTBSetStandardCompilerFlags.cmake +++ b/CMake/OTBSetStandardCompilerFlags.cmake @@ -73,7 +73,7 @@ function(check_compiler_warning_flags c_warning_flags_var cxx_warning_flags_var) ## is reporting 1000's of wanings in windows ## header files, for now, limit the number of ## warnings to level 3 - if( WIN32 ) + if( MSVC ) set(VerboseWarningsFlag -W3 ) ## A better solution would be to use -Wall, ## and then disable warnings one by one diff --git a/CMakeLists.txt b/CMakeLists.txt index 26357a57a010120eb2a95f6c3f3eea677571a160..c37da5d534cf2c4946514fe36c65850b6bda365d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -457,7 +457,7 @@ endforeach() # message("OTB_MODULES_ENABLED = ${modules_list_for_summary}") # unset(modules_list_for_summary) -list(REMOVE_ITEM option_list "OTB_USE_6S" "OTB_USE_SIFTFAST" "OTB_USE_OPENCV" "OTB_USE_QT4") +list(REMOVE_ITEM option_list "OTB_USE_6S" "OTB_USE_SIFTFAST" "OTB_USE_QT4") foreach(item ${option_list}) if(NOT ${item}) list(REMOVE_ITEM option_list "${item}" ) @@ -475,9 +475,6 @@ list(APPEND option_list TINYXML) #RK #Q: Why these two guys here? we already have option_list #A: Because cmake case sensitivity with variables. -if(OTB_USE_OPENCV) - list(APPEND option_list OpenCV) -endif() if(OTB_USE_QT4) list(APPEND option_list QT) diff --git a/Examples/DisparityMap/FineRegistrationImageFilterExample.cxx b/Examples/DisparityMap/FineRegistrationImageFilterExample.cxx index 0808cf5b4c884240491161a95b11b84b09c9499a..d8900a709e65b1a91afef09039ad25f036727e5c 100644 --- a/Examples/DisparityMap/FineRegistrationImageFilterExample.cxx +++ b/Examples/DisparityMap/FineRegistrationImageFilterExample.cxx @@ -181,7 +181,7 @@ int main(int argc, char** argv) // We need to set the sub-pixel accuracy we want to obtain: // // Software Guide : EndLatex - registrator->SetSubPixelAccuracy(atof(argv[10])); + registrator->SetConvergenceAccuracy(atof(argv[10])); // Software Guide : BeginLatex // diff --git a/Modules/Applications/AppClassification/app/CMakeLists.txt b/Modules/Applications/AppClassification/app/CMakeLists.txt index 3f46b917da7fa5c1e6d2e17623635bb6cbcd6e38..982bbaedaef870ea5fe5d292b68479aa8e7bc46c 100644 --- a/Modules/Applications/AppClassification/app/CMakeLists.txt +++ b/Modules/Applications/AppClassification/app/CMakeLists.txt @@ -125,3 +125,8 @@ otb_create_application( NAME SampleExtraction SOURCES otbSampleExtraction.cxx LINK_LIBRARIES ${${otb-module}_LIBRARIES}) + +otb_create_application( + NAME MultiImageSamplingRate + SOURCES otbMultiImageSamplingRate.cxx + LINK_LIBRARIES ${${otb-module}_LIBRARIES}) diff --git a/Modules/Applications/AppClassification/app/otbMultiImageSamplingRate.cxx b/Modules/Applications/AppClassification/app/otbMultiImageSamplingRate.cxx new file mode 100644 index 0000000000000000000000000000000000000000..0a33c98a8652d209d57418b1dc8cba96352ed38b --- /dev/null +++ b/Modules/Applications/AppClassification/app/otbMultiImageSamplingRate.cxx @@ -0,0 +1,302 @@ +/*========================================================================= + + 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. + + =========================================================================*/ +#include "otbWrapperApplication.h" +#include "otbWrapperApplicationFactory.h" +#include "otbSamplingRateCalculatorList.h" +#include "otbStatisticsXMLFileReader.h" + +namespace otb +{ +namespace Wrapper +{ + +class MultiImageSamplingRate : public Application +{ +public: + /** Standard class typedefs. */ + typedef MultiImageSamplingRate Self; + typedef Application Superclass; + typedef itk::SmartPointer<Self> Pointer; + typedef itk::SmartPointer<const Self> ConstPointer; + + /** Standard macro */ + itkNewMacro(Self); + + itkTypeMacro(MultiImageSamplingRate, otb::Application); + + /** typedef */ + typedef otb::SamplingRateCalculatorList RateCalculatorListType; + + typedef RateCalculatorListType::ClassCountMapType ClassCountMapType; + typedef RateCalculatorListType::MapRateType MapRateType; + + typedef itk::VariableLengthVector<float> MeasurementType; + typedef otb::StatisticsXMLFileReader<MeasurementType> XMLReaderType; + +private: + MultiImageSamplingRate() + { + m_CalculatorList = RateCalculatorListType::New(); + } + + void DoInit() + { + SetName("MultiImageSamplingRate"); + SetDescription("Compute sampling rate for an input set of images."); + + // Documentation + SetDocName("Multi-image sampling rate estimation"); + SetDocLongDescription("The application computes sampling rates for a set of" + " input images. Before calling this application, each pair of image and " + "training vectors has to be analysed with the application " + "PolygonClassStatistics. The statistics file is then used to compute the " + "sampling rates for each class in each image. Several types of sampling " + " are implemented. Each one is a combination of a mono-image strategy " + "and a multi-image mode. The mono-image strategies are :\n" + " - smallest (default) : select the same number of sample in each " + "class so that the smallest one is fully sampled.\n" + " - constant : select the same number of samples N in each class " + "(with N below or equal to the size of the smallest class).\n" + " - byclass : set the required number for each class manually, with an " + "input CSV file (first column is class name, second one is the required " + "samples number).\n" + "The multi-image modes (mim) are : proportional, equal and custom. The custom " + "mode lets the users choose the distribution of samples among the " + "images. The different behaviours are described below. Ti(c) and Ni(c) " + " refers resp. to the total number and needed number of samples in " + "image i for class c. Let's call L the total number of images.\n" + " > strategy = all\n" + " + Same behaviour for all modes : take all samples\n" + " > strategy = constant\n" + " (let's call M the global number of samples required per class)\n" + " + mim = proportional : For each image i and each class c,\n" + " Ni( c ) = M * Ti( c ) / sum_k( Tk(c) )\n" + " + mim = equal : For each image i and each class c,\n" + " Ni( c ) = M / L\n" + " + mim = custom : For each image i and each class c,\n" + " Ni( c ) = Mi where Mi is the custom requested number of samples for image i\n" + " > strategy = byClass\n" + " (let's call M(c) the global number of samples for class c)\n" + " + mim = proportional : For each image i and each class c\n" + " Ni( c ) = M(c) * Ti( c ) / sum_k( Tk(c) )\n" + " + mim = equal : For each image i and each class c,\n" + " Ni( c ) = M(c) / L\n" + " + mim = custom : For each image i and each class c,\n" + " Ni( c ) = Mi(c) where Mi(c) is the custom requested number of samples for image i and class c\n" + " > strategy = smallest class\n" + " + mim = proportional :\n" + " The smallest class size (computed globally) is used for the strategy constant+proportional\n" + " + mim = equal :\n" + " The smallest class size (computed globally) is used for the strategy constant+equal\n" + " + mim = custom :\n" + " The smallest class is computed and used for each image separately\n" + ); + SetDocLimitations("None"); + SetDocAuthors("OTB-Team"); + SetDocSeeAlso(" "); + + AddDocTag(Tags::Learning); + + AddParameter(ParameterType_InputFilenameList, "il", "Input statistics"); + SetParameterDescription("il", "List of statistics files for each input image."); + + AddParameter(ParameterType_OutputFilename, "out", "Output sampling rates"); + SetParameterDescription("out","Output filename storing sampling rates (CSV " + "format with class name, required samples, total samples, and rate). " + "The given filename will be used with a suffix to indicate the " + "corresponding input index (for instance: rates.csv will give rates_1.csv" + ", rates_2.csv, ...)."); + + AddParameter(ParameterType_Choice, "strategy", "Sampling strategy"); + + AddChoice("strategy.byclass","Set samples count for each class"); + SetParameterDescription("strategy.byclass","Set samples count for each class"); + + AddParameter(ParameterType_InputFilenameList, "strategy.byclass.in", "Number of samples by class"); + SetParameterDescription("strategy.byclass.in", "Number of samples by class " + "(CSV format with class name in 1st column and required samples in the 2nd)." + "In the case of the custom multi-image mode, several inputs may be given for each image."); + + AddChoice("strategy.constant","Set the same samples counts for all classes"); + SetParameterDescription("strategy.constant","Set the same samples counts for all classes"); + + AddParameter(ParameterType_String, "strategy.constant.nb", "Number of samples for all classes"); + SetParameterDescription("strategy.constant.nb", "Number of samples for all classes." + "In the case of the custom multi-image mode, several values can be given for each image."); + + AddChoice("strategy.smallest","Set same number of samples for all classes, with the smallest class fully sampled"); + SetParameterDescription("strategy.smallest","Set same number of samples for all classes, with the smallest class fully sampled"); + + AddChoice("strategy.all","Take all samples"); + SetParameterDescription("strategy.all","Take all samples"); + + // Default strategy : smallest + SetParameterString("strategy","smallest"); + + AddParameter(ParameterType_Choice, "mim", "Multi-Image Mode"); + + AddChoice("mim.proportional", "Proportional"); + SetParameterDescription("mim.proportional","Split proportionally the required number of samples"); + + AddChoice("mim.equal", "equal"); + SetParameterDescription("mim.equal","Split equally the required number of samples"); + + AddChoice("mim.custom", "Custom"); + SetParameterDescription("mim.custom","Split the required number of samples following user choice."); + + // Doc example parameter settings + SetDocExampleParameterValue("il", "stats_1.xml stats_2.xml"); + SetDocExampleParameterValue("out", "rates.csv"); + SetDocExampleParameterValue("strategy", "smallest"); + SetDocExampleParameterValue("mode","proportional"); + } + + void DoUpdateParameters() + { + } + + void DoExecute() + { + // Clear state + m_CalculatorList->Clear(); + std::vector<std::string> inputs = this->GetParameterStringList("il"); + unsigned int nbInputs = inputs.size(); + XMLReaderType::Pointer statReader = XMLReaderType::New(); + for (unsigned int i=0 ; i<nbInputs ; i++ ) + { + m_CalculatorList->PushBack(otb::SamplingRateCalculator::New()); + statReader->SetFileName(inputs[i]); + ClassCountMapType classCount = statReader->GetStatisticMapByName<ClassCountMapType>("samplesPerClass"); + m_CalculatorList->SetNthClassCount(i,classCount); + } + + // Cautions : direct mapping between the enum PartitionType and the choice order + RateCalculatorListType::PartitionType partitionMode = + static_cast<RateCalculatorListType::PartitionType>(this->GetParameterInt("mim")); + + unsigned int minParamSize = 1; + if (partitionMode == RateCalculatorListType::CUSTOM) + { + // Check we have enough inputs for the custom mode + minParamSize = nbInputs; + } + + switch (this->GetParameterInt("strategy")) + { + // byclass + case 0: + { + std::vector<std::string> requiredFiles = this->GetParameterStringList("strategy.byclass.in"); + std::vector<ClassCountMapType> requiredCounts; + if (requiredFiles.size() < minParamSize) + { + otbAppLogFATAL("Missing arguments in strategy.byclass.in to process sampling rates"); + } + otbAppLogINFO("Sampling strategy : set number of samples for each class"); + for (unsigned int i=0 ; i<minParamSize ; i++) + { + requiredCounts.push_back(otb::SamplingRateCalculator::ReadRequiredSamples(requiredFiles[i])); + } + m_CalculatorList->SetNbOfSamplesByClass(requiredCounts, partitionMode); + } + break; + // constant + case 1: + { + std::vector<itksys::String> parts = itksys::SystemTools::SplitString(this->GetParameterString("strategy.constant.nb"),' '); + std::vector<unsigned long> countList; + for (unsigned int i=0 ; i<parts.size() ; i++) + { + if (!parts[i].empty()) + { + std::string::size_type pos1 = parts[i].find_first_not_of(" \t"); + std::string::size_type pos2 = parts[i].find_last_not_of(" \t"); + std::string value(parts[i].substr(pos1, pos2 - pos1 + 1)); + countList.push_back(boost::lexical_cast<unsigned long>(parts[i])); + } + } + if (countList.size() < minParamSize) + { + otbAppLogFATAL("Missing arguments in strategy.constant.nb to process sampling rates"); + } + otbAppLogINFO("Sampling strategy : set a constant number of samples for all classes"); + m_CalculatorList->SetNbOfSamplesAllClasses(countList, partitionMode); + } + break; + // smallest class + case 2: + { + otbAppLogINFO("Sampling strategy : fit the number of samples based on the smallest class"); + m_CalculatorList->SetMinimumNbOfSamplesByClass(partitionMode); + } + break; + // all samples + case 3: + { + otbAppLogINFO("Sampling strategy : take all samples"); + m_CalculatorList->SetAllSamples(partitionMode); + } + break; + default: + otbAppLogFATAL("Strategy mode unknown :"<<this->GetParameterString("strategy")); + break; + } + + std::ostringstream oss; + std::string outputPath(this->GetParameterString("out")); + std::string outputBase = outputPath.substr(0, outputPath.find_last_of('.')); + std::string outputExt = outputPath.substr(outputPath.find_last_of('.'), std::string::npos); + unsigned int overflowCount = 0; + for (unsigned int i=0 ; i<nbInputs ; i++ ) + { + // Print results + oss.str(std::string("")); + oss << " className requiredSamples totalSamples rate" << std::endl; + MapRateType rates = m_CalculatorList->GetRatesByClass(i); + MapRateType::const_iterator itRates = rates.begin(); + for(; itRates != rates.end(); ++itRates) + { + otb::SamplingRateCalculator::TripletType tpt = itRates->second; + oss << itRates->first << "\t" << tpt.Required << "\t" << tpt.Tot << "\t" << tpt.Rate; + if (tpt.Required > tpt.Tot) + { + overflowCount++; + oss << "\t[OVERFLOW]"; + } + oss << std::endl; + } + otbAppLogINFO("Sampling rates for image "<< i+1 <<" : " << oss.str()); + // Output results to disk + oss.str(std::string("")); + oss << outputBase << "_" << i+1 << outputExt; + m_CalculatorList->GetNthElement(i)->Write(oss.str()); + } + if (overflowCount) + { + std::string plural(overflowCount>1?"s":""); + otbAppLogWARNING(<< overflowCount << " case"<<plural<<" of overflow detected! (requested number of samples higher than total available samples)"); + } + } + + RateCalculatorListType::Pointer m_CalculatorList; +}; + +} // end namespace Wrapper +} // end namespace otb + +OTB_APPLICATION_EXPORT(otb::Wrapper::MultiImageSamplingRate) diff --git a/Modules/Applications/AppClassification/app/otbSampleSelection.cxx b/Modules/Applications/AppClassification/app/otbSampleSelection.cxx index 4b401889674b4dee94cb15bb01ad974fecc290d1..35a70a2673d1fd3ae2f4388fdf6505fae667c9f8 100644 --- a/Modules/Applications/AppClassification/app/otbSampleSelection.cxx +++ b/Modules/Applications/AppClassification/app/otbSampleSelection.cxx @@ -201,63 +201,6 @@ private: { } - ClassCountMapType ReadRequiredSamples(std::string filename) - { - ClassCountMapType output; - std::ifstream ifs(filename.c_str()); - - if (ifs) - { - std::string line; - std::string sep(""); - - while(!ifs.eof()) - { - std::getline(ifs,line); - if (line.empty()) continue; - std::string::size_type pos = line.find_first_not_of(" \t"); - if (pos != std::string::npos && line[pos] == '#') continue; - - if (sep.size() == 0) - { - // Try to detect the separator - std::string separators("\t;,"); - for (unsigned int k=0 ; k<separators.size() ; k++) - { - std::vector<itksys::String> words = itksys::SystemTools::SplitString(line,separators[k]); - if (words.size() >= 2) - { - sep.push_back(separators[k]); - break; - } - } - if (sep.size() == 0) continue; - } - // parse the line - std::vector<itksys::String> parts = itksys::SystemTools::SplitString(line,sep[0]); - if (parts.size() >= 2) - { - std::string::size_type pos1 = parts[0].find_first_not_of(" \t"); - std::string::size_type pos2 = parts[0].find_last_not_of(" \t"); - std::string::size_type pos3 = parts[1].find_first_not_of(" \t"); - std::string::size_type pos4 = parts[1].find_last_not_of(" \t"); - if (pos1 != std::string::npos && pos3 != std::string::npos) - { - std::string name = parts[0].substr(pos1, pos2 - pos1 + 1); - std::string value = parts[1].substr(pos3, pos4 - pos3 + 1); - output[name] = boost::lexical_cast<unsigned long>(value); - } - } - } - ifs.close(); - } - else - { - otbAppLogFATAL(<< " Couldn't open " << filename); - } - return output; - } - void DoExecute() { // Clear state @@ -280,7 +223,7 @@ private: { otbAppLogINFO("Sampling strategy : set number of samples for each class"); ClassCountMapType requiredCount = - this->ReadRequiredSamples(this->GetParameterString("strategy.byclass.in")); + otb::SamplingRateCalculator::ReadRequiredSamples(this->GetParameterString("strategy.byclass.in")); m_RateCalculator->SetNbOfSamplesByClass(requiredCount); } break; @@ -319,12 +262,24 @@ private: std::ostringstream oss; oss << " className requiredSamples totalSamples rate" << std::endl; MapRateType::const_iterator itRates = rates.begin(); + unsigned int overflowCount = 0; for(; itRates != rates.end(); ++itRates) { otb::SamplingRateCalculator::TripletType tpt = itRates->second; - oss << itRates->first << "\t" << tpt.Required << "\t" << tpt.Tot << "\t" << tpt.Rate << std::endl; + oss << itRates->first << "\t" << tpt.Required << "\t" << tpt.Tot << "\t" << tpt.Rate; + if (tpt.Required > tpt.Tot) + { + overflowCount++; + oss << "\t[OVERFLOW]"; + } + oss << std::endl; } otbAppLogINFO("Sampling rates : " << oss.str()); + if (overflowCount) + { + std::string plural(overflowCount>1?"s":""); + otbAppLogWARNING(<< overflowCount << " case"<<plural<<" of overflow detected! (requested number of samples higher than total available samples)"); + } // Open input geometries otb::ogr::DataSource::Pointer vectors = diff --git a/Modules/Applications/AppClassification/app/otbTrainVectorClassifier.cxx b/Modules/Applications/AppClassification/app/otbTrainVectorClassifier.cxx index 7c9c6d91d8aff60477d5e2a74cf4c3645730def5..09edbc6cf385cb8397ca166d4c9394081e1f6019 100644 --- a/Modules/Applications/AppClassification/app/otbTrainVectorClassifier.cxx +++ b/Modules/Applications/AppClassification/app/otbTrainVectorClassifier.cxx @@ -92,7 +92,7 @@ private: AddParameter(ParameterType_Group, "io", "Input and output data"); SetParameterDescription("io", "This group of parameters allows setting input and output data."); - AddParameter(ParameterType_InputVectorData, "io.vd", "Input Vector Data"); + AddParameter(ParameterType_InputVectorDataList, "io.vd", "Input Vector Data"); SetParameterDescription("io.vd", "Input geometries used for training (note : all geometries from the layer will be used)"); AddParameter(ParameterType_InputFilename, "io.stats", "Input XML image statistics file"); @@ -122,7 +122,7 @@ private: AddParameter(ParameterType_Group, "valid", "Validation data"); SetParameterDescription("valid", "This group of parameters defines validation data."); - AddParameter(ParameterType_InputVectorData, "valid.vd", "Validation Vector Data"); + AddParameter(ParameterType_InputVectorDataList, "valid.vd", "Validation Vector Data"); SetParameterDescription("valid.vd", "Geometries used for validation " "(must contain the same fields used for training, all geometries from the layer will be used)"); MandatoryOff("valid.vd"); @@ -148,9 +148,9 @@ private: { if ( HasValue("io.vd") ) { - std::string vectorFile = GetParameterString("io.vd"); + std::vector<std::string> vectorFileList = GetParameterStringList("io.vd"); ogr::DataSource::Pointer ogrDS = - ogr::DataSource::New(vectorFile, ogr::DataSource::Modes::Read); + ogr::DataSource::New(vectorFileList[0], ogr::DataSource::Modes::Read); ogr::Layer layer = ogrDS->GetLayer(this->GetParameterInt("layer")); ogr::Feature feature = layer.ogr().GetNextFeature(); @@ -242,13 +242,30 @@ void LogConfusionMatrix(ConfusionMatrixCalculatorType* confMatCalc) void DoExecute() { - std::string shapefile = GetParameterString("io.vd"); - std::string modelfile = GetParameterString("io.out"); typedef int LabelPixelType; typedef itk::FixedArray<LabelPixelType,1> LabelSampleType; typedef itk::Statistics::ListSample <LabelSampleType> LabelListSampleType; - const int nbFeatures = GetSelectedItems("feat").size(); + // Prepare selected field names (their position may change between two inputs) + std::vector<int> selectedIdx = GetSelectedItems("feat"); + const unsigned int nbFeatures = selectedIdx.size(); + std::vector<std::string> fieldNames = GetChoiceNames("feat"); + std::vector<std::string> selectedNames(nbFeatures); + for (unsigned int i=0 ; i<nbFeatures ; i++) + { + selectedNames[i] = fieldNames[selectedIdx[i]]; + } + std::vector<int> featureFieldIndex(nbFeatures, -1); + int cFieldIndex = -1; + + // List of available fields + std::ostringstream oss; + for (unsigned int i=0 ; i<fieldNames.size() ; i++) + { + if (i) oss << ", "; + oss << fieldNames[i]; + } + std::string availableFields(oss.str()); // Statistics for shift/scale MeasurementType meanMeasurementVector; @@ -269,50 +286,55 @@ void DoExecute() stddevMeasurementVector.Fill(1.); } - ogr::DataSource::Pointer source = ogr::DataSource::New(shapefile, ogr::DataSource::Modes::Read); - ogr::Layer layer = source->GetLayer(this->GetParameterInt("layer")); - ogr::Feature feature = layer.ogr().GetNextFeature(); - bool goesOn = feature.addr() != 0; - ListSampleType::Pointer input = ListSampleType::New(); LabelListSampleType::Pointer target = LabelListSampleType::New(); input->SetMeasurementVectorSize(nbFeatures); - int cFieldIndex=-1; - std::vector<int> featureFieldIndex = GetSelectedItems("feat"); - if (feature.addr()) + std::vector<std::string> vectorFileList = GetParameterStringList("io.vd"); + for (unsigned int k=0 ; k<vectorFileList.size() ; k++) { - cFieldIndex = feature.ogr().GetFieldIndex(GetParameterString("cfield").c_str()); - } + otbAppLogINFO("Reading input vector file "<<k+1<<"/"<<vectorFileList.size()); + ogr::DataSource::Pointer source = ogr::DataSource::New(vectorFileList[k], ogr::DataSource::Modes::Read); + ogr::Layer layer = source->GetLayer(this->GetParameterInt("layer")); + ogr::Feature feature = layer.ogr().GetNextFeature(); + bool goesOn = feature.addr() != 0; + if (!goesOn) + { + otbAppLogWARNING("The layer "<<GetParameterInt("layer")<<" of " + <<vectorFileList[k]<<" is empty, input is skipped."); + continue; + } - // Check that the class field exists - if (cFieldIndex < 0) - { - std::ostringstream oss; - std::vector<std::string> names = GetChoiceNames("feat"); - for (unsigned int i=0 ; i<names.size() ; i++) + // Check all needed fields are present : + // - check class field + cFieldIndex = feature.ogr().GetFieldIndex(GetParameterString("cfield").c_str()); + if (cFieldIndex < 0) + otbAppLogFATAL("The field name for class label ("<<GetParameterString("cfield") + <<") has not been found in the input vector file! Choices are "<< availableFields); + // - check feature fields + for (unsigned int i=0 ; i<nbFeatures ; i++) { - if (i) oss << ", "; - oss << names[i]; + featureFieldIndex[i] = feature.ogr().GetFieldIndex(selectedNames[i].c_str()); + if (featureFieldIndex[i] < 0) + otbAppLogFATAL("The field name for feature "<<selectedNames[i] + <<" has not been found in the input vector file! Choices are "<< availableFields); } - otbAppLogFATAL("The field name for class label ("<<GetParameterString("cfield") - <<") has not been found in the input vector file! Choices are "<< oss.str()); - } - while(goesOn) - { - if(feature.ogr().IsFieldSet(cFieldIndex)) + while(goesOn) { - MeasurementType mv; - mv.SetSize(nbFeatures); - for(int idx=0; idx < nbFeatures; ++idx) - mv[idx] = feature.ogr().GetFieldAsDouble(featureFieldIndex[idx]); + if(feature.ogr().IsFieldSet(cFieldIndex)) + { + MeasurementType mv; + mv.SetSize(nbFeatures); + for(unsigned int idx=0; idx < nbFeatures; ++idx) + mv[idx] = feature.ogr().GetFieldAsDouble(featureFieldIndex[idx]); - input->PushBack(mv); - target->PushBack(feature.ogr().GetFieldAsInteger(cFieldIndex)); + input->PushBack(mv); + target->PushBack(feature.ogr().GetFieldAsInteger(cFieldIndex)); + } + feature = layer.ogr().GetNextFeature(); + goesOn = feature.addr() != 0; } - feature = layer.ogr().GetNextFeature(); - goesOn = feature.addr() != 0; } ShiftScaleFilterType::Pointer trainingShiftScaleFilter = ShiftScaleFilterType::New(); @@ -321,14 +343,8 @@ void DoExecute() trainingShiftScaleFilter->SetScales(stddevMeasurementVector); trainingShiftScaleFilter->Update(); - ListSampleType::Pointer listSample; - LabelListSampleType::Pointer labelListSample; - - listSample = trainingShiftScaleFilter->GetOutput(); - labelListSample = target; - - ListSampleType::Pointer trainingListSample = listSample; - LabelListSampleType::Pointer trainingLabeledListSample = labelListSample; + ListSampleType::Pointer trainingListSample= trainingShiftScaleFilter->GetOutput(); + TargetListSampleType::Pointer trainingLabeledListSample = target; //-------------------------- // Estimate model @@ -344,34 +360,55 @@ void DoExecute() // Import validation data if (HasValue("valid.vd") && IsParameterEnabled("valid.vd")) { - std::string validFile = this->GetParameterString("valid.vd"); - source = ogr::DataSource::New(validFile, ogr::DataSource::Modes::Read); - layer = source->GetLayer(this->GetParameterInt("valid.layer")); - feature = layer.ogr().GetNextFeature(); - goesOn = feature.addr() != 0; - - // find useful field indexes - - // TODO : detect corresponding indexes in validation data set, for the moment - // Assume they have the same fields, in the same order. - input = ListSampleType::New(); target = LabelListSampleType::New(); input->SetMeasurementVectorSize(nbFeatures); - while(goesOn) + + std::vector<std::string> validFileList = this->GetParameterStringList("valid.vd"); + for (unsigned int k=0 ; k<validFileList.size() ; k++) { - if(feature.ogr().IsFieldSet(cFieldIndex)) + otbAppLogINFO("Reading validation vector file "<<k+1<<"/"<<validFileList.size()); + ogr::DataSource::Pointer source = ogr::DataSource::New(validFileList[k], ogr::DataSource::Modes::Read); + ogr::Layer layer = source->GetLayer(this->GetParameterInt("valid.layer")); + ogr::Feature feature = layer.ogr().GetNextFeature(); + bool goesOn = feature.addr() != 0; + if (!goesOn) { - MeasurementType mv; - mv.SetSize(nbFeatures); - for(int idx=0; idx < nbFeatures; ++idx) - mv[idx] = feature.ogr().GetFieldAsDouble(featureFieldIndex[idx]); + otbAppLogWARNING("The layer "<<GetParameterInt("valid.layer")<<" of " + <<validFileList[k]<<" is empty, input is skipped."); + continue; + } - input->PushBack(mv); - target->PushBack(feature.ogr().GetFieldAsInteger(cFieldIndex)); + // Check all needed fields are present : + // - check class field + cFieldIndex = feature.ogr().GetFieldIndex(GetParameterString("cfield").c_str()); + if (cFieldIndex < 0) + otbAppLogFATAL("The field name for class label ("<<GetParameterString("cfield") + <<") has not been found in the input vector file! Choices are "<< availableFields); + // - check feature fields + for (unsigned int i=0 ; i<nbFeatures ; i++) + { + featureFieldIndex[i] = feature.ogr().GetFieldIndex(selectedNames[i].c_str()); + if (featureFieldIndex[i] < 0) + otbAppLogFATAL("The field name for feature "<<selectedNames[i] + <<" has not been found in the input vector file! Choices are "<< availableFields); + } + + while(goesOn) + { + if(feature.ogr().IsFieldSet(cFieldIndex)) + { + MeasurementType mv; + mv.SetSize(nbFeatures); + for(unsigned int idx=0; idx < nbFeatures; ++idx) + mv[idx] = feature.ogr().GetFieldAsDouble(featureFieldIndex[idx]); + + input->PushBack(mv); + target->PushBack(feature.ogr().GetFieldAsInteger(cFieldIndex)); + } + feature = layer.ogr().GetNextFeature(); + goesOn = feature.addr() != 0; } - feature = layer.ogr().GetNextFeature(); - goesOn = feature.addr() != 0; } ShiftScaleFilterType::Pointer validShiftScaleFilter = ShiftScaleFilterType::New(); diff --git a/Modules/Applications/AppClassification/test/CMakeLists.txt b/Modules/Applications/AppClassification/test/CMakeLists.txt index 2cd581340af32dc944aa1b9f8d665e35535fbfb0..5e9109e01b0a8aa3e65c06aefe3d978889a14f7b 100644 --- a/Modules/Applications/AppClassification/test/CMakeLists.txt +++ b/Modules/Applications/AppClassification/test/CMakeLists.txt @@ -897,4 +897,23 @@ if(OTB_USE_OPENCV) VALID --compare-ascii ${NOTOL} ${OTBAPP_BASELINE_FILES}/apTvClTrainVectorClassifierModel.rf ${TEMP}/apTvClTrainVectorClassifierModel.rf) -endif() \ No newline at end of file +endif() + +#------------ MultiImageSamplingRate TESTS ---------------- +otb_test_application(NAME apTvClMultiImageSamplingRate + APP MultiImageSamplingRate + OPTIONS -il ${INPUTDATA}/Classification/vector_stats_QB1.xml + ${INPUTDATA}/Classification/vector_stats_QB2.xml + ${INPUTDATA}/Classification/vector_stats_QB3.xml + -out ${TEMP}/apTvClMultiImageSamplingRate_out.csv + -strategy constant + -strategy.constant.nb "300" + -mim proportional + VALID --compare-n-ascii ${NOTOL} 3 + ${OTBAPP_BASELINE_FILES}/apTvClMultiImageSamplingRate_out_1.csv + ${TEMP}/apTvClMultiImageSamplingRate_out_1.csv + ${OTBAPP_BASELINE_FILES}/apTvClMultiImageSamplingRate_out_2.csv + ${TEMP}/apTvClMultiImageSamplingRate_out_2.csv + ${OTBAPP_BASELINE_FILES}/apTvClMultiImageSamplingRate_out_3.csv + ${TEMP}/apTvClMultiImageSamplingRate_out_3.csv + ) diff --git a/Modules/Applications/AppStereo/app/otbFineRegistration.cxx b/Modules/Applications/AppStereo/app/otbFineRegistration.cxx index 33481d3185cb8c5d25ae58798e8315b8dac57d12..8808d6d1cba02fa416808f41559c54472a721146 100644 --- a/Modules/Applications/AppStereo/app/otbFineRegistration.cxx +++ b/Modules/Applications/AppStereo/app/otbFineRegistration.cxx @@ -205,6 +205,12 @@ private: SetMinimumParameterFloatValue("spa", 0.0); MandatoryOff("spa"); + AddParameter(ParameterType_Float, "cva", "ConvergenceAccuracy"); + SetParameterDescription( "cva", "Metric extrema will be refined up to the given accuracy. Default is 0.01" ); + SetDefaultParameterFloat("cva", 0.01); + SetMinimumParameterFloatValue("cva", 0.0); + MandatoryOff("cva"); + AddParameter(ParameterType_Float, "vmlt", "Validity Mask Lower Threshold"); SetParameterDescription( "vmlt", "Lower threshold to obtain a validity mask." ); MandatoryOff("vmlt"); @@ -249,8 +255,6 @@ private: radius[0] = GetParameterInt("mrx"); radius[1] = GetParameterInt("mry"); - double accuracy = static_cast<double>(GetParameterFloat("spa")); - ssrate[0] = GetParameterFloat("ssrx"); ssrate[1] = GetParameterFloat("ssry"); @@ -265,12 +269,22 @@ private: otbAppLogINFO("Metric radius : "<<radius<<" (pixels)"); otbAppLogINFO("Sub-sampling rate : "<<ssrate<<" (pixels)"); otbAppLogINFO("Coarse offset : "<<initialOffset<<" (physical unit)"); - otbAppLogINFO("Accuracy : "<<accuracy<<" (physical unit)"); m_Registration = RegistrationFilterType::New(); m_Registration->SetRadius(radius); m_Registration->SetSearchRadius(sradius); - m_Registration->SetSubPixelAccuracy(accuracy); + if (IsParameterEnabled("cva") && HasValue("cva")) + { + double convAccuracy = static_cast<double>(GetParameterFloat("cva")); + m_Registration->SetConvergenceAccuracy(convAccuracy); + otbAppLogINFO("Convergence Accuracy : "<<convAccuracy<<" (physical unit)"); + } + if (IsParameterEnabled("spa") && HasValue("spa")) + { + double subPixAccuracy = static_cast<double>(GetParameterFloat("spa")); + m_Registration->SetSubPixelAccuracy(subPixAccuracy); + otbAppLogINFO("SubPixel Accuracy : "<<subPixAccuracy<<" (physical unit)"); + } m_Registration->SetGridStep(ssrate); m_Registration->SetInitialOffset(initialOffset); diff --git a/Modules/Applications/AppStereo/test/CMakeLists.txt b/Modules/Applications/AppStereo/test/CMakeLists.txt index fa2da88605e0afbba9394deaac40281ab7219cc2..36883cd248fda8d6269cc7a4eb2e3de7ae0007b5 100644 --- a/Modules/Applications/AppStereo/test/CMakeLists.txt +++ b/Modules/Applications/AppStereo/test/CMakeLists.txt @@ -85,6 +85,7 @@ otb_test_application(NAME apTvDmFineRegistrationTest -ssrx 8 -ssry 8 -spa 0.1 + -cva 0.01 -cox -2 -vmlt 0.999 VALID --compare-image ${EPSILON_10} diff --git a/Modules/Filtering/Statistics/src/otbSamplerBase.cxx b/Modules/Filtering/Statistics/src/otbSamplerBase.cxx index 45ed4efe3820b2ff9f1ff147bbe07812891bac9f..8d84acaa395ff202cfccd4924b2e05ccb55e6f1b 100644 --- a/Modules/Filtering/Statistics/src/otbSamplerBase.cxx +++ b/Modules/Filtering/Statistics/src/otbSamplerBase.cxx @@ -18,6 +18,8 @@ #include "otbSamplerBase.h" #include "otbMath.h" +#include "itkMath.h" +#include <cmath> namespace otb { @@ -33,14 +35,16 @@ void SamplerBase::SetNumberOfElements(unsigned long needed, unsigned long total) { bool modified = false; + unsigned long neededChecked = needed; if (needed > total) { - itkExceptionMacro(<< "Needed elements (" << needed << - ") greater than total elements" << total << ")." << std::endl); + itkWarningMacro(<< "Needed elements (" << needed << + ") will be clamped to total elements (" << total << ")" << std::endl); + neededChecked = total; } - if (m_NeededElements != needed) + if (m_NeededElements != neededChecked) { - m_NeededElements = needed; + m_NeededElements = neededChecked; modified = true; } if (m_TotalElements != total) @@ -66,9 +70,22 @@ void SamplerBase::SetRate(double rate, unsigned long total) { bool modified = false; - if (m_Rate != rate) + double rateChecked = rate; + if (rate > 1.0) { - m_Rate = rate; + itkWarningMacro(<< "Rate (" << rate << + ") will be clamped to 1.0" << std::endl); + rateChecked = 1.0; + } + if (rate < 0.0) + { + itkWarningMacro(<< "Rate (" << rate << + ") will be clamped to 0.0" << std::endl); + rateChecked = 0.0; + } + if (std::fabs(m_Rate-rateChecked) > 1e-12) + { + m_Rate = rateChecked; modified = true; } if (m_TotalElements != total) diff --git a/Modules/Learning/Sampling/include/otbSamplingRateCalculator.h b/Modules/Learning/Sampling/include/otbSamplingRateCalculator.h index bb66f69478be08073eeca38b12294ba3745de5f6..8743a95553695f3f3e5bff34a1e5ab0378f30bbf 100644 --- a/Modules/Learning/Sampling/include/otbSamplingRateCalculator.h +++ b/Modules/Learning/Sampling/include/otbSamplingRateCalculator.h @@ -38,7 +38,7 @@ class ITK_EXPORT SamplingRateCalculator { public: /** Standard typedefs */ - typedef SamplingRateCalculator Self; + typedef SamplingRateCalculator Self; typedef itk::Object Superclass; typedef itk::SmartPointer<Self> Pointer; typedef itk::SmartPointer<const Self> ConstPointer; @@ -94,6 +94,8 @@ public: /** Clear internal data */ void ClearRates(void); + static ClassCountMapType ReadRequiredSamples(const std::string& filename); + protected: /** Constructor */ SamplingRateCalculator(); diff --git a/Modules/Learning/Sampling/include/otbSamplingRateCalculatorList.h b/Modules/Learning/Sampling/include/otbSamplingRateCalculatorList.h new file mode 100644 index 0000000000000000000000000000000000000000..2e1b622efa8e9c9db7f4fca98f58eff96c92ca0d --- /dev/null +++ b/Modules/Learning/Sampling/include/otbSamplingRateCalculatorList.h @@ -0,0 +1,103 @@ +/*========================================================================= + + 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 otbSamplingRateCalculatorList_h +#define otbSamplingRateCalculatorList_h + +#include "otbSamplingRateCalculator.h" +#include "otbObjectList.h" + +namespace otb +{ +/** \class SamplingRateCalculatorList + * \brief This class is a list of sampling rate calculators. + * + * It computes sampling rates for each image and each class, given input + * statistics on available samples, and a sampling strategy. The sampling + * strategies are made from the combination of : + * - the sampling strategies of otb::SamplingRateCalculator (mono-image) + * - a multi-image behaviour : proportional / constant / custom + * + * \ingroup OTBSampling + */ +class ITK_EXPORT SamplingRateCalculatorList + : public ObjectList<SamplingRateCalculator> +{ +public: + /** Standard typedefs */ + typedef SamplingRateCalculatorList Self; + typedef ObjectList<SamplingRateCalculator> Superclass; + typedef itk::SmartPointer<Self> Pointer; + typedef itk::SmartPointer<const Self> ConstPointer; + + typedef SamplingRateCalculator::ClassCountMapType ClassCountMapType; + typedef SamplingRateCalculator::MapRateType MapRateType; + + enum PartitionType + { + PROPORTIONAL, + EQUAL, + CUSTOM + }; + + /** Type macro */ + itkNewMacro(Self); + + /** Creation through object factory macro */ + itkTypeMacro(SamplingRateCalculatorList, ObjectList); + + /** Set the class counts for input 'index' */ + void SetNthClassCount(unsigned int index,const ClassCountMapType &map); + + /** Get the sampling rates computed for input 'index' */ + const MapRateType & GetRatesByClass(unsigned int index); + + /** Clear internal data */ + void ClearRates(void); + + /** Method to select all available samples */ + void SetAllSamples(PartitionType t); + + /** Method to choose a sampling strategy based on the smallest class. + * The number of samples in each class is set to this minimum size*/ + void SetMinimumNbOfSamplesByClass(PartitionType t); + + /** Method to set the same number of required samples in each class */ + void SetNbOfSamplesAllClasses(std::vector<unsigned long> &nb, PartitionType t); + + /** Method to manually set the number of samples required in each class */ + void SetNbOfSamplesByClass(const std::vector<ClassCountMapType> &required, PartitionType t); + +protected: + /** Constructor */ + SamplingRateCalculatorList(){} + + /** Destructor */ + ~SamplingRateCalculatorList() ITK_OVERRIDE {} + +private: + SamplingRateCalculatorList(const Self &); //purposely not implemented + void operator =(const Self&); //purposely not implemented + + void UpdateGlobalCounts(); + + ClassCountMapType m_GlobalCountMap; + +}; +} // end namespace otb + +#endif diff --git a/Modules/Learning/Sampling/src/CMakeLists.txt b/Modules/Learning/Sampling/src/CMakeLists.txt index 90087a2744a9e007261d971e807d54ca7cca3d23..2ffa8f935110bfe23da6f70abca35219fd31c2a2 100644 --- a/Modules/Learning/Sampling/src/CMakeLists.txt +++ b/Modules/Learning/Sampling/src/CMakeLists.txt @@ -1,5 +1,6 @@ set(OTBSampling_SRC otbSamplingRateCalculator.cxx + otbSamplingRateCalculatorList.cxx ) add_library(OTBSampling ${OTBSampling_SRC}) diff --git a/Modules/Learning/Sampling/src/otbSamplingRateCalculator.cxx b/Modules/Learning/Sampling/src/otbSamplingRateCalculator.cxx index 2378ff1e42bc4e76dea516b9526050f7160b0bbf..65a85589b9450b7d33d3c801d5a03ae3816369c5 100644 --- a/Modules/Learning/Sampling/src/otbSamplingRateCalculator.cxx +++ b/Modules/Learning/Sampling/src/otbSamplingRateCalculator.cxx @@ -90,8 +90,8 @@ SamplingRateCalculator else { TripletType triplet; - triplet.Tot = it->second; - triplet.Required = 0UL; + triplet.Tot = 0UL; + triplet.Required = it->second; triplet.Rate = 0.0; m_RatesByClass[it->first] = triplet; } @@ -134,56 +134,65 @@ SamplingRateCalculator { std::ifstream ifs(filename.c_str()); + typedef std::vector<boost::iterator_range<std::string::const_iterator> > ListType; + if (ifs) { this->ClearRates(); std::string line; TripletType tpt; std::string sep(""); - while(!ifs.eof()) + ListType parts; + while(std::getline(ifs,line)) { - std::getline(ifs,line); if (line.empty()) continue; std::string::size_type pos = line.find_first_not_of(" \t"); if (pos != std::string::npos && line[pos] == '#') continue; - if (sep.size() == 0) + if (sep.empty()) { // Try to detect the separator - std::string separators("\t;,"); + std::vector<std::string> separators(4); + separators[0] = "\t"; + separators[1] = ";"; + separators[2] = ","; + separators[3] = " "; + ListType words; for (unsigned int k=0 ; k<separators.size() ; k++) { - std::vector<itksys::String> words = itksys::SystemTools::SplitString(line,separators[k]); + boost::split(words, line, boost::is_any_of(separators[k])); if (words.size() == 4) { - sep.push_back(separators[k]); + sep = separators[k]; break; } } - if (sep.size() == 0) continue; + if (sep.empty()) continue; } // parse the line - std::vector<itksys::String> parts = itksys::SystemTools::SplitString(line,sep[0]); + boost::split(parts, line, boost::is_any_of(sep)); if (parts.size() == 4) { - std::string::size_type pos1 = parts[0].find_first_not_of(" \t"); - std::string::size_type pos2 = parts[0].find_last_not_of(" \t"); - std::string::size_type pos3 = parts[1].find_first_not_of(" \t"); - std::string::size_type pos4 = parts[1].find_last_not_of(" \t"); - std::string::size_type pos5 = parts[2].find_first_not_of(" \t"); - std::string::size_type pos6 = parts[2].find_last_not_of(" \t"); - std::string::size_type pos7 = parts[3].find_first_not_of(" \t"); - std::string::size_type pos8 = parts[3].find_last_not_of(" \t"); - if (pos1 != std::string::npos && pos3 != std::string::npos && - pos5 != std::string::npos && pos7 != std::string::npos) + std::string::size_type pos1 = line.find_first_not_of(" \t", parts[0].begin() - line.begin()); + std::string::size_type pos2 = line.find_last_not_of(" \t", parts[0].end() - line.begin() -1); + std::string::size_type pos3 = line.find_first_not_of(" \t", parts[1].begin() - line.begin()); + std::string::size_type pos4 = line.find_last_not_of(" \t", parts[1].end() - line.begin() -1); + std::string::size_type pos5 = line.find_first_not_of(" \t", parts[2].begin() - line.begin()); + std::string::size_type pos6 = line.find_last_not_of(" \t", parts[2].end() - line.begin() -1); + std::string::size_type pos7 = line.find_first_not_of(" \t", parts[3].begin() - line.begin()); + std::string::size_type pos8 = line.find_last_not_of(" \t", parts[3].end() - line.begin() -1); + if (pos2 != std::string::npos && pos1 <= pos2 && + pos4 != std::string::npos && pos3 <= pos4 && + pos6 != std::string::npos && pos5 <= pos6 && + pos8 != std::string::npos && pos7 <= pos8) { - std::string name = parts[0].substr(pos1, pos2 - pos1 + 1); - std::string val1 = parts[1].substr(pos3, pos4 - pos3 + 1); - std::string val2 = parts[2].substr(pos5, pos6 - pos5 + 1); - std::string val3 = parts[3].substr(pos7, pos8 - pos7 + 1); - tpt.Required = boost::lexical_cast<unsigned long>(val1); - tpt.Tot = boost::lexical_cast<unsigned long>(val2); - tpt.Rate = boost::lexical_cast<double>(val3); + std::string name = line.substr(pos1, pos2 - pos1 + 1); + std::string val1 = line.substr(pos3, pos4 - pos3 + 1); + std::string val2 = line.substr(pos5, pos6 - pos5 + 1); + std::string val3 = line.substr(pos7, pos8 - pos7 + 1); + tpt.Required = Utils::LexicalCast<unsigned long>(val1, "number of samples"); + tpt.Tot = Utils::LexicalCast<unsigned long>(val2, "number of samples"); + tpt.Rate = Utils::LexicalCast<double>(val3, "rate"); m_RatesByClass[name] = tpt; } } @@ -226,6 +235,73 @@ SamplingRateCalculator m_RatesByClass.clear(); } +SamplingRateCalculator::ClassCountMapType +SamplingRateCalculator +::ReadRequiredSamples(const std::string& filename) +{ + ClassCountMapType output; + std::ifstream ifs(filename.c_str()); + + typedef std::vector<boost::iterator_range<std::string::const_iterator> > ListType; + + if (ifs) + { + std::string line; + std::string sep(""); + ListType parts; + + while(std::getline(ifs,line)) + { + if (line.empty()) continue; + std::string::size_type pos = line.find_first_not_of(" \t"); + if (pos != std::string::npos && line[pos] == '#') continue; + + if (sep.empty()) + { + // Try to detect the separator + std::vector<std::string> separators(4); + separators[0] = "\t"; + separators[1] = ";"; + separators[2] = ","; + separators[3] = " "; + ListType words; + for (unsigned int k=0 ; k<separators.size() ; k++) + { + boost::split(words, line, boost::is_any_of(separators[k])); + if (words.size() >= 2) + { + sep = separators[k]; + break; + } + } + if (sep.empty()) continue; + } + // parse the line + boost::split(parts, line, boost::is_any_of(sep)); + if (parts.size() >= 2) + { + std::string::size_type pos1 = line.find_first_not_of(" \t", parts[0].begin() - line.begin()); + std::string::size_type pos2 = line.find_last_not_of(" \t", parts[0].end() - line.begin() -1); + std::string::size_type pos3 = line.find_first_not_of(" \t", parts[1].begin() - line.begin()); + std::string::size_type pos4 = line.find_last_not_of(" \t", parts[1].end() - line.begin() -1); + if (pos2 != std::string::npos && pos1 <= pos2 && + pos4 != std::string::npos && pos3 <= pos4) + { + std::string name = line.substr(pos1, pos2 - pos1 + 1); + std::string value = line.substr(pos3, pos4 - pos3 + 1); + output[name] = Utils::LexicalCast<unsigned long>(value, "number of samples"); + } + } + } + ifs.close(); + } + else + { + itkGenericExceptionMacro(<< " Couldn't open " << filename); + } + return output; +} + void SamplingRateCalculator ::UpdateRate(const std::string &name) diff --git a/Modules/Learning/Sampling/src/otbSamplingRateCalculatorList.cxx b/Modules/Learning/Sampling/src/otbSamplingRateCalculatorList.cxx new file mode 100644 index 0000000000000000000000000000000000000000..829e3aefd6b3a2468826d8772c24eb7bdd3aa81a --- /dev/null +++ b/Modules/Learning/Sampling/src/otbSamplingRateCalculatorList.cxx @@ -0,0 +1,304 @@ +/*========================================================================= + + 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. + +=========================================================================*/ + +#include "otbSamplingRateCalculatorList.h" +#include "otbMacro.h" + +namespace otb +{ + +void +SamplingRateCalculatorList +::SetNthClassCount(unsigned int index,const ClassCountMapType &map) +{ + if (index >= this->Size()) + { + unsigned int currentSize = this->Size(); + for (unsigned int i = currentSize ; i < (index+1) ; i++ ) + { + this->PushBack(SamplingRateCalculator::New()); + } + } + this->GetNthElement(index)->SetClassCount(map); +} + +const SamplingRateCalculatorList::MapRateType & +SamplingRateCalculatorList +::GetRatesByClass(unsigned int index) +{ + if (index >= this->Size()) + { + itkGenericExceptionMacro("Requesting an index ("<<index<<") larger than list size ("<<this->Size()<<")"); + } + return this->GetNthElement(index)->GetRatesByClass(); +} + +void +SamplingRateCalculatorList +::ClearRates(void) +{ + for (unsigned int i=0 ; i<this->Size() ; i++) + { + this->GetNthElement(i)->ClearRates(); + } +} + +void +SamplingRateCalculatorList +::SetAllSamples(PartitionType t) +{ + this->UpdateGlobalCounts(); + switch (t) + { + case PROPORTIONAL: + case EQUAL: + case CUSTOM: + { + for (unsigned int i=0 ; i<this->Size() ; i++) + { + this->GetNthElement(i)->SetAllSamples(); + } + break; + } + default: + itkGenericExceptionMacro("Unknown partition mode"); + break; + } +} + +void +SamplingRateCalculatorList +::SetMinimumNbOfSamplesByClass(PartitionType t) +{ + this->UpdateGlobalCounts(); + switch (t) + { + case PROPORTIONAL: + case EQUAL: + { + // Use the smallest class globally to derive the constant needed + unsigned long smallest = itk::NumericTraits<unsigned long>::max(); + ClassCountMapType::iterator it = m_GlobalCountMap.begin(); + for (; it != m_GlobalCountMap.end() ; ++it) + { + if (smallest > it->second && it->second > 0UL) + { + smallest = it->second; + } + } + if (smallest == itk::NumericTraits<unsigned long>::max()) + { + otbWarningMacro("All classes are empty !"); + smallest = 0UL; + } + std::vector<unsigned long> needed; + needed.push_back(smallest); + this->SetNbOfSamplesAllClasses(needed,t); + break; + } + case CUSTOM: + { + // Use the smallest class in each input + for (unsigned int i=0 ; i<this->Size() ; i++) + { + this->GetNthElement(i)->SetMinimumNbOfSamplesByClass(); + } + break; + } + default: + itkGenericExceptionMacro("Unknown partition mode"); + break; + } +} + +void +SamplingRateCalculatorList +::SetNbOfSamplesAllClasses(std::vector<unsigned long> &nb, PartitionType t) +{ + if (nb.empty()) + { + itkGenericExceptionMacro("No number of samples given"); + } + this->UpdateGlobalCounts(); + ClassCountMapType::const_iterator it; + ClassCountMapType needed; + switch (t) + { + case PROPORTIONAL: + { + for (unsigned int i=0 ; i<this->Size() ; i++) + { + needed.clear(); + for (it = m_GlobalCountMap.begin(); it != m_GlobalCountMap.end() ; ++it) + { + const MapRateType& rates = this->GetNthElement(i)->GetRatesByClass(); + MapRateType::const_iterator curIt = rates.find(it->first); + if (curIt != rates.end() && + it->second > 0UL) + { + unsigned long curTotal = (curIt->second).Tot; + needed[it->first] = static_cast<unsigned long>(vcl_floor( + static_cast<double>(nb[0]) * + static_cast<double>(curTotal) / + static_cast<double>(it->second))); + } + else + { + needed[it->first] = 0UL; + } + } + this->GetNthElement(i)->SetNbOfSamplesByClass(needed); + } + break; + } + case EQUAL: + { + for (unsigned int i=0 ; i<this->Size() ; i++) + { + this->GetNthElement(i)->SetNbOfSamplesAllClasses( + static_cast<unsigned long>(vcl_floor( + (double)nb[0] / (double)this->Size()))); + } + break; + } + case CUSTOM: + { + if (nb.size() < this->Size()) + { + itkGenericExceptionMacro("Not enough values present to set custom requested numbers in all inputs"); + } + for (unsigned int i=0 ; i<this->Size() ; i++) + { + this->GetNthElement(i)->SetNbOfSamplesAllClasses(nb[i]); + } + break; + } + default: + itkGenericExceptionMacro("Unknown partition mode"); + break; + } +} + +void +SamplingRateCalculatorList +::SetNbOfSamplesByClass(const std::vector<ClassCountMapType> &required, PartitionType t) +{ + if (required.empty()) + { + itkGenericExceptionMacro("No number of samples given"); + } + this->UpdateGlobalCounts(); + ClassCountMapType::const_iterator it; + ClassCountMapType::const_iterator inputIt; + ClassCountMapType needed; + switch (t) + { + case PROPORTIONAL: + { + for (unsigned int i=0 ; i<this->Size() ; i++) + { + needed.clear(); + for (it = m_GlobalCountMap.begin(); it != m_GlobalCountMap.end() ; ++it) + { + const MapRateType &rates = this->GetNthElement(i)->GetRatesByClass(); + MapRateType::const_iterator curIt = rates.find(it->first); + inputIt = required[0].find(it->first); + if (curIt != rates.end() && + inputIt != required[0].end() && + it->second > 0UL) + { + unsigned long curTotal = (curIt->second).Tot; + needed[it->first] = static_cast<unsigned long>(vcl_floor( + static_cast<double>(inputIt->second) * + static_cast<double>(curTotal) / + static_cast<double>(it->second))); + } + else + { + needed[it->first] = 0UL; + } + } + this->GetNthElement(i)->SetNbOfSamplesByClass(needed); + } + break; + } + case EQUAL: + { + for (unsigned int i=0 ; i<this->Size() ; i++) + { + needed.clear(); + for (it = m_GlobalCountMap.begin(); it != m_GlobalCountMap.end() ; ++it) + { + const MapRateType &rates = this->GetNthElement(i)->GetRatesByClass(); + MapRateType::const_iterator curIt = rates.find(it->first); + inputIt = required[0].find(it->first); + if (curIt != rates.end() && + inputIt != required[0].end() && + it->second > 0UL) + { + unsigned long curTotal = (curIt->second).Tot; + unsigned long curNeeded = static_cast<unsigned long>(vcl_floor( + static_cast<double>(inputIt->second) / + static_cast<double>(this->Size()))); + needed[it->first] = std::min(curTotal,curNeeded); + } + else + { + needed[it->first] = 0UL; + } + } + this->GetNthElement(i)->SetNbOfSamplesByClass(needed); + } + break; + } + case CUSTOM: + { + if (required.size() < this->Size()) + { + itkGenericExceptionMacro("Not enough values present to set custom requested numbers in all inputs"); + } + for (unsigned int i=0 ; i<this->Size() ; i++) + { + this->GetNthElement(i)->SetNbOfSamplesByClass(required[i]); + } + break; + } + default: + itkGenericExceptionMacro("Unknown partition mode"); + break; + } +} + +void +SamplingRateCalculatorList +::UpdateGlobalCounts() +{ + m_GlobalCountMap.clear(); + for (unsigned int i=0 ; i<this->Size() ; i++) + { + const MapRateType &rates = this->GetNthElement(i)->GetRatesByClass(); + MapRateType::const_iterator it = rates.begin(); + for (; it != rates.end() ; ++it) + { + m_GlobalCountMap[it->first] += it->second.Tot; + } + } +} + +} // end of namespace otb + diff --git a/Modules/Learning/Sampling/test/CMakeLists.txt b/Modules/Learning/Sampling/test/CMakeLists.txt index 7c7fd77347377ad6c7265285b0004834b2455632..e3273eec8205c16aaaaf638941c919cc1d09b40d 100644 --- a/Modules/Learning/Sampling/test/CMakeLists.txt +++ b/Modules/Learning/Sampling/test/CMakeLists.txt @@ -6,6 +6,7 @@ otbOGRDataToSamplePositionFilterTest.cxx otbSamplingRateCalculatorTest.cxx otbOGRDataToClassStatisticsFilterTest.cxx otbImageSampleExtractorFilterTest.cxx +otbSamplingRateCalculatorListTest.cxx ) add_executable(otbSamplingTestDriver ${OTBSamplingTests}) @@ -92,3 +93,16 @@ otb_add_test(NAME leTvImageSampleExtractorFilterUpdate COMMAND otbSamplingTestDr otbImageSampleExtractorFilterUpdate ${INPUTDATA}/variousVectors.sqlite ${TEMP}/leTvImageSampleExtractorFilterUpdateTest.shp) + +# ---------------- SamplingRateCalculatorList --------------------------------- +otb_add_test(NAME leTuSamplingRateCalculatorListNew COMMAND otbSamplingTestDriver + otbSamplingRateCalculatorListNew + + ) + +otb_add_test(NAME leTvSamplingRateCalculatorList COMMAND otbSamplingTestDriver + --compare-ascii ${NOTOL} + ${BASELINE_FILES}/leTvSamplingRateCalculatorList.txt + ${TEMP}/leTvSamplingRateCalculatorList.txt + otbSamplingRateCalculatorList + ${TEMP}/leTvSamplingRateCalculatorList.txt) diff --git a/Modules/Learning/Sampling/test/otbSamplingRateCalculatorListTest.cxx b/Modules/Learning/Sampling/test/otbSamplingRateCalculatorListTest.cxx new file mode 100644 index 0000000000000000000000000000000000000000..6210ed101e99f7e87cbdc28f2aba332aada775d1 --- /dev/null +++ b/Modules/Learning/Sampling/test/otbSamplingRateCalculatorListTest.cxx @@ -0,0 +1,188 @@ +/*========================================================================= + + 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. + +=========================================================================*/ + +#include "otbSamplingRateCalculatorList.h" + +#include "otbStatisticsXMLFileReader.h" +#include "itkVariableLengthVector.h" +#include <fstream> + +int otbSamplingRateCalculatorListNew(int itkNotUsed(argc), char* itkNotUsed(argv) []) +{ + typedef otb::SamplingRateCalculatorList RateCalculatorListType; + + RateCalculatorListType::Pointer rateCalculator = RateCalculatorListType::New(); + std::cout << rateCalculator << std::endl; + return EXIT_SUCCESS; +} + +int otbSamplingRateCalculatorList(int itkNotUsed(argc), char* argv[]) +{ + std::ofstream file(argv[1], std::ios::out | std::ios::trunc); + + if (!file) + { + std::cout << " Couldn't open " << argv[1]; + return EXIT_FAILURE; + } + + itk::Indent indent_1(1); + + typedef otb::SamplingRateCalculatorList::ClassCountMapType ClassCountMapType; + typedef otb::SamplingRateCalculatorList::PartitionType PartitionType; + + PartitionType typeProportional = otb::SamplingRateCalculatorList::PROPORTIONAL; + PartitionType typeEqual = otb::SamplingRateCalculatorList::EQUAL; + PartitionType typeCustom = otb::SamplingRateCalculatorList::CUSTOM; + + std::string c1("1"); + std::string c2("2"); + std::string c3("3"); + std::string c4("4"); + + ClassCountMapType classCount1, classCount2, classCount3; + // input 1 + classCount1[c1] = 104; + classCount1[c2] = 160; + classCount1[c3] = 211; + // input 2 + classCount2[c1] = 98; + classCount2[c2] = 190; + classCount2[c3] = 178; + // input 3 + classCount3[c1] = 130; + classCount3[c2] = 144; + classCount3[c4] = 250; + + std::vector<unsigned long> nbSamplesCst; + nbSamplesCst.push_back(151); + + std::vector<unsigned long> nbSamplesCstCustom; + nbSamplesCstCustom.push_back(70); + nbSamplesCstCustom.push_back(80); + nbSamplesCstCustom.push_back(90); + + ClassCountMapType needed1; + needed1[c1] = 140; + needed1[c2] = 130; + needed1[c3] = 124; + + std::vector<ClassCountMapType> nbByClass; + nbByClass.push_back(needed1); + + ClassCountMapType needed2; + needed2[c1] = 67; + needed2[c2] = 55; + needed2[c3] = 72; + + ClassCountMapType needed3; + needed3[c1] = 77; + needed3[c2] = 69; + needed3[c3] = 34; + + ClassCountMapType needed4; + needed4[c1] = 86; + needed4[c2] = 43; + needed4[c4] = 98; + + std::vector<ClassCountMapType> nbByClassCustom; + nbByClassCustom.push_back(needed2); + nbByClassCustom.push_back(needed3); + nbByClassCustom.push_back(needed4); + + typedef otb::SamplingRateCalculatorList RateCalculatorListType; + RateCalculatorListType::Pointer rateCalcList = RateCalculatorListType::New(); + rateCalcList->SetNthClassCount(0,classCount1); + rateCalcList->SetNthClassCount(2,classCount3); + rateCalcList->SetNthClassCount(1,classCount2); + + file << "# Test the strategy : smallest - equal" << std::endl; + rateCalcList->SetMinimumNbOfSamplesByClass(typeEqual); + for (unsigned int i=0 ; i<rateCalcList->Size() ; i++) + { + file << "# Input "<< i << std::endl; + rateCalcList->GetNthElement(i)->Print(file, indent_1); + } + + file << "# Test the strategy : smallest - custom" << std::endl; + rateCalcList->SetMinimumNbOfSamplesByClass(typeCustom); + for (unsigned int i=0 ; i<rateCalcList->Size() ; i++) + { + file << "# Input "<< i << std::endl; + rateCalcList->GetNthElement(i)->Print(file, indent_1); + } + + file << "# Test the strategy : constant - proportional" << std::endl; + rateCalcList->SetNbOfSamplesAllClasses(nbSamplesCst,typeProportional); + for (unsigned int i=0 ; i<rateCalcList->Size() ; i++) + { + file << "# Input "<< i << std::endl; + rateCalcList->GetNthElement(i)->Print(file, indent_1); + } + + file << "# Test the strategy : constant - equal" << std::endl; + rateCalcList->SetNbOfSamplesAllClasses(nbSamplesCst,typeEqual); + for (unsigned int i=0 ; i<rateCalcList->Size() ; i++) + { + file << "# Input "<< i << std::endl; + rateCalcList->GetNthElement(i)->Print(file, indent_1); + } + + file << "# Test the strategy : constant - custom" << std::endl; + rateCalcList->SetNbOfSamplesAllClasses(nbSamplesCstCustom,typeCustom); + for (unsigned int i=0 ; i<rateCalcList->Size() ; i++) + { + file << "# Input "<< i << std::endl; + rateCalcList->GetNthElement(i)->Print(file, indent_1); + } + + file << "# Test the strategy : byClass - proportional" << std::endl; + rateCalcList->SetNbOfSamplesByClass(nbByClass,typeProportional); + for (unsigned int i=0 ; i<rateCalcList->Size() ; i++) + { + file << "# Input "<< i << std::endl; + rateCalcList->GetNthElement(i)->Print(file, indent_1); + } + + file << "# Test the strategy : byClass - equal" << std::endl; + rateCalcList->SetNbOfSamplesByClass(nbByClass,typeEqual); + for (unsigned int i=0 ; i<rateCalcList->Size() ; i++) + { + file << "# Input "<< i << std::endl; + rateCalcList->GetNthElement(i)->Print(file, indent_1); + } + + file << "# Test the strategy : byClass - custom" << std::endl; + rateCalcList->SetNbOfSamplesByClass(nbByClassCustom,typeCustom); + for (unsigned int i=0 ; i<rateCalcList->Size() ; i++) + { + file << "# Input "<< i << std::endl; + rateCalcList->GetNthElement(i)->Print(file, indent_1); + } + + file << "# Test the strategy : all - equal" << std::endl; + rateCalcList->SetAllSamples(typeEqual); + for (unsigned int i=0 ; i<rateCalcList->Size() ; i++) + { + file << "# Input "<< i << std::endl; + rateCalcList->GetNthElement(i)->Print(file, indent_1); + } + + file.close(); + return EXIT_SUCCESS; +} diff --git a/Modules/Learning/Sampling/test/otbSamplingTestDriver.cxx b/Modules/Learning/Sampling/test/otbSamplingTestDriver.cxx index 20a48dc1de860e25d0a0eb78326ad4a9b277d77b..c051e9a7dad8e71ab75bf69db4d370ab2142df37 100644 --- a/Modules/Learning/Sampling/test/otbSamplingTestDriver.cxx +++ b/Modules/Learning/Sampling/test/otbSamplingTestDriver.cxx @@ -11,4 +11,6 @@ void RegisterTests() REGISTER_TEST(otbImageSampleExtractorFilterNew); REGISTER_TEST(otbImageSampleExtractorFilter); REGISTER_TEST(otbImageSampleExtractorFilterUpdate); + REGISTER_TEST(otbSamplingRateCalculatorListNew); + REGISTER_TEST(otbSamplingRateCalculatorList); } diff --git a/Modules/Registration/DisparityMap/include/otbFineRegistrationImageFilter.h b/Modules/Registration/DisparityMap/include/otbFineRegistrationImageFilter.h index e964838baeebaa7c92c7f4aecc8c50701b837969..8aa20aab3dab7935a498087b3cfe072dd0b4f8d5 100644 --- a/Modules/Registration/DisparityMap/include/otbFineRegistrationImageFilter.h +++ b/Modules/Registration/DisparityMap/include/otbFineRegistrationImageFilter.h @@ -39,8 +39,8 @@ namespace otb * deriving from the itk::ImageToImageMetric. The MinimizeOn()/MinimizeOff() flag allows searching for * minimum or maximum depending on the metric (default is On). * - * Once a coarse (pixel wise) offset has been found, this match is further refined using dichotomic search - * until sub-pixel accuracy given by the SetSubPixelAccuracy() is reached. + * Once a coarse (pixel wise) offset has been found, this match is further refined using golden section search search + * until convergence accuracy (given by the SetConvergenceAccuracy()) is reached, or when the max number of iteration () is reached. * * The filter proposes two outputs: GetOutput() return the image of the metric optimum at each location, and * the GetOutputDisplacementField() method returns the corresponding offset. @@ -132,9 +132,17 @@ public: itkSetMacro(SearchRadius, SizeType); itkGetMacro(SearchRadius, SizeType); + /** Set/Get convergence accuracy */ + itkSetMacro(ConvergenceAccuracy, double); + itkGetMacro(ConvergenceAccuracy, double); + /** Set/Get subpixel accuracy */ itkSetMacro(SubPixelAccuracy, double); itkGetMacro(SubPixelAccuracy, double); + + /** Set/Get max number of iterations */ + itkSetMacro(MaxIter, int); + itkGetMacro(MaxIter, int); /** True if metric should be minimized. False otherwise */ itkSetMacro(Minimize, bool); @@ -192,6 +200,13 @@ protected: private: FineRegistrationImageFilter(const Self&); //purposely not implemented void operator=(const Self&); //purposely not implemented + + inline double callMetric(double val1,double val2,double &oldRes,bool &flag); + inline void updateOptParams(double potBestVal,double parx,double pary, //inputs + double &bestVal, typename TranslationType::ParametersType& optParams); //outputs + inline void updatePoints(double& gn, double& in1, double& in2, double &in3, //inputs + double& out1, double& out2, double& out3, double& out4); //outputs + inline void updateMinimize(double& a, double& b); /** The radius for correlation */ SizeType m_Radius; @@ -206,7 +221,11 @@ private: bool m_UseSpacing; /** Search step */ + double m_ConvergenceAccuracy; double m_SubPixelAccuracy; + + /** Max number of iterations */ + int m_MaxIter; /** The interpolator */ InterpolatorPointerType m_Interpolator; diff --git a/Modules/Registration/DisparityMap/include/otbFineRegistrationImageFilter.txx b/Modules/Registration/DisparityMap/include/otbFineRegistrationImageFilter.txx index 1eb1d442f6a49debc4d3a631e8ec9e3cb138945e..41c1719a80623ae2345a4379657f64a2b4fefe0f 100644 --- a/Modules/Registration/DisparityMap/include/otbFineRegistrationImageFilter.txx +++ b/Modules/Registration/DisparityMap/include/otbFineRegistrationImageFilter.txx @@ -45,6 +45,10 @@ FineRegistrationImageFilter<TInputImage, T0utputCorrelation, TOutputDisplacement // Default sub-pixel precision m_SubPixelAccuracy = 0.1; + m_ConvergenceAccuracy = 0.01; + + // Max number of iterations + m_MaxIter = 100; // Flags m_UseSpacing = true; @@ -282,6 +286,72 @@ FineRegistrationImageFilter<TInputImage, TOutputCorrelation, TOutputDisplacement } return; } + +template <class TInputImage, class TOutputCorrelation, class TOutputDisplacementField> +double +FineRegistrationImageFilter<TInputImage, TOutputCorrelation, TOutputDisplacementField> +::callMetric(double val1,double val2,double &oldRes,bool &flag) +{ + typename TranslationType::ParametersType paramsgn(2); + paramsgn[0]=val1; + paramsgn[1]=val2; + + double res=oldRes; + flag=false; + + try + { + res=m_Metric->GetValue(paramsgn); + } + catch(itk::ExceptionObject& err) + { + flag=true; + itkWarningMacro(<< err.GetDescription()); + } + + return res; +} + +template <class TInputImage, class TOutputCorrelation, class TOutputDisplacementField> +void +FineRegistrationImageFilter<TInputImage, TOutputCorrelation, TOutputDisplacementField> +::updateOptParams(double potBestVal,double parx,double pary,double &bestVal, typename TranslationType::ParametersType& optParams) +{ + if (bestVal>potBestVal) + { + bestVal=potBestVal; + optParams[0]=parx; + optParams[1]=pary; + } +} + + +template <class TInputImage, class TOutputCorrelation, class TOutputDisplacementField> +void +FineRegistrationImageFilter<TInputImage, TOutputCorrelation, TOutputDisplacementField> +::updatePoints(double& gn, double& in1, double& in2, double &in3, + double& out1, double& out2, double& out3, double& out4) +{ + out1=out2; + out2=in3; + out3=in1+gn*(in2-in1); + out4=in2-gn*(in2-in1); + +} + + +template <class TInputImage, class TOutputCorrelation, class TOutputDisplacementField> +void +FineRegistrationImageFilter<TInputImage, TOutputCorrelation, TOutputDisplacementField> +::updateMinimize(double& a, double& b) +{ + if(!m_Minimize) + { + a = -a; + b = -b; + } +} + template <class TInputImage, class TOutputCorrelation, class TOutputDisplacementField> void @@ -322,7 +392,7 @@ FineRegistrationImageFilter<TInputImage, TOutputCorrelation, TOutputDisplacement double currentMetric, optMetric; // Optimal translation parameters - typename TranslationType::ParametersType params(2), optParams(2), tmpOptParams(2); + typename TranslationType::ParametersType params(2), optParams(2); // Final displacement value DisplacementValueType displacementValue; @@ -335,6 +405,7 @@ FineRegistrationImageFilter<TInputImage, TOutputCorrelation, TOutputDisplacement // Get fixed image spacing SpacingType fixedSpacing = fixedPtr->GetSpacing(); + // Walk the images while (!outputIt.IsAtEnd() && !outputDfIt.IsAtEnd() ) { @@ -367,6 +438,7 @@ FineRegistrationImageFilter<TInputImage, TOutputCorrelation, TOutputDisplacement m_Metric->SetFixedImageRegion(currentMetricRegion); m_Metric->Initialize(); + // Compute the local offset if required (and the transform was specified) if (m_Transform.IsNotNull()) { @@ -408,45 +480,90 @@ FineRegistrationImageFilter<TInputImage, TOutputCorrelation, TOutputDisplacement } } } + + - // Dichotomic sub-pixel + //golden section search + typename TranslationType::ParametersType paramsgn(2); + double gn=(sqrt(5.0)-1.0)/2.0; SpacingType subPixelSpacing = fixedSpacing; - while(subPixelSpacing[0] > m_SubPixelAccuracy || subPixelSpacing[1] > m_SubPixelAccuracy) - { - // Perform 1 step of dichotomic search - subPixelSpacing /= 2.; - - // Store last opt params - tmpOptParams = optParams; - - for(int i = -1; i <= 1; i+=2) + double ax=optParams[0]-static_cast<double>(subPixelSpacing[0]); + double ay=optParams[1]-static_cast<double>(subPixelSpacing[1]); + double bx=optParams[0]+static_cast<double>(subPixelSpacing[0]); + double by=optParams[1]+static_cast<double>(subPixelSpacing[1]); + + + double cx=bx-gn*(bx-ax); + double cy=optParams[1];//by-gn*(by-ay); + double dx=ax+gn*(bx-ax); + double dy=optParams[1];//ay+gn*(by-ay); + double fc=0,fd=0; + bool exitWhile=false; + int nbIter=0; + double bestvalold=itk::NumericTraits<double>::max(),bestval=optMetric; + double diff=itk::NumericTraits<double>::max(); + double diffx=itk::NumericTraits<double>::max(); + double diffy=itk::NumericTraits<double>::max(); + + //init + fc=callMetric(cx,cy,fc,exitWhile); + fd=callMetric(dx,dy,fd,exitWhile); + updateMinimize(fc,fd); + + //loop + while ( (diffx > m_SubPixelAccuracy || diffy > m_SubPixelAccuracy) + && (diff>m_ConvergenceAccuracy) + && (!exitWhile) + && (nbIter<=m_MaxIter)) + { + nbIter++; + + // x direction + if (fc<fd) { - for(int j = -1; j <= 1; j+=2) - { - params = tmpOptParams; - params[0] += static_cast<double>(i*subPixelSpacing[0]); - params[1] += static_cast<double>(j*subPixelSpacing[1]); - - try - { - // compute currentMetric - currentMetric = m_Metric->GetValue(params); - - // Check for maximum - if((m_Minimize && (currentMetric < optMetric)) || (!m_Minimize && (currentMetric > optMetric))) - { - optMetric = currentMetric; - optParams = params; - } - } - catch(itk::ExceptionObject& err) - { - itkWarningMacro(<<err.GetDescription()); - - } - } + updateOptParams(fc,cx,cy,bestval,optParams); + updatePoints(gn,ay,by,cx,bx,dx,dy,cy); + fc=callMetric(cx,cy,fc,exitWhile); + fd=callMetric(dx,dy,fd,exitWhile); } - } + else + { + updateOptParams(fd,dx,dy,bestval,optParams); + updatePoints(gn,by,ay,dx,ax,cx,cy,dy); + fc=callMetric(cx,cy,fc,exitWhile); + fd=callMetric(dx,dy,fd,exitWhile); + } + updateMinimize(fc,fd); + + // y direction + if (fc<fd) + { + updateOptParams(fc,cx,cy,bestval,optParams); + updatePoints(gn,ax,bx,cy,by,dy,dx,cx); + fc=callMetric(cx,cy,fc,exitWhile); + fd=callMetric(dx,dy,fd,exitWhile); + } + else + { + updateOptParams(fd,dx,dy,bestval,optParams); + updatePoints(gn,bx,ax,dy,ay,cy,cx,dx); + fc=callMetric(cx,cy,fc,exitWhile); + fd=callMetric(dx,dy,fd,exitWhile); + } + updateMinimize(fc,fd); + + // Before eventual exit, take benefit from the last evaluations of fd and fc + updateOptParams(fd,dx,dy,bestval,optParams); + updateOptParams(fc,cx,cy,bestval,optParams); + + if(!m_Minimize) + bestval=-bestval; + + diff= fabs(bestval-bestvalold); + bestvalold=bestval; + diffx=fabs(bx-ax); + diffy=fabs(by-ay); + } // Store the offset and the correlation value outputIt.Set(optMetric); @@ -461,6 +578,8 @@ FineRegistrationImageFilter<TInputImage, TOutputCorrelation, TOutputDisplacement displacementValue[1] = optParams[1]/fixedSpacing[1]; } outputDfIt.Set(displacementValue); + + // Update iterators ++outputIt; ++outputDfIt; diff --git a/Modules/Registration/DisparityMap/test/CMakeLists.txt b/Modules/Registration/DisparityMap/test/CMakeLists.txt index eea166fe1b2ede6202d219c3eb9da4241864acce..26d170da988976fcbf729ee2c241e4b9fb4222f7 100644 --- a/Modules/Registration/DisparityMap/test/CMakeLists.txt +++ b/Modules/Registration/DisparityMap/test/CMakeLists.txt @@ -168,7 +168,7 @@ otb_add_test(NAME dmTvFineRegistrationImageFilterTestWithMeanSquare COMMAND otbD ${TEMP}/feTvFineRegistrationImageFilterTestWithMeanSquareField.tif # output fieldFileName 3 # radius 2 # sradius - 0.1 # precision + 0.01 # precision 2 # Mean square 1 # Grid step 0 # Initial offset x @@ -188,7 +188,7 @@ otb_add_test(NAME dmTvFineRegistrationImageFilterTestWithMeanReciprocalDifferenc ${TEMP}/feTvFineRegistrationImageFilterTestWithMeanReciprocalDifferenceField.tif # output fieldFileName 3 # radius 2 # sradius - 0.1 # precision + 0.01 # precision 3 # Mean reciprocal difference 1 # Grid step 0 # Initial offset x @@ -208,7 +208,7 @@ otb_add_test(NAME dmTvFineRegistrationImageFilterTestWithNormalizedCorrelation C ${TEMP}/feTvFineRegistrationImageFilterTestWithNormalizedCorrelationField.tif # output fieldFileName 3 # radius 2 # sradius - 0.1 # precision + 0.01 # precision 1 # Normalized Correlation 1 # Grid step 0 # Initial offset x @@ -228,7 +228,7 @@ otb_add_test(NAME dmTvFineRegistrationImageFilterTestWithCorrelation COMMAND otb ${TEMP}/feTvFineRegistrationImageFilterTestWithCorrelationField.tif # output fieldFileName 3 # radius 2 # sradius - 0.1 # precision + 0.01 # precision 0 # Correlation 1 # Grid step 0 # Initial offset x diff --git a/Modules/Registration/DisparityMap/test/otbFineRegistrationImageFilterTest.cxx b/Modules/Registration/DisparityMap/test/otbFineRegistrationImageFilterTest.cxx index 9f0fb19aad883b3580baaa9ef663cad3c4f6c59a..bf849a5209d3451e0d88b0011eda17e8340e88ba 100644 --- a/Modules/Registration/DisparityMap/test/otbFineRegistrationImageFilterTest.cxx +++ b/Modules/Registration/DisparityMap/test/otbFineRegistrationImageFilterTest.cxx @@ -94,7 +94,7 @@ int otbFineRegistrationImageFilterTest( int argc, char * argv[] ) registration->SetMovingInput(/*mreader*/mextract->GetOutput()); registration->SetRadius(radius); registration->SetSearchRadius(sradius); - registration->SetSubPixelAccuracy(precision); + registration->SetConvergenceAccuracy(precision); registration->SetGridStep(gridStep); RegistrationFilterType::SpacingType offset; diff --git a/Modules/ThirdParty/6S/src/CMakeLists.txt b/Modules/ThirdParty/6S/src/CMakeLists.txt index 7c2b3f496722924079ca714b3a31c527f726f74c..bcf7063d09a15b51a474dc48b332a6a3aaf16453 100644 --- a/Modules/ThirdParty/6S/src/CMakeLists.txt +++ b/Modules/ThirdParty/6S/src/CMakeLists.txt @@ -73,6 +73,15 @@ if(OTB_LIBRARY_PROPERTIES) set_target_properties(otb6S PROPERTIES ${OTB_LIBRARY_PROPERTIES}) endif() +# Below "extra" compile flags are needed for proper functioning of 6S +# library. Without these flags especially /Oy- OpticalCalibration tests +# are failing. the flags are automatially added by VS generator. +# But due to time constraints and processing power available on testing VMs, +# using Visual Studio generator is not possible. +if(MSVC_VERSION GREATER 1800) + set_target_properties(otb6S PROPERTIES COMPILE_FLAGS "/Ob1 /Oy- /GS") +endif() + install(TARGETS otb6S EXPORT ${OTB3P_INSTALL_EXPORT_NAME} RUNTIME DESTINATION ${OTB3P_INSTALL_RUNTIME_DIR} COMPONENT RuntimeLibraries diff --git a/Modules/ThirdParty/OssimPlugins/src/ossim/ossimWin32FindFileHandle.cpp b/Modules/ThirdParty/OssimPlugins/src/ossim/ossimWin32FindFileHandle.cpp index 9c76477470466ac48a8d4059f6843f2508561dc1..cc1209ab9bc058b63cf1b863e52f798f605db674 100644 --- a/Modules/ThirdParty/OssimPlugins/src/ossim/ossimWin32FindFileHandle.cpp +++ b/Modules/ThirdParty/OssimPlugins/src/ossim/ossimWin32FindFileHandle.cpp @@ -16,7 +16,7 @@ ossimWin32FindFileHandle::ossimWin32FindFileHandle(std::string const& path) s_printf( err_msg, "ossimSentinel1Model: cannot list files in directory '%s' : '%d'", - path, + path.c_str(), GetLastError() ); throw std::runtime_error( err_msg ); diff --git a/Modules/Visualization/Mapla/src/mvdMaplaApplication.cxx b/Modules/Visualization/Mapla/src/mvdMaplaApplication.cxx index b3c587aa64761afee21628e45ba67969b7c022e5..4a1a871a2765d59d932c377223b97e22f0433a24 100644 --- a/Modules/Visualization/Mapla/src/mvdMaplaApplication.cxx +++ b/Modules/Visualization/Mapla/src/mvdMaplaApplication.cxx @@ -86,8 +86,10 @@ MaplaApplication setObjectName( "MaplaApplication" ); InitializeCore( - "Monteverdi Application Launcher", Monteverdi_VERSION_STRING, - "OrfeoToolBox", "orfeo-toolbox.org" + "Monteverdi Application Launcher", + QString( "OTB %1" ).arg( OTB_VERSION_STRING ), + "OrfeoToolBox", + "orfeo-toolbox.org" ); } diff --git a/Modules/Visualization/Monteverdi/src/mvdApplication.cxx b/Modules/Visualization/Monteverdi/src/mvdApplication.cxx index 5768bf4b3bb7095e841246c3f53ff6b60892e428..e42c38f39ab21199b0e4e0d3346581fc1b9ea0b8 100644 --- a/Modules/Visualization/Monteverdi/src/mvdApplication.cxx +++ b/Modules/Visualization/Monteverdi/src/mvdApplication.cxx @@ -128,7 +128,7 @@ Application setObjectName( "Application" ); InitializeCore( - PROJECT_NAME, Monteverdi_VERSION_STRING, + PROJECT_NAME, QString( "OTB %1" ).arg( OTB_VERSION_STRING ), "OrfeoToolBox", "orfeo-toolbox.org" ); diff --git a/Modules/Visualization/MonteverdiCore/include/mvdHistogramModel.h b/Modules/Visualization/MonteverdiCore/include/mvdHistogramModel.h index d8db32ffece7847f0418843b9c274606377ac32a..adc0e3f1aee0bccbdeeef81870ebe42cb39cdd1d 100644 --- a/Modules/Visualization/MonteverdiCore/include/mvdHistogramModel.h +++ b/Modules/Visualization/MonteverdiCore/include/mvdHistogramModel.h @@ -37,6 +37,7 @@ // // System includes (sorted by alphabetic order) +#include <boost/type_traits/is_floating_point.hpp> // // ITK includes (sorted by alphabetic order) @@ -102,8 +103,13 @@ class OTBMonteverdiCore_EXPORT HistogramModel : /*-[ PUBLIC SECTION ]------------------------------------------------------*/ // -// Public types. +// Public types & constants. public: + + /** + */ + static const int PRECISION = 4; + /** */ typedef // itk::NumericTraits< T >::FloatType and @@ -160,6 +166,9 @@ public: // // Public methods. public: + /** + */ + static RealType GetEpsilon(); /** \brief Constructor. */ HistogramModel( QObject* p =NULL ); @@ -171,11 +180,15 @@ public: */ bool IsValid() const; + /** + */ + bool IsMonoValue() const; + /** */ - inline MeasurementType Quantile( CountType band, double p ) const; + MeasurementType Quantile( CountType band, double p ) const; /** */ - inline MeasurementType Quantile( CountType band, double p, Bound bound ) const; + MeasurementType Quantile( CountType band, double p, Bound bound ) const; /** */ double @@ -326,34 +339,6 @@ HistogramModel return m_MaxPixel; } -/*******************************************************************************/ -inline -HistogramModel::MeasurementType -HistogramModel -::Quantile( unsigned int band, - double p ) const -{ - assert( band<m_Histograms->Size() ); - - return m_Histograms->GetNthElement( band )->Quantile( 0, p ); -} - -/*******************************************************************************/ -inline -HistogramModel::MeasurementType -HistogramModel -::Quantile( unsigned int band, - double p, - Bound bound ) const -{ - assert( band<m_Histograms->Size() ); - - return m_Histograms->GetNthElement( band )->Quantile( - 0, - bound==BOUND_UPPER ? 1.0 - p : p - ); -} - /*******************************************************************************/ inline size_t @@ -580,6 +565,10 @@ HistogramModel m_MinPixel = filterStats->GetMinimum(); m_MaxPixel = filterStats->GetMaximum(); + // Define corrected min/MAX pixels. + DefaultImageType::PixelType minPixel( m_MinPixel ); + DefaultImageType::PixelType maxPixel( m_MaxPixel ); + // Extract sigmas for each band from covariance matrix. typename StatisticsFilter::MatrixType covariance( filterStats->GetFilter()->GetCovariance() @@ -630,9 +619,26 @@ HistogramModel << ceil( ( m_MaxPixel[ i ] - m_MinPixel[ i ] ) / h ); */ - bins[ i ] = ceil( ( m_MaxPixel[ i ] - m_MinPixel[ i ] ) / h ); + bins[ i ] = ceil( ( maxPixel[ i ] - minPixel[ i ] ) / h ); } } + + // MANTIS-1275 + // { + if( minPixel[ i ]==maxPixel[ i ] ) + { + double epsilon = HistogramModel::GetEpsilon(); + + if( minPixel[ i ] >= + std::numeric_limits< DefaultImageType::PixelType::ValueType >::min() + epsilon ) + minPixel[ i ] -= epsilon; + + if( maxPixel[ i ] <= + std::numeric_limits< DefaultImageType::PixelType::ValueType >::max() - epsilon ) + maxPixel[ i ] += epsilon; + } + // } + // } // std::cout << bins; @@ -656,8 +662,8 @@ HistogramModel histogramFilter->SetInput( imageModel->ToImage() ); // Setup histogram filter. - histogramFilter->GetFilter()->SetHistogramMin( m_MinPixel ); - histogramFilter->GetFilter()->SetHistogramMax( m_MaxPixel ); + histogramFilter->GetFilter()->SetHistogramMin( minPixel ); + histogramFilter->GetFilter()->SetHistogramMax( maxPixel ); histogramFilter->GetFilter()->SetNumberOfBins( bins ); histogramFilter->GetFilter()->SetSubSamplingRate( 1 ); histogramFilter->GetFilter()->SetNoDataFlag( diff --git a/Modules/Visualization/MonteverdiCore/otb-module.cmake b/Modules/Visualization/MonteverdiCore/otb-module.cmake index 1a988f2d047bb2158fd3418ca72c79d72be717e2..e18fe5d07133cefbe35e44c30c4ceb34c3bc3eec 100644 --- a/Modules/Visualization/MonteverdiCore/otb-module.cmake +++ b/Modules/Visualization/MonteverdiCore/otb-module.cmake @@ -5,6 +5,7 @@ set(DOCUMENTATION otb_module( OTBMonteverdiCore ENABLE_SHARED DEPENDS + OTBBoost OTBCarto OTBCommon OTBImageBase diff --git a/Modules/Visualization/MonteverdiCore/src/mvdHistogramModel.cxx b/Modules/Visualization/MonteverdiCore/src/mvdHistogramModel.cxx index 046e0517c93528508291838535d0a7e58f5394da..2b59b02b7dcaa7a2afa5e3dd0ceb2d86a2e6e8fc 100644 --- a/Modules/Visualization/MonteverdiCore/src/mvdHistogramModel.cxx +++ b/Modules/Visualization/MonteverdiCore/src/mvdHistogramModel.cxx @@ -60,12 +60,26 @@ namespace /*****************************************************************************/ /* STATIC IMPLEMENTATION SECTION */ - +/*****************************************************************************/ +HistogramModel::RealType +HistogramModel +::GetEpsilon() +{ + if( boost::is_floating_point< DefaultImageType::PixelType::ValueType >::value ) + return + std::pow( + 10.0, + static_cast< DefaultImageType::PixelType::ValueType >( + -HistogramModel::PRECISION - 1 + ) + ); + else + return 1; +} /*****************************************************************************/ /* CLASS IMPLEMENTATION SECTION */ - -/*******************************************************************************/ +/*****************************************************************************/ HistogramModel ::HistogramModel( QObject* p ) : AbstractModel( p ), @@ -93,6 +107,51 @@ HistogramModel return !m_Histograms.IsNull() && m_Histograms->Size()>0; } +/*******************************************************************************/ +bool +HistogramModel +::IsMonoValue() const +{ + // qDebug() << this << "::IsMonoValue() ->" << m_MinPixel==m_MaxPixel; + + return m_MinPixel==m_MaxPixel; +} + +/*******************************************************************************/ +HistogramModel::MeasurementType +HistogramModel +::Quantile( unsigned int band, + double p ) const +{ + assert( band<m_Histograms->Size() ); + + // qDebug() + // << "Quantile" << p << ":" << + // ( IsMonoValue() + // ? m_MinPixel[ band ] + // : m_Histograms->GetNthElement( band )->Quantile( 0, p ) ); + + return + IsMonoValue() + ? m_MinPixel[ band ] + : m_Histograms->GetNthElement( band )->Quantile( 0, p ); +} + +/*******************************************************************************/ +HistogramModel::MeasurementType +HistogramModel +::Quantile( unsigned int band, + double p, + Bound bound ) const +{ + return Quantile( + band, + bound==BOUND_UPPER + ? 1.0 - p + : p + ); +} + /*******************************************************************************/ double HistogramModel @@ -104,19 +163,28 @@ HistogramModel // Construct 1D measurement vector. assert( histogram->GetMeasurementVectorSize()==1 ); Histogram::MeasurementVectorType measurement( 1 ); - measurement[ 0 ] = intensity; - - // Due to float/double conversion, it can happen - // that the minimum or maximum value go slightly outside the histogram - // Clamping the value solves the issue and avoid RangeError - // itk::NumericsTraits<>::Clamp(...) was removed - // TODO : when otb::Clamp will be developed, use this function - measurement[0] = - measurement[0] < histogram->GetBinMin(0, 0) - ? histogram->GetBinMin(0, 0) - : ( measurement[0] > histogram->GetBinMax(0, histogram->GetSize(0) - 1) - ? histogram->GetBinMax(0, histogram->GetSize(0) - 1) - : measurement[0] ); + + { + assert( histogram->GetSize( 0 ) > 0 ); + + Histogram::MeasurementType binMin( histogram->GetBinMin( 0, 0 ) ); + + Histogram::MeasurementType binMax( + histogram->GetBinMax( 0, histogram->GetSize( 0 ) - 1 ) + ); + + // Due to float/double conversion, it can happen + // that the minimum or maximum value go slightly outside the histogram + // Clamping the value solves the issue and avoid RangeError + // itk::NumericsTraits<>::Clamp(...) was removed + // TODO : when otb::Clamp will be developed, use this function + measurement[ 0 ] = + intensity < binMin + ? binMin + : ( intensity > binMax + ? binMax + : intensity ); + } // Get the index of measurement in 1D-histogram. Histogram::IndexType index; @@ -129,19 +197,24 @@ HistogramModel MeasurementType minI = histogram->GetBinMin( 0, index[ 0 ] ); MeasurementType maxI = histogram->GetBinMax( 0, index[ 0 ] ); - // Frequency of current bin - Histogram::AbsoluteFrequencyType frequency( histogram->GetFrequency( index ) ); + assert( minI <= maxI ); - // Initialize result (contribution of current bin) - const MeasurementType epsilon = 1.0E-6; - double percent = 0.; + MeasurementType rangeI = vcl_abs( maxI - minI ); - if ( vcl_abs(maxI - minI) > epsilon ) - { - percent = frequency - * (bound == BOUND_LOWER ? (intensity - minI) : (maxI - intensity) ) - / ( maxI - minI ); - } + // Frequency of current bin + Histogram::AbsoluteFrequencyType frequency( + histogram->GetFrequency( index ) + ); + + double percent = + ( IsMonoValue() || + rangeI <= std::numeric_limits< MeasurementType >::epsilon() ) + ? 0.0 + : frequency + * ( bound == BOUND_LOWER + ? ( intensity - minI ) + : ( maxI - intensity ) ) + / rangeI; // Number of bins of histogram. Histogram::SizeType::SizeValueType binCount = histogram->Size(); diff --git a/Modules/Visualization/MonteverdiCore/src/mvdVectorImageModel.cxx b/Modules/Visualization/MonteverdiCore/src/mvdVectorImageModel.cxx index ee81cf3e4b9dc96b3ecc11520432ff41897b0911..02e935debaa6eed00b7c31c12046b0db65f64c21 100644 --- a/Modules/Visualization/MonteverdiCore/src/mvdVectorImageModel.cxx +++ b/Modules/Visualization/MonteverdiCore/src/mvdVectorImageModel.cxx @@ -421,16 +421,18 @@ VectorImageModel VectorImageSettings::ChannelVector::value_type band = GetSettings().GetRgbwChannel( channel ); + bool isInvalid = !histogramModel->IsValid() /* || histogramModel->IsMonoValue() */; + GetSettings().SetLowIntensity( channel, - !histogramModel->IsValid() + isInvalid ? min[ band ] : histogramModel->Quantile( band , 0.02, BOUND_LOWER ) ); GetSettings().SetHighIntensity( channel, - !histogramModel->IsValid() + isInvalid ? max[ band ] : histogramModel->Quantile( band , 0.02, BOUND_UPPER ) ); diff --git a/Modules/Visualization/MonteverdiGui/include/mvdHistogramWidget.h b/Modules/Visualization/MonteverdiGui/include/mvdHistogramWidget.h index ddd4dd355e7990cb28b80b68cd13671f6350b70b..d41059107d219cb89459686dc7e5349f4cd04ae9 100644 --- a/Modules/Visualization/MonteverdiGui/include/mvdHistogramWidget.h +++ b/Modules/Visualization/MonteverdiGui/include/mvdHistogramWidget.h @@ -159,6 +159,14 @@ public: */ void Clear(); + /** + */ + void SetPrecision( double ); + + /** + */ + double GetPrecision() const; + /*-[ PUBLIC SLOTS SECTION ]------------------------------------------------*/ // @@ -291,6 +299,10 @@ private: */ Bounds m_Bounds[ CURVE_COUNT ]; + /** + */ + double m_Precision; + /** */ bool m_IsGrayscaleActivated; diff --git a/Modules/Visualization/MonteverdiGui/src/mvdAboutDialog.ui b/Modules/Visualization/MonteverdiGui/src/mvdAboutDialog.ui index 9edead10086cfebcc5761ebbf19806675f23beeb..3affacf899429573beedef1fdc2b5dc7104bb338 100644 --- a/Modules/Visualization/MonteverdiGui/src/mvdAboutDialog.ui +++ b/Modules/Visualization/MonteverdiGui/src/mvdAboutDialog.ui @@ -80,7 +80,7 @@ </font> </property> <property name="text"> - <string>Version M.m.pl (codename)</string> + <string>OTB version M.m.pl (codename)</string> </property> </widget> </item> diff --git a/Modules/Visualization/MonteverdiGui/src/mvdColorBandDynamicsWidget.ui b/Modules/Visualization/MonteverdiGui/src/mvdColorBandDynamicsWidget.ui index 1e6c6b20b20a9bfb9d0ffff0e745c82d8ec6f827..dd697ccb77c7f316937a185e2effa32c335876be 100644 --- a/Modules/Visualization/MonteverdiGui/src/mvdColorBandDynamicsWidget.ui +++ b/Modules/Visualization/MonteverdiGui/src/mvdColorBandDynamicsWidget.ui @@ -304,9 +304,9 @@ <string/> </property> <property name="icon"> - <iconset resource="mvdIcons.qrc"> - <normaloff>:/icons/Data/Icons/unlocked.png</normaloff> - <normalon>:/icons/Data/Icons/locked.png</normalon>:/icons/Data/Icons/unlocked.png</iconset> + <iconset> + <normaloff>:/icons/Utilities/Data/Icons/unlocked.png</normaloff> + <normalon>:/icons/Utilities/Data/Icons/locked.png</normalon>:/icons/Utilities/Data/Icons/unlocked.png</iconset> </property> <property name="iconSize"> <size> @@ -342,7 +342,7 @@ <string/> </property> <property name="icon"> - <iconset resource="mvdIcons.qrc"> + <iconset> <normaloff>:/icons/Data/Icons/execute.png</normaloff>:/icons/Data/Icons/execute.png</iconset> </property> <property name="iconSize"> diff --git a/Modules/Visualization/MonteverdiGui/src/mvdColorDynamicsController.cxx b/Modules/Visualization/MonteverdiGui/src/mvdColorDynamicsController.cxx index 396af201034cef1348c584d1b34505295741a2a3..718441879df60d3dbdba0ee2bfe6159a75752452 100644 --- a/Modules/Visualization/MonteverdiGui/src/mvdColorDynamicsController.cxx +++ b/Modules/Visualization/MonteverdiGui/src/mvdColorDynamicsController.cxx @@ -699,8 +699,12 @@ ColorDynamicsController // // Access histogram-model. - assert( imageModel->GetHistogramModel()!=NULL ); - bool hasValidHistogram = imageModel->GetHistogramModel()->IsValid(); + const HistogramModel * histogramModel = imageModel->GetHistogramModel(); + assert( histogramModel!=NULL ); + + bool hasValidHistogram = + histogramModel->IsValid() && + !histogramModel->IsMonoValue(); // Block this controller's signals to prevent display refreshed // but let let widget(s) signal their changes so linked values diff --git a/Modules/Visualization/MonteverdiGui/src/mvdHistogramController.cxx b/Modules/Visualization/MonteverdiGui/src/mvdHistogramController.cxx index 9b8b15f0140740e8c44a3c4690ba3a9cddaaaf86..888a54aa56ac167e2cf83eeaa14c1db486e7ce04 100644 --- a/Modules/Visualization/MonteverdiGui/src/mvdHistogramController.cxx +++ b/Modules/Visualization/MonteverdiGui/src/mvdHistogramController.cxx @@ -167,6 +167,7 @@ HistogramController widget->SetData( chan, x, y, size, xMin, yMin, xMax, yMax ); + widget->SetPrecision( HistogramModel::GetEpsilon() ); widget->SetLowMarker( chan, settings.GetLowIntensity( chan ) ); widget->SetHighMarker( chan, settings.GetHighIntensity( chan ) ); diff --git a/Modules/Visualization/MonteverdiGui/src/mvdHistogramWidget.cxx b/Modules/Visualization/MonteverdiGui/src/mvdHistogramWidget.cxx index 88f3d66de2e59aa43175e3b9f97fc5980ce2ef2e..577f507494697de56b6b1a316f846c59bb014296 100644 --- a/Modules/Visualization/MonteverdiGui/src/mvdHistogramWidget.cxx +++ b/Modules/Visualization/MonteverdiGui/src/mvdHistogramWidget.cxx @@ -65,6 +65,7 @@ namespace mvd /*****************************************************************************/ /* CONSTANTS */ +#define PRECISION_MARGIN 4 /** * \brief Array of enhanced band names that OTB can return. @@ -146,6 +147,7 @@ HistogramWidget // m_LowPlotMarkers(), // m_HighPlotMarkers(), m_Bounds(), + m_Precision( 0.0 ), m_IsGrayscaleActivated( false ) { m_UI->setupUi( this ); @@ -323,6 +325,22 @@ HistogramWidget m_UI = NULL; } +/*******************************************************************************/ +void +HistogramWidget +::SetPrecision( double p ) +{ + m_Precision = p; +} + +/*******************************************************************************/ +double +HistogramWidget +::GetPrecision() const +{ + return m_Precision; +} + /*******************************************************************************/ void HistogramWidget @@ -576,11 +594,20 @@ HistogramWidget yMax = m_Bounds[ i ].m_YMax; } - /* - qDebug() - << "[" << xMin << "; " << xMax << "]" - << "x [" << yMin << "; " << yMax << "]"; - */ + if( xMin==xMax ) + { + double epsilon = PRECISION_MARGIN * m_Precision; + + if( xMin >= std::numeric_limits< double >::min() + epsilon ) + xMin -= epsilon; + + if( xMax <= std::numeric_limits< double >::max() - epsilon ) + xMax += epsilon; + } + + // qDebug() + // << "[" << xMin << "; " << xMax << "]" + // << "x [" << yMin << "; " << yMax << "]"; m_UI->histogramPlot->setAxisScale( QwtPlot::xBottom, xMin, xMax ); m_UI->histogramPlot->setAxisScale( QwtPlot::yLeft, yMin, yMax ); diff --git a/Modules/Visualization/MonteverdiGui/src/mvdIcons.qrc b/Modules/Visualization/MonteverdiGui/src/mvdIcons.qrc index 9f9236089a34a49e8f81eb96bc8958c2d93db0db..835ed7133726a2cc2d89f0f6b2f5cba01b3c3314 100644 --- a/Modules/Visualization/MonteverdiGui/src/mvdIcons.qrc +++ b/Modules/Visualization/MonteverdiGui/src/mvdIcons.qrc @@ -1,5 +1,5 @@ <RCC> - <qresource prefix="/icons"> + <qresource prefix="/"> <file>../../../../Utilities/Data/Icons/execute.png</file> <file>../../../../Utilities/Data/Icons/locked.png</file> <file>../../../../Utilities/Data/Icons/unlocked.png</file> diff --git a/Utilities/Doxygen/CMakeLists.txt b/Utilities/Doxygen/CMakeLists.txt index 4dbab71b71477a9dabd49c9fd90949153e4dc0b7..388df1334256117f4f9fffac32cc5ed4fa356122 100644 --- a/Utilities/Doxygen/CMakeLists.txt +++ b/Utilities/Doxygen/CMakeLists.txt @@ -109,10 +109,7 @@ find_package( Perl ) if( PERL_FOUND ) set( OTB_DOXYGEN_INPUT_FILTER - "${PERL_EXECUTABLE} ${OTB_BINARY_DIR}/Utilities/Doxygen/otbdoxygen.pl" ) - - configure_file(${OTB_SOURCE_DIR}/Utilities/Doxygen/otbdoxygen.pl.in - ${OTB_BINARY_DIR}/Utilities/Doxygen/otbdoxygen.pl) + "${PERL_EXECUTABLE} ${OTB_SOURCE_DIR}/Utilities/Doxygen/otbgroup.pl" ) else() set( OTB_DOXYGEN_INPUT_FILTER ) endif() @@ -154,7 +151,6 @@ if (BUILD_DOCUMENTATION) #COMMAND ${CMAKE_COMMAND} -E copy # ${OTB_SOURCE_DIR}/Utilities/Doxygen/favicon.ico # ${OTB_BINARY_DIR}/Documentation/Doxygen/html - DEPENDS ${OTB_BINARY_DIR}/Utilities/Doxygen/otbdoxygen.pl # DEPENDS ${OTB_BINARY_DIR}/Documentation/Doxygen/Examples.dox WORKING_DIRECTORY ${OTB_BINARY_DIR}/Utilities/Doxygen ) diff --git a/Utilities/Doxygen/otbdoxygen.pl.in b/Utilities/Doxygen/otbdoxygen.pl.in deleted file mode 100644 index 18fc1a0663e4cb39e847dae243d09883982f3767..0000000000000000000000000000000000000000 --- a/Utilities/Doxygen/otbdoxygen.pl.in +++ /dev/null @@ -1,9 +0,0 @@ -# for vxl files run the vxl_doxy.pl script, and use otbgroup.pl for all other files -if ( $ARGV[0] =~ /(vxl|vcl|vnl)/) -{ - system ("perl @OTB_SOURCE_DIR@/Utilities/Doxygen/vxl_doxy.pl $ARGV[0]"); -} -else -{ - system ("perl @OTB_SOURCE_DIR@/Utilities/Doxygen/otbgroup.pl $ARGV[0]"); -} diff --git a/Utilities/Doxygen/vxl_doxy.pl b/Utilities/Doxygen/vxl_doxy.pl deleted file mode 100644 index 87458fe6852553a0c8a93134ea638424c5c508be..0000000000000000000000000000000000000000 --- a/Utilities/Doxygen/vxl_doxy.pl +++ /dev/null @@ -1,127 +0,0 @@ -#!/bin/sh -# -*- perl -*- -exec perl -w -x $0 ${1+"$@"} -#!perl -#line 6 -# If Windows barfs at line 3 here, you will need to run perl -x vxl_doxy.pl -# You can set up as a permanent file association using the following commands -# >assoc .pl-PerlScript -# >ftype PerlScript=Perl=C:\Perl\bin\Perl.exe -x "%1" %* - -# Script to change the perceps documentation format to Doxygen (JavaDoc) format -# Authors: -# Dave Cooper -# Maarten Vergauwen -# Date: -# 17/02/2000 -# Modified: -# 11 April 2001 Ian Scott. Remove support for old perceps commands -# 5 May 2001 Geoff Cross. Correctly handle end of verbatim blocks. Allow two contiguous comments -# 10 May 2001 Ian Scott. Merged Geoffs and my changes - - -# patterns to be matched -$verbpatt = '\\\\verbatim'; -$endverbpatt = '\\\\endverbatim'; -$slashslashpatt = '^\\s*//'; -$slashslashcolonpatt = '^\\s*//:'; -$slashstarstarpatt = '/**'; -$spacespacepatt = ' '; -$starpatt = '*'; -$starslashpatt = '*/'; - -# variables that keep state: - -# comment found -> first line should start with /**, next lines with *, last line with */ -$comment = 0; - -# verbatim found -> lines should not start with * (visible in Doxygen) -$verbatim = 0; -# finish verbatim mode at the end of this line. -$should_end_verbatim = 0; - -$debug = 0; - -# mainloop -while (<>) -{ - # preprocessing - s/\bVCL_SUNPRO_CLASS_SCOPE_HACK\s*\([^()]*\)//g; - s/\bVCL_SUNPRO_ALLOCATOR_HACK\s*\(([^()]*)\)/$1/g; - s/\bVCL_CAN_STATIC_CONST_INIT_(INT|FLOAT)\b/1/g; - s/\bVCL_STATIC_CONST_INIT_(INT|FLOAT)\s*\(([^()]*)\)/= $2/g; - s/\bVCL_DFL_TYPE_PARAM_STLDECL\s*\(([^,()]*),([^,()]*)\)/class $1 = $2 /g; - s/\bDECLARE_DYNCREATE\s*\([^()]*\)//g; # for MFC - - if ( $should_end_verbatim ) - { - $verbatim = 0; - $should_end_verbatim = 0; - } - - # found verbatim ? - if ( m/$verbpatt/ ) { $verbatim = 1; }; - - # found endverbatim ? - if ( m/$endverbpatt/ ) { $should_end_verbatim = 1; }; - - # found start of comment: "//:" ? - if ( s!$slashslashcolonpatt!$slashstarstarpatt! ) - { - chomp; s/\s*$//; - # escape a space following a dot, add a dot at the end, -# # and finish brief doc, unless the line is empty or only has '\file': - unless (m!^\s*\/\*\*\s*(\\file)?\s*$!) { -# s/\. /.\\ /g; s/(\.)?\s*$/. \*\/\n\/\*/; - s/\. /.\\ /g; s/(\.)?\s*$/.\n/; - } - else { s/$/\n/; } - - if ($comment) - { - # Previous comment hasn't ended--two contiguous comment blocks. - # (Should not happen.) - print STDERR "Two contiguous comment blocks -- this should not happen\n"; - print "*/\n"; - } - $comment = 1; - print; next; - } - - # Replace '$' with '\f$' (TeX math mode) - s/(\\f)?\$(.+?)(\\f)?\$/\\f\$$2\\f\$/g if ($comment); - - # found continuation of comment WITH verbatim -> no "*" - if ( m!$slashslashpatt! && $verbatim && $comment) - { - s!$slashslashpatt!$spacespacepatt!; -# # Make 'Modifications' a section title: -# s!\b(Modifications?)\b\:?!\<H2\>$1\<\/H2\>!; - # remove lines of the form ========= or +-+-+-+-+ or ********* or longer: - print unless m/^\s*[*=+-]{9,}\s*$/; next; - } - - # found continuation of comment WITHOUT verbatim -> start line with "*" - if ( m!$slashslashpatt! && $comment ) - { - s!$slashslashpatt!$starpatt!; - # remove lines of the form ========= or +-+-+-+-+ or ********* or longer: - print unless m/^\s*[*=+-]{9,}\s*$/; next; - } - - # found end of comment -> start line with */ - # NOTE that *every* line within a comment (also empty lines) *must* start with // ! - # (In an earlier version of this script, empty lines were allowed inside comments.) - if ( $comment && ! m!$slashslashpatt! ) - { - print "$starslashpatt\n"; - $comment = 0; - print; next; - } - - # just print line if not in comment or in file - if ( !$comment ) { print; next; } - - # debug - print unprocessed lines (s.b. none) - if ($debug) { print "LNP:\t"; print; } -}