[v6,20/31] ipa: libipa: lsc: Introduce LscAlgorithm
diff mbox series

Message ID 20260720-libipa-algorithms-v6-20-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
Introduce the lsc.h and lsc.cpp files with the definition of the
Lsc algorithm active state, frame context and the LscAlgorithm class.

The LscAlgorithm class uses two backends (LscPolynomial and LscTable) to
perform tuning file parsing and re-scaling of the LSC gains.

Signed-off-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>
Reviewed-by: Stefan Klug <stefan.klug@ideasonboard.com>
Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
---
 src/ipa/libipa/lsc.cpp     | 253 +++++++++++++++++++++++++++++++++++++++++++++
 src/ipa/libipa/lsc.h       |  71 +++++++++++++
 src/ipa/libipa/meson.build |   2 +
 3 files changed, 326 insertions(+)

Patch
diff mbox series

diff --git a/src/ipa/libipa/lsc.cpp b/src/ipa/libipa/lsc.cpp
new file mode 100644
index 000000000000..dd69c7a80b17
--- /dev/null
+++ b/src/ipa/libipa/lsc.cpp
@@ -0,0 +1,253 @@ 
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2026 Ideas on Board Oy
+ *
+ * libIPA Lsc algorithms
+ */
+
+#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
+ */
+
+namespace libcamera {
+
+LOG_DEFINE_CATEGORY(Lsc)
+
+namespace ipa {
+
+namespace lsc {
+
+/**
+ * \struct ActiveState
+ * \brief The LSC active state
+ *
+ * \var ActiveState::enabled
+ * \brief Boolean flag for the LscAlgorithm enable status
+ */
+
+/**
+ * \struct FrameContext
+ * \brief The LSC frame context
+ *
+ * \var FrameContext::enabled
+ * \brief Boolean flag for the LscAlgorithm enable status
+ *
+ * \var FrameContext::update
+ * \brief Boolean flag for the LscAlgorithm updated status
+ */
+
+} /* namespace lsc */
+
+/**
+ * \class LscAlgorithm
+ * \brief libIPA LSC algorithm implementation
+ *
+ * Due to the optical characteristics of the lens, the light intensity received
+ * by the sensor is not uniform. The Lens Shading Correction algorithm applies
+ * multipliers to all pixels to compensate for the lens shading effect.
+ *
+ * The LscAlgorithm implements the libipa Lens Shading Correction algorithm
+ * using an implementation of the LscImplementation interface.
+ *
+ * This class provides support for parsing the tuning file content and
+ * generates tables indexed by colour temperature to store per-colour-channel
+ * gains for the IPA algorithm to be able to program the LSC engine.
+ *
+ * The init() function parses the tuning file and loads the gain tables either
+ * in tabular form (LscTable) or as radial polynomials (LscPolynomial). The gain
+ * tables are indexed by colour temperature with per-colour-components vectors
+ * of gain values or polynomial coefficients.
+ *
+ * At LscAlgorithm::configure() time the LSC tables are re-sampled on the
+ * sensor's crop rectangle in use to adapt them to the configuration in use for
+ * a streaming session. Polynomial LSC tables support re-sampling and can be
+ * applied to any sensor configuration. Grid-based LSC tables cannot currently
+ * be re-sampled and the configuration as parsed from the tuning file is used
+ * for all sensor configurations providing best-effort results.
+ *
+ * \todo Implement grid based re-sampling
+ *
+ * When the IPA algorithms wants to get access to the (re-sampled) tables to
+ * program its LSC engine, it uses LscAlgorithm::interpolateComponents() to get
+ * an LSC table interpolated by the LscAlgorithm class for the specified colour
+ * temperature. If the algorithm wants to access the non-interpolated tables it
+ * can retrieve them using LscAlgorithm::getComponents().
+ */
+
+/**
+ * \param[in] tuningData The tuning data
+ * \param[in] sensorSize The physical sensor size
+ * \param[in] controls The IPA list of supported controls
+ *
+ * Parse \a tuningData according to the settings specified in \a descriptor to
+ * populate the LSC data and registers LSC controls in \a controls.
+ *
+ * \return 0 on success, a negative error code otherwise
+ */
+int LscAlgorithm::init(const ValueNode &tuningData, const Size &sensorSize,
+		       ControlInfoMap::Map &controls)
+{
+	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") {
+		/*
+		 * \todo Most likely the reference frame should be native_size.
+		 * Let's wait how the internal discussions progress.
+		 */
+		impl_ = std::make_unique<LscPolynomial>(sensorSize);
+		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);
+	if (ret)
+		return ret;
+
+	controls[&controls::LensShadingCorrectionEnable] =
+		ControlInfo(false, true, true);
+
+	return 0;
+}
+
+/**
+ * \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
+ * \param[in] yPos List of vertical positions of the LSC grid nodes
+ *
+ * Re-sample the LSC data for an \a analogCrop.
+ *
+ * LSC tables are generated at tuning time using a known sensor configuration.
+ * When a new streaming session is started, it might use a different sensor
+ * configuration for which the LSC tables need to be adjusted to.
+ *
+ * This function re-generates the LSC tables to adapt them to a new sensor
+ * configuration, specifically it re-samples the LSC data for a new \a
+ * analogCrop on a grid specified by \a xPos and \a yPos. Re-sampling of
+ * LSC data is currently supported by polynomial-based LSC tables.
+ *
+ * \sa LscImplementation::sampleForCrop
+ *
+ * \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
+ * \brief Interpolate the LSC tables for a given colour temperature
+ * \param[in] ct The colour temperature
+ *
+ * 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.
+ *
+ * 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.
+ *
+ * Calling this function is only valid after LscAlgorithm::configure() has been
+ * called. An empty components list is returned otherwise.
+ *
+ * \return The LSC gains table interpolated for temperature \a ct
+ */
+
+/**
+ * \fn LscAlgorithm::getComponents
+ *
+ * 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.
+ *
+ * \return The map of LSC gains tables per-colour-temperature
+ */
+
+} /* namespace ipa */
+
+} /* namespace libcamera */
diff --git a/src/ipa/libipa/lsc.h b/src/ipa/libipa/lsc.h
new file mode 100644
index 000000000000..9a51d32c3464
--- /dev/null
+++ b/src/ipa/libipa/lsc.h
@@ -0,0 +1,71 @@ 
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2026 Ideas on Board Oy
+ *
+ * libIPA Lsc algorithm
+ */
+
+#pragma once
+
+#include <memory>
+#include <vector>
+
+#include <libcamera/controls.h>
+#include <libcamera/geometry.h>
+
+#include "libcamera/internal/value_node.h"
+
+#include "interpolator.h"
+#include "lsc_base.h"
+
+namespace libcamera {
+
+namespace ipa {
+
+namespace lsc {
+
+struct ActiveState {
+	bool enabled;
+};
+
+struct FrameContext {
+	bool enabled;
+	bool update;
+};
+
+} /* namespace lsc */
+
+class LscAlgorithm
+{
+public:
+	int init(const ValueNode &tuningData, const Size &sensorSize,
+		 ControlInfoMap::Map &controls);
+
+	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)
+	{
+		return sets_.getInterpolated(ct);
+	}
+
+	const lsc::ComponentsMap getComponents()
+	{
+		return lscData_;
+	}
+
+private:
+	std::unique_ptr<LscImplementation> impl_;
+	Interpolator<lsc::Components> sets_;
+	lsc::ComponentsMap lscData_;
+	bool polynomial_;
+};
+
+} /* namespace ipa */
+
+} /* namespace libcamera */
diff --git a/src/ipa/libipa/meson.build b/src/ipa/libipa/meson.build
index 95d398d99fd6..42f89d9820fa 100644
--- a/src/ipa/libipa/meson.build
+++ b/src/ipa/libipa/meson.build
@@ -14,6 +14,7 @@  libipa_headers = files([
     'fixedpoint.h',
     'histogram.h',
     'interpolator.h',
+    'lsc.h',
     'lsc_base.h',
     'lsc_polynomial.h',
     'lsc_table.h',
@@ -38,6 +39,7 @@  libipa_sources = files([
     'fixedpoint.cpp',
     'histogram.cpp',
     'interpolator.cpp',
+    'lsc.cpp',
     'lsc_base.cpp',
     'lsc_polynomial.cpp',
     'lsc_table.cpp',