[RFC,v2,1/1] ipa: libipa: awb: Allow tuning files to lower the minimum gain
diff mbox series

Message ID 20260829093523.41754-2-dmanresa@gmail.com
State New
Headers show
Series
  • ipa: libipa: awb: Support modules calibrated with gains below 1.0
Related show

Commit Message

D. Manresa Aug. 29, 2026, 9:35 a.m. UTC
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 <dmanresa@gmail.com>
---
 src/ipa/libipa/awb.cpp |  5 +++++
 src/ipa/libipa/awb.h   | 16 +++++++++++++++-
 2 files changed, 20 insertions(+), 1 deletion(-)

Patch
diff mbox series

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 <algorithm>
 #include <array>
 #include <map>
 #include <optional>
@@ -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<float>(1.0f);
+		gainMin_ = std::clamp(gainMin, Q::TraitsType::min,
+				      Q::TraitsType::max);
 		gainMax_ = Q::TraitsType::max;
 
 		controls_[&controls::ColourGains] =