From patchwork Wed May 6 23:07:20 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: devve X-Patchwork-Id: 26665 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 E1BB0C3305 for ; Wed, 6 May 2026 23:07:41 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 1CC5E6302D; Thu, 7 May 2026 01:07:40 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.b="hRk4KXZp"; dkim-atps=neutral Received: from mail-qk1-x72e.google.com (mail-qk1-x72e.google.com [IPv6:2607:f8b0:4864:20::72e]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 20D23630BB for ; Thu, 7 May 2026 01:07:36 +0200 (CEST) Received: by mail-qk1-x72e.google.com with SMTP id af79cd13be357-8e8c0c2d2bcso34900385a.1 for ; Wed, 06 May 2026 16:07:36 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1778108855; x=1778713655; darn=lists.libcamera.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:to:from:from:to:cc:subject:date:message-id :reply-to; bh=RzzlD4NGXBC8Ir7lyUZbzdtSSu1wXbM8QJB9JRJvLYY=; b=hRk4KXZpZVnPg1SVmCl0mjO/SP+PUPGA7FVBqDUgL5lLmVuakqmdST74wUk8zEhtNl EOyUkF5LaCTsaFukGR584fPHUAmhZtwm7Gux5LcFouhpRjhWFs9ebDCGHPgom/7cvweb 9l7BvgyLQOFXEHLSrEeVOCmHPOniwWY89wNgbLXSTQX6H+FX+9bEjUJlQHjyWHzQaSdn cwRc2d3NP+hx91lxLgU/jn9DHMtjhk6WrhaTvMEHQzqKTknrWLlZjshgleYXApYHSvlB KplsHKiY8IBt/zWQKDINQ4F5mOv7t4nfuoiIe/vwA2GNkg+Q4Z6oAG4X0E0hNQAQiJ99 m1ZA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1778108855; x=1778713655; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:to:from:x-gm-gg:x-gm-message-state:from:to :cc:subject:date:message-id:reply-to; bh=RzzlD4NGXBC8Ir7lyUZbzdtSSu1wXbM8QJB9JRJvLYY=; b=PsfbwYhv6UqfIoRNyH2+MGfVIBVGxJARFE0s5PNQDsVZnGRlhduhwbg/1EYxNcIYxz +mI61Q98iyckyL6Ocf10HsH8i3ynwTD8ekQN2jsMNFyHQWUynE1uWJxNDK15aK/kBpnR k9/mVF48jLM4inFQ7DlAj/yi5FpcC/4MVt+WU8+dK4geZxA2DGQUdH22vW1pcTrqDEhr dbfj/x6CJNFlkqQEqhlcV7vMqLGqR1cqRgwklldHMULL6s3tvyulEMqjbEFKAy3f4afs bNWKWhELWWnTA5UrQvigJSmdj4ZLz2NgcDYgBN+wF1mZvzNDWMorTzRdIKFqiQfPAT1r uQfQ== X-Gm-Message-State: AOJu0YxUhcs1Uo9POhgtQNInQQqEpoEzvR+w9QNXE3aCQgnI5g2IXSPy yHVTn3Z704CEDGEOWQV9jYEhepGjZyunFkuoXNAWTVBl9MYSryDpn2340qLUlg== X-Gm-Gg: AeBDievbLRQwxWKr1U+YGyw1nnDJduogzCnUZQ/ujTPOF9/4X9NQ7dhw7OHnjSciNbY zj6eaLDxwqtzd2/AELzHc8CUKaXFGGLndhDMOj7HN3TNuoRlcifmZuptdu75mmtD/l4tn4Nc9Jq +JK6v94evdjl3XK8FPXuk66F5Qq3g7d7njzee5Q0Jc3wiNIa6lgkqy//fG47X5eP+An6ycLhwKg pR/flOzw4b3PSXKG8/6wEiH0AfH04a3IqSkLBI8xiHiTs/HFXHJ52ZdxLKwibt+eu/hNt0+VAQo iwGlvYd2jWUzXtPp+pRWMn+UCj6N3pAvwBgLot7ETg/JiWW+m8pl4htyGOYhBUlJ6Gea+I2Gxxt XQoHeV5g/bXvPxwnE1m2zbV7XjgT++FajSzJKuerE1I+ltrAXwc6GwNM7q7QCEfAHZegSTawI+Q xnb6uiVzzgRKzoQcq5XVlwa0jhSD1wz/C9UTDZLnYsrAvyaEcyJJwSHCZSJGPBlQdLbxTdsP7SP ZwyD4hYQSzsXJ77UkDogPJycjthzKuY X-Received: by 2002:a05:620a:29cc:b0:8ed:d906:a8da with SMTP id af79cd13be357-904d4a61b92mr784088785a.18.1778108855000; Wed, 06 May 2026 16:07:35 -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 af79cd13be357-8fc2c25324esm2035266385a.23.2026.05.06.16.07.33 for (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 06 May 2026 16:07:34 -0700 (PDT) From: d3vv3 To: libcamera-devel@lists.libcamera.org Subject: [PATCH v2 08/10] ipa: simple: agc: Read exposure target and gain from YAML Date: Thu, 7 May 2026 01:07:20 +0200 Message-ID: <20260506230722.1041596-9-devve.3@gmail.com> X-Mailer: git-send-email 2.54.0 In-Reply-To: <20260506230722.1041596-1-devve.3@gmail.com> References: <20260506230722.1041596-1-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 */