[v6,28/31] ipa: libipa: lsc: Quantize gains in LscAlgorithm
diff mbox series

Message ID 20260720-libipa-algorithms-v6-28-ececb73f97cb@ideasonboard.com
State New
Headers show
Series
  • ipa: libipa: Introduce libipa algorithms
Related show

Commit Message

Jacopo Mondi July 20, 2026, 2:59 p.m. UTC
Template the LscAlgorithm class with the LSC engine register format.

This allows to quantize gains in the LscAlgorithm class and not in the
IPA modules.

Split the LscAlgorithm class in two, one LscAlgorithmBase one for
non-templated function implementations and one for the templated
configure() implementation and for templated data members and accessor.

Modify the LscAlgorithm interpolator interface. As now the returned list
of gains is in register format, a dedicated Interpolator::interpolate()
overload has to be defined for each IPA module.

Signed-off-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>
---
 src/ipa/libipa/lsc.cpp            | 88 +++++++++++++++++++++-----------------
 src/ipa/libipa/lsc.h              | 90 +++++++++++++++++++++++++++++++++------
 src/ipa/libipa/lsc_base.cpp       | 13 ------
 src/ipa/libipa/lsc_base.h         | 19 ---------
 src/ipa/rkisp1/algorithms/lsc.cpp | 40 +++++------------
 src/ipa/rkisp1/algorithms/lsc.h   | 44 +++++++++++++++++--
 6 files changed, 177 insertions(+), 117 deletions(-)

Patch
diff mbox series

diff --git a/src/ipa/libipa/lsc.cpp b/src/ipa/libipa/lsc.cpp
index 8c5c28077cce..8f881edef94c 100644
--- a/src/ipa/libipa/lsc.cpp
+++ b/src/ipa/libipa/lsc.cpp
@@ -48,6 +48,29 @@  namespace lsc {
 
 } /* namespace lsc */
 
+/**
+ * \class LscAlgorithmBase
+ * \brief Base class for LscAlgorithm
+ *
+ * Base class for LscAlgorithm for non-templated functions implementation
+ */
+
+/**
+ * \var LscAlgorithmBase::impl_
+ * \brief The LSC algorithm implementation
+ *
+ * There are two classes derived from LscImplementation, the LscTable and
+ * LscPolynomial ones. Which one to instantiate is decided by parsing the tuning
+ * file.
+ */
+
+/**
+ * \var LscAlgorithmBase::polynomial_
+ * \brief Boolean flag for polynomial LSC
+ *
+ * Set to true if polynomial LSC is in use.
+ */
+
 /**
  * \class LscAlgorithm
  * \brief libIPA LSC algorithm implementation
@@ -199,6 +222,16 @@  namespace lsc {
  * can retrieve them using LscAlgorithm::getComponents().
  */
 
+/**
+ * \typedef LscAlgorithm::Components
+ * \brief Map a colour component to a list of gains
+ */
+
+/**
+ * \typedef LscAlgorithm::ComponentsMap
+ * \brief Map a colour temperature to an LSC componenet
+ */
+
 /**
  * \param[in] tuningData The tuning data
  * \param[in] controls The IPA list of supported controls
@@ -209,8 +242,8 @@  namespace lsc {
  *
  * \return 0 on success, a negative error code otherwise
  */
-int LscAlgorithm::init(const ValueNode &tuningData, ControlInfoMap::Map &controls,
-		       const LscDescriptor &descriptor)
+int LscAlgorithmBase::init(const ValueNode &tuningData, ControlInfoMap::Map &controls,
+			   const LscDescriptor &descriptor)
 {
 	polynomial_ = false;
 
@@ -245,6 +278,7 @@  int LscAlgorithm::init(const ValueNode &tuningData, ControlInfoMap::Map &control
 }
 
 /**
+ * \fn LscAlgorithm::configure()
  * \param[in] state The LSC active state
  * \param[in] analogCrop The current sensor analog crop rectangle
  * \param[in] xPos List of horizontal positions of the LSC grid nodes
@@ -265,28 +299,6 @@  int LscAlgorithm::init(const ValueNode &tuningData, ControlInfoMap::Map &control
  *
  * \return 0 on success, a negative error code otherwise
  */
-int LscAlgorithm::configure(lsc::ActiveState &state, const Rectangle &analogCrop,
-			    const std::vector<double> &xPos,
-			    const std::vector<double> &yPos)
-{
-	LOG(Lsc, Debug) << "Sample Lsc data for " << analogCrop;
-	lsc::ComponentsMap lscData =
-		impl_->sampleForCrop(analogCrop, xPos, yPos);
-
-	/*
-	 * Retain a copy of the components table.
-	 *
-	 * We could avoid a copy here if getComponents() could
-	 * return sets_.data() but I wasn't able to work around the
-	 * compiler refusing it.
-	 */
-	lscData_ = lscData;
-
-	sets_.setData(std::move(lscData));
-	state.enabled = true;
-
-	return 0;
-}
 
 /**
  * \brief Queue a request to the lsc algorithm
@@ -297,9 +309,9 @@  int LscAlgorithm::configure(lsc::ActiveState &state, const Rectangle &analogCrop
  * Queue a new list of \a controls to the lsc algorithm.
  * The only supported control is controls::LensShadingCorrectionEnable.
  */
-void LscAlgorithm::queueRequest(lsc::ActiveState &state,
-				lsc::FrameContext &context,
-				const ControlList &controls)
+void LscAlgorithmBase::queueRequest(lsc::ActiveState &state,
+				    lsc::FrameContext &context,
+				    const ControlList &controls)
 {
 	const auto &lscEnable = controls.get(controls::LensShadingCorrectionEnable);
 	if (lscEnable && *lscEnable != state.enabled) {
@@ -322,25 +334,22 @@  void LscAlgorithm::queueRequest(lsc::ActiveState &state,
  * Populates the list of \a metadata with controls handled by the LscAlgorithm
  * class. The only supported metadata is controls::LensShadingCorrectionEnable.
  */
-void LscAlgorithm::process(lsc::FrameContext &context, ControlList &metadata)
+void LscAlgorithmBase::process(lsc::FrameContext &context, ControlList &metadata)
 {
 	metadata.set(controls::LensShadingCorrectionEnable, context.enabled);
 }
 
 /**
- * \fn LscAlgorithm::interpolateComponents
- * \brief Interpolate the LSC tables for a given colour temperature
- * \param[in] ct The colour temperature
+ * \fn LscAlgorithm::getInterpolator
+ * \brief Retrieve the LSC tables interpolator
  *
  * LSC tables are generated using different colour temperatures during the
- * tuning phase.
- *
- * This function returns the interpolated LSC data for a given \a ct
- * colour temperature.
+ * tuning phase. This function returns the interpolator for the LSC data.
  *
- * IPA algorithm can use this function to obtain a list of per-colour-component
- * gains to program their LSC engines with every time a significant enough
- * change in colour temperature is detected.
+ * IPA algorithm can use the interpolator to obtain a list of
+ * per-colour-component gains to program their LSC engines with every time a
+ * significant enough change in colour temperature is detected by calling the
+ * Interpolator::getInterpolated() function.
  *
  * Calling this function is only valid after LscAlgorithm::configure() has been
  * called. An empty components list is returned otherwise.
@@ -350,8 +359,7 @@  void LscAlgorithm::process(lsc::FrameContext &context, ControlList &metadata)
 
 /**
  * \fn LscAlgorithm::getComponents
- *
- * Return the map of LSC data per-colour-temperature.
+ * \brief Return the map of LSC data per-colour-temperature
  *
  * Calling this function is only valid after LscAlgorithm::configure() has been
  * called. An empty components list is returned otherwise.
diff --git a/src/ipa/libipa/lsc.h b/src/ipa/libipa/lsc.h
index 9fe8ad67ea33..15c8a9cb7b9c 100644
--- a/src/ipa/libipa/lsc.h
+++ b/src/ipa/libipa/lsc.h
@@ -35,35 +35,101 @@  struct FrameContext {
 
 } /* namespace lsc */
 
-class LscAlgorithm
+class LscAlgorithmBase
 {
 public:
 	int init(const ValueNode &tuningData, ControlInfoMap::Map &controls,
 		 const LscDescriptor &descriptor);
 
-	int configure(lsc::ActiveState &state, const Rectangle &analogCrop,
-		      const std::vector<double> &xPos,
-		      const std::vector<double> &yPos);
-
 	void queueRequest(lsc::ActiveState &state, lsc::FrameContext &context,
 			  const ControlList &controls);
 	void process(lsc::FrameContext &context, ControlList &metadata);
 
-	const lsc::Components interpolateComponents(unsigned int ct)
+protected:
+	LscAlgorithmBase() = default;
+
+	std::unique_ptr<LscImplementation> impl_;
+	bool polynomial_;
+};
+
+template<typename U>
+class LscAlgorithm : public LscAlgorithmBase
+{
+private:
+	using T = typename U::QuantizedType;
+
+	template<typename V>
+	class _Components : public std::map<std::string, std::vector<V>>
+	{
+	};
+
+	template<typename V>
+	class _ComponentsMap : public std::map<unsigned int, _Components<V>>
 	{
-		return sets_.getInterpolated(ct);
+	};
+
+public:
+	using Components = _Components<T>;
+	using ComponentsMap = _ComponentsMap<T>;
+
+	LscAlgorithm() = default;
+
+	int configure(lsc::ActiveState &state, const Rectangle &analogCrop,
+		      const std::vector<double> &xPos,
+		      const std::vector<double> &yPos)
+	{
+		lsc::ComponentsMap data =
+			impl_->sampleForCrop(analogCrop, xPos, yPos);
+
+		ComponentsMap lscData;
+		for (const auto &[t, c] : data) {
+			Components comp;
+
+			for (const auto &[k, gains] : c) {
+				std::vector<T> quantizedGains;
+				quantizedGains.reserve(gains.size());
+
+				for (const float &gain : gains) {
+					if (polynomial_)
+						quantizedGains.push_back(U(gain).quantized());
+					else
+						quantizedGains.push_back(gain);
+				}
+
+				comp[k] = std::move(quantizedGains);
+			}
+
+			lscData[t] = comp;
+		}
+
+		/*
+		 * Retain a copy of the components table.
+		 *
+		 * We could avoid a copy here if getComponents() could
+		 * return sets_.data() but I wasn't able to work around the
+		 * compiler refusing it.
+		 */
+		lscData_ = lscData;
+
+		sets_.setData(std::move(lscData));
+		state.enabled = true;
+
+		return 0;
+	}
+
+	Interpolator<Components> &getInterpolator()
+	{
+		return sets_;
 	}
 
-	const lsc::ComponentsMap getComponents()
+	const ComponentsMap &getComponents() const
 	{
 		return lscData_;
 	}
 
 private:
-	std::unique_ptr<LscImplementation> impl_;
-	Interpolator<lsc::Components> sets_;
-	lsc::ComponentsMap lscData_;
-	bool polynomial_;
+	ComponentsMap lscData_;
+	Interpolator<Components> sets_;
 };
 
 } /* namespace ipa */
diff --git a/src/ipa/libipa/lsc_base.cpp b/src/ipa/libipa/lsc_base.cpp
index cf2c3bfc382f..a98358daf4ec 100644
--- a/src/ipa/libipa/lsc_base.cpp
+++ b/src/ipa/libipa/lsc_base.cpp
@@ -46,19 +46,6 @@  namespace lsc {
 
 } /* namespace lsc */
 
-#ifndef __DOXYGEN__
-template<>
-void Interpolator<lsc::Components>::
-	interpolate(const lsc::Components &a,
-		    const lsc::Components &b,
-		    lsc::Components &dest,
-		    double lambda)
-{
-	for (auto const &[k, v] : a)
-		interpolateVector(v, b.at(k), dest[k], lambda);
-}
-#endif
-
 /**
  * \struct LscDescriptor
  * \brief Describe the ISP LSC engine
diff --git a/src/ipa/libipa/lsc_base.h b/src/ipa/libipa/lsc_base.h
index 386cac55be34..8053de7d2edb 100644
--- a/src/ipa/libipa/lsc_base.h
+++ b/src/ipa/libipa/lsc_base.h
@@ -31,25 +31,6 @@  using ComponentsMap = std::map<unsigned int, Components>;
 
 } /* namespace lsc */
 
-#ifndef __DOXYGEN__
-template<typename T>
-void interpolateVector(const std::vector<T> &a, const std::vector<T> &b,
-		       std::vector<T> &dest, double lambda)
-{
-	ASSERT(a.size() == b.size());
-	dest.resize(a.size());
-	for (size_t i = 0; i < a.size(); i++)
-		dest[i] = a[i] * (1.0 - lambda) + b[i] * lambda;
-}
-
-template<>
-void Interpolator<lsc::Components>::
-	interpolate(const lsc::Components &a,
-		    const lsc::Components &b,
-		    lsc::Components &dest,
-		    double lambda);
-#endif /* __DOXYGEN__ */
-
 struct LscDescriptor {
 	std::vector<std::string> keys;
 	unsigned int numHSamples;
diff --git a/src/ipa/rkisp1/algorithms/lsc.cpp b/src/ipa/rkisp1/algorithms/lsc.cpp
index dba1a01118ee..157403691ffc 100644
--- a/src/ipa/rkisp1/algorithms/lsc.cpp
+++ b/src/ipa/rkisp1/algorithms/lsc.cpp
@@ -175,34 +175,16 @@  void LensShadingCorrection::setParameters(rkisp1_cif_isp_lsc_config &config)
 }
 
 void LensShadingCorrection::copyTable(rkisp1_cif_isp_lsc_config &config,
-				      const lsc::Components &set)
+				      const RkISP1Components &set)
 {
-	/*
-	 * The hardware uses 2.10 fixed point format and limits the legal values
-	 * to [1..3.999]. Scale and clamp the sampled values accordingly.
-	 */
-	std::vector<uint16_t> regs;
-	regs.reserve(RKISP1_CIF_ISP_LSC_SAMPLES_MAX *
-		     RKISP1_CIF_ISP_LSC_SAMPLES_MAX);
-
-	for (const float &f : set.at("r"))
-		regs.emplace_back(std::clamp(static_cast<int>(f * 1024), 1024, 4095));
-	std::copy(regs.begin(), regs.end(), &config.r_data_tbl[0][0]);
-
-	regs = {};
-	for (const float &f : set.at("gr"))
-		regs.emplace_back(std::clamp(static_cast<int>(f * 1024), 1024, 4095));
-	std::copy(regs.begin(), regs.end(), &config.gr_data_tbl[0][0]);
-
-	regs = {};
-	for (const float &f : set.at("gb"))
-		regs.emplace_back(std::clamp(static_cast<int>(f * 1024), 1024, 4095));
-	std::copy(regs.begin(), regs.end(), &config.gb_data_tbl[0][0]);
-
-	regs = {};
-	for (const float &f : set.at("b"))
-		regs.emplace_back(std::clamp(static_cast<int>(f * 1024), 1024, 4095));
-	std::copy(regs.begin(), regs.end(), &config.b_data_tbl[0][0]);
+	const auto &r = set.at("r");
+	std::copy(r.begin(), r.end(), &config.r_data_tbl[0][0]);
+	const auto &gr = set.at("gr");
+	std::copy(gr.begin(), gr.end(), &config.gr_data_tbl[0][0]);
+	const auto &gb = set.at("gb");
+	std::copy(gb.begin(), gb.end(), &config.gb_data_tbl[0][0]);
+	const auto &b = set.at("b");
+	std::copy(b.begin(), b.end(), &config.b_data_tbl[0][0]);
 }
 
 /**
@@ -252,8 +234,8 @@  void LensShadingCorrection::prepare([[maybe_unused]] IPAContext &context,
 
 	setParameters(*config);
 
-	const lsc::Components &set = lscAlgo_.interpolateComponents(quantizedCt);
-	copyTable(*config, set);
+	auto &lscData = lscAlgo_.getInterpolator();
+	copyTable(*config, lscData.getInterpolated(quantizedCt));
 
 	lastAppliedCt_ = ct;
 	lastAppliedQuantizedCt_ = quantizedCt;
diff --git a/src/ipa/rkisp1/algorithms/lsc.h b/src/ipa/rkisp1/algorithms/lsc.h
index 63f0887f0838..55a6980014f2 100644
--- a/src/ipa/rkisp1/algorithms/lsc.h
+++ b/src/ipa/rkisp1/algorithms/lsc.h
@@ -12,6 +12,7 @@ 
 #include <linux/rkisp1-config.h>
 
 #include "libcamera/internal/value_node.h"
+#include "libipa/fixedpoint.h"
 
 #include "libipa/lsc.h"
 
@@ -19,7 +20,13 @@ 
 
 namespace libcamera {
 
-namespace ipa::rkisp1::algorithms {
+namespace ipa {
+
+namespace rkisp1::algorithms {
+
+using RkISP1LscAlgorithm = LscAlgorithm<UQ<2, 10>>;
+using RkISP1Components = RkISP1LscAlgorithm::Components;
+using RkISP1ComponentsMap = RkISP1LscAlgorithm::ComponentsMap;
 
 class LensShadingCorrection : public Algorithm
 {
@@ -39,9 +46,11 @@  public:
 		     IPAFrameContext &frameContext,
 		     const rkisp1_stat_buffer *stats,
 		     ControlList &metadata) override;
+
 private:
 	void setParameters(rkisp1_cif_isp_lsc_config &config);
-	void copyTable(rkisp1_cif_isp_lsc_config &config, const lsc::Components &set0);
+	void copyTable(rkisp1_cif_isp_lsc_config &config,
+		       const RkISP1Components &set);
 
 	std::vector<double> xSize_;
 	std::vector<double> ySize_;
@@ -55,8 +64,35 @@  private:
 	unsigned int lastAppliedCt_;
 	unsigned int lastAppliedQuantizedCt_;
 
-	LscAlgorithm lscAlgo_;
+	RkISP1LscAlgorithm lscAlgo_;
 };
 
-} /* namespace ipa::rkisp1::algorithms */
+} /* namespace rkisp1::algorithms */
+
+#ifndef __DOXYGEN__
+template<typename T>
+void interpolateVector(const std::vector<T> &a,
+		       const std::vector<T> &b,
+		       std::vector<T> &dest, double lambda)
+{
+	ASSERT(a.size() == b.size());
+	dest.resize(a.size());
+	for (size_t i = 0; i < a.size(); i++)
+		dest[i] = a[i] * (1.0 - lambda) + b[i] * lambda;
+}
+
+template<>
+void Interpolator<rkisp1::algorithms::RkISP1Components>::
+	interpolate(const rkisp1::algorithms::RkISP1Components &a,
+		    const rkisp1::algorithms::RkISP1Components &b,
+		    rkisp1::algorithms::RkISP1Components &dest,
+		    double lambda)
+{
+	for (auto const &[k, v] : a)
+		interpolateVector(v, b.at(k), dest[k], lambda);
+}
+#endif /* __DOXYGEN__ */
+
+} /* namespace ipa */
+
 } /* namespace libcamera */