From patchwork Wed Nov 11 11:55:45 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Barnab=C3=A1s_P=C5=91cze?= X-Patchwork-Id: 28845 Return-Path: X-Original-To: parsemail@patchwork.libcamera.org Delivered-To: parsemail@patchwork.libcamera.org Received: from lancelot.ideasonboard.com (lancelot.ideasonboard.com [92.243.16.209]) by patchwork.libcamera.org (Postfix) with ESMTPS id 3C179C3307 for ; Wed, 11 Nov 2026 11:56:18 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id C84446A7B8; Wed, 11 Nov 2026 12:56:08 +0100 (CET) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="sKXn1JDF"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 8215469119 for ; Wed, 11 Nov 2026 12:56:04 +0100 (CET) Received: from pb-laptop.local (185.221.143.99.nat.pool.zt.hu [185.221.143.99]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 61DC2161F; Wed, 11 Nov 2026 12:53:41 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1794398021; bh=217l7ZIPXhw+4aZLOFrhQWdqkWzZZtMtcijXSoSdiks=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=sKXn1JDFAq6i/IeAIrPlwMcaMVEG1BWXYMArkM8s4Fudc7uR/5t1/cnsi2M/CY/tq yqVA7/EasSj4lCTHknkfc+K+SuJ2ecUJEzm6khO4WfnB3HDInqyi7UOUUL9YOHApTI u8zBgwYkEiCUE0K5A1QobqsK70Hvo7FjR9o3bv/s= From: =?utf-8?q?Barnab=C3=A1s_P=C5=91cze?= To: libcamera-devel@lists.libcamera.org Cc: Stefan Klug , Jacopo Mondi Subject: [PATCH v9 04/18] ipa: libipa: agc_mean_luminance: Split tuning file parsing Date: Wed, 11 Nov 2026 12:55:45 +0100 Message-ID: <20261111115600.1097488-5-barnabas.pocze@ideasonboard.com> X-Mailer: git-send-email 2.56.0 In-Reply-To: <20261111115600.1097488-1-barnabas.pocze@ideasonboard.com> References: <20261111115600.1097488-1-barnabas.pocze@ideasonboard.com> MIME-Version: 1.0 X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" Create a separate type that contains every piece of information that is found in tuning files, and add a separate `parse()` function to populate an instance of that. The main goal of this is for the tuning file data to be inspected before the fallback defaults are added, so those are removed from the parsing and moved into the constructor. Signed-off-by: Barnabás Pőcze Reviewed-by: Stefan Klug Reviewed-by: Jacopo Mondi --- src/ipa/libipa/agc.cpp | 11 +- src/ipa/libipa/agc_mean_luminance.cpp | 225 +++++++++++++++----------- src/ipa/libipa/agc_mean_luminance.h | 26 +-- 3 files changed, 154 insertions(+), 108 deletions(-) diff --git a/src/ipa/libipa/agc.cpp b/src/ipa/libipa/agc.cpp index 182dd7380e..0d7822ab63 100644 --- a/src/ipa/libipa/agc.cpp +++ b/src/ipa/libipa/agc.cpp @@ -299,7 +299,7 @@ namespace agc { * * If a CameraSensorHelper is provided in \a sensor, an instance of * AgcMeanLuminance is instantiated and the tuning data is loaded by - * calling AgcMeanLuminance::parseTuningData(). Otherwise an instance + * calling AgcMeanLuminance::Configuration::parse(). Otherwise an instance * of AgcMSV is instantiated, and in this case the tuning data is ignored. * * \return 0 on success, or a negative error code @@ -310,10 +310,11 @@ int AgcAlgorithm::init(const ValueNode &tuningData, CameraSensorHelper *sensor, const ConfigurationParams &config) { if (sensor) { - auto &impl = impl_.emplace(); - int ret = impl.parseTuningData(tuningData); - if (ret) - return ret; + auto agcConfig = AgcMeanLuminance::Configuration::parse(tuningData); + if (!agcConfig) + return -EINVAL; + + impl_.emplace(std::move(*agcConfig)); } else { impl_.emplace(); } diff --git a/src/ipa/libipa/agc_mean_luminance.cpp b/src/ipa/libipa/agc_mean_luminance.cpp index a763a3b94d..50b30d75cf 100644 --- a/src/ipa/libipa/agc_mean_luminance.cpp +++ b/src/ipa/libipa/agc_mean_luminance.cpp @@ -9,7 +9,7 @@ #include #include -#include +#include #include #include @@ -106,6 +106,29 @@ static constexpr unsigned int kDefaultLuxLevel = 500; * \brief The luminance target for the constraint */ +/** + * \class AgcMeanLuminance::Configuration + * \brief A collection of parameters + * + * This type contains various configuration parameters for the algorithm + * that are generally parsed from a tuning file. + */ + +/** + * \var AgcMeanLuminance::Configuration::relativeLuminanceTarget + * \brief The relative mean luminance target by lux value + */ + +/** + * \var AgcMeanLuminance::Configuration::constraintModes + * \brief The available constraint modes + */ + +/** + * \var AgcMeanLuminance::Configuration::exposureModeHelpers + * \brief The available exposure modes + */ + /** * \class AgcMeanLuminance::Traits * \brief A collection of callbacks @@ -148,17 +171,14 @@ static constexpr unsigned int kDefaultLuxLevel = 500; * section of a histogram to a target value, where the target and the boundaries * of the section of the histogram used in the calculation are taken from the * values defined for the currently configured AeConstraintMode within the - * tuning data. This class provides a helper function to parse those tuning data - * to discover the constraints, and so requires a specific format for those - * data which is described in \ref parseTuningData(). The gain from the first - * stage is then clamped to the gain from this stage. + * configuration. The gain from the first stage is then clamped to the gain from + * this stage. * * The final gain is used to adjust the effective exposure value of the image, * and that new exposure value is divided into exposure time, analogue gain and * digital gain according to the selected AeExposureMode. This class uses the - * \ref ExposureModeHelper class to assist in that division, and expects the - * data needed to initialise that class to be present in tuning data in a - * format described in \ref parseTuningData(). + * \ref ExposureModeHelper class to assist in that division, as provided by the + * configuration. * * In order to be able to use this algorithm an IPA module needs to be able to * do the following: @@ -170,27 +190,21 @@ static constexpr unsigned int kDefaultLuxLevel = 500; * * IPA modules that want to use this class to implement their AEGC algorithm * should derive AgcMeanLuminance::Traits and override the necessary functions. - * The users must call parseTuningData() in init(), and must also call configure() - * and setLimits() in configure(). They may then use calculateNewEv() in process(). - * If the limits passed to setLimits() change for any reason (for example, in - * response to a FrameDurationLimit control being passed in queueRequest()) then - * setLimits() must be called again with the new values. + * The users can use AgcMeanLuminance::Configuration::parse() to parse the + * configuration from tuning data to pass to the constructor. Furthermore, they + * must call configure() and setLimits() in configure(). They may then use + * calculateNewEv() in process(). If the limits passed to setLimits() change + * for any reason (for example, in response to a FrameDurationLimit control + * being passed in queueRequest()) then setLimits() must be called again with + * the new values. */ -AgcMeanLuminance::AgcMeanLuminance() - : filteredExposure_(0s), luxWarningEnabled_(true), frameCount_(0) -{ -} - -AgcMeanLuminance::~AgcMeanLuminance() = default; +namespace { -int AgcMeanLuminance::parseRelativeLuminanceTarget(const ValueNode &tuningData) +int parseRelativeLuminanceTarget(AgcMeanLuminance::Configuration &config, const ValueNode &target) { - auto &target = tuningData["relativeLuminanceTarget"]; - if (!target) { - relativeLuminanceTarget_ = { { { { 0.0, kDefaultRelativeLuminanceTarget } } } }; + if (!target) return 0; - } auto pwl = target.get(); if (!pwl) { @@ -199,11 +213,11 @@ int AgcMeanLuminance::parseRelativeLuminanceTarget(const ValueNode &tuningData) return -EINVAL; } - relativeLuminanceTarget_ = std::move(*pwl); + config.relativeLuminanceTarget = std::move(*pwl); return 0; } -int AgcMeanLuminance::parseConstraint(const ValueNode &modeDict, int32_t id) +int parseConstraint(AgcMeanLuminance::Configuration &config, const ValueNode &modeDict, int32_t id) { for (const auto &[boundName, content] : modeDict.asDict()) { if (boundName != "upper" && boundName != "lower") { @@ -213,7 +227,7 @@ int AgcMeanLuminance::parseConstraint(const ValueNode &modeDict, int32_t id) } unsigned int idx = static_cast(boundName == "upper"); - AgcConstraint::Bound bound = static_cast(idx); + auto bound = static_cast(idx); double qLo = content["qLo"].get().value_or(0.98); double qHi = content["qHi"].get().value_or(1.0); auto yTarget = content["yTarget"].get(); @@ -223,8 +237,13 @@ int AgcMeanLuminance::parseConstraint(const ValueNode &modeDict, int32_t id) return -EINVAL; } - AgcConstraint constraint = { bound, qLo, qHi, std::move(*yTarget) }; - auto &constraints = constraintModes_[id]; + AgcMeanLuminance::AgcConstraint constraint = { + bound, + qLo, + qHi, + std::move(*yTarget), + }; + auto &constraints = config.constraintModes[id]; if (idx) constraints.push_back(std::move(constraint)); @@ -235,7 +254,7 @@ int AgcMeanLuminance::parseConstraint(const ValueNode &modeDict, int32_t id) return 0; } -int AgcMeanLuminance::parseConstraintModes(const ValueNode &tuningData) +int parseConstraintModes(AgcMeanLuminance::Configuration &config, const ValueNode &constraintModes) { static constexpr std::tuple compat[] = { { "ConstraintNormal", "Normal", controls::AeConstraintModeNormal }, @@ -244,7 +263,6 @@ int AgcMeanLuminance::parseConstraintModes(const ValueNode &tuningData) { "ConstraintCustom", "Custom", controls::AeConstraintModeCustom }, }; - const ValueNode &constraintModes = tuningData[controls::AeConstraintMode.name()]; if (constraintModes.isDictionary()) { for (const auto &[modeName, modeDict] : constraintModes.asDict()) { std::optional modeValue; @@ -278,33 +296,16 @@ int AgcMeanLuminance::parseConstraintModes(const ValueNode &tuningData) return -EINVAL; } - int ret = parseConstraint(modeDict, *modeValue); + int ret = parseConstraint(config, modeDict, *modeValue); if (ret) return ret; } } - /* - * If the tuning data file contains no constraints then we use the - * default constraint that the IPU3/RkISP1 Agc algorithms were adhering - * to anyway before centralisation; this constraint forces the top 2% of - * the histogram to be at least 0.5. - */ - if (constraintModes_.empty()) { - AgcConstraint constraint = { - AgcConstraint::Bound::Lower, - 0.98, - 1.0, - Pwl({ { { 0.0, 0.5 } } }) - }; - - constraintModes_[controls::AeConstraintModeNormal].push_back(std::move(constraint)); - } - return 0; } -int AgcMeanLuminance::parseExposureModes(const ValueNode &tuningData) +int parseExposureModes(AgcMeanLuminance::Configuration &config, const ValueNode &exposureModes) { static constexpr std::tuple compat[] = { { "ExposureNormal", "Normal", controls::AeExposureModeNormal }, @@ -313,7 +314,6 @@ int AgcMeanLuminance::parseExposureModes(const ValueNode &tuningData) { "ExposureCustom", "Custom", controls::AeExposureModeCustom }, }; - const ValueNode &exposureModes = tuningData[controls::AeExposureMode.name()]; if (exposureModes.isDictionary()) { for (const auto &[modeName, modeValues] : exposureModes.asDict()) { std::optional modeValue; @@ -372,44 +372,18 @@ int AgcMeanLuminance::parseExposureModes(const ValueNode &tuningData) }); } - exposureModeHelpers_.try_emplace(*modeValue, std::move(stages)); + config.exposureModeHelpers.try_emplace( + *modeValue, std::move(stages)); } } - /* - * If we don't have any exposure modes in the tuning data we create an - * ExposureModeHelper using an empty vector of stages. This will result - * in the ExposureModeHelper simply driving the exposure time as high as - * possible before touching gain. - */ - if (exposureModeHelpers_.empty()) - exposureModeHelpers_.try_emplace(controls::AeExposureModeNormal, - std::vector{}); - return 0; } -/** - * \brief Configure the exposure mode helpers - * \param[in] lineDuration The sensor line length - * \param[in] sensorHelper The sensor helper - * - * This function configures the exposure mode helpers so they can correctly - * take quantization effects into account. - */ -void AgcMeanLuminance::configure(utils::Duration lineDuration, - const CameraSensorHelper *sensorHelper) -{ - for (auto &[id, helper] : exposureModeHelpers_) - helper.configure(lineDuration, sensorHelper); - - luxWarningEnabled_ = true; - filteredExposure_ = 0s; - frameCount_ = 0; -} +} /* namespace */ /** - * \brief Parse tuning data for AeConstraintMode and AeExposureMode controls + * \brief Parse tuning data * \param[in] tuningData the ValueNode representing the tuning data * * This function parses tuning data to build the list of allowed values for the @@ -463,19 +437,86 @@ void AgcMeanLuminance::configure(utils::Duration lineDuration, * * \return 0 on success or a negative error code */ -int AgcMeanLuminance::parseTuningData(const ValueNode &tuningData) +std::optional +AgcMeanLuminance::Configuration::parse(const ValueNode &tuningData) { - int ret; + AgcMeanLuminance::Configuration config; + + if (parseRelativeLuminanceTarget(config, tuningData["relativeLuminanceTarget"])) + return {}; + + if (parseConstraintModes(config, tuningData[controls::AeConstraintMode.name()])) + return {}; + + if (parseExposureModes(config, tuningData[controls::AeExposureMode.name()])) + return {}; + + return config; +} + +/** + * \brief Construct an instance + * \param[in] config The configuration + * + * This function constructs and instance and initializes it with the + * configuration parameters specified in \a config. + */ +AgcMeanLuminance::AgcMeanLuminance(Configuration &&config) + : filteredExposure_(0s), luxWarningEnabled_(true), frameCount_(0), + relativeLuminanceTarget_(std::move(config.relativeLuminanceTarget)), + constraintModes_(std::move(config.constraintModes)), + exposureModeHelpers_(std::move(config.exposureModeHelpers)) +{ + if (relativeLuminanceTarget_.empty()) + relativeLuminanceTarget_ = { { { { 0.0, kDefaultRelativeLuminanceTarget } } } }; - ret = parseRelativeLuminanceTarget(tuningData); - if (ret) - return ret; + /* + * If the tuning data file contains no constraints then we use the + * default constraint that the IPU3/RkISP1 Agc algorithms were adhering + * to anyway before centralisation; this constraint forces the top 2% of + * the histogram to be at least 0.5. + */ + if (constraintModes_.empty()) { + AgcConstraint constraint = { + AgcConstraint::Bound::Lower, + 0.98, + 1.0, + Pwl({ { { 0.0, 0.5 } } }), + }; - ret = parseConstraintModes(tuningData); - if (ret) - return ret; + constraintModes_[controls::AeConstraintModeNormal].push_back(std::move(constraint)); + } - return parseExposureModes(tuningData); + /* + * If we don't have any exposure modes in the tuning data we create an + * ExposureModeHelper using an empty vector of stages. This will result + * in the ExposureModeHelper simply driving the exposure time as high as + * possible before touching gain. + */ + if (exposureModeHelpers_.empty()) + exposureModeHelpers_.try_emplace(controls::AeExposureModeNormal, + std::vector{}); +} + +AgcMeanLuminance::~AgcMeanLuminance() = default; + +/** + * \brief Configure the exposure mode helpers + * \param[in] lineDuration The sensor line length + * \param[in] sensorHelper The sensor helper + * + * This function configures the exposure mode helpers so they can correctly + * take quantization effects into account. + */ +void AgcMeanLuminance::configure(utils::Duration lineDuration, + const CameraSensorHelper *sensorHelper) +{ + for (auto &[id, helper] : exposureModeHelpers_) + helper.configure(lineDuration, sensorHelper); + + luxWarningEnabled_ = true; + filteredExposure_ = 0s; + frameCount_ = 0; } /** @@ -502,12 +543,12 @@ void AgcMeanLuminance::setLimits(utils::Duration minExposureTime, /** * \fn AgcMeanLuminance::constraintModes() - * \brief Get the constraint modes that have been parsed from tuning data + * \brief Get the available constraint modes */ /** * \fn AgcMeanLuminance::exposureModeHelpers() - * \brief Get the ExposureModeHelpers that have been parsed from tuning data + * \brief Get the available ExposureModeHelpers */ /** diff --git a/src/ipa/libipa/agc_mean_luminance.h b/src/ipa/libipa/agc_mean_luminance.h index f799618ffc..ffd1f1e25a 100644 --- a/src/ipa/libipa/agc_mean_luminance.h +++ b/src/ipa/libipa/agc_mean_luminance.h @@ -8,7 +8,7 @@ #pragma once #include -#include +#include #include #include @@ -26,9 +26,6 @@ namespace ipa { class AgcMeanLuminance { public: - AgcMeanLuminance(); - ~AgcMeanLuminance(); - struct AgcConstraint { enum class Bound { Lower = 0, @@ -45,9 +42,19 @@ public: virtual double estimateLuminance(double gain) const = 0; }; - void configure(utils::Duration lineDuration, const CameraSensorHelper *sensorHelper); - int parseTuningData(const ValueNode &tuningData); + struct Configuration { + Pwl relativeLuminanceTarget; + std::map> constraintModes; + std::map exposureModeHelpers; + [[nodiscard]] + static std::optional parse(const ValueNode &tuningData); + }; + + AgcMeanLuminance(Configuration &&config); + ~AgcMeanLuminance(); + + void configure(utils::Duration lineDuration, const CameraSensorHelper *sensorHelper); void setLimits(utils::Duration minExposureTime, utils::Duration maxExposureTime, double minGain, double maxGain, std::vector constraints); @@ -80,20 +87,17 @@ public: double effectiveYTarget(double lux, double exposureCompensation) const; private: - int parseRelativeLuminanceTarget(const ValueNode &tuningData); - int parseConstraint(const ValueNode &modeDict, int32_t id); - int parseConstraintModes(const ValueNode &tuningData); - int parseExposureModes(const ValueNode &tuningData); double estimateInitialGain(const Traits &traits, double yTarget) const; double constraintClampGain(const Params ¶ms, double gain) const; utils::Duration filterExposure(utils::Duration exposureValue); utils::Duration filteredExposure_; mutable bool luxWarningEnabled_; - Pwl relativeLuminanceTarget_; uint64_t frameCount_; std::vector additionalConstraints_; + + Pwl relativeLuminanceTarget_; std::map> constraintModes_; std::map exposureModeHelpers_; };