[v3,16/35] ipa: libipa: lsc_polynomial: Move LscPolynomial from RkISP1
diff mbox series

Message ID 20260706-libipa-algorithms-v3-16-968757b038bb@ideasonboard.com
State New
Headers show
Series
  • ipa: libipa: Introduce libipa algorithms
Related show

Commit Message

Jacopo Mondi July 6, 2026, 8:01 a.m. UTC
Move the LscPolynomial class to libipa from RkISP1.

Rename the class from LscPolynomialImpl to a simpler LscPolynomial.
The class is copied verbatim, only documntation has been added.

Signed-off-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>
---
 src/ipa/libipa/lsc_polynomial.cpp | 179 +++++++++++++++++++++++++++++++++++++-
 src/ipa/libipa/lsc_polynomial.h   |  46 ++++++++--
 src/ipa/rkisp1/algorithms/lsc.cpp | 162 +---------------------------------
 src/ipa/rkisp1/algorithms/lsc.h   |   1 -
 4 files changed, 218 insertions(+), 170 deletions(-)

Patch
diff mbox series

diff --git a/src/ipa/libipa/lsc_polynomial.cpp b/src/ipa/libipa/lsc_polynomial.cpp
index a845d8b1783b..5c4ffc98797e 100644
--- a/src/ipa/libipa/lsc_polynomial.cpp
+++ b/src/ipa/libipa/lsc_polynomial.cpp
@@ -2,11 +2,14 @@ 
 /*
  * Copyright (C) 2024, Ideas On Board
  *
- * Polynomial class to represent lens shading correction
+ * Polynomial based lens shading correction
  */
 
 #include "lsc_polynomial.h"
 
+#include <assert.h>
+#include <cmath>
+
 #include <libcamera/base/log.h>
 
 /**
@@ -109,6 +112,180 @@  void Polynomial::setReferenceImageSize(const Size &size)
 
 } /* namespace lsc */
 
+/**
+ * \class LscPolynomial
+ * \brief Radial Polynomial lsc algorithm implementation
+ *
+ * Polynomial-based lsc algorithm implementation. The LscPolynomial class
+ * implements lsc support using Polynomial to represent the shading artifacts
+ * map.
+ *
+ * \sa LscImplementation
+ */
+
+/**
+ * \fn LscPolynomial::LscPolynomial
+ * \param[in] sensorSize The physical sensor size
+ *
+ * Construct an LscPolynomial
+ */
+
+/**
+ * \brief Parse polynomial lsc data
+ * \param[in] sets The tuning file content
+ *
+ * Parse the lsc data in polyomial form from the \a sets tuning data.
+ *
+ * \return 0 on success or a negative error number otherwise
+ */
+int LscPolynomial::parseLscData(const ValueNode &sets)
+{
+	for (const auto &set : sets.asList()) {
+		std::optional<lsc::Polynomial> pr, pgr, pgb, pb;
+		uint32_t ct = set["ct"].get<uint32_t>(0);
+
+		if (lscData_.count(ct)) {
+			LOG(LscPolynomial, Error)
+				<< "Multiple sets found for "
+				<< "color temperature " << ct;
+			return -EINVAL;
+		}
+
+		pr = set["r"].get<lsc::Polynomial>();
+		pgr = set["gr"].get<lsc::Polynomial>();
+		pgb = set["gb"].get<lsc::Polynomial>();
+		pb = set["b"].get<lsc::Polynomial>();
+
+		if (!(pr || pgr || pgb || pb)) {
+			LOG(LscPolynomial, Error)
+				<< "Failed to parse polynomial for "
+				<< "colour temperature " << ct;
+			return -EINVAL;
+		}
+
+		pr->setReferenceImageSize(sensorSize_);
+		pgr->setReferenceImageSize(sensorSize_);
+		pgb->setReferenceImageSize(sensorSize_);
+		pb->setReferenceImageSize(sensorSize_);
+
+		lscData_.emplace(std::piecewise_construct,
+				 std::forward_as_tuple(ct),
+				 std::forward_as_tuple(PolynomialComponents{ *pr, *pgr, *pgb, *pb }));
+	}
+
+	if (lscData_.empty()) {
+		LOG(LscPolynomial, Error) << "Failed to load any sets";
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+/**
+ * \brief Re-sample the lsc components for \a cropRectangle
+ * \param[in] cropRectangle The sensor analogue crop rectangle
+ * \param[in] xSizes List of horizontal positions of the lsc grid nodes
+ * \param[in] ySizes List of vertical positions of the lsc grid nodes
+ *
+ * Lsc tables have to be re-sampled every time a new sensor configuration is
+ * used, as each streaming session might use a different sensor crop rectangle.
+ *
+ * Polynomial Lsc tables can be re-sampled for a given sensor frame resolution
+ * using a list of horizontal and vertical nodes that define the lsc grid on
+ * which the polynomial is re-sampled on.
+ *
+ * \a cropRectangle represents the size of the frame on which the Lsc tables
+ * have to be re-sampled on.
+ *
+ * \a xSizes and \a ySizes represent the position of the grid nodes vertexes in
+ * the [0, 1] interval. In example an equally spaced grid of 16 nodes will have
+ * each segment of size 0.0625 and the list of nodes position will be
+ * [0, 0.0625, 0.125, 0.1875, ... , 1]. It is expected that the first position
+ * is 0 and the last position is 1.
+ */
+lsc::ComponentsMap
+LscPolynomial::sampleForCrop(const Rectangle &cropRectangle,
+			     Span<const double> xSizes,
+			     Span<const double> ySizes)
+{
+	std::vector<double> xPos = sizesListToPositions(xSizes);
+	std::vector<double> yPos = sizesListToPositions(ySizes);
+
+	lsc::ComponentsMap components;
+
+	for (const auto &[k, p] : lscData_) {
+		components[k] = {
+			samplePolynomial(p.pr, xPos, yPos, cropRectangle),
+			samplePolynomial(p.pgr, xPos, yPos, cropRectangle),
+			samplePolynomial(p.pgb, xPos, yPos, cropRectangle),
+			samplePolynomial(p.pb, xPos, yPos, cropRectangle)
+		};
+	}
+
+	return components;
+}
+
+std::vector<uint16_t>
+LscPolynomial::samplePolynomial(const lsc::Polynomial &poly,
+				Span<const double> xPositions,
+				Span<const double> yPositions,
+				const Rectangle &cropRectangle)
+{
+	double m = poly.getM();
+	double x0 = cropRectangle.x / m;
+	double y0 = cropRectangle.y / m;
+	double w = cropRectangle.width / m;
+	double h = cropRectangle.height / m;
+	std::vector<uint16_t> samples;
+
+	samples.reserve(xPositions.size() * yPositions.size());
+
+	for (double y : yPositions) {
+		for (double x : xPositions) {
+			double xp = x0 + x * w;
+			double yp = y0 + y * h;
+			/*
+			 * The hardware uses 2.10 fixed point format and limits
+			 * the legal values to [1..3.999]. Scale and clamp the
+			 * sampled value accordingly.
+			 */
+			int v = static_cast<int>(
+				poly.sampleAtNormalizedPixelPos(xp, yp) *
+				1024);
+			v = std::clamp(v, 1024, 4095);
+			samples.push_back(v);
+		}
+	}
+	return samples;
+}
+
+/*
+ * The rkisp1 LSC grid spacing is defined by the cell sizes on the top-left
+ * quadrant of the grid. This is then mirrored in hardware to the other
+ * quadrants. See parseSizes() for further details. For easier handling, this
+ * function converts the cell sizes of half the grid to a list of position of
+ * the whole grid (on one axis). Example:
+ *
+ * input:   | 0.2 | 0.3 |
+ * output: 0.0   0.2   0.5   0.8   1.0
+ */
+std::vector<double>
+LscPolynomial::sizesListToPositions(Span<const double> sizes)
+{
+	const int half = sizes.size();
+	std::vector<double> positions(half * 2 + 1);
+	double x = 0.0;
+
+	positions[half] = 0.5;
+	for (int i = 1; i <= half; i++) {
+		x += sizes[half - i];
+		positions[half - i] = 0.5 - x;
+		positions[half + i] = 0.5 + x;
+	}
+
+	return positions;
+}
+
 } /* namespace ipa */
 
 #ifndef __DOXYGEN__
diff --git a/src/ipa/libipa/lsc_polynomial.h b/src/ipa/libipa/lsc_polynomial.h
index 2caf46d2d759..17c2c34499f0 100644
--- a/src/ipa/libipa/lsc_polynomial.h
+++ b/src/ipa/libipa/lsc_polynomial.h
@@ -2,25 +2,23 @@ 
 /*
  * Copyright (C) 2024, Ideas On Board
  *
- * Helper for radial polynomial used in lens shading correction.
+ * Polynomial based lens shading correction
  */
 #pragma once
 
-#include <algorithm>
 #include <array>
-#include <assert.h>
-#include <cmath>
+#include <map>
+#include <vector>
 
-#include <libcamera/base/log.h>
 #include <libcamera/base/span.h>
 
 #include <libcamera/geometry.h>
 
 #include "libcamera/internal/value_node.h"
 
-namespace libcamera {
+#include "lsc_base.h"
 
-LOG_DECLARE_CATEGORY(LscPolynomial)
+namespace libcamera {
 
 namespace ipa {
 
@@ -52,6 +50,40 @@  private:
 
 } /* namespace lsc */
 
+class LscPolynomial : public LscImplementation
+{
+private:
+	struct PolynomialComponents {
+		lsc::Polynomial pr;
+		lsc::Polynomial pgr;
+		lsc::Polynomial pgb;
+		lsc::Polynomial pb;
+	};
+	using PolynomialComponentsMap = std::map<unsigned int, PolynomialComponents>;
+
+public:
+	LscPolynomial(const Size &sensorSize)
+		: sensorSize_(sensorSize)
+	{
+	}
+
+	int parseLscData(const ValueNode &sets) override;
+
+	lsc::ComponentsMap
+	sampleForCrop(const Rectangle &cropRectangle,
+		      Span<const double> xSizes,
+		      Span<const double> ySizes) override;
+
+private:
+	std::vector<double> sizesListToPositions(Span<const double> sizes);
+	std::vector<uint16_t> samplePolynomial(const lsc::Polynomial &poly,
+					       Span<const double> xPositions,
+					       Span<const double> yPositions,
+					       const Rectangle &cropRectangle);
+	PolynomialComponentsMap lscData_;
+	Size sensorSize_;
+};
+
 } /* namespace ipa */
 
 } /* namespace libcamera */
diff --git a/src/ipa/rkisp1/algorithms/lsc.cpp b/src/ipa/rkisp1/algorithms/lsc.cpp
index 910b2ced292e..a4ea879aaa83 100644
--- a/src/ipa/rkisp1/algorithms/lsc.cpp
+++ b/src/ipa/rkisp1/algorithms/lsc.cpp
@@ -33,166 +33,6 @@  namespace {
 
 constexpr int kColourTemperatureQuantization = 10;
 
-class LscPolynomialImpl : public LscImplementation
-{
-private:
-	struct PolynomialComponents {
-		lsc::Polynomial pr;
-		lsc::Polynomial pgr;
-		lsc::Polynomial pgb;
-		lsc::Polynomial pb;
-	};
-	using PolynomialComponentsMap = std::map<unsigned int, PolynomialComponents>;
-
-public:
-	LscPolynomialImpl(const Size &sensorSize)
-		: sensorSize_(sensorSize)
-	{
-	}
-
-	int parseLscData(const ValueNode &sets) override;
-
-	lsc::ComponentsMap
-	sampleForCrop(const Rectangle &cropRectangle,
-		      Span<const double> xSizes,
-		      Span<const double> ySizes) override;
-
-private:
-	std::vector<double> sizesListToPositions(Span<const double> sizes);
-	std::vector<uint16_t> samplePolynomial(const lsc::Polynomial &poly,
-					       Span<const double> xPositions,
-					       Span<const double> yPositions,
-					       const Rectangle &cropRectangle);
-	PolynomialComponentsMap lscData_;
-	Size sensorSize_;
-};
-
-int LscPolynomialImpl::parseLscData(const ValueNode &sets)
-{
-	for (const auto &set : sets.asList()) {
-		std::optional<lsc::Polynomial> pr, pgr, pgb, pb;
-		uint32_t ct = set["ct"].get<uint32_t>(0);
-
-		if (lscData_.count(ct)) {
-			LOG(RkISP1Lsc, Error)
-				<< "Multiple sets found for "
-				<< "color temperature " << ct;
-			return -EINVAL;
-		}
-
-		pr = set["r"].get<lsc::Polynomial>();
-		pgr = set["gr"].get<lsc::Polynomial>();
-		pgb = set["gb"].get<lsc::Polynomial>();
-		pb = set["b"].get<lsc::Polynomial>();
-
-		if (!(pr || pgr || pgb || pb)) {
-			LOG(RkISP1Lsc, Error)
-				<< "Failed to parse polynomial for "
-				<< "colour temperature " << ct;
-			return -EINVAL;
-		}
-
-		pr->setReferenceImageSize(sensorSize_);
-		pgr->setReferenceImageSize(sensorSize_);
-		pgb->setReferenceImageSize(sensorSize_);
-		pb->setReferenceImageSize(sensorSize_);
-
-		lscData_.emplace(std::piecewise_construct,
-				 std::forward_as_tuple(ct),
-				 std::forward_as_tuple(PolynomialComponents{ *pr, *pgr, *pgb, *pb }));
-	}
-
-	if (lscData_.empty()) {
-		LOG(RkISP1Lsc, Error) << "Failed to load any sets";
-		return -EINVAL;
-	}
-
-	return 0;
-}
-
-lsc::ComponentsMap
-LscPolynomialImpl::sampleForCrop(const Rectangle &cropRectangle,
-				 Span<const double> xSizes,
-				 Span<const double> ySizes)
-{
-	std::vector<double> xPos = sizesListToPositions(xSizes);
-	std::vector<double> yPos = sizesListToPositions(ySizes);
-
-	lsc::ComponentsMap components;
-
-	for (const auto &[k, p] : lscData_) {
-		components[k] = {
-			samplePolynomial(p.pr, xPos, yPos, cropRectangle),
-			samplePolynomial(p.pgr, xPos, yPos, cropRectangle),
-			samplePolynomial(p.pgb, xPos, yPos, cropRectangle),
-			samplePolynomial(p.pb, xPos, yPos, cropRectangle)
-		};
-	}
-
-	return components;
-}
-
-std::vector<uint16_t>
-LscPolynomialImpl::samplePolynomial(const lsc::Polynomial &poly,
-				    Span<const double> xPositions,
-				    Span<const double> yPositions,
-				    const Rectangle &cropRectangle)
-{
-	double m = poly.getM();
-	double x0 = cropRectangle.x / m;
-	double y0 = cropRectangle.y / m;
-	double w = cropRectangle.width / m;
-	double h = cropRectangle.height / m;
-	std::vector<uint16_t> samples;
-
-	samples.reserve(xPositions.size() * yPositions.size());
-
-	for (double y : yPositions) {
-		for (double x : xPositions) {
-			double xp = x0 + x * w;
-			double yp = y0 + y * h;
-			/*
-			 * The hardware uses 2.10 fixed point format and limits
-			 * the legal values to [1..3.999]. Scale and clamp the
-			 * sampled value accordingly.
-			 */
-			int v = static_cast<int>(
-				poly.sampleAtNormalizedPixelPos(xp, yp) *
-				1024);
-			v = std::clamp(v, 1024, 4095);
-			samples.push_back(v);
-		}
-	}
-	return samples;
-}
-
-/*
- * The rkisp1 LSC grid spacing is defined by the cell sizes on the top-left
- * quadrant of the grid. This is then mirrored in hardware to the other
- * quadrants. See parseSizes() for further details. For easier handling, this
- * function converts the cell sizes of half the grid to a list of position of
- * the whole grid (on one axis). Example:
- *
- * input:   | 0.2 | 0.3 |
- * output: 0.0   0.2   0.5   0.8   1.0
- */
-std::vector<double>
-LscPolynomialImpl::sizesListToPositions(Span<const double> sizes)
-{
-	const int half = sizes.size();
-	std::vector<double> positions(half * 2 + 1);
-	double x = 0.0;
-
-	positions[half] = 0.5;
-	for (int i = 1; i <= half; i++) {
-		x += sizes[half - i];
-		positions[half - i] = 0.5 - x;
-		positions[half + i] = 0.5 + x;
-	}
-
-	return positions;
-}
-
 class LscTableImpl : public LscImplementation
 {
 public:
@@ -358,7 +198,7 @@  int LensShadingCorrection::init([[maybe_unused]] IPAContext &context,
 		 * \todo: Most likely the reference frame should be native_size.
 		 * Let's wait how the internal discussions progress.
 		 */
-		algo_ = std::make_unique<LscPolynomialImpl>(context.sensorInfo.activeAreaSize);
+		algo_ = std::make_unique<LscPolynomial>(context.sensorInfo.activeAreaSize);
 		ret = algo_->parseLscData(sets);
 	} else {
 		LOG(RkISP1Lsc, Error) << "Unsupported LSC data type '"
diff --git a/src/ipa/rkisp1/algorithms/lsc.h b/src/ipa/rkisp1/algorithms/lsc.h
index b21c91c0d81f..2a428293cb2e 100644
--- a/src/ipa/rkisp1/algorithms/lsc.h
+++ b/src/ipa/rkisp1/algorithms/lsc.h
@@ -7,7 +7,6 @@ 
 
 #pragma once
 
-#include <map>
 #include <memory>
 
 #include "libipa/interpolator.h"