From patchwork Sat Aug 29 09:35:23 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "D. Manresa" X-Patchwork-Id: 28144 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 301C3C3341 for ; Sat, 29 Aug 2026 09:35:31 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 23E6E684B0; Sat, 29 Aug 2026 11:35:30 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.b="EP/tVw+y"; dkim-atps=neutral Received: from mail-wm1-x32a.google.com (mail-wm1-x32a.google.com [IPv6:2a00:1450:4864:20::32a]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 93E7668450 for ; Sat, 29 Aug 2026 11:35:27 +0200 (CEST) Received: by mail-wm1-x32a.google.com with SMTP id 5b1f17b1804b1-4921eed3fa2so16190465e9.0 for ; Sat, 29 Aug 2026 02:35:27 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1787996127; x=1788600927; 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:content-type; bh=29COOdP99R4+S1ZcYflLTmFrqqCxGiymFjxM5c0p+q0=; b=EP/tVw+ylqpRNZgByeSr1VR3BQhrsHwGCuGyLu02xMegc92VfUhZjDI/b2OmeEge6j 5ksHFc7v4kXDjZwSTPPPK0zwNLoCc2XjaCataYwwzG6NHdThCCx+zA+yV8JA6B0PsUZB CERJMEMMoKYtzSCf80RtklfQ43AXmFi/TSO8hmuf7fW+Jrl23+4WJ4D1x4yVA/rLI+BC J31gWzvvF6RojmhM2zIcGYBwFQBsNPgJOXMX3xyUUJonKjTWr2q0Ntp0qkR44MUokmgo VZbJdWNuFrADuLJYczcsroczgz8QNIG4TkPbcKCBiZYG1px9HqHDE+I0Bz1qebXOAaC2 J/SQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1787996127; x=1788600927; 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:content-type; bh=29COOdP99R4+S1ZcYflLTmFrqqCxGiymFjxM5c0p+q0=; b=X7e83eBiIaJhnzml5MEZxT78yAvW7H1Yb/Fz+eRCki9/6mruvMwc74i+UsgFX8N67x AS9ixqrlQBBn4MbJeZmQu3Nhg7G6iNoC8F97PPGrKPwHoRj/0i89o8eYcA2fT1S4i12x +KL4x06kPNz9GnmXs0h8tq+OeicrjuKRROVFSvZexHYwK9v9fYt73CnJapOVfW0yX1s6 pzs4pQnEbcypSrjqArbvCfxiIw+jkgL0KT2hVSDsrz6K3vJcPMglF82sGD5IVmpwT1hr hy/EYBCO1Tu6GqD7wwceXGPFlBdwV4za5LwIbGhhI2Si4l5xvnu+OW5Yo5NufvIj9E9C 6riQ== X-Gm-Message-State: AFuF++nZ6HwGl2tOsLoNT2tL4C4jLeF0fHHrtYrOviKlp0M8zo/NcnZK qqKHqP/UvKYb6xfPyqDcLOV3DIpwRvXyYBSFnwviTqkVySDKdRUIKcPXMEkjwhCccgZs X-Gm-Gg: AR+sD11csxBouYSoYsuR97QT4UCkVGWtHMECViz1CMzAC/+hnAvpMKlRHgHqI/mkwSR U+aEP3VyI2r0Yv6f2bb/9TX4NhvUW7HgyTf6ezLzBEZ/xD9IkCb4iJQnJwCTmBZ7A5wX8Sig5Rs DaaFNbWSDa0wbk8ugjEes3G9GVtSYOos6PamTpFozJp+XOoLgHerzB0B8qhVUW3yJs06y2IIYLq PxhgIGBzwMQUtoc9KKcvM7KXnlCBYQ5d0cRVSPkjt9LyAXNz39VGROUzul1b8Ngbtq9hPMChlxS fq13aTH/Umj1Ezhgb3mm8teC7KeLE9QOs2ijlesXDwxkFyrpaP5U3WHSACM1HTX6InQQGc0hMC3 zBTYcgLpZECNIqORdtnX4hy8SaIK5+xLduI5gSE6RW0D0BNExauvkLucqDOReQD7IQnrynAUoS5 GONiT+1e77rhfDHDaE17alOeYYPassrSNj7pBdVwwBGYqTOV1a1NVX/xGKtKLgRboBBpMWeodj9 AAtRDuUf8rw X-Received: by 2002:a05:600c:a418:b0:49b:9433:ea44 with SMTP id 5b1f17b1804b1-49b9433eb4dmr171256595e9.4.1787996126943; Sat, 29 Aug 2026 02:35:26 -0700 (PDT) Received: from surface.. ([217.61.227.23]) by smtp.gmail.com with ESMTPSA id 5b1f17b1804b1-49b9617179asm51181995e9.2.2026.08.29.02.35.25 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sat, 29 Aug 2026 02:35:26 -0700 (PDT) From: "D. Manresa" To: libcamera-devel@lists.libcamera.org Cc: Jacopo Mondi , Kieran Bingham , Milan Zamazal , Hans de Goede , =?utf-8?q?Barnab=C3=A1s_P=C5=91cze?= , "D . Manresa" Subject: [RFC PATCH v2 1/1] ipa: libipa: awb: Allow tuning files to lower the minimum gain Date: Sat, 29 Aug 2026 11:35:23 +0200 Message-ID: <20260829093523.41754-2-dmanresa@gmail.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260829093523.41754-1-dmanresa@gmail.com> References: <20260828140629.128831-1-dmanresa@gmail.com> <20260829093523.41754-1-dmanresa@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" AwbAlgorithm clamps the computed white balance gains to a minimum of 1.0. The floor is a sensible default, as attenuating a colour channel discards part of its dynamic range and balancing by amplifying the other channels is normally preferable, but it is applied even on platforms whose gain range does go below 1.0, most notably the software ISP which applies the gains in floating point during debayering. Some camera modules are calibrated with a white point that genuinely requires attenuating a channel. The front OV5693 of the Microsoft Surface Pro 7+ is one such module: the OEM calibration Microsoft ships in the public Surface driver package (the Intel CPFF/.aiqb container OV5693_MSHW0220_TGL.aiqb) stores per-illuminant sensor white points, and decoding them yields a red gain of 0.87 at 2592 K (illuminant A). Raw captures of the sensor under an incandescent lamp confirm the calibration: the red channel averages 1.15x the green channel on a white surface. With the floor in place the algorithm cannot reach the calibrated white point under warm light, and the picture keeps a red cast that no tuning can remove. Keep the 1.0 default and let the tuning file lower the limit with a new optional "gainMin" property, clamped to the gain range of the platform AWB engine. Platforms and sensors that do not opt in behave exactly as before. An alternative would be to drop the floor entirely and trust the platform gain range alone, but hardware IPA modules currently reuse their gain register format (e.g. UQ2.8) as the algorithm limits, and their pipelines may rely on gains never attenuating. An opt-in tuning property keeps the default behaviour unchanged everywhere. Developed with the assistance of an AI tool (Claude) and verified on a Surface Pro 7+ (software ISP, OEM-derived colour tuning) against the Windows 11 rendition of the same scenes. Signed-off-by: D. Manresa --- src/ipa/libipa/awb.cpp | 5 +++++ src/ipa/libipa/awb.h | 16 +++++++++++++++- 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/src/ipa/libipa/awb.cpp b/src/ipa/libipa/awb.cpp index da835be..1c6f34d 100644 --- a/src/ipa/libipa/awb.cpp +++ b/src/ipa/libipa/awb.cpp @@ -526,6 +526,11 @@ int AwbAlgorithmBase::parseModeConfigs(const ValueNode &tuningData, * Minimum gain value used to clamp the AWB algorithm calculation results in the * range supported by the platform AWB engine. * + * The minimum defaults to 1.0: gains below 1.0 attenuate a colour channel, + * which is normally undesirable. On platforms whose gain range goes below + * 1.0, the "gainMin" tuning file property may lower the limit, within the + * range supported by the platform AWB engine. + * * The min and max gain values are initialized by AwbAlgorithm::init(). */ diff --git a/src/ipa/libipa/awb.h b/src/ipa/libipa/awb.h index d35a851..9d6fa3c 100644 --- a/src/ipa/libipa/awb.h +++ b/src/ipa/libipa/awb.h @@ -7,6 +7,7 @@ #pragma once +#include #include #include #include @@ -117,7 +118,20 @@ public: { AwbAlgorithmBase::init(tuningData); - gainMin_ = std::max(Q::TraitsType::min, 1.0f); + /* + * Gains are not allowed to go below 1.0 by default: attenuating + * a colour channel discards part of its dynamic range, so + * balancing by amplifying the other channels is normally + * preferable. Some camera modules are however calibrated with + * a white point that requires attenuating a channel, which + * platforms whose AWB gains can go below 1.0 (such as the + * software ISP) are able to apply. Let the tuning file lower + * the limit with the "gainMin" property, within the platform's + * gain range. + */ + float gainMin = tuningData["gainMin"].get(1.0f); + gainMin_ = std::clamp(gainMin, Q::TraitsType::min, + Q::TraitsType::max); gainMax_ = Q::TraitsType::max; controls_[&controls::ColourGains] =