From patchwork Mon Sep 28 11:13:57 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Dan Scally X-Patchwork-Id: 28387 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 5C49BBDCBC for ; Mon, 28 Sep 2026 11:14:21 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 800DF68A60; Mon, 28 Sep 2026 13:14:17 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="HMIrKdX2"; 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 E764D68A43 for ; Mon, 28 Sep 2026 13:14:14 +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 78557C24; 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=1790593944; bh=U4cfUevO0l2/kVQFQunKUlycGNlMZZdci/AwnQIl26g=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=HMIrKdX2M0gmcRHqy10BRuaUMKKxnU+GCmWIX873vp1fU4W4WU0YjZa7XVhBnqYkP UdJGKV11RZ+POmv0PF9lDbIJVMEAl9hBNpAt8S1OsPDrhOc9b41vGAHdk7+WkeQAxX t3sq9fwy4MQS0YKzYwcjfYWUmEDgrnVz8D/giSkU= From: Daniel Scally Date: Mon, 28 Sep 2026 12:13:57 +0100 Subject: [PATCH 1/5] libcamera: controls: Expand AWB controls MIME-Version: 1.0 Message-Id: <20260928-awb-state-v1-1-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=4320; i=dan.scally@ideasonboard.com; h=from:subject:message-id; bh=U4cfUevO0l2/kVQFQunKUlycGNlMZZdci/AwnQIl26g=; b=owEBbQKS/ZANAwAKAchJV3psRXUyAcsmYgBqukwEOocscTg18yE6ZJiRVT7raadUEVkXIZ0r+ 5YSsbpU9OeJAjMEAAEKAB0WIQQqyuwyDnZdb+mxmm/ISVd6bEV1MgUCarpMBAAKCRDISVd6bEV1 MtGED/93IvG/dOfq2lhOsmapy63Bgw7Mww/LthBeRuCi/pPCMa3wLUYkg49uM4Kgwf6KCDXlC5c EGT+LoCwm7+Gdd3+P5EB6Han9DcUbBHcAAYI1qr6jsS+jRW+BNQepLs7TpkHrRXR4y1v4XLI9w9 auPWi+WxL8cQBOovtN4BbRKnm1Heis9snVYQ6RD0l5ix+d90TQ6C5EfIEeLIA25mkqfrN7KrBdH VBCrIWDfDfIZ66gBLW7xR3WjNB4RybAyql3LZ+h7Meh09o3v+PzMqaNMqQrsrSuXj59yZuzI0gp lVlO9blu1FozSwV49PN+5TKZW2FRQSEKEA3cHNno0kkfAiF2NhmcdeBgom/HaySnUur5llozPY/ +RcEixN/ojhtIPE67MCyKb7LLBfucXijZ8sm2UWiAmnub2EL4eebWNUgG+6YEsQiW/NI7chuEzj UCvQSo97MCcSQAPVFYZCui1tSGOBBDk7ctbm8FBF9hFG/t2LcAgZVs2xJ1SB9FsIFevNAHKD1A4 l0N5HmaNsabb+r7j5gqRup32bjcv1hjn6uTN8Ebf1lhks9UrOivpVRPr/Xhty7V/Rp6rS8QpqQY OLya/jqbBaICKZmN04duudLAchyCFDnmgkJL6c05lnWz8EcsG9J/Ed1Pc4n10INa4ow/50biYDL VBI6yaoVhlu7foA== 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" Expand the AWB controls. AwbState is moved from draft to core, but the Idle state is dropped since it effectively replicates the meaning of AwbEnable == false. The AwbLocked metadata item is repurposed as a control designed to allow users to freeze the AWB state. AwbTrigger is added a mechanism for forcing a recalculation of the gains to be applied despite that nominally frozen state. Signed-off-by: Daniel Scally --- src/libcamera/control_ids_core.yaml | 44 +++++++++++++++++++++++++++++++----- src/libcamera/control_ids_draft.yaml | 22 ------------------ 2 files changed, 38 insertions(+), 28 deletions(-) diff --git a/src/libcamera/control_ids_core.yaml b/src/libcamera/control_ids_core.yaml index 89991d03d79a352197c38c22c5003ab186281e24..ad35b4a7b9e4b1dd59af7b29f0ec3f87eec334e2 100644 --- a/src/libcamera/control_ids_core.yaml +++ b/src/libcamera/control_ids_core.yaml @@ -568,17 +568,49 @@ controls: value: 7 description: Custom AWB mode. - - AwbLocked: - type: bool + - AwbState: + type: int32_t direction: out description: | - Report the lock status of a running AWB algorithm. + Reports the current AWB algorithm state. Where the algorithm has + calculated colour gains that differ by more than 5% from the preceding + frame the state will be reported as Searching. Where the calculated + colour gains are within that margin the state will be reported as + Converged. Where the algorithm has been disabled via the AwbEnable + control and a re-scan has not been triggered by the AwbTrigger control + the state will be reported as Locked. + + Note that there is no concept of an "idle" state. Whether the automatic + gains are applied is controlled by the AwbEnable control, but regardless + of the state of that control the automatic calculation of gains will be + performed for every frame. + + \sa AwbLocked + \sa AwbTrigger + + enum: + - name: AwbStateSearching + value: 0 + description: The AWB algorithm has not converged yet. + - name: AwbStateConverged + value: 1 + description: The AWB algorithm has converged. + - name: AwbStateLocked + value: 2 + description: The AWB algorithm is locked. + + - AwbTrigger: + type: bool + direction: in + description: | + Trigger a re-scan of the AWB algorithm in its Locked state. This causes + the state to drop to AwbSearching until the algorithm determines that it + has converged. - If the AWB algorithm is locked the value shall be set to true, if it's - converging it shall be set to false. If the AWB algorithm is not - running the control shall not be present in the metadata control list. + If AwbEnable is set true, then this control has no effect. \sa AwbEnable + \sa AwbState - ColourGains: type: float diff --git a/src/libcamera/control_ids_draft.yaml b/src/libcamera/control_ids_draft.yaml index 03309eeac34fa76eee4bb5d1c87d6467b890c9a7..17ec6d5b5ee6b84faaee6cc481a027b3b5ef4421 100644 --- a/src/libcamera/control_ids_draft.yaml +++ b/src/libcamera/control_ids_draft.yaml @@ -80,28 +80,6 @@ controls: High quality aberration correction which might reduce the frame rate. - - AwbState: - type: int32_t - direction: out - description: | - Control to report the current AWB algorithm state. Currently identical - to ANDROID_CONTROL_AWB_STATE. - - Current state of the AWB algorithm. - enum: - - name: AwbStateInactive - value: 0 - description: The AWB algorithm is inactive. - - name: AwbStateSearching - value: 1 - description: The AWB algorithm has not converged yet. - - name: AwbConverged - value: 2 - description: The AWB algorithm has converged. - - name: AwbLocked - value: 3 - description: The AWB algorithm is locked. - - SensorRollingShutterSkew: type: int64_t direction: out From patchwork Mon Sep 28 11:13:58 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Dan Scally X-Patchwork-Id: 28388 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 291A7BDCBC for ; Mon, 28 Sep 2026 11:14:24 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id E374768A5C; Mon, 28 Sep 2026 13:14:19 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="X95WpQWt"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 32FFB68A4B 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 B26E813D9; 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=1790593944; bh=jHByg/sDAjHaCoGYfgbJUrD7r54dF9uBkw8vTovkmCc=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=X95WpQWtS2Oe1IDUj4WmsXlKJv7/R+2tzZ6xNzycsyZf+Zv5yrG0LmiOJbCQ97MOp +0NklLDssbT+1x81KC1nrQUN0p++hqSHIDP0aWTx61jPH82f66YZP68+VFQtjDNubF cCE3TDVqHjENhptGVdzt24n8Fu/BYhM7PsZxFGWk= From: Daniel Scally Date: Mon, 28 Sep 2026 12:13:58 +0100 Subject: [PATCH 2/5] libcamera: vector: Add more comparison operators MIME-Version: 1.0 Message-Id: <20260928-awb-state-v1-2-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=1834; i=dan.scally@ideasonboard.com; h=from:subject:message-id; bh=jHByg/sDAjHaCoGYfgbJUrD7r54dF9uBkw8vTovkmCc=; b=owEBbQKS/ZANAwAKAchJV3psRXUyAcsmYgBqukwFvrSinWevG91849lLq7DopsnhivR0PixOz fxaRiBrueyJAjMEAAEKAB0WIQQqyuwyDnZdb+mxmm/ISVd6bEV1MgUCarpMBQAKCRDISVd6bEV1 MoXpD/9Vh42TQMrw47M/W5fWWTXEFCXna/AMWcmMuFyq0h5xhCgYdEL+JoU+iWMKFft+trnZcmx 3Q8ugO2mAeRLNPKbexZ6jpdJd4QDRJmB5l3FNEXeOaEH/oAuXlZamp54Ri8fYJlC/9z8ab/fKXb GUbTXP5MFdIJWa8YVLooyx9pcSY7YwGnRc28ZftsDPDqW4YOCS8qk0x/j8Lvx3OENfrLRoBk+nr vkt+JeXCDu6E1SuhoTYmzR3KxKeAOHYF7TBbM+IOh/iwIWLSJXRSh+gbsKVg1loq76iwyEHJK7U iwNQ3yKHDaS+Or0ZZZkRBSY8t2Fi1a6rVPugxZieuUDzY/3HqbM7ojcMT56VbGjVUBSwHgGD8Bc ec1jyfUMIgjySDJpPB7Qv0o4FZikZbBR95oY/VV8gu+RSxLwMVMarAfycu49VTvSEzFxacf9O+k VOqgc8ep/eP/JzzdFcYYRIAuVAk513Mq15qSlj//7azbNjTrOhJjbe7XPEqR7xoKRyuIyRCwPrJ 4dvjJv0CpIBYGNWmJrh7wqJnfdZnYaWMP7yZn/5rUTMdJ0Nq0UAtT2TQaILJxN/jfgucadnGke3 FXMDZghdeS9rHQVyCH84bPPBoOMwegcSG8+PsR99G2386GwZANd5suZ2K2/RnBsv6SS8eeXYcdR zjZwsERrtuj893g== 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" We want to be able to compare Vectors of gains to see if any of the vector values are larger / smaller than the other. Add four new operators for <, >, <= and >=. Signed-off-by: Daniel Scally --- include/libcamera/internal/vector.h | 44 +++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/include/libcamera/internal/vector.h b/include/libcamera/internal/vector.h index 4fb610dbc0aec12a934d7f971c8ee62375a6d726..b89239aa9253209726450a63f29af97b8a808cb8 100644 --- a/include/libcamera/internal/vector.h +++ b/include/libcamera/internal/vector.h @@ -343,6 +343,50 @@ bool operator==(const Vector &lhs, const Vector &rhs) return true; } +template +bool operator>(const Vector &lhs, const Vector &rhs) +{ + for (unsigned int i = 0; i < Rows; i++) { + if (lhs[i] > rhs[i]) + return true; + } + + return false; +} + +template +bool operator>=(const Vector &lhs, const Vector &rhs) +{ + for (unsigned int i = 0; i < Rows; i++) { + if (lhs[i] >= rhs[i]) + return true; + } + + return false; +} + +template +bool operator<=(const Vector &lhs, const Vector &rhs) +{ + for (unsigned int i = 0; i < Rows; i++) { + if (lhs[i] <= rhs[i]) + return true; + } + + return false; +} + +template +bool operator<(const Vector &lhs, const Vector &rhs) +{ + for (unsigned int i = 0; i < Rows; i++) { + if (lhs[i] < rhs[i]) + return true; + } + + return false; +} + template bool operator!=(const Vector &lhs, const Vector &rhs) { 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 From patchwork Mon Sep 28 11:14:00 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Dan Scally X-Patchwork-Id: 28390 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 650EDC32D4 for ; Mon, 28 Sep 2026 11:14:26 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 66D3B68A6B; Mon, 28 Sep 2026 13:14:22 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="Ll0Sj26E"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 7E34868A50 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 34F74B07; Mon, 28 Sep 2026 13:12:25 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1790593945; bh=wROGbeJx7zGkWhpfT5mI4iS3iBybvaOX4VhvlhlCN/U=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=Ll0Sj26EZzROqpt0CgiBnXgRXuVEttMwkksg66A/d1sek3+8vpH7HNiZfa0m0wq9l zdY++WQykCZNefd/LGKYzs7nMBeUy8UFgMKC9kZHWKrBfC3xX7M8y4gcisQsDodI+/ UHzNfz4ZcTBAsiyVvdJN3BHEEgjftjwLBT4HVp8E= From: Daniel Scally Date: Mon, 28 Sep 2026 12:14:00 +0100 Subject: [PATCH 4/5] ipa: libipa: awb: Update manual gains with automatic MIME-Version: 1.0 Message-Id: <20260928-awb-state-v1-4-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=2475; i=dan.scally@ideasonboard.com; h=from:subject:message-id; bh=wROGbeJx7zGkWhpfT5mI4iS3iBybvaOX4VhvlhlCN/U=; b=owEBbQKS/ZANAwAKAchJV3psRXUyAcsmYgBqukwFv7bX7eE8O2gIlazQR3vRuHz31oegLW5uK HAt3jIpU9mJAjMEAAEKAB0WIQQqyuwyDnZdb+mxmm/ISVd6bEV1MgUCarpMBQAKCRDISVd6bEV1 MhthD/9U5F3YkE71+bEhzNdjovyHVdvqavb9XhGNq3lTI9jlUt7XWq7OQbQOJn7oihn+y4aMTNE Vw+H6xeVtI8eWA3ACdMxCqnmyW5khkqvXSxfpY2k+wjIZYuQtKEDIIlpsdo0Yf0XX4dQu2ERkZo rYGmxHhn10WjDzE1X8GfQmEYW5nJtbSilkVZ+MYMNy9QN5VJwhx5IK052mQMpr3zaFLvWWZby/K XWJJDQYRYV5PKnM7uidLaITWTrA4vt3JDgbGxAlU4ppmPus2Mu3dGeXzCZRPMTbvU2+K+/Z1wr9 h+CJkRSC5xm56hvxaJrI30ADHfHVn0/F7hawIE9rn/cs0K+llMF8RIZK++Bbql9e124veWHyF28 fQZ2z8YQ9rjUd7aYZTHhl5lT7onhl13oH7uP6pccJ0EUaAhKbpK/MrowYhZn/ksZope6mENS+Tb ydvDd2qazzCwpUFoBiGkydj6NaOgv7uanWCAtCXTc3xhgxkvBDpTyDxxW/suuS+wsfIzrZ/mh7L Dh6Wwc408gnJK4BMjQgT5hbjktFGJ2Q6HW8Aye+nS4y1AuxVMxEQL/WPPwn+HDVAPrO+zdaVPdd wARmjGtGZVGj4LwxWbE8ABEnWbJilQ9MBnN6SUrm/TTyEv/PE8GN7X9yFkWaQaW6elt3umpsgZ6 TZdVnZcg/9J7WKw== 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" The present behaviour of the AWB algorithm is to decouple manual and automatic gains completely. If AwbEnable is set to false, this leads to a jarring transition to the (possibly default 1.0) manual gains. Instead update the manual gains with the automatic ones if we're in automatic mode so that when AwbEnable is set to false the gains are seen to "freeze" at their current values rather than revert to the last configured manual value. As this is the exact behaviour of the AwbStateLocked value of the AwbState metadata item, set that when AwbEnable == false is passed. Signed-off-by: Daniel Scally --- src/ipa/libipa/awb.cpp | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/src/ipa/libipa/awb.cpp b/src/ipa/libipa/awb.cpp index d1a75020843a03b3530dce4b6bd82f228ec6bed9..0c6a01bd780ce00d21c1f7fdd8ba2ed468c4f4a1 100644 --- a/src/ipa/libipa/awb.cpp +++ b/src/ipa/libipa/awb.cpp @@ -246,12 +246,14 @@ int AwbAlgorithmBase::init(const ValueNode &tuningData) */ int AwbAlgorithmBase::configure(awb::ActiveState &state) { - state.manual.gains = RGB{ 1.0 }; auto gains = impl_->gainsFromColourTemperature(kDefaultColourTemperature); - if (gains) + if (gains) { + state.manual.gains = *gains; state.automatic.gains = *gains; - else + } else { + state.manual.gains = RGB{ 1.0 }; state.automatic.gains = RGB{ 1.0 }; + } state.autoEnabled = true; state.manual.colourTemperature = kDefaultColourTemperature; @@ -286,6 +288,10 @@ void AwbAlgorithmBase::queueRequest(awb::ActiveState &state, const auto &awbEnable = controls.get(controls::AwbEnable); if (awbEnable && *awbEnable != state.autoEnabled) { state.autoEnabled = *awbEnable; + if (state.autoEnabled) + convergedState_ = controls::AwbStateEnum::AwbStateSearching; + else + convergedState_ = controls::AwbStateEnum::AwbStateLocked; LOG(Awb, Debug) << (*awbEnable ? "Enabling" : "Disabling") << " Awb"; @@ -445,6 +451,9 @@ void AwbAlgorithmBase::process(awb::ActiveState &state, state.automatic.colourTemperature = awbResult.colourTemperature; state.automatic.gains = newGains; + if (state.autoEnabled) + state.manual.gains = newGains; + /* Populate metadata. */ metadata.set(controls::AwbEnable, frameContext.autoEnabled); metadata.set(controls::ColourGains, { static_cast(frameContext.gains.r()), From patchwork Mon Sep 28 11:14:01 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Dan Scally X-Patchwork-Id: 28391 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 21CA7BDCBC for ; Mon, 28 Sep 2026 11:14:27 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id DD50968A43; Mon, 28 Sep 2026 13:14:23 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="mw+UUH8p"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id AD03168A51 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 709C0C24; Mon, 28 Sep 2026 13:12:25 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1790593945; bh=AIdl7JndoiShxJBnbhZgI3egG9CKzymwaobBMbbGXD4=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=mw+UUH8p4XHNAVwRxeUenS9DPhDGI9dICdOg2f2gnXvOQZWR8nVpjXIwUxq/ukZ5W N/ukpim0f7ixxA0WqkiPbPBLySw/iyn0wtGVTsKjzKoNNshEM0OELdaNZw04E9065t ei/ih8s71CnGVTGxOdAafpMrABOwOqMRNbcft/Qw= From: Daniel Scally Date: Mon, 28 Sep 2026 12:14:01 +0100 Subject: [PATCH 5/5] ipa: libipa: awb: Add AwbTrigger control MIME-Version: 1.0 Message-Id: <20260928-awb-state-v1-5-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=2727; i=dan.scally@ideasonboard.com; h=from:subject:message-id; bh=AIdl7JndoiShxJBnbhZgI3egG9CKzymwaobBMbbGXD4=; b=owEBbQKS/ZANAwAKAchJV3psRXUyAcsmYgBqukwF9bIy4BO6JVcLAmXkMLMcRuI9zi6WIifgT H8VRKDALuCJAjMEAAEKAB0WIQQqyuwyDnZdb+mxmm/ISVd6bEV1MgUCarpMBQAKCRDISVd6bEV1 Mvw9EACR3sNAqSKZ+04jHdUg41lvFZPH58V2+k622jf6PeIr3VaSphznArENuulSvl0fd25e/gz oVH9SbHWZLSA+rJfTUJDWjCmOHtFtWdD8RHWxvjw+9ZEY+61f6QL7yOSKaCEUqkKkcOqNwZ/cx3 1CQWr8uU/n9k9+AkczVotZLTSQE+ZYAkCjDb2G3XDaPP2e//yNO1H25SSDdwWGTPwKbrNj7Nu3r FTl603BHM5Lor1uacPaYiS1fp3OWaJ7VXmtbSkYws2pNirUbrvcu8J/8pJba1Zf3FKQ9rlO2wyT WfyrdrDJYIlVYqTEg8Laa6sx5cr0JrDGUAOFCPC3AwXC38vnQh+0LOF3zBmcHQjWzBsHHv5hkK3 oZNM1BK3iQskPz9n1+Qhd/98SDD43U+d7C9WW9J6HLxofQfb+r8qEWSkQ48F54rhMMhZEguYv1Z MWrBUlj2mjlXsChL3pSbseuJLwcARzDEgX1Qh0mExz1nATmD34qBpAgp384+kzzfpExVUHXFzrY ZlY5fVAY3dcHHRimsdh4E5skyXimkfblM/cHAIc7WtypTg8dtwQeAQZimOSf4kvurI2q3WPiTs8 pDtGINfFIY6Mx6sLNXnKk182ppJf3u4x/jmAY0u3L+zvuq/lhJFxRWAQJLEv9xjgg+9bcpR1MY8 VDu6Wuvx4dySHng== 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" Add the AwbTrigger control, which works when AwbEnable == false and allows the algorithm to settle into the Converged state before updating the manual gains and applying them. Signed-off-by: Daniel Scally --- src/ipa/libipa/awb.cpp | 18 +++++++++++++++++- src/ipa/libipa/awb.h | 1 + 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/src/ipa/libipa/awb.cpp b/src/ipa/libipa/awb.cpp index 0c6a01bd780ce00d21c1f7fdd8ba2ed468c4f4a1..2001275fc82c91172a86c5bda5925bb624da414d 100644 --- a/src/ipa/libipa/awb.cpp +++ b/src/ipa/libipa/awb.cpp @@ -235,6 +235,7 @@ int AwbAlgorithmBase::init(const ValueNode &tuningData) ControlInfo(kMinColourTemperature, kMaxColourTemperature, kDefaultColourTemperature); controls_[&controls::AwbEnable] = ControlInfo(false, true); + controls_[&controls::AwbTrigger] = ControlInfo(false, true); return parseModeConfigs(tuningData, controls::AwbAuto); } @@ -308,6 +309,13 @@ void AwbAlgorithmBase::queueRequest(awb::ActiveState &state, currentMode_ = &it->second; } + auto trigger = controls.get(controls::AwbTrigger); + if (trigger && *trigger && !state.autoEnabled) { + lockedCount_ = 0; + convergedState_ = controls::AwbStateEnum::AwbStateSearching; + rescanning_ = true; + } + frameContext.autoEnabled = state.autoEnabled; if (frameContext.autoEnabled) @@ -447,13 +455,21 @@ void AwbAlgorithmBase::process(awb::ActiveState &state, RGB newGains = awbResult.gains * speed + state.automatic.gains * (1 - speed); - updateConvergedState(state.automatic.gains, newGains); + if (state.autoEnabled || rescanning_) + updateConvergedState(state.automatic.gains, newGains); state.automatic.colourTemperature = awbResult.colourTemperature; state.automatic.gains = newGains; if (state.autoEnabled) state.manual.gains = newGains; + if (rescanning_ && + convergedState_ == controls::AwbStateEnum::AwbStateConverged) { + state.manual.gains = newGains; + rescanning_ = false; + convergedState_ = controls::AwbStateEnum::AwbStateLocked; + } + /* Populate metadata. */ metadata.set(controls::AwbEnable, frameContext.autoEnabled); metadata.set(controls::ColourGains, { static_cast(frameContext.gains.r()), diff --git a/src/ipa/libipa/awb.h b/src/ipa/libipa/awb.h index 5d80f241ec92280e867af5f9724471c5784683d1..588d8c5a9fb179a0c7a11143659fa400befa60b0 100644 --- a/src/ipa/libipa/awb.h +++ b/src/ipa/libipa/awb.h @@ -112,6 +112,7 @@ private: controls::AwbStateEnum convergedState_; RGB convergedGains_; unsigned int lockedCount_; + bool rescanning_; }; template