{"id":27651,"url":"https://patchwork.libcamera.org/api/patches/27651/?format=json","web_url":"https://patchwork.libcamera.org/patch/27651/","project":{"id":1,"url":"https://patchwork.libcamera.org/api/projects/1/?format=json","name":"libcamera","link_name":"libcamera","list_id":"libcamera_core","list_email":"libcamera-devel@lists.libcamera.org","web_url":"","scm_url":"","webscm_url":""},"msgid":"<20260805-libipa-algorithms-v7-28-7425b5b795d4@ideasonboard.com>","date":"2026-08-05T16:13:10","name":"[v7,28/32] ipa: libipa: lsc: Quantize gains in LscAlgorithm","commit_ref":null,"pull_url":null,"state":"new","archived":false,"hash":"707719cb257091ebb97a85a2af304892b8ece2e3","submitter":{"id":143,"url":"https://patchwork.libcamera.org/api/people/143/?format=json","name":"Jacopo Mondi","email":"jacopo.mondi@ideasonboard.com"},"delegate":null,"mbox":"https://patchwork.libcamera.org/patch/27651/mbox/","series":[{"id":6110,"url":"https://patchwork.libcamera.org/api/series/6110/?format=json","web_url":"https://patchwork.libcamera.org/project/libcamera/list/?series=6110","date":"2026-08-05T16:12:42","name":"ipa: libipa: Introduce libipa algorithms","version":7,"mbox":"https://patchwork.libcamera.org/series/6110/mbox/"}],"comments":"https://patchwork.libcamera.org/api/patches/27651/comments/","check":"pending","checks":"https://patchwork.libcamera.org/api/patches/27651/checks/","tags":{},"headers":{"Return-Path":"<libcamera-devel-bounces@lists.libcamera.org>","X-Original-To":"parsemail@patchwork.libcamera.org","Delivered-To":"parsemail@patchwork.libcamera.org","Received":["from lancelot.ideasonboard.com (lancelot.ideasonboard.com\n\t[92.243.16.209])\n\tby patchwork.libcamera.org (Postfix) with ESMTPS id ED4AFC334C\n\tfor <parsemail@patchwork.libcamera.org>;\n\tWed,  5 Aug 2026 16:14:00 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 704BB68186;\n\tWed,  5 Aug 2026 18:14:00 +0200 (CEST)","from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[IPv6:2001:4b98:dc2:55:216:3eff:fef7:d647])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id D25066816D\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tWed,  5 Aug 2026 18:13:42 +0200 (CEST)","from [192.168.125.177] (mob-109-113-25-35.net.vodafone.it\n\t[109.113.25.35])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id 1D4B13A20;\n\tWed,  5 Aug 2026 18:12:32 +0200 (CEST)"],"Authentication-Results":"lancelot.ideasonboard.com; dkim=pass (1024-bit key;\n\tunprotected) header.d=ideasonboard.com header.i=@ideasonboard.com\n\theader.b=\"Y5U6sqLk\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1785946352;\n\tbh=0icdnYN3wJsMUHWBqdqovgOrFElVBnBhToX1Em0XrvE=;\n\th=From:Date:Subject:References:In-Reply-To:To:Cc:From;\n\tb=Y5U6sqLkzVQiPDp/YFqKESF2pv1w1VFVt2/tYNy60JzAIhwslptoySUCEHOc/8KNu\n\tG95x1eJ3dk9iMALtkNepdg6XShQ+bg7DvutZHtdJs8928S25nrBXLEY9kd2lcrUQKd\n\tJ31lMIPLB7qsPVDD0lmTp9RGJ9jzEkRkuLw8TJjM=","From":"Jacopo Mondi <jacopo.mondi@ideasonboard.com>","Date":"Wed, 05 Aug 2026 18:13:10 +0200","Subject":"[PATCH v7 28/32] ipa: libipa: lsc: Quantize gains in LscAlgorithm","MIME-Version":"1.0","Content-Type":"text/plain; charset=\"utf-8\"","Content-Transfer-Encoding":"7bit","Message-Id":"<20260805-libipa-algorithms-v7-28-7425b5b795d4@ideasonboard.com>","References":"<20260805-libipa-algorithms-v7-0-7425b5b795d4@ideasonboard.com>","In-Reply-To":"<20260805-libipa-algorithms-v7-0-7425b5b795d4@ideasonboard.com>","To":"libcamera-devel@lists.libcamera.org, \n\tStefan Klug <stefan.klug@ideasonboard.com>, \n\tMilan Zamazal <mzamazal@redhat.com>","Cc":"Jacopo Mondi <jacopo.mondi@ideasonboard.com>","X-Mailer":"b4 0.16-dev","X-Developer-Signature":"v=1; a=openpgp-sha256; l=22720;\n\ti=jacopo.mondi@ideasonboard.com; h=from:subject:message-id;\n\tbh=0icdnYN3wJsMUHWBqdqovgOrFElVBnBhToX1Em0XrvE=;\n\tb=owEBbQKS/ZANAwAKAXI0Bo8WoVY8AcsmYgBqc2Eiz0WxptMA3R8PH+5xCNItHtLe1QDsE6h4J\n\tBbiOkx3TwmJAjMEAAEKAB0WIQS1xD1IgJogio9YOMByNAaPFqFWPAUCanNhIgAKCRByNAaPFqFW\n\tPGKyEADDLVscLnW2s4txMV3SEBCSSG0jo/SENjppPDym/sMwwr3/SokOupYcir+G4lElM260Mjn\n\t7RRFrKeBqxE853PdkqDUyUelFg+zE/U1blpOsG4fiss7OjhFt0mooFFRR6j0CaTiGDGEVkpnKH8\n\trG5h7VRX/mxt2uxEGUUFWYsg/uP3xJNivBqQc/5pQkYcq8Qk4/zTk4dVNA/I6Vl8kNLpruPWL8L\n\twDD4twq5qLznrcKtg80OLdPQKBzWc7SNKMR4C6z+bejSY/y65IuJ0hjAk6eQdpjCk9gJRHry0yf\n\ta9Otv9CVGxmrssGw9djQvJmEoo4bpqkhp5pb13grQi0gcL4AYKXZbsVAD9JzchubNqD0Socv4Mh\n\tEZv4riRLHo38TusStYKjbS52b2DCZ+BnzDMNC4Vbzq8ywx3PfYLWS8WcsQFtP4B1cv2e6qDc4cC\n\txrz0U9B4jmMnKewGamyo6AiWS1FpIZHpaT9VCBKV3kGxb5CTOSD8DO69GKEOfI41l4GIvIOcgii\n\tupapCJUXot1x3iwrpQZUJwT1TeUCqb73ec5eifyEHQ3qcPUttHlSleJb/+hrtNckLM2AHKebg5q\n\tHknr4EjQZ//NEpHuHpWyFEL7nId2jaLq2AjBno9F9BLbg7HnV2+Fe545iNqcJsUeofrsu3sYg0W\n\tKX/dZP7CMYdIhfw==","X-Developer-Key":"i=jacopo.mondi@ideasonboard.com; a=openpgp;\n\tfpr=72392EDC88144A65C701EA9BA5826A2587AD026B","X-BeenThere":"libcamera-devel@lists.libcamera.org","X-Mailman-Version":"2.1.29","Precedence":"list","List-Id":"<libcamera-devel.lists.libcamera.org>","List-Unsubscribe":"<https://lists.libcamera.org/options/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=unsubscribe>","List-Archive":"<https://lists.libcamera.org/pipermail/libcamera-devel/>","List-Post":"<mailto:libcamera-devel@lists.libcamera.org>","List-Help":"<mailto:libcamera-devel-request@lists.libcamera.org?subject=help>","List-Subscribe":"<https://lists.libcamera.org/listinfo/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=subscribe>","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"},"content":"Template the LscAlgorithm class with the LSC engine register format and\nmake lsc::Components<> parameteric on the type used to store the\nquantized LSC gains.\n\nThis allows to quantize gains in the LscAlgorithm class and not in the\nIPA modules.\n\nMove the Components<> and ComponentsMap<> type to lsc.h in order for IPA\nmodules to be able to access the quantized gains, and make the existing\nfloat-based Components internal to LscImplementation to be used as the\nexchange format between LscImplementation and LscAlgorithm.\n\nSplit the LscAlgorithm class in two, one LscAlgorithmBase one for\nnon-templated function implementations and one for the templated\nconfigure() implementation and for templated data members and accessor.\n\nNote that currently table-based LSC data do not require quantization as\nthey're already expressed in register format in tuning files. This will\nchange soon to make it easier to re-use LSC data between different\nplatforms by expressing gains in floating point format in tuning files\nas well.\n\nSigned-off-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>\n---\n src/ipa/libipa/lsc.cpp            | 134 +++++++++++++++++++++++++++++---------\n src/ipa/libipa/lsc.h              |  83 +++++++++++++++++++----\n src/ipa/libipa/lsc_base.cpp       |  75 +++++++++------------\n src/ipa/libipa/lsc_base.h         |  31 ++-------\n src/ipa/libipa/lsc_polynomial.cpp |   6 +-\n src/ipa/libipa/lsc_polynomial.h   |   2 +-\n src/ipa/libipa/lsc_table.cpp      |   2 +-\n src/ipa/libipa/lsc_table.h        |   4 +-\n src/ipa/rkisp1/algorithms/lsc.cpp |  28 +++-----\n src/ipa/rkisp1/algorithms/lsc.h   |   6 +-\n 10 files changed, 231 insertions(+), 140 deletions(-)","diff":"diff --git a/src/ipa/libipa/lsc.cpp b/src/ipa/libipa/lsc.cpp\nindex 8c5c28077cce..bb02290fc9ad 100644\n--- a/src/ipa/libipa/lsc.cpp\n+++ b/src/ipa/libipa/lsc.cpp\n@@ -46,11 +46,91 @@ namespace lsc {\n  * \\brief Boolean flag for the LscAlgorithm updated status\n  */\n \n+/**\n+ * \\typedef Components\n+ * \\brief Associate colour components with a list of gains in register format\n+ * \\tparam T The type used to store the gain values\n+ *\n+ * LSC tables are defined as a list of gain values associated to a colour\n+ * component.\n+ *\n+ * As different ISPs support different colour components (usually 'r', 'gr',\n+ * 'gb', 'b' or just 'r', 'g', 'b') this class associates a string\n+ * identifier for the colour component to a list of gains.\n+ *\n+ * Each key name shall match an entry in the tuning file.\n+ *\n+ * The list of keys is provided to the LscAlgorithm class using \\a\n+ * LscDescriptor::keys.\n+ *\n+ * The gain values are expressed in fixed-point register format stored in\n+ * variabled of type \\a T, as each platform potentially has a different register\n+ * width.\n+ */\n+\n+/**\n+ * \\typedef ComponentsMap\n+ * \\brief Associate a colour temperature to a LSC table\n+ * \\tparam T The type of the gain values\n+ *\n+ * An LSC table is generated during the tuning phase for a specific colour\n+ * temperature, and a tuning file usually contains LSC tables generated for\n+ * several different colour temperatures.\n+ */\n+\n } /* namespace lsc */\n \n+#ifndef __DOXYGEN__\n+template<typename T>\n+void interpolateVector(const std::vector<T> &a,\n+\t\t       const std::vector<T> &b,\n+\t\t       std::vector<T> &dest, double lambda)\n+{\n+\tASSERT(a.size() == b.size());\n+\tdest.resize(a.size());\n+\tfor (size_t i = 0; i < a.size(); i++)\n+\t\tdest[i] = a[i] * (1.0 - lambda) + b[i] * lambda;\n+}\n+\n+template<>\n+void Interpolator<lsc::Components<uint16_t>>::\n+\tinterpolate(const lsc::Components<uint16_t> &a,\n+\t\t    const lsc::Components<uint16_t> &b,\n+\t\t    lsc::Components<uint16_t> &dest,\n+\t\t    double lambda)\n+{\n+\tfor (auto const &[k, v] : a)\n+\t\tinterpolateVector(v, b.at(k), dest[k], lambda);\n+}\n+#endif\n+\n+/**\n+ * \\class LscAlgorithmBase\n+ * \\brief Base class for LscAlgorithm\n+ *\n+ * Base class for LscAlgorithm for non-templated functions implementation\n+ */\n+\n+/**\n+ * \\var LscAlgorithmBase::impl_\n+ * \\brief The LSC algorithm implementation\n+ *\n+ * There are two classes derived from LscImplementation, the LscTable and\n+ * LscPolynomial ones. Which one to instantiate is decided by parsing the tuning\n+ * file.\n+ */\n+\n+/**\n+ * \\var LscAlgorithmBase::polynomial_\n+ * \\brief Boolean flag for polynomial LSC\n+ *\n+ * Set to true if polynomial LSC is in use.\n+ */\n+\n /**\n  * \\class LscAlgorithm\n  * \\brief libIPA LSC algorithm implementation\n+ * \\tparam U The platform fixed-point register format representation\n  *\n  * Due to the optical characteristics of the lens, the light intensity received\n  * by the sensor is not uniform. The Lens Shading Correction algorithm applies\n@@ -192,6 +272,14 @@ namespace lsc {\n  *\n  * \\todo Implement grid based re-sampling\n  *\n+ * After re-sampling, the LSC tables gain values are convereted from their\n+ * floating point representation (LscImplementation::Components) to the\n+ * platform's register representation (lsc::Components<>). Grid-based LSC tables\n+ * currently already contain gains represented in register format, so no\n+ * quantization is necessary but only a simple cast is required.\n+ *\n+ * \\todo Express gains in floating point format for grid-based LSC tables\n+ *\n  * When the IPA algorithms wants to get access to the (re-sampled) tables to\n  * program its LSC engine, it uses LscAlgorithm::interpolateComponents() to get\n  * an LSC table interpolated by the LscAlgorithm class for the specified colour\n@@ -209,8 +297,8 @@ namespace lsc {\n  *\n  * \\return 0 on success, a negative error code otherwise\n  */\n-int LscAlgorithm::init(const ValueNode &tuningData, ControlInfoMap::Map &controls,\n-\t\t       const LscDescriptor &descriptor)\n+int LscAlgorithmBase::init(const ValueNode &tuningData, ControlInfoMap::Map &controls,\n+\t\t\t   const LscDescriptor &descriptor)\n {\n \tpolynomial_ = false;\n \n@@ -245,12 +333,15 @@ int LscAlgorithm::init(const ValueNode &tuningData, ControlInfoMap::Map &control\n }\n \n /**\n+ * \\fn LscAlgorithm::configure()\n+ * \\brief Re-sample and quantize LSC data\n  * \\param[in] state The LSC active state\n  * \\param[in] analogCrop The current sensor analog crop rectangle\n  * \\param[in] xPos List of horizontal positions of the LSC grid nodes\n  * \\param[in] yPos List of vertical positions of the LSC grid nodes\n  *\n- * Re-sample the LSC data for an \\a analogCrop.\n+ * Re-sample the LSC data for an \\a analogCrop and convert gains to their\n+ * register representation using the class template paramter \\a U.\n  *\n  * LSC tables are generated at tuning time using a known sensor configuration.\n  * When a new streaming session is started, it might use a different sensor\n@@ -263,30 +354,12 @@ int LscAlgorithm::init(const ValueNode &tuningData, ControlInfoMap::Map &control\n  *\n  * \\sa LscImplementation::sampleForCrop\n  *\n+ * Once tables have been re-sampled, they get quantized to the platform's\n+ * fixed-point register representation using the LscAlgorithm template parameter\n+ * \\a U.\n+ *\n  * \\return 0 on success, a negative error code otherwise\n  */\n-int LscAlgorithm::configure(lsc::ActiveState &state, const Rectangle &analogCrop,\n-\t\t\t    const std::vector<double> &xPos,\n-\t\t\t    const std::vector<double> &yPos)\n-{\n-\tLOG(Lsc, Debug) << \"Sample Lsc data for \" << analogCrop;\n-\tlsc::ComponentsMap lscData =\n-\t\timpl_->sampleForCrop(analogCrop, xPos, yPos);\n-\n-\t/*\n-\t * Retain a copy of the components table.\n-\t *\n-\t * We could avoid a copy here if getComponents() could\n-\t * return sets_.data() but I wasn't able to work around the\n-\t * compiler refusing it.\n-\t */\n-\tlscData_ = lscData;\n-\n-\tsets_.setData(std::move(lscData));\n-\tstate.enabled = true;\n-\n-\treturn 0;\n-}\n \n /**\n  * \\brief Queue a request to the lsc algorithm\n@@ -297,9 +370,9 @@ int LscAlgorithm::configure(lsc::ActiveState &state, const Rectangle &analogCrop\n  * Queue a new list of \\a controls to the lsc algorithm.\n  * The only supported control is controls::LensShadingCorrectionEnable.\n  */\n-void LscAlgorithm::queueRequest(lsc::ActiveState &state,\n-\t\t\t\tlsc::FrameContext &context,\n-\t\t\t\tconst ControlList &controls)\n+void LscAlgorithmBase::queueRequest(lsc::ActiveState &state,\n+\t\t\t\t    lsc::FrameContext &context,\n+\t\t\t\t    const ControlList &controls)\n {\n \tconst auto &lscEnable = controls.get(controls::LensShadingCorrectionEnable);\n \tif (lscEnable && *lscEnable != state.enabled) {\n@@ -322,7 +395,7 @@ void LscAlgorithm::queueRequest(lsc::ActiveState &state,\n  * Populates the list of \\a metadata with controls handled by the LscAlgorithm\n  * class. The only supported metadata is controls::LensShadingCorrectionEnable.\n  */\n-void LscAlgorithm::process(lsc::FrameContext &context, ControlList &metadata)\n+void LscAlgorithmBase::process(lsc::FrameContext &context, ControlList &metadata)\n {\n \tmetadata.set(controls::LensShadingCorrectionEnable, context.enabled);\n }\n@@ -350,8 +423,7 @@ void LscAlgorithm::process(lsc::FrameContext &context, ControlList &metadata)\n \n /**\n  * \\fn LscAlgorithm::getComponents\n- *\n- * Return the map of LSC data per-colour-temperature.\n+ * \\brief Return the map of LSC data per-colour-temperature\n  *\n  * Calling this function is only valid after LscAlgorithm::configure() has been\n  * called. An empty components list is returned otherwise.\ndiff --git a/src/ipa/libipa/lsc.h b/src/ipa/libipa/lsc.h\nindex 9fe8ad67ea33..30330f284013 100644\n--- a/src/ipa/libipa/lsc.h\n+++ b/src/ipa/libipa/lsc.h\n@@ -33,37 +33,96 @@ struct FrameContext {\n \tbool update;\n };\n \n+template<typename T>\n+using Components = std::map<std::string, std::vector<T>, std::less<>>;\n+\n+template<typename T>\n+using ComponentsMap = std::map<unsigned int, Components<T>>;\n+\n } /* namespace lsc */\n \n-class LscAlgorithm\n+#ifndef __DOXYGEN__\n+template<>\n+void Interpolator<lsc::Components<uint16_t>>::\n+\tinterpolate(const lsc::Components<uint16_t> &a,\n+\t\t    const lsc::Components<uint16_t> &b,\n+\t\t    lsc::Components<uint16_t> &dest,\n+\t\t    double lambda);\n+#endif /* __DOXYGEN__ */\n+\n+class LscAlgorithmBase\n {\n public:\n \tint init(const ValueNode &tuningData, ControlInfoMap::Map &controls,\n \t\t const LscDescriptor &descriptor);\n \n-\tint configure(lsc::ActiveState &state, const Rectangle &analogCrop,\n-\t\t      const std::vector<double> &xPos,\n-\t\t      const std::vector<double> &yPos);\n-\n \tvoid queueRequest(lsc::ActiveState &state, lsc::FrameContext &context,\n \t\t\t  const ControlList &controls);\n \tvoid process(lsc::FrameContext &context, ControlList &metadata);\n \n-\tconst lsc::Components interpolateComponents(unsigned int ct)\n+protected:\n+\tLscAlgorithmBase() = default;\n+\n+\tstd::unique_ptr<LscImplementation> impl_;\n+\tbool polynomial_;\n+};\n+\n+template<typename U>\n+class LscAlgorithm : public LscAlgorithmBase\n+{\n+private:\n+\tusing Components = lsc::Components<typename U::QuantizedType>;\n+\tusing ComponentsMap = lsc::ComponentsMap<typename U::QuantizedType>;\n+\n+public:\n+\tLscAlgorithm() = default;\n+\n+\tint configure(lsc::ActiveState &state, const Rectangle &analogCrop,\n+\t\t      const std::vector<double> &xPos,\n+\t\t      const std::vector<double> &yPos)\n+\t{\n+\t\tLscImplementation::ComponentsMap data =\n+\t\t\timpl_->sampleForCrop(analogCrop, xPos, yPos);\n+\n+\t\tComponentsMap lscData;\n+\t\tfor (const auto &[t, c] : data) {\n+\t\t\tComponents &comp = lscData[t];\n+\n+\t\t\tfor (const auto &[k, gains] : c) {\n+\t\t\t\tauto &quantizedGains = comp[k];\n+\t\t\t\tquantizedGains.reserve(gains.size());\n+\n+\t\t\t\tfor (const float &gain : gains) {\n+\t\t\t\t\t/*\n+\t\t\t\t\t * Tabular LSC tables already store\n+\t\t\t\t\t * quantized values.\n+\t\t\t\t\t */\n+\t\t\t\t\tif (polynomial_)\n+\t\t\t\t\t\tquantizedGains.push_back(U(gain).quantized());\n+\t\t\t\t\telse\n+\t\t\t\t\t\tquantizedGains.push_back(gain);\n+\t\t\t\t}\n+\t\t\t}\n+\t\t}\n+\n+\t\tsets_.setData(std::move(lscData));\n+\t\tstate.enabled = true;\n+\n+\t\treturn 0;\n+\t}\n+\n+\tconst Components interpolateComponents(unsigned int ct)\n \t{\n \t\treturn sets_.getInterpolated(ct);\n \t}\n \n-\tconst lsc::ComponentsMap getComponents()\n+\tconst ComponentsMap &getComponents() const\n \t{\n-\t\treturn lscData_;\n+\t\treturn sets_.data();\n \t}\n \n private:\n-\tstd::unique_ptr<LscImplementation> impl_;\n-\tInterpolator<lsc::Components> sets_;\n-\tlsc::ComponentsMap lscData_;\n-\tbool polynomial_;\n+\tInterpolator<Components> sets_;\n };\n \n } /* namespace ipa */\ndiff --git a/src/ipa/libipa/lsc_base.cpp b/src/ipa/libipa/lsc_base.cpp\nindex cb770f164d53..baf25df6b908 100644\n--- a/src/ipa/libipa/lsc_base.cpp\n+++ b/src/ipa/libipa/lsc_base.cpp\n@@ -16,49 +16,6 @@ namespace libcamera {\n \n namespace ipa {\n \n-namespace lsc {\n-\n-/**\n- * \\typedef Components\n- * \\brief Associate colour components with a list of gains\n- *\n- * LSC tables are defined as a list of gain values associated to a colour\n- * component.\n- *\n- * As different ISPs support different colour components (usually 'r', 'gr',\n- * 'gb', 'b' or just 'r', 'g', 'b') this class associates a string\n- * identifier for the colour component to a list of gains.\n- *\n- * Each key name shall match an entry in the tuning file.\n- *\n- * The list of keys is provided to the LscAlgorithm class using \\a\n- * LscDescriptor::keys.\n- */\n-\n-/**\n- * \\typedef ComponentsMap\n- * \\brief Associate a colour temperature to a LSC table\n- *\n- * An LSC table is generated during the tuning phase for a specific colour\n- * temperature, and a tuning file usually contains LSC tables generated for\n- * several different colour temperatures.\n- */\n-\n-} /* namespace lsc */\n-\n-#ifndef __DOXYGEN__\n-template<>\n-void Interpolator<lsc::Components>::\n-\tinterpolate(const lsc::Components &a,\n-\t\t    const lsc::Components &b,\n-\t\t    lsc::Components &dest,\n-\t\t    double lambda)\n-{\n-\tfor (auto const &[k, v] : a)\n-\t\tinterpolateVector(v, b.at(k), dest[k], lambda);\n-}\n-#endif\n-\n /**\n  * \\struct LscDescriptor\n  * \\brief Describe the ISP LSC engine\n@@ -90,6 +47,38 @@ void Interpolator<lsc::Components>::\n  * Defines the interface for the LSC algorithm implementation.\n  */\n \n+/**\n+ * \\typedef LscImplementation::Components\n+ * \\brief Associate colour components with a list of gains in float format\n+ *\n+ * LSC tables are defined as a list of gain values associated to a colour\n+ * component.\n+ *\n+ * As different ISP support different colour components (usually 'r', 'gr',\n+ * 'gb', 'b' or just 'r', 'g', 'b') this class associates a string\n+ * identifier for the colour component to a list of gains.\n+ *\n+ * The gain values are represented as floats and are either loaded from tuning\n+ * file or are the result of a polynomial expansion, depending on the\n+ * LscImplementation derived class in use (LscTable or LscPolynomial).\n+ *\n+ * The gain values are converted to the platform's register format before being\n+ * returned to the IPA module by LscAlgorithm::configure().\n+ *\n+ * \\todo Tabular LSC data are already expressed in register format. Make the\n+ * tuning files express gains in float format as well and actually perform\n+ * quantization in LscAlgorithm::configure().\n+ */\n+\n+/**\n+ * \\typedef LscImplementation::ComponentsMap\n+ * \\brief Associate a colour temperature to a LSC table\n+ *\n+ * An LSC table is generated during the tuning phase for a specific colour\n+ * temperature, and a tuning file usually contains LSC tables generated for\n+ * several different colour temperatures.\n+ */\n+\n /**\n  * \\fn LscImplementation::~LscImplementation\n  * \\brief Virtual class destructor\ndiff --git a/src/ipa/libipa/lsc_base.h b/src/ipa/libipa/lsc_base.h\nindex 2cff34b32a2b..d53736cb71d3 100644\n--- a/src/ipa/libipa/lsc_base.h\n+++ b/src/ipa/libipa/lsc_base.h\n@@ -24,32 +24,6 @@ namespace libcamera {\n \n namespace ipa {\n \n-namespace lsc {\n-\n-using Components = std::map<std::string, std::vector<float>, std::less<>>;\n-using ComponentsMap = std::map<unsigned int, Components>;\n-\n-} /* namespace lsc */\n-\n-#ifndef __DOXYGEN__\n-template<typename T>\n-void interpolateVector(const std::vector<T> &a, const std::vector<T> &b,\n-\t\t       std::vector<T> &dest, double lambda)\n-{\n-\tASSERT(a.size() == b.size());\n-\tdest.resize(a.size());\n-\tfor (size_t i = 0; i < a.size(); i++)\n-\t\tdest[i] = a[i] * (1.0 - lambda) + b[i] * lambda;\n-}\n-\n-template<>\n-void Interpolator<lsc::Components>::\n-\tinterpolate(const lsc::Components &a,\n-\t\t    const lsc::Components &b,\n-\t\t    lsc::Components &dest,\n-\t\t    double lambda);\n-#endif /* __DOXYGEN__ */\n-\n struct LscDescriptor {\n \tstd::vector<std::string> keys;\n \tunsigned int numHSamples;\n@@ -60,12 +34,15 @@ struct LscDescriptor {\n class LscImplementation\n {\n public:\n+\tusing Components = std::map<std::string, std::vector<float>, std::less<>>;\n+\tusing ComponentsMap = std::map<unsigned int, Components>;\n+\n \tvirtual ~LscImplementation() {}\n \n \tvirtual int parseLscData(const ValueNode &tuningData,\n \t\t\t\t const LscDescriptor &descriptor) = 0;\n \n-\tvirtual lsc::ComponentsMap\n+\tvirtual ComponentsMap\n \tsampleForCrop(const Rectangle &cropRectangle,\n \t\t      std::vector<double> xPos, std::vector<double> yPos) = 0;\n };\ndiff --git a/src/ipa/libipa/lsc_polynomial.cpp b/src/ipa/libipa/lsc_polynomial.cpp\nindex 419de2c12cf0..6c38c7f7eeb1 100644\n--- a/src/ipa/libipa/lsc_polynomial.cpp\n+++ b/src/ipa/libipa/lsc_polynomial.cpp\n@@ -194,15 +194,15 @@ int LscPolynomial::parseLscData(const ValueNode &sets,\n  * [0, 0.0625, 0.125, 0.1875, ... , 1]. It is expected that the first position\n  * is 0 and the last position is 1.\n  */\n-lsc::ComponentsMap\n+LscImplementation::ComponentsMap\n LscPolynomial::sampleForCrop(const Rectangle &cropRectangle,\n \t\t\t     std::vector<double> xPos, std::vector<double> yPos)\n {\n \n-\tlsc::ComponentsMap components;\n+\tLscImplementation::ComponentsMap components;\n \n \tfor (const auto &[t, c] : lscData_) {\n-\t\tlsc::Components &comp = components[t];\n+\t\tLscImplementation::Components &comp = components[t];\n \n \t\tfor (const auto &[k, p] : c)\n \t\t\tcomp.try_emplace(k, samplePolynomial(p, xPos, yPos,\ndiff --git a/src/ipa/libipa/lsc_polynomial.h b/src/ipa/libipa/lsc_polynomial.h\nindex 24b216a1aa25..b9d3d834662b 100644\n--- a/src/ipa/libipa/lsc_polynomial.h\n+++ b/src/ipa/libipa/lsc_polynomial.h\n@@ -60,7 +60,7 @@ public:\n \tint parseLscData(const ValueNode &sets,\n \t\t\t const LscDescriptor &descriptor) override;\n \n-\tlsc::ComponentsMap\n+\tLscImplementation::ComponentsMap\n \tsampleForCrop(const Rectangle &cropRectangle,\n \t\t      std::vector<double> xPos, std::vector<double> yPos) override;\n \ndiff --git a/src/ipa/libipa/lsc_table.cpp b/src/ipa/libipa/lsc_table.cpp\nindex 3e8b1a060ce7..74ea8a7266e1 100644\n--- a/src/ipa/libipa/lsc_table.cpp\n+++ b/src/ipa/libipa/lsc_table.cpp\n@@ -59,7 +59,7 @@ int LscTable::parseLscData(const ValueNode &sets,\n int LscTable::parseLscComponent(const ValueNode &yamlSet,\n \t\t\t\tunsigned int ct, const LscDescriptor &descriptor)\n {\n-\tlsc::Components component;\n+\tLscImplementation::Components component;\n \tfor (auto &k : descriptor.keys) {\n \t\tauto [it, inserted] =\n \t\t\tcomponent.try_emplace(k, parseTable(yamlSet,\ndiff --git a/src/ipa/libipa/lsc_table.h b/src/ipa/libipa/lsc_table.h\nindex 6d9b0c692b7f..9f39b38e258c 100644\n--- a/src/ipa/libipa/lsc_table.h\n+++ b/src/ipa/libipa/lsc_table.h\n@@ -29,7 +29,7 @@ public:\n \tint parseLscData(const ValueNode &sets,\n \t\t\t const LscDescriptor &descriptor) override;\n \n-\tlsc::ComponentsMap\n+\tLscImplementation::ComponentsMap\n \tsampleForCrop([[maybe_unused]] const Rectangle &cropRectangle,\n \t\t      [[maybe_unused]] std::vector<double> xPos,\n \t\t      [[maybe_unused]] std::vector<double> yPos) override\n@@ -47,7 +47,7 @@ private:\n \t\t\t\t\t unsigned int numHSamples,\n \t\t\t\t\t unsigned int numVSamples);\n \n-\tlsc::ComponentsMap lscData_;\n+\tLscImplementation::ComponentsMap lscData_;\n };\n \n } /* namespace ipa */\ndiff --git a/src/ipa/rkisp1/algorithms/lsc.cpp b/src/ipa/rkisp1/algorithms/lsc.cpp\nindex 911ac6895a14..f2858656023e 100644\n--- a/src/ipa/rkisp1/algorithms/lsc.cpp\n+++ b/src/ipa/rkisp1/algorithms/lsc.cpp\n@@ -175,23 +175,16 @@ void LensShadingCorrection::setParameters(rkisp1_cif_isp_lsc_config &config)\n }\n \n void LensShadingCorrection::copyTable(rkisp1_cif_isp_lsc_config &config,\n-\t\t\t\t      const lsc::Components &set)\n+\t\t\t\t      const lsc::Components<uint16_t> &set)\n {\n-\t/*\n-\t * The hardware uses 2.10 fixed point format and limits the legal values\n-\t * to [1..3.999]. Scale and clamp the sampled values accordingly.\n-\t */\n-\tconst auto quantizeSet = [&](const std::string &key, uint16_t *dst) {\n-\t\tconst auto &s = set.at(key);\n-\t\tstd::transform(s.begin(), s.end(), dst, [](float f) {\n-\t\t\t\treturn std::clamp<uint16_t>(f * 1024, 1024, 4095);\n-\t\t\t\t});\n-\t};\n-\n-\tquantizeSet(\"r\", &config.r_data_tbl[0][0]);\n-\tquantizeSet(\"gr\", &config.gr_data_tbl[0][0]);\n-\tquantizeSet(\"gb\", &config.gb_data_tbl[0][0]);\n-\tquantizeSet(\"b\", &config.b_data_tbl[0][0]);\n+\tconst auto &r = set.at(\"r\");\n+\tstd::copy(r.begin(), r.end(), &config.r_data_tbl[0][0]);\n+\tconst auto &gr = set.at(\"gr\");\n+\tstd::copy(gr.begin(), gr.end(), &config.gr_data_tbl[0][0]);\n+\tconst auto &gb = set.at(\"gb\");\n+\tstd::copy(gb.begin(), gb.end(), &config.gb_data_tbl[0][0]);\n+\tconst auto &b = set.at(\"b\");\n+\tstd::copy(b.begin(), b.end(), &config.b_data_tbl[0][0]);\n }\n \n /**\n@@ -241,8 +234,7 @@ void LensShadingCorrection::prepare([[maybe_unused]] IPAContext &context,\n \n \tsetParameters(*config);\n \n-\tconst lsc::Components &set = lscAlgo_.interpolateComponents(quantizedCt);\n-\tcopyTable(*config, set);\n+\tcopyTable(*config, lscAlgo_.interpolateComponents(quantizedCt));\n \n \tlastAppliedCt_ = ct;\n \tlastAppliedQuantizedCt_ = quantizedCt;\ndiff --git a/src/ipa/rkisp1/algorithms/lsc.h b/src/ipa/rkisp1/algorithms/lsc.h\nindex 63f0887f0838..c1f8904426c0 100644\n--- a/src/ipa/rkisp1/algorithms/lsc.h\n+++ b/src/ipa/rkisp1/algorithms/lsc.h\n@@ -12,6 +12,7 @@\n #include <linux/rkisp1-config.h>\n \n #include \"libcamera/internal/value_node.h\"\n+#include \"libipa/fixedpoint.h\"\n \n #include \"libipa/lsc.h\"\n \n@@ -41,7 +42,8 @@ public:\n \t\t     ControlList &metadata) override;\n private:\n \tvoid setParameters(rkisp1_cif_isp_lsc_config &config);\n-\tvoid copyTable(rkisp1_cif_isp_lsc_config &config, const lsc::Components &set0);\n+\tvoid copyTable(rkisp1_cif_isp_lsc_config &config,\n+\t\t       const lsc::Components<uint16_t> &set);\n \n \tstd::vector<double> xSize_;\n \tstd::vector<double> ySize_;\n@@ -55,7 +57,7 @@ private:\n \tunsigned int lastAppliedCt_;\n \tunsigned int lastAppliedQuantizedCt_;\n \n-\tLscAlgorithm lscAlgo_;\n+\tLscAlgorithm<UQ<2, 10>> lscAlgo_;\n };\n \n } /* namespace ipa::rkisp1::algorithms */\n","prefixes":["v7","28/32"]}