{"id":27209,"url":"https://patchwork.libcamera.org/api/1.1/patches/27209/?format=json","web_url":"https://patchwork.libcamera.org/patch/27209/","project":{"id":1,"url":"https://patchwork.libcamera.org/api/1.1/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-9-968757b038bb@ideasonboard.com>","date":"2026-07-06T08:01:29","name":"[v3,09/35] ipa: rkisp1: lsc: Re-architecture Lsc support","commit_ref":null,"pull_url":null,"state":"new","archived":false,"hash":"04a3f4f0f86d6bb981c72c6b06be15939bdf47d6","submitter":{"id":143,"url":"https://patchwork.libcamera.org/api/1.1/people/143/?format=json","name":"Jacopo Mondi","email":"jacopo.mondi@ideasonboard.com"},"delegate":null,"mbox":"https://patchwork.libcamera.org/patch/27209/mbox/","series":[{"id":6038,"url":"https://patchwork.libcamera.org/api/1.1/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/27209/comments/","check":"pending","checks":"https://patchwork.libcamera.org/api/patches/27209/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 26C17C3308\n\tfor <parsemail@patchwork.libcamera.org>;\n\tMon,  6 Jul 2026 08:02:13 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id A83C266035;\n\tMon,  6 Jul 2026 10:02:12 +0200 (CEST)","from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[213.167.242.64])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id AA58E65FF1\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tMon,  6 Jul 2026 10:02:03 +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 4F68A1D17;\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=\"ZWvb4mp3\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1783324875;\n\tbh=+zh1/KZ1jgRGtmTQ5vykJHqeNYyq/krkRSwJSU9o9yo=;\n\th=From:Date:Subject:References:In-Reply-To:To:Cc:From;\n\tb=ZWvb4mp3J8+RNckHT69WRODlW98QK4tAF8ORszFzQKeiyQyJ8z1MqSsfzHtysDFK3\n\tq50PqouIh3BF0vTU/MArQSgrlmj/kSvsu0QtSQAyYyVllaV23kaZpTTci4/EJWl8qB\n\tRxsjZ7yFNKDw/yEcEIfOkFMKd0ksdaRkzzMhNt/4=","From":"Jacopo Mondi <jacopo.mondi@ideasonboard.com>","Date":"Mon, 06 Jul 2026 10:01:29 +0200","Subject":"[PATCH v3 09/35] ipa: rkisp1: lsc: Re-architecture Lsc support","MIME-Version":"1.0","Content-Type":"text/plain; charset=\"utf-8\"","Content-Transfer-Encoding":"7bit","Message-Id":"<20260706-libipa-algorithms-v3-9-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=15591;\n\ti=jacopo.mondi@ideasonboard.com; h=from:subject:message-id;\n\tbh=+zh1/KZ1jgRGtmTQ5vykJHqeNYyq/krkRSwJSU9o9yo=;\n\tb=owEBbQKS/ZANAwAKAXI0Bo8WoVY8AcsmYgBqS2DwZaTAE3zju3/ivFXXa0VqqbkOH/tJwDnDB\n\tXIh71deS/OJAjMEAAEKAB0WIQS1xD1IgJogio9YOMByNAaPFqFWPAUCaktg8AAKCRByNAaPFqFW\n\tPOLMD/9YNhNPRsuVh4LE09XXCUeuiQTgeMGA2sn0WnYwqftnFv5qfAQuq5mI25h75h3idwsoX90\n\tutxJAyg66Wm5eWBLAEVqcG8NvmOzvj7gJDdeiPjgEm7iyzZpa051rcg/v2P0lRUzmAFrtm1O9t1\n\t8JbWp5EuScR4ZpvK0BC8eEM0cX2wH9wyNf9S1LB1r96iSAhcqZ9RoM6jPXf8gtj1CZXTgCgogWi\n\tZWFog+nfTcUtsNHzIYpUcQ2FMv0S1miuaDl+Oq1bVvOZXRPTMuhtQ8aLEBsUWcxsyQwG9zZE+K6\n\tF8SPu2QrJ/FjwXYk5bR5srCfc/J1yDga7ZNcTENzqWCvG4vhCngKfTDiewC0WVtdnGFquULSS3Y\n\tWqpBEGUmduptOTdyAZVfgsQ+KSLg9vN2/gPsyW6FYFGRFrlBNb43eJTDPMxLsjodsmTThGOqN4a\n\tO70Y4sYhTTNF8OsXBK98ctWrKY0H8sNty40x7MCdgT8P3Rl2Nbo2ppnmdZCCUafBBoyrSIxqqWq\n\tgkDXOHVQvqRKXZMyTm5paaH167IvOxAXX2YNkUEc10sSTwz0A/EXzJGWW8iaIu3qxWXhJ2d9s10\n\twBKjd5nr5DLnGjQPe3/HOfQUXXaQpba0dTNPmxg7aIWEPUUuzzcemWsv0e55k5cy8C0EMNBl4uJ\n\txqeMKUDNyru8R6w==","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 RkISP1 LSC implementation is realized by the following components:\n\n- 2 \"tuning file\" parser classes:\n\n  - LscPolynomialLoader\n  - LscTableLoader\n\n    which implement the \"parseLscData\" function that loads from tuning\n    file a polynomial representation of the lsc gains or a table of gains\n    respectively.\n\n- A hierachy of \"ShadingDescriptors\"\n  - LscPolynomialShadingDescriptor\n  - LscTableShadingDescriptor\n\n  that implement the \"sampleForCrop\" interface which expands a\n  polynomial to a list of gains for the LscPolynomialShadingDescriptor\n  and simply return the data parsed from tuning file for\n  LscTableShadingDescriptor.\n\nUnify the two functionalities in a single class hierarchy whose\ninterface is defined by the LscImplementation pure virtual class\nimplemented by LscPolynomialImpl and LscTableImpl.\n\nThe LensShadingCorrection algorithm class instantiates the correct\nsub-class and uses the file parsing and table expansion functionalities\nby implementing the composition pattern.\n\nSigned-off-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>\n---\n src/ipa/rkisp1/algorithms/lsc.cpp | 247 +++++++++++++++++---------------------\n src/ipa/rkisp1/algorithms/lsc.h   |  49 ++++----\n 2 files changed, 141 insertions(+), 155 deletions(-)","diff":"diff --git a/src/ipa/rkisp1/algorithms/lsc.cpp b/src/ipa/rkisp1/algorithms/lsc.cpp\nindex faf88effde01..92001319279e 100644\n--- a/src/ipa/rkisp1/algorithms/lsc.cpp\n+++ b/src/ipa/rkisp1/algorithms/lsc.cpp\n@@ -54,17 +54,6 @@ void Interpolator<rkisp1::algorithms::LensShadingCorrection::Components>::\n \n namespace ipa::rkisp1::algorithms {\n \n-/**\n- * \\class LensShadingCorrection\n- * \\brief RkISP1 Lens Shading Correction control\n- *\n- * Due to the optical characteristics of the lens, the light intensity received\n- * by the sensor is not uniform.\n- *\n- * The Lens Shading Correction algorithm applies multipliers to all pixels to\n- * compensate for the lens shading effect. The coefficients are specified in a\n- * downscaled table in the tuning data.\n- */\n \n LOG_DEFINE_CATEGORY(RkISP1Lsc)\n \n@@ -72,54 +61,110 @@ namespace {\n \n constexpr int kColourTemperatureQuantization = 10;\n \n-class LscPolynomialShadingDescriptor : public LensShadingCorrection::ShadingDescriptor\n+class LscPolynomialImpl : public LscImplementation\n {\n+private:\n+\tstruct PolynomialComponents {\n+\t\tLscPolynomial pr;\n+\t\tLscPolynomial pgr;\n+\t\tLscPolynomial pgb;\n+\t\tLscPolynomial pb;\n+\t};\n+\tusing PolynomialComponentsMap = std::map<unsigned int, PolynomialComponents>;\n+\n public:\n-\tLscPolynomialShadingDescriptor(const LscPolynomial &pr, const LscPolynomial &pgr,\n-\t\t\t\t       const LscPolynomial &pgb, const LscPolynomial &pb)\n-\t\t: pr_(pr), pgr_(pgr), pgb_(pgb), pb_(pb)\n+\tLscPolynomialImpl(const Size &sensorSize)\n+\t\t: sensorSize_(sensorSize)\n \t{\n \t}\n \n-\tLensShadingCorrection::Components sampleForCrop(const Rectangle &cropRectangle,\n-\t\t\t\t\t\t\tSpan<const double> xSizes,\n-\t\t\t\t\t\t\tSpan<const double> ySizes) override;\n+\tint parseLscData(const ValueNode &sets) override;\n+\n+\tLensShadingCorrection::ComponentsMap\n+\tsampleForCrop(const Rectangle &cropRectangle,\n+\t\t      Span<const double> xSizes,\n+\t\t      Span<const double> ySizes) override;\n \n private:\n+\tstd::vector<double> sizesListToPositions(Span<const double> sizes);\n \tstd::vector<uint16_t> samplePolynomial(const LscPolynomial &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+\tSize sensorSize_;\n+};\n \n-\tstd::vector<double> sizesListToPositions(Span<const double> sizes);\n+int LscPolynomialImpl::parseLscData(const ValueNode &sets)\n+{\n+\tfor (const auto &set : sets.asList()) {\n+\t\tstd::optional<LscPolynomial> pr, pgr, pgb, pb;\n+\t\tuint32_t ct = set[\"ct\"].get<uint32_t>(0);\n \n-\tLscPolynomial pr_;\n-\tLscPolynomial pgr_;\n-\tLscPolynomial pgb_;\n-\tLscPolynomial pb_;\n-};\n+\t\tif (lscData_.count(ct)) {\n+\t\t\tLOG(RkISP1Lsc, Error)\n+\t\t\t\t<< \"Multiple sets found for \"\n+\t\t\t\t<< \"color temperature \" << ct;\n+\t\t\treturn -EINVAL;\n+\t\t}\n+\n+\t\tpr = set[\"r\"].get<LscPolynomial>();\n+\t\tpgr = set[\"gr\"].get<LscPolynomial>();\n+\t\tpgb = set[\"gb\"].get<LscPolynomial>();\n+\t\tpb = set[\"b\"].get<LscPolynomial>();\n+\n+\t\tif (!(pr || pgr || pgb || pb)) {\n+\t\t\tLOG(RkISP1Lsc, Error)\n+\t\t\t\t<< \"Failed to parse polynomial for \"\n+\t\t\t\t<< \"colour temperature \" << ct;\n+\t\t\treturn -EINVAL;\n+\t\t}\n \n-LensShadingCorrection::Components\n-LscPolynomialShadingDescriptor::sampleForCrop(const Rectangle &cropRectangle,\n-\t\t\t\t\t      Span<const double> xSizes,\n-\t\t\t\t\t      Span<const double> ySizes)\n+\t\tpr->setReferenceImageSize(sensorSize_);\n+\t\tpgr->setReferenceImageSize(sensorSize_);\n+\t\tpgb->setReferenceImageSize(sensorSize_);\n+\t\tpb->setReferenceImageSize(sensorSize_);\n+\n+\t\tlscData_.emplace(std::piecewise_construct,\n+\t\t\t\t std::forward_as_tuple(ct),\n+\t\t\t\t std::forward_as_tuple(PolynomialComponents{ *pr, *pgr, *pgb, *pb }));\n+\t}\n+\n+\tif (lscData_.empty()) {\n+\t\tLOG(RkISP1Lsc, Error) << \"Failed to load any sets\";\n+\t\treturn -EINVAL;\n+\t}\n+\n+\treturn 0;\n+}\n+\n+LensShadingCorrection::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 {\n \tstd::vector<double> xPos = sizesListToPositions(xSizes);\n \tstd::vector<double> yPos = sizesListToPositions(ySizes);\n \n-\treturn {\n-\t\t.r = samplePolynomial(pr_, xPos, yPos, cropRectangle),\n-\t\t.gr = samplePolynomial(pgr_, xPos, yPos, cropRectangle),\n-\t\t.gb = samplePolynomial(pgb_, xPos, yPos, cropRectangle),\n-\t\t.b = samplePolynomial(pb_, xPos, yPos, cropRectangle)\n-\t};\n+\tLensShadingCorrection::ComponentsMap components;\n+\n+\tfor (const auto &[k, p] : lscData_) {\n+\t\tcomponents[k] = {\n+\t\t\tsamplePolynomial(p.pr, xPos, yPos, cropRectangle),\n+\t\t\tsamplePolynomial(p.pgr, xPos, yPos, cropRectangle),\n+\t\t\tsamplePolynomial(p.pgb, xPos, yPos, cropRectangle),\n+\t\t\tsamplePolynomial(p.pb, xPos, yPos, cropRectangle)\n+\t\t};\n+\t}\n+\n+\treturn components;\n }\n \n std::vector<uint16_t>\n-LscPolynomialShadingDescriptor::samplePolynomial(const LscPolynomial &poly,\n-\t\t\t\t\t\t Span<const double> xPositions,\n-\t\t\t\t\t\t Span<const double> yPositions,\n-\t\t\t\t\t\t const Rectangle &cropRectangle)\n+LscPolynomialImpl::samplePolynomial(const LscPolynomial &poly,\n+\t\t\t\t    Span<const double> xPositions,\n+\t\t\t\t    Span<const double> yPositions,\n+\t\t\t\t    const Rectangle &cropRectangle)\n {\n \tdouble m = poly.getM();\n \tdouble x0 = cropRectangle.x / m;\n@@ -160,7 +205,7 @@ LscPolynomialShadingDescriptor::samplePolynomial(const LscPolynomial &poly,\n  * output: 0.0   0.2   0.5   0.8   1.0\n  */\n std::vector<double>\n-LscPolynomialShadingDescriptor::sizesListToPositions(Span<const double> sizes)\n+LscPolynomialImpl::sizesListToPositions(Span<const double> sizes)\n {\n \tconst int half = sizes.size();\n \tstd::vector<double> positions(half * 2 + 1);\n@@ -176,74 +221,12 @@ LscPolynomialShadingDescriptor::sizesListToPositions(Span<const double> sizes)\n \treturn positions;\n }\n \n-class LscPolynomialLoader\n+class LscTableImpl : public LscImplementation\n {\n public:\n-\tLscPolynomialLoader(const Size &sensorSize)\n-\t\t: sensorSize_(sensorSize)\n-\t{\n-\t}\n-\n-\tint parseLscData(const ValueNode &sets,\n-\t\t\t LensShadingCorrection::ShadingDescriptorMap &lscData);\n+\tint parseLscData(const ValueNode &sets) override;\n \n-private:\n-\tSize sensorSize_;\n-};\n-\n-int LscPolynomialLoader::parseLscData(const ValueNode &sets,\n-\t\t\t\t      LensShadingCorrection::ShadingDescriptorMap &lscData)\n-{\n-\tfor (const auto &set : sets.asList()) {\n-\t\tstd::optional<LscPolynomial> pr, pgr, pgb, pb;\n-\t\tuint32_t ct = set[\"ct\"].get<uint32_t>(0);\n-\n-\t\tif (lscData.count(ct)) {\n-\t\t\tLOG(RkISP1Lsc, Error)\n-\t\t\t\t<< \"Multiple sets found for \"\n-\t\t\t\t<< \"color temperature \" << ct;\n-\t\t\treturn -EINVAL;\n-\t\t}\n-\n-\t\tpr = set[\"r\"].get<LscPolynomial>();\n-\t\tpgr = set[\"gr\"].get<LscPolynomial>();\n-\t\tpgb = set[\"gb\"].get<LscPolynomial>();\n-\t\tpb = set[\"b\"].get<LscPolynomial>();\n-\n-\t\tif (!(pr || pgr || pgb || pb)) {\n-\t\t\tLOG(RkISP1Lsc, Error)\n-\t\t\t\t<< \"Failed to parse polynomial for \"\n-\t\t\t\t<< \"colour temperature \" << ct;\n-\t\t\treturn -EINVAL;\n-\t\t}\n-\n-\t\tpr->setReferenceImageSize(sensorSize_);\n-\t\tpgr->setReferenceImageSize(sensorSize_);\n-\t\tpgb->setReferenceImageSize(sensorSize_);\n-\t\tpb->setReferenceImageSize(sensorSize_);\n-\n-\t\tlscData.emplace(\n-\t\t\tct, std::make_unique<LscPolynomialShadingDescriptor>(\n-\t\t\t\t    *pr, *pgr, *pgb, *pb));\n-\t}\n-\n-\tif (lscData.empty()) {\n-\t\tLOG(RkISP1Lsc, Error) << \"Failed to load any sets\";\n-\t\treturn -EINVAL;\n-\t}\n-\n-\treturn 0;\n-}\n-\n-class LscTableShadingDescriptor : public LensShadingCorrection::ShadingDescriptor\n-{\n-public:\n-\tLscTableShadingDescriptor(LensShadingCorrection::Components components)\n-\t\t: lscData_(std::move(components))\n-\t{\n-\t}\n-\n-\tLensShadingCorrection::Components\n+\tLensShadingCorrection::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@@ -253,28 +236,19 @@ public:\n \t\treturn lscData_;\n \t}\n \n-private:\n-\tLensShadingCorrection::Components lscData_;\n-};\n-\n-class LscTableLoader\n-{\n-public:\n-\tint parseLscData(const ValueNode &sets,\n-\t\t\t LensShadingCorrection::ShadingDescriptorMap &lscData);\n-\n 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 };\n \n-int LscTableLoader::parseLscData(const ValueNode &sets,\n-\t\t\t\t LensShadingCorrection::ShadingDescriptorMap &lscData)\n+int LscTableImpl::parseLscData(const ValueNode &sets)\n {\n \tfor (const auto &set : sets.asList()) {\n \t\tuint32_t ct = set[\"ct\"].get<uint32_t>(0);\n \n-\t\tif (lscData.count(ct)) {\n+\t\tif (lscData_.count(ct)) {\n \t\t\tLOG(RkISP1Lsc, Error)\n \t\t\t\t<< \"Multiple sets found for color temperature \"\n \t\t\t\t<< ct;\n@@ -295,11 +269,10 @@ int LscTableLoader::parseLscData(const ValueNode &sets,\n \t\t\treturn -EINVAL;\n \t\t}\n \n-\t\tlscData.emplace(\n-\t\t\tct, std::make_unique<LscTableShadingDescriptor>(std::move(components)));\n+\t\tlscData_.emplace(ct, std::move(components));\n \t}\n \n-\tif (lscData.empty()) {\n+\tif (lscData_.empty()) {\n \t\tLOG(RkISP1Lsc, Error) << \"Failed to load any sets\";\n \t\treturn -EINVAL;\n \t}\n@@ -307,7 +280,7 @@ int LscTableLoader::parseLscData(const ValueNode &sets,\n \treturn 0;\n }\n \n-std::vector<uint16_t> LscTableLoader::parseTable(const ValueNode &tuningData,\n+std::vector<uint16_t> LscTableImpl::parseTable(const ValueNode &tuningData,\n \t\t\t\t\t\t const char *prop)\n {\n \tstatic constexpr unsigned int kLscNumSamples =\n@@ -363,6 +336,18 @@ unsigned int quantize(unsigned int value, unsigned int step)\n \n } /* namespace */\n \n+/**\n+ * \\class LensShadingCorrection\n+ * \\brief RkISP1 Lens Shading Correction control\n+ *\n+ * Due to the optical characteristics of the lens, the light intensity received\n+ * by the sensor is not uniform.\n+ *\n+ * The Lens Shading Correction algorithm applies multipliers to all pixels to\n+ * compensate for the lens shading effect. The coefficients are specified in a\n+ * downscaled table in the tuning data.\n+ */\n+\n LensShadingCorrection::LensShadingCorrection()\n \t: lastAppliedCt_(0), lastAppliedQuantizedCt_(0)\n {\n@@ -388,22 +373,21 @@ int LensShadingCorrection::init([[maybe_unused]] IPAContext &context,\n \t\treturn -EINVAL;\n \t}\n \n-\tShadingDescriptorMap lscData;\n-\tint ret = 0;\n+\tint ret;\n \n \tstd::string type = tuningData[\"type\"].get<std::string>(\"table\");\n \tif (type == \"table\") {\n \t\tLOG(RkISP1Lsc, Debug) << \"Loading tabular LSC data.\";\n-\t\tauto loader = LscTableLoader();\n-\t\tret = loader.parseLscData(sets, lscData);\n+\t\talgo_ = std::make_unique<LscTableImpl>();\n+\t\tret = algo_->parseLscData(sets);\n \t} else if (type == \"polynomial\") {\n \t\tLOG(RkISP1Lsc, Debug) << \"Loading polynomial LSC data.\";\n \t\t/*\n \t\t * \\todo: Most likely the reference frame should be native_size.\n \t\t * Let's wait how the internal discussions progress.\n \t\t */\n-\t\tauto loader = LscPolynomialLoader(context.sensorInfo.activeAreaSize);\n-\t\tret = loader.parseLscData(sets, lscData);\n+\t\talgo_ = std::make_unique<LscPolynomialImpl>(context.sensorInfo.activeAreaSize);\n+\t\tret = algo_->parseLscData(sets);\n \t} else {\n \t\tLOG(RkISP1Lsc, Error) << \"Unsupported LSC data type '\"\n \t\t\t\t      << type << \"'\";\n@@ -416,8 +400,6 @@ int LensShadingCorrection::init([[maybe_unused]] IPAContext &context,\n \tcontext.ctrlMap[&controls::LensShadingCorrectionEnable] =\n \t\tControlInfo(false, true, true);\n \n-\tshadingDescriptors_ = std::move(lscData);\n-\n \treturn 0;\n }\n \n@@ -453,11 +435,8 @@ int LensShadingCorrection::configure(IPAContext &context,\n \t}\n \n \tLOG(RkISP1Lsc, Debug) << \"Sample LSC data for \" << configInfo.analogCrop;\n-\tstd::map<unsigned int, LensShadingCorrection::Components> shadingData;\n-\tfor (const auto &[t, descriptor] : shadingDescriptors_)\n-\t\tshadingData[t] = descriptor->sampleForCrop(configInfo.analogCrop,\n-\t\t\t\t\t\t\t   xSize_, ySize_);\n-\n+\tComponentsMap 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;\ndiff --git a/src/ipa/rkisp1/algorithms/lsc.h b/src/ipa/rkisp1/algorithms/lsc.h\nindex 0a256e225327..3463531f2ab2 100644\n--- a/src/ipa/rkisp1/algorithms/lsc.h\n+++ b/src/ipa/rkisp1/algorithms/lsc.h\n@@ -18,9 +18,20 @@ 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@@ -36,31 +47,10 @@ public:\n \t\t     IPAFrameContext &frameContext,\n \t\t     const rkisp1_stat_buffer *stats,\n \t\t     ControlList &metadata) override;\n-\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-\tclass ShadingDescriptor\n-\t{\n-\tpublic:\n-\t\tvirtual ~ShadingDescriptor() = default;\n-\t\tvirtual Components sampleForCrop(const Rectangle &cropRectangle,\n-\t\t\t\t\t\t Span<const double> xSizes,\n-\t\t\t\t\t\t Span<const double> ySizes) = 0;\n-\t};\n-\n-\tusing ShadingDescriptorMap = std::map<unsigned int, std::unique_ptr<ShadingDescriptor>>;\n-\n private:\n \tvoid setParameters(rkisp1_cif_isp_lsc_config &config);\n \tvoid copyTable(rkisp1_cif_isp_lsc_config &config, const Components &set0);\n \n-\tShadingDescriptorMap shadingDescriptors_;\n-\tipa::Interpolator<Components> sets_;\n \tstd::vector<double> xSize_;\n \tstd::vector<double> ySize_;\n \tuint16_t xGrad_[RKISP1_CIF_ISP_LSC_SECTORS_TBL_SIZE];\n@@ -70,6 +60,23 @@ private:\n \n \tunsigned int lastAppliedCt_;\n \tunsigned int lastAppliedQuantizedCt_;\n+\n+\tipa::Interpolator<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","prefixes":["v3","09/35"]}