From 5b31a356ce648a2991dad035ca7456b58e9e1375 Mon Sep 17 00:00:00 2001 From: Rainer Kartmann <rainer.kartmann@student.kit.edu> Date: Thu, 21 Feb 2019 16:03:18 +0100 Subject: [PATCH] Added histogram --- VirtualRobot/math/Histogram.cpp | 177 ++++++++++++++++++++++++++++++++ VirtualRobot/math/Histogram.h | 98 ++++++++++++++++++ 2 files changed, 275 insertions(+) create mode 100644 VirtualRobot/math/Histogram.cpp create mode 100644 VirtualRobot/math/Histogram.h diff --git a/VirtualRobot/math/Histogram.cpp b/VirtualRobot/math/Histogram.cpp new file mode 100644 index 000000000..3ec3cd128 --- /dev/null +++ b/VirtualRobot/math/Histogram.cpp @@ -0,0 +1,177 @@ +#include "Histogram.h" + +#include <algorithm> + + +namespace VirtualRobot +{ + +Histogram::Histogram() +{} + +Histogram::Histogram(const std::vector<float>& data, std::size_t numBins) +{ + setMinMax(data); + resetBins(numBins); + insert(data); +} + +Histogram::Histogram(const std::vector<float>& data, float min, float max, std::size_t numBins) +{ + setMinMax(min, max); + resetBins(numBins); + insert(data); +} + +void Histogram::resetBins(std::size_t numBins) +{ + bins.assign(numBins, 0); +} + +std::size_t Histogram::valueToIndex(float value) const +{ + // normalized = 0..1 + float normalized = (value - min) / (max - min); + std::size_t index = static_cast<std::size_t>(normalized * getNumberOfBins()); + + // avoid out-of-bounce errors + // (if histogram is falsely used, this could lead to peaks at the edges) + return std::max(std::size_t(0), std::min(getNumberOfBins() - 1, index)); +} + +float Histogram::indexToValue(std::size_t index) const +{ + float normalized = static_cast<float>(index) / getNumberOfBins(); + float value = normalized * (max - min) + min; + return value; +} + +void Histogram::insert(float value) +{ + bins[valueToIndex(value)]++; +} + +void Histogram::insert(const std::vector<float>& values) +{ + for (float v : values) + { + insert(v); + } +} + +void Histogram::setMinMax(float min, float max) +{ + this->min = min; + this->max = max; +} + +void Histogram::setMinMax(const std::vector<float>& data) +{ + float min = data.front(); + float max = data.front(); + + for (float d : data) + { + min = std::min(min, d); + max = std::max(max, d); + } + setMinMax(min, max); +} + +const std::vector<std::size_t>& Histogram::getBins() const +{ + return bins; +} + +std::size_t Histogram::getNumberOfBins() const +{ + return bins.size(); +} + +float Histogram::getMin() const +{ + return min; +} + +float Histogram::getMax() const +{ + return max; +} + +std::size_t Histogram::getMinBinIndex() const +{ + return static_cast<std::size_t>( + std::distance(bins.begin(), std::min_element(bins.begin(), bins.end()))); +} + +float Histogram::getMinBinValue() const +{ + return indexToValue(getMaxBinIndex()); +} + +std::size_t Histogram::getMaxBinIndex() const +{ + return static_cast<std::size_t>( + std::distance(bins.begin(), std::max_element(bins.begin(), bins.end()))); +} + +float Histogram::getMaxBinValue() const +{ + return indexToValue(getMaxBinIndex()); +} + +void Histogram::applyMedianFilter(std::size_t size) +{ + std::vector<std::size_t> newBins(bins.size()); + std::vector<std::size_t> neighborhood(2 * size + 1); + + for (std::size_t index = 0; index < bins.size(); index++) + { + // handle cases at borders + std::size_t beginIndex = std::max(index - size, std::size_t(0)); + std::size_t endIndex = std::min(index + size + 1, bins.size()); + std::size_t num = endIndex - beginIndex; // common case: num == 2*size+1 + + // example: index = 3, size = 2 + // => beginIndex = 3-2 = 1, endIndex = 3+2+1 = 6, num = 6-1 = 5 + // => neighborhood = [1, 6) == [1,5] + + neighborhood.assign(bins.begin() + beginIndex, bins.begin() + endIndex); + + std::sort(neighborhood.begin(), neighborhood.begin() + num); + + newBins[index] = neighborhood[num / 2]; // == median + // (ignore cases where num is even, this only happens at the borders) + } + + bins = newBins; +} + +std::ostream& operator<<(std::ostream& os, const Histogram& histo) +{ + os << "Histogram:\n"; + os << "min:;" << histo.min << ";max:;" << histo.max << ";\n"; + + os << "Index:;"; + for (std::size_t index = 0; index < histo.bins.size(); index++) + { + os << index << ";"; + } + os << "\n"; + os << "Values:;"; + for (std::size_t index = 0; index < histo.bins.size(); index++) + { + os << histo.indexToValue(index) << ";"; + } + os << "\n"; + os << "Counts:;"; + for (std::size_t index = 0; index < histo.bins.size(); index++) + { + os << histo.bins[index] << ";"; + } + os << "\n"; + + return os; +} + +} diff --git a/VirtualRobot/math/Histogram.h b/VirtualRobot/math/Histogram.h new file mode 100644 index 000000000..a4b74150b --- /dev/null +++ b/VirtualRobot/math/Histogram.h @@ -0,0 +1,98 @@ +#pragma once + +#include <vector> +#include <ostream> + + +namespace VirtualRobot +{ + + /** + * @brief Histogram for one-dimensional float data. + */ + class Histogram + { + public: + + /// No initialization constructor. + Histogram(); + + /// Construct with the given data. + /// Minimum and maximum are derived automatically. + Histogram(const std::vector<float>& data, std::size_t numBins = 128); + + /// Construct with the given data and given limits. + Histogram(const std::vector<float>& data, float min, float max, std::size_t numBins = 128); + + + /// Set the number of bins and set them to 0. + void resetBins(std::size_t numBins); + + /// Insert the given value into the histogram. + void insert(float value); + /// Inserts the given values into the histogram. + void insert(const std::vector<float>& value); + + /// Set the limits. + void setMinMax(float min, float max); + /// Set the limits from the given data. + void setMinMax(const std::vector<float>& data); + + + /// Get the bins. + const std::vector<std::size_t>& getBins() const; + + /// Get the number of bins. + std::size_t getNumberOfBins() const; + + /// Transfer the given value to its corresponding bin index. + std::size_t valueToIndex(float value) const; + /// Get the value at the center of the bin with the given index. + float indexToValue(std::size_t index) const; + + /// Get the minimum, i.e. the lower limit. + float getMin() const; + /// Get the maximum, i.e. the upper limit. + float getMax() const; + + /// Return the index of the bin containing the fewest data points. + std::size_t getMinBinIndex() const; + /// Returns the corresponding value of the bin containing the fewest data points. + float getMinBinValue() const; + + /// Return the index of the bin containing the most data points. + std::size_t getMaxBinIndex() const; + /// Returns the corresponding value of the bin containing the most data points. + float getMaxBinValue() const; + + + /** + * @brief Applies a median filter to the histogram bins. + * + * The size specifies the number of neighours (in each direction) + * considered. + * That is, if k = size, 2k+1 values are considered for each point + * (k neighbours in each direction). + * + * @param size the size of the neighbourhood (in each direction) + */ + void applyMedianFilter(std::size_t size = 2); + + + /// Streams a CVS-like description of the histogram containing the bin data. + friend std::ostream& operator<<(std::ostream& os, const Histogram& histo); + + + private: + + /// The minimum mapped value. + float min; + /// The maximum mapped value. + float max; + + /// The bins. + std::vector<std::size_t> bins; + + }; + +} -- GitLab