From patchwork Fri Aug 15 10:29:29 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Stefan Klug X-Patchwork-Id: 24119 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 43F44BDCC1 for ; Fri, 15 Aug 2025 10:30:21 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id E6AA26925A; Fri, 15 Aug 2025 12:30:20 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="H+bVHwiX"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 7DC936924E for ; Fri, 15 Aug 2025 12:30:19 +0200 (CEST) Received: from ideasonboard.com (unknown [IPv6:2a00:6020:448c:6c00:83d1:ae5d:93a6:6837]) by perceval.ideasonboard.com (Postfix) with UTF8SMTPSA id 9873963B; Fri, 15 Aug 2025 12:29:24 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1755253764; bh=bXjqGHkjgMDvK4m6fe5NMGbj4qM2GN3l96WLiQW3eX4=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=H+bVHwiXH/E2YuUygh9DOP6tsKBQ6/FI7HDl+Y2/wTEL3o43bATmxle5VDtwY3OHZ sw/5LHrIh4UZuNQOQdKGcPSaV5elP70bDauP1/SL2PXR4R8Lenrqa/o3HYDO+mNV+X c0SqgedAtUPWQzM7+n+0tBftHmCfifCXbOCnYxCM= From: Stefan Klug To: libcamera-devel@lists.libcamera.org Cc: Stefan Klug Subject: [PATCH v3 09/19] libipa: exposure_mode_helper: Calculate quantization gain in splitExposure() Date: Fri, 15 Aug 2025 12:29:29 +0200 Message-ID: <20250815102945.1602071-10-stefan.klug@ideasonboard.com> X-Mailer: git-send-email 2.48.1 In-Reply-To: <20250815102945.1602071-1-stefan.klug@ideasonboard.com> References: <20250815102945.1602071-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" Calculate the error introduced by quantization as "quantization gain" and return it separately from splitExposure(). It is not included in the digital gain, to not silently ignore the limits imposed by the AGC configuration. Signed-off-by: Stefan Klug Reviewed-by: Daniel Scally --- Changes in v3: - Fixed mali-c55 build - Fixed return of splitExposure in case of gain & exposure time fixed - Calculate quantizationGain for exposure and gain separately and do not feed it into the next stage --- src/ipa/ipu3/algorithms/agc.cpp | 4 +- src/ipa/libipa/agc_mean_luminance.cpp | 7 ++-- src/ipa/libipa/agc_mean_luminance.h | 2 +- src/ipa/libipa/exposure_mode_helper.cpp | 51 +++++++++++++++++-------- src/ipa/libipa/exposure_mode_helper.h | 2 +- src/ipa/mali-c55/algorithms/agc.cpp | 4 +- src/ipa/rkisp1/algorithms/agc.cpp | 9 +++-- 7 files changed, 51 insertions(+), 28 deletions(-) diff --git a/src/ipa/ipu3/algorithms/agc.cpp b/src/ipa/ipu3/algorithms/agc.cpp index 39d0aebb0838..da045640d569 100644 --- a/src/ipa/ipu3/algorithms/agc.cpp +++ b/src/ipa/ipu3/algorithms/agc.cpp @@ -222,8 +222,8 @@ void Agc::process(IPAContext &context, [[maybe_unused]] const uint32_t frame, utils::Duration effectiveExposureValue = exposureTime * analogueGain; utils::Duration newExposureTime; - double aGain, dGain; - std::tie(newExposureTime, aGain, dGain) = + double aGain, qGain, dGain; + std::tie(newExposureTime, aGain, qGain, dGain) = calculateNewEv(context.activeState.agc.constraintMode, context.activeState.agc.exposureMode, hist, effectiveExposureValue); diff --git a/src/ipa/libipa/agc_mean_luminance.cpp b/src/ipa/libipa/agc_mean_luminance.cpp index 1cbb3a0f9818..6c7cf0038b16 100644 --- a/src/ipa/libipa/agc_mean_luminance.cpp +++ b/src/ipa/libipa/agc_mean_luminance.cpp @@ -566,11 +566,12 @@ utils::Duration AgcMeanLuminance::filterExposure(utils::Duration exposureValue) * * Calculate a new exposure value to try to obtain the target. The calculated * exposure value is filtered to prevent rapid changes from frame to frame, and - * divided into exposure time, analogue and digital gain. + * divided into exposure time, analogue, quantization and digital gain. * - * \return Tuple of exposure time, analogue gain, and digital gain + * \return Tuple of exposure time, analogue gain, quantization gain and digital + * gain */ -std::tuple +std::tuple AgcMeanLuminance::calculateNewEv(uint32_t constraintModeIndex, uint32_t exposureModeIndex, const Histogram &yHist, diff --git a/src/ipa/libipa/agc_mean_luminance.h b/src/ipa/libipa/agc_mean_luminance.h index 49985481ac51..fbb526f6ae8e 100644 --- a/src/ipa/libipa/agc_mean_luminance.h +++ b/src/ipa/libipa/agc_mean_luminance.h @@ -68,7 +68,7 @@ public: return controls_; } - std::tuple + std::tuple calculateNewEv(uint32_t constraintModeIndex, uint32_t exposureModeIndex, const Histogram &yHist, utils::Duration effectiveExposureValue); diff --git a/src/ipa/libipa/exposure_mode_helper.cpp b/src/ipa/libipa/exposure_mode_helper.cpp index 21efc4356afa..6c10aa757866 100644 --- a/src/ipa/libipa/exposure_mode_helper.cpp +++ b/src/ipa/libipa/exposure_mode_helper.cpp @@ -178,14 +178,24 @@ double ExposureModeHelper::clampGain(double gain, double *quantizationGain) cons * required exposure, the helper falls-back to simply maximising the exposure * time first, followed by analogue gain, followed by digital gain. * - * \return Tuple of exposure time, analogue gain, and digital gain + * During the calculations the gain missed due to quantization is recorded and + * returned as quantization gain. The quantization gain is not included in the + * digital gain. So to exactly apply the given exposure, both quantization gain + * and digital gain must be applied. + * + * \return Tuple of exposure time, analogue gain, quantization gain and digital + * gain */ -std::tuple +std::tuple ExposureModeHelper::splitExposure(utils::Duration exposure) const { ASSERT(maxExposureTime_); ASSERT(maxGain_); + utils::Duration exposureTime; + double gain; + double quantGain; + double quantGain2; bool gainFixed = minGain_ == maxGain_; bool exposureTimeFixed = minExposureTime_ == maxExposureTime_; @@ -193,16 +203,21 @@ ExposureModeHelper::splitExposure(utils::Duration exposure) const * There's no point entering the loop if we cannot change either gain * nor exposure time anyway. */ - if (exposureTimeFixed && gainFixed) - return { minExposureTime_, minGain_, exposure / (minExposureTime_ * minGain_) }; + if (exposureTimeFixed && gainFixed) { + exposureTime = clampExposureTime(minExposureTime_, &quantGain); + gain = clampGain(minGain_, &quantGain2); + quantGain *= quantGain2; + + return { exposureTime, gain, quantGain, + exposure / (exposureTime * gain * quantGain) }; + } - utils::Duration exposureTime; double stageGain = clampGain(1.0); double lastStageGain = stageGain; - double gain; for (unsigned int stage = 0; stage < gains_.size(); stage++) { - utils::Duration stageExposureTime = clampExposureTime(exposureTimes_[stage]); + utils::Duration stageExposureTime = clampExposureTime(exposureTimes_[stage], + &quantGain); stageGain = clampGain(gains_[stage]); /* @@ -215,18 +230,22 @@ ExposureModeHelper::splitExposure(utils::Duration exposure) const /* Clamp the gain to lastStageGain and regulate exposureTime. */ if (stageExposureTime * lastStageGain >= exposure) { - exposureTime = clampExposureTime(exposure / lastStageGain); - gain = clampGain(exposure / exposureTime); + exposureTime = clampExposureTime(exposure / lastStageGain, &quantGain); + gain = clampGain(exposure / exposureTime, &quantGain2); + quantGain *= quantGain2; - return { exposureTime, gain, exposure / (exposureTime * gain) }; + return { exposureTime, gain, quantGain, + exposure / (exposureTime * gain * quantGain) }; } /* Clamp the exposureTime to stageExposureTime and regulate gain. */ if (stageExposureTime * stageGain >= exposure) { exposureTime = stageExposureTime; - gain = clampGain(exposure / exposureTime); + gain = clampGain(exposure / exposureTime, &quantGain2); + quantGain *= quantGain2; - return { exposureTime, gain, exposure / (exposureTime * gain) }; + return { exposureTime, gain, quantGain, + exposure / (exposureTime * gain * quantGain) }; } lastStageGain = stageGain; @@ -239,10 +258,12 @@ ExposureModeHelper::splitExposure(utils::Duration exposure) const * stages to use then the default stageGain of 1.0 is used so that * exposure time is maxed before gain is touched at all. */ - exposureTime = clampExposureTime(exposure / stageGain); - gain = clampGain(exposure / exposureTime); + exposureTime = clampExposureTime(exposure / stageGain, &quantGain); + gain = clampGain(exposure / exposureTime, &quantGain2); + quantGain *= quantGain2; - return { exposureTime, gain, exposure / (exposureTime * gain) }; + return { exposureTime, gain, quantGain, + exposure / (exposureTime * gain * quantGain) }; } /** diff --git a/src/ipa/libipa/exposure_mode_helper.h b/src/ipa/libipa/exposure_mode_helper.h index ac7e8da95c6c..968192ddc5af 100644 --- a/src/ipa/libipa/exposure_mode_helper.h +++ b/src/ipa/libipa/exposure_mode_helper.h @@ -30,7 +30,7 @@ public: void setLimits(utils::Duration minExposureTime, utils::Duration maxExposureTime, double minGain, double maxGain); - std::tuple + std::tuple splitExposure(utils::Duration exposure) const; utils::Duration minExposureTime() const { return minExposureTime_; } diff --git a/src/ipa/mali-c55/algorithms/agc.cpp b/src/ipa/mali-c55/algorithms/agc.cpp index 15963994b2d6..88f8664c9823 100644 --- a/src/ipa/mali-c55/algorithms/agc.cpp +++ b/src/ipa/mali-c55/algorithms/agc.cpp @@ -381,8 +381,8 @@ void Agc::process(IPAContext &context, utils::Duration effectiveExposureValue = currentShutter * totalGain; utils::Duration shutterTime; - double aGain, dGain; - std::tie(shutterTime, aGain, dGain) = + double aGain, qGain, dGain; + std::tie(shutterTime, aGain, qGain, dGain) = calculateNewEv(activeState.agc.constraintMode, activeState.agc.exposureMode, statistics_.yHist, effectiveExposureValue); diff --git a/src/ipa/rkisp1/algorithms/agc.cpp b/src/ipa/rkisp1/algorithms/agc.cpp index 35440b67e999..0a29326841fb 100644 --- a/src/ipa/rkisp1/algorithms/agc.cpp +++ b/src/ipa/rkisp1/algorithms/agc.cpp @@ -567,15 +567,16 @@ void Agc::process(IPAContext &context, [[maybe_unused]] const uint32_t frame, setExposureCompensation(pow(2.0, frameContext.agc.exposureValue)); utils::Duration newExposureTime; - double aGain, dGain; - std::tie(newExposureTime, aGain, dGain) = + double aGain, qGain, dGain; + std::tie(newExposureTime, aGain, qGain, dGain) = calculateNewEv(frameContext.agc.constraintMode, frameContext.agc.exposureMode, hist, effectiveExposureValue); LOG(RkISP1Agc, Debug) - << "Divided up exposure time, analogue gain and digital gain are " - << newExposureTime << ", " << aGain << " and " << dGain; + << "Divided up exposure time, analogue gain, quantization gain" + << " and digital gain are " << newExposureTime << ", " << aGain + << ", " << qGain << " and " << dGain; IPAActiveState &activeState = context.activeState; /* Update the estimated exposure and gain. */