Message ID | 20241218074601.3552093-2-paul.elder@ideasonboard.com |
---|---|
State | New |
Headers | show |
Series |
|
Related | show |
Quoting Paul Elder (2024-12-18 07:46:00) > Add a Lux helper to libipa that does the estimation of the lux level > given gain, exposure, and luminance histogram. The helper also > handles reading the reference values from the tuning file. These are > expected to be common operations of lux algorithm modules in IPAs, and > is modeled/copied from Raspberry Pi. > > Signed-off-by: Paul Elder <paul.elder@ideasonboard.com> > Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com> Looking forward to being able to report more helpful metadata to the applciations about the image! Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com> > > --- > Changes in v3: > - improve documentation > - minor docs formatting fixes > - replace setBinSize() with constructor parameter > - s/readYaml/parseTuningData/ > - remove unnecessary includes > > Changes in v2: > - improve documentation > - add binSize member variable and corresponding setter > - remove aperture > - split gain into analogue and digital > - change tuning file names into camel case > --- > src/ipa/libipa/lux.cpp | 181 +++++++++++++++++++++++++++++++++++++ > src/ipa/libipa/lux.h | 42 +++++++++ > src/ipa/libipa/meson.build | 2 + > 3 files changed, 225 insertions(+) > create mode 100644 src/ipa/libipa/lux.cpp > create mode 100644 src/ipa/libipa/lux.h > > diff --git a/src/ipa/libipa/lux.cpp b/src/ipa/libipa/lux.cpp > new file mode 100644 > index 000000000000..b60060e21803 > --- /dev/null > +++ b/src/ipa/libipa/lux.cpp > @@ -0,0 +1,181 @@ > +/* SPDX-License-Identifier: BSD-2-Clause */ > +/* > + * Copyright (C) 2019, Raspberry Pi Ltd > + * Copyright (C) 2024, Paul Elder <paul.elder@ideasonboard.com> > + * > + * Helper class that implements lux estimation > + */ > +#include "lux.h" > + > +#include <algorithm> > +#include <chrono> > + > +#include <libcamera/base/log.h> > + > +#include "libcamera/internal/yaml_parser.h" > + > +#include "histogram.h" > + > +/** > + * \file lux.h > + * \brief Helper class that implements lux estimation > + * > + * Estimating the lux level of an image is a common operation that can for > + * instance be used to adjust the target Y value in AGC or for Bayesian AWB > + * estimation. > + */ > + > +namespace libcamera { > + > +using namespace std::literals::chrono_literals; > + > +LOG_DEFINE_CATEGORY(Lux) > + > +namespace ipa { > + > +/** > + * \class Lux > + * \brief Class that implements lux estimation > + * > + * IPAs that wish to use lux esimation should create a Lux algorithm module > + * that lightly wraps this module by providing the platform-specific luminance > + * histogram. The Lux entry in the tuning file must then precede the algorithms > + * that depend on the estimated lux value. > + */ > + > +/** > + * \var Lux::binSize_ > + * \brief The maximum count of each bin > + */ > + > +/** > + * \var Lux::referenceExposureTime_ > + * \brief The exposure time of the reference image, in microseconds > + */ > + > +/** > + * \var Lux::referenceAnalogueGain_ > + * \brief The analogue gain of the reference image > + */ > + > +/** > + * \var Lux::referenceDigitalGain_ > + * \brief The analogue gain of the reference image > + */ > + > +/** > + * \var Lux::referenceY_ > + * \brief The measured luminance of the reference image, out of the bin size > + * > + * \sa binSize_ > + */ > + > +/** > + * \var Lux::referenceLux_ > + * \brief The estimated lux level of the reference image > + */ > + > +/** > + * \brief Construct the Lux helper module > + * \param[in] binSize The maximum count of each bin > + */ > +Lux::Lux(unsigned int binSize) > + : binSize_(binSize) > +{ > +} > + > +/** > + * \brief Parse tuning data > + * \param[in] tuningData The YamlObject representing the tuning data > + * > + * This function parses yaml tuning data for the common Lux module. It requires > + * reference exposure time, analogue gain, digital gain, and lux values. > + * > + * \code{.unparsed} > + * algorithms: > + * - Lux: > + * referenceExposureTime: 10000 > + * referenceAnalogueGain: 4.0 > + * referenceDigitalGain: 1.0 > + * referenceY: 12000 > + * referenceLux: 1000 > + * \endcode > + * > + * \return 0 on success or a negative error code > + */ > +int Lux::parseTuningData(const YamlObject &tuningData) > +{ > + auto value = tuningData["referenceExposureTime"].get<double>(); > + if (!value) { > + LOG(Lux, Error) << "Missing tuning parameter: " > + << "'referenceExposureTime'"; > + return -EINVAL; > + } > + referenceExposureTime_ = *value * 1.0us; > + > + value = tuningData["referenceAnalogueGain"].get<double>(); > + if (!value) { > + LOG(Lux, Error) << "Missing tuning parameter: " > + << "'referenceAnalogueGain'"; > + return -EINVAL; > + } > + referenceAnalogueGain_ = *value; > + > + value = tuningData["referenceDigitalGain"].get<double>(); > + if (!value) { > + LOG(Lux, Error) << "Missing tuning parameter: " > + << "'referenceDigitalGain'"; > + return -EINVAL; > + } > + referenceDigitalGain_ = *value; > + > + value = tuningData["referenceY"].get<double>(); > + if (!value) { > + LOG(Lux, Error) << "Missing tuning parameter: " > + << "'referenceY'"; > + return -EINVAL; > + } > + referenceY_ = *value; > + > + value = tuningData["referenceLux"].get<double>(); > + if (!value) { > + LOG(Lux, Error) << "Missing tuning parameter: " > + << "'referenceLux'"; > + return -EINVAL; > + } > + referenceLux_ = *value; > + > + return 0; > +} > + > +/** > + * \brief Estimate lux given runtime values > + * \param[in] exposureTime Exposure time applied to the frame > + * \param[in] aGain Analogue gain applied to the frame > + * \param[in] dGain Digital gain applied to the frame > + * \param[in] yHist Histogram from the ISP statistics > + * > + * Estimate the lux given the exposure time, gain, and histogram. > + * > + * \return Estimated lux value > + */ > +double Lux::estimateLux(utils::Duration exposureTime, > + double aGain, double dGain, > + const Histogram &yHist) const > +{ > + double currentY = yHist.interQuantileMean(0, 1); > + double exposureTimeRatio = referenceExposureTime_ / exposureTime; > + double aGainRatio = referenceAnalogueGain_ / aGain; > + double dGainRatio = referenceDigitalGain_ / dGain; > + double yRatio = currentY * (binSize_ / yHist.bins()) / referenceY_; > + > + double estimatedLux = exposureTimeRatio * aGainRatio * dGainRatio * > + yRatio * referenceLux_; > + > + LOG(Lux, Debug) << "Estimated lux " << estimatedLux; > + return estimatedLux; > +} > + > +} /* namespace ipa */ > + > +} /* namespace libcamera */ > diff --git a/src/ipa/libipa/lux.h b/src/ipa/libipa/lux.h > new file mode 100644 > index 000000000000..93ca64795803 > --- /dev/null > +++ b/src/ipa/libipa/lux.h > @@ -0,0 +1,42 @@ > +/* SPDX-License-Identifier: BSD-2-Clause */ > +/* > + * Copyright (C) 2019, Raspberry Pi Ltd > + * Copyright (C) 2024, Paul Elder <paul.elder@ideasonboard.com> > + * > + * Helper class that implements lux estimation > + */ > + > +#pragma once > + > +#include <libcamera/base/utils.h> > + > +namespace libcamera { > + > +class YamlObject; > + > +namespace ipa { > + > +class Histogram; > + > +class Lux > +{ > +public: > + Lux(unsigned int binSize); > + > + int parseTuningData(const YamlObject &tuningData); > + double estimateLux(utils::Duration exposureTime, > + double aGain, double dGain, > + const Histogram &yHist) const; > + > +private: > + unsigned int binSize_; > + utils::Duration referenceExposureTime_; > + double referenceAnalogueGain_; > + double referenceDigitalGain_; > + double referenceY_; > + double referenceLux_; > +}; > + > +} /* namespace ipa */ > + > +} /* namespace libcamera */ > diff --git a/src/ipa/libipa/meson.build b/src/ipa/libipa/meson.build > index a7f16ff63079..f2b2f4be50db 100644 > --- a/src/ipa/libipa/meson.build > +++ b/src/ipa/libipa/meson.build > @@ -11,6 +11,7 @@ libipa_headers = files([ > 'histogram.h', > 'interpolator.h', > 'lsc_polynomial.h', > + 'lux.h', > 'module.h', > 'pwl.h', > 'vector.h', > @@ -27,6 +28,7 @@ libipa_sources = files([ > 'histogram.cpp', > 'interpolator.cpp', > 'lsc_polynomial.cpp', > + 'lux.cpp', > 'module.cpp', > 'pwl.cpp', > 'vector.cpp', > -- > 2.39.2 >
On Wed, 2024-12-18 at 16:46 +0900, Paul Elder wrote: > Add a Lux helper to libipa that does the estimation of the lux level > given gain, exposure, and luminance histogram. The helper also > handles reading the reference values from the tuning file. These are > expected to be common operations of lux algorithm modules in IPAs, > and > is modeled/copied from Raspberry Pi. > > Signed-off-by: Paul Elder <paul.elder@ideasonboard.com> > Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com> > > --- > Changes in v3: > - improve documentation > - minor docs formatting fixes > - replace setBinSize() with constructor parameter > - s/readYaml/parseTuningData/ > - remove unnecessary includes > > Changes in v2: > - improve documentation > - add binSize member variable and corresponding setter > - remove aperture > - split gain into analogue and digital > - change tuning file names into camel case > --- > src/ipa/libipa/lux.cpp | 181 > +++++++++++++++++++++++++++++++++++++ > src/ipa/libipa/lux.h | 42 +++++++++ > src/ipa/libipa/meson.build | 2 + > 3 files changed, 225 insertions(+) > create mode 100644 src/ipa/libipa/lux.cpp > create mode 100644 src/ipa/libipa/lux.h > > diff --git a/src/ipa/libipa/lux.cpp b/src/ipa/libipa/lux.cpp > new file mode 100644 > index 000000000000..b60060e21803 > --- /dev/null > +++ b/src/ipa/libipa/lux.cpp > @@ -0,0 +1,181 @@ > +/* SPDX-License-Identifier: BSD-2-Clause */ > +/* > + * Copyright (C) 2019, Raspberry Pi Ltd > + * Copyright (C) 2024, Paul Elder <paul.elder@ideasonboard.com> > + * > + * Helper class that implements lux estimation > + */ > +#include "lux.h" > + > +#include <algorithm> > +#include <chrono> > + > +#include <libcamera/base/log.h> > + > +#include "libcamera/internal/yaml_parser.h" > + > +#include "histogram.h" > + > +/** > + * \file lux.h > + * \brief Helper class that implements lux estimation > + * > + * Estimating the lux level of an image is a common operation that > can for > + * instance be used to adjust the target Y value in AGC or for > Bayesian AWB > + * estimation. > + */ > + > +namespace libcamera { > + > +using namespace std::literals::chrono_literals; > + > +LOG_DEFINE_CATEGORY(Lux) > + > +namespace ipa { > + > +/** > + * \class Lux > + * \brief Class that implements lux estimation > + * > + * IPAs that wish to use lux esimation should create a Lux algorithm > module s/esimation/estimation Very nitpicky... but otherwise Reviewed-by: Isaac Scott <isaac.scott@ideasonboard.com> > + * that lightly wraps this module by providing the platform-specific > luminance > + * histogram. The Lux entry in the tuning file must then precede the > algorithms > + * that depend on the estimated lux value. > + */ > + > +/** > + * \var Lux::binSize_ > + * \brief The maximum count of each bin > + */ > + > +/** > + * \var Lux::referenceExposureTime_ > + * \brief The exposure time of the reference image, in microseconds > + */ > + > +/** > + * \var Lux::referenceAnalogueGain_ > + * \brief The analogue gain of the reference image > + */ > + > +/** > + * \var Lux::referenceDigitalGain_ > + * \brief The analogue gain of the reference image > + */ > + > +/** > + * \var Lux::referenceY_ > + * \brief The measured luminance of the reference image, out of the > bin size > + * > + * \sa binSize_ > + */ > + > +/** > + * \var Lux::referenceLux_ > + * \brief The estimated lux level of the reference image > + */ > + > +/** > + * \brief Construct the Lux helper module > + * \param[in] binSize The maximum count of each bin > + */ > +Lux::Lux(unsigned int binSize) > + : binSize_(binSize) > +{ > +} > + > +/** > + * \brief Parse tuning data > + * \param[in] tuningData The YamlObject representing the tuning data > + * > + * This function parses yaml tuning data for the common Lux module. > It requires > + * reference exposure time, analogue gain, digital gain, and lux > values. > + * > + * \code{.unparsed} > + * algorithms: > + * - Lux: > + * referenceExposureTime: 10000 > + * referenceAnalogueGain: 4.0 > + * referenceDigitalGain: 1.0 > + * referenceY: 12000 > + * referenceLux: 1000 > + * \endcode > + * > + * \return 0 on success or a negative error code > + */ > +int Lux::parseTuningData(const YamlObject &tuningData) > +{ > + auto value = > tuningData["referenceExposureTime"].get<double>(); > + if (!value) { > + LOG(Lux, Error) << "Missing tuning parameter: " > + << "'referenceExposureTime'"; > + return -EINVAL; > + } > + referenceExposureTime_ = *value * 1.0us; > + > + value = tuningData["referenceAnalogueGain"].get<double>(); > + if (!value) { > + LOG(Lux, Error) << "Missing tuning parameter: " > + << "'referenceAnalogueGain'"; > + return -EINVAL; > + } > + referenceAnalogueGain_ = *value; > + > + value = tuningData["referenceDigitalGain"].get<double>(); > + if (!value) { > + LOG(Lux, Error) << "Missing tuning parameter: " > + << "'referenceDigitalGain'"; > + return -EINVAL; > + } > + referenceDigitalGain_ = *value; > + > + value = tuningData["referenceY"].get<double>(); > + if (!value) { > + LOG(Lux, Error) << "Missing tuning parameter: " > + << "'referenceY'"; > + return -EINVAL; > + } > + referenceY_ = *value; > + > + value = tuningData["referenceLux"].get<double>(); > + if (!value) { > + LOG(Lux, Error) << "Missing tuning parameter: " > + << "'referenceLux'"; > + return -EINVAL; > + } > + referenceLux_ = *value; > + > + return 0; > +} > + > +/** > + * \brief Estimate lux given runtime values > + * \param[in] exposureTime Exposure time applied to the frame > + * \param[in] aGain Analogue gain applied to the frame > + * \param[in] dGain Digital gain applied to the frame > + * \param[in] yHist Histogram from the ISP statistics > + * > + * Estimate the lux given the exposure time, gain, and histogram. > + * > + * \return Estimated lux value > + */ > +double Lux::estimateLux(utils::Duration exposureTime, > + double aGain, double dGain, > + const Histogram &yHist) const > +{ > + double currentY = yHist.interQuantileMean(0, 1); > + double exposureTimeRatio = referenceExposureTime_ / > exposureTime; > + double aGainRatio = referenceAnalogueGain_ / aGain; > + double dGainRatio = referenceDigitalGain_ / dGain; > + double yRatio = currentY * (binSize_ / yHist.bins()) / > referenceY_; > + > + double estimatedLux = exposureTimeRatio * aGainRatio * > dGainRatio * > + yRatio * referenceLux_; > + > + LOG(Lux, Debug) << "Estimated lux " << estimatedLux; > + return estimatedLux; > +} > + > +} /* namespace ipa */ > + > +} /* namespace libcamera */ > diff --git a/src/ipa/libipa/lux.h b/src/ipa/libipa/lux.h > new file mode 100644 > index 000000000000..93ca64795803 > --- /dev/null > +++ b/src/ipa/libipa/lux.h > @@ -0,0 +1,42 @@ > +/* SPDX-License-Identifier: BSD-2-Clause */ > +/* > + * Copyright (C) 2019, Raspberry Pi Ltd > + * Copyright (C) 2024, Paul Elder <paul.elder@ideasonboard.com> > + * > + * Helper class that implements lux estimation > + */ > + > +#pragma once > + > +#include <libcamera/base/utils.h> > + > +namespace libcamera { > + > +class YamlObject; > + > +namespace ipa { > + > +class Histogram; > + > +class Lux > +{ > +public: > + Lux(unsigned int binSize); > + > + int parseTuningData(const YamlObject &tuningData); > + double estimateLux(utils::Duration exposureTime, > + double aGain, double dGain, > + const Histogram &yHist) const; > + > +private: > + unsigned int binSize_; > + utils::Duration referenceExposureTime_; > + double referenceAnalogueGain_; > + double referenceDigitalGain_; > + double referenceY_; > + double referenceLux_; > +}; > + > +} /* namespace ipa */ > + > +} /* namespace libcamera */ > diff --git a/src/ipa/libipa/meson.build b/src/ipa/libipa/meson.build > index a7f16ff63079..f2b2f4be50db 100644 > --- a/src/ipa/libipa/meson.build > +++ b/src/ipa/libipa/meson.build > @@ -11,6 +11,7 @@ libipa_headers = files([ > 'histogram.h', > 'interpolator.h', > 'lsc_polynomial.h', > + 'lux.h', > 'module.h', > 'pwl.h', > 'vector.h', > @@ -27,6 +28,7 @@ libipa_sources = files([ > 'histogram.cpp', > 'interpolator.cpp', > 'lsc_polynomial.cpp', > + 'lux.cpp', > 'module.cpp', > 'pwl.cpp', > 'vector.cpp',
diff --git a/src/ipa/libipa/lux.cpp b/src/ipa/libipa/lux.cpp new file mode 100644 index 000000000000..b60060e21803 --- /dev/null +++ b/src/ipa/libipa/lux.cpp @@ -0,0 +1,181 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * Copyright (C) 2019, Raspberry Pi Ltd + * Copyright (C) 2024, Paul Elder <paul.elder@ideasonboard.com> + * + * Helper class that implements lux estimation + */ +#include "lux.h" + +#include <algorithm> +#include <chrono> + +#include <libcamera/base/log.h> + +#include "libcamera/internal/yaml_parser.h" + +#include "histogram.h" + +/** + * \file lux.h + * \brief Helper class that implements lux estimation + * + * Estimating the lux level of an image is a common operation that can for + * instance be used to adjust the target Y value in AGC or for Bayesian AWB + * estimation. + */ + +namespace libcamera { + +using namespace std::literals::chrono_literals; + +LOG_DEFINE_CATEGORY(Lux) + +namespace ipa { + +/** + * \class Lux + * \brief Class that implements lux estimation + * + * IPAs that wish to use lux esimation should create a Lux algorithm module + * that lightly wraps this module by providing the platform-specific luminance + * histogram. The Lux entry in the tuning file must then precede the algorithms + * that depend on the estimated lux value. + */ + +/** + * \var Lux::binSize_ + * \brief The maximum count of each bin + */ + +/** + * \var Lux::referenceExposureTime_ + * \brief The exposure time of the reference image, in microseconds + */ + +/** + * \var Lux::referenceAnalogueGain_ + * \brief The analogue gain of the reference image + */ + +/** + * \var Lux::referenceDigitalGain_ + * \brief The analogue gain of the reference image + */ + +/** + * \var Lux::referenceY_ + * \brief The measured luminance of the reference image, out of the bin size + * + * \sa binSize_ + */ + +/** + * \var Lux::referenceLux_ + * \brief The estimated lux level of the reference image + */ + +/** + * \brief Construct the Lux helper module + * \param[in] binSize The maximum count of each bin + */ +Lux::Lux(unsigned int binSize) + : binSize_(binSize) +{ +} + +/** + * \brief Parse tuning data + * \param[in] tuningData The YamlObject representing the tuning data + * + * This function parses yaml tuning data for the common Lux module. It requires + * reference exposure time, analogue gain, digital gain, and lux values. + * + * \code{.unparsed} + * algorithms: + * - Lux: + * referenceExposureTime: 10000 + * referenceAnalogueGain: 4.0 + * referenceDigitalGain: 1.0 + * referenceY: 12000 + * referenceLux: 1000 + * \endcode + * + * \return 0 on success or a negative error code + */ +int Lux::parseTuningData(const YamlObject &tuningData) +{ + auto value = tuningData["referenceExposureTime"].get<double>(); + if (!value) { + LOG(Lux, Error) << "Missing tuning parameter: " + << "'referenceExposureTime'"; + return -EINVAL; + } + referenceExposureTime_ = *value * 1.0us; + + value = tuningData["referenceAnalogueGain"].get<double>(); + if (!value) { + LOG(Lux, Error) << "Missing tuning parameter: " + << "'referenceAnalogueGain'"; + return -EINVAL; + } + referenceAnalogueGain_ = *value; + + value = tuningData["referenceDigitalGain"].get<double>(); + if (!value) { + LOG(Lux, Error) << "Missing tuning parameter: " + << "'referenceDigitalGain'"; + return -EINVAL; + } + referenceDigitalGain_ = *value; + + value = tuningData["referenceY"].get<double>(); + if (!value) { + LOG(Lux, Error) << "Missing tuning parameter: " + << "'referenceY'"; + return -EINVAL; + } + referenceY_ = *value; + + value = tuningData["referenceLux"].get<double>(); + if (!value) { + LOG(Lux, Error) << "Missing tuning parameter: " + << "'referenceLux'"; + return -EINVAL; + } + referenceLux_ = *value; + + return 0; +} + +/** + * \brief Estimate lux given runtime values + * \param[in] exposureTime Exposure time applied to the frame + * \param[in] aGain Analogue gain applied to the frame + * \param[in] dGain Digital gain applied to the frame + * \param[in] yHist Histogram from the ISP statistics + * + * Estimate the lux given the exposure time, gain, and histogram. + * + * \return Estimated lux value + */ +double Lux::estimateLux(utils::Duration exposureTime, + double aGain, double dGain, + const Histogram &yHist) const +{ + double currentY = yHist.interQuantileMean(0, 1); + double exposureTimeRatio = referenceExposureTime_ / exposureTime; + double aGainRatio = referenceAnalogueGain_ / aGain; + double dGainRatio = referenceDigitalGain_ / dGain; + double yRatio = currentY * (binSize_ / yHist.bins()) / referenceY_; + + double estimatedLux = exposureTimeRatio * aGainRatio * dGainRatio * + yRatio * referenceLux_; + + LOG(Lux, Debug) << "Estimated lux " << estimatedLux; + return estimatedLux; +} + +} /* namespace ipa */ + +} /* namespace libcamera */ diff --git a/src/ipa/libipa/lux.h b/src/ipa/libipa/lux.h new file mode 100644 index 000000000000..93ca64795803 --- /dev/null +++ b/src/ipa/libipa/lux.h @@ -0,0 +1,42 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * Copyright (C) 2019, Raspberry Pi Ltd + * Copyright (C) 2024, Paul Elder <paul.elder@ideasonboard.com> + * + * Helper class that implements lux estimation + */ + +#pragma once + +#include <libcamera/base/utils.h> + +namespace libcamera { + +class YamlObject; + +namespace ipa { + +class Histogram; + +class Lux +{ +public: + Lux(unsigned int binSize); + + int parseTuningData(const YamlObject &tuningData); + double estimateLux(utils::Duration exposureTime, + double aGain, double dGain, + const Histogram &yHist) const; + +private: + unsigned int binSize_; + utils::Duration referenceExposureTime_; + double referenceAnalogueGain_; + double referenceDigitalGain_; + double referenceY_; + double referenceLux_; +}; + +} /* namespace ipa */ + +} /* namespace libcamera */ diff --git a/src/ipa/libipa/meson.build b/src/ipa/libipa/meson.build index a7f16ff63079..f2b2f4be50db 100644 --- a/src/ipa/libipa/meson.build +++ b/src/ipa/libipa/meson.build @@ -11,6 +11,7 @@ libipa_headers = files([ 'histogram.h', 'interpolator.h', 'lsc_polynomial.h', + 'lux.h', 'module.h', 'pwl.h', 'vector.h', @@ -27,6 +28,7 @@ libipa_sources = files([ 'histogram.cpp', 'interpolator.cpp', 'lsc_polynomial.cpp', + 'lux.cpp', 'module.cpp', 'pwl.cpp', 'vector.cpp',