| Message ID | 20260708-libipa-algorithms-v5-23-0759d0359f52@ideasonboard.com |
|---|---|
| State | Superseded |
| Headers | show |
| Series |
|
| Related | show |
Quoting Jacopo Mondi (2026-07-08 16:51:05) > Introduce the LscAlgorithm class that implements the LSC algorithm > for libipa. > > The 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> > --- > src/ipa/libipa/lsc.cpp | 209 +++++++++++++++++++++++++++++++++++++++++++++++++ > src/ipa/libipa/lsc.h | 42 ++++++++++ > 2 files changed, 251 insertions(+) > > diff --git a/src/ipa/libipa/lsc.cpp b/src/ipa/libipa/lsc.cpp > index 382cafc04ab4..cf748b766d1a 100644 > --- a/src/ipa/libipa/lsc.cpp > +++ b/src/ipa/libipa/lsc.cpp > @@ -7,6 +7,13 @@ > > #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 > @@ -14,6 +21,8 @@ > > namespace libcamera { > > +LOG_DEFINE_CATEGORY(Lsc) > + > namespace ipa { > > namespace lsc { > @@ -39,6 +48,206 @@ namespace lsc { > > } /* 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. > + * > + * It provides support for parsing the tuning file content and generates tables > + * of per-colour temperature gains that IPA algorithms can use to program their hrm this sounds like it's per colour - which have temperature gains. Reworking seems awkward to do with just adjusting a '-'. Trying deeper: """ This class provides support for parsing the tuning file content and generates tables indexed by colour temperature to store per-channel gains for the IPA algorithm to be able to program the LSC engine. """ Maybe that's too much - but it's only the 'per-colour' part that I'm worried about! > + * 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 organized per-colour temperature with per-colour components gain > + * vectors or polynomial coefficients. Oh it sneaks in here again too. > + * > + * 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 No colon needed on the \todo > + * > + * 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. no colon > + * 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 gains per-colour > + * component to program their LSC engines with every time a significant enough If we're reworking, "a list of per-colour component gains" sounds better ... I don't know what grammar rule makes it that way - but it just seems like it should be that way around ;-) But we have the same issue as we had with 'per-colour temperature' maybe it needs to be: of per-colour-component gains ? > + * 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 I think throughout the use of 'per' and then 'two words' needs to be normalised to be consistent. With that: Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com> > + */ > + > } /* namespace ipa */ > > } /* namespace libcamera */ > diff --git a/src/ipa/libipa/lsc.h b/src/ipa/libipa/lsc.h > index 73519564feff..9a51d32c3464 100644 > --- a/src/ipa/libipa/lsc.h > +++ b/src/ipa/libipa/lsc.h > @@ -7,6 +7,17 @@ > > #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 { > @@ -24,6 +35,37 @@ struct FrameContext { > > } /* 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 */ > > -- > 2.54.0 >
2026. 07. 08. 17:51 keltezéssel, Jacopo Mondi írta: > Introduce the LscAlgorithm class that implements the LSC algorithm > for libipa. > > The 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> > --- > src/ipa/libipa/lsc.cpp | 209 +++++++++++++++++++++++++++++++++++++++++++++++++ > src/ipa/libipa/lsc.h | 42 ++++++++++ > 2 files changed, 251 insertions(+) > > [...] > diff --git a/src/ipa/libipa/lsc.h b/src/ipa/libipa/lsc.h > index 73519564feff..9a51d32c3464 100644 > --- a/src/ipa/libipa/lsc.h > +++ b/src/ipa/libipa/lsc.h > @@ -7,6 +7,17 @@ > > #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 { > @@ -24,6 +35,37 @@ struct FrameContext { > > } /* 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_; This does not seem to be used anywhere? > +}; > + > } /* namespace ipa */ > > } /* namespace libcamera */ >
diff --git a/src/ipa/libipa/lsc.cpp b/src/ipa/libipa/lsc.cpp index 382cafc04ab4..cf748b766d1a 100644 --- a/src/ipa/libipa/lsc.cpp +++ b/src/ipa/libipa/lsc.cpp @@ -7,6 +7,13 @@ #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 @@ -14,6 +21,8 @@ namespace libcamera { +LOG_DEFINE_CATEGORY(Lsc) + namespace ipa { namespace lsc { @@ -39,6 +48,206 @@ namespace lsc { } /* 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. + * + * It provides support for parsing the tuning file content and generates tables + * of per-colour temperature gains that IPA algorithms can use to program their + * 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 organized per-colour temperature with per-colour components gain + * vectors 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 gains per-colour + * component 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 index 73519564feff..9a51d32c3464 100644 --- a/src/ipa/libipa/lsc.h +++ b/src/ipa/libipa/lsc.h @@ -7,6 +7,17 @@ #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 { @@ -24,6 +35,37 @@ struct FrameContext { } /* 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 */
Introduce the LscAlgorithm class that implements the LSC algorithm for libipa. The 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> --- src/ipa/libipa/lsc.cpp | 209 +++++++++++++++++++++++++++++++++++++++++++++++++ src/ipa/libipa/lsc.h | 42 ++++++++++ 2 files changed, 251 insertions(+)