otbComputeConfusionMatrix.cxx 22.7 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
/*
 * Copyright (C) 2005-2017 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.
 */
20 21 22 23 24 25 26 27


#include "otbWrapperApplication.h"
#include "otbWrapperApplicationFactory.h"

#include "otbOGRDataSourceToLabelImageFilter.h"
#include "itkImageRegionConstIterator.h"

28
#include "otbRAMDrivenAdaptativeStreamingManager.h"
29

30
#include "otbConfusionMatrixMeasurements.h"
31 32
#include "otbContingencyTableCalculator.h"
#include "otbContingencyTable.h"
33

34 35
#include "otbMacro.h"

36 37 38 39
namespace otb
{
namespace Wrapper
{
40 41 42 43 44
/** Utility function to negate std::isalnum */
bool IsNotAlphaNum(char c)
  {
  return !std::isalnum(c);
  }
45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60

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;
61 62

  typedef otb::OGRDataSourceToLabelImageFilter<Int32ImageType> RasterizeFilterType;
63 64 65
  
  typedef RAMDrivenAdaptativeStreamingManager
    <Int32ImageType>                            RAMDrivenAdaptativeStreamingManagerType;
66 67

  typedef Int32ImageType::RegionType RegionType;
68

69 70 71 72 73 74 75 76
  typedef int                                              ClassLabelType;
  typedef unsigned long                                    ConfusionMatrixEltType;
  typedef itk::VariableSizeMatrix<ConfusionMatrixEltType>  ConfusionMatrixType;

  typedef std::map<
                   ClassLabelType,
                   std::map<ClassLabelType, ConfusionMatrixEltType>
                  > OutputConfusionMatrixType;
77 78


79 80 81 82 83
  // filter type
  typedef otb::ConfusionMatrixMeasurements<ConfusionMatrixType, ClassLabelType> ConfusionMatrixMeasurementsType;
  typedef ConfusionMatrixMeasurementsType::MapOfClassesType                     MapOfClassesType;
  typedef ConfusionMatrixMeasurementsType::MeasurementType                      MeasurementType;

84
  typedef ContingencyTable<ClassLabelType>  ContingencyTableType;
85
  typedef ContingencyTableType::Pointer     ContingencyTablePointerType;
86

87 88 89 90
protected:

  ComputeConfusionMatrix()
    {
91
    m_Input = nullptr;
92
    }
93

94
private:
95 96 97

  struct StreamingInitializationData
  {
98 99 100 101
    bool refhasnodata;
    bool prodhasnodata;
    int  prodnodata;
    int  refnodata;
102 103 104 105
    unsigned long numberOfStreamDivisions;
  };


106
  void DoInit() override
107 108 109 110 111
  {
  SetName("ComputeConfusionMatrix");
  SetDescription("Computes the confusion matrix of a classification");

  // Documentation
112
  SetDocName("Confusion matrix Computation");
113
  SetDocLongDescription("This application computes the confusion matrix of a classification map relative to a ground truth dataset. "
114 115 116 117
      "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.");
118 119 120 121
  SetDocLimitations("None");
  SetDocAuthors("OTB-Team");
  SetDocSeeAlso(" ");

122
  AddDocTag(Tags::Learning);
123 124 125 126 127 128

  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)");
129

130 131 132 133 134 135
  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.");

136 137
  AddParameter(ParameterType_Choice,"ref","Ground truth");
  SetParameterDescription("ref","Choice of ground truth format");
138

139
  AddChoice("ref.raster","Ground truth as a raster image");
140

141
  AddChoice("ref.vector","Ground truth as a vector data file");
142

143 144
  AddParameter(ParameterType_InputImage,"ref.raster.in","Input reference image");
  SetParameterDescription("ref.raster.in","Input image containing the ground truth labels");
145

146 147
  AddParameter(ParameterType_InputFilename,"ref.vector.in","Input reference vector data");
  SetParameterDescription("ref.vector.in", "Input vector data of the ground truth");
148

149
  AddParameter(ParameterType_ListView,"ref.vector.field","Field name");
150
  SetParameterDescription("ref.vector.field","Field name containing the label values");
151 152
  SetListViewSingleSelectionMode("ref.vector.field",true);
  
153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168
  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);
169
  
170 171
  MandatoryOff("nodatalabel");
  DisableParameter("nodatalabel");
172

173 174 175 176
  AddRAMParameter();

  // Doc example parameter settings
  SetDocExampleParameterValue("in", "clLabeledImageQB1.tif");
177
  SetDocExampleParameterValue("out", "ConfusionMatrix.csv");
178 179 180
  SetDocExampleParameterValue("ref", "vector");
  SetDocExampleParameterValue("ref.vector.in","VectorData_QB1_bis.shp");
  SetDocExampleParameterValue("ref.vector.field","Class");
181
  SetDocExampleParameterValue("ref.vector.nodata","255");
182

183
  SetOfficialDocLink();
184 185
  }

186
  void DoUpdateParameters() override
187
  {
188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206
    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();
        
207
        if(fieldType == OFTString || fieldType == OFTInteger || fieldType == OFTInteger64)
208 209 210 211 212 213
          {
          std::string tmpKey="ref.vector.field."+key.substr(0, end - key.begin());
          AddChoice(tmpKey,item);
          }
        }
      }    
214 215
  }

216
  void LogContingencyTable(const ContingencyTablePointerType& contingencyTable)
217
  {
218
    otbAppLogINFO("Contingency table: reference labels (rows) vs. produced labels (cols)\n" << (*contingencyTable.GetPointer()));
219 220
  }

221
  void m_WriteContingencyTable(const ContingencyTablePointerType& contingencyTable)
222 223
  {
    std::ofstream outFile;
224
    outFile.open( this->GetParameterString( "out" ) );
225
    outFile << contingencyTable->ToCSV();
226 227 228
    outFile.close();
  }

229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300
  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;

      // Each line of confusion matrix
      it = mapOfClasses->begin();
      for (unsigned int i = 0; i < matrix->Rows(); i++)
        {
        ClassLabelType label = it->first;
        os << "[" << std::setw(minwidth - 2) << label << "]" << " ";
        for (unsigned int j = 0; j < matrix->Cols(); j++)
          {
          os << std::setw(minwidth) << (*matrix)(i,j) << " ";
          }
        os << std::endl;
        ++it;
        }

      otbAppLogINFO("Confusion matrix (rows = reference labels, columns = produced labels):\n" << os.str());
      return os.str();
    }


301
  StreamingInitializationData InitStreamingData()
302
  {
303

304
    StreamingInitializationData sid;
305

306
    m_Input = this->GetParameterInt32Image("in");
307

308 309
    std::string field;

310 311
    sid.prodnodata = this->GetParameterInt("nodatalabel");
    sid.prodhasnodata = this->IsParameterEnabled("nodatalabel");
312

313 314
    if (GetParameterString("ref") == "raster")
      {
315 316 317
      sid.refnodata = this->GetParameterInt("ref.raster.nodata");
      sid.refhasnodata = this->IsParameterEnabled("ref.raster.nodata");
      m_Reference = this->GetParameterInt32Image("ref.raster.in");
318 319
      }
    else
OTB Bot's avatar
OTB Bot committed
320
      {
321 322
      // Force nodata to true since it will be generated during rasterization
      sid.refhasnodata = true;
323
      sid.refnodata = this->GetParameterInt("ref.vector.nodata");
324
      
325
      otb::ogr::DataSource::Pointer ogrRef = otb::ogr::DataSource::New(GetParameterString("ref.vector.in"), otb::ogr::DataSource::Modes::Read);
326

327 328 329 330 331 332 333 334 335
      // Get field name
      std::vector<int> selectedCFieldIdx = GetSelectedItems("ref.vector.field");

      if(selectedCFieldIdx.empty())
        {
        otbAppLogFATAL(<<"No field has been selected for data labelling!");
        }

      std::vector<std::string> cFieldNames = GetChoiceNames("ref.vector.field");
336
      field = cFieldNames[selectedCFieldIdx.front()];
337

338
      m_RasterizeReference = RasterizeFilterType::New();
339
      m_RasterizeReference->AddOGRDataSource(ogrRef);
340 341 342
      m_RasterizeReference->SetOutputParametersFromImage(m_Input);
      m_RasterizeReference->SetBackgroundValue(sid.refnodata);
      m_RasterizeReference->SetBurnAttribute(field.c_str());
343

344 345
      m_Reference = m_RasterizeReference->GetOutput();
      m_Reference->UpdateOutputInformation();
346 347 348
      }

    // Prepare local streaming
349 350


351
    m_StreamingManager = RAMDrivenAdaptativeStreamingManagerType::New();
352
    int availableRAM = GetParameterInt("ram");
353
    m_StreamingManager->SetAvailableRAMInMB( static_cast<unsigned int>( availableRAM ) );
354
    float bias = 2.0; // empiric value;
355
    m_StreamingManager->SetBias(bias);
356

357
    m_StreamingManager->PrepareStreaming(m_Input, m_Input->GetLargestPossibleRegion());
358

359
    sid.numberOfStreamDivisions = m_StreamingManager->GetNumberOfSplits();
360 361

    otbAppLogINFO("Number of stream divisions : "<<sid.numberOfStreamDivisions);
362

363 364 365
    return sid;
  }

366
  void DoExecute() override
367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386
  {
    StreamingInitializationData sid = InitStreamingData();

    if(GetParameterString("format") == "contingencytable")
      {
      DoExecuteContingencyTable( sid );
      }
    else
      {
      DoExecuteConfusionMatrix( sid );
      }
  }

  void DoExecuteContingencyTable(const StreamingInitializationData& sid)
  {
    typedef ContingencyTableCalculator<ClassLabelType> ContingencyTableCalculatorType;
    ContingencyTableCalculatorType::Pointer calculator = ContingencyTableCalculatorType::New();

    for (unsigned int index = 0; index < sid.numberOfStreamDivisions; index++)
      {
387
      RegionType streamRegion = m_StreamingManager->GetSplit( index );
388

389 390 391
      m_Input->SetRequestedRegion( streamRegion );
      m_Input->PropagateRequestedRegion();
      m_Input->UpdateOutputData();
392

393 394 395
      m_Reference->SetRequestedRegion( streamRegion );
      m_Reference->PropagateRequestedRegion();
      m_Reference->UpdateOutputData();
396

397
      ImageIteratorType itInput( m_Input, streamRegion );
398 399
      itInput.GoToBegin();

400
      ImageIteratorType itRef( m_Reference, streamRegion );
401 402
      itRef.GoToBegin();

403
      calculator->Compute( itRef, itInput,sid.refhasnodata,sid.refnodata,sid.prodhasnodata,sid.prodnodata);
404 405
      }

406
    ContingencyTablePointerType contingencyTable = calculator->BuildContingencyTable();
407
    LogContingencyTable(contingencyTable);
408
    m_WriteContingencyTable(contingencyTable);
409 410
  }

411
  void DoExecuteConfusionMatrix(const StreamingInitializationData& sid)
412
  {
413

414
    // Extraction of the Class Labels from the Reference image/rasterized vector data + filling of m_Matrix
415 416
    MapOfClassesType  mapOfClassesRef, mapOfClassesProd;
    MapOfClassesType::iterator  itMapOfClassesRef, itMapOfClassesProd;
417
    ClassLabelType labelRef = 0, labelProd = 0;
418 419
    int itLabelRef = 0, itLabelProd = 0;

420
    for (unsigned int index = 0; index < sid.numberOfStreamDivisions; index++)
OTB Bot's avatar
OTB Bot committed
421
      {
422
      RegionType streamRegion = m_StreamingManager->GetSplit(index);
423

424 425 426
      m_Input->SetRequestedRegion(streamRegion);
      m_Input->PropagateRequestedRegion();
      m_Input->UpdateOutputData();
427

428 429 430
      m_Reference->SetRequestedRegion(streamRegion);
      m_Reference->PropagateRequestedRegion();
      m_Reference->UpdateOutputData();
431

432
      ImageIteratorType itInput(m_Input, streamRegion);
433
      itInput.GoToBegin();
434

435
      ImageIteratorType itRef(m_Reference, streamRegion);
436
      itRef.GoToBegin();
437

438
      while (!itRef.IsAtEnd())
439
        {
440 441 442 443
        labelRef = static_cast<ClassLabelType> (itRef.Get());
        labelProd = static_cast<ClassLabelType> (itInput.Get());

        // Extraction of the reference/produced class labels
444
        if ((!sid.refhasnodata || labelRef != sid.refnodata) && (!sid.prodhasnodata || labelProd != sid.prodnodata))
445
          {
446
          // If the current labels have not been added to their respective mapOfClasses yet
447
          if (mapOfClassesRef.insert(MapOfClassesType::value_type(labelRef, itLabelRef)).second)
448
            {
449
            ++itLabelRef;
450
            }
451
          if (mapOfClassesProd.insert(MapOfClassesType::value_type(labelProd, itLabelProd)).second)
452 453 454 455
            {
            ++itLabelProd;
            }

456
          // Filling of m_Matrix
457
          m_Matrix[labelRef][labelProd]++;
458
          } // END if ((labelRef != nodata) && (labelProd != nodata))
459
        ++itRef;
460 461 462 463 464
        ++itInput;
        }
      } // END of for (unsigned int index = 0; index < numberOfStreamDivisions; index++)


465 466
    /////////////////////////////////////////////
    // Filling the 2 headers for the output file
467 468 469 470 471 472 473
    const std::string commentRefStr = "#Reference labels (rows):";
    const std::string commentProdStr = "#Produced labels (columns):";
    const char separatorChar = ',';
    std::ostringstream ossHeaderRefLabels, ossHeaderProdLabels;

    // Filling ossHeaderRefLabels for the output file
    ossHeaderRefLabels << commentRefStr;
474 475

    MapOfClassesType::iterator itMapOfClassesRefEnd = mapOfClassesRef.end();
476
    itMapOfClassesRef = mapOfClassesRef.begin();
477
    int indexLabelRef = 0;
478
    while (itMapOfClassesRef != itMapOfClassesRefEnd)
479
      {
480
      // labels labelRef of mapOfClassesRef are already sorted
481
      labelRef = itMapOfClassesRef->first;
482 483

      // SORTING the itMapOfClassesRef->second items of mapOfClassesRef
484 485
      mapOfClassesRef[labelRef] = indexLabelRef;
      otbAppLogINFO("mapOfClassesRef[" << labelRef << "] = " << mapOfClassesRef[labelRef]);
486

487 488
      ossHeaderRefLabels << labelRef;
      ++itMapOfClassesRef;
489
      if (itMapOfClassesRef != itMapOfClassesRefEnd)
490 491 492 493 494 495
        {
        ossHeaderRefLabels << separatorChar;
        }
      else
        {
        ossHeaderRefLabels << std::endl;
496
        }
497 498

      ++indexLabelRef;
499
      }
500

501 502
    // Filling ossHeaderProdLabels for the output file
    ossHeaderProdLabels << commentProdStr;
503
    MapOfClassesType::iterator itMapOfClassesProdEnd = mapOfClassesProd.end();
504
    itMapOfClassesProd = mapOfClassesProd.begin();
505
    int indexLabelProd = 0;
506
    while (itMapOfClassesProd != itMapOfClassesProdEnd)
507
      {
508
      // labels labelProd of mapOfClassesProd are already sorted
509
      labelProd = itMapOfClassesProd->first;
510 511 512 513 514

      // SORTING the itMapOfClassesProd->second items of mapOfClassesProd
      mapOfClassesProd[labelProd] = indexLabelProd;
      otbAppLogINFO("mapOfClassesProd[" << labelProd << "] = " << mapOfClassesProd[labelProd]);

515 516
      ossHeaderProdLabels << labelProd;
      ++itMapOfClassesProd;
517
      if (itMapOfClassesProd != itMapOfClassesProdEnd)
518 519 520 521 522 523 524
        {
        ossHeaderProdLabels << separatorChar;
        }
      else
        {
        ossHeaderProdLabels << std::endl;
        }
525 526

      ++indexLabelProd;
527 528 529
      }


530
    std::ofstream outFile;
531
    outFile.open(this->GetParameterString("out"));
532
    outFile << std::fixed;
533
    outFile.precision(10);
534 535 536 537 538 539 540 541

    /////////////////////////////////////
    // Writing the 2 headers
    outFile << ossHeaderRefLabels.str();
    outFile << ossHeaderProdLabels.str();
    /////////////////////////////////////


542
    // Initialization of the Confusion Matrix for the application LOG and for measurements
543 544
    int nbClassesRef = static_cast<int>(mapOfClassesRef.size());
    int nbClassesProd = static_cast<int>(mapOfClassesProd.size());
545

546 547
    // Formatting m_MatrixLOG from m_Matrix in order to make m_MatrixLOG a square matrix
    // from the reference labels in mapOfClassesRef
548 549
    indexLabelRef = 0;
    int indexLabelProdInRef = 0;
550 551 552 553

    // Initialization of m_MatrixLOG
    m_MatrixLOG.SetSize(nbClassesRef, nbClassesRef);
    m_MatrixLOG.Fill(0);
554
    for (itMapOfClassesRef = mapOfClassesRef.begin(); itMapOfClassesRef != itMapOfClassesRefEnd; ++itMapOfClassesRef)
555
      {
556 557 558
      // labels labelRef of mapOfClassesRef are already sorted
      labelRef = itMapOfClassesRef->first;

559
      indexLabelProd = 0;
560
      for (itMapOfClassesProd = mapOfClassesProd.begin(); itMapOfClassesProd != itMapOfClassesProdEnd; ++itMapOfClassesProd)
561
        {
562 563 564
        // labels labelProd of mapOfClassesProd are already sorted
        labelProd = itMapOfClassesProd->first;

565 566 567
        // If labelProd is present in mapOfClassesRef
        if (mapOfClassesRef.count(labelProd) != 0)
          {
568 569 570
          // Index of labelProd in mapOfClassesRef; itMapOfClassesRef->second elements are now SORTED
          indexLabelProdInRef = mapOfClassesRef[labelProd];
          m_MatrixLOG(indexLabelRef, indexLabelProdInRef) = m_Matrix[labelRef][labelProd];
571
          }
572 573 574

        ///////////////////////////////////////////////////////////
        // Writing the ordered confusion matrix in the output file
575
        outFile << m_Matrix[labelRef][labelProd];
576
        if (indexLabelProd < (nbClassesProd - 1))
577
          {
578
          outFile << separatorChar;
579 580 581
          }
        else
          {
582
          outFile << std::endl;
583
          }
584
        ///////////////////////////////////////////////////////////
585
        ++indexLabelProd;
586
        }
587

588
      m_Matrix[labelRef].clear();
589
      ++indexLabelRef;
590
      }
591

592 593
    // m_Matrix is cleared in order to remove old results in case of successive runs of the GUI application
    m_Matrix.clear();
594
    outFile.close();
595

596 597
    otbAppLogINFO("Reference class labels ordered according to the rows of the output confusion matrix: " << ossHeaderRefLabels.str());
    otbAppLogINFO("Produced class labels ordered according to the columns of the output confusion matrix: " << ossHeaderProdLabels.str());
598 599 600 601 602 603 604 605 606 607 608 609
    //otbAppLogINFO("Output confusion matrix (rows = reference labels, columns = produced labels):\n" << m_MatrixLOG);

    LogConfusionMatrix(&mapOfClassesRef, &m_MatrixLOG);


    // Measurements of the Confusion Matrix parameters
    ConfusionMatrixMeasurementsType::Pointer confMatMeasurements = ConfusionMatrixMeasurementsType::New();

    confMatMeasurements->SetMapOfClasses(mapOfClassesRef);
    confMatMeasurements->SetConfusionMatrix(m_MatrixLOG);
    confMatMeasurements->Compute();

610
    for (itMapOfClassesRef = mapOfClassesRef.begin(); itMapOfClassesRef != itMapOfClassesRefEnd; ++itMapOfClassesRef)
611 612
      {
      labelRef = itMapOfClassesRef->first;
613
      indexLabelRef = itMapOfClassesRef->second;
614

615 616 617
      otbAppLogINFO("Precision of class [" << labelRef << "] vs all: " << confMatMeasurements->GetPrecisions()[indexLabelRef]);
      otbAppLogINFO("Recall of class [" << labelRef << "] vs all: " << confMatMeasurements->GetRecalls()[indexLabelRef]);
      otbAppLogINFO("F-score of class [" << labelRef << "] vs all: " << confMatMeasurements->GetFScores()[indexLabelRef] << std::endl);
618 619 620 621 622 623 624 625
      }

    otbAppLogINFO("Precision of the different classes: " << confMatMeasurements->GetPrecisions());
    otbAppLogINFO("Recall of the different classes: " << confMatMeasurements->GetRecalls());
    otbAppLogINFO("F-score of the different classes: " << confMatMeasurements->GetFScores() << std::endl);

    otbAppLogINFO("Kappa index: " << confMatMeasurements->GetKappaIndex());
    otbAppLogINFO("Overall accuracy index: " << confMatMeasurements->GetOverallAccuracy());
626

627
  }// END Execute()
628

629 630
  ConfusionMatrixType m_MatrixLOG;
  OutputConfusionMatrixType m_Matrix;
631 632 633 634
  Int32ImageType* m_Input;
  Int32ImageType::Pointer m_Reference;
  RAMDrivenAdaptativeStreamingManagerType::Pointer m_StreamingManager;
  RasterizeFilterType::Pointer m_RasterizeReference;
635 636 637 638 639
};

}
}
OTB_APPLICATION_EXPORT(otb::Wrapper::ComputeConfusionMatrix)