Patch Detail
Show a patch.
GET /api/1.1/patches/27229/?format=api
{ "id": 27229, "url": "https://patchwork.libcamera.org/api/1.1/patches/27229/?format=api", "web_url": "https://patchwork.libcamera.org/patch/27229/", "project": { "id": 1, "url": "https://patchwork.libcamera.org/api/1.1/projects/1/?format=api", "name": "libcamera", "link_name": "libcamera", "list_id": "libcamera_core", "list_email": "libcamera-devel@lists.libcamera.org", "web_url": "", "scm_url": "", "webscm_url": "" }, "msgid": "<20260706-libipa-algorithms-v3-29-968757b038bb@ideasonboard.com>", "date": "2026-07-06T08:01:49", "name": "[v3,29/35] ipa: libipa: lsc: Template LscAlgorithm with register format", "commit_ref": null, "pull_url": null, "state": "new", "archived": false, "hash": "8179fecd4b3b23e8ee7eb964f971fd4f156ed8aa", "submitter": { "id": 143, "url": "https://patchwork.libcamera.org/api/1.1/people/143/?format=api", "name": "Jacopo Mondi", "email": "jacopo.mondi@ideasonboard.com" }, "delegate": null, "mbox": "https://patchwork.libcamera.org/patch/27229/mbox/", "series": [ { "id": 6038, "url": "https://patchwork.libcamera.org/api/1.1/series/6038/?format=api", "web_url": "https://patchwork.libcamera.org/project/libcamera/list/?series=6038", "date": "2026-07-06T08:01:20", "name": "ipa: libipa: Introduce libipa algorithms", "version": 3, "mbox": "https://patchwork.libcamera.org/series/6038/mbox/" } ], "comments": "https://patchwork.libcamera.org/api/patches/27229/comments/", "check": "pending", "checks": "https://patchwork.libcamera.org/api/patches/27229/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 6887BC3318\n\tfor <parsemail@patchwork.libcamera.org>;\n\tMon, 6 Jul 2026 08:02:37 +0000 (UTC)", "from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 06F8F66069;\n\tMon, 6 Jul 2026 10:02:37 +0200 (CEST)", "from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[213.167.242.64])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 07AB266031\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tMon, 6 Jul 2026 10:02:16 +0200 (CEST)", "from [192.168.125.177] (mob-109-113-60-81.net.vodafone.it\n\t[109.113.60.81])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id 7C4A93FE8;\n\tMon, 6 Jul 2026 10:01:27 +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=\"ETRrjvWP\"; dkim-atps=neutral", "DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1783324888;\n\tbh=WEvfaM6MO/uOsVIRRFYs+XFPvBWUs8JbZ1zNEbDqhtg=;\n\th=From:Date:Subject:References:In-Reply-To:To:Cc:From;\n\tb=ETRrjvWPTlPo1XM181ztjhecX29DQYBBmTwgfoRnCCQySmWmaSRHatuDEFxJlA+LB\n\tunH7csiZbR49Sm9mWGrjeCuMRqTHDxsh6igGgx+ihkgVX06W9xrrEWOg7ixL5xq3E8\n\tZfO4A6FSmOesnLAzjveevq23aYqy8R0y4zSpAD1U=", "From": "Jacopo Mondi <jacopo.mondi@ideasonboard.com>", "Date": "Mon, 06 Jul 2026 10:01:49 +0200", "Subject": "[PATCH v3 29/35] ipa: libipa: lsc: Template LscAlgorithm with\n\tregister format", "MIME-Version": "1.0", "Content-Type": "text/plain; charset=\"utf-8\"", "Content-Transfer-Encoding": "7bit", "Message-Id": "<20260706-libipa-algorithms-v3-29-968757b038bb@ideasonboard.com>", "References": "<20260706-libipa-algorithms-v3-0-968757b038bb@ideasonboard.com>", "In-Reply-To": "<20260706-libipa-algorithms-v3-0-968757b038bb@ideasonboard.com>", "To": "libcamera-devel@lists.libcamera.org, \n\tStefan Klug <stefan.klug@ideasonboard.com>", "Cc": "Jacopo Mondi <jacopo.mondi@ideasonboard.com>", "X-Mailer": "b4 0.14.3", "X-Developer-Signature": "v=1; a=openpgp-sha256; l=23337;\n\ti=jacopo.mondi@ideasonboard.com; h=from:subject:message-id;\n\tbh=WEvfaM6MO/uOsVIRRFYs+XFPvBWUs8JbZ1zNEbDqhtg=;\n\tb=owEBbQKS/ZANAwAKAXI0Bo8WoVY8AcsmYgBqS2DyJunQgTeZ6lynttiVkuB0bLgDO1dtzP7qo\n\t4tlNCMksViJAjMEAAEKAB0WIQS1xD1IgJogio9YOMByNAaPFqFWPAUCaktg8gAKCRByNAaPFqFW\n\tPEZwD/9CeRXfVdiLB3FlTRA+Sn+50Wssqo8i7bAjP9F5/V1Yebv3i5UY7SlTau6Nv02ccF2TYbY\n\tGZE7/oyl4ygbhwnurterckIPlHTUxs0BI/SCDCdwxShwkEt6s9HgGo4nFl4tVbTx2Y+zEBg1RhX\n\tEk53lSGqY41cYvayKvCesDCfqPqvskliXog8sYrM+furZRpdRqcLiD3pva39tUlSQEq3iFyfOWA\n\tUesN0AXZ7xDgn53iPu6gS3q+1nlhOyM9tuCQ24Sg/GSeKnxWIrgJkGLxaC+D5IUHuPbDOahtuuO\n\thHlt4s0uPUFg28LPgfiWWMl+a+H3hRbrx8Utn/L7GRtzGierdXOnqkX8fWqu6zNAcbCAt5MDZAh\n\ttMdciATF3O1aHMeagLaLM/28+anFQgHuvBMxM9/aRpQpcGbW9AzCuULwfXvvkg2aLVE7QkL9tQn\n\t0su1eZruu4dnB1QCKwBSh6h9bkkOauKWC4oSGL6bnQX1VfTDjYliuGBZyNUq9ldihWPGxP3tHZs\n\t8T+kKZvVkd8VvciJIsYWi6+RFgnSYEB8ewP67nxkY3aNH1zS2DwsLT9/ESvsAvvJeWVsbXd3yNf\n\t+IMF2/W6c22/AmQzwt7u450jNsmMzprqvsskz5w/AjOfY6icxLR9BouHDd3+e882IZLTdp1lM2V\n\tcRDaOoigA+azhXw==", "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": "The LscPolynomial::samplePolynomial function expands the radial\npolynomial assuming the RkISP1 Q<2, 10> register format:\n\n /*\n\t* The hardware uses 2.10 fixed point format and limits\n\t* the legal values to [1..3.999]. Scale and clamp the\n\t* sampled value accordingly.\n\t*/\n int v = static_cast<int>(\n\t poly.sampleAtNormalizedPixelPos(xp, yp) *\n\t 1024);\n v = std::clamp(v, 1024, 4095);\n\nIn order to remove all assumptions about the platform register format\ntemplate the LscAlgorithm class with the fixed point format of the\nISP Lsc engine registers.\n\n float sample = static_cast<float>\n\t\t (poly.sampleAtNormalizedPixelPos(xp, yp));\n\n samples.push_back(U(sample).quantized());\n\nIn order to template the LscPolynomial class, template the whole class\nhierarchy including the LscTable class that doesn't need (yet) the\ntemplate argument but will use it once re-sampling LscTable will be\nimplemented.\n\nSplit all involved classes (LscAlgorithm, LscTable and LscPolynomial) in\ntwo classes: a Lsc*Base class for non-templated function implementations\nand a Lsc* class for templated functions implementations.\n\nSigned-off-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>\n---\n src/ipa/libipa/lsc.cpp | 151 +++++++++++++-------------------------\n src/ipa/libipa/lsc.h | 81 +++++++++++++++++---\n src/ipa/libipa/lsc_base.cpp | 1 +\n src/ipa/libipa/lsc_base.h | 1 +\n src/ipa/libipa/lsc_polynomial.cpp | 90 +++++++----------------\n src/ipa/libipa/lsc_polynomial.h | 67 +++++++++++++++--\n src/ipa/libipa/lsc_table.cpp | 49 +++++++++----\n src/ipa/libipa/lsc_table.h | 35 ++++++---\n src/ipa/rkisp1/algorithms/lsc.h | 3 +-\n 9 files changed, 270 insertions(+), 208 deletions(-)", "diff": "diff --git a/src/ipa/libipa/lsc.cpp b/src/ipa/libipa/lsc.cpp\nindex c1207292518a..621c04d354a8 100644\n--- a/src/ipa/libipa/lsc.cpp\n+++ b/src/ipa/libipa/lsc.cpp\n@@ -7,13 +7,6 @@\n \n #include \"lsc.h\"\n \n-#include <libcamera/base/log.h>\n-\n-#include <libcamera/control_ids.h>\n-\n-#include \"lsc_polynomial.h\"\n-#include \"lsc_table.h\"\n-\n /**\n * \\file lsc.h\n * \\brief libipa lsc algorithm\n@@ -48,9 +41,56 @@ namespace lsc {\n \n } /* namespace lsc */\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+ * \\brief Queue a request to the lsc algorithm\n+ * \\param[in] state The lsc active state\n+ * \\param[in] context The lsc frame context\n+ * \\param[in] controls The list of controls associated with a Request\n+ *\n+ * Queue a new list of \\a controls to the lsc algorithm.\n+ * The only supported control is controls::LensShadingCorrectionEnable.\n+ */\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+\t\tstate.enabled = *lscEnable;\n+\n+\t\tLOG(Lsc, Debug)\n+\t\t\t<< (state.enabled ? \"Enabling\" : \"Disabling\") << \" Lsc\";\n+\n+\t\tcontext.update = true;\n+\t}\n+\n+\tcontext.enabled = state.enabled;\n+}\n+\n+/**\n+ * \\brief Populate the list of lsc metadata\n+ * \\param[in] context The lsc frame context\n+ * \\param[in] metadata The list of metadata\n+ *\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 LscAlgorithmBase::process(lsc::FrameContext &context, ControlList &metadata)\n+{\n+\tmetadata.set(controls::LensShadingCorrectionEnable, context.enabled);\n+}\n+\n /**\n * \\class LscAlgorithm\n * \\brief libIPA lsc algorithm implementation\n+ * \\tparam U The fixedpoint lsc engine register format\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@@ -200,6 +240,7 @@ namespace lsc {\n */\n \n /**\n+ * \\fn LscAlgorithm::init()\n * \\param[in] tuningData The tuning data\n * \\param[in] controls The IPA list of supported controls\n * \\param[in] descriptor The lsc engine descriptor\n@@ -209,42 +250,9 @@ 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-{\n-\tpolynomial_ = false;\n-\n-\tstd::string type = tuningData[\"type\"].get<std::string>(\"table\");\n-\tif (type == \"table\") {\n-\t\timpl_ = std::make_unique<LscTable>();\n-\t\tLOG(Lsc, Debug) << \"Using table-based Lsc\";\n-\t} else if (type == \"polynomial\") {\n-\t\timpl_ = std::make_unique<LscPolynomial>();\n-\t\tpolynomial_ = true;\n-\t\tLOG(Lsc, Debug) << \"Using polynomial Lsc\";\n-\t} else {\n-\t\tLOG(Lsc, Error) << \"Unsupported Lsc algorithm '\"\n-\t\t\t\t<< type << \"'\";\n-\t\treturn -EINVAL;\n-\t}\n-\n-\tconst ValueNode &yamlSets = tuningData[\"sets\"];\n-\tif (!yamlSets.isList()) {\n-\t\tLOG(Lsc, Error) << \"'sets' parameter not found in tuning file\";\n-\t\treturn -EINVAL;\n-\t}\n-\n-\tint ret = impl_->parseLscData(yamlSets, descriptor);\n-\tif (ret)\n-\t\treturn ret;\n-\n-\tcontrols[&controls::LensShadingCorrectionEnable] =\n-\t\tControlInfo(false, true, true);\n-\n-\treturn 0;\n-}\n \n /**\n+ * \\fn LscAlgorithm::configure()\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@@ -265,67 +273,6 @@ int LscAlgorithm::init(const ValueNode &tuningData, ControlInfoMap::Map &control\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- * \\param[in] state The lsc active state\n- * \\param[in] context The lsc frame context\n- * \\param[in] controls The list of controls associated with a Request\n- *\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-{\n-\tconst auto &lscEnable = controls.get(controls::LensShadingCorrectionEnable);\n-\tif (lscEnable && *lscEnable != state.enabled) {\n-\t\tstate.enabled = *lscEnable;\n-\n-\t\tLOG(Lsc, Debug)\n-\t\t\t<< (state.enabled ? \"Enabling\" : \"Disabling\") << \" Lsc\";\n-\n-\t\tcontext.update = true;\n-\t}\n-\n-\tcontext.enabled = state.enabled;\n-}\n-\n-/**\n- * \\brief Populate the list of lsc metadata\n- * \\param[in] context The lsc frame context\n- * \\param[in] metadata The list of metadata\n- *\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-{\n-\tmetadata.set(controls::LensShadingCorrectionEnable, context.enabled);\n-}\n \n /**\n * \\fn LscAlgorithm::interpolateComponents\ndiff --git a/src/ipa/libipa/lsc.h b/src/ipa/libipa/lsc.h\nindex 9fe8ad67ea33..d48336b169f4 100644\n--- a/src/ipa/libipa/lsc.h\n+++ b/src/ipa/libipa/lsc.h\n@@ -8,18 +8,25 @@\n #pragma once\n \n #include <memory>\n+#include <string>\n #include <vector>\n \n+#include <libcamera/base/log.h>\n+\n+#include <libcamera/control_ids.h>\n #include <libcamera/controls.h>\n #include <libcamera/geometry.h>\n \n #include \"libcamera/internal/value_node.h\"\n \n #include \"interpolator.h\"\n-#include \"lsc_base.h\"\n+#include \"lsc_polynomial.h\"\n+#include \"lsc_table.h\"\n \n namespace libcamera {\n \n+LOG_DECLARE_CATEGORY(Lsc)\n+\n namespace ipa {\n \n namespace lsc {\n@@ -35,19 +42,75 @@ struct FrameContext {\n \n } /* namespace lsc */\n \n-class LscAlgorithm\n+class LscAlgorithmBase\n+{\n+public:\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+\n+template<typename U>\n+class LscAlgorithm : public LscAlgorithmBase\n {\n public:\n \tint init(const ValueNode &tuningData, ControlInfoMap::Map &controls,\n-\t\t const LscDescriptor &descriptor);\n+\t\t const LscDescriptor &descriptor)\n+\t{\n+\t\tpolynomial_ = false;\n+\n+\t\tstd::string type = tuningData[\"type\"].get<std::string>(\"table\");\n+\t\tif (type == \"table\") {\n+\t\t\timpl_ = std::make_unique<LscTable<U>>();\n+\t\t\tLOG(Lsc, Debug) << \"Using table-based Lsc\";\n+\t\t} else if (type == \"polynomial\") {\n+\t\t\timpl_ = std::make_unique<LscPolynomial<U>>();\n+\t\t\tpolynomial_ = true;\n+\t\t\tLOG(Lsc, Debug) << \"Using polynomial Lsc\";\n+\t\t} else {\n+\t\t\tLOG(Lsc, Error) << \"Unsupported Lsc algorithm '\"\n+\t\t\t\t\t<< type << \"'\";\n+\t\t\treturn -EINVAL;\n+\t\t}\n+\n+\t\tconst ValueNode &yamlSets = tuningData[\"sets\"];\n+\t\tif (!yamlSets.isList()) {\n+\t\t\tLOG(Lsc, Error) << \"'sets' parameter not found in tuning file\";\n+\t\t\treturn -EINVAL;\n+\t\t}\n+\n+\t\tint ret = impl_->parseLscData(yamlSets, descriptor);\n+\t\tif (ret)\n+\t\t\treturn ret;\n+\n+\t\tcontrols[&controls::LensShadingCorrectionEnable] =\n+\t\t\tControlInfo(false, true, true);\n+\n+\t\treturn 0;\n+\t}\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+\t\t const std::vector<double> &yPos)\n+\t{\n+\t\tLOG(Lsc, Debug) << \"Sample Lsc data for \" << analogCrop;\n+\t\tlsc::ComponentsMap lscData =\n+\t\t\timpl_->sampleForCrop(analogCrop, xPos, yPos);\n+\n+\t\t/*\n+\t\t * Retain a copy of the components table.\n+\t\t *\n+\t\t * We could avoid a copy here if getComponents() could\n+\t\t * return sets_.data() but I wasn't able to work around the\n+\t\t * compiler refusing it.\n+\t\t */\n+\t\tlscData_ = lscData;\n+\n+\t\tsets_.setData(std::move(lscData));\n+\t\tstate.enabled = true;\n+\n+\t\treturn 0;\n+\t}\n \n \tconst lsc::Components interpolateComponents(unsigned int ct)\n \t{\n@@ -60,7 +123,7 @@ public:\n \t}\n \n private:\n-\tstd::unique_ptr<LscImplementation> impl_;\n+\tstd::unique_ptr<LscImplementation<U>> impl_;\n \tInterpolator<lsc::Components> sets_;\n \tlsc::ComponentsMap lscData_;\n \tbool polynomial_;\ndiff --git a/src/ipa/libipa/lsc_base.cpp b/src/ipa/libipa/lsc_base.cpp\nindex 7de6ee70e7a0..0eeb99f6881a 100644\n--- a/src/ipa/libipa/lsc_base.cpp\n+++ b/src/ipa/libipa/lsc_base.cpp\n@@ -86,6 +86,7 @@ void Interpolator<lsc::Components>::\n /**\n * \\class LscImplementation\n * \\brief Pure virtual base class for lsc algorithm implementations\n+ * \\tparam U The fixedpoint lsc engine register format\n *\n * Defines the interface for the lsc algorithm implementation.\n */\ndiff --git a/src/ipa/libipa/lsc_base.h b/src/ipa/libipa/lsc_base.h\nindex 48307455362a..721fd137ef21 100644\n--- a/src/ipa/libipa/lsc_base.h\n+++ b/src/ipa/libipa/lsc_base.h\n@@ -57,6 +57,7 @@ struct LscDescriptor {\n \tSize sensorSize;\n };\n \n+template<typename U>\n class LscImplementation\n {\n public:\ndiff --git a/src/ipa/libipa/lsc_polynomial.cpp b/src/ipa/libipa/lsc_polynomial.cpp\nindex 013a4469546e..2b743ed53be7 100644\n--- a/src/ipa/libipa/lsc_polynomial.cpp\n+++ b/src/ipa/libipa/lsc_polynomial.cpp\n@@ -113,14 +113,10 @@ void Polynomial::setReferenceImageSize(const Size &size)\n } /* namespace lsc */\n \n /**\n- * \\class LscPolynomial\n- * \\brief Radial Polynomial lsc algorithm implementation\n+ * \\class LscPolynomialBase\n+ * \\brief Base class for LscPolynomial\n *\n- * Polynomial-based lsc algorithm implementation. The LscPolynomial class\n- * implements lsc support using Polynomial to represent the shading artifacts\n- * map.\n- *\n- * \\sa LscImplementation\n+ * Base class for LscPolynomial for non-templated functions.\n */\n \n /**\n@@ -132,8 +128,8 @@ void Polynomial::setReferenceImageSize(const Size &size)\n *\n * \\return 0 on success or a negative error number otherwise\n */\n-int LscPolynomial::parseLscData(const ValueNode &sets,\n-\t\t\t\tconst LscDescriptor &descriptor)\n+int LscPolynomialBase::parseLscData(const ValueNode &sets,\n+\t\t\t\t const LscDescriptor &descriptor)\n {\n \tfor (const auto &set : sets.asList()) {\n \t\tuint32_t ct = set[\"ct\"].get<uint32_t>(0);\n@@ -174,6 +170,27 @@ int LscPolynomial::parseLscData(const ValueNode &sets,\n }\n \n /**\n+ * \\var LscPolynomialBase::lscData_\n+ * \\brief The polynomial lsc data\n+ *\n+ * Maps colour temperatures to per-colour radial polynomial definitions.\n+ */\n+\n+/**\n+ * \\class LscPolynomial\n+ * \\brief Radial Polynomial lsc algorithm implementation\n+ * \\tparam U The fixedpoint format used to convert gain values generated by\n+ * polynomial expansion to the register format\n+ *\n+ * Polynomial-based lsc algorithm implementation. The LscPolynomial class\n+ * implements lsc support using Polynomial to represent the shading artifacts\n+ * map.\n+ *\n+ * \\sa LscImplementation\n+ */\n+\n+/**\n+ * \\fn LscPolynomial::sampleForCrop()\n * \\brief Re-sample the lsc components for \\a cropRectangle\n * \\param[in] cropRectangle The sensor analogue crop rectangle\n * \\param[in] xPos List of horizontal positions of the lsc grid nodes\n@@ -195,62 +212,7 @@ 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-LscPolynomial::sampleForCrop(const Rectangle &cropRectangle,\n-\t\t\t std::vector<double> xPos, std::vector<double> yPos)\n-{\n-\n-\tlsc::ComponentsMap components;\n-\n-\tfor (const auto &[t, c] : lscData_) {\n-\t\tlsc::Components comp;\n-\n-\t\tfor (const auto &[k, p] : c) {\n-\t\t\tcomp.emplace(std::piecewise_construct,\n-\t\t\t\t std::forward_as_tuple(k),\n-\t\t\t\t std::forward_as_tuple(samplePolynomial(p, xPos, yPos,\n-\t\t\t\t\t\t\t\t\t cropRectangle)));\n-\t\t}\n \n-\t\tcomponents[t] = comp;\n-\t}\n-\n-\treturn components;\n-}\n-\n-std::vector<uint16_t>\n-LscPolynomial::samplePolynomial(const lsc::Polynomial &poly,\n-\t\t\t\tSpan<const double> xPositions,\n-\t\t\t\tSpan<const double> yPositions,\n-\t\t\t\tconst Rectangle &cropRectangle)\n-{\n-\tdouble m = poly.getM();\n-\tdouble x0 = cropRectangle.x / m;\n-\tdouble y0 = cropRectangle.y / m;\n-\tdouble w = cropRectangle.width / m;\n-\tdouble h = cropRectangle.height / m;\n-\tstd::vector<uint16_t> samples;\n-\n-\tsamples.reserve(xPositions.size() * yPositions.size());\n-\n-\tfor (double y : yPositions) {\n-\t\tfor (double x : xPositions) {\n-\t\t\tdouble xp = x0 + x * w;\n-\t\t\tdouble yp = y0 + y * h;\n-\t\t\t/*\n-\t\t\t * The hardware uses 2.10 fixed point format and limits\n-\t\t\t * the legal values to [1..3.999]. Scale and clamp the\n-\t\t\t * sampled value accordingly.\n-\t\t\t */\n-\t\t\tint v = static_cast<int>(\n-\t\t\t\tpoly.sampleAtNormalizedPixelPos(xp, yp) *\n-\t\t\t\t1024);\n-\t\t\tv = std::clamp(v, 1024, 4095);\n-\t\t\tsamples.push_back(v);\n-\t\t}\n-\t}\n-\treturn samples;\n-}\n \n } /* namespace ipa */\n \ndiff --git a/src/ipa/libipa/lsc_polynomial.h b/src/ipa/libipa/lsc_polynomial.h\nindex 6d19e46a50b7..04d7465b3af0 100644\n--- a/src/ipa/libipa/lsc_polynomial.h\n+++ b/src/ipa/libipa/lsc_polynomial.h\n@@ -8,6 +8,7 @@\n \n #include <array>\n #include <map>\n+#include <tuple>\n #include <vector>\n \n #include <libcamera/base/span.h>\n@@ -50,26 +51,80 @@ private:\n \n } /* namespace lsc */\n \n-class LscPolynomial : public LscImplementation\n+class LscPolynomialBase\n {\n private:\n \tusing PolynomialComponents = std::map<std::string, lsc::Polynomial>;\n \tusing PolynomialComponentsMap = std::map<unsigned int, PolynomialComponents>;\n \n+protected:\n+\tint parseLscData(const ValueNode &yamlSets,\n+\t\t\t const LscDescriptor &descriptor);\n+\n+\tPolynomialComponentsMap lscData_;\n+};\n+\n+template<typename U>\n+class LscPolynomial : public LscPolynomialBase, public LscImplementation<U>\n+{\n public:\n-\tint parseLscData(const ValueNode &sets,\n-\t\t\t const LscDescriptor &descriptor) override;\n+\tint parseLscData(const ValueNode &yamlSets,\n+\t\t\t const LscDescriptor &descriptor) override\n+\t{\n+\t\treturn LscPolynomialBase::parseLscData(yamlSets, descriptor);\n+\t}\n \n \tlsc::ComponentsMap\n \tsampleForCrop(const Rectangle &cropRectangle,\n-\t\t std::vector<double> xPos, std::vector<double> yPos) override;\n+\t\t std::vector<double> xPos, std::vector<double> yPos) override\n+\t{\n+\t\tlsc::ComponentsMap components;\n+\n+\t\tfor (const auto &[t, c] : lscData_) {\n+\t\t\tlsc::Components comp;\n+\n+\t\t\tfor (const auto &[k, p] : c) {\n+\t\t\t\tcomp.emplace(std::piecewise_construct,\n+\t\t\t\t\t std::forward_as_tuple(k),\n+\t\t\t\t\t std::forward_as_tuple(samplePolynomial(p, xPos, yPos,\n+\t\t\t\t\t\t\t\t\t\t cropRectangle)));\n+\t\t\t}\n+\n+\t\t\tcomponents[t] = comp;\n+\t\t}\n+\n+\t\treturn components;\n+\t}\n \n private:\n \tstd::vector<uint16_t> samplePolynomial(const lsc::Polynomial &poly,\n \t\t\t\t\t Span<const double> xPositions,\n \t\t\t\t\t Span<const double> yPositions,\n-\t\t\t\t\t const Rectangle &cropRectangle);\n-\tPolynomialComponentsMap lscData_;\n+\t\t\t\t\t const Rectangle &cropRectangle)\n+\t{\n+\t\tdouble m = poly.getM();\n+\t\tdouble x0 = cropRectangle.x / m;\n+\t\tdouble y0 = cropRectangle.y / m;\n+\t\tdouble w = cropRectangle.width / m;\n+\t\tdouble h = cropRectangle.height / m;\n+\t\tstd::vector<uint16_t> samples;\n+\n+\t\tsamples.reserve(xPositions.size() * yPositions.size());\n+\n+\t\tfor (double y : yPositions) {\n+\t\t\tfor (double x : xPositions) {\n+\t\t\t\tdouble xp = x0 + x * w;\n+\t\t\t\tdouble yp = y0 + y * h;\n+\n+\t\t\t\tfloat sample = static_cast<float>\n+\t\t\t\t\t\t(poly.sampleAtNormalizedPixelPos(xp, yp));\n+\n+\t\t\t\tsamples.push_back(U(sample).quantized());\n+\t\t\t}\n+\t\t}\n+\n+\t\treturn samples;\n+\t}\n };\n \n } /* namespace ipa */\ndiff --git a/src/ipa/libipa/lsc_table.cpp b/src/ipa/libipa/lsc_table.cpp\nindex 415c1cc0ccd4..bb3c76b12332 100644\n--- a/src/ipa/libipa/lsc_table.cpp\n+++ b/src/ipa/libipa/lsc_table.cpp\n@@ -14,13 +14,10 @@ LOG_DEFINE_CATEGORY(LscTable)\n namespace ipa {\n \n /**\n- * \\class LscTable\n- * \\brief Table based lsc algorithm implementation\n- *\n- * Table based lsc algorithm implementation. The LscTable class implements lsc\n- * support using tabular lsc data.\n+ * \\class LscTableBase\n+ * \\brief Base class for LscTable\n *\n- * \\sa LscImplementation\n+ * Base class for LscTable for non-templated functions.\n */\n \n /**\n@@ -32,8 +29,8 @@ namespace ipa {\n *\n * \\return 0 on success or a negative error number otherwise\n */\n-int LscTable::parseLscData(const ValueNode &sets,\n-\t\t\t const LscDescriptor &descriptor)\n+int LscTableBase::parseLscData(const ValueNode &sets,\n+\t\t\t const LscDescriptor &descriptor)\n {\n \tfor (const auto &set : sets.asList()) {\n \t\tuint32_t ct = set[\"ct\"].get<uint32_t>(0);\n@@ -51,8 +48,8 @@ int LscTable::parseLscData(const ValueNode &sets,\n \treturn 0;\n }\n \n-int LscTable::parseLscComponent(const ValueNode &yamlSet,\n-\t\t\t\tunsigned int ct, const LscDescriptor &descriptor)\n+int LscTableBase::parseLscComponent(const ValueNode &yamlSet,\n+\t\t\t\t unsigned int ct, const LscDescriptor &descriptor)\n {\n \tlsc::Components component;\n \tfor (auto &k : descriptor.keys) {\n@@ -82,10 +79,10 @@ int LscTable::parseLscComponent(const ValueNode &yamlSet,\n \treturn 0;\n }\n \n-std::vector<uint16_t> LscTable::parseTable(const ValueNode &tuningData,\n-\t\t\t\t\t const char *prop,\n-\t\t\t\t\t unsigned int numHCells,\n-\t\t\t\t\t unsigned int numVCells)\n+std::vector<uint16_t> LscTableBase::parseTable(const ValueNode &tuningData,\n+\t\t\t\t\t const char *prop,\n+\t\t\t\t\t unsigned int numHCells,\n+\t\t\t\t\t unsigned int numVCells)\n {\n \tunsigned int lscNumSamples = numHCells * numVCells;\n \n@@ -102,6 +99,30 @@ std::vector<uint16_t> LscTable::parseTable(const ValueNode &tuningData,\n \treturn table;\n }\n \n+/**\n+ * \\var LscTableBase::lscData_\n+ * \\brief The tabular lsc data\n+ *\n+ * Maps colour temperatures to per-colour channel vector of gains\n+ */\n+\n+/**\n+ * \\class LscTable\n+ * \\brief Table based lsc algorithm implementation\n+ * \\tparam U The fixedpoint lsc engine register format\n+ *\n+ * Table based lsc algorithm implementation. The LscTable class implements lsc\n+ * support using tabular lsc data.\n+ *\n+ * \\sa LscImplementation\n+ */\n+\n+/**\n+ * \\fn LscTable::parseLscData()\n+ *\n+ * \\copydoc LscTableBase::parseLscData()\n+ */\n+\n } /* namespace ipa */\n \n } /* namespace libcamera */\ndiff --git a/src/ipa/libipa/lsc_table.h b/src/ipa/libipa/lsc_table.h\nindex 3fe52afbad7b..65e04b09c3ff 100644\n--- a/src/ipa/libipa/lsc_table.h\n+++ b/src/ipa/libipa/lsc_table.h\n@@ -23,11 +23,32 @@ LOG_DECLARE_CATEGORY(LscTable)\n \n namespace ipa {\n \n-class LscTable : public LscImplementation\n+class LscTableBase\n+{\n+protected:\n+\tint parseLscData(const ValueNode &sets,\n+\t\t\t const LscDescriptor &descriptor);\n+\n+private:\n+\tint parseLscComponent(const ValueNode &yamlSet,\n+\t\t\t unsigned int ct, const LscDescriptor &descriptor);\n+\tstd::vector<uint16_t> parseTable(const ValueNode &tuningData,\n+\t\t\t\t\t const char *prop,\n+\t\t\t\t\t unsigned int numHCells,\n+\t\t\t\t\t unsigned int numVCells);\n+protected:\n+\tlsc::ComponentsMap lscData_;\n+};\n+\n+template<typename U>\n+class LscTable : public LscTableBase, public LscImplementation<U>\n {\n public:\n \tint parseLscData(const ValueNode &sets,\n-\t\t\t const LscDescriptor &descriptor) override;\n+\t\t\t const LscDescriptor &descriptor) override\n+\t{\n+\t\treturn LscTableBase::parseLscData(sets, descriptor);\n+\t}\n \n \tlsc::ComponentsMap\n \tsampleForCrop([[maybe_unused]] const Rectangle &cropRectangle,\n@@ -38,16 +59,6 @@ public:\n \t\t\t<< \"Tabular LSC data doesn't support resampling\";\n \t\treturn lscData_;\n \t}\n-\n-private:\n-\tint parseLscComponent(const ValueNode &yamlSet,\n-\t\t\t unsigned int ct, const LscDescriptor &descriptor);\n-\tstd::vector<uint16_t> parseTable(const ValueNode &tuningData,\n-\t\t\t\t\t const char *prop,\n-\t\t\t\t\t unsigned int numHCells,\n-\t\t\t\t\t unsigned int numVCells);\n-\n-\tlsc::ComponentsMap lscData_;\n };\n \n } /* namespace ipa */\ndiff --git a/src/ipa/rkisp1/algorithms/lsc.h b/src/ipa/rkisp1/algorithms/lsc.h\nindex e7d3091a36c5..31156f56cd21 100644\n--- a/src/ipa/rkisp1/algorithms/lsc.h\n+++ b/src/ipa/rkisp1/algorithms/lsc.h\n@@ -13,6 +13,7 @@\n \n #include \"libcamera/internal/value_node.h\"\n \n+#include \"libipa/fixedpoint.h\"\n #include \"libipa/lsc.h\"\n \n #include \"algorithm.h\"\n@@ -58,7 +59,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": [ "v3", "29/35" ] }