HooverMetricsEstimation.cxxΒΆ
Example usage:
./HooverMetricsEstimation Input/maur_GT.tif Input/maur_labelled.tif Output/maur_colored_GT.tif
Example source code (HooverMetricsEstimation.cxx):
/*
* Copyright (C) 1999-2011 Insight Software Consortium
* Copyright (C) 2005-2024 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.
*/
// The following example shows how to compare two segmentations, using Hoover
// metrics. For instance, it can be used to compare a segmentation produced
// by your algorithm against a partial ground truth segmentation. In this
// example, the ground truth segmentation will be referred by the letters GT
// whereas the machine segmentation will be referred by MS.
//
// The estimation of Hoover metrics is done with two filters :
// \doxygen{otb}{HooverMatrixFilter} and \doxygen{otb}{HooverInstanceFilter}.
// The first one produces a matrix containing the number of overlapping pixels
// between MS regions and GT regions. The second one classifies each region among
// four types (called Hoover instances):
// \begin{itemize}
// \item Correct detection : a region is matched with an other one in the
// opposite segmentation, because they cover nearly the same area.
// \item Over-segmentation : a GT region is matched with a group of MS
// regions because they cover nearly the same area.
// \item Under-segmentation : a MS region is matched with a group of GT
// regions because they cover nearly the same area.
// \item Missed detection (for GT regions) or Noise (for MS region) :
// un-matched regions.
// \end{itemize}
// Note that a region can be tagged with two types. When the Hoover instance
// have been found, the instance filter computes overall scores for each
// category : they are the Hoover metrics \footnote{see http://www.trop.mips.uha.fr/pdf/ORASIS-2009.pdf}.
#include "otbHooverMatrixFilter.h"
#include "otbHooverInstanceFilter.h"
#include "otbLabelMapToAttributeImageFilter.h"
#include "otbImage.h"
#include "otbVectorImage.h"
#include "otbImageFileReader.h"
#include "otbImageFileWriter.h"
#include "itkLabelImageToLabelMapFilter.h"
int main(int argc, char* argv[])
{
if (argc != 4)
{
std::cerr << "Usage: " << argv[0];
std::cerr << " segmentationGT segmentationMS outputAttributeImage" << std::endl;
return EXIT_FAILURE;
}
// The filters \doxygen{otb}{HooverMatrixFilter} and \doxygen{otb}{HooverInstanceFilter}
// are designed to handle \doxygen{itk}{LabelMap} images, made with \doxygen{otb}{AttributesMapLabelObject}.
// This type of label object allows storing generic attributes. Each region can store
// a set of attributes: in this case, Hoover instances and metrics will be stored.
using LabelObjectType = otb::AttributesMapLabelObject<unsigned int, 2, float>;
using LabelMapType = itk::LabelMap<LabelObjectType>;
using HooverMatrixFilterType = otb::HooverMatrixFilter<LabelMapType>;
using InstanceFilterType = otb::HooverInstanceFilter<LabelMapType>;
using ImageType = otb::Image<unsigned int, 2>;
using ImageToLabelMapFilterType = itk::LabelImageToLabelMapFilter<ImageType, LabelMapType>;
using VectorImageType = otb::VectorImage<float, 2>;
using AttributeImageFilterType = otb::LabelMapToAttributeImageFilter<LabelMapType, VectorImageType>;
using ImageReaderType = otb::ImageFileReader<ImageType>;
using WriterType = otb::ImageFileWriter<VectorImageType>;
ImageReaderType::Pointer gt_reader = ImageReaderType::New();
gt_reader->SetFileName(argv[1]);
ImageReaderType::Pointer ms_reader = ImageReaderType::New();
ms_reader->SetFileName(argv[2]);
// The first step is to convert the images to label maps : we use
// \doxygen{itk}{LabelImageToLabelMapFilter}. The background value sets
// the label value of regions considered as background: there is no label object for the
// background region.
ImageToLabelMapFilterType::Pointer gt_filter = ImageToLabelMapFilterType::New();
gt_filter->SetInput(gt_reader->GetOutput());
gt_filter->SetBackgroundValue(0);
ImageToLabelMapFilterType::Pointer ms_filter = ImageToLabelMapFilterType::New();
ms_filter->SetInput(ms_reader->GetOutput());
ms_filter->SetBackgroundValue(0);
// The Hoover matrix filter has to be updated here. This matrix must be computed
// before being given to the instance filter.
HooverMatrixFilterType::Pointer hooverFilter = HooverMatrixFilterType::New();
hooverFilter->SetGroundTruthLabelMap(gt_filter->GetOutput());
hooverFilter->SetMachineSegmentationLabelMap(ms_filter->GetOutput());
hooverFilter->Update();
// The instance filter computes the Hoover metrics for each region. These metrics
// are stored as attributes in each label object. The threshold parameter
// corresponds to the overlapping ratio above which two regions can be matched.
// The extended attributes can be used if the user wants to keep a trace of the
// associations between MS and GT regions : i.e. if a GT region has been matched
// as a correct detection, it will carry an attribute containing the label value
// of the associated MS region (the same principle goes for other types of instance).
InstanceFilterType::Pointer instances = InstanceFilterType::New();
instances->SetGroundTruthLabelMap(gt_filter->GetOutput());
instances->SetMachineSegmentationLabelMap(ms_filter->GetOutput());
instances->SetThreshold(0.75);
instances->SetHooverMatrix(hooverFilter->GetHooverConfusionMatrix());
instances->SetUseExtendedAttributes(false);
// The \doxygen{otb}{LabelMapToAttributeImageFilter} is designed to extract attributes
// values from a label map and output them in the channels of a vector image. We set
// the attribute to plot in each channel.
AttributeImageFilterType::Pointer attributeImageGT = AttributeImageFilterType::New();
attributeImageGT->SetInput(instances->GetOutputGroundTruthLabelMap());
attributeImageGT->SetAttributeForNthChannel(0, InstanceFilterType::GetNameFromAttribute(InstanceFilterType::ATTRIBUTE_RC));
attributeImageGT->SetAttributeForNthChannel(1, InstanceFilterType::GetNameFromAttribute(InstanceFilterType::ATTRIBUTE_RF));
attributeImageGT->SetAttributeForNthChannel(2, InstanceFilterType::GetNameFromAttribute(InstanceFilterType::ATTRIBUTE_RA));
attributeImageGT->SetAttributeForNthChannel(3, InstanceFilterType::GetNameFromAttribute(InstanceFilterType::ATTRIBUTE_RM));
WriterType::Pointer writer = WriterType::New();
writer->SetInput(attributeImageGT->GetOutput());
writer->SetFileName(argv[3]);
writer->Update();
// The output image contains for each GT region its correct detection score ("RC", band 1),
// its over-segmentation score ("RF", band 2), its under-segmentation score ("RA", band 3)
// and its missed detection score ("RM", band 4).
std::cout << "Mean RC =" << instances->GetMeanRC() << std::endl;
std::cout << "Mean RF =" << instances->GetMeanRF() << std::endl;
std::cout << "Mean RA =" << instances->GetMeanRA() << std::endl;
std::cout << "Mean RM =" << instances->GetMeanRM() << std::endl;
std::cout << "Mean RN =" << instances->GetMeanRN() << std::endl;
// The Hoover scores are also computed for the whole segmentations. Here is some explanation about the score names :
// C = correct, F = fragmentation, A = aggregation, M = missed, N = noise.
return EXIT_SUCCESS;
}