From patchwork Fri May 1 19:13:12 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: devve X-Patchwork-Id: 26595 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 47B7AC32F7 for ; Fri, 1 May 2026 19:55:31 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 7B8BC63020; Fri, 1 May 2026 21:55:28 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.b="nOS10SpO"; dkim-atps=neutral Received: from mail-wm1-x332.google.com (mail-wm1-x332.google.com [IPv6:2a00:1450:4864:20::332]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id E5A106301A for ; Fri, 1 May 2026 21:14:24 +0200 (CEST) Received: by mail-wm1-x332.google.com with SMTP id 5b1f17b1804b1-488ab2db91aso25729605e9.3 for ; Fri, 01 May 2026 12:14:24 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1777662864; x=1778267664; darn=lists.libcamera.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=Np2KpHav7jquUv7l8lhExD9bUwRg7NkVkFY4AMLAGpQ=; b=nOS10SpO4bJao0IGdtIucVXodVS7Jla+wbAWjZjo+JEdJkdTaAlcKdL2OE3HFZlrpX tu5C/yLj34jKmuias+w2v9rGRmJdf7C2WYFEKaBWjHzDFsLB7hwwEV6/ycsHw3VdhMKp 17NJbnwqrdGL7Waxss1sfwMJ9Fe/+oDnXETBiGQB+7GF/F4yrSu6rltPbwUEJjranrkT Vjq9YHauVOmStgW+PCaCb+xpHZ/gLJZUePrcSv93NgOkD9CDRV7G3n/vPFtBNIJ56rj1 0PIMTegRcq+x5StGj2bso/mGDCuyY4K26XHbjgALLK80mBCjKQT296yfIY5U949xnY4O Lx6Q== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1777662864; x=1778267664; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=Np2KpHav7jquUv7l8lhExD9bUwRg7NkVkFY4AMLAGpQ=; b=I9ljDs92ULszzROXmAnzhfu/BIfGxuUyTcq+j2RJu3UTfTt7x1K5fvpn3bfApxLzrT Q1nb/mrsE4vTHJKs+VLwUht4TvYoUmSMnRLmssXQQIxp5aEgEj8nNWjuQ4pLcQ3+/6Nq PTUQej+l0UawVzRjmd29DY90fLgckokF3ehIY4zOorPLSdv0FuFLRswKRDRC25J9XHQa ygrnZ8vcxnZNdONXY4yxZOMUOz+QROUx7ewydHqVc6vJbeHU/+UxhvywFM2e8q12szba S4tsyQN0F/Aa8qCjlVb3Fp5CwQdI9JU0B0RHXzHthpdDLc2GstlSCoPdFlMiY0RyD9ZW 80vg== X-Gm-Message-State: AOJu0YxgQNJVp082QP4JblJFAVw8e/fqTJtZ8Q4pHb3S372Z3ehR36Ho UltY9sgU8hkDuu/YnrRnqJ2eWC+hIMRFKgD7GqPEP1zDGdIOSGuNhsSfSHVV5Q== X-Gm-Gg: AeBDiesf/Jy+e4pYIjwxzeAXGMrpbJ2WCWVul/wqHph374pDIyhvjUHJUjQJGMcyzCE nPSUkAUC4Yfay/kmUULWxDappyEXnR9R/jtgGjD7inorBcE+2Td2hhWL1/ILQLLzFDkUnlbN2v6 SIBh5fr27zCOXGC1IqQ+wRHpXeKBXX23TN6oymAE2jsdiXcGKSn+Roj7fVj0G87wCJFYezgfX3V eiCKkRsYfc93/mKOAVhBff+05nujM2cmS8QDVl9/+SeGNx/XzURxOEtyU5S9H/N4b9OU8E2DvbF fMZDkVrz2IXB2zDp3MYmNtmXNBBoXRxDwxHlxSZo09Zn/SOP4959B5NytmkLsyBcYiwcA3PRM3D riY/RaplODtJxiNhme6MiCuUWZQus3WDoOiN/9P4JPpqromV0tnzG1gtUFqPP1vuvDz4to6tiPa YfNqIN9hKZd2c6sHKY+p6ZvbrLWzq23coLi0OeWZ1eiUuD1S2+/YBTQKoUZSsRKFX1WjYh0rNYe yopzoax+cUZ+VDV+Z72z7NUhqLrgqd5gY3OT8rI928= X-Received: by 2002:a05:600c:a402:b0:486:fba7:b150 with SMTP id 5b1f17b1804b1-48a9865f7c3mr4466595e9.15.1777662864353; Fri, 01 May 2026 12:14:24 -0700 (PDT) Received: from dexps.speedport.ip (p200300eda74453f7a35e44f7d2f0427a.dip0.t-ipconnect.de. [2003:ed:a744:53f7:a35e:44f7:d2f0:427a]) by smtp.gmail.com with ESMTPSA id 5b1f17b1804b1-48a8eb69698sm139146305e9.1.2026.05.01.12.14.23 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 01 May 2026 12:14:24 -0700 (PDT) From: d3vv3 To: libcamera-devel@lists.libcamera.org Cc: d3vv3 Subject: [PATCH] ipa: simple: agc: Read exposure target and gain from YAML Date: Fri, 1 May 2026 21:13:12 +0200 Message-ID: <20260501191400.985920-4-devve.3@gmail.com> X-Mailer: git-send-email 2.54.0 In-Reply-To: <20260501191400.985920-1-devve.3@gmail.com> References: <20260501191400.985920-1-devve.3@gmail.com> MIME-Version: 1.0 X-Mailman-Approved-At: Fri, 01 May 2026 21:55:24 +0200 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" Replace the hardcoded kExposureOptimal, kExposureSatisfactory, and kExpProportionalGain constants with member variables read from the tuning file as exposureTarget, hysteresis, and proportionalGain. Defaults match the previous values (2.5, 0.2, 0.04). Signed-off-by: d3vv3 --- src/ipa/simple/algorithms/agc.cpp | 33 ++++++++++++++++++++----------- src/ipa/simple/algorithms/agc.h | 8 ++++++++ 2 files changed, 30 insertions(+), 11 deletions(-) diff --git a/src/ipa/simple/algorithms/agc.cpp b/src/ipa/simple/algorithms/agc.cpp index ac977d5f..66618d0f 100644 --- a/src/ipa/simple/algorithms/agc.cpp +++ b/src/ipa/simple/algorithms/agc.cpp @@ -28,45 +28,56 @@ 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. + * in the middle of the range. Overridable via YAML exposureTarget. */ -static constexpr float kExposureOptimal = kExposureBinsCount / 2.0; +static constexpr float kExposureTargetDefault = kExposureBinsCount / 2.0; /* * This implements the hysteresis for the exposure adjustment. * It is small enough to have the exposure close to the optimal, and is big * enough to prevent the exposure from wobbling around the optimal value. */ -static constexpr float kExposureSatisfactory = 0.2; +static constexpr float kHysteresisDefault = 0.2; /* * Proportional gain for exposure/gain adjustment. Maps the MSV error to a * multiplicative correction factor: * - * factor = 1.0 + kExpProportionalGain * error + * factor = 1.0 + proportionalGain_ * error * - * With kExpProportionalGain = 0.04: + * With proportionalGain_ = 0.04: * - max error ~2.5 -> factor 1.10 (~10% step, same as before) * - error 1.0 -> factor 1.04 (~4% step) * - error 0.3 -> factor 1.012 (~1.2% step) * - * This replaces the fixed 10% bang-bang step with a proportional correction - * that converges smoothly and avoids overshooting near the target. + * Overridable via YAML proportionalGain. */ -static constexpr float kExpProportionalGain = 0.04; +static constexpr float kProportionalGainDefault = 0.04; Agc::Agc() { } +int Agc::init([[maybe_unused]] IPAContext &context, const ValueNode &tuningData) +{ + exposureTarget_ = tuningData["exposureTarget"].get() + .value_or(kExposureTargetDefault); + hysteresis_ = tuningData["hysteresis"].get() + .value_or(kHysteresisDefault); + proportionalGain_ = tuningData["proportionalGain"].get() + .value_or(kProportionalGainDefault); + + return 0; +} + void Agc::updateExposure(IPAContext &context, IPAFrameContext &frameContext, double exposureMSV) { int32_t &exposure = frameContext.sensor.exposure; double &again = frameContext.sensor.gain; - double error = kExposureOptimal - exposureMSV; + double error = exposureTarget_ - exposureMSV; - if (std::abs(error) <= kExposureSatisfactory) + if (std::abs(error) <= hysteresis_) return; /* @@ -74,7 +85,7 @@ void Agc::updateExposure(IPAContext &context, IPAFrameContext &frameContext, dou * determines the direction: positive error means too dark (increase), * negative means too bright (decrease). */ - float factor = 1.0f + static_cast(error) * kExpProportionalGain; + float factor = 1.0f + static_cast(error) * proportionalGain_; if (factor > 1.0f) { /* Scene too dark: increase exposure first, then gain. */ diff --git a/src/ipa/simple/algorithms/agc.h b/src/ipa/simple/algorithms/agc.h index 112d9f5a..b8ed542b 100644 --- a/src/ipa/simple/algorithms/agc.h +++ b/src/ipa/simple/algorithms/agc.h @@ -7,6 +7,8 @@ #pragma once +#include + #include "algorithm.h" namespace libcamera { @@ -19,6 +21,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 +30,10 @@ public: private: void updateExposure(IPAContext &context, IPAFrameContext &frameContext, double exposureMSV); + + float exposureTarget_; + float hysteresis_; + float proportionalGain_; }; } /* namespace ipa::soft::algorithms */ From patchwork Wed May 6 22:10:50 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: devve X-Patchwork-Id: 26644 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 C0F68BE173 for ; Wed, 6 May 2026 22:10:59 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id BBDCC63026; Thu, 7 May 2026 00:10:58 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.b="I3hhx9qB"; dkim-atps=neutral Received: from mail-wm1-x32d.google.com (mail-wm1-x32d.google.com [IPv6:2a00:1450:4864:20::32d]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 0EF8E62DC4 for ; Thu, 7 May 2026 00:10:57 +0200 (CEST) Received: by mail-wm1-x32d.google.com with SMTP id 5b1f17b1804b1-4893940bb5eso962965e9.3 for ; Wed, 06 May 2026 15:10:57 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1778105456; x=1778710256; darn=lists.libcamera.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=RzzlD4NGXBC8Ir7lyUZbzdtSSu1wXbM8QJB9JRJvLYY=; b=I3hhx9qB5NxKC/KfR4upbQX6weYAP+YRKXYGPRcxeX/UAu1R8n5GCloQThVXqML6x/ lXHlYA2u+LFQ/3n6AsTjawo7nslWsiqJoHqqcwu5vOkKqztp+cgiNDp/BmOrs6PK/19V GMc/HHyKmILO+Qj5NDBLv5XF7MZnPlvQbS0CY5sIewCdQ9j8wVU1nHnMCmjLTlLjwzLv 3PFvGCekUu093SwE/RACx12bvOwhXOS53uN71CGJl6Kq6BF1h82jkiDAVsM9N/2jA3Vo G5agO3MpTPXE6/zUMkbTW3uEdplIQD6qJKJWLSTJ3InpdIXc1eLTW29gfdSOUNrGOE3a H4cw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1778105456; x=1778710256; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=RzzlD4NGXBC8Ir7lyUZbzdtSSu1wXbM8QJB9JRJvLYY=; b=HGus4N33cHBnFyu8ai8JTH3Hh/vgJuAG8XSyJ1OvBCkePIwgR7happ735JaQDOaAV8 z1h2lGKTHVc/vMS+wxmZEj7V+boVckIA4Y4PYx8mhADtGqvN8pPQQlURq+jk5xNIRSL8 TqvtkQS+fwljvUhCHIkcY95P2RseUzJf3ZCjRUwAWSS28EiqR/JXwusXn28mPCEX4qLb XblPft/gjcid1D6lMlStpVb/lswu2w00+iXXBxGlbYazhAAU/+11/TwWw7B7wVTm02hQ zyFkV8Qc9efAotHhpVjIGWB5mcLgtsfQsKqTbNEEXNGWVjvz8r+qxv4vesuj+seJHWQV f3rQ== X-Gm-Message-State: AOJu0YzGuD12p5Dc6naW9ulJvLWLAGvA7rnxArz/rjzJSPlBVZQTRDWI KIOa7LCYAguUXn4/LbRB1QFvtY2UgxJbkLwfYRLHzBqGtlNO4hWxV8fczNd0TA== X-Gm-Gg: AeBDietg7nFNMxx1F9jN7OQNJt4ZRxDAnzQHwPlPaCBTaj8Rvsnu2bUesjRBKRmGB2f mahukSaD68eyi+Vl1xNDybQLthexFmApde3oc0PDMiL+n0gD9PO9laS/IK+CbgzVNM5wcx09DUI PBE3eDpKpHmVuPxLtoxjMhCsP0R0VKnracRNyBAFci5+Af1OaaIYFTGtvIhzBqi/Muf/PF92+n9 JIXLxNwtk2GAfz8rX10Z2Dvm27ZaJlvuRPQpekq+UJ8R+j735cUqKnREaU0WgG1nNF9P7k3AMX1 pbsHF+eGYc2vsZ4/QeQxV3KvOFtLMY69UvPUZHHAW5IbMzVb+wFCO8YIsrZSPEkDSRGlBnzaOie tU+xD/64oaVwLYz04QpT6GAuv+AMVWrHxBYP68Srsy++fLvsdANqFiGjtYso+5EdqBtImRHsNIZ a7K+uAlOvZgzUUuuPXZPohnTdJtoEAjlxhotWXBWqQJBhaNJUbhTcnGdGwqG4eozirydv/inDvI iu+RGWqT4+7H/vqRdW3XtR9HQNpDrOa X-Received: by 2002:a05:600c:3420:b0:48e:51f5:2764 with SMTP id 5b1f17b1804b1-48e51f52779mr55654665e9.27.1778105456437; Wed, 06 May 2026 15:10:56 -0700 (PDT) Received: from dexps.speedport.ip (p200300eda74453cf3cf3f7929e513b94.dip0.t-ipconnect.de. [2003:ed:a744:53cf:3cf3:f792:9e51:3b94]) by smtp.gmail.com with ESMTPSA id 5b1f17b1804b1-48e538b6e9bsm128690215e9.10.2026.05.06.15.10.56 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 06 May 2026 15:10:56 -0700 (PDT) From: d3vv3 To: libcamera-devel@lists.libcamera.org Cc: d3vv3 Subject: [PATCH v2 7/9] ipa: simple: agc: Read exposure target and gain from YAML Date: Thu, 7 May 2026 00:10:50 +0200 Message-ID: <20260506221050.869131-1-devve.3@gmail.com> X-Mailer: git-send-email 2.54.0 In-Reply-To: <20260501191400.985920-4-devve.3@gmail.com> References: <20260501191400.985920-4-devve.3@gmail.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" Replace the hardcoded kExposureOptimal, kExposureSatisfactory, and kExpProportionalGain constants with member variables read from the tuning file as exposureTarget, hysteresis, and proportionalGain. Defaults match the previous values (2.5, 0.2, 0.04). The constants are renamed to match their YAML keys and to use standard control-theory terminology: - kExposureOptimal -> exposureTarget: "optimal" implies a single universally correct value; "target" is the conventional ISP/AGC term for the setpoint the controller drives towards and is sensor-dependent. - kExposureSatisfactory -> hysteresis: the old name described the effect (exposure is satisfactory within this band) rather than the mechanism. "hysteresis" is the standard term for a deadband that prevents oscillation around a setpoint. - kExpProportionalGain -> proportionalGain: drops the redundant kExp prefix and matches the YAML key name directly. Signed-off-by: d3vv3 --- src/ipa/simple/algorithms/agc.cpp | 33 ++++++++++++++++++++----------- src/ipa/simple/algorithms/agc.h | 8 ++++++++ 2 files changed, 30 insertions(+), 11 deletions(-) diff --git a/src/ipa/simple/algorithms/agc.cpp b/src/ipa/simple/algorithms/agc.cpp index ac977d5f..66618d0f 100644 --- a/src/ipa/simple/algorithms/agc.cpp +++ b/src/ipa/simple/algorithms/agc.cpp @@ -28,45 +28,56 @@ 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. + * in the middle of the range. Overridable via YAML exposureTarget. */ -static constexpr float kExposureOptimal = kExposureBinsCount / 2.0; +static constexpr float kExposureTargetDefault = kExposureBinsCount / 2.0; /* * This implements the hysteresis for the exposure adjustment. * It is small enough to have the exposure close to the optimal, and is big * enough to prevent the exposure from wobbling around the optimal value. */ -static constexpr float kExposureSatisfactory = 0.2; +static constexpr float kHysteresisDefault = 0.2; /* * Proportional gain for exposure/gain adjustment. Maps the MSV error to a * multiplicative correction factor: * - * factor = 1.0 + kExpProportionalGain * error + * factor = 1.0 + proportionalGain_ * error * - * With kExpProportionalGain = 0.04: + * With proportionalGain_ = 0.04: * - max error ~2.5 -> factor 1.10 (~10% step, same as before) * - error 1.0 -> factor 1.04 (~4% step) * - error 0.3 -> factor 1.012 (~1.2% step) * - * This replaces the fixed 10% bang-bang step with a proportional correction - * that converges smoothly and avoids overshooting near the target. + * Overridable via YAML proportionalGain. */ -static constexpr float kExpProportionalGain = 0.04; +static constexpr float kProportionalGainDefault = 0.04; Agc::Agc() { } +int Agc::init([[maybe_unused]] IPAContext &context, const ValueNode &tuningData) +{ + exposureTarget_ = tuningData["exposureTarget"].get() + .value_or(kExposureTargetDefault); + hysteresis_ = tuningData["hysteresis"].get() + .value_or(kHysteresisDefault); + proportionalGain_ = tuningData["proportionalGain"].get() + .value_or(kProportionalGainDefault); + + return 0; +} + void Agc::updateExposure(IPAContext &context, IPAFrameContext &frameContext, double exposureMSV) { int32_t &exposure = frameContext.sensor.exposure; double &again = frameContext.sensor.gain; - double error = kExposureOptimal - exposureMSV; + double error = exposureTarget_ - exposureMSV; - if (std::abs(error) <= kExposureSatisfactory) + if (std::abs(error) <= hysteresis_) return; /* @@ -74,7 +85,7 @@ void Agc::updateExposure(IPAContext &context, IPAFrameContext &frameContext, dou * determines the direction: positive error means too dark (increase), * negative means too bright (decrease). */ - float factor = 1.0f + static_cast(error) * kExpProportionalGain; + float factor = 1.0f + static_cast(error) * proportionalGain_; if (factor > 1.0f) { /* Scene too dark: increase exposure first, then gain. */ diff --git a/src/ipa/simple/algorithms/agc.h b/src/ipa/simple/algorithms/agc.h index 112d9f5a..b8ed542b 100644 --- a/src/ipa/simple/algorithms/agc.h +++ b/src/ipa/simple/algorithms/agc.h @@ -7,6 +7,8 @@ #pragma once +#include + #include "algorithm.h" namespace libcamera { @@ -19,6 +21,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 +30,10 @@ public: private: void updateExposure(IPAContext &context, IPAFrameContext &frameContext, double exposureMSV); + + float exposureTarget_; + float hysteresis_; + float proportionalGain_; }; } /* namespace ipa::soft::algorithms */