{"id":27210,"url":"https://patchwork.libcamera.org/api/patches/27210/?format=json","web_url":"https://patchwork.libcamera.org/patch/27210/","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":"<20260706-libipa-algorithms-v3-10-968757b038bb@ideasonboard.com>","date":"2026-07-06T08:01:30","name":"[v3,10/35] ipa: libipa: Introduce lsc_base.h","commit_ref":null,"pull_url":null,"state":"new","archived":false,"hash":"5fddfce7dc1338a76c7f586dbb929694a2e75152","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/27210/mbox/","series":[{"id":6038,"url":"https://patchwork.libcamera.org/api/series/6038/?format=json","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/27210/comments/","check":"pending","checks":"https://patchwork.libcamera.org/api/patches/27210/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 955B8C330A\n\tfor <parsemail@patchwork.libcamera.org>;\n\tMon,  6 Jul 2026 08:02:14 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id F12E966030;\n\tMon,  6 Jul 2026 10:02:13 +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 2EDC366013\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tMon,  6 Jul 2026 10:02:04 +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 DABDF1E5A;\n\tMon,  6 Jul 2026 10:01:15 +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=\"qC44I5a7\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1783324876;\n\tbh=jrdxoAgoJgnLXUNO4CoaLct/yjp0+J0ih1FtMsDks9Q=;\n\th=From:Date:Subject:References:In-Reply-To:To:Cc:From;\n\tb=qC44I5a78VZ0XNVmdNI/v4v7CIPk2WTNsTDt+HhbnkLIX23sCf7YZvwUkJY5Z783R\n\tvSpALxRu80BVe4YAeJxB3waSqla1h+yWVy1MbFWMKBrvjCibomorcH5/LGdNHiGNG9\n\tcktnZ4VRclX5EGKdERJpNrjPp+qNge9CZovFLWS4=","From":"Jacopo Mondi <jacopo.mondi@ideasonboard.com>","Date":"Mon, 06 Jul 2026 10:01:30 +0200","Subject":"[PATCH v3 10/35] ipa: libipa: Introduce lsc_base.h","MIME-Version":"1.0","Content-Type":"text/plain; charset=\"utf-8\"","Content-Transfer-Encoding":"7bit","Message-Id":"<20260706-libipa-algorithms-v3-10-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=11124;\n\ti=jacopo.mondi@ideasonboard.com; h=from:subject:message-id;\n\tbh=jrdxoAgoJgnLXUNO4CoaLct/yjp0+J0ih1FtMsDks9Q=;\n\tb=owEBbQKS/ZANAwAKAXI0Bo8WoVY8AcsmYgBqS2DwCKezc03cDU4xhKEch0WOul5t5Jec9nH+c\n\tpkyC3hvUdqJAjMEAAEKAB0WIQS1xD1IgJogio9YOMByNAaPFqFWPAUCaktg8AAKCRByNAaPFqFW\n\tPF86D/9sL1Qgu4cbR8QHfo9adWlQU4pCxzjqxVkUj8u49QKfDLpGnDyUrHMZs+LP8nggEJiOnfa\n\tTsOzazjNNzO3pVhmOTEoEkRNbqUmYoGqQk6ebIcjw7CzXw3s80naqVU0joggi9krb2MrW7L2bUt\n\t4BSyvUSN34Qh5LoYDilEnfFkaJxRLDQiePg813Q82wM5wlBfgWD/+nytW1Yc4cB2vRBF2KZl+qb\n\tdZ3ylVu/L3OHjOL4U0H30X7CiQyKM+CSwEsyD5xsw/eo9kkZ41WNwmgJOKs21mtT1+D5dBWQH/X\n\tWuhONQgQeMXwquS2FFuqA8eq+KWPSa0jm6rgmGZSEHC6o6/OfsDulpFjjA6oIOh/u8MIQsozBJt\n\tYdPnR1nz7qKZMCmu95GXVDxnpZhvJH1/plL4Ek0GuFZFK54AdWOWnHmeRx/XqxgeM1OC2wHu3BS\n\tgvzt6vrDnKS8TqY+SdnqjopK5d+ynGlZnBqv4HN6Ki0EfM+9GvXHrTv8M3Hj422y+/+VrrmCtVp\n\t2gHOhdHAJaHug2cVV6Tmuj72TdMC9gXkThvFMiN4LTfF0Z9dV3m2aCMagMAXWYY6NwrSGmeg3zv\n\tngNZh5V6aaOu7xIXGE5hexqQocajZjq0atT1GHiESdcEWhRy5GT1I0wfc5RvfbTCmbCrawUZs4S\n\tBp4Z2VOTVhvb8+g==","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":"Group in an header file part of libipa the definition of the lsc\nComponents and the LscImplementation interface definition.\n\nSigned-off-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>\n---\n src/ipa/libipa/lsc_base.cpp       | 95 +++++++++++++++++++++++++++++++++++++++\n src/ipa/libipa/lsc_base.h         | 53 ++++++++++++++++++++++\n src/ipa/libipa/meson.build        |  2 +\n src/ipa/rkisp1/algorithms/lsc.cpp | 28 ++++++------\n src/ipa/rkisp1/algorithms/lsc.h   | 29 ++----------\n 5 files changed, 167 insertions(+), 40 deletions(-)","diff":"diff --git a/src/ipa/libipa/lsc_base.cpp b/src/ipa/libipa/lsc_base.cpp\nnew file mode 100644\nindex 000000000000..d545e9a5f7ed\n--- /dev/null\n+++ b/src/ipa/libipa/lsc_base.cpp\n@@ -0,0 +1,95 @@\n+/* SPDX-License-Identifier: LGPL-2.1-or-later */\n+/*\n+ * Copyright (C) 2026 Ideas on Board Oy\n+ *\n+ * Base classes and types for LSC algorithms implementations\n+ */\n+\n+#include \"lsc_base.h\"\n+\n+/**\n+ * \\file lsc_base.h\n+ * \\brief Base types and definitions for LscImplementation class hierarchy\n+ */\n+\n+namespace libcamera {\n+\n+namespace ipa {\n+\n+namespace lsc {\n+\n+/**\n+ * \\struct Components\n+ * \\brief Associate a 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+ * \\var Components::r\n+ * \\brief The list of gains for the Red colour component\n+ *\n+ * \\var Components::gr\n+ * \\brief The list of gains for the Green/Red colour component\n+ *\n+ * \\var Components::gb\n+ * \\brief The list of gains for the Green/Blue colour component\n+ *\n+ * \\var Components::b\n+ * \\brief The list of gains for the Blue colour component\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 light\n+ * temperature, and a tuning file usually contains lsc tables generated for\n+ * several different colour temperatures.\n+ */\n+\n+} /* namespace lsc */\n+\n+/**\n+ * \\class LscImplementation\n+ * \\brief Pure virtual base class for lsc algorithm implementations\n+ *\n+ * Defines the interface for the lsc algorithm implementation.\n+ */\n+\n+/**\n+ * \\fn LscImplementation::~LscImplementation\n+ * \\brief Virtual class destructor\n+ */\n+\n+/**\n+ * \\fn LscImplementation::parseLscData\n+ * \\brief Parse \\a tuningData\n+ * \\param[in] tuningData The tuning data\n+ *\n+ * \\return 0 on success, a negative error number otherwise\n+ */\n+\n+/**\n+ * \\fn LscImplementation::sampleForCrop\n+ * \\brief Re-sample the lsc components for \\a cropRectangle\n+ * \\param[in] cropRectangle The sensor analogue crop rectangle\n+ * \\param[in] xSizes List of horizontal positions of the lsc grid nodes\n+ * \\param[in] ySizes List of vertical positions of the lsc grid nodes\n+ *\n+ * Lsc tables have to be re-sampled every time a new sensor configuration is\n+ * used, as each streaming session might use a different sensor crop rectangle\n+ * \\a cropRectangle.\n+ *\n+ * \\a cropRectangle represents the size of the frame on which the Lsc tables\n+ * have to be re-sampled on.\n+ *\n+ * \\a xSizes and \\a ySizes represent the position of the grid nodes vertexes in\n+ * the [0, 1] interval. In example an equally spaced grid of 16 nodes will have\n+ * each segment of size 0.0625 and the list of nodes position will be\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+\n+} /* namespace ipa */\n+\n+} /* namespace libcamera */\ndiff --git a/src/ipa/libipa/lsc_base.h b/src/ipa/libipa/lsc_base.h\nnew file mode 100644\nindex 000000000000..93f819d3c2af\n--- /dev/null\n+++ b/src/ipa/libipa/lsc_base.h\n@@ -0,0 +1,53 @@\n+/* SPDX-License-Identifier: LGPL-2.1-or-later */\n+/*\n+ * Copyright (C) 2026 Ideas on Board Oy\n+ *\n+ * Base classes and types for LSC algorithms implementations\n+ */\n+\n+#pragma once\n+\n+#include <map>\n+#include <stdint.h>\n+#include <string>\n+#include <vector>\n+\n+#include <libcamera/base/span.h>\n+\n+#include <libcamera/geometry.h>\n+\n+#include \"libcamera/internal/value_node.h\"\n+\n+namespace libcamera {\n+\n+namespace ipa {\n+\n+namespace lsc {\n+\n+struct Components {\n+\tstd::vector<uint16_t> r;\n+\tstd::vector<uint16_t> gr;\n+\tstd::vector<uint16_t> gb;\n+\tstd::vector<uint16_t> b;\n+};\n+\n+using ComponentsMap = std::map<unsigned int, Components>;\n+\n+} /* namespace lsc */\n+\n+class LscImplementation\n+{\n+public:\n+\tvirtual ~LscImplementation() {}\n+\n+\tvirtual int parseLscData(const ValueNode &tuningData) = 0;\n+\n+\tvirtual lsc::ComponentsMap\n+\tsampleForCrop(const Rectangle &cropRectangle,\n+\t\t      Span<const double> xSizes,\n+\t\t      Span<const double> ySizes) = 0;\n+};\n+\n+} /* namespace ipa */\n+\n+} /* namespace libcamera */\ndiff --git a/src/ipa/libipa/meson.build b/src/ipa/libipa/meson.build\nindex edf8eabd8b78..96611e90ee1e 100644\n--- a/src/ipa/libipa/meson.build\n+++ b/src/ipa/libipa/meson.build\n@@ -14,6 +14,7 @@ libipa_headers = files([\n     'fixedpoint.h',\n     'histogram.h',\n     'interpolator.h',\n+    'lsc_base.h',\n     'lsc_polynomial.h',\n     'lux.h',\n     'module.h',\n@@ -36,6 +37,7 @@ libipa_sources = files([\n     'fixedpoint.cpp',\n     'histogram.cpp',\n     'interpolator.cpp',\n+    'lsc_base.cpp',\n     'lsc_polynomial.cpp',\n     'lux.cpp',\n     'module.cpp',\ndiff --git a/src/ipa/rkisp1/algorithms/lsc.cpp b/src/ipa/rkisp1/algorithms/lsc.cpp\nindex 92001319279e..e4d9f97ad009 100644\n--- a/src/ipa/rkisp1/algorithms/lsc.cpp\n+++ b/src/ipa/rkisp1/algorithms/lsc.cpp\n@@ -38,10 +38,10 @@ void interpolateVector(const std::vector<T> &a, const std::vector<T> &b,\n }\n \n template<>\n-void Interpolator<rkisp1::algorithms::LensShadingCorrection::Components>::\n-\tinterpolate(const rkisp1::algorithms::LensShadingCorrection::Components &a,\n-\t\t    const rkisp1::algorithms::LensShadingCorrection::Components &b,\n-\t\t    rkisp1::algorithms::LensShadingCorrection::Components &dest,\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 \tinterpolateVector(a.r, b.r, dest.r, lambda);\n@@ -80,7 +80,7 @@ public:\n \n \tint parseLscData(const ValueNode &sets) override;\n \n-\tLensShadingCorrection::ComponentsMap\n+\tlsc::ComponentsMap\n \tsampleForCrop(const Rectangle &cropRectangle,\n \t\t      Span<const double> xSizes,\n \t\t      Span<const double> ySizes) override;\n@@ -138,7 +138,7 @@ int LscPolynomialImpl::parseLscData(const ValueNode &sets)\n \treturn 0;\n }\n \n-LensShadingCorrection::ComponentsMap\n+lsc::ComponentsMap\n LscPolynomialImpl::sampleForCrop(const Rectangle &cropRectangle,\n \t\t\t\t Span<const double> xSizes,\n \t\t\t\t Span<const double> ySizes)\n@@ -146,7 +146,7 @@ LscPolynomialImpl::sampleForCrop(const Rectangle &cropRectangle,\n \tstd::vector<double> xPos = sizesListToPositions(xSizes);\n \tstd::vector<double> yPos = sizesListToPositions(ySizes);\n \n-\tLensShadingCorrection::ComponentsMap components;\n+\tlsc::ComponentsMap components;\n \n \tfor (const auto &[k, p] : lscData_) {\n \t\tcomponents[k] = {\n@@ -226,7 +226,7 @@ class LscTableImpl : public LscImplementation\n public:\n \tint parseLscData(const ValueNode &sets) override;\n \n-\tLensShadingCorrection::ComponentsMap\n+\tlsc::ComponentsMap\n \tsampleForCrop([[maybe_unused]] const Rectangle &cropRectangle,\n \t\t      [[maybe_unused]] Span<const double> xSizes,\n \t\t      [[maybe_unused]] Span<const double> ySizes) override\n@@ -240,7 +240,7 @@ private:\n \tstd::vector<uint16_t> parseTable(const ValueNode &tuningData,\n \t\t\t\t\t const char *prop);\n \n-\tLensShadingCorrection::ComponentsMap lscData_;\n+\tlsc::ComponentsMap lscData_;\n };\n \n int LscTableImpl::parseLscData(const ValueNode &sets)\n@@ -255,7 +255,7 @@ int LscTableImpl::parseLscData(const ValueNode &sets)\n \t\t\treturn -EINVAL;\n \t\t}\n \n-\t\tLensShadingCorrection::Components components;\n+\t\tlsc::Components components;\n \t\tcomponents.r = parseTable(set, \"r\");\n \t\tcomponents.gr = parseTable(set, \"gr\");\n \t\tcomponents.gb = parseTable(set, \"gb\");\n@@ -435,8 +435,8 @@ int LensShadingCorrection::configure(IPAContext &context,\n \t}\n \n \tLOG(RkISP1Lsc, Debug) << \"Sample LSC data for \" << configInfo.analogCrop;\n-\tComponentsMap shadingData = algo_->sampleForCrop(configInfo.analogCrop,\n-\t\t\t\t\t\t\t xSize_, ySize_);\n+\tlsc::ComponentsMap shadingData = algo_->sampleForCrop(configInfo.analogCrop,\n+\t\t\t\t\t\t\t      xSize_, ySize_);\n \tsets_.setData(std::move(shadingData));\n \n \tcontext.activeState.lsc.enabled = true;\n@@ -452,7 +452,7 @@ 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 Components &set)\n+\t\t\t\t      const lsc::Components &set)\n {\n \tstd::copy(set.r.begin(), set.r.end(), &config.r_data_tbl[0][0]);\n \tstd::copy(set.gr.begin(), set.gr.end(), &config.gr_data_tbl[0][0]);\n@@ -518,7 +518,7 @@ void LensShadingCorrection::prepare([[maybe_unused]] IPAContext &context,\n \n \tsetParameters(*config);\n \n-\tconst Components &set = sets_.getInterpolated(quantizedCt);\n+\tconst lsc::Components &set = sets_.getInterpolated(quantizedCt);\n \tcopyTable(*config, set);\n \n \tlastAppliedCt_ = ct;\ndiff --git a/src/ipa/rkisp1/algorithms/lsc.h b/src/ipa/rkisp1/algorithms/lsc.h\nindex 3463531f2ab2..b21c91c0d81f 100644\n--- a/src/ipa/rkisp1/algorithms/lsc.h\n+++ b/src/ipa/rkisp1/algorithms/lsc.h\n@@ -11,6 +11,7 @@\n #include <memory>\n \n #include \"libipa/interpolator.h\"\n+#include \"libipa/lsc_base.h\"\n \n #include \"algorithm.h\"\n \n@@ -18,20 +19,9 @@ namespace libcamera {\n \n namespace ipa::rkisp1::algorithms {\n \n-class LscImplementation;\n-\n class LensShadingCorrection : public Algorithm\n {\n public:\n-\tstruct Components {\n-\t\tstd::vector<uint16_t> r;\n-\t\tstd::vector<uint16_t> gr;\n-\t\tstd::vector<uint16_t> gb;\n-\t\tstd::vector<uint16_t> b;\n-\t};\n-\n-\tusing ComponentsMap = std::map<unsigned int, Components>;\n-\n \tLensShadingCorrection();\n \t~LensShadingCorrection() = default;\n \n@@ -49,7 +39,7 @@ 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 Components &set0);\n+\tvoid copyTable(rkisp1_cif_isp_lsc_config &config, const lsc::Components &set0);\n \n \tstd::vector<double> xSize_;\n \tstd::vector<double> ySize_;\n@@ -61,23 +51,10 @@ private:\n \tunsigned int lastAppliedCt_;\n \tunsigned int lastAppliedQuantizedCt_;\n \n-\tipa::Interpolator<Components> sets_;\n+\tipa::Interpolator<lsc::Components> sets_;\n \n \tstd::unique_ptr<LscImplementation> algo_;\n };\n \n-class LscImplementation\n-{\n-public:\n-\tvirtual ~LscImplementation() {}\n-\n-\tvirtual int parseLscData(const ValueNode &sets) = 0;\n-\n-\tvirtual LensShadingCorrection::ComponentsMap\n-\tsampleForCrop(const Rectangle &cropRectangle,\n-\t\t      Span<const double> xSizes,\n-\t\t      Span<const double> ySizes) = 0;\n-};\n-\n } /* namespace ipa::rkisp1::algorithms */\n } /* namespace libcamera */\n","prefixes":["v3","10/35"]}