Commit cc8b6266 authored by Guillaume Pasero's avatar Guillaume Pasero
Browse files

Merge branch 'sampling_polygon_analysis' into develop

parents 2751123a fbb52340
......@@ -104,3 +104,8 @@ otb_create_application(
NAME VectorDataDSValidation
SOURCES otbVectorDataDSValidation.cxx
LINK_LIBRARIES ${${otb-module}_LIBRARIES})
otb_create_application(
NAME PolygonClassStatistics
SOURCES otbPolygonClassStatistics.cxx
LINK_LIBRARIES ${${otb-module}_LIBRARIES})
/*=========================================================================
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 "otbOGRDataToClassStatisticsFilter.h"
#include "otbStatisticsXMLFileWriter.h"
namespace otb
{
namespace Wrapper
{
class PolygonClassStatistics : public Application
{
public:
/** Standard class typedefs. */
typedef PolygonClassStatistics Self;
typedef Application Superclass;
typedef itk::SmartPointer<Self> Pointer;
typedef itk::SmartPointer<const Self> ConstPointer;
/** Standard macro */
itkNewMacro(Self);
itkTypeMacro(PolygonClassStatistics, otb::Application);
/** Filters typedef */
typedef otb::OGRDataToClassStatisticsFilter<FloatVectorImageType,UInt8ImageType> FilterType;
typedef otb::StatisticsXMLFileWriter<FloatVectorImageType::PixelType> StatWriterType;
private:
PolygonClassStatistics()
{
}
void DoInit()
{
SetName("PolygonClassStatistics");
SetDescription("Computes statistics on a training polygon set.");
// Documentation
SetDocName("Polygon Class Statistics");
SetDocLongDescription("The application processes a set of geometries "
"intended for training (they should have a field giving the associated "
"class). The geometries are analysed against a support image to compute "
"statistics : \n"
" - number of samples per class\n"
" - number of samples per geometry\n"
"An optional raster mask can be used to discard samples. Different types"
" of geometry are supported : polygons, lines, points. The behaviour is "
"different for each type of geometry :\n"
" - polygon: select pixels whose center is inside the polygon\n"
" - lines : select pixels intersecting the line\n"
" - points : select closest pixel to the point\n");
SetDocLimitations("None");
SetDocAuthors("OTB-Team");
SetDocSeeAlso(" ");
AddDocTag(Tags::Learning);
AddParameter(ParameterType_InputImage, "in", "InputImage");
SetParameterDescription("in", "Support image that will be classified");
AddParameter(ParameterType_InputImage, "mask", "InputMask");
SetParameterDescription("mask", "Validity mask (only pixels corresponding to a mask value greater than 0 will be used for statistics)");
MandatoryOff("mask");
AddParameter(ParameterType_InputFilename, "vec", "Input vectors");
SetParameterDescription("vec","Input geometries to analyse");
AddParameter(ParameterType_OutputFilename, "out", "Output Statistics");
SetParameterDescription("out","Output file to store statistics (XML format)");
AddParameter(ParameterType_String, "field", "Field Name");
SetParameterDescription("field","Name of the field carrying the class name in the input vectors.");
MandatoryOff("field");
SetParameterString("field", "class");
AddParameter(ParameterType_Int, "layer", "Layer Index");
SetParameterDescription("layer", "Layer index to read in the input vector file.");
MandatoryOff("layer");
SetDefaultParameterInt("layer",0);
AddRAMParameter();
// Doc example parameter settings
SetDocExampleParameterValue("in", "support_image.tif");
SetDocExampleParameterValue("vec", "variousVectors.sqlite");
SetDocExampleParameterValue("field", "label");
SetDocExampleParameterValue("out","polygonStat.xml");
}
void DoUpdateParameters()
{
// Nothing to do
}
void DoExecute()
{
otb::ogr::DataSource::Pointer vectors =
otb::ogr::DataSource::New(this->GetParameterString("vec"));
std::string fieldName = this->GetParameterString("field");
FilterType::Pointer filter = FilterType::New();
filter->SetInput(this->GetParameterImage("in"));
if (IsParameterEnabled("mask") && HasValue("mask"))
{
filter->SetMask(this->GetParameterImage<UInt8ImageType>("mask"));
}
filter->SetOGRData(vectors);
filter->SetFieldName(fieldName);
filter->SetLayerIndex(this->GetParameterInt("layer"));
filter->Update();
FilterType::ClassCountMapType &classCount = filter->GetClassCountOutput()->Get();
FilterType::PolygonSizeMapType &polySize = filter->GetPolygonSizeOutput()->Get();
StatWriterType::Pointer statWriter = StatWriterType::New();
statWriter->SetFileName(this->GetParameterString("out"));
statWriter->AddInputMap<FilterType::ClassCountMapType>("samplesPerClass",classCount);
statWriter->AddInputMap<FilterType::PolygonSizeMapType>("samplesPerVector",polySize);
statWriter->Update();
}
};
} // end of namespace Wrapper
} // end of namespace otb
OTB_APPLICATION_EXPORT(otb::Wrapper::PolygonClassStatistics)
......@@ -26,6 +26,7 @@ otb_module(OTBAppClassification
OTBImageManipulation
OTBObjectList
OTBCommon
OTBSampling
TEST_DEPENDS
OTBTestKernel
......
......@@ -843,3 +843,13 @@ otb_test_application(NAME apTvClPredictRegressionTest_monovar
endif()
#----------- PolygonClassStatistics TESTS ----------------
otb_test_application(NAME apTvClPolygonClassStatisticsTest
APP PolygonClassStatistics
OPTIONS -in ${INPUTDATA}/cthead1_flip.tif
-vec ${INPUTDATA}/variousVectors.sqlite
-field label
-out ${TEMP}/apTvClPolygonClassStatisticsOut.xml
VALID --compare-ascii ${NOTOL}
${OTBAPP_BASELINE_FILES}/apTvClPolygonClassStatisticsOut.xml
${TEMP}/apTvClPolygonClassStatisticsOut.xml)
......@@ -58,6 +58,9 @@ public:
typedef std::pair<std::string , MeasurementVectorType> InputDataType;
typedef std::vector< InputDataType > MeasurementVectorContainer;
typedef std::map<std::string , std::string> GenericMapType;
typedef std::map<std::string , GenericMapType> GenericMapContainer;
virtual void Modified() const
{
m_IsUpdated = false;
......@@ -68,18 +71,29 @@ public:
itkGetStringMacro(FileName);
/** Get the number of Outputs*/
itkGetMacro(NumberOfOutputs, unsigned int);
unsigned int GetNumberOfOutputs();
/** Get the list of vector statistics names */
std::vector<std::string> GetStatisticVectorNames();
/** Get the list of map statistics names */
std::vector<std::string> GetStatisticMapNames();
/** Method to get the MeasurementVector by name */
MeasurementVectorType GetStatisticVectorByName(const char * statisticName);
/** Method to get a statistics map by name */
template <typename MapType>
MapType GetStatisticMapByName(const char * statisticName);
protected:
/** Read into the file and extract information in vector and map containers */
virtual void Read();
StatisticsXMLFileReader();
virtual ~StatisticsXMLFileReader() {}
void PrintSelf(std::ostream& os, itk::Indent indent) const;
virtual void PrintSelf(std::ostream& os, itk::Indent indent) const;
private:
StatisticsXMLFileReader(const Self&); //purposely not implemented
......@@ -87,8 +101,8 @@ private:
std::string m_FileName;
MeasurementVectorContainer m_MeasurementVectorContainer;
unsigned int m_NumberOfOutputs;
mutable bool m_IsUpdated;
GenericMapContainer m_GenericMapContainer;
}; // end of class StatisticsXMLFileReader
......
......@@ -22,6 +22,7 @@
#include "itkMacro.h"
#include "itksys/SystemTools.hxx"
#include "otb_tinyxml.h"
#include "otbStringUtils.h"
namespace otb {
......@@ -29,10 +30,57 @@ namespace otb {
template < class TMeasurementVector >
StatisticsXMLFileReader<TMeasurementVector>
::StatisticsXMLFileReader(): m_FileName(""),
m_NumberOfOutputs(0),
m_IsUpdated(false)
{}
template < class TMeasurementVector >
unsigned int
StatisticsXMLFileReader<TMeasurementVector>
::GetNumberOfOutputs()
{
return m_MeasurementVectorContainer.size() + m_GenericMapContainer.size();
}
template < class TMeasurementVector >
std::vector<std::string>
StatisticsXMLFileReader<TMeasurementVector>
::GetStatisticVectorNames()
{
// Read the xml file once
if(!m_IsUpdated)
{
this->Read();
}
std::vector<std::string> output;
for (unsigned int i=0 ; i < m_MeasurementVectorContainer.size() ; ++i )
{
output.push_back(m_MeasurementVectorContainer[i].first);
}
return output;
}
template < class TMeasurementVector >
std::vector<std::string>
StatisticsXMLFileReader<TMeasurementVector>
::GetStatisticMapNames()
{
// Read the xml file once
if(!m_IsUpdated)
{
this->Read();
}
std::vector<std::string> output;
for ( GenericMapContainer::iterator it = m_GenericMapContainer.begin() ;
it != m_GenericMapContainer.end() ;
++it)
{
output.push_back(it->first);
}
return output;
}
template < class TMeasurementVector >
typename StatisticsXMLFileReader<TMeasurementVector>
::MeasurementVectorType
......@@ -48,7 +96,7 @@ StatisticsXMLFileReader<TMeasurementVector>
// Check if the name of the Statistic is present
bool found = false;
unsigned int index = 0;
for(unsigned int idx = 0; idx < m_NumberOfOutputs; ++idx)
for(unsigned int idx = 0; idx < m_MeasurementVectorContainer.size() ; ++idx)
{
if(strcmp(m_MeasurementVectorContainer[idx].first.c_str(), statisticName) == 0 )
{
......@@ -64,6 +112,39 @@ StatisticsXMLFileReader<TMeasurementVector>
return m_MeasurementVectorContainer[index].second;
}
template < class TMeasurementVector >
template <typename MapType>
MapType
StatisticsXMLFileReader<TMeasurementVector>
::GetStatisticMapByName(const char * statisticName)
{
// Read the xml file once
if(!m_IsUpdated)
{
this->Read();
}
// Check if the name of the Statistic is present
std::string statName(statisticName);
if (m_GenericMapContainer.count(statName) == 0)
{
itkExceptionMacro(<<"No entry corresponding to the token selected ("<<statName<<") in the XML file");
}
MapType outputMap;
typename MapType::key_type tmpKey;
typename MapType::mapped_type tmpVal;
for ( GenericMapType::iterator it = m_GenericMapContainer[statName].begin() ;
it != m_GenericMapContainer[statName].end() ;
++it)
{
tmpKey = boost::lexical_cast<typename MapType::key_type>(it->first);
tmpVal = boost::lexical_cast<typename MapType::mapped_type>(it->second);
outputMap[tmpKey] = tmpVal;
}
return outputMap;
}
template < class TMeasurementVector >
void
StatisticsXMLFileReader<TMeasurementVector>
......@@ -80,6 +161,10 @@ StatisticsXMLFileReader<TMeasurementVector>
<<" is a wrong Extension FileName : Expected .xml");
}
// Clean outputs
m_MeasurementVectorContainer.clear();
m_GenericMapContainer.clear();
// Open the xml file
TiXmlDocument doc(m_FileName.c_str());
if (!doc.LoadFile())
......@@ -88,45 +173,83 @@ StatisticsXMLFileReader<TMeasurementVector>
}
TiXmlHandle hDoc(&doc);
TiXmlHandle root = hDoc.FirstChildElement("FeatureStatistics");
TiXmlElement *root = hDoc.FirstChildElement("FeatureStatistics").ToElement();
// Iterate through the tree to get all the stats
for( TiXmlElement* currentStat = root.FirstChildElement().ToElement();
currentStat != NULL;
currentStat = currentStat->NextSiblingElement() )
if (root)
{
InputDataType currentStatisticVector;
// Iterate through the tree to get all the stats
for( TiXmlElement* currentStat = root->FirstChildElement();
currentStat != NULL;
currentStat = currentStat->NextSiblingElement() )
{
InputDataType currentStatisticVector;
// Store the stat type name
currentStatisticVector.first = currentStat->Attribute("name");
// Store the stat type name
currentStatisticVector.first = currentStat->Attribute("name");
// The size is not stored in the XML file
// Store the value in a std::vector, get the size and then
// build a measurement vector
std::vector<double> tempMeasurementVector;
// The size is not stored in the XML file
// Store the value in a std::vector, get the size and then
// build a measurement vector
std::vector<double> tempMeasurementVector;
for( TiXmlElement* sample = currentStat->FirstChildElement("StatisticVector");
sample != NULL;
sample = sample->NextSiblingElement() )
{
// Get the current value of the statistic vector
double value;
sample->QueryDoubleAttribute("value", &value);
// Store the value
tempMeasurementVector.push_back(value);
for( TiXmlElement* sample = currentStat->FirstChildElement("StatisticVector");
sample != NULL;
sample = sample->NextSiblingElement() )
{
// Get the current value of the statistic vector
double value;
sample->QueryDoubleAttribute("value", &value);
// Store the value
tempMeasurementVector.push_back(value);
}
// resize the Measurement Vector
currentStatisticVector.second.SetSize(tempMeasurementVector.size());
for(unsigned int i = 0; i < tempMeasurementVector.size(); ++i)
currentStatisticVector.second.SetElement(i,
(static_cast<InputValueType>(tempMeasurementVector[i])));
m_MeasurementVectorContainer.push_back(currentStatisticVector);
}
}
// resize the Measurement Vector
currentStatisticVector.second.SetSize(tempMeasurementVector.size());
for(unsigned int i = 0; i < tempMeasurementVector.size(); ++i)
currentStatisticVector.second.SetElement(i,
(static_cast<InputValueType>(tempMeasurementVector[i])));
// Parse Map statistics
std::string key;
std::string value;
root = hDoc.FirstChildElement("GeneralStatistics").ToElement();
if (root)
{
// Iterate through the tree to get all the stats
for( TiXmlElement* currentStat = root->FirstChildElement();
currentStat != NULL;
currentStat = currentStat->NextSiblingElement() )
{
GenericMapType currentMap;
std::string currentName(currentStat->Attribute("name"));
// Increment the number of output
m_MeasurementVectorContainer.push_back(currentStatisticVector);
m_NumberOfOutputs++;
for( TiXmlElement* sample = currentStat->FirstChildElement("StatisticMap");
sample != NULL;
sample = sample->NextSiblingElement() )
{
// Get the current pair of the statistic map
const char *c_key = sample->Attribute("key");
const char *c_value = sample->Attribute("value");
if (c_key == NULL)
{
itkExceptionMacro("'key' attribute not found in StatisticMap !");
}
if (c_value == NULL)
{
itkExceptionMacro("'value' attribute not found in StatisticMap !");
}
key = std::string(c_key);
value = std::string(c_value);
// Store the pair
currentMap[key] = value;
}
m_GenericMapContainer[currentName] = currentMap;
}
}
// Reader is up-to-date
m_IsUpdated = true;
}
......@@ -134,10 +257,27 @@ StatisticsXMLFileReader<TMeasurementVector>
template < class TMeasurementVector >
void
StatisticsXMLFileReader<TMeasurementVector>
::PrintSelf(std::ostream& itkNotUsed(os), itk::Indent itkNotUsed(indent)) const
::PrintSelf(std::ostream& os, itk::Indent indent) const
{
// Call superclass implementation
//Superclass::PrintSelf(os, indent);
Superclass::PrintSelf(os, indent);
// Print info about statistics
os << indent << "Input FileName: "<< m_FileName << std::endl;
os << indent << "Vector statistics: ";
for (unsigned int i=0 ; i < m_MeasurementVectorContainer.size() ; ++i)
{
if (i>0) os <<", ";
os << m_MeasurementVectorContainer[i].first;
}
os << std::endl;
os << indent << "Map statistics: ";
for (GenericMapContainer::const_iterator it = m_GenericMapContainer.begin() ; it != m_GenericMapContainer.end() ; ++it)
{
if (it != m_GenericMapContainer.begin()) os <<", ";
os << it->first;
}
os << std::endl;
}
} // End namespace otb
......
......@@ -60,9 +60,19 @@ public:
/** Convenient typedef */
typedef std::pair<std::string , MeasurementVectorType> InputDataType;
typedef std::vector< InputDataType > MeasurementVectorContainer;
typedef std::map<std::string , std::string> GenericMapType;
typedef std::map<std::string , GenericMapType> GenericMapContainer;
/** Method to set/get the input list sample */
void AddInput(const char * name, const MeasurementVectorType& inputVector );
/** Method to add a map statistic with a given type */
template <typename MapType>
void AddInputMap(const char * name, const MapType& map );
/** Remove previously added inputs (vectors and maps) */
void CleanInputs();
/** Trigger the processing */
void Update()
......@@ -88,7 +98,7 @@ private:
std::string m_FileName;
MeasurementVectorContainer m_MeasurementVectorContainer;
GenericMapContainer m_GenericMapContainer;
}; // end of class StatisticsXMLFileWriter
......
......@@ -22,6 +22,7 @@
#include "itkMacro.h"
#include "itksys/SystemTools.hxx"
#include "otb_tinyxml.h"
#include "otbStringUtils.h"
namespace otb {
......@@ -60,8 +61,8 @@ StatisticsXMLFileWriter<TMeasurementVector>
::GenerateData()
{
// Check if the input are not null
if(m_MeasurementVectorContainer.size() == 0)
itkExceptionMacro(<<"At Least one input is required, please set input using the method AddInput");
if(m_MeasurementVectorContainer.size() == 0 && m_GenericMapContainer.size() == 0)
itkExceptionMacro(<<"At least one input is required, please set input using the methods AddInput or AddInputMap");
// Check if the filename is not empty
if(m_FileName.empty())
......@@ -80,8 +81,12 @@ StatisticsXMLFileWriter<TMeasurementVector>
TiXmlDeclaration* decl = new TiXmlDeclaration( "1.0", "", "" );
doc.LinkEndChild( decl );
TiXmlElement * root = new TiXmlElement( "FeatureStatistics");
doc.LinkEndChild( root );
TiXmlElement * root = NULL;
if (m_MeasurementVectorContainer.size())
{
root = new TiXmlElement( "FeatureStatistics");
doc.LinkEndChild( root );
}
// Iterate through the input
for (unsigned int i = 0; i < m_MeasurementVectorContainer.size(); ++i)
......@@ -103,6 +108,39 @@ StatisticsXMLFileWriter<TMeasurementVector>
feature->LinkEndChild(curStatisticVector);
}
}
// Iterate on map containers
TiXmlElement * mapRoot = NULL;
if (m_GenericMapContainer.size())
{
mapRoot = new TiXmlElement( "GeneralStatistics");
doc.LinkEndChild( mapRoot );
}
std::string keyAttr("key");
std::string valAttr("value");
GenericMapContainer::const_iterator containerIt;
for ( containerIt = m_GenericMapContainer.begin() ; containerIt != m_GenericMapContainer.end() ; ++containerIt)
{
std::string mapName = containerIt->first;
GenericMapType::const_iterator mapIter;
// The current statistic
TiXmlElement * feature = new TiXmlElement("Statistic");
feature->SetAttribute("name", mapName.c_str());
mapRoot->LinkEndChild( feature );
// Store the value for this statistic
for( mapIter = containerIt->second.begin() ; mapIter != containerIt->second.end() ; ++mapIter )
{
// For each value in Measurementvector
TiXmlElement * curStatisticMap = new TiXmlElement("StatisticMap");
curStatisticMap->SetAttribute(keyAttr , mapIter->first);
curStatisticMap->SetAttribute(valAttr, mapIter->second);
feature->LinkEndChild(curStatisticMap);
}
}
// Finally, write the file
if (! doc.SaveFile( m_FileName.c_str() ) )
......@@ -114,14 +152,67 @@ StatisticsXMLFileWriter<TMeasurementVector>
}
template < class TMeasurementVector >