@@ -7,13 +7,6 @@
#include "lsc.h"
-#include <libcamera/base/log.h>
-
-#include <libcamera/control_ids.h>
-
-#include "lsc_polynomial.h"
-#include "lsc_table.h"
-
/**
* \file lsc.h
* \brief libipa lsc algorithm
@@ -48,9 +41,56 @@ namespace lsc {
} /* namespace lsc */
+/**
+ * \class LscAlgorithmBase
+ * \brief Base class for LscAlgorithm
+ *
+ * Base class for LscAlgorithm for non-templated functions implementation
+ */
+
+/**
+ * \brief Queue a request to the lsc algorithm
+ * \param[in] state The lsc active state
+ * \param[in] context The lsc frame context
+ * \param[in] controls The list of controls associated with a Request
+ *
+ * Queue a new list of \a controls to the lsc algorithm.
+ * The only supported control is controls::LensShadingCorrectionEnable.
+ */
+void LscAlgorithmBase::queueRequest(lsc::ActiveState &state,
+ lsc::FrameContext &context,
+ const ControlList &controls)
+{
+ const auto &lscEnable = controls.get(controls::LensShadingCorrectionEnable);
+ if (lscEnable && *lscEnable != state.enabled) {
+ state.enabled = *lscEnable;
+
+ LOG(Lsc, Debug)
+ << (state.enabled ? "Enabling" : "Disabling") << " Lsc";
+
+ context.update = true;
+ }
+
+ context.enabled = state.enabled;
+}
+
+/**
+ * \brief Populate the list of lsc metadata
+ * \param[in] context The lsc frame context
+ * \param[in] metadata The list of metadata
+ *
+ * Populates the list of \a metadata with controls handled by the LscAlgorithm
+ * class. The only supported metadata is controls::LensShadingCorrectionEnable.
+ */
+void LscAlgorithmBase::process(lsc::FrameContext &context, ControlList &metadata)
+{
+ metadata.set(controls::LensShadingCorrectionEnable, context.enabled);
+}
+
/**
* \class LscAlgorithm
* \brief libIPA lsc algorithm implementation
+ * \tparam U The fixedpoint lsc engine register format
*
* Due to the optical characteristics of the lens, the light intensity received
* by the sensor is not uniform. The Lens Shading Correction algorithm applies
@@ -200,6 +240,7 @@ namespace lsc {
*/
/**
+ * \fn LscAlgorithm::init()
* \param[in] tuningData The tuning data
* \param[in] controls The IPA list of supported controls
* \param[in] descriptor The lsc engine descriptor
@@ -209,42 +250,9 @@ namespace lsc {
*
* \return 0 on success, a negative error code otherwise
*/
-int LscAlgorithm::init(const ValueNode &tuningData, ControlInfoMap::Map &controls,
- const LscDescriptor &descriptor)
-{
- polynomial_ = false;
-
- std::string type = tuningData["type"].get<std::string>("table");
- if (type == "table") {
- impl_ = std::make_unique<LscTable>();
- LOG(Lsc, Debug) << "Using table-based Lsc";
- } else if (type == "polynomial") {
- impl_ = std::make_unique<LscPolynomial>();
- polynomial_ = true;
- LOG(Lsc, Debug) << "Using polynomial Lsc";
- } else {
- LOG(Lsc, Error) << "Unsupported Lsc algorithm '"
- << type << "'";
- return -EINVAL;
- }
-
- const ValueNode &yamlSets = tuningData["sets"];
- if (!yamlSets.isList()) {
- LOG(Lsc, Error) << "'sets' parameter not found in tuning file";
- return -EINVAL;
- }
-
- int ret = impl_->parseLscData(yamlSets, descriptor);
- if (ret)
- return ret;
-
- controls[&controls::LensShadingCorrectionEnable] =
- ControlInfo(false, true, true);
-
- return 0;
-}
/**
+ * \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,67 +273,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
- * \param[in] state The lsc active state
- * \param[in] context The lsc frame context
- * \param[in] controls The list of controls associated with a Request
- *
- * 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)
-{
- const auto &lscEnable = controls.get(controls::LensShadingCorrectionEnable);
- if (lscEnable && *lscEnable != state.enabled) {
- state.enabled = *lscEnable;
-
- LOG(Lsc, Debug)
- << (state.enabled ? "Enabling" : "Disabling") << " Lsc";
-
- context.update = true;
- }
-
- context.enabled = state.enabled;
-}
-
-/**
- * \brief Populate the list of lsc metadata
- * \param[in] context The lsc frame context
- * \param[in] metadata The list of metadata
- *
- * 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)
-{
- metadata.set(controls::LensShadingCorrectionEnable, context.enabled);
-}
/**
* \fn LscAlgorithm::interpolateComponents
@@ -8,18 +8,25 @@
#pragma once
#include <memory>
+#include <string>
#include <vector>
+#include <libcamera/base/log.h>
+
+#include <libcamera/control_ids.h>
#include <libcamera/controls.h>
#include <libcamera/geometry.h>
#include "libcamera/internal/value_node.h"
#include "interpolator.h"
-#include "lsc_base.h"
+#include "lsc_polynomial.h"
+#include "lsc_table.h"
namespace libcamera {
+LOG_DECLARE_CATEGORY(Lsc)
+
namespace ipa {
namespace lsc {
@@ -35,19 +42,75 @@ struct FrameContext {
} /* namespace lsc */
-class LscAlgorithm
+class LscAlgorithmBase
+{
+public:
+ void queueRequest(lsc::ActiveState &state, lsc::FrameContext &context,
+ const ControlList &controls);
+ void process(lsc::FrameContext &context, ControlList &metadata);
+};
+
+template<typename U>
+class LscAlgorithm : public LscAlgorithmBase
{
public:
int init(const ValueNode &tuningData, ControlInfoMap::Map &controls,
- const LscDescriptor &descriptor);
+ const LscDescriptor &descriptor)
+ {
+ polynomial_ = false;
+
+ std::string type = tuningData["type"].get<std::string>("table");
+ if (type == "table") {
+ impl_ = std::make_unique<LscTable<U>>();
+ LOG(Lsc, Debug) << "Using table-based Lsc";
+ } else if (type == "polynomial") {
+ impl_ = std::make_unique<LscPolynomial<U>>();
+ polynomial_ = true;
+ LOG(Lsc, Debug) << "Using polynomial Lsc";
+ } else {
+ LOG(Lsc, Error) << "Unsupported Lsc algorithm '"
+ << type << "'";
+ return -EINVAL;
+ }
+
+ const ValueNode &yamlSets = tuningData["sets"];
+ if (!yamlSets.isList()) {
+ LOG(Lsc, Error) << "'sets' parameter not found in tuning file";
+ return -EINVAL;
+ }
+
+ int ret = impl_->parseLscData(yamlSets, descriptor);
+ if (ret)
+ return ret;
+
+ controls[&controls::LensShadingCorrectionEnable] =
+ ControlInfo(false, true, true);
+
+ return 0;
+ }
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 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;
+ }
const lsc::Components interpolateComponents(unsigned int ct)
{
@@ -60,7 +123,7 @@ public:
}
private:
- std::unique_ptr<LscImplementation> impl_;
+ std::unique_ptr<LscImplementation<U>> impl_;
Interpolator<lsc::Components> sets_;
lsc::ComponentsMap lscData_;
bool polynomial_;
@@ -86,6 +86,7 @@ void Interpolator<lsc::Components>::
/**
* \class LscImplementation
* \brief Pure virtual base class for lsc algorithm implementations
+ * \tparam U The fixedpoint lsc engine register format
*
* Defines the interface for the lsc algorithm implementation.
*/
@@ -57,6 +57,7 @@ struct LscDescriptor {
Size sensorSize;
};
+template<typename U>
class LscImplementation
{
public:
@@ -113,14 +113,10 @@ void Polynomial::setReferenceImageSize(const Size &size)
} /* namespace lsc */
/**
- * \class LscPolynomial
- * \brief Radial Polynomial lsc algorithm implementation
+ * \class LscPolynomialBase
+ * \brief Base class for LscPolynomial
*
- * Polynomial-based lsc algorithm implementation. The LscPolynomial class
- * implements lsc support using Polynomial to represent the shading artifacts
- * map.
- *
- * \sa LscImplementation
+ * Base class for LscPolynomial for non-templated functions.
*/
/**
@@ -132,8 +128,8 @@ void Polynomial::setReferenceImageSize(const Size &size)
*
* \return 0 on success or a negative error number otherwise
*/
-int LscPolynomial::parseLscData(const ValueNode &sets,
- const LscDescriptor &descriptor)
+int LscPolynomialBase::parseLscData(const ValueNode &sets,
+ const LscDescriptor &descriptor)
{
for (const auto &set : sets.asList()) {
uint32_t ct = set["ct"].get<uint32_t>(0);
@@ -174,6 +170,27 @@ int LscPolynomial::parseLscData(const ValueNode &sets,
}
/**
+ * \var LscPolynomialBase::lscData_
+ * \brief The polynomial lsc data
+ *
+ * Maps colour temperatures to per-colour radial polynomial definitions.
+ */
+
+/**
+ * \class LscPolynomial
+ * \brief Radial Polynomial lsc algorithm implementation
+ * \tparam U The fixedpoint format used to convert gain values generated by
+ * polynomial expansion to the register format
+ *
+ * Polynomial-based lsc algorithm implementation. The LscPolynomial class
+ * implements lsc support using Polynomial to represent the shading artifacts
+ * map.
+ *
+ * \sa LscImplementation
+ */
+
+/**
+ * \fn LscPolynomial::sampleForCrop()
* \brief Re-sample the lsc components for \a cropRectangle
* \param[in] cropRectangle The sensor analogue crop rectangle
* \param[in] xPos List of horizontal positions of the lsc grid nodes
@@ -195,62 +212,7 @@ int LscPolynomial::parseLscData(const ValueNode &sets,
* [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,
- std::vector<double> xPos, std::vector<double> yPos)
-{
-
- lsc::ComponentsMap components;
-
- for (const auto &[t, c] : lscData_) {
- lsc::Components comp;
-
- for (const auto &[k, p] : c) {
- comp.emplace(std::piecewise_construct,
- std::forward_as_tuple(k),
- std::forward_as_tuple(samplePolynomial(p, xPos, yPos,
- cropRectangle)));
- }
- components[t] = comp;
- }
-
- 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;
-}
} /* namespace ipa */
@@ -8,6 +8,7 @@
#include <array>
#include <map>
+#include <tuple>
#include <vector>
#include <libcamera/base/span.h>
@@ -50,26 +51,80 @@ private:
} /* namespace lsc */
-class LscPolynomial : public LscImplementation
+class LscPolynomialBase
{
private:
using PolynomialComponents = std::map<std::string, lsc::Polynomial>;
using PolynomialComponentsMap = std::map<unsigned int, PolynomialComponents>;
+protected:
+ int parseLscData(const ValueNode &yamlSets,
+ const LscDescriptor &descriptor);
+
+ PolynomialComponentsMap lscData_;
+};
+
+template<typename U>
+class LscPolynomial : public LscPolynomialBase, public LscImplementation<U>
+{
public:
- int parseLscData(const ValueNode &sets,
- const LscDescriptor &descriptor) override;
+ int parseLscData(const ValueNode &yamlSets,
+ const LscDescriptor &descriptor) override
+ {
+ return LscPolynomialBase::parseLscData(yamlSets, descriptor);
+ }
lsc::ComponentsMap
sampleForCrop(const Rectangle &cropRectangle,
- std::vector<double> xPos, std::vector<double> yPos) override;
+ std::vector<double> xPos, std::vector<double> yPos) override
+ {
+ lsc::ComponentsMap components;
+
+ for (const auto &[t, c] : lscData_) {
+ lsc::Components comp;
+
+ for (const auto &[k, p] : c) {
+ comp.emplace(std::piecewise_construct,
+ std::forward_as_tuple(k),
+ std::forward_as_tuple(samplePolynomial(p, xPos, yPos,
+ cropRectangle)));
+ }
+
+ components[t] = comp;
+ }
+
+ return components;
+ }
private:
std::vector<uint16_t> samplePolynomial(const lsc::Polynomial &poly,
Span<const double> xPositions,
Span<const double> yPositions,
- const Rectangle &cropRectangle);
- PolynomialComponentsMap lscData_;
+ 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;
+
+ float sample = static_cast<float>
+ (poly.sampleAtNormalizedPixelPos(xp, yp));
+
+ samples.push_back(U(sample).quantized());
+ }
+ }
+
+ return samples;
+ }
};
} /* namespace ipa */
@@ -14,13 +14,10 @@ LOG_DEFINE_CATEGORY(LscTable)
namespace ipa {
/**
- * \class LscTable
- * \brief Table based lsc algorithm implementation
- *
- * Table based lsc algorithm implementation. The LscTable class implements lsc
- * support using tabular lsc data.
+ * \class LscTableBase
+ * \brief Base class for LscTable
*
- * \sa LscImplementation
+ * Base class for LscTable for non-templated functions.
*/
/**
@@ -32,8 +29,8 @@ namespace ipa {
*
* \return 0 on success or a negative error number otherwise
*/
-int LscTable::parseLscData(const ValueNode &sets,
- const LscDescriptor &descriptor)
+int LscTableBase::parseLscData(const ValueNode &sets,
+ const LscDescriptor &descriptor)
{
for (const auto &set : sets.asList()) {
uint32_t ct = set["ct"].get<uint32_t>(0);
@@ -51,8 +48,8 @@ int LscTable::parseLscData(const ValueNode &sets,
return 0;
}
-int LscTable::parseLscComponent(const ValueNode &yamlSet,
- unsigned int ct, const LscDescriptor &descriptor)
+int LscTableBase::parseLscComponent(const ValueNode &yamlSet,
+ unsigned int ct, const LscDescriptor &descriptor)
{
lsc::Components component;
for (auto &k : descriptor.keys) {
@@ -82,10 +79,10 @@ int LscTable::parseLscComponent(const ValueNode &yamlSet,
return 0;
}
-std::vector<uint16_t> LscTable::parseTable(const ValueNode &tuningData,
- const char *prop,
- unsigned int numHCells,
- unsigned int numVCells)
+std::vector<uint16_t> LscTableBase::parseTable(const ValueNode &tuningData,
+ const char *prop,
+ unsigned int numHCells,
+ unsigned int numVCells)
{
unsigned int lscNumSamples = numHCells * numVCells;
@@ -102,6 +99,30 @@ std::vector<uint16_t> LscTable::parseTable(const ValueNode &tuningData,
return table;
}
+/**
+ * \var LscTableBase::lscData_
+ * \brief The tabular lsc data
+ *
+ * Maps colour temperatures to per-colour channel vector of gains
+ */
+
+/**
+ * \class LscTable
+ * \brief Table based lsc algorithm implementation
+ * \tparam U The fixedpoint lsc engine register format
+ *
+ * Table based lsc algorithm implementation. The LscTable class implements lsc
+ * support using tabular lsc data.
+ *
+ * \sa LscImplementation
+ */
+
+/**
+ * \fn LscTable::parseLscData()
+ *
+ * \copydoc LscTableBase::parseLscData()
+ */
+
} /* namespace ipa */
} /* namespace libcamera */
@@ -23,11 +23,32 @@ LOG_DECLARE_CATEGORY(LscTable)
namespace ipa {
-class LscTable : public LscImplementation
+class LscTableBase
+{
+protected:
+ int parseLscData(const ValueNode &sets,
+ const LscDescriptor &descriptor);
+
+private:
+ int parseLscComponent(const ValueNode &yamlSet,
+ unsigned int ct, const LscDescriptor &descriptor);
+ std::vector<uint16_t> parseTable(const ValueNode &tuningData,
+ const char *prop,
+ unsigned int numHCells,
+ unsigned int numVCells);
+protected:
+ lsc::ComponentsMap lscData_;
+};
+
+template<typename U>
+class LscTable : public LscTableBase, public LscImplementation<U>
{
public:
int parseLscData(const ValueNode &sets,
- const LscDescriptor &descriptor) override;
+ const LscDescriptor &descriptor) override
+ {
+ return LscTableBase::parseLscData(sets, descriptor);
+ }
lsc::ComponentsMap
sampleForCrop([[maybe_unused]] const Rectangle &cropRectangle,
@@ -38,16 +59,6 @@ public:
<< "Tabular LSC data doesn't support resampling";
return lscData_;
}
-
-private:
- int parseLscComponent(const ValueNode &yamlSet,
- unsigned int ct, const LscDescriptor &descriptor);
- std::vector<uint16_t> parseTable(const ValueNode &tuningData,
- const char *prop,
- unsigned int numHCells,
- unsigned int numVCells);
-
- lsc::ComponentsMap lscData_;
};
} /* namespace ipa */
@@ -13,6 +13,7 @@
#include "libcamera/internal/value_node.h"
+#include "libipa/fixedpoint.h"
#include "libipa/lsc.h"
#include "algorithm.h"
@@ -58,7 +59,7 @@ private:
unsigned int lastAppliedCt_;
unsigned int lastAppliedQuantizedCt_;
- LscAlgorithm lscAlgo_;
+ LscAlgorithm<UQ<2, 10>> lscAlgo_;
};
} /* namespace ipa::rkisp1::algorithms */
The LscPolynomial::samplePolynomial function expands the radial polynomial assuming the RkISP1 Q<2, 10> register format: /* * 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); In order to remove all assumptions about the platform register format template the LscAlgorithm class with the fixed point format of the ISP Lsc engine registers. float sample = static_cast<float> (poly.sampleAtNormalizedPixelPos(xp, yp)); samples.push_back(U(sample).quantized()); In order to template the LscPolynomial class, template the whole class hierarchy including the LscTable class that doesn't need (yet) the template argument but will use it once re-sampling LscTable will be implemented. Split all involved classes (LscAlgorithm, LscTable and LscPolynomial) in two classes: a Lsc*Base class for non-templated function implementations and a Lsc* class for templated functions implementations. Signed-off-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com> --- src/ipa/libipa/lsc.cpp | 151 +++++++++++++------------------------- src/ipa/libipa/lsc.h | 81 +++++++++++++++++--- src/ipa/libipa/lsc_base.cpp | 1 + src/ipa/libipa/lsc_base.h | 1 + src/ipa/libipa/lsc_polynomial.cpp | 90 +++++++---------------- src/ipa/libipa/lsc_polynomial.h | 67 +++++++++++++++-- src/ipa/libipa/lsc_table.cpp | 49 +++++++++---- src/ipa/libipa/lsc_table.h | 35 ++++++--- src/ipa/rkisp1/algorithms/lsc.h | 3 +- 9 files changed, 270 insertions(+), 208 deletions(-)