cgal/Classification/include/CGAL/Classification/Label_set.h

335 lines
12 KiB
C++

// Copyright (c) 2017 GeometryFactory Sarl (France).
// All rights reserved.
//
// This file is part of CGAL (www.cgal.org).
//
// $URL$
// $Id$
// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-Commercial
//
// Author(s) : Simon Giraudot
#ifndef CGAL_CLASSIFICATION_LABEL_SET_H
#define CGAL_CLASSIFICATION_LABEL_SET_H
#include <CGAL/license/Classification.h>
#include <CGAL/Classification/Label.h>
#include <CGAL/Random.h>
#include <vector>
#include <unordered_map>
namespace CGAL {
namespace Classification {
/*!
\ingroup PkgClassificationLabel
\brief sets of `Label` used as input by classification
algorithms.
*/
class Label_set
{
using Base = std::vector<Label_handle>;
CGAL::Random m_random;
Base m_labels;
public:
#ifdef DOXYGEN_RUNNING
using const_iterator = unspecified_type; ///< A random access constant iterator with value type `Label_handle`.
using iterator = unspecified_type; ///< A random access iterator with value type `Label_handle`.
#else
using const_iterator = std::vector<Label_handle>::const_iterator;
using iterator = std::vector<Label_handle>::iterator;
#endif
/// \name Constructors
/// @{
Label_set() { }
/*!
\brief constructs a label set from a set of label names.
*/
Label_set(std::initializer_list<const char*> labels)
{
for (const char* l : labels)
add (l);
}
/// @}
/// \name Modifications
/// @{
/*!
\brief adds a label.
\note Names, standard indices and colors are not used for
identification: two labels in the same set can have the same name,
standard index or color, but not the same handle. Each call to
`add()` generates a new distinct label.
\param name name of the label.
\param color used to represent the label.
\param standard_index standard index of the classification label
(i.e. index in the ASPRS standard).
\return a handle to the newly added label.
*/
Label_handle add (const char* name,
CGAL::IO::Color color,
std::size_t standard_index = -1)
{
Label_handle out = std::make_shared<Classification::Label>
(name, m_labels.size(), standard_index, color);
m_labels.push_back (out);
return out;
}
/*!
\brief adds a label with default standard index and color.
This functions tries to map label names to standard ASPRS labels
and automatically picks the `standard_index` and `color` of the
label:
- `"unassigned"` is given standard index 2 and color `(0, 0, 0)`
- `"ground"` is given standard index 2 and color `(186, 189, 182)`
- `"low_vegetation"` is given standard index 3 and color `(78, 154, 6)`
- `"medium_vegetation"` is given standard index 4 and color `(138, 226, 52)`
- `"high_vegetation"` is given standard index 5 and color `(204, 255, 201)`
- `"building"` is given standard index 6 and color `(245, 121, 0)`
- `"noise"` is given standard index 7 and color `(128, 0, 0)`
- `"reserved"` is given standard index 8 and color `(233, 185, 110)`
- `"water"` is given standard index 9 and color `(114, 159, 207)`
- `"rail"` is given standard index 10 and color `(136, 46, 25)`
- `"road_surface"` is given standard index 11 and color `(56, 56, 56)`
- `"reserved_2"` is given standard index 12 and color `(193, 138, 51)`
- `"wire_guard"` is given standard index 13 and color `(37, 61, 136)`
- `"wire_conductor"` is given standard index 14 and color `(173, 127, 168)`
- `"transmission_tower"` is given standard index 15 and color `(136, 138, 133)`
- `"wire_connect"` is given standard index 16 and color `(145, 64, 236)`
- `"bridge_deck"` is given standard index 17 and color `(213, 93, 93)`
- `"high_noise"` is given standard index 18 and color `(255, 0, 0)`
If the name is not found, the label is given standard index
`std::size_t(-1)` and a random color.
\note Names are not used for identification: two labels in the
same set can have the same name but not the same handle. Each call
to `add()` generates a new distinct label.
\param name name of the label.
\return a handle to the newly added label.
*/
Label_handle add (const char* name)
{
static std::unordered_map<std::string, std::pair<std::size_t, CGAL::IO::Color> > init_map;
if (init_map.empty())
{
init_map.insert (std::make_pair ("unassigned",
std::make_pair (2, CGAL::IO::Color (0, 0, 0))));
init_map.insert (std::make_pair ("ground",
std::make_pair (2, CGAL::IO::Color (186, 189, 182))));
init_map.insert (std::make_pair ("low_vegetation",
std::make_pair (3, CGAL::IO::Color (78, 154, 6))));
init_map.insert (std::make_pair ("medium_vegetation",
std::make_pair (4, CGAL::IO::Color (138, 226, 52))));
init_map.insert (std::make_pair ("high_vegetation",
std::make_pair (5, CGAL::IO::Color (204, 255, 201))));
init_map.insert (std::make_pair ("building",
std::make_pair (6, CGAL::IO::Color (245, 121, 0))));
init_map.insert (std::make_pair ("noise",
std::make_pair (7, CGAL::IO::Color (128, 0, 0))));
init_map.insert (std::make_pair ("reserved",
std::make_pair (8, CGAL::IO::Color (233, 185, 110))));
init_map.insert (std::make_pair ("water",
std::make_pair (9, CGAL::IO::Color (114, 159, 207))));
init_map.insert (std::make_pair ("rail",
std::make_pair (10, CGAL::IO::Color (136, 46, 25))));
init_map.insert (std::make_pair ("road_surface",
std::make_pair (11, CGAL::IO::Color (56, 56, 56))));
init_map.insert (std::make_pair ("reserved_2",
std::make_pair (12, CGAL::IO::Color (193, 138, 51))));
init_map.insert (std::make_pair ("wire_guard",
std::make_pair (13, CGAL::IO::Color (37, 61, 136))));
init_map.insert (std::make_pair ("wire_conductor",
std::make_pair (14, CGAL::IO::Color (173, 127, 168))));
init_map.insert (std::make_pair ("wire_conduct",
std::make_pair (14, CGAL::IO::Color (173, 127, 168))));
init_map.insert (std::make_pair ("transmission_tower",
std::make_pair (15, CGAL::IO::Color (136, 138, 133))));
init_map.insert (std::make_pair ("trans_tower",
std::make_pair (15, CGAL::IO::Color (136, 138, 133))));
init_map.insert (std::make_pair ("wire_connect",
std::make_pair (16, CGAL::IO::Color (145, 64, 236))));
init_map.insert (std::make_pair ("bridge_deck",
std::make_pair (17, CGAL::IO::Color (213, 93, 93))));
init_map.insert (std::make_pair ("high_noise",
std::make_pair (18, CGAL::IO::Color (255, 0, 0))));
// Undocumented additions
init_map.insert (std::make_pair ("low_veget",
std::make_pair (3, CGAL::IO::Color (78, 154, 6))));
init_map.insert (std::make_pair ("medium_veget",
std::make_pair (4, CGAL::IO::Color (138, 226, 52))));
init_map.insert (std::make_pair ("vegetation",
std::make_pair (4, CGAL::IO::Color (138, 226, 52))));
init_map.insert (std::make_pair ("high_veget",
std::make_pair (5, CGAL::IO::Color (204, 255, 201))));
init_map.insert (std::make_pair ("roof",
std::make_pair (6, CGAL::IO::Color (245, 121, 0))));
init_map.insert (std::make_pair ("facade",
std::make_pair (-1, CGAL::IO::Color (77, 131, 186))));
}
std::string sname (name);
auto found = init_map.find (sname);
if (found == init_map.end())
return add (name,
CGAL::IO::Color ((unsigned char)(m_random.get_int(64, 192)),
(unsigned char)(m_random.get_int(64, 192)),
(unsigned char)(m_random.get_int(64, 192))));
// else
return add (name, found->second.second, found->second.first);
}
/*!
\brief removes a label.
\param label the handle to the label that must be removed.
\return `true` if the label was correctly removed,
`false` if its handle was not found.
*/
bool remove (Label_handle label)
{
if (label->index() >= m_labels.size()
|| m_labels[label->index()] != label)
return false;
for (std::size_t i = label->index() + 1; i < m_labels.size(); ++ i)
m_labels[i]->m_index --;
m_labels.erase (m_labels.begin() + label->index());
return true;
}
/*!
\brief removes all labels.
*/
void clear ()
{
m_labels.clear();
}
/// @}
/// \name Access
/// @{
const_iterator begin() const { return m_labels.begin(); }
iterator begin() { return m_labels.begin(); }
const_iterator end() const { return m_labels.end(); }
iterator end() { return m_labels.end(); }
/*!
\brief returns how many labels are defined.
*/
std::size_t size () const
{
return m_labels.size();
}
/*!
\brief returns the \f$i^{th}\f$ label.
*/
Label_handle operator[] (std::size_t i) const
{
return m_labels[i];
}
/// @}
/// \name Validity
/// @{
/*!
\brief checks the validity of the ground truth with respect to the
label set.
\param ground_truth range of label indices. This function checks
that all these indices are either -1 (for unclassified) or a valid
index of one of the labels. If at least one of the indices is out
of range, this function returns `false`, otherwise it returns
`true`.
\param verbose if set to `true`, the number of inliers of each
label, the number of unclassified items and the potential number
of out-of-range items are displayed. Otherwise, this function does
not display anything.
*/
template <typename LabelIndexRange>
bool is_valid_ground_truth (const LabelIndexRange& ground_truth,
bool verbose = false) const
{
std::vector<std::size_t> nb_inliers (m_labels.size() + 2, 0);
std::size_t total = 0;
for (const auto& gt : ground_truth)
{
int g = int(gt);
if (g == -1)
++ nb_inliers[m_labels.size()];
else if (g >= int(m_labels.size()))
{
++ nb_inliers[m_labels.size() + 1];
if (!verbose)
break;
}
else
++ nb_inliers[std::size_t(gt)];
++ total;
}
bool valid = (nb_inliers[m_labels.size() + 1] == 0);
if (verbose)
{
std::cout << "Ground truth is " << (valid ? "valid" : "invalid") << ":" << std::endl;
std::cout << " * " << nb_inliers[m_labels.size()] << " unclassified item(s) ("
<< 100. * (nb_inliers[m_labels.size()] / double(total)) << "%)" << std::endl;
for (std::size_t i = 0; i < m_labels.size(); ++ i)
std::cout << " * " << nb_inliers[i] << " " << m_labels[i]->name() << " inlier(s) ("
<< 100. * (nb_inliers[i] / double(total)) << "%)" << std::endl;
if (!valid)
std::cout << " * " << nb_inliers[m_labels.size() + 1] << " item(s) with out-of-range index ("
<< 100. * (nb_inliers[m_labels.size() + 1] / double(total)) << "%)" << std::endl;
}
return valid;
}
/// @}
};
} // namespace Classification
} // namespace CGAL
#endif // CGAL_CLASSIFICATION_LABEL_SET_H