From patchwork Wed Jul 8 15:51:05 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jacopo Mondi X-Patchwork-Id: 27330 Return-Path: X-Original-To: parsemail@patchwork.libcamera.org Delivered-To: parsemail@patchwork.libcamera.org Received: from lancelot.ideasonboard.com (lancelot.ideasonboard.com [92.243.16.209]) by patchwork.libcamera.org (Postfix) with ESMTPS id CE81AC3329 for ; Wed, 8 Jul 2026 15:51:48 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 69F146611A; Wed, 8 Jul 2026 17:51:48 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="R2p4SYtZ"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [IPv6:2001:4b98:dc2:55:216:3eff:fef7:d647]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 553BB660D9 for ; Wed, 8 Jul 2026 17:51:19 +0200 (CEST) Received: from [192.168.1.182] (93-46-82-201.ip106.fastwebnet.it [93.46.82.201]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 7A4523FBF; Wed, 8 Jul 2026 17:50:29 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1783525829; bh=ufGN47Y+M2apUceF3o/l4FY6OxM8VSz3Mf75D2CYr2A=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=R2p4SYtZ5azUuaS0iZa6OXQKAGeBi6qmns1CakvzrubLpTPsoIRNXWkembBI86jqr c8EIWSaZTm6IHZ6hfm5RhKFeEeRCvYdsUYOw5Sp6urbt8rtDZlAievhpT2m/JTChDp PqHlZC6Zn7epzfHIs8rqwkFIdGXkbrtf2GA/ny3Y= From: Jacopo Mondi Date: Wed, 08 Jul 2026 17:51:05 +0200 Subject: [PATCH v5 23/36] ipa: libipa: lsc: Introduce LscAlgorithm MIME-Version: 1.0 Message-Id: <20260708-libipa-algorithms-v5-23-0759d0359f52@ideasonboard.com> References: <20260708-libipa-algorithms-v5-0-0759d0359f52@ideasonboard.com> In-Reply-To: <20260708-libipa-algorithms-v5-0-0759d0359f52@ideasonboard.com> To: libcamera-devel@lists.libcamera.org, Stefan Klug Cc: Jacopo Mondi X-Mailer: b4 0.14.3 X-Developer-Signature: v=1; a=openpgp-sha256; l=9923; i=jacopo.mondi@ideasonboard.com; h=from:subject:message-id; bh=ufGN47Y+M2apUceF3o/l4FY6OxM8VSz3Mf75D2CYr2A=; b=owEBbQKS/ZANAwAKAXI0Bo8WoVY8AcsmYgBqTnHpa9gOML3KL0ZDRXAsvm3+q1VkM1qhrhps4 XfUZwykdleJAjMEAAEKAB0WIQS1xD1IgJogio9YOMByNAaPFqFWPAUCak5x6QAKCRByNAaPFqFW PKlOD/4mWWYcJzyMqFMOX2kB2t5YpmGvZbpvUsCxl0BYHEEgbAOSjxCB68C29DfhYQ8qb4d4/7l evNhZMGV9/CAqQgzyHGRfaYeYS/uSIZu3IOcaMgkkq8OKGdWlJgZLySfvR4Cjoqnteuhp7ml1x8 0+2snlSkv3B/+qgNCYhZlRVFHzuTfQfzh4oWF2QU+eB7EcL1WHUh72TaCWEebDN03lQsrW4jcU5 tribMxFEvZFjrjY26hm/XlzfbqpqZLTtErgYJfnrZ8fO8sCgsxhKXD5I09cy+lb8PPb0W6QEml8 +4fPuHO39aXZEUdZahx7MHp0mYxE5tJ5KDVEWp8xcIKbFlPHAuDAhXP/+rhpg1j7ml0vO+w5sGW EuZucw8HyxacYRuGGVHRq0cXmbj8pxJa9Fm+we0i1IM4Gug6rixF+MaRl5Z3Kh1tn474p2WNvno aM0mJKmYVREuCdX8xgkHF6Ii0rC8CJEDI+HNsKoGQ+9/uwuHfABr874fUxeTCFGeNgfc/8jFq8U xCIWZCh+vr3Y8kfypCos+30+OOaLpg6V093xn8wdiSNnxg2iC7wq8jwk3YV4bYdKti+a5W4+DPm ccd5b52gVPeR7hwiIRIY2QJI0Hp6e/2QFh66QU26zZ/Re2GQ8JA0mX1E25yIWbCTYJMqAgDaxCB zbgf5fyV5Oy0AgQ== X-Developer-Key: i=jacopo.mondi@ideasonboard.com; a=openpgp; fpr=72392EDC88144A65C701EA9BA5826A2587AD026B X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" 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 --- 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 + +#include + +#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("table"); + if (type == "table") { + impl_ = std::make_unique(); + 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(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 &xPos, + const std::vector &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 +#include + +#include +#include + +#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 &xPos, + const std::vector &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 impl_; + Interpolator sets_; + lsc::ComponentsMap lscData_; + bool polynomial_; +}; + } /* namespace ipa */ } /* namespace libcamera */