An error occurred while loading the file. Please try again.
-
Victor Poughon authoredb8578d99
/*
* Copyright (C) 2005-2019 Centre National d'Etudes Spatiales (CNES)
*
* This file is part of Orfeo Toolbox
*
* https://www.orfeo-toolbox.org/
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "otbWrapperApplication.h"
#include "otbWrapperApplicationFactory.h"
#include "otbOGRDataSourceToLabelImageFilter.h"
#include "itkImageRegionConstIterator.h"
#include "otbRAMDrivenAdaptativeStreamingManager.h"
#include "otbConfusionMatrixMeasurements.h"
#include "otbContingencyTableCalculator.h"
#include "otbContingencyTable.h"
#include "otbMacro.h"
namespace otb
{
namespace Wrapper
{
/** Utility function to negate std::isalnum */
bool IsNotAlphaNum(char c)
{
return !std::isalnum(c);
}
class ComputeConfusionMatrix : public Application
{
public:
/** Standard class typedefs. */
typedef ComputeConfusionMatrix Self;
typedef Application Superclass;
typedef itk::SmartPointer<Self> Pointer;
typedef itk::SmartPointer<const Self> ConstPointer;
/** Standard macro */
itkNewMacro(Self);
itkTypeMacro(ComputeConfusionMatrix, otb::Application);
typedef itk::ImageRegionConstIterator<Int32ImageType> ImageIteratorType;
typedef otb::OGRDataSourceToLabelImageFilter<Int32ImageType> RasterizeFilterType;
typedef RAMDrivenAdaptativeStreamingManager
<Int32ImageType> RAMDrivenAdaptativeStreamingManagerType;
typedef Int32ImageType::RegionType RegionType;
typedef int ClassLabelType;
typedef unsigned long ConfusionMatrixEltType;
7172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140
typedef itk::VariableSizeMatrix<ConfusionMatrixEltType> ConfusionMatrixType;
typedef std::map<
ClassLabelType,
std::map<ClassLabelType, ConfusionMatrixEltType>
> OutputConfusionMatrixType;
// filter type
typedef otb::ConfusionMatrixMeasurements<ConfusionMatrixType, ClassLabelType> ConfusionMatrixMeasurementsType;
typedef ConfusionMatrixMeasurementsType::MapOfClassesType MapOfClassesType;
typedef ConfusionMatrixMeasurementsType::MeasurementType MeasurementType;
typedef ContingencyTable<ClassLabelType> ContingencyTableType;
typedef ContingencyTableType::Pointer ContingencyTablePointerType;
protected:
ComputeConfusionMatrix()
{
m_Input = nullptr;
}
private:
struct StreamingInitializationData
{
bool refhasnodata;
bool prodhasnodata;
int prodnodata;
int refnodata;
unsigned long numberOfStreamDivisions;
};
void DoInit() override
{
SetName("ComputeConfusionMatrix");
SetDescription("Computes the confusion matrix of a classification");
// Documentation
SetDocLongDescription("This application computes the confusion matrix of a classification map relative to a ground truth dataset. "
"This ground truth can be given as a raster or a vector data. Only reference and produced pixels with values different "
"from NoData are handled in the calculation of the confusion matrix. The confusion matrix is organized the following way: "
"rows = reference labels, columns = produced labels. In the header of the output file, the reference and produced class labels "
"are ordered according to the rows/columns of the confusion matrix.");
SetDocLimitations("None");
SetDocAuthors("OTB-Team");
SetDocSeeAlso(" ");
AddDocTag(Tags::Learning);
AddParameter(ParameterType_InputImage, "in", "Input Image");
SetParameterDescription( "in", "The input classification image." );
AddParameter(ParameterType_OutputFilename, "out", "Matrix output");
SetParameterDescription("out", "Filename to store the output matrix (csv format)");
AddParameter(ParameterType_Choice,"format","set the output format to contingency table or confusion matrix");
SetParameterDescription("format","Choice of the output format as a contingency table for unsupervised algorithms"
"or confusion matrix for supervised ones.");
AddChoice("format.confusionmatrix","Choice of a confusion matrix as output.");
AddChoice("format.contingencytable","Choice of a contingency table as output.");
AddParameter(ParameterType_Choice,"ref","Ground truth");
SetParameterDescription("ref","Choice of ground truth format");
AddChoice("ref.raster","Ground truth as a raster image");
AddChoice("ref.vector","Ground truth as a vector data file");
141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210
AddParameter(ParameterType_InputImage,"ref.raster.in","Input reference image");
SetParameterDescription("ref.raster.in","Input image containing the ground truth labels");
AddParameter(ParameterType_InputFilename,"ref.vector.in","Input reference vector data");
SetParameterDescription("ref.vector.in", "Input vector data of the ground truth");
AddParameter(ParameterType_ListView,"ref.vector.field","Field name");
SetParameterDescription("ref.vector.field","Field name containing the label values");
SetListViewSingleSelectionMode("ref.vector.field",true);
AddParameter(ParameterType_Int,"ref.raster.nodata","Value for nodata pixels in ref raster");
SetDefaultParameterInt("ref.raster.nodata",0);
SetParameterDescription("ref.raster.nodata","Label to be treated as no data in ref raster.");
MandatoryOff("ref.raster.nodata");
DisableParameter("ref.raster.nodata");
AddParameter(ParameterType_Int,"ref.vector.nodata","Value for nodata pixels in ref vector");
SetDefaultParameterInt("ref.vector.nodata",0);
SetParameterDescription("ref.vector.nodata","Label to be treated as no data in ref vector. Please note that this value is always used in vector mode, to generate default values. Please set it to a value that does not correspond to a class label.");
MandatoryOff("ref.vector.nodata");
DisableParameter("ref.vector.nodata");
AddParameter(ParameterType_Int,"nodatalabel","Value for nodata pixels in input image");
SetParameterDescription("nodatalabel","Label to be treated as no data in input image");
SetDefaultParameterInt("nodatalabel",0);
MandatoryOff("nodatalabel");
DisableParameter("nodatalabel");
AddRAMParameter();
// Doc example parameter settings
SetDocExampleParameterValue("in", "clLabeledImageQB1.tif");
SetDocExampleParameterValue("out", "ConfusionMatrix.csv");
SetDocExampleParameterValue("ref", "vector");
SetDocExampleParameterValue("ref.vector.in","VectorData_QB1_bis.shp");
SetDocExampleParameterValue("ref.vector.field","Class");
SetDocExampleParameterValue("ref.vector.nodata","255");
SetOfficialDocLink();
}
void DoUpdateParameters() override
{
if ( HasValue("ref.vector.in") )
{
std::string vectorFile = GetParameterString("ref.vector.in");
ogr::DataSource::Pointer ogrDS =
ogr::DataSource::New(vectorFile, ogr::DataSource::Modes::Read);
ogr::Layer layer = ogrDS->GetLayer(0);
ogr::Feature feature = layer.ogr().GetNextFeature();
ClearChoices("ref.vector.field");
for(int iField=0; iField<feature.ogr().GetFieldCount(); iField++)
{
std::string key, item = feature.ogr().GetFieldDefnRef(iField)->GetNameRef();
key = item;
std::string::iterator end = std::remove_if(key.begin(),key.end(),IsNotAlphaNum);
std::transform(key.begin(), end, key.begin(), tolower);
OGRFieldType fieldType = feature.ogr().GetFieldDefnRef(iField)->GetType();
if(fieldType == OFTString || fieldType == OFTInteger || fieldType == OFTInteger64)
{
std::string tmpKey="ref.vector.field."+key.substr(0, end - key.begin());
AddChoice(tmpKey,item);
}
211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280
}
}
}
void LogContingencyTable(const ContingencyTablePointerType& contingencyTable)
{
otbAppLogINFO("Contingency table: reference labels (rows) vs. produced labels (cols)\n" << (*contingencyTable.GetPointer()));
}
void m_WriteContingencyTable(const ContingencyTablePointerType& contingencyTable)
{
std::ofstream outFile;
outFile.open( this->GetParameterString( "out" ) );
outFile << contingencyTable->ToCSV();
outFile.close();
}
std::string LogConfusionMatrix(MapOfClassesType* mapOfClasses, ConfusionMatrixType* matrix)
{
// Compute minimal width
size_t minwidth = 0;
for (unsigned int i = 0; i < matrix->Rows(); i++)
{
for (unsigned int j = 0; j < matrix->Cols(); j++)
{
std::ostringstream os;
os << (*matrix)(i,j);
size_t size = os.str().size();
if (size > minwidth)
{
minwidth = size;
}
}
}
MapOfClassesType::const_iterator it = mapOfClasses->begin();
MapOfClassesType::const_iterator end = mapOfClasses->end();
for(; it != end; ++it)
{
std::ostringstream os;
os << "[" << it->first << "]";
size_t size = os.str().size();
if (size > minwidth)
{
minwidth = size;
}
}
// Generate matrix string, with 'minwidth' as size specifier
std::ostringstream os;
// Header line
for (size_t i = 0; i < minwidth; ++i)
os << " ";
os << " ";
it = mapOfClasses->begin();
end = mapOfClasses->end();
for(; it != end; ++it)
{
//os << "[" << it->first << "]" << " ";
os << "[" << std::setw(minwidth - 2) << it->first << "]" << " ";
}
os << std::endl;