From patchwork Tue Oct 14 14:24:16 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Stefan Klug X-Patchwork-Id: 24641 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 0A997BE080 for ; Tue, 14 Oct 2025 14:24:36 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id ABA3C60605; Tue, 14 Oct 2025 16:24:36 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="SQ20sqSw"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id BA1D8605F3 for ; Tue, 14 Oct 2025 16:24:34 +0200 (CEST) Received: from ideasonboard.com (unknown [IPv6:2a00:6020:448c:6c00:61fb:8e55:ff1e:be62]) by perceval.ideasonboard.com (Postfix) with UTF8SMTPSA id 5B347741; Tue, 14 Oct 2025 16:22:56 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1760451776; bh=lYJKih6OAxqqpu1s/AsK4pIrkBG5o2wRtCxuXW32Ejw=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=SQ20sqSwre/d5vg4oHaivFCgx6dncoOZrAljMgHmxx4dKSqEzUb2y5iZ2wHm/b2Tf nIXGhb0gaocGaYH4J9ZqnklQT51qHNQepBaFbWKwjmPcQJYQNYlK6Idi4zYIrYRjKI nOSWISJ3peav5cHArBovAdbc8QKAOBAXGxmDSpJU= From: Stefan Klug To: libcamera-devel@lists.libcamera.org Cc: Stefan Klug Subject: [PATCH v1 1/4] ipa: libipa: pwl: Add swap() function Date: Tue, 14 Oct 2025 16:24:16 +0200 Message-ID: <20251014142427.3107490-2-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" Add a swap() function to easily swap the contents of two PWLs. Signed-off-by: Stefan Klug Reviewed-by: Barnabás Pőcze --- src/ipa/libipa/pwl.cpp | 6 ++++++ src/ipa/libipa/pwl.h | 1 + 2 files changed, 7 insertions(+) diff --git a/src/ipa/libipa/pwl.cpp b/src/ipa/libipa/pwl.cpp index 69a9334112e8..1858ab37b101 100644 --- a/src/ipa/libipa/pwl.cpp +++ b/src/ipa/libipa/pwl.cpp @@ -169,6 +169,12 @@ void Pwl::prepend(double x, double y, const double eps) * \return The number of points in the piecewise linear function */ +/** + * \fn Pwl::swap(Pwl &other) + * \brief Swap the contents with another PWL + * \param[in] other The PWL to swap with + */ + /** * \brief Get the domain of the piecewise linear function * \return An interval representing the domain diff --git a/src/ipa/libipa/pwl.h b/src/ipa/libipa/pwl.h index c1496c300ee0..add20b5867af 100644 --- a/src/ipa/libipa/pwl.h +++ b/src/ipa/libipa/pwl.h @@ -51,6 +51,7 @@ public: bool empty() const { return points_.empty(); } void clear() { points_.clear(); } size_t size() const { return points_.size(); } + void swap(Pwl &other) { points_.swap(other.points_); } Interval domain() const; Interval range() const; 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; From patchwork Tue Oct 14 14:24:18 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Stefan Klug X-Patchwork-Id: 24643 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 5C476BE080 for ; Tue, 14 Oct 2025 14:24:42 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 0EA9060605; Tue, 14 Oct 2025 16:24:42 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="X9MSVqqc"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 5101660609 for ; Tue, 14 Oct 2025 16:24:40 +0200 (CEST) Received: from ideasonboard.com (unknown [IPv6:2a00:6020:448c:6c00:61fb:8e55:ff1e:be62]) by perceval.ideasonboard.com (Postfix) with UTF8SMTPSA id D2241104D; Tue, 14 Oct 2025 16:23:01 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1760451781; bh=lgIAzDG0NRsV3A0ed7UTU2hFjwNgg3Gr1KofJ+C/whw=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=X9MSVqqcsPcPGiniOpk5MpIu2P0/tSVuwamwRl2CvVUxaZUKzyqHButqBhjw4rBQP 201v7tZyTKgRI4HlSF9TkFABCsHr0otHqjFp/xLemIZtI8MjiWtTWuhDrj6TnLF/u+ 1aIXNwSJaV7i9fjdUwiPmIGPr89EIzxND3tUcZow= From: Stefan Klug To: libcamera-devel@lists.libcamera.org Cc: Stefan Klug Subject: [PATCH v1 3/4] ipa: libipa: pwl: Add construction from initializer_list Date: Tue, 14 Oct 2025 16:24:18 +0200 Message-ID: <20251014142427.3107490-4-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 the tuning files it is possible to construct the PWL from a linear list of numbers. Add a initializer_list based constructor to replicate similar style in C++ code. An initializer_list is preferred to a Span based constructor because the Span makes the static construction cumbersome ( {{{ 1.0, 2.0 }}} vs { 1.0, 2.0 } ) and initialization is the only use case at the moment. Signed-off-by: Stefan Klug --- src/ipa/libipa/pwl.cpp | 19 +++++++++++++++++++ src/ipa/libipa/pwl.h | 1 + 2 files changed, 20 insertions(+) diff --git a/src/ipa/libipa/pwl.cpp b/src/ipa/libipa/pwl.cpp index 1858ab37b101..501ac79ea8b5 100644 --- a/src/ipa/libipa/pwl.cpp +++ b/src/ipa/libipa/pwl.cpp @@ -122,6 +122,25 @@ Pwl::Pwl(std::vector &&points) { } +/** + * \brief Construct a piecewise linear function from a initializer list + * \param[in] data initializer list of doubles + * + * This constructor takes a even number of doubles and treats them as + * x0,y0,x1,y1,...,xn,yn coordinates for the PWL. + */ +Pwl::Pwl(std::initializer_list data) +{ + ASSERT((data.size() % 2) == 0); + + auto iter = data.begin(); + while (iter != data.end()) { + double x = *iter++; + double y = *iter++; + append(x, y); + } +} + /** * \brief Append a point to the end of the piecewise linear function * \param[in] x x-coordinate of the point to add to the piecewise linear function diff --git a/src/ipa/libipa/pwl.h b/src/ipa/libipa/pwl.h index add20b5867af..b82b17317a5e 100644 --- a/src/ipa/libipa/pwl.h +++ b/src/ipa/libipa/pwl.h @@ -45,6 +45,7 @@ public: Pwl(); Pwl(const std::vector &points); Pwl(std::vector &&points); + Pwl(std::initializer_list data); void append(double x, double y, double eps = 1e-6); From patchwork Tue Oct 14 14:24:19 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Stefan Klug X-Patchwork-Id: 24644 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 D5AEFBE080 for ; Tue, 14 Oct 2025 14:24:44 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 8ACAA60609; Tue, 14 Oct 2025 16:24:44 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="rTvwV+8q"; 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 434A2605FE for ; Tue, 14 Oct 2025 16:24:43 +0200 (CEST) Received: from ideasonboard.com (unknown [IPv6:2a00:6020:448c:6c00:61fb:8e55:ff1e:be62]) by perceval.ideasonboard.com (Postfix) with UTF8SMTPSA id B54B611DA; Tue, 14 Oct 2025 16:23:04 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1760451784; bh=YOPnK+KnhPDEHq3fPF5AcMw2cIJr38dzYQjRfsgPQus=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=rTvwV+8qd9j2VND1E8pqzQ0F8kOEDFAUg2WjqyyhHu1NkDzdKZWw+lWbJFsvzxZ4Q TWP6We6vznEFUXHMqAhgPMncZ1V3F0yef7+TZDfey+aIsgNn/aLqC+hMearaIqKcq9 K89HVRQdt/IE2NQbUDM443sHiMc+taLJ7Q3P8EIc= From: Stefan Klug To: libcamera-devel@lists.libcamera.org Cc: Stefan Klug Subject: [PATCH v1 4/4] ipa: libipa: agc_mean_luminance: Fix yTarget handling in constraints Date: Tue, 14 Oct 2025 16:24:19 +0200 Message-ID: <20251014142427.3107490-5-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" The yTarget loading code is broken and works neither for plain values nor for arrays of values to form a PWL. Fix this by supporting both cases. If a list is provided in the tuning file construct a PWL, otherwise construct a single point PWL with the given value. Fixes: 24247a12c7d3 ("ipa: libipa: Add AgcMeanLuminance base class") Signed-off-by: Stefan Klug --- src/ipa/libipa/agc_mean_luminance.cpp | 54 +++++++++++++++++++++------ src/ipa/libipa/agc_mean_luminance.h | 5 +-- src/ipa/rkisp1/algorithms/wdr.cpp | 3 +- 3 files changed, 46 insertions(+), 16 deletions(-) diff --git a/src/ipa/libipa/agc_mean_luminance.cpp b/src/ipa/libipa/agc_mean_luminance.cpp index 62b1918a45a7..551f6515e849 100644 --- a/src/ipa/libipa/agc_mean_luminance.cpp +++ b/src/ipa/libipa/agc_mean_luminance.cpp @@ -181,7 +181,7 @@ int AgcMeanLuminance::parseRelativeLuminanceTarget(const YamlObject &tuningData) return 0; } -void AgcMeanLuminance::parseConstraint(const YamlObject &modeDict, int32_t id) +int AgcMeanLuminance::parseConstraint(const YamlObject &modeDict, int32_t id) { for (const auto &[boundName, content] : modeDict.asDict()) { if (boundName != "upper" && boundName != "lower") { @@ -194,10 +194,27 @@ void AgcMeanLuminance::parseConstraint(const YamlObject &modeDict, int32_t id) AgcConstraint::Bound bound = static_cast(idx); double qLo = content["qLo"].get().value_or(0.98); double qHi = content["qHi"].get().value_or(1.0); - double yTarget = - content["yTarget"].getList().value_or(std::vector{ 0.5 }).at(0); + auto &target = content["yTarget"]; + Pwl yTarget; + if (target.isValue()) { + auto v = target.get(); + if (!v) { + LOG(AgcMeanLuminance, Error) + << "Failed to parse single value yTarget"; + return -EINVAL; + } + yTarget.append(0, *v); + } else { + auto v = target.get(); + if (!v) { + LOG(AgcMeanLuminance, Error) + << "Failed to parse PWL based yTarget"; + return -EINVAL; + } + yTarget.swap(*v); + } - AgcConstraint constraint = { bound, qLo, qHi, yTarget }; + AgcConstraint constraint = { bound, qLo, qHi, std::move(yTarget) }; if (!constraintModes_.count(id)) constraintModes_[id] = {}; @@ -207,6 +224,8 @@ void AgcMeanLuminance::parseConstraint(const YamlObject &modeDict, int32_t id) else constraintModes_[id].insert(constraintModes_[id].begin(), constraint); } + + return 0; } int AgcMeanLuminance::parseConstraintModes(const YamlObject &tuningData) @@ -229,8 +248,11 @@ int AgcMeanLuminance::parseConstraintModes(const YamlObject &tuningData) return -EINVAL; } - parseConstraint(modeDict, - AeConstraintModeNameValueMap.at(modeName)); + int ret = parseConstraint(modeDict, + AeConstraintModeNameValueMap.at(modeName)); + if (ret) + return ret; + availableConstraintModes.push_back( AeConstraintModeNameValueMap.at(modeName)); } @@ -247,7 +269,7 @@ int AgcMeanLuminance::parseConstraintModes(const YamlObject &tuningData) AgcConstraint::Bound::Lower, 0.98, 1.0, - 0.5 + { 0.0, 0.5 } }; constraintModes_[controls::ConstraintNormal].insert( @@ -360,8 +382,9 @@ void AgcMeanLuminance::configure(utils::Duration lineDuration, * the data in a specific format; the Agc algorithm's tuning data should contain * a dictionary called AeConstraintMode containing per-mode setting dictionaries * with the key being a value from \ref controls::AeConstraintModeNameValueMap. - * Each mode dict may contain either a "lower" or "upper" key or both, for - * example: + * The yTarget can either be provided as single value or as array in which case + * it is interpreted as a PWL mapping lux levels to yTarget values. Each mode + * dict may contain either a "lower" or "upper" key or both, for example: * * \code{.unparsed} * algorithms: @@ -380,7 +403,7 @@ void AgcMeanLuminance::configure(utils::Duration lineDuration, * upper: * qLo: 0.98 * qHi: 1.0 - * yTarget: 0.8 + * yTarget: [ 100, 0.8, 20000, 0.5 ] * * \endcode * @@ -536,8 +559,15 @@ double AgcMeanLuminance::constraintClampGain(uint32_t constraintModeIndex, const Histogram &hist, double gain) { - auto applyConstraint = [&gain, &hist](const AgcConstraint &constraint) { - double newGain = constraint.yTarget * hist.bins() / + auto applyConstraint = [this, &gain, &hist](const AgcConstraint &constraint) { + double lux = lux_; + + if (relativeLuminanceTarget_.size() > 1 && lux_ == 0) + lux = kDefaultLuxLevel; + + double target = constraint.yTarget.eval( + constraint.yTarget.domain().clamp(lux)); + double newGain = target * hist.bins() / hist.interQuantileMean(constraint.qLo, constraint.qHi); if (constraint.bound == AgcConstraint::Bound::Lower && diff --git a/src/ipa/libipa/agc_mean_luminance.h b/src/ipa/libipa/agc_mean_luminance.h index 1fbff304a72b..d0d1fc139509 100644 --- a/src/ipa/libipa/agc_mean_luminance.h +++ b/src/ipa/libipa/agc_mean_luminance.h @@ -40,7 +40,7 @@ public: Bound bound; double qLo; double qHi; - double yTarget; + Pwl yTarget; }; void configure(utils::Duration lineDuration, const CameraSensorHelper *sensorHelper); @@ -88,8 +88,7 @@ public: private: virtual double estimateLuminance(const double gain) const = 0; int parseRelativeLuminanceTarget(const YamlObject &tuningData); - - void parseConstraint(const YamlObject &modeDict, int32_t id); + int parseConstraint(const YamlObject &modeDict, int32_t id); int parseConstraintModes(const YamlObject &tuningData); int parseExposureModes(const YamlObject &tuningData); double estimateInitialGain() const; diff --git a/src/ipa/rkisp1/algorithms/wdr.cpp b/src/ipa/rkisp1/algorithms/wdr.cpp index 45144913dcd8..ed81628c032c 100644 --- a/src/ipa/rkisp1/algorithms/wdr.cpp +++ b/src/ipa/rkisp1/algorithms/wdr.cpp @@ -175,7 +175,8 @@ int WideDynamicRange::configure(IPAContext &context, constraint.bound = AgcMeanLuminance::AgcConstraint::Bound::Upper; constraint.qHi = 1.0; constraint.qLo = 1.0 - exposureConstraintMaxBrightPixels_; - constraint.yTarget = exposureConstraintY_; + constraint.yTarget.clear(); + constraint.yTarget.append(0, exposureConstraintY_); return 0; }