From patchwork Sat Oct 17 15:38:29 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Qianhui Xu X-Patchwork-Id: 28499 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 BC47BC3305 for ; Sat, 17 Oct 2026 15:38:37 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 3ADE568E5A; Sat, 17 Oct 2026 17:38:35 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=mail.ustc.edu.cn header.i=@mail.ustc.edu.cn header.b="GIr1OhwQ"; dkim-atps=neutral Received: from ustc.edu.cn (smtp.ustc.edu.cn [202.38.64.46]) by lancelot.ideasonboard.com (Postfix) with ESMTP id A191968E43 for ; Sat, 17 Oct 2026 17:38:31 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=mail.ustc.edu.cn; s=dkim; h=Received:From:To:Cc:Subject:Date: Message-ID:In-Reply-To:References:MIME-Version: Content-Transfer-Encoding; bh=5qjyqHwQ0/XrT4hAgX6Rs/w7r0YgFLAkpr 7woY0qr3k=; b=GIr1OhwQyA4woWtcJIs09ZdarTKMKQOQ4hr6NFtnvxq+1X4kfv JwpwpWpy686jWJIQ/8qvK1tKoAVdyr+gv4C92vXI9IMJZ+yUV4LKHLyJ2KqRGf9m Cj5Ik0tBn+zzRTunkIuh2kkhDWq4Jb0O9VQYw1q8SHNeBK+TlwJB1h+ps= Received: from xuqianhui.ustc.edu.cn (unknown [114.214.230.12]) by mailimap2024 (Coremail) with SMTP id 3pYKCgCXGD11ltNqUG0EAA--.1829S4; Sat, 17 Oct 2026 23:38:29 +0800 (CST) From: Qianhui Xu To: libcamera-devel@lists.libcamera.org Cc: Milan Zamazal , Hans de Goede , Kieran Bingham , Laurent Pinchart Subject: [PATCH 2/2] ipa: simple: agc: make the exposure target tunable Date: Sat, 17 Oct 2026 23:38:29 +0800 Message-ID: <20261017153829.442320-3-xiaqinghuan@mail.ustc.edu.cn> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20261017153829.442320-1-xiaqinghuan@mail.ustc.edu.cn> References: <20261017153829.442320-1-xiaqinghuan@mail.ustc.edu.cn> MIME-Version: 1.0 X-CM-TRANSID: 3pYKCgCXGD11ltNqUG0EAA--.1829S4 X-Coremail-Antispam: 1UD129KBjvJXoWxGr1DWr48XFWxCr1rGr18AFb_yoWrXFykpr 4kAr98Ar48GFy2yr95ta97WF45Ja48Gr1SkwsYk39Yva9xXr1qkw42yrs09Fyaqr15AF47 ZF1DtFWUAr4DCFJanT9S1TB71UUUUUDqnTZGkaVYY2UrUUUUjbIjqfuFe4nvWSU5nxnvy2 9KBjDU0xBIdaVrnRJUUUBab7Iv0xC_tr1lb4IE77IF4wAFF20E14v26ryj6rWUM7CY07I2 0VC2zVCF04k26cxKx2IYs7xG6rWj6s0DM7CIcVAFz4kK6r1j6r18M28IrcIa0xkI8VA2jI 8067AKxVWUXwA2048vs2IY020Ec7CjxVAFwI0_JFI_Gr1l8cAvFVAK0II2c7xJM28CjxkF 64kEwVA0rcxSw2x7M28EF7xvwVC0I7IYx2IY67AKxVW7JVWDJwA2z4x0Y4vE2Ix0cI8IcV CY1x0267AKxVWxJVW8Jr1l84ACjcxK6I8E87Iv67AKxVW0oVCq3wA2z4x0Y4vEx4A2jsIE c7CjxVAFwI0_GcCE3s1le2I262IYc4CY6c8Ij28IcVAaY2xG8wAqx4xG64xvF2IEw4CE5I 8CrVC2j2WlYx0E2Ix0cI8IcVAFwI0_Jrv_JF1lYx0Ex4A2jsIE14v26r1j6r4UMcvjeVCF s4IE7xkEbVWUJVW8JwACjcxG0xvY0x0EwIxGrwCY1x0262kKe7AKxVWUtVW8ZwCF04k20x vY0x0EwIxGrwCFx2IqxVCFs4IE7xkEbVWUJVW8JwC20s026c02F40E14v26r1j6r18MI8I 3I0E7480Y4vE14v26r106r1rMI8E67AF67kF1VAFwI0_JF0_Jw1lIxkGc2Ij64vIr41lIx AIcVC0I7IYx2IY67AKxVWUCVW8JwCI42IY6xIIjxv20xvEc7CjxVAFwI0_Gr0_Cr1lIxAI cVCF04k26cxKx2IYs7xG6r1j6r1xMIIF0xvEx4A2jsIE14v26r4j6F4UMIIF0xvEx4A2js IEc7CjxVAFwI0_Gr0_Gr1UYxBIdaVFxhVjvjDU0xZFpf9x07jY38nUUUUU= X-CM-SenderInfo: 50ld1x5qjk3t3q6ptxnoox23vfohv3gofq/1tbiAREDB2rSe1QyUQBDsj 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 simple IPA AGC targets the middle of the histogram range, i.e. a mean sample value of 0.5. As the debayer step gamma-encodes its output (the default gamma is 2.2), such a target maps to roughly 73% of the full code range on output, which looks washed out and clips highlights in scenes with a high dynamic range. Allow platforms to override the target from the tuning file with a new "exposureTarget" parameter, expressed as a fraction of the histogram range. The default keeps the existing behaviour, so existing tuning files keep their meaning. Signed-off-by: Qianhui Xu --- src/ipa/simple/algorithms/agc.cpp | 24 +++++++++++++++++++----- src/ipa/simple/algorithms/agc.h | 4 ++++ 2 files changed, 23 insertions(+), 5 deletions(-) diff --git a/src/ipa/simple/algorithms/agc.cpp b/src/ipa/simple/algorithms/agc.cpp index 2f7e040..b6e9d30 100644 --- a/src/ipa/simple/algorithms/agc.cpp +++ b/src/ipa/simple/algorithms/agc.cpp @@ -25,10 +25,11 @@ namespace ipa::soft::algorithms { static constexpr unsigned int kExposureBinsCount = 5; /* - * The exposure is optimal when the mean sample value of the histogram is - * in the middle of the range. + * The exposure is optimal when the mean sample value of the histogram matches + * the configured target. 0.5 (the middle of the range) is a bright default; + * sensors tuned for photographic output usually target a lower code value. */ -static constexpr float kExposureOptimal = kExposureBinsCount / 2.0; +static constexpr double kDefaultExposureTarget = 0.5; /* * This implements the hysteresis for the exposure adjustment. @@ -38,9 +39,21 @@ static constexpr float kExposureOptimal = kExposureBinsCount / 2.0; static constexpr float kExposureSatisfactory = 0.2; Agc::Agc() + : exposureTarget_(kDefaultExposureTarget) { } +int Agc::init([[maybe_unused]] IPAContext &context, const ValueNode &tuningData) +{ + const ValueNode &target = tuningData["exposureTarget"]; + + exposureTarget_ = target.get(kDefaultExposureTarget); + LOG(IPASoftExposure, Debug) + << "Exposure target " << exposureTarget_; + + return 0; +} + void Agc::updateExposure(IPAContext &context, IPAFrameContext &frameContext, double exposureMSV) { /* @@ -53,8 +66,9 @@ void Agc::updateExposure(IPAContext &context, IPAFrameContext &frameContext, dou int32_t &exposure = frameContext.sensor.exposure; double &again = frameContext.sensor.gain; + const double optimal = exposureTarget_ * kExposureBinsCount; - if (exposureMSV < kExposureOptimal - kExposureSatisfactory) { + if (exposureMSV < optimal - kExposureSatisfactory) { if (exposure < context.configuration.agc.exposureMax) { int32_t next = exposure * kExpNumeratorUp / kExpDenominator; if (next - exposure < 1) @@ -70,7 +84,7 @@ void Agc::updateExposure(IPAContext &context, IPAFrameContext &frameContext, dou } } - if (exposureMSV > kExposureOptimal + kExposureSatisfactory) { + if (exposureMSV > optimal + kExposureSatisfactory) { if (again > context.configuration.agc.again10) { double next = again * kExpNumeratorDown / kExpDenominator; if (again - next < context.configuration.agc.againMinStep) diff --git a/src/ipa/simple/algorithms/agc.h b/src/ipa/simple/algorithms/agc.h index 112d9f5..8d3d2f6 100644 --- a/src/ipa/simple/algorithms/agc.h +++ b/src/ipa/simple/algorithms/agc.h @@ -19,6 +19,8 @@ public: Agc(); ~Agc() = default; + int init(IPAContext &context, const ValueNode &tuningData) override; + void process(IPAContext &context, const uint32_t frame, IPAFrameContext &frameContext, const SwIspStats *stats, @@ -26,6 +28,8 @@ public: private: void updateExposure(IPAContext &context, IPAFrameContext &frameContext, double exposureMSV); + + double exposureTarget_; }; } /* namespace ipa::soft::algorithms */