otbComputeConfusionMatrix.cxx 22.68 KiB
/*
 * 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;