From patchwork Fri Nov 14 14:17:09 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jacopo Mondi X-Patchwork-Id: 25066 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 94173C3334 for ; Fri, 14 Nov 2025 14:17:41 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id E30B660B33; Fri, 14 Nov 2025 15:17:39 +0100 (CET) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="g5sIHvyG"; 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 37D1F60AB3 for ; Fri, 14 Nov 2025 15:17:23 +0100 (CET) Received: from [192.168.1.101] (93-61-96-190.ip145.fastwebnet.it [93.61.96.190]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 3802D1198; Fri, 14 Nov 2025 15:15:22 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1763129722; bh=PVnj5hhNX5FryIopipaQwwhWxXo05VGoLhKTcRrE8uc=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=g5sIHvyGF+nH61/QwlRWPPNA4W3B7Rjszm5HDfIhkCGuo9JMId+RA8+gfBG2cu8/O Pjr1AJGX2koaRbTvKaqc5DAGmvHII+lYSVXowBCKSzyvj7vK7rOlNThDkrGOD/sGj+ VWvs2TTPkHjM/anN9BJTuaLo2J1/gHCGRB9hD4/E= From: Jacopo Mondi Date: Fri, 14 Nov 2025 15:17:09 +0100 Subject: [PATCH v3 14/19] ipa: libipa: agc: Rework setLimits() interface MIME-Version: 1.0 Message-Id: <20251114-exposure-limits-v3-14-b7c07feba026@ideasonboard.com> References: <20251114-exposure-limits-v3-0-b7c07feba026@ideasonboard.com> In-Reply-To: <20251114-exposure-limits-v3-0-b7c07feba026@ideasonboard.com> To: =?utf-8?q?Niklas_S=C3=B6derlund?= , Robert Mader , libcamera-devel@lists.libcamera.org Cc: Jacopo Mondi X-Mailer: b4 0.14.2 X-Developer-Signature: v=1; a=openpgp-sha256; l=13940; i=jacopo.mondi@ideasonboard.com; h=from:subject:message-id; bh=PVnj5hhNX5FryIopipaQwwhWxXo05VGoLhKTcRrE8uc=; b=owEBbQKS/ZANAwAKAXI0Bo8WoVY8AcsmYgBpFznsDJM/vjrRewm8vNXi1+aVvf5UPh6YhJv3A YE9/cE4jAyJAjMEAAEKAB0WIQS1xD1IgJogio9YOMByNAaPFqFWPAUCaRc57AAKCRByNAaPFqFW PMJeD/91/O9r9/D22O5n4dI9m8+ONe/DVMKZMS4EJ33nz9QeVJ2v4ggIW7Q3ORbJMvYAxVs+1yQ oe4QtNXapXz3Lxx0WYaQVzTej9dFZ/TpjJ5v2lil/QynsbIQ7JyB1oZwKz+G7F9aFAB3ya4mr6V hs+CAKnxgPLKTj20VOXf1WH/RAVilNkFfvofvJvauQrjV7L3vDss2dywqb7LGpYDtnI5Db94VfO 2PQqMkP/0cgSEGi/uPriYNRqU68GWEJ/4oIfhAexitwyDoSQOX4a4k271SbQB5lx7+rZrNRSjUk FLMcWEMDkZ/QcfAU6vpF/ZnlWg8sQxBvu+x5TjPTSi2wHe01Ff8oXtfxnzOanrIuF/WfY1JwI/b KuWo31TQyMOjpyD7IQuXB7XsIytvja7Vue6Mx/vybT8XlXx7Agcuf1bVm508c742XFyhz/xeJqJ zWsOwQ+Nm6QlqLgaREkrwkn3uy3mJHoHaXsoMElPdCZsqk1yy6FU74ZIls473muBt2hK75PiXHz 1vv7QEn13NGlSCZEvKwBHev7aUG2ihozF5D6lLwnWg3R0/7IzyIq69MZjidVph5Hcda8ZqCjUdx Di6qdGtmkB+J7qcavUxnULYpCHPIeXpNlaoq0uRtLZjvwOQnu+dKAva42oXYZvon8J3M9rLwS/L jBCkYU9cD/vX+tw== X-Developer-Key: i=jacopo.mondi@ideasonboard.com; a=openpgp; fpr=72392EDC88144A65C701EA9BA5826A2587AD026B 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 setLimits() interface accepts a min/max exposure and gains and detect a request for a fixed value if min == max. For the shutter time, the concept of max exposure is ill-formed as the maximum achievable exposure is determinate by the frame duration, while the min frame duration is a sensor's default parameter. For gains instead either we want a fixed value or it can range in the sensor's min and max values. As we now store the sensor's default in the exposure mode helper, rework the interface of setLimits() both in AgcMeanLuminance and ExposureModeHelper so that it receives - an optional fixed shutter time - an optional fixed gain - the current maximum frame duration If the optional fixed values are populated they are used to fix either the shutter time or the exposure. If they are not set instead the algorithm is free to range in the: - shutterTime = [minExposure, frameDuration - margin] - gain = [minGain, maxGain] Signed-off-by: Jacopo Mondi --- src/ipa/libipa/agc_mean_luminance.cpp | 30 ++++++------- src/ipa/libipa/agc_mean_luminance.h | 7 +-- src/ipa/libipa/exposure_mode_helper.cpp | 79 +++++++++++++++++---------------- src/ipa/libipa/exposure_mode_helper.h | 13 +++--- src/ipa/rkisp1/algorithms/agc.cpp | 29 ++++-------- 5 files changed, 75 insertions(+), 83 deletions(-) diff --git a/src/ipa/libipa/agc_mean_luminance.cpp b/src/ipa/libipa/agc_mean_luminance.cpp index 0c8e15030c377ac0797dbdc9d53694ee894cd9b8..9fc275ea9e5b81ce107eabe1982be3c44c01479c 100644 --- a/src/ipa/libipa/agc_mean_luminance.cpp +++ b/src/ipa/libipa/agc_mean_luminance.cpp @@ -9,6 +9,7 @@ #include #include +#include #include #include @@ -171,11 +172,10 @@ static constexpr double kMaxRelativeLuminanceTarget = 0.95; * IPA modules that want to use this class to implement their AEGC algorithm * should derive it and provide an overriding estimateLuminance() function for * this class to use. They must call parseTuningData() in init(), and must also - * call setLimits() and resetFrameCounter() 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. + * call resetFrameCounter() in configure(). They may then use calculateNewEv() + * in process(). To update the algorithm limits for example, in response to a + * FrameDurationLimit control being passed in queueRequest()) then + * setExposureLimits() must be called with the new values. */ AgcMeanLuminance::AgcMeanLuminance() @@ -459,25 +459,21 @@ int AgcMeanLuminance::parseTuningData(const YamlObject &tuningData) /** * \brief Set the ExposureModeHelper limits for this class - * \param[in] minExposureTime Minimum exposure time to allow - * \param[in] maxExposureTime Maximum ewposure time to allow + * \param[in] shutterTime The (optional) fixed shutter time + * \param[in] gain The (optional) analogue gain value * \param[in] maxFrameDuration Maximum frame duration - * \param[in] minGain Minimum gain to allow - * \param[in] maxGain Maximum gain to allow * \param[in] constraints Additional constraints to apply * - * This function calls \ref ExposureModeHelper::setLimits() for each + * This function calls \ref ExposureModeHelper::setExposureLimits() for each * ExposureModeHelper that has been created for this class. */ -void AgcMeanLuminance::setLimits(utils::Duration minExposureTime, - utils::Duration maxExposureTime, - utils::Duration maxFrameDuration, - double minGain, double maxGain, - std::vector constraints) +void AgcMeanLuminance::setExposureLimits(std::optional shutterTime, + std::optional gain, + utils::Duration maxFrameDuration, + std::vector constraints) { for (auto &[id, helper] : exposureModeHelpers_) - helper->setLimits(minExposureTime, maxExposureTime, maxFrameDuration, - minGain, maxGain); + helper->setExposureLimits(shutterTime, gain, maxFrameDuration); additionalConstraints_ = std::move(constraints); } diff --git a/src/ipa/libipa/agc_mean_luminance.h b/src/ipa/libipa/agc_mean_luminance.h index 535f94502dc2649a5f4ba49a7040de12f9f74179..12316ca8bbd7d8b5783a948f5e01d5f0f56bfe3a 100644 --- a/src/ipa/libipa/agc_mean_luminance.h +++ b/src/ipa/libipa/agc_mean_luminance.h @@ -61,9 +61,10 @@ public: exposureCompensation_ = gain; } - void setLimits(utils::Duration minExposureTime, utils::Duration maxExposureTime, - utils::Duration maxFrameDuration, double minGain, double maxGain, - std::vector constraints); + void setExposureLimits(std::optional shutterTime, + std::optional gain, + utils::Duration maxFrameDuration, + std::vector constraints); const std::map> &constraintModes() const { diff --git a/src/ipa/libipa/exposure_mode_helper.cpp b/src/ipa/libipa/exposure_mode_helper.cpp index fad13ff1521498244224a8a5f375738ee3fc9ff2..e1e36eb1820d4080f2dc295a963a37782a484f02 100644 --- a/src/ipa/libipa/exposure_mode_helper.cpp +++ b/src/ipa/libipa/exposure_mode_helper.cpp @@ -109,25 +109,33 @@ ExposureModeHelper::ExposureModeHelper(const Span shutterTime, + utils::Duration maxFrameDuration) { - minExposureTime_ = minExposureTime; - /* - * Compute the maximum shutter time. - * - * If maxExposureTime is equal to minExposureTime then we use them - * to fix the exposure time. + * If shutterTime is populated we use it to to fix the shutter time. * - * Otherwise, if the exposure can range between a min and max delegate - * the maximum shutter time calculation to the sensor helper. + * Otherwise, if the exposure is not fixed delegate the maximum shutter + * time calculation to the sensor helper and restore the min exposure + * time to the sensor's default value. */ - maxExposureTime_ = minExposureTime != maxExposureTime - ? sensorHelper_->maxShutterTime(maxFrameDuration, - sensor_.lineDuration_) - : minExposureTime; + + maxExposureTime_ = shutterTime.has_value() + ? shutterTime.value() + : sensorHelper_->maxShutterTime(maxFrameDuration, + sensor_.lineDuration_); + minExposureTime_ = shutterTime.has_value() + ? shutterTime.value() : sensor_.minExposureTime_; +} + +void ExposureModeHelper::setGainLimits(std::optional gain) +{ + /* + * Use the fixed value as limits if populated, otherwise use the + * sensor's default ones. + */ + minGain_ = gain.has_value() ? gain.value() : sensor_.minGain_; + maxGain_ = gain.has_value() ? gain.value() : sensor_.maxGain_; } /** @@ -154,42 +162,37 @@ void ExposureModeHelper::configure(const SensorConfiguration &sensorConfig, sensorHelper_ = sensorHelper; /* Initialize run-time limits with sensor's default. */ - minGain_ = sensor_.minGain_; - maxGain_ = sensor_.maxGain_; - - setMaxExposure(sensorConfig.minExposureTime_, sensorConfig.maxExposureTime_, - sensorConfig.maxFrameDuration_); + setShutterLimits({}, sensorConfig.maxFrameDuration_); + setGainLimits({}); } /** * \brief Set the exposure time and gain limits - * \param[in] minExposureTime The minimum exposure time supported - * \param[in] maxExposureTime The maximum exposure time supported + * \param[in] shutterTime The (optional) fixed shutter time + * \param[in] gain The (optional) fixed gain * \param[in] maxFrameDuration The maximum frame duration - * \param[in] minGain The minimum analogue gain supported - * \param[in] maxGain The maximum analogue gain supported * - * This function configures the exposure time and analogue gain limits that need + * This function configures the shutter time and analogue gain limits that need * to be adhered to as the helper divides up exposure. Note that this function * *must* be called whenever those limits change and before splitExposure() is * used. * - * If the algorithm using the helpers needs to indicate that either exposure time - * or analogue gain or both should be fixed it can do so by setting both the - * minima and maxima to the same value. + * If the algorithm using the helpers needs to indicate that the shutter time + * should be fixed it should populate the optional \a shutterTime argument. + * If the shutter time is not fixed, the maximum achievable shutter time is + * calculated using \a maxFrameDuration as the upper bound while the minimum + * exposure time is reset to the sensor's default. * - * The exposure time limits are calculated using \a maxFrameDuration as the - * upper bound, the \a maxExposureTime paramter effectivelly only serves - * to indicate that the caller wants a fixed exposure value. + * If the analogue gain should be fixed the optional \a gain argument should + * be populated. If the analogue gain is not fixed its min and max values are + * reset to the sensor's default. */ -void ExposureModeHelper::setLimits(utils::Duration minExposureTime, - utils::Duration maxExposureTime, - utils::Duration maxFrameDuration, - double minGain, double maxGain) +void ExposureModeHelper::setExposureLimits(std::optional shutterTime, + std::optional gain, + utils::Duration maxFrameDuration) { - minGain_ = minGain; - maxGain_ = maxGain; - setMaxExposure(minExposureTime, maxExposureTime, maxFrameDuration); + setShutterLimits(shutterTime, maxFrameDuration); + setGainLimits(gain); } utils::Duration ExposureModeHelper::clampExposureTime(utils::Duration exposureTime, diff --git a/src/ipa/libipa/exposure_mode_helper.h b/src/ipa/libipa/exposure_mode_helper.h index 36791c99face056835b0bb2d28b533380e8d9b95..8831ed0751c75d60b61b608c2b0cccfaf1d59726 100644 --- a/src/ipa/libipa/exposure_mode_helper.h +++ b/src/ipa/libipa/exposure_mode_helper.h @@ -7,6 +7,7 @@ #pragma once +#include #include #include #include @@ -40,16 +41,18 @@ public: void configure(const SensorConfiguration &sensorConfig, const CameraSensorHelper *sensorHelper); - void setLimits(utils::Duration minExposureTime, utils::Duration maxExposureTime, - utils::Duration maxFrameDuration, double minGain, double maxGain); + void setExposureLimits(std::optional shutterTime, + std::optional gain, + utils::Duration maxFrameDuration); std::tuple splitExposure(utils::Duration exposure) const; private: - void setMaxExposure(utils::Duration minExposureTime, - utils::Duration maxExposureTime, - utils::Duration maxFrameDuration); + void setShutterLimits(std::optional shutterTime, + utils::Duration maxFrameDuration); + void setGainLimits(std::optional gain); + utils::Duration clampExposureTime(utils::Duration exposureTime, double *quantizationGain = nullptr) const; double clampGain(double gain, double *quantizationGain = nullptr) const; diff --git a/src/ipa/rkisp1/algorithms/agc.cpp b/src/ipa/rkisp1/algorithms/agc.cpp index db318a2c49f2fbd9b00222ec699a657eed131595..c8210e175186a282faf586378c5a0a761612047c 100644 --- a/src/ipa/rkisp1/algorithms/agc.cpp +++ b/src/ipa/rkisp1/algorithms/agc.cpp @@ -573,34 +573,23 @@ void Agc::process(IPAContext &context, [[maybe_unused]] const uint32_t frame, * Set the AGC limits using the fixed exposure time and/or gain in * manual mode, or the sensor limits in auto mode. */ - utils::Duration minExposureTime; - utils::Duration maxExposureTime; - double minAnalogueGain; - double maxAnalogueGain; + std::optional shutterTime; + std::optional gain; - if (frameContext.agc.autoExposureEnabled) { - minExposureTime = context.configuration.sensor.minExposureTime; - maxExposureTime = context.configuration.sensor.maxExposureTime; - } else { - minExposureTime = context.configuration.sensor.lineDuration - * frameContext.agc.exposure; - maxExposureTime = minExposureTime; + if (!frameContext.agc.autoExposureEnabled) { + shutterTime = context.configuration.sensor.lineDuration + * frameContext.agc.exposure; } - if (frameContext.agc.autoGainEnabled) { - minAnalogueGain = context.configuration.sensor.minAnalogueGain; - maxAnalogueGain = context.configuration.sensor.maxAnalogueGain; - } else { - minAnalogueGain = frameContext.agc.gain; - maxAnalogueGain = frameContext.agc.gain; - } + if (!frameContext.agc.autoGainEnabled) + gain = frameContext.agc.gain; std::vector additionalConstraints; if (context.activeState.wdr.mode != controls::WdrOff) additionalConstraints.push_back(context.activeState.wdr.constraint); - setLimits(minExposureTime, maxExposureTime, frameContext.agc.maxFrameDuration, - minAnalogueGain, maxAnalogueGain, std::move(additionalConstraints)); + setExposureLimits(shutterTime, gain, frameContext.agc.maxFrameDuration, + std::move(additionalConstraints)); /* * The Agc algorithm needs to know the effective exposure value that was