From patchwork Tue Oct 14 14:24:17 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Stefan Klug X-Patchwork-Id: 24642 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 AC520BE080 for ; Tue, 14 Oct 2025 14:24:39 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 663E360600; Tue, 14 Oct 2025 16:24:39 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="dE/+rTXi"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [IPv6:2001:4b98:dc2:55:216:3eff:fef7:d647]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id A2D51605FD for ; Tue, 14 Oct 2025 16:24:37 +0200 (CEST) Received: from ideasonboard.com (unknown [IPv6:2a00:6020:448c:6c00:61fb:8e55:ff1e:be62]) by perceval.ideasonboard.com (Postfix) with UTF8SMTPSA id 49049E70; Tue, 14 Oct 2025 16:22:59 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1760451779; bh=iuCCQkeQaqENPADjKbkX3uw3EpK81ZnZ2UFwEBdPKT0=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=dE/+rTXijL5enoWqorZssd6jxV2rMJpyvsezJZZRWRppcPnNZnq5rLlVHKZjIp61X pa2zaNZukiDvqYukdGRJkTr/pyCEkgs5jEFB4PU+l49ULHpvB6JCckv6Ga9d0Wztk+ e6ygo9W6fojNZTRNNbv9hgEPM67Bp/a2sBy3FZLI= From: Stefan Klug To: libcamera-devel@lists.libcamera.org Cc: Stefan Klug , Paul Elder Subject: [PATCH v1 2/4] ipa: libipa: agc_mean_luminance: Change luminance target to piecewise linear function Date: Tue, 14 Oct 2025 16:24:17 +0200 Message-ID: <20251014142427.3107490-3-stefan.klug@ideasonboard.com> X-Mailer: git-send-email 2.48.1 In-Reply-To: <20251014142427.3107490-1-stefan.klug@ideasonboard.com> References: <20251014142427.3107490-1-stefan.klug@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" In some situations it is necessary to specify the target brightness value depending on the overall lux level. This is a rework [1] to fit current master. For backwards compatibility of the tuning files and easier tuning file handling it is still allowed to specify the luminance target as plain value. [1] https://patchwork.libcamera.org/patch/20231/ Signed-off-by: Paul Elder Signed-off-by: Stefan Klug --- src/ipa/libipa/agc_mean_luminance.cpp | 59 ++++++++++++++++++++++++--- src/ipa/libipa/agc_mean_luminance.h | 11 ++++- src/ipa/rkisp1/algorithms/agc.cpp | 1 + 3 files changed, 63 insertions(+), 8 deletions(-) diff --git a/src/ipa/libipa/agc_mean_luminance.cpp b/src/ipa/libipa/agc_mean_luminance.cpp index 64f36bd75dd2..62b1918a45a7 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 @@ -145,16 +153,32 @@ static constexpr double kMaxRelativeLuminanceTarget = 0.95; AgcMeanLuminance::AgcMeanLuminance() : exposureCompensation_(1.0), frameCount_(0), filteredExposure_(0s), - relativeLuminanceTarget_(0) + lux_(0) { } AgcMeanLuminance::~AgcMeanLuminance() = default; -void AgcMeanLuminance::parseRelativeLuminanceTarget(const YamlObject &tuningData) +int AgcMeanLuminance::parseRelativeLuminanceTarget(const YamlObject &tuningData) { - relativeLuminanceTarget_ = - tuningData["relativeLuminanceTarget"].get(kDefaultRelativeLuminanceTarget); + relativeLuminanceTarget_.clear(); + + auto &target = tuningData["relativeLuminanceTarget"]; + if (target.isValue()) { + double t = target.get(kDefaultRelativeLuminanceTarget); + relativeLuminanceTarget_.append(0, t); + return 0; + } + + std::optional pwl = target.get(); + if (!pwl) { + LOG(AgcMeanLuminance, Error) + << "Failed to load relative luminance target."; + return -EINVAL; + } + + relativeLuminanceTarget_.swap(*pwl); + return 0; } void AgcMeanLuminance::parseConstraint(const YamlObject &modeDict, int32_t id) @@ -385,7 +409,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 +429,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 +574,18 @@ double AgcMeanLuminance::constraintClampGain(uint32_t constraintModeIndex, */ double AgcMeanLuminance::effectiveYTarget() const { - return std::min(relativeLuminanceTarget_ * exposureCompensation_, + double lux = lux_; + if (relativeLuminanceTarget_.size() > 1 && lux_ == 0) { + LOG(AgcMeanLuminance, Debug) + << "Missing lux value for luminance target calculation, default to " + << kDefaultLuxLevel; + 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 d7ec548e3e58..1fbff304a72b 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); @@ -81,8 +87,8 @@ public: private: virtual double estimateLuminance(const double gain) const = 0; + int parseRelativeLuminanceTarget(const YamlObject &tuningData); - void parseRelativeLuminanceTarget(const YamlObject &tuningData); void parseConstraint(const YamlObject &modeDict, int32_t id); int parseConstraintModes(const YamlObject &tuningData); int parseExposureModes(const YamlObject &tuningData); @@ -95,7 +101,8 @@ private: double exposureCompensation_; uint64_t frameCount_; utils::Duration filteredExposure_; - double relativeLuminanceTarget_; + unsigned int lux_; + Pwl relativeLuminanceTarget_; std::vector additionalConstraints_; std::map> constraintModes_; diff --git a/src/ipa/rkisp1/algorithms/agc.cpp b/src/ipa/rkisp1/algorithms/agc.cpp index f5a3c917cb69..1ecaff680978 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;