@@ -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__
@@ -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 */
@@ -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 '"
@@ -7,7 +7,6 @@
#pragma once
-#include <map>
#include <memory>
#include "libipa/interpolator.h"
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(-)