[RFC,v1,17/17] ipa: simple: agc: Use `AgcMeanLuminance` if sensor helper is available
diff mbox series

Message ID 20260703153819.1088752-18-barnabas.pocze@ideasonboard.com
State Superseded
Headers show
Series
  • ipa: libipa: agc rework
Related show

Commit Message

Barnabás Pőcze July 3, 2026, 3:38 p.m. UTC
Signed-off-by: Barnabás Pőcze <barnabas.pocze@ideasonboard.com>
---
 src/ipa/simple/algorithms/agc.cpp | 153 +++++++++++++++++++++++++-----
 src/ipa/simple/algorithms/agc.h   |   9 +-
 src/ipa/simple/ipa_context.h      |   4 +
 3 files changed, 143 insertions(+), 23 deletions(-)

Comments

Barnabás Pőcze July 15, 2026, 2:48 p.m. UTC | #1
2026. 07. 03. 17:38 keltezéssel, Barnabás Pőcze írta:
> Signed-off-by: Barnabás Pőcze <barnabas.pocze@ideasonboard.com>
> ---
>   src/ipa/simple/algorithms/agc.cpp | 153 +++++++++++++++++++++++++-----
>   src/ipa/simple/algorithms/agc.h   |   9 +-
>   src/ipa/simple/ipa_context.h      |   4 +
>   3 files changed, 143 insertions(+), 23 deletions(-)
> 
> diff --git a/src/ipa/simple/algorithms/agc.cpp b/src/ipa/simple/algorithms/agc.cpp
> index db4f63eba5..7d035618e8 100644
> --- a/src/ipa/simple/algorithms/agc.cpp
> +++ b/src/ipa/simple/algorithms/agc.cpp
> @@ -7,7 +7,12 @@
> 
>   #include "agc.h"
> 
> +#include <variant>
> +
>   #include <libcamera/base/log.h>
> +#include <libcamera/base/utils.h>
> +
> +#include <libipa/histogram.h>
> 
>   namespace libcamera {
> 
> @@ -15,38 +20,119 @@ LOG_DEFINE_CATEGORY(IPASoftExposure)
> 
>   namespace ipa::soft::algorithms {
> 
> -int Agc::init(IPAContext &context, [[maybe_unused]] const ValueNode &tuningData)
> +namespace {
> +
> +class AgcTraits : public AgcMeanLuminance::Traits
>   {
> -	return agc_.configure(context.configuration.agc.simple, context.activeState.agc.simple, {
> +public:
> +	AgcTraits(const SwIspStats &stats)
> +		: stats_(stats)
> +	{
> +	}
> +
> +	double estimateLuminance(double gain) const override
> +	{
> +		double sum = 0;
> +		double count = 0;
> +
> +		for (const auto &[i, cnt] : utils::enumerate(stats_.yHistogram)) {
> +			sum += std::min(1.0, gain * i / stats_.yHistogram.size()) * cnt;
> +			count += cnt;
> +		}
> +
> +		return sum / count;
> +	}
> +
> +private:
> +	const SwIspStats &stats_;
> +};
> +
> +}
> +
> +int Agc::init(IPAContext &context, const ValueNode &tuningData)
> +{
> +	const AgcAlgorithm::ConfigurationParams config = {
>   		.sensor = context.camHelper.get(),
>   		.sensorInfo = context.sensorInfo,
>   		.sensorControls = context.sensorControls,
>   		.ctrlMap = context.ctrlMap,
>   		.autoAllowed = true,
> -	});
> +	};
> +
> +	if (config.sensor)
> +		agc_.emplace<AgcMeanLuminanceAlgorithm>();
> +	else
> +		agc_.emplace<AgcSimpleAlgorithm>();
> +
> +	return std::visit(utils::overloaded {
> +		[&](AgcSimpleAlgorithm &impl) {
> +			return impl.configure(context.configuration.agc.simple,
> +					      context.activeState.agc.simple,
> +					      config);
> +		},
> +		[&](AgcMeanLuminanceAlgorithm &impl) {
> +			int ret = impl.init(tuningData);
> +			if (ret)
> +				return ret;
> +
> +			return impl.configure(context.configuration.agc.ml,
> +					      context.activeState.agc.ml,
> +					      config);
> +		},
> +	}, agc_);
>   }
> 
>   int Agc::configure(IPAContext &context, [[maybe_unused]] const IPAConfigInfo &configInfo)
>   {
> -	return agc_.configure(context.configuration.agc.simple, context.activeState.agc.simple, {
> +	const AgcAlgorithm::ConfigurationParams config = {
>   		.sensor = context.camHelper.get(),
>   		.sensorInfo = context.sensorInfo,
>   		.sensorControls = context.sensorControls,
>   		.ctrlMap = context.ctrlMap,
> -		.autoAllowed = true, // \todo not if raw?
> -	});
> +		.autoAllowed = true, // \todo if not raw?
> +	};
> +
> +	return std::visit(utils::overloaded {
> +		[&](AgcSimpleAlgorithm &impl) {
> +			return impl.configure(context.configuration.agc.simple,
> +					      context.activeState.agc.simple,
> +					      config);
> +		},
> +		[&](AgcMeanLuminanceAlgorithm &impl) {
> +			return impl.configure(context.configuration.agc.ml,
> +					      context.activeState.agc.ml,
> +					      config);
> +		},
> +	}, agc_);
>   }
> 
>   void Agc::queueRequest(IPAContext &context, [[maybe_unused]] const uint32_t frame, IPAFrameContext &frameContext, const ControlList &controls)
>   {
> -	agc_.queueRequest(context.configuration.agc.simple, context.activeState.agc.simple,
> -			  frameContext.agc.simple, controls);
> +	std::visit(utils::overloaded {
> +		[&](AgcSimpleAlgorithm &impl) {
> +			impl.queueRequest(context.configuration.agc.simple,
> +					  context.activeState.agc.simple,
> +					  frameContext.agc.simple, controls);
> +		},
> +		[&](AgcMeanLuminanceAlgorithm &impl) {
> +			impl.queueRequest(context.configuration.agc.ml,
> +					  context.activeState.agc.ml,
> +					  frameContext.agc.ml, controls);
> +		},
> +	}, agc_);
>   }
> 
>   void Agc::prepare(IPAContext &context, [[maybe_unused]] const uint32_t frame,
>   		  IPAFrameContext &frameContext, [[maybe_unused]] DebayerParams *params)
>   {
> -	agc_.prepare(context.activeState.agc.simple, frameContext.agc.simple);
> +	std::visit(utils::overloaded {
> +		[&](AgcSimpleAlgorithm &impl) {
> +			impl.prepare(context.activeState.agc.simple, frameContext.agc.simple);
> +		},
> +		[&](AgcMeanLuminanceAlgorithm &impl) {
> +			impl.prepare(context.activeState.agc.ml, frameContext.agc.ml);
> +		},
> +	}, agc_);
>   }
> 
>   void Agc::process(IPAContext &context,
> @@ -55,20 +141,43 @@ void Agc::process(IPAContext &context,
>   		  const SwIspStats *stats,
>   		  ControlList &metadata)
>   {
> -	if (stats->valid) {
> -		agc_.process(context.configuration.agc.simple, context.activeState.agc.simple, frameContext.agc.simple, {{
> -			.exposure = frameContext.sensor.exposure,
> -			.gain = frameContext.sensor.gain,
> -			.stats = *stats,
> -			.blackLevel = context.activeState.blc.level,
> -		}}, metadata);
> -	} else {
> -		agc_.process(context.configuration.agc.simple, context.activeState.agc.simple,
> -			     frameContext.agc.simple, {}, metadata);
> -	}
> +	std::visit(utils::overloaded {
> +		[&](AgcSimpleAlgorithm &impl) {
> +			if (stats->valid) {
> +				impl.process(context.configuration.agc.simple, context.activeState.agc.simple, frameContext.agc.simple, {{
> +					.exposure = frameContext.sensor.exposure,
> +					.gain = frameContext.sensor.gain,
> +					.stats = *stats,
> +					.blackLevel = context.activeState.blc.level,
> +				}}, metadata);
> +			} else {
> +				impl.process(context.configuration.agc.simple, context.activeState.agc.simple,
> +					     frameContext.agc.simple, {}, metadata);
> +			}
> +
> +			frameContext.agc.exposure = frameContext.agc.simple.exposure;
> +			frameContext.agc.gain = frameContext.agc.simple.gain;
> +		},
> +		[&](AgcMeanLuminanceAlgorithm &impl) {
> +			if (stats->valid) {
> +				Histogram hist(stats->yHistogram);
> +
> +				impl.process(context.configuration.agc.ml, context.activeState.agc.ml, frameContext.agc.ml, {{
> +					.traits = AgcTraits(*stats),
> +					.hist = hist,
> +					.exposure = uint32_t(frameContext.sensor.exposure),
> +					.gain = frameContext.sensor.gain,
> +				}}, metadata);
> +
> +			} else {
> +				impl.process(context.configuration.agc.ml, context.activeState.agc.ml,
> +					     frameContext.agc.ml, {}, metadata);
> +			}
> 
> -	frameContext.agc.exposure = frameContext.agc.simple.exposure;
> -	frameContext.agc.gain = frameContext.agc.simple.gain;
> +			frameContext.agc.exposure = frameContext.agc.ml.exposure;
> +			frameContext.agc.gain = frameContext.agc.ml.gain;
> +		},
> +	}, agc_);
>   }
> 
>   REGISTER_IPA_ALGORITHM(Agc, "Agc")
> diff --git a/src/ipa/simple/algorithms/agc.h b/src/ipa/simple/algorithms/agc.h
> index 869817fc95..5dac17ab1d 100644
> --- a/src/ipa/simple/algorithms/agc.h
> +++ b/src/ipa/simple/algorithms/agc.h
> @@ -7,6 +7,10 @@
> 
>   #pragma once
> 
> +#include <variant>
> +
> +#include <libipa/agc_mean_luminance.h>
> +
>   #include "algorithm.h"
>   #include "agc_simple.h"
> 
> @@ -30,7 +34,10 @@ public:
>   		     ControlList &metadata) override;
> 
>   private:
> -	AgcSimpleAlgorithm agc_;
> +	std::variant<
> +		AgcSimpleAlgorithm,
> +		AgcMeanLuminanceAlgorithm
> +	> agc_;
>   };
> 
>   } /* namespace ipa::soft::algorithms */
> diff --git a/src/ipa/simple/ipa_context.h b/src/ipa/simple/ipa_context.h
> index 36e1d8bba8..b4a11b04cd 100644
> --- a/src/ipa/simple/ipa_context.h
> +++ b/src/ipa/simple/ipa_context.h
> @@ -15,6 +15,7 @@
>   #include "libcamera/internal/matrix.h"
>   #include "libcamera/internal/vector.h"
> 
> +#include <libipa/agc_mean_luminance.h>
>   #include <libipa/camera_sensor_helper.h>
>   #include <libipa/fc_queue.h>
> 
> @@ -29,6 +30,7 @@ namespace ipa::soft {
>   struct IPASessionConfiguration {
>   	struct {
>   		AgcSimpleAlgorithm::Session simple;
> +		AgcMeanLuminanceAlgorithm::Session ml;

This is of course greatly suboptimal, this should really be a `union`
or something similar.


>   		double again10, againMinStep;
>   	} agc;
>   	struct {
> @@ -39,6 +41,7 @@ struct IPASessionConfiguration {
>   struct IPAActiveState {
>   	struct {
>   		AgcSimpleAlgorithm::ActiveState simple;
> +		AgcMeanLuminanceAlgorithm::ActiveState ml;
>   	} agc;
> 
>   	struct {
> @@ -67,6 +70,7 @@ struct IPAFrameContext : public FrameContext {
> 
>   	struct {
>   		AgcSimpleAlgorithm::FrameContext simple;
> +		AgcMeanLuminanceAlgorithm::FrameContext ml;
>   		int32_t exposure;
>   		double gain;
>   	} agc;
> --
> 2.54.0
>

Patch
diff mbox series

diff --git a/src/ipa/simple/algorithms/agc.cpp b/src/ipa/simple/algorithms/agc.cpp
index db4f63eba5..7d035618e8 100644
--- a/src/ipa/simple/algorithms/agc.cpp
+++ b/src/ipa/simple/algorithms/agc.cpp
@@ -7,7 +7,12 @@ 
 
 #include "agc.h"
 
+#include <variant>
+
 #include <libcamera/base/log.h>
+#include <libcamera/base/utils.h>
+
+#include <libipa/histogram.h>
 
 namespace libcamera {
 
@@ -15,38 +20,119 @@  LOG_DEFINE_CATEGORY(IPASoftExposure)
 
 namespace ipa::soft::algorithms {
 
-int Agc::init(IPAContext &context, [[maybe_unused]] const ValueNode &tuningData)
+namespace {
+
+class AgcTraits : public AgcMeanLuminance::Traits
 {
-	return agc_.configure(context.configuration.agc.simple, context.activeState.agc.simple, {
+public:
+	AgcTraits(const SwIspStats &stats)
+		: stats_(stats)
+	{
+	}
+
+	double estimateLuminance(double gain) const override
+	{
+		double sum = 0;
+		double count = 0;
+
+		for (const auto &[i, cnt] : utils::enumerate(stats_.yHistogram)) {
+			sum += std::min(1.0, gain * i / stats_.yHistogram.size()) * cnt;
+			count += cnt;
+		}
+
+		return sum / count;
+	}
+
+private:
+	const SwIspStats &stats_;
+};
+
+}
+
+int Agc::init(IPAContext &context, const ValueNode &tuningData)
+{
+	const AgcAlgorithm::ConfigurationParams config = {
 		.sensor = context.camHelper.get(),
 		.sensorInfo = context.sensorInfo,
 		.sensorControls = context.sensorControls,
 		.ctrlMap = context.ctrlMap,
 		.autoAllowed = true,
-	});
+	};
+
+	if (config.sensor)
+		agc_.emplace<AgcMeanLuminanceAlgorithm>();
+	else
+		agc_.emplace<AgcSimpleAlgorithm>();
+
+	return std::visit(utils::overloaded {
+		[&](AgcSimpleAlgorithm &impl) {
+			return impl.configure(context.configuration.agc.simple,
+					      context.activeState.agc.simple,
+					      config);
+		},
+		[&](AgcMeanLuminanceAlgorithm &impl) {
+			int ret = impl.init(tuningData);
+			if (ret)
+				return ret;
+
+			return impl.configure(context.configuration.agc.ml,
+					      context.activeState.agc.ml,
+					      config);
+		},
+	}, agc_);
 }
 
 int Agc::configure(IPAContext &context, [[maybe_unused]] const IPAConfigInfo &configInfo)
 {
-	return agc_.configure(context.configuration.agc.simple, context.activeState.agc.simple, {
+	const AgcAlgorithm::ConfigurationParams config = {
 		.sensor = context.camHelper.get(),
 		.sensorInfo = context.sensorInfo,
 		.sensorControls = context.sensorControls,
 		.ctrlMap = context.ctrlMap,
-		.autoAllowed = true, // \todo not if raw?
-	});
+		.autoAllowed = true, // \todo if not raw?
+	};
+
+	return std::visit(utils::overloaded {
+		[&](AgcSimpleAlgorithm &impl) {
+			return impl.configure(context.configuration.agc.simple,
+					      context.activeState.agc.simple,
+					      config);
+		},
+		[&](AgcMeanLuminanceAlgorithm &impl) {
+			return impl.configure(context.configuration.agc.ml,
+					      context.activeState.agc.ml,
+					      config);
+		},
+	}, agc_);
 }
 
 void Agc::queueRequest(IPAContext &context, [[maybe_unused]] const uint32_t frame, IPAFrameContext &frameContext, const ControlList &controls)
 {
-	agc_.queueRequest(context.configuration.agc.simple, context.activeState.agc.simple,
-			  frameContext.agc.simple, controls);
+	std::visit(utils::overloaded {
+		[&](AgcSimpleAlgorithm &impl) {
+			impl.queueRequest(context.configuration.agc.simple,
+					  context.activeState.agc.simple,
+					  frameContext.agc.simple, controls);
+		},
+		[&](AgcMeanLuminanceAlgorithm &impl) {
+			impl.queueRequest(context.configuration.agc.ml,
+					  context.activeState.agc.ml,
+					  frameContext.agc.ml, controls);
+		},
+	}, agc_);
 }
 
 void Agc::prepare(IPAContext &context, [[maybe_unused]] const uint32_t frame,
 		  IPAFrameContext &frameContext, [[maybe_unused]] DebayerParams *params)
 {
-	agc_.prepare(context.activeState.agc.simple, frameContext.agc.simple);
+	std::visit(utils::overloaded {
+		[&](AgcSimpleAlgorithm &impl) {
+			impl.prepare(context.activeState.agc.simple, frameContext.agc.simple);
+		},
+		[&](AgcMeanLuminanceAlgorithm &impl) {
+			impl.prepare(context.activeState.agc.ml, frameContext.agc.ml);
+		},
+	}, agc_);
 }
 
 void Agc::process(IPAContext &context,
@@ -55,20 +141,43 @@  void Agc::process(IPAContext &context,
 		  const SwIspStats *stats,
 		  ControlList &metadata)
 {
-	if (stats->valid) {
-		agc_.process(context.configuration.agc.simple, context.activeState.agc.simple, frameContext.agc.simple, {{
-			.exposure = frameContext.sensor.exposure,
-			.gain = frameContext.sensor.gain,
-			.stats = *stats,
-			.blackLevel = context.activeState.blc.level,
-		}}, metadata);
-	} else {
-		agc_.process(context.configuration.agc.simple, context.activeState.agc.simple,
-			     frameContext.agc.simple, {}, metadata);
-	}
+	std::visit(utils::overloaded {
+		[&](AgcSimpleAlgorithm &impl) {
+			if (stats->valid) {
+				impl.process(context.configuration.agc.simple, context.activeState.agc.simple, frameContext.agc.simple, {{
+					.exposure = frameContext.sensor.exposure,
+					.gain = frameContext.sensor.gain,
+					.stats = *stats,
+					.blackLevel = context.activeState.blc.level,
+				}}, metadata);
+			} else {
+				impl.process(context.configuration.agc.simple, context.activeState.agc.simple,
+					     frameContext.agc.simple, {}, metadata);
+			}
+
+			frameContext.agc.exposure = frameContext.agc.simple.exposure;
+			frameContext.agc.gain = frameContext.agc.simple.gain;
+		},
+		[&](AgcMeanLuminanceAlgorithm &impl) {
+			if (stats->valid) {
+				Histogram hist(stats->yHistogram);
+
+				impl.process(context.configuration.agc.ml, context.activeState.agc.ml, frameContext.agc.ml, {{
+					.traits = AgcTraits(*stats),
+					.hist = hist,
+					.exposure = uint32_t(frameContext.sensor.exposure),
+					.gain = frameContext.sensor.gain,
+				}}, metadata);
+
+			} else {
+				impl.process(context.configuration.agc.ml, context.activeState.agc.ml,
+					     frameContext.agc.ml, {}, metadata);
+			}
 
-	frameContext.agc.exposure = frameContext.agc.simple.exposure;
-	frameContext.agc.gain = frameContext.agc.simple.gain;
+			frameContext.agc.exposure = frameContext.agc.ml.exposure;
+			frameContext.agc.gain = frameContext.agc.ml.gain;
+		},
+	}, agc_);
 }
 
 REGISTER_IPA_ALGORITHM(Agc, "Agc")
diff --git a/src/ipa/simple/algorithms/agc.h b/src/ipa/simple/algorithms/agc.h
index 869817fc95..5dac17ab1d 100644
--- a/src/ipa/simple/algorithms/agc.h
+++ b/src/ipa/simple/algorithms/agc.h
@@ -7,6 +7,10 @@ 
 
 #pragma once
 
+#include <variant>
+
+#include <libipa/agc_mean_luminance.h>
+
 #include "algorithm.h"
 #include "agc_simple.h"
 
@@ -30,7 +34,10 @@  public:
 		     ControlList &metadata) override;
 
 private:
-	AgcSimpleAlgorithm agc_;
+	std::variant<
+		AgcSimpleAlgorithm,
+		AgcMeanLuminanceAlgorithm
+	> agc_;
 };
 
 } /* namespace ipa::soft::algorithms */
diff --git a/src/ipa/simple/ipa_context.h b/src/ipa/simple/ipa_context.h
index 36e1d8bba8..b4a11b04cd 100644
--- a/src/ipa/simple/ipa_context.h
+++ b/src/ipa/simple/ipa_context.h
@@ -15,6 +15,7 @@ 
 #include "libcamera/internal/matrix.h"
 #include "libcamera/internal/vector.h"
 
+#include <libipa/agc_mean_luminance.h>
 #include <libipa/camera_sensor_helper.h>
 #include <libipa/fc_queue.h>
 
@@ -29,6 +30,7 @@  namespace ipa::soft {
 struct IPASessionConfiguration {
 	struct {
 		AgcSimpleAlgorithm::Session simple;
+		AgcMeanLuminanceAlgorithm::Session ml;
 		double again10, againMinStep;
 	} agc;
 	struct {
@@ -39,6 +41,7 @@  struct IPASessionConfiguration {
 struct IPAActiveState {
 	struct {
 		AgcSimpleAlgorithm::ActiveState simple;
+		AgcMeanLuminanceAlgorithm::ActiveState ml;
 	} agc;
 
 	struct {
@@ -67,6 +70,7 @@  struct IPAFrameContext : public FrameContext {
 
 	struct {
 		AgcSimpleAlgorithm::FrameContext simple;
+		AgcMeanLuminanceAlgorithm::FrameContext ml;
 		int32_t exposure;
 		double gain;
 	} agc;