For debugging purposes it is helpful to access the internal data of the histogram. Add an accessor for that. Signed-off-by: Stefan Klug <stefan.klug@ideasonboard.com> Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com> Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
175 lines
4.9 KiB
C++
175 lines
4.9 KiB
C++
/* SPDX-License-Identifier: BSD-2-Clause */
|
|
/*
|
|
* Copyright (C) 2019, Raspberry Pi Ltd
|
|
*
|
|
* histogram calculations
|
|
*/
|
|
#include "histogram.h"
|
|
|
|
#include <cmath>
|
|
|
|
#include <libcamera/base/log.h>
|
|
|
|
/**
|
|
* \file histogram.h
|
|
* \brief Class to represent Histograms and manipulate them
|
|
*/
|
|
|
|
namespace libcamera {
|
|
|
|
namespace ipa {
|
|
|
|
/**
|
|
* \class Histogram
|
|
* \brief The base class for creating histograms
|
|
*
|
|
* This class stores a cumulative frequency histogram, which is a mapping that
|
|
* counts the cumulative number of observations in all of the bins up to the
|
|
* specified bin. It can be used to find quantiles and averages between quantiles.
|
|
*/
|
|
|
|
/**
|
|
* \fn Histogram::Histogram()
|
|
* \brief Construct an empty Histogram
|
|
*
|
|
* This empty constructor exists largely to allow Histograms to be embedded in
|
|
* other classes which may be created before the contents of the Histogram are
|
|
* known.
|
|
*/
|
|
|
|
/**
|
|
* \brief Create a cumulative histogram
|
|
* \param[in] data A (non-cumulative) histogram
|
|
*/
|
|
Histogram::Histogram(Span<const uint32_t> data)
|
|
{
|
|
cumulative_.resize(data.size() + 1);
|
|
cumulative_[0] = 0;
|
|
for (const auto &[i, value] : utils::enumerate(data))
|
|
cumulative_[i + 1] = cumulative_[i] + value;
|
|
}
|
|
|
|
/**
|
|
* \fn Histogram::Histogram(Span<const uint32_t> data, Transform transform)
|
|
* \brief Create a cumulative histogram
|
|
* \param[in] data A (non-cumulative) histogram
|
|
* \param[in] transform The transformation function to apply to every bin
|
|
*/
|
|
|
|
/**
|
|
* \fn Histogram::bins()
|
|
* \brief Retrieve the number of bins currently used by the Histogram
|
|
* \return Number of bins
|
|
*/
|
|
|
|
/**
|
|
* \fn Histogram::data()
|
|
* \brief Retrieve the internal data
|
|
* \return The data
|
|
*/
|
|
|
|
/**
|
|
* \fn Histogram::total()
|
|
* \brief Retrieve the total number of values in the data set
|
|
* \return Number of values
|
|
*/
|
|
|
|
/**
|
|
* \brief Cumulative frequency up to a (fractional) point in a bin
|
|
* \param[in] bin The bin up to which to cumulate
|
|
*
|
|
* With F(p) the cumulative frequency of the histogram, the value is 0 at
|
|
* the bottom of the histogram, and the maximum is the number of bins.
|
|
* The pixels are spread evenly throughout the “bin” in which they lie, so that
|
|
* F(p) is a continuous (monotonically increasing) function.
|
|
*
|
|
* \return The cumulative frequency from 0 up to the specified bin
|
|
*/
|
|
uint64_t Histogram::cumulativeFrequency(double bin) const
|
|
{
|
|
if (bin <= 0)
|
|
return 0;
|
|
else if (bin >= bins())
|
|
return total();
|
|
int b = static_cast<int32_t>(bin);
|
|
return cumulative_[b] +
|
|
(bin - b) * (cumulative_[b + 1] - cumulative_[b]);
|
|
}
|
|
|
|
/**
|
|
* \brief Return the (fractional) bin of the point through the histogram
|
|
* \param[in] q the desired point (0 <= q <= 1)
|
|
* \param[in] first low limit (default is 0)
|
|
* \param[in] last high limit (default is UINT_MAX)
|
|
*
|
|
* A quantile gives us the point p = Q(q) in the range such that a proportion
|
|
* q of the pixels lie below p. A familiar quantile is Q(0.5) which is the median
|
|
* of a distribution.
|
|
*
|
|
* \return The fractional bin of the point
|
|
*/
|
|
double Histogram::quantile(double q, uint32_t first, uint32_t last) const
|
|
{
|
|
if (last == UINT_MAX)
|
|
last = cumulative_.size() - 2;
|
|
ASSERT(first <= last);
|
|
|
|
uint64_t item = q * total();
|
|
/* Binary search to find the right bin */
|
|
while (first < last) {
|
|
int middle = (first + last) / 2;
|
|
/* Is it between first and middle ? */
|
|
if (cumulative_[middle + 1] > item)
|
|
last = middle;
|
|
else
|
|
first = middle + 1;
|
|
}
|
|
ASSERT(item >= cumulative_[first] && item <= cumulative_[last + 1]);
|
|
|
|
double frac;
|
|
if (cumulative_[first + 1] == cumulative_[first])
|
|
frac = 0;
|
|
else
|
|
frac = (item - cumulative_[first]) / (cumulative_[first + 1] - cumulative_[first]);
|
|
return first + frac;
|
|
}
|
|
|
|
/**
|
|
* \brief Calculate the mean between two quantiles
|
|
* \param[in] lowQuantile low Quantile
|
|
* \param[in] highQuantile high Quantile
|
|
*
|
|
* Quantiles are not ideal for metering as they suffer several limitations.
|
|
* Instead, a concept is introduced here: inter-quantile mean.
|
|
* It returns the mean of all pixels between lowQuantile and highQuantile.
|
|
*
|
|
* \return The mean histogram bin value between the two quantiles
|
|
*/
|
|
double Histogram::interQuantileMean(double lowQuantile, double highQuantile) const
|
|
{
|
|
ASSERT(highQuantile > lowQuantile);
|
|
/* Proportion of pixels which lies below lowQuantile */
|
|
double lowPoint = quantile(lowQuantile);
|
|
/* Proportion of pixels which lies below highQuantile */
|
|
double highPoint = quantile(highQuantile, static_cast<uint32_t>(lowPoint));
|
|
double sumBinFreq = 0, cumulFreq = 0;
|
|
|
|
for (double p_next = floor(lowPoint) + 1.0;
|
|
p_next <= ceil(highPoint);
|
|
lowPoint = p_next, p_next += 1.0) {
|
|
int bin = floor(lowPoint);
|
|
double freq = (cumulative_[bin + 1] - cumulative_[bin])
|
|
* (std::min(p_next, highPoint) - lowPoint);
|
|
|
|
/* Accumulate weighted bin */
|
|
sumBinFreq += bin * freq;
|
|
/* Accumulate weights */
|
|
cumulFreq += freq;
|
|
}
|
|
/* add 0.5 to give an average for bin mid-points */
|
|
return sumBinFreq / cumulFreq + 0.5;
|
|
}
|
|
|
|
} /* namespace ipa */
|
|
|
|
} /* namespace libcamera */
|