[v3,06/11] ipa: c3-isp: Add Awb algorithm
diff mbox series

Message ID 20250227105733.187611-7-keke.li@amlogic.com
State New
Headers show
Series
  • Add Amlogic C3 ISP pipeline handler and IPA
Related show

Commit Message

Keke Li Feb. 27, 2025, 10:57 a.m. UTC
Add a new Awb algorithm for the C3 ISP.

Signed-off-by: Keke Li <keke.li@amlogic.com>
---
 src/ipa/c3-isp/algorithms/awb.cpp     | 200 ++++++++++++++++++++++++++
 src/ipa/c3-isp/algorithms/awb.h       |  39 +++++
 src/ipa/c3-isp/algorithms/meson.build |   1 +
 3 files changed, 240 insertions(+)
 create mode 100644 src/ipa/c3-isp/algorithms/awb.cpp
 create mode 100644 src/ipa/c3-isp/algorithms/awb.h

Patch
diff mbox series

diff --git a/src/ipa/c3-isp/algorithms/awb.cpp b/src/ipa/c3-isp/algorithms/awb.cpp
new file mode 100644
index 00000000..786dcc81
--- /dev/null
+++ b/src/ipa/c3-isp/algorithms/awb.cpp
@@ -0,0 +1,200 @@ 
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2024, Amlogic
+ *
+ * C3ISP AWB control algorithm
+ */
+
+#include "awb.h"
+
+#include <cmath>
+
+#include <libcamera/base/log.h>
+#include <libcamera/base/utils.h>
+
+#include <libcamera/control_ids.h>
+
+#include "libipa/colours.h"
+#include "libipa/fixedpoint.h"
+
+/**
+ * \file awb.h
+ */
+
+namespace libcamera {
+
+namespace ipa::c3isp::algorithms {
+
+/**
+ * \class Awb
+ * \brief A Grey world white balance correction algorithm
+ */
+
+LOG_DEFINE_CATEGORY(C3ISPAwb)
+
+Awb::Awb()
+{
+}
+
+/**
+ * \copydoc libcamera::ipa::Algorithm::configure
+ */
+int Awb::configure(IPAContext &context,
+		   [[maybe_unused]] const IPACameraSensorInfo &configInfo)
+{
+	context.activeState.awb.rGain = 1.0;
+	context.activeState.awb.bGain = 1.0;
+
+	return 0;
+}
+
+void Awb::fillGainsParam(IPAContext &context, IPAFrameContext &frameContext,
+			 C3ISPParams *params)
+{
+	double rGain = context.activeState.awb.rGain;
+	double bGain = context.activeState.awb.bGain;
+
+	auto AWBGains = params->block<BlockType::AWBGains>();
+	AWBGains.setEnabled(C3_ISP_PARAMS_BLOCK_FL_ENABLE);
+
+	AWBGains->gr_gain = floatingToFixedPoint<4, 8, uint16_t, double>(1.0);
+	AWBGains->r_gain = floatingToFixedPoint<4, 8, uint16_t, double>(rGain);
+	AWBGains->b_gain = floatingToFixedPoint<4, 8, uint16_t, double>(bGain);
+	AWBGains->gb_gain = floatingToFixedPoint<4, 8, uint16_t, double>(1.0);
+
+	frameContext.awb.rGain = rGain;
+	frameContext.awb.bGain = bGain;
+}
+
+void Awb::fillConfigParam(IPAContext &context, C3ISPParams *params)
+{
+	auto AWBCfg = params->block<BlockType::AWBConfig>();
+	AWBCfg.setEnabled(C3_ISP_PARAMS_BLOCK_FL_ENABLE);
+
+	AWBCfg->tap_point = C3_ISP_AWB_STATS_TAP_BEFORE_WB;
+	AWBCfg->satur_vald = 1;
+
+	/* We use the full 32x24 zoning scheme */
+	AWBCfg->horiz_zones_num = 32;
+	AWBCfg->vert_zones_num = 24;
+
+	/* The ratios themselves are stored in Q4.8 format */
+	AWBCfg->rg_min = 75;
+	AWBCfg->rg_max = 256;
+	AWBCfg->bg_min = 44;
+	AWBCfg->bg_max = 222;
+
+	/* The triming of ratios are stored in Q4.8 format */
+	AWBCfg->rg_low = 93;
+	AWBCfg->rg_high = 244;
+	AWBCfg->bg_low = 61;
+	AWBCfg->bg_high = 205;
+
+	Span<uint8_t> weights{ AWBCfg->zone_weight, C3_ISP_AWB_MAX_ZONES };
+	std::fill(weights.begin(), weights.end(), 1);
+
+	Size sensorSize = context.configuration.sensor.size;
+	uint8_t maxPointNum =
+		std::max(AWBCfg->horiz_zones_num, AWBCfg->vert_zones_num) + 1;
+
+	for (unsigned int i = 0; i < maxPointNum; i++) {
+		uint16_t hidx = i * sensorSize.width / AWBCfg->horiz_zones_num;
+
+		/* Aligned with 2 */
+		hidx = hidx / 2 * 2;
+		AWBCfg->horiz_coord[i] = std::min(hidx, (uint16_t)sensorSize.width);
+
+		uint16_t vidx = i * sensorSize.height / AWBCfg->vert_zones_num;
+
+		/* Aligned with 2 */
+		vidx = vidx / 2 * 2;
+		AWBCfg->vert_coord[i] = std::min(vidx, (uint16_t)sensorSize.height);
+	}
+}
+
+/**
+ * \copydoc libcamera::ipa::Algorithm::prepare
+ */
+void Awb::prepare(IPAContext &context, const uint32_t frame,
+		  IPAFrameContext &frameContext, C3ISPParams *params)
+{
+	fillGainsParam(context, frameContext, params);
+
+	if (frame > 0)
+		return;
+
+	fillConfigParam(context, params);
+}
+
+/**
+ * \copydoc libcamera::ipa::Algorithm::process
+ */
+void Awb::process(IPAContext &context, [[maybe_unused]] const uint32_t frame,
+		  IPAFrameContext &frameContext, const c3_isp_stats_info *stats,
+		  ControlList &metadata)
+{
+	IPAActiveState &activeState = context.activeState;
+	const struct c3_isp_awb_stats *awb = &stats->awb;
+
+	/* The AWB statistics are R:G and B:G ratios of the zones. */
+	uint32_t counted_zones = 0;
+	double rgSum = 0, bgSum = 0;
+
+	for (unsigned int i = 0; i < C3_ISP_AWB_MAX_ZONES; i++) {
+		if (!awb->stats[i].pixel_sum)
+			continue;
+
+		/* The statistics are in Q4.12 format. */
+		rgSum += fixedToFloatingPoint<4, 12, double, uint16_t>(awb->stats[i].rg);
+		bgSum += fixedToFloatingPoint<4, 12, double, uint16_t>(awb->stats[i].bg);
+
+		counted_zones++;
+	}
+
+	double rgAvg, bgAvg;
+	if (!counted_zones) {
+		rgAvg = 1.0;
+		bgAvg = 1.0;
+	} else {
+		rgAvg = rgSum / counted_zones;
+		bgAvg = bgSum / counted_zones;
+	}
+
+	/*
+	 * To simplify the calculation, the average green is hardcoded
+	 * to 1.0.
+	 */
+	activeState.awb.temperatureK = estimateCCT({ { rgAvg, 1.0, bgAvg } });
+
+	/* Metadata shall contain the up to date measurement */
+	metadata.set(controls::ColourTemperature, activeState.awb.temperatureK);
+
+	/*
+	 * Estimate the red and blue gains to apply in a grey world.
+	 * The green gain is hardcoded to 1.0. Avoid division by zero
+	 * by clamping the divisor to mininum value of 0.0625.
+	 */
+	double rGain = 1 / std::max(rgAvg, 0.0625);
+	double bGain = 1 / std::max(bgAvg, 0.0625);
+
+	/* Filter the values to avoid oscillations. */
+	double speed = 0.2;
+	rGain = speed * rGain + (1 - speed) * activeState.awb.rGain;
+	bGain = speed * bGain + (1 - speed) * activeState.awb.bGain;
+
+	activeState.awb.rGain = rGain;
+	activeState.awb.bGain = bGain;
+
+	metadata.set(controls::ColourGains, { static_cast<float>(frameContext.awb.rGain),
+					      static_cast<float>(frameContext.awb.bGain) });
+
+	LOG(C3ISPAwb, Debug) << "Gains: R " << activeState.awb.rGain
+			     << ", B " << activeState.awb.bGain
+			     << ", Ct " << activeState.awb.temperatureK << "K";
+}
+
+REGISTER_IPA_ALGORITHM(Awb, "Awb")
+
+} /* namespace ipa::c3isp::algorithms */
+
+} /* namespace libcamera */
diff --git a/src/ipa/c3-isp/algorithms/awb.h b/src/ipa/c3-isp/algorithms/awb.h
new file mode 100644
index 00000000..bd036c11
--- /dev/null
+++ b/src/ipa/c3-isp/algorithms/awb.h
@@ -0,0 +1,39 @@ 
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2024, Amlogic
+ *
+ * C3ISP AWB control algorithm
+ */
+
+#pragma once
+
+#include "algorithm.h"
+
+namespace libcamera {
+
+namespace ipa::c3isp::algorithms {
+
+class Awb : public Algorithm
+{
+public:
+	Awb();
+	~Awb() = default;
+
+	int configure(IPAContext &context, const IPACameraSensorInfo &configInfo) override;
+	void prepare(IPAContext &context, const uint32_t frame,
+		     IPAFrameContext &frameContext,
+		     C3ISPParams *params) override;
+	void process(IPAContext &context, const uint32_t frame,
+		     IPAFrameContext &frameContext,
+		     const c3_isp_stats_info *stats,
+		     ControlList &metadata) override;
+
+private:
+	void fillGainsParam(IPAContext &context, IPAFrameContext &frameContext,
+			    C3ISPParams *params);
+	void fillConfigParam(IPAContext &context, C3ISPParams *params);
+};
+
+} /* namespace ipa::c3isp::algorithms */
+
+} /* namespace libcamera */
diff --git a/src/ipa/c3-isp/algorithms/meson.build b/src/ipa/c3-isp/algorithms/meson.build
index 3e44fe91..9460da99 100644
--- a/src/ipa/c3-isp/algorithms/meson.build
+++ b/src/ipa/c3-isp/algorithms/meson.build
@@ -2,4 +2,5 @@ 
 
 c3isp_ipa_algorithms = files([
     'agc.cpp',
+    'awb.cpp',
 ])