From patchwork Mon Sep 28 11:13:59 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Dan Scally X-Patchwork-Id: 28389 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 806FDBDCBC for ; Mon, 28 Sep 2026 11:14:25 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id D640D68A51; Mon, 28 Sep 2026 13:14:20 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="XMLkC4P8"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 33FDA68A4D for ; Mon, 28 Sep 2026 13:14:15 +0200 (CEST) Received: from [127.0.1.1] (chfd-03-b2-v4wan-176392-cust229.vm15.cable.virginm.net [82.19.20.230]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id ED193177A; Mon, 28 Sep 2026 13:12:24 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1790593945; bh=PJ5N4Pqk2Go8Zog7U28zfad0DRvxbpepyyOkObr8Fy4=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=XMLkC4P8DO+yoYGvR9UsBbsQVs6jnkhx/4ysp5DI7kt/WLNQSR1cbqH1Mz+7e8VR5 WP3G4/6FIULalTYPg/N6Ox0bEOLIZxkR+k3YAkMHDDViSTEZrrHcGCsfJ7z/2QCvo0 0/y9eYihTqrDwwZ+rGt74vv+9kvXFDvJeMsdvA3o= From: Daniel Scally Date: Mon, 28 Sep 2026 12:13:59 +0100 Subject: [PATCH 3/5] ipa: libipa: awb: Report AwbState MIME-Version: 1.0 Message-Id: <20260928-awb-state-v1-3-9b1bb8b9e51b@ideasonboard.com> References: <20260928-awb-state-v1-0-9b1bb8b9e51b@ideasonboard.com> In-Reply-To: <20260928-awb-state-v1-0-9b1bb8b9e51b@ideasonboard.com> To: libcamera-devel@lists.libcamera.org Cc: Daniel Scally X-Mailer: b4 0.14.2 X-Developer-Signature: v=1; a=openpgp-sha256; l=5549; i=dan.scally@ideasonboard.com; h=from:subject:message-id; bh=PJ5N4Pqk2Go8Zog7U28zfad0DRvxbpepyyOkObr8Fy4=; b=owEBbQKS/ZANAwAKAchJV3psRXUyAcsmYgBqukwFVrUZ8IDeiPhAZDwrrcIrEPknOV04TYIM8 DPPzBUZrLKJAjMEAAEKAB0WIQQqyuwyDnZdb+mxmm/ISVd6bEV1MgUCarpMBQAKCRDISVd6bEV1 Mo43D/9NUZBZ7Lm+FGbVUSO/liPfNa7GTnIFlSQsvWmXqXUukcaerRIck7nBDanZyasef1Nxz66 VzTN4v9oTlIh/JY1iu8PZcg8j0jT9aSt+e528gWwjli63ngPPohAGFKK/tceQENSD2ESrRxeapc TCBdUzjFMxrXimZ972nONG/hIjSNkmSNcTQbFjuNJ63xbLcdsOOU+QlHkkmkcONgg1Y2VtWNOVI gSBaEJCoSGpfTDcigvLRvrjZ74EkJNrNQrC8UwPBQArtLpvugvBNgyRBU6lx3OhSqUqwecdjK0r 41TGTnRMio4TtLWgb1DzmUYV/h1FJO2DZFC2/enQINBIAKjNwHBczEC6jvgHm+wZ49nbM4Qe+kJ nXJ+OaCLbxEm5IsAu18Vnk5lDdcTbHisbQ/LEfbJKu6mZyhhYKiLb8bwCa4T7dtuq0VOtLhkzJY 8nxn5JZM8JSzj9gX5nDae5sJ9DU7e/PvlaQp+mZHD5ucETCi5f/uJrqd/6Yzy8x6oTqDhpQJP3o 3FTug84aZFX4P5vFmihcnF6kjRZAoi3qV5ZKgwE5fRr4do9K6ydlKRSlzPORX5ETiIFwa+06Xhy x7rMjt7ODCTHyYFM32NN7Jpm37Jsw9vJCuF84CrtKVyCJFJYzMsoaPcKawq2O0k9TihijROuFIP +WiHNqnT8pRKFGA== X-Developer-Key: i=dan.scally@ideasonboard.com; a=openpgp; fpr=EEC699ACA1B7CB5D31330C0BBD501C2A3546CCF6 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" Report the AwbState in metadata. At the moment the implementation only covers the AwbStateSearching and AwbStateConverged enum values. Where the calculated gains for a frame have been within 10% of those calculated for the previous frame for 5 frames, the algorithm is treated as having converged and the gains calculated for this frame are recorded, and AwbState reports AwbStateConverged. If the gains calculated for any subsequent frame stray more than 10% from those recorded gains the state returns to AwbStateSearching. Signed-off-by: Daniel Scally --- src/ipa/libipa/awb.cpp | 62 ++++++++++++++++++++++++++++++++++++++++++++++++-- src/ipa/libipa/awb.h | 5 ++++ 2 files changed, 65 insertions(+), 2 deletions(-) diff --git a/src/ipa/libipa/awb.cpp b/src/ipa/libipa/awb.cpp index 0e312690b8d2becccbb1405d93cac9c04b2d61a4..d1a75020843a03b3530dce4b6bd82f228ec6bed9 100644 --- a/src/ipa/libipa/awb.cpp +++ b/src/ipa/libipa/awb.cpp @@ -17,6 +17,8 @@ constexpr int32_t kMinColourTemperature = 2500; constexpr int32_t kMaxColourTemperature = 10000; constexpr int32_t kDefaultColourTemperature = 5000; +constexpr double kConvergenceErrorMargin = 0.10; +constexpr unsigned int kNumFramesForConvergence = 5; /** * \file awb.h @@ -255,6 +257,8 @@ int AwbAlgorithmBase::configure(awb::ActiveState &state) state.manual.colourTemperature = kDefaultColourTemperature; state.automatic.colourTemperature = kDefaultColourTemperature; + lockedCount_ = 0; + return 0; } @@ -351,6 +355,57 @@ void AwbAlgorithmBase::prepare(awb::ActiveState &state, } } +/* + * We want to assess whether the algorithm has "converged" or not. When we're in + * the Searching state then we look for stability of the calculated gains within + * 10% of the previous frame's calculated gain, for at least 5 frames. Greater + * than 10% but within 15% difference does not count towards those 5 frames, but + * will not reset the count. More than 15% difference from the last frame resets + * it. + * + * Once we have reached convergence the gains that were calculated for that + * frame are recorded, and future frames gains are compared against those gains + * instead of the previous frame's gains. A difference greater than 10% will + * cause the state to return to Searching. + */ +void AwbAlgorithmBase::updateConvergedState(RGB &oldGains, RGB &newGains) +{ + if (convergedState_ == controls::AwbStateEnum::AwbStateSearching) { + RGB smallGainError = oldGains * kConvergenceErrorMargin; + RGB bigGainError = smallGainError * 1.5; + + if (newGains <= oldGains - bigGainError || + newGains >= oldGains + bigGainError) { + lockedCount_ = 0; + } else if (newGains <= oldGains - smallGainError || + newGains >= oldGains + smallGainError) { + // do nothing in this case + } else { + lockedCount_ = std::min(lockedCount_ + 1, kNumFramesForConvergence); + } + + if (lockedCount_ == kNumFramesForConvergence) { + convergedGains_ = newGains; + convergedState_ = controls::AwbStateEnum::AwbStateConverged; + } + + return; + } + + /* + * If we're not AwbStateSearching then we're converged, and we check to + * make sure that we have not strayed too far from the converged gains. + */ + + RGB gainError = convergedGains_ * kConvergenceErrorMargin; + + if (newGains < convergedGains_ - gainError || + newGains > convergedGains_ + gainError) { + convergedState_ = controls::AwbStateEnum::AwbStateSearching; + lockedCount_ = 0; + } +} + /** * \brief Process AWB statistics to calculate gains and populate metadata * \param[in] state The AWB active state @@ -384,15 +439,18 @@ void AwbAlgorithmBase::process(awb::ActiveState &state, double ct = awbResult.colourTemperature; ct = ct * speed + state.automatic.colourTemperature * (1 - speed); + RGB newGains = awbResult.gains * speed + + state.automatic.gains * (1 - speed); + updateConvergedState(state.automatic.gains, newGains); state.automatic.colourTemperature = awbResult.colourTemperature; - state.automatic.gains = awbResult.gains * speed + - state.automatic.gains * (1 - speed); + state.automatic.gains = newGains; /* Populate metadata. */ metadata.set(controls::AwbEnable, frameContext.autoEnabled); metadata.set(controls::ColourGains, { static_cast(frameContext.gains.r()), static_cast(frameContext.gains.b()) }); metadata.set(controls::ColourTemperature, frameContext.colourTemperature); + metadata.set(controls::AwbState, convergedState_); LOG(Awb, Debug) << std::showpoint << "Means " << stats.rgbMeans() << ", gains " << state.automatic.gains diff --git a/src/ipa/libipa/awb.h b/src/ipa/libipa/awb.h index ef9e78b1e819331b71c06cb48f91bfad0857d10c..5d80f241ec92280e867af5f9724471c5784683d1 100644 --- a/src/ipa/libipa/awb.h +++ b/src/ipa/libipa/awb.h @@ -102,11 +102,16 @@ private: int parseModeConfigs(const ValueNode &tuningData, const ControlValue &def = {}); + void updateConvergedState(RGB &oldGains, RGB &newGains); std::map modes_; const ModeConfig *currentMode_ = nullptr; std::unique_ptr impl_; bool bayes_ = false; + + controls::AwbStateEnum convergedState_; + RGB convergedGains_; + unsigned int lockedCount_; }; template