[RFC,v6,3/6] ipa: simple: Add LSC algorithm
diff mbox series

Message ID 20260708141436.119165-4-mzamazal@redhat.com
State New
Headers show
Series
  • LSC for SoftISP simple pipeline
Related show

Commit Message

Milan Zamazal July 8, 2026, 2:14 p.m. UTC
From: Xander Pronk <xander.c.pronk@gmail.com>

The algorithm is based on the common libipa lens shading correction
implementation.  The grid values obtained from the libipa algorithm are
passed to debayering in an array to be used as an RGB texture.

Notes to the implementation:

- The overall idea is to keep things simple, to not make the LSC
  computation unnecessarily expensive.

- 16 equally spaced grid positions are used for LscAlgorithm; for no
  particular reason other than that we already use the same number for
  the whole grid sizes.

- LscAlgorithm accepts only quantised types.  UQ<2,6> is used, to be
  converted to float for debayering.

- The limit of 100 to consider a temperature change noticeable is
  arbitrary.

Co-developed-by: Rick ten Wolde <rick_libcamera@wolde.info>
Signed-off-by: Rick ten Wolde <rick_libcamera@wolde.info>
Signed-off-by: Xander Pronk <xander.c.pronk@gmail.com>
Signed-off-by: Milan Zamazal <mzamazal@redhat.com>
---
 src/ipa/simple/algorithms/lsc.cpp     | 87 +++++++++++++++++++++++++++
 src/ipa/simple/algorithms/lsc.h       | 50 +++++++++++++++
 src/ipa/simple/algorithms/meson.build |  1 +
 src/ipa/simple/ipa_context.h          |  5 ++
 4 files changed, 143 insertions(+)
 create mode 100644 src/ipa/simple/algorithms/lsc.cpp
 create mode 100644 src/ipa/simple/algorithms/lsc.h

Patch
diff mbox series

diff --git a/src/ipa/simple/algorithms/lsc.cpp b/src/ipa/simple/algorithms/lsc.cpp
new file mode 100644
index 000000000..6961b95dc
--- /dev/null
+++ b/src/ipa/simple/algorithms/lsc.cpp
@@ -0,0 +1,87 @@ 
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Lens shading correction
+ */
+
+#include "lsc.h"
+
+#include <libcamera/base/log.h>
+
+namespace libcamera {
+
+namespace ipa::soft::algorithms {
+
+LOG_DEFINE_CATEGORY(IPASoftLsc)
+
+int Lsc::init(IPAContext &context, const ValueNode &tuningData)
+{
+	static constexpr unsigned int kGridSize = DebayerParams::kLscGridSize;
+
+	for (unsigned int i = 0; i < kGridSize; i++)
+		gridPos_.push_back(static_cast<double>(i) / (kGridSize - 1));
+
+	return lscAlgo_.init(tuningData, context.ctrlMap,
+			     { .keys = { "r", "g", "b" },
+			       .numHCells = kGridSize,
+			       .numVCells = kGridSize,
+			       .sensorSize = context.sensorInfo.activeAreaSize });
+}
+
+int Lsc::configure(IPAContext &context,
+		   [[maybe_unused]] const IPAConfigInfo &configInfo)
+{
+	return lscAlgo_.configure(context.activeState.lsc,
+				  context.sensorInfo.analogCrop,
+				  gridPos_, gridPos_);
+}
+
+void Lsc::prepare(IPAContext &context, [[maybe_unused]] const uint32_t frame,
+		  IPAFrameContext &frameContext, DebayerParams *params)
+{
+	unsigned int ct = context.activeState.awb.automatic.temperatureK;
+
+	params->lscEnabled = frameContext.lsc.enabled;
+
+	if (!frameContext.lsc.enabled ||
+	    utils::abs_diff(ct, lastAppliedCt_) < 100)
+		return;
+
+	const lsc::Components<uint8_t> &set = lscAlgo_.interpolateComponents(ct);
+
+	const auto &red = set.at("r");
+	const auto &green = set.at("g");
+	const auto &blue = set.at("b");
+
+	DebayerParams::LscLookupTable lut;
+	constexpr unsigned int gridSize = DebayerParams::kLscGridSize;
+	for (unsigned int i = 0, j = 0; i < gridSize * gridSize; i++) {
+		lut[j++] = red[i] / 64.0;
+		lut[j++] = green[i] / 64.0;
+		lut[j++] = blue[i] / 64.0;
+	}
+	params->lscLut = lut;
+
+	lastAppliedCt_ = ct;
+}
+
+void Lsc::queueRequest(IPAContext &context, [[maybe_unused]] const uint32_t frame,
+		       IPAFrameContext &frameContext, const ControlList &controls)
+{
+	lscAlgo_.queueRequest(context.activeState.lsc, frameContext.lsc,
+			      controls);
+}
+
+void Lsc::process([[maybe_unused]] IPAContext &context,
+		  [[maybe_unused]] const uint32_t frame,
+		  IPAFrameContext &frameContext,
+		  [[maybe_unused]] const SwIspStats *stats,
+		  ControlList &metadata)
+{
+	lscAlgo_.process(frameContext.lsc, metadata);
+}
+
+REGISTER_IPA_ALGORITHM(Lsc, "Lsc")
+
+} /* namespace ipa::soft::algorithms */
+
+} /* namespace libcamera */
diff --git a/src/ipa/simple/algorithms/lsc.h b/src/ipa/simple/algorithms/lsc.h
new file mode 100644
index 000000000..9418fac39
--- /dev/null
+++ b/src/ipa/simple/algorithms/lsc.h
@@ -0,0 +1,50 @@ 
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Lens shading correction
+ */
+
+#pragma once
+
+#include <libipa/interpolator.h>
+
+#include "libipa/fixedpoint.h"
+#include "libipa/lsc.h"
+
+#include "algorithm.h"
+#include "ipa_context.h"
+
+namespace libcamera {
+
+namespace ipa::soft::algorithms {
+
+class Lsc : public Algorithm
+{
+public:
+	Lsc() = default;
+	~Lsc() = default;
+
+	int init(IPAContext &context, const ValueNode &tuningData) override;
+	int configure(IPAContext &context,
+		      const IPAConfigInfo &configInfo) override;
+	void queueRequest(IPAContext &context, [[maybe_unused]] const uint32_t frame,
+			  IPAFrameContext &frameContext, const ControlList &controls) override;
+	void prepare(IPAContext &context,
+		     const uint32_t frame,
+		     IPAFrameContext &frameContext,
+		     DebayerParams *params) override;
+	void process([[maybe_unused]] IPAContext &context,
+		     [[maybe_unused]] const uint32_t frame,
+		     IPAFrameContext &frameContext,
+		     [[maybe_unused]] const SwIspStats *stats,
+		     ControlList &metadata) override;
+
+private:
+	LscAlgorithm<UQ<2, 6>> lscAlgo_;
+	std::vector<double> gridPos_;
+
+	unsigned int lastAppliedCt_ = 0;
+};
+
+} /* namespace ipa::soft::algorithms */
+
+} /* namespace libcamera */
diff --git a/src/ipa/simple/algorithms/meson.build b/src/ipa/simple/algorithms/meson.build
index 73c637220..c9f6e5590 100644
--- a/src/ipa/simple/algorithms/meson.build
+++ b/src/ipa/simple/algorithms/meson.build
@@ -6,4 +6,5 @@  soft_simple_ipa_algorithms = files([
     'agc.cpp',
     'blc.cpp',
     'ccm.cpp',
+    'lsc.cpp',
 ])
diff --git a/src/ipa/simple/ipa_context.h b/src/ipa/simple/ipa_context.h
index ff312ae8f..23c2cfd0a 100644
--- a/src/ipa/simple/ipa_context.h
+++ b/src/ipa/simple/ipa_context.h
@@ -19,6 +19,7 @@ 
 #include <libipa/awb.h>
 #include <libipa/ccm.h>
 #include <libipa/fc_queue.h>
+#include "libipa/lsc.h"
 
 #include "core_ipa_interface.h"
 
@@ -61,6 +62,8 @@  struct IPAActiveState {
 		std::optional<float> contrast;
 		std::optional<float> saturation;
 	} knobs;
+
+	ipa::lsc::ActiveState lsc;
 };
 
 struct IPAFrameContext : public FrameContext {
@@ -75,6 +78,7 @@  struct IPAFrameContext : public FrameContext {
 	float gamma;
 	std::optional<float> contrast;
 	std::optional<float> saturation;
+	ipa::lsc::FrameContext lsc;
 };
 
 struct IPAContext {
@@ -89,6 +93,7 @@  struct IPAContext {
 	FCQueue<IPAFrameContext> frameContexts;
 	ControlInfoMap::Map ctrlMap;
 	bool ccmEnabled = false;
+	ipa::lsc::ActiveState lsc;
 };
 
 } /* namespace ipa::soft */