From patchwork Fri Sep 19 09:40:25 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Stefan Klug X-Patchwork-Id: 24419 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 03B63C328C for ; Fri, 19 Sep 2025 09:41:25 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 685856B5B9; Fri, 19 Sep 2025 11:41:25 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="m8HgF/r0"; 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 962856B5A9 for ; Fri, 19 Sep 2025 11:41:22 +0200 (CEST) Received: from ideasonboard.com (unknown [IPv6:2a00:6020:448c:6c00:4d54:eab8:98ca:163b]) by perceval.ideasonboard.com (Postfix) with UTF8SMTPSA id 6598E842; Fri, 19 Sep 2025 11:40:02 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1758274802; bh=8NrnZ1Svug0IEpVFOG2l9atAlmoOVvrNZc39ea8krp4=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=m8HgF/r02W8YnKsziYOK9UkKBJAEd9mdL7su4BKnu6PRuKx/AHQgvT/L5+xJ62x55 p7aXM6bVuQI4LT3JAqsPNQ/fAb/HB4DvJh+tUeoUpIS2bPKlT6Nw03toS/MxJI0wQz 1lmsfaVLpx85TkUPaxfIggRGfyrgbhqGA4YYYORU= From: Stefan Klug To: libcamera-devel@lists.libcamera.org Cc: Stefan Klug , Kieran Bingham , Daniel Scally , Paul Elder Subject: [PATCH v5 10/19] ipa: rkisp1: agc: Add correction for exposure quantization Date: Fri, 19 Sep 2025 11:40:25 +0200 Message-ID: <20250919094041.183031-11-stefan.klug@ideasonboard.com> X-Mailer: git-send-email 2.48.1 In-Reply-To: <20250919094041.183031-1-stefan.klug@ideasonboard.com> References: <20250919094041.183031-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" There are several occasions where quantization can lead to visible effects. In WDR mode it can happen that exposure times get set to very low values (Sometimes 2-3 lines). This intentionally introduced underexposure is corrected by the GWDR module. As exposure time is quantized by lines, the smallest possible change in exposure time now results in a quite visible change in perceived brightness. On some sensors the possible gain steps are also quite large leading to visible jumps if e.g. if the exposure time is fixed. Mitigate that by applying a global gain to account for the error introduced by the exposure quantization. ToDo: This needs perfect frame synchronous control of the sensor to work properly which is not guaranteed in all cases. It still improves the behavior with the current regulation and can easily be skipped, be removing the compress algorithm from the tuning file. Signed-off-by: Stefan Klug Reviewed-by: Kieran Bingham Reviewed-by: Daniel Scally Reviewed-by: Paul Elder --- Changes in v4: - Include quantization gain in effective exposure value - Correctly initialize quantizationGain in frameContext - Collected tags Changes in v3: - Collected tags --- src/ipa/rkisp1/algorithms/agc.cpp | 33 ++++++++++++++++++++++++++++--- src/ipa/rkisp1/ipa_context.h | 2 ++ 2 files changed, 32 insertions(+), 3 deletions(-) diff --git a/src/ipa/rkisp1/algorithms/agc.cpp b/src/ipa/rkisp1/algorithms/agc.cpp index 0a29326841fb..fe2f66cf6ee4 100644 --- a/src/ipa/rkisp1/algorithms/agc.cpp +++ b/src/ipa/rkisp1/algorithms/agc.cpp @@ -176,6 +176,7 @@ int Agc::configure(IPAContext &context, const IPACameraSensorInfo &configInfo) context.activeState.agc.automatic.gain = context.configuration.sensor.minAnalogueGain; context.activeState.agc.automatic.exposure = 10ms / context.configuration.sensor.lineDuration; + context.activeState.agc.automatic.quantizationGain = 1.0; context.activeState.agc.manual.gain = context.activeState.agc.automatic.gain; context.activeState.agc.manual.exposure = context.activeState.agc.automatic.exposure; context.activeState.agc.autoExposureEnabled = !context.configuration.raw; @@ -199,6 +200,9 @@ int Agc::configure(IPAContext &context, const IPACameraSensorInfo &configInfo) context.configuration.agc.measureWindow.h_size = configInfo.outputSize.width; context.configuration.agc.measureWindow.v_size = configInfo.outputSize.height; + AgcMeanLuminance::configure(context.configuration.sensor.lineDuration, + context.camHelper.get()); + setLimits(context.configuration.sensor.minExposureTime, context.configuration.sensor.maxExposureTime, context.configuration.sensor.minAnalogueGain, @@ -283,6 +287,10 @@ void Agc::queueRequest(IPAContext &context, if (!frameContext.agc.autoGainEnabled) frameContext.agc.gain = agc.manual.gain; + if (!frameContext.agc.autoExposureEnabled && + !frameContext.agc.autoGainEnabled) + frameContext.agc.quantizationGain = 1.0; + const auto &meteringMode = controls.get(controls::AeMeteringMode); if (meteringMode) { frameContext.agc.updateMetering = agc.meteringMode != *meteringMode; @@ -336,12 +344,17 @@ void Agc::prepare(IPAContext &context, const uint32_t frame, { uint32_t activeAutoExposure = context.activeState.agc.automatic.exposure; double activeAutoGain = context.activeState.agc.automatic.gain; + double activeAutoQGain = context.activeState.agc.automatic.quantizationGain; /* Populate exposure and gain in auto mode */ - if (frameContext.agc.autoExposureEnabled) + if (frameContext.agc.autoExposureEnabled) { frameContext.agc.exposure = activeAutoExposure; - if (frameContext.agc.autoGainEnabled) + frameContext.agc.quantizationGain = activeAutoQGain; + } + if (frameContext.agc.autoGainEnabled) { frameContext.agc.gain = activeAutoGain; + frameContext.agc.quantizationGain = activeAutoQGain; + } /* * Populate manual exposure and gain from the active auto values when @@ -354,6 +367,12 @@ void Agc::prepare(IPAContext &context, const uint32_t frame, if (!frameContext.agc.autoGainEnabled && frameContext.agc.autoGainModeChange) { context.activeState.agc.manual.gain = activeAutoGain; frameContext.agc.gain = activeAutoGain; + frameContext.agc.quantizationGain = activeAutoQGain; + } + + if (context.configuration.compress.supported) { + frameContext.compress.enable = true; + frameContext.compress.gain = frameContext.agc.quantizationGain; } if (frame > 0 && !frameContext.agc.updateMetering) @@ -564,6 +583,14 @@ void Agc::process(IPAContext &context, [[maybe_unused]] const uint32_t frame, double analogueGain = frameContext.sensor.gain; utils::Duration effectiveExposureValue = exposureTime * analogueGain; + /* + * Include the quantization gain if it was applied. Do not use + * compress.gain because it will include gains that shall not be + * reported to the user when HDR is implemented. + */ + if (frameContext.compress.enable) + effectiveExposureValue *= frameContext.agc.quantizationGain; + setExposureCompensation(pow(2.0, frameContext.agc.exposureValue)); utils::Duration newExposureTime; @@ -582,7 +609,7 @@ void Agc::process(IPAContext &context, [[maybe_unused]] const uint32_t frame, /* Update the estimated exposure and gain. */ activeState.agc.automatic.exposure = newExposureTime / lineDuration; activeState.agc.automatic.gain = aGain; - + activeState.agc.automatic.quantizationGain = qGain; /* * Expand the target frame duration so that we do not run faster than * the minimum frame duration when we have short exposures. diff --git a/src/ipa/rkisp1/ipa_context.h b/src/ipa/rkisp1/ipa_context.h index a723c79b04d9..35d25d555e65 100644 --- a/src/ipa/rkisp1/ipa_context.h +++ b/src/ipa/rkisp1/ipa_context.h @@ -81,6 +81,7 @@ struct IPAActiveState { struct { uint32_t exposure; double gain; + double quantizationGain; } automatic; bool autoExposureEnabled; @@ -135,6 +136,7 @@ struct IPAFrameContext : public FrameContext { uint32_t exposure; double gain; double exposureValue; + double quantizationGain; uint32_t vblank; bool autoExposureEnabled; bool autoGainEnabled;