[v2,02/10] ipa: simple: awb: Add temporal smoothing and per-channel gain limits
diff mbox series

Message ID 20260506230722.1041596-3-devve.3@gmail.com
State New
Headers show
Series
  • OV01A10 sensor support and simple IPA improvements
Related show

Commit Message

devve May 6, 2026, 11:07 p.m. UTC
Add configurable YAML parameters maxGainR, maxGainB, and speed to AWB.
Replace the single hardcoded max gain (4.0x) with per-channel limits,
and apply exponential moving average smoothing to reduce colour
temperature oscillation between frames.

Signed-off-by: d3vv3 <devve.3@gmail.com>
---
 src/ipa/simple/algorithms/awb.cpp | 34 ++++++++++++++++++++++++++-----
 src/ipa/simple/algorithms/awb.h   |  6 ++++++
 2 files changed, 35 insertions(+), 5 deletions(-)

Comments

Milan Zamazal May 11, 2026, 9:27 a.m. UTC | #1
d3vv3 <devve.3@gmail.com> writes:

> Add configurable YAML parameters maxGainR, maxGainB, and speed to AWB.
> Replace the single hardcoded max gain (4.0x) with per-channel limits,
> and apply exponential moving average smoothing to reduce colour
> temperature oscillation between frames.
>
> Signed-off-by: d3vv3 <devve.3@gmail.com>

Reviewed-by: Milan Zamazal <mzamazal@redhat.com>

> ---
>  src/ipa/simple/algorithms/awb.cpp | 34 ++++++++++++++++++++++++++-----
>  src/ipa/simple/algorithms/awb.h   |  6 ++++++
>  2 files changed, 35 insertions(+), 5 deletions(-)
>
> diff --git a/src/ipa/simple/algorithms/awb.cpp b/src/ipa/simple/algorithms/awb.cpp
> index f5c88ea6..937aabc8 100644
> --- a/src/ipa/simple/algorithms/awb.cpp
> +++ b/src/ipa/simple/algorithms/awb.cpp
> @@ -14,6 +14,8 @@
>  
>  #include <libcamera/control_ids.h>
>  
> +#include "libcamera/internal/yaml_parser.h"
> +
>  #include "libipa/colours.h"
>  #include "simple/ipa_context.h"
>  
> @@ -23,6 +25,21 @@ LOG_DEFINE_CATEGORY(IPASoftAwb)
>  
>  namespace ipa::soft::algorithms {
>  
> +int Awb::init([[maybe_unused]] IPAContext &context,
> +	      const ValueNode &tuningData)
> +{
> +	maxGainR_ = tuningData["maxGainR"].get<float>().value_or(4.0f);
> +	maxGainB_ = tuningData["maxGainB"].get<float>().value_or(4.0f);
> +	speed_ = tuningData["speed"].get<float>().value_or(1.0f);
> +
> +	LOG(IPASoftAwb, Debug)
> +		<< "AWB: maxGainR " << maxGainR_
> +		<< ", maxGainB " << maxGainB_
> +		<< ", speed " << speed_;
> +
> +	return 0;
> +}
> +
>  int Awb::configure(IPAContext &context,
>  		   [[maybe_unused]] const IPAConfigInfo &configInfo)
>  {
> @@ -84,14 +101,21 @@ void Awb::process(IPAContext &context,
>  	const RGB<uint64_t> sum = stats->sum_.max(offset + minValid) - offset;
>  
>  	/*
> -	 * Calculate red and blue gains for AWB.
> -	 * Clamp max gain at 4.0, this also avoids 0 division.
> +	 * Calculate red and blue gains for AWB. Clamp max gain to avoid
> +	 * division by zero and extreme color casts.
>  	 */
>  	auto &gains = context.activeState.awb.gains;
> +	float rawRGain = sum.r() <= sum.g() / maxGainR_ ? maxGainR_ :
> +				static_cast<float>(sum.g()) / sum.r();
> +	float rawBGain = sum.b() <= sum.g() / maxGainB_ ? maxGainB_ :
> +				static_cast<float>(sum.g()) / sum.b();
> +
> +	/* Apply temporal smoothing to avoid rapid white balance changes. */
> +	float alpha = std::clamp(speed_, 0.01f, 1.0f);
>  	gains = { {
> -		sum.r() <= sum.g() / 4 ? 4.0f : static_cast<float>(sum.g()) / sum.r(),
> -		1.0,
> -		sum.b() <= sum.g() / 4 ? 4.0f : static_cast<float>(sum.g()) / sum.b(),
> +		gains.r() * (1.0f - alpha) + rawRGain * alpha,
> +		1.0f,
> +		gains.b() * (1.0f - alpha) + rawBGain * alpha,
>  	} };
>  
>  	RGB<double> rgbGains{ { 1 / gains.r(), 1 / gains.g(), 1 / gains.b() } };
> diff --git a/src/ipa/simple/algorithms/awb.h b/src/ipa/simple/algorithms/awb.h
> index ad993f39..0aedc1d1 100644
> --- a/src/ipa/simple/algorithms/awb.h
> +++ b/src/ipa/simple/algorithms/awb.h
> @@ -19,6 +19,7 @@ public:
>  	Awb() = default;
>  	~Awb() = default;
>  
> +	int init(IPAContext &context, const ValueNode &tuningData) override;
>  	int configure(IPAContext &context, const IPAConfigInfo &configInfo) override;
>  	void prepare(IPAContext &context,
>  		     const uint32_t frame,
> @@ -29,6 +30,11 @@ public:
>  		     IPAFrameContext &frameContext,
>  		     const SwIspStats *stats,
>  		     ControlList &metadata) override;
> +
> +private:
> +	float maxGainR_;
> +	float maxGainB_;
> +	float speed_;
>  };
>  
>  } /* namespace ipa::soft::algorithms */

Patch
diff mbox series

diff --git a/src/ipa/simple/algorithms/awb.cpp b/src/ipa/simple/algorithms/awb.cpp
index f5c88ea6..937aabc8 100644
--- a/src/ipa/simple/algorithms/awb.cpp
+++ b/src/ipa/simple/algorithms/awb.cpp
@@ -14,6 +14,8 @@ 
 
 #include <libcamera/control_ids.h>
 
+#include "libcamera/internal/yaml_parser.h"
+
 #include "libipa/colours.h"
 #include "simple/ipa_context.h"
 
@@ -23,6 +25,21 @@  LOG_DEFINE_CATEGORY(IPASoftAwb)
 
 namespace ipa::soft::algorithms {
 
+int Awb::init([[maybe_unused]] IPAContext &context,
+	      const ValueNode &tuningData)
+{
+	maxGainR_ = tuningData["maxGainR"].get<float>().value_or(4.0f);
+	maxGainB_ = tuningData["maxGainB"].get<float>().value_or(4.0f);
+	speed_ = tuningData["speed"].get<float>().value_or(1.0f);
+
+	LOG(IPASoftAwb, Debug)
+		<< "AWB: maxGainR " << maxGainR_
+		<< ", maxGainB " << maxGainB_
+		<< ", speed " << speed_;
+
+	return 0;
+}
+
 int Awb::configure(IPAContext &context,
 		   [[maybe_unused]] const IPAConfigInfo &configInfo)
 {
@@ -84,14 +101,21 @@  void Awb::process(IPAContext &context,
 	const RGB<uint64_t> sum = stats->sum_.max(offset + minValid) - offset;
 
 	/*
-	 * Calculate red and blue gains for AWB.
-	 * Clamp max gain at 4.0, this also avoids 0 division.
+	 * Calculate red and blue gains for AWB. Clamp max gain to avoid
+	 * division by zero and extreme color casts.
 	 */
 	auto &gains = context.activeState.awb.gains;
+	float rawRGain = sum.r() <= sum.g() / maxGainR_ ? maxGainR_ :
+				static_cast<float>(sum.g()) / sum.r();
+	float rawBGain = sum.b() <= sum.g() / maxGainB_ ? maxGainB_ :
+				static_cast<float>(sum.g()) / sum.b();
+
+	/* Apply temporal smoothing to avoid rapid white balance changes. */
+	float alpha = std::clamp(speed_, 0.01f, 1.0f);
 	gains = { {
-		sum.r() <= sum.g() / 4 ? 4.0f : static_cast<float>(sum.g()) / sum.r(),
-		1.0,
-		sum.b() <= sum.g() / 4 ? 4.0f : static_cast<float>(sum.g()) / sum.b(),
+		gains.r() * (1.0f - alpha) + rawRGain * alpha,
+		1.0f,
+		gains.b() * (1.0f - alpha) + rawBGain * alpha,
 	} };
 
 	RGB<double> rgbGains{ { 1 / gains.r(), 1 / gains.g(), 1 / gains.b() } };
diff --git a/src/ipa/simple/algorithms/awb.h b/src/ipa/simple/algorithms/awb.h
index ad993f39..0aedc1d1 100644
--- a/src/ipa/simple/algorithms/awb.h
+++ b/src/ipa/simple/algorithms/awb.h
@@ -19,6 +19,7 @@  public:
 	Awb() = default;
 	~Awb() = default;
 
+	int init(IPAContext &context, const ValueNode &tuningData) override;
 	int configure(IPAContext &context, const IPAConfigInfo &configInfo) override;
 	void prepare(IPAContext &context,
 		     const uint32_t frame,
@@ -29,6 +30,11 @@  public:
 		     IPAFrameContext &frameContext,
 		     const SwIspStats *stats,
 		     ControlList &metadata) override;
+
+private:
+	float maxGainR_;
+	float maxGainB_;
+	float speed_;
 };
 
 } /* namespace ipa::soft::algorithms */