[RFC,09/19] ipa: rkisp2: algo: awb: Implement automatic white balance control
diff mbox series

Message ID 20260703122543.1991189-10-paul.elder@ideasonboard.com
State New
Headers show
Series
  • Add support for rkisp2
Related show

Commit Message

Paul Elder July 3, 2026, 12:25 p.m. UTC
Implement auto white balance control algorithm for the rkisp2 IPA.
This leverages the libipa awb, so it supports baysian awb, grey world
awb, as well as manual white balance.

Signed-off-by: Paul Elder <paul.elder@ideasonboard.com>
---
 src/ipa/rkisp2/algorithms/awb.cpp     | 174 ++++++++++++++++++++++++++
 src/ipa/rkisp2/algorithms/awb.h       |  56 +++++++++
 src/ipa/rkisp2/algorithms/meson.build |   1 +
 3 files changed, 231 insertions(+)
 create mode 100644 src/ipa/rkisp2/algorithms/awb.cpp
 create mode 100644 src/ipa/rkisp2/algorithms/awb.h

Patch
diff mbox series

diff --git a/src/ipa/rkisp2/algorithms/awb.cpp b/src/ipa/rkisp2/algorithms/awb.cpp
new file mode 100644
index 000000000000..4928ab13d220
--- /dev/null
+++ b/src/ipa/rkisp2/algorithms/awb.cpp
@@ -0,0 +1,174 @@ 
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2026, Ideas On Board
+ *
+ * AWB control algorithm
+ */
+
+#include "awb.h"
+
+#include <algorithm>
+
+#include <libcamera/base/log.h>
+
+#include <libcamera/ipa/core_ipa_interface.h>
+
+#include "libcamera/internal/vector.h"
+
+/**
+ * \file awb.h
+ */
+
+namespace libcamera {
+
+namespace ipa::rkisp2::algorithms {
+
+LOG_DEFINE_CATEGORY(RkISP2Awb)
+
+class RkISP2AwbStats final : public AwbStats
+{
+public:
+	RkISP2AwbStats() = default;
+	RkISP2AwbStats(const RGB<double> means)
+		: AwbStats(means)
+	{
+	}
+
+	/* Minimum mean value below which AWB can't operate. */
+	double minColourValue() const override
+	{
+		return 2.0;
+	}
+};
+
+namespace {
+
+} /* namespace */
+
+/**
+ * \class Awb
+ * \brief Manage the white balance with automatic and manual controls
+ */
+
+Awb::Awb()
+{
+}
+
+/**
+ * \copydoc libcamera::ipa::Algorithm::init
+ */
+int Awb::init(IPAContext &context, const ValueNode &tuningData)
+{
+	return awbAlgo_.init(tuningData, context.ctrlMap);
+}
+
+int Awb::configure(IPAContext &context,
+		   const IPACameraSensorInfo &configInfo)
+{
+	awbAlgo_.configure(context.activeState.awb);
+
+	context.configuration.awb.measureWindow.h_offs = 0;
+	context.configuration.awb.measureWindow.v_offs = 0;
+	/*
+	 * Unlike ae lite, this seems to still work when height == full window
+	 * height
+	 */
+	context.configuration.awb.measureWindow.h_size = configInfo.outputSize.width / 15;
+	context.configuration.awb.measureWindow.v_size = configInfo.outputSize.height / 15;
+
+	return 0;
+}
+
+/**
+ * \copydoc libcamera::ipa::Algorithm::queueRequest
+ */
+void Awb::queueRequest(IPAContext &context, const uint32_t frame,
+		       IPAFrameContext &frameContext,
+		       const ControlList &controls)
+{
+	awbAlgo_.queueRequest(context.activeState.awb, frame, frameContext.awb,
+			      controls);
+}
+
+/**
+ * \copydoc libcamera::ipa::Algorithm::prepare
+ */
+void Awb::prepare([[maybe_unused]] IPAContext &context,
+		  [[maybe_unused]] const uint32_t frame,
+		  IPAFrameContext &frameContext, RkISP2Params *params)
+{
+	awbAlgo_.prepare(context.activeState.awb, frameContext.awb);
+
+	auto gainConfig = params->block<RkISP2Blocks::AwbGains>();
+	gainConfig.setEnabled(true);
+
+	RGB<double> gains = frameContext.awb.gains;
+
+	/* \todo Use quantized class */
+	gainConfig->gains[0].gb = std::clamp<int>(256 * 1.0, 0, 0x3fff);
+	gainConfig->gains[0].b = std::clamp<int>(256 * gains.b(), 0, 0x3fff);
+	gainConfig->gains[0].r = std::clamp<int>(256 * gains.r(), 0, 0x3fff);
+	gainConfig->gains[0].gr = std::clamp<int>(256 * 1.0, 0, 0x3fff);
+
+	auto measConfig = params->block<RkISP2Blocks::AwbMeas>();
+	measConfig.setEnabled(true);
+
+	measConfig->meas_window = context.configuration.awb.measureWindow;
+	struct rkisp2_isp_awb_color_quad minLimits = { 0, 0, 0, 0 };
+	struct rkisp2_isp_awb_color_quad maxLimits = { 255, 255, 255, 255 };
+	measConfig->limits[0] = minLimits;
+	measConfig->limits[1] = maxLimits;
+
+	for (unsigned int i = 0; i < RKISP2_ISP_AWB_COUNTS_SIZE; i++)
+		measConfig->weights[i] = 0x20;
+}
+
+/**
+ * \copydoc libcamera::ipa::Algorithm::process
+ */
+void Awb::process([[maybe_unused]] IPAContext &context,
+		  [[maybe_unused]] const uint32_t frame,
+		  IPAFrameContext &frameContext,
+		  const rkisp2_stats_buffer *stats,
+		  ControlList &metadata)
+{
+	RkISP2AwbStats awbStats = calculateRgbMeans(frameContext, stats);
+
+	awbAlgo_.process(context.activeState.awb, frameContext.awb, awbStats,
+			 frameContext.lux.lux, metadata);
+}
+
+RkISP2AwbStats Awb::calculateRgbMeans([[maybe_unused]] const IPAFrameContext &frameContext,
+				      const rkisp2_stats_buffer *stats) const
+{
+	if (!stats->awb.done) {
+		LOG(RkISP2Awb, Error) << "No awb stats";
+		return {};
+	}
+
+	std::array<int32_t, RKISP2_ISP_AWB_COUNTS_SIZE> counts;
+	RGB<double> means;
+
+	for (size_t i = 0; i < RKISP2_ISP_AWB_COUNTS_SIZE; i++)
+		counts[i] = static_cast<int32_t>(stats->awb.counts_r[i]);
+	means.r() = std::accumulate(counts.begin(), counts.end(), 0) / counts.size();
+
+	for (size_t i = 0; i < RKISP2_ISP_AWB_COUNTS_SIZE; i++)
+		counts[i] = static_cast<int32_t>(stats->awb.counts_g[i]);
+	means.g() = std::accumulate(counts.begin(), counts.end(), 0) / counts.size();
+
+	for (size_t i = 0; i < RKISP2_ISP_AWB_COUNTS_SIZE; i++)
+		counts[i] = static_cast<int32_t>(stats->awb.counts_b[i]);
+	means.b() = std::accumulate(counts.begin(), counts.end(), 0) / counts.size();
+
+	for (size_t i = 0; i < RKISP2_ISP_AWB_COUNTS_SIZE; i++)
+		counts[i] = static_cast<int32_t>(stats->awb.counts_w[i]);
+
+	return RkISP2AwbStats(means);
+}
+
+REGISTER_IPA_ALGORITHM(Awb, "Awb")
+
+} /* namespace ipa::rkisp2::algorithms */
+
+} /* namespace libcamera */
diff --git a/src/ipa/rkisp2/algorithms/awb.h b/src/ipa/rkisp2/algorithms/awb.h
new file mode 100644
index 000000000000..5f25ae1f1f05
--- /dev/null
+++ b/src/ipa/rkisp2/algorithms/awb.h
@@ -0,0 +1,56 @@ 
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2026, Ideas On Board
+ *
+ * AWB control algorithm
+ */
+
+#pragma once
+
+#include <linux/rkisp2-config.h>
+
+#include <libcamera/controls.h>
+
+#include "libcamera/internal/value_node.h"
+
+#include "libipa/awb.h"
+#include "libipa/fixedpoint.h"
+
+#include "algorithm.h"
+#include "ipa_context.h"
+#include "params.h"
+
+namespace libcamera {
+
+namespace ipa::rkisp2::algorithms {
+
+class RkISP2AwbStats;
+
+class Awb : public Algorithm
+{
+public:
+	Awb();
+	~Awb() = default;
+
+	int init(IPAContext &context, const ValueNode &tuningData) override;
+	int configure(IPAContext &context, const IPACameraSensorInfo &configInfo) override;
+	void queueRequest(IPAContext &context, const uint32_t frame,
+			  IPAFrameContext &frameContext,
+			  const ControlList &controls) override;
+	void prepare(IPAContext &context, const uint32_t frame,
+		     IPAFrameContext &frameContext,
+		     RkISP2Params *params) override;
+	void process(IPAContext &context, const uint32_t frame,
+		     IPAFrameContext &frameContext,
+		     const rkisp2_stats_buffer *stats,
+		     ControlList &metadata) override;
+
+private:
+	RkISP2AwbStats calculateRgbMeans(const IPAFrameContext &frameContext,
+					 const rkisp2_stats_buffer *stats) const;
+
+	AwbAlgorithm<UQ<6, 8>> awbAlgo_;
+};
+
+} /* namespace ipa::rkisp2::algorithms */
+} /* namespace libcamera */
diff --git a/src/ipa/rkisp2/algorithms/meson.build b/src/ipa/rkisp2/algorithms/meson.build
index 36996918b39e..30133ac4fa68 100644
--- a/src/ipa/rkisp2/algorithms/meson.build
+++ b/src/ipa/rkisp2/algorithms/meson.build
@@ -1,6 +1,7 @@ 
 # SPDX-License-Identifier: CC0-1.0
 
 rkisp2_ipa_algorithms = files([
+    'awb.cpp',
     'bls.cpp',
 ])