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