{"id":27652,"url":"https://patchwork.libcamera.org/api/patches/27652/?format=json","web_url":"https://patchwork.libcamera.org/patch/27652/","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-27-7425b5b795d4@ideasonboard.com>","date":"2026-08-05T16:13:09","name":"[v7,27/32] ipa: libipa: lsc: Quantize gains in IPA","commit_ref":null,"pull_url":null,"state":"new","archived":false,"hash":"b6f0619ebad12505b4a694b80762e5b771223daa","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/27652/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/27652/comments/","check":"pending","checks":"https://patchwork.libcamera.org/api/patches/27652/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 87C5CC334D\n\tfor <parsemail@patchwork.libcamera.org>;\n\tWed,  5 Aug 2026 16:14:01 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 140706816D;\n\tWed,  5 Aug 2026 18:14:01 +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 4C61E68163\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 88C4B981;\n\tWed,  5 Aug 2026 18:12:31 +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=\"KYDKyGBA\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1785946351;\n\tbh=vDonS8TQg8+qGGtDqSm4kxg7JZ/OvVkZjmA0BUQhIOs=;\n\th=From:Date:Subject:References:In-Reply-To:To:Cc:From;\n\tb=KYDKyGBAl70pEvS31HZBJmCtNqL0sRubx3GR/VDK5p+zzvCvW5W2qnZzp0Wr1mWFJ\n\tXPgkCPZaDdD1MUKdg6qpwy/aQbwyIyVrXG7+KKmI3T5SDJbJ5jkm+LGKoxlqYJSUWZ\n\tErtR6nwbVIYQ0P9WBA0h3SlNQ6UeqalhGsBqcqws=","From":"Jacopo Mondi <jacopo.mondi@ideasonboard.com>","Date":"Wed, 05 Aug 2026 18:13:09 +0200","Subject":"[PATCH v7 27/32] ipa: libipa: lsc: Quantize gains in IPA","MIME-Version":"1.0","Content-Type":"text/plain; charset=\"utf-8\"","Content-Transfer-Encoding":"7bit","Message-Id":"<20260805-libipa-algorithms-v7-27-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=7176;\n\ti=jacopo.mondi@ideasonboard.com; h=from:subject:message-id;\n\tbh=vDonS8TQg8+qGGtDqSm4kxg7JZ/OvVkZjmA0BUQhIOs=;\n\tb=owEBbQKS/ZANAwAKAXI0Bo8WoVY8AcsmYgBqc2EiZ1l3BVAans4LNGssd6KgWb1lspup9Sv3j\n\tg2lhWWXZ0GJAjMEAAEKAB0WIQS1xD1IgJogio9YOMByNAaPFqFWPAUCanNhIgAKCRByNAaPFqFW\n\tPAKvD/9f+X0r1VKpnuXVzfWJY5Ubgr4G5Ez1rKKOalN8SJgxL0ZmCPwYHAKTKsoL4/yUQdWkmRE\n\tNx2TJMTCM/nHmK9GYpOowQMLt6dh8DiEMoDR3X8lsjlyrSui11J8dTuKSkZaZ8uumQ0mjXMMefJ\n\t8CTT5Tw4e6m4gHR+w4FpVX65VSpl3oIUUChZzPLbyqRwX7UsyFFEQIQXtBVo3WvNRGOL/fIbHUF\n\tAsO6ZCCqyPXm6j92cPEGvJbRXKoUbWj34YjjKq9902qmvg2GuiKXVcfoWZKmlPMf8f70r6ZzUR3\n\tCbztqLBnsm9vlCMDogSy1W/PwdzHUod5x11T+SVikcIH4YcpU0sX05WfGerdCpw1DEh1uU1PlGy\n\t7rIfG/a7CZg9XW1iTJjFl4ZccvcyNlsQzL/b1L7a+GOCd+iokB8unA6STv579K3vrWeZqPSxLsS\n\tm2O+41YbBo6jwsJ1+QRlz+Gdu/e+3rFRhDAH7FjahqthDw6vLBFLIMZs3Iz/u7w4NMoEkNtKrEB\n\tAAzqjgntsajGzOnZvFx63kLDbX750gyHAHZhWsjuagNmXTi16RZsg5j+LUo37PMoXUjWB3vRuFM\n\t/WcyQdrxtYE7lKw4pG7f92xZQFR8DqMnrGBZjiSNvIX3qMMTMOKDaVqjX7zw9gayf+ig4NMpqeg\n\tzRBDfamU+1fSisQ==","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 libIPA Lsc algorithm currently assumes the register format of\nthe RkISP1 platform (16 bits Q2.10 format), as that's where the\nimplementation has been derived from.\n\nMake lsc::Components use floats as the default exchange type between the\nLscAlgorithm and the IPA module and move the gains quantization to the\nplaltform IPA module.\n\nSigned-off-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>\n---\n src/ipa/libipa/lsc_base.h         |  2 +-\n src/ipa/libipa/lsc_polynomial.cpp | 17 +++++------------\n src/ipa/libipa/lsc_polynomial.h   |  8 ++++----\n src/ipa/libipa/lsc_table.cpp      | 22 ++++++++++++++++------\n src/ipa/libipa/lsc_table.h        |  2 +-\n src/ipa/rkisp1/algorithms/lsc.cpp | 23 +++++++++++++++--------\n 6 files changed, 42 insertions(+), 32 deletions(-)","diff":"diff --git a/src/ipa/libipa/lsc_base.h b/src/ipa/libipa/lsc_base.h\nindex 4ad754b3727a..2cff34b32a2b 100644\n--- a/src/ipa/libipa/lsc_base.h\n+++ b/src/ipa/libipa/lsc_base.h\n@@ -26,7 +26,7 @@ namespace ipa {\n \n namespace lsc {\n \n-using Components = std::map<std::string, std::vector<uint16_t>, std::less<>>;\n+using Components = std::map<std::string, std::vector<float>, std::less<>>;\n using ComponentsMap = std::map<unsigned int, Components>;\n \n } /* namespace lsc */\ndiff --git a/src/ipa/libipa/lsc_polynomial.cpp b/src/ipa/libipa/lsc_polynomial.cpp\nindex 0eb017de6b50..419de2c12cf0 100644\n--- a/src/ipa/libipa/lsc_polynomial.cpp\n+++ b/src/ipa/libipa/lsc_polynomial.cpp\n@@ -212,7 +212,7 @@ LscPolynomial::sampleForCrop(const Rectangle &cropRectangle,\n \treturn components;\n }\n \n-std::vector<uint16_t>\n+std::vector<float>\n LscPolynomial::samplePolynomial(const lsc::Polynomial &poly,\n \t\t\t\tSpan<const double> xPositions,\n \t\t\t\tSpan<const double> yPositions,\n@@ -223,7 +223,7 @@ LscPolynomial::samplePolynomial(const lsc::Polynomial &poly,\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+\tstd::vector<float> samples;\n \n \tsamples.reserve(xPositions.size() * yPositions.size());\n \n@@ -231,16 +231,9 @@ LscPolynomial::samplePolynomial(const lsc::Polynomial &poly,\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+\n+\t\t\tsamples.push_back(static_cast<float>\n+\t\t\t\t\t (poly.sampleAtNormalizedPixelPos(xp, yp)));\n \t\t}\n \t}\n \treturn samples;\ndiff --git a/src/ipa/libipa/lsc_polynomial.h b/src/ipa/libipa/lsc_polynomial.h\nindex 548ed94b2461..24b216a1aa25 100644\n--- a/src/ipa/libipa/lsc_polynomial.h\n+++ b/src/ipa/libipa/lsc_polynomial.h\n@@ -65,10 +65,10 @@ public:\n \t\t      std::vector<double> xPos, std::vector<double> yPos) override;\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+\tstd::vector<float> 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 };\n \ndiff --git a/src/ipa/libipa/lsc_table.cpp b/src/ipa/libipa/lsc_table.cpp\nindex 639f4f6aa8b0..3e8b1a060ce7 100644\n--- a/src/ipa/libipa/lsc_table.cpp\n+++ b/src/ipa/libipa/lsc_table.cpp\n@@ -30,6 +30,11 @@ namespace ipa {\n  *\n  * Parse the LSC data in tabular form from the \\a sets tuning data.\n  *\n+ * \\todo Currently the gain values parsed from tuning file are expressed in the\n+ * platform fixed-point register format. Use gains in floating point\n+ * representation for tabular LSC data in order to facilitate re-use of LSC\n+ * tuning data across different platforms.\n+ *\n  * \\return 0 on success or a negative error number otherwise\n  */\n int LscTable::parseLscData(const ValueNode &sets,\n@@ -82,15 +87,20 @@ 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 numHSamples,\n-\t\t\t\t\t   unsigned int numVSamples)\n+std::vector<float> LscTable::parseTable(const ValueNode &tuningData,\n+\t\t\t\t\tconst char *prop,\n+\t\t\t\t\tunsigned int numHSamples,\n+\t\t\t\t\tunsigned int numVSamples)\n {\n \tunsigned int lscNumSamples = numHSamples * numVSamples;\n \n-\tstd::vector<uint16_t> table =\n-\t\ttuningData[prop].get<std::vector<uint16_t>>().value_or(utils::defopt);\n+\t/*\n+\t * Cast to float even if gains are expressed as fixed-point\n+\t * representations. This prepares to express gains in floating point\n+\t * formats in tuning files.\n+\t */\n+\tstd::vector<float> table =\n+\t\ttuningData[prop].get<std::vector<float>>().value_or(utils::defopt);\n \tif (table.size() != lscNumSamples) {\n \t\tLOG(LscTable, Error)\n \t\t\t<< \"Invalid '\" << prop << \"' values: expected \"\ndiff --git a/src/ipa/libipa/lsc_table.h b/src/ipa/libipa/lsc_table.h\nindex a33208761c15..6d9b0c692b7f 100644\n--- a/src/ipa/libipa/lsc_table.h\n+++ b/src/ipa/libipa/lsc_table.h\n@@ -42,7 +42,7 @@ public:\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+\tstd::vector<float> parseTable(const ValueNode &tuningData,\n \t\t\t\t\t const char *prop,\n \t\t\t\t\t unsigned int numHSamples,\n \t\t\t\t\t unsigned int numVSamples);\ndiff --git a/src/ipa/rkisp1/algorithms/lsc.cpp b/src/ipa/rkisp1/algorithms/lsc.cpp\nindex 7a95bfcfb93a..911ac6895a14 100644\n--- a/src/ipa/rkisp1/algorithms/lsc.cpp\n+++ b/src/ipa/rkisp1/algorithms/lsc.cpp\n@@ -177,14 +177,21 @@ void LensShadingCorrection::setParameters(rkisp1_cif_isp_lsc_config &config)\n void LensShadingCorrection::copyTable(rkisp1_cif_isp_lsc_config &config,\n \t\t\t\t      const lsc::Components &set)\n {\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+\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 }\n \n /**\n","prefixes":["v7","27/32"]}