From 3ed8dabe5a52024fe6b5d671d1e1a8410dfe4e15 Mon Sep 17 00:00:00 2001 From: Stefan Klug Date: Wed, 19 Nov 2025 14:22:12 +0100 Subject: [PATCH] ipa: libipa: agc_mean_luminance: Change luminance target to piecewise linear function In some situations it is necessary to specify the target brightness value depending on the overall lux level. Replace the float relativeLuminanceTraget by a PWL. As the PWL loading code loads a plain value as single point PWL, backwards compatibility to existing tuning files is ensured. While at it, order the class members in reverse xmas tree notation. Signed-off-by: Paul Elder Signed-off-by: Stefan Klug Reviewed-by: Daniel Scally Reviewed-by: Kieran Bingham --- src/ipa/libipa/agc_mean_luminance.cpp | 74 ++++++++++++++++++++++++--- src/ipa/libipa/agc_mean_luminance.h | 16 ++++-- src/ipa/rkisp1/algorithms/agc.cpp | 1 + 3 files changed, 80 insertions(+), 11 deletions(-) diff --git a/src/ipa/libipa/agc_mean_luminance.cpp b/src/ipa/libipa/agc_mean_luminance.cpp index 64f36bd7..d04cd6bc 100644 --- a/src/ipa/libipa/agc_mean_luminance.cpp +++ b/src/ipa/libipa/agc_mean_luminance.cpp @@ -54,6 +54,14 @@ static constexpr double kDefaultRelativeLuminanceTarget = 0.16; */ static constexpr double kMaxRelativeLuminanceTarget = 0.95; +/* + * Default lux level + * + * If no lux level or a zero lux level is specified, but PWLs are used to + * specify luminance targets, this default level is used. + */ +static constexpr unsigned int kDefaultLuxLevel = 500; + /** * \struct AgcMeanLuminance::AgcConstraint * \brief The boundaries and target for an AeConstraintMode constraint @@ -144,17 +152,30 @@ static constexpr double kMaxRelativeLuminanceTarget = 0.95; */ AgcMeanLuminance::AgcMeanLuminance() - : exposureCompensation_(1.0), frameCount_(0), filteredExposure_(0s), - relativeLuminanceTarget_(0) + : filteredExposure_(0s), luxWarningEnabled_(true), + exposureCompensation_(1.0), frameCount_(0), lux_(0) { } AgcMeanLuminance::~AgcMeanLuminance() = default; -void AgcMeanLuminance::parseRelativeLuminanceTarget(const YamlObject &tuningData) +int AgcMeanLuminance::parseRelativeLuminanceTarget(const YamlObject &tuningData) { - relativeLuminanceTarget_ = - tuningData["relativeLuminanceTarget"].get(kDefaultRelativeLuminanceTarget); + auto &target = tuningData["relativeLuminanceTarget"]; + if (!target) { + relativeLuminanceTarget_ = { { { { 0.0, kDefaultRelativeLuminanceTarget } } } }; + return 0; + } + + auto pwl = target.get(); + if (!pwl) { + LOG(AgcMeanLuminance, Error) + << "Failed to load relative luminance target."; + return -EINVAL; + } + + relativeLuminanceTarget_ = std::move(*pwl); + return 0; } void AgcMeanLuminance::parseConstraint(const YamlObject &modeDict, int32_t id) @@ -325,6 +346,8 @@ void AgcMeanLuminance::configure(utils::Duration lineDuration, { for (auto &[id, helper] : exposureModeHelpers_) helper->configure(lineDuration, sensorHelper); + + luxWarningEnabled_ = true; } /** @@ -385,7 +408,9 @@ int AgcMeanLuminance::parseTuningData(const YamlObject &tuningData) { int ret; - parseRelativeLuminanceTarget(tuningData); + ret = parseRelativeLuminanceTarget(tuningData); + if (ret) + return ret; ret = parseConstraintModes(tuningData); if (ret) @@ -403,6 +428,16 @@ int AgcMeanLuminance::parseTuningData(const YamlObject &tuningData) * AGC calculations. It is expressed as gain instead of EV. */ +/** + * \fn AgcMeanLuminance::setLux(int lux) + * \brief Set the lux level + * \param[in] lux The lux level + * + * This function sets the lux level to be used in the AGC calculations. A value + * of 0 means no measurement and a default value of \a kDefaultLuxLevel is used + * if necessary. + */ + /** * \brief Set the ExposureModeHelper limits for this class * \param[in] minExposureTime Minimum exposure time to allow @@ -538,7 +573,32 @@ double AgcMeanLuminance::constraintClampGain(uint32_t constraintModeIndex, */ double AgcMeanLuminance::effectiveYTarget() const { - return std::min(relativeLuminanceTarget_ * exposureCompensation_, + double lux = lux_; + if (relativeLuminanceTarget_.size() > 1 && lux_ == 0) { + /* + * Warn after a few frames if there is still no lux measurement + * available. The number of 10 is chosen a bit arbitrarily. It + * is big enough to skip the frames that get queued on start + * (and therefore are expected to have no valid lux value) and + * small enough to show up quickly. + */ + if (frameCount_ > 10 && luxWarningEnabled_) { + luxWarningEnabled_ = false; + LOG(AgcMeanLuminance, Warning) + << "Missing lux value for luminance target " + "calculation, default to " + << kDefaultLuxLevel + << ". Note that the Lux algorithm must be " + "included before the Agc algorithm."; + } + + lux = kDefaultLuxLevel; + } + + double luminanceTarget = relativeLuminanceTarget_.eval( + relativeLuminanceTarget_.domain().clamp(lux)); + + return std::min(luminanceTarget * exposureCompensation_, kMaxRelativeLuminanceTarget); } diff --git a/src/ipa/libipa/agc_mean_luminance.h b/src/ipa/libipa/agc_mean_luminance.h index e5f164c3..746b97b1 100644 --- a/src/ipa/libipa/agc_mean_luminance.h +++ b/src/ipa/libipa/agc_mean_luminance.h @@ -20,6 +20,7 @@ #include "exposure_mode_helper.h" #include "histogram.h" +#include "pwl.h" namespace libcamera { @@ -50,6 +51,11 @@ public: exposureCompensation_ = gain; } + void setLux(unsigned int lux) + { + lux_ = lux; + } + void setLimits(utils::Duration minExposureTime, utils::Duration maxExposureTime, double minGain, double maxGain, std::vector constraints); @@ -82,7 +88,7 @@ public: private: virtual double estimateLuminance(const double gain) const = 0; - void parseRelativeLuminanceTarget(const YamlObject &tuningData); + int parseRelativeLuminanceTarget(const YamlObject &tuningData); void parseConstraint(const YamlObject &modeDict, int32_t id); int parseConstraintModes(const YamlObject &tuningData); int parseExposureModes(const YamlObject &tuningData); @@ -92,10 +98,12 @@ private: double gain); utils::Duration filterExposure(utils::Duration exposureValue); - double exposureCompensation_; - uint64_t frameCount_; utils::Duration filteredExposure_; - double relativeLuminanceTarget_; + mutable bool luxWarningEnabled_; + double exposureCompensation_; + Pwl relativeLuminanceTarget_; + uint64_t frameCount_; + unsigned int lux_; std::vector additionalConstraints_; std::map> constraintModes_; diff --git a/src/ipa/rkisp1/algorithms/agc.cpp b/src/ipa/rkisp1/algorithms/agc.cpp index f5a3c917..1ecaff68 100644 --- a/src/ipa/rkisp1/algorithms/agc.cpp +++ b/src/ipa/rkisp1/algorithms/agc.cpp @@ -618,6 +618,7 @@ void Agc::process(IPAContext &context, [[maybe_unused]] const uint32_t frame, effectiveExposureValue *= frameContext.agc.quantizationGain; setExposureCompensation(pow(2.0, frameContext.agc.exposureValue)); + setLux(frameContext.lux.lux); utils::Duration newExposureTime; double aGain, qGain, dGain;