{"id":27419,"url":"https://patchwork.libcamera.org/api/patches/27419/?format=json","web_url":"https://patchwork.libcamera.org/patch/27419/","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":"<20260720-libipa-algorithms-v6-27-ececb73f97cb@ideasonboard.com>","date":"2026-07-20T14:59:45","name":"[v6,27/31] ipa: libipa: lsc: Quantize lsc gains in IPA","commit_ref":null,"pull_url":null,"state":"new","archived":false,"hash":"04e1118bc0a496200676b9c4c8fbfb19ddcd05ce","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/27419/mbox/","series":[{"id":6067,"url":"https://patchwork.libcamera.org/api/series/6067/?format=json","web_url":"https://patchwork.libcamera.org/project/libcamera/list/?series=6067","date":"2026-07-20T14:59:18","name":"ipa: libipa: Introduce libipa algorithms","version":6,"mbox":"https://patchwork.libcamera.org/series/6067/mbox/"}],"comments":"https://patchwork.libcamera.org/api/patches/27419/comments/","check":"pending","checks":"https://patchwork.libcamera.org/api/patches/27419/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 E4C42C333D\n\tfor <parsemail@patchwork.libcamera.org>;\n\tMon, 20 Jul 2026 15:00:20 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 6542267EA4;\n\tMon, 20 Jul 2026 17:00:20 +0200 (CEST)","from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[213.167.242.64])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 5033D67E83\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tMon, 20 Jul 2026 16:59:56 +0200 (CEST)","from [192.168.1.106] (mob-5-90-63-152.net.vodafone.it\n\t[5.90.63.152])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id 671811CE9;\n\tMon, 20 Jul 2026 16:58:57 +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=\"rLN1wwr9\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1784559537;\n\tbh=dlfNgaxvpp06Pxl62gjETV+oyFIXVXC6rt1lzcZEpYo=;\n\th=From:Date:Subject:References:In-Reply-To:To:Cc:From;\n\tb=rLN1wwr9zqi/qgIR9hvlKTL6j2N52d1il4reeM25lYqKzdLQV2VrNtfy3GjuXWe2C\n\tFY3Mt7Z0qbvNhFEWXevh4MyrfnHMD1PiurGtUgN54pIrhD4cKHSzD3p//MSFdwNA9W\n\tWJqUjgl2kSGIymB56WmoKLL2mxsuad6LCNNOMrro=","From":"Jacopo Mondi <jacopo.mondi@ideasonboard.com>","Date":"Mon, 20 Jul 2026 16:59:45 +0200","Subject":"[PATCH v6 27/31] ipa: libipa: lsc: Quantize lsc gains in IPA","MIME-Version":"1.0","Content-Type":"text/plain; charset=\"utf-8\"","Content-Transfer-Encoding":"7bit","Message-Id":"<20260720-libipa-algorithms-v6-27-ececb73f97cb@ideasonboard.com>","References":"<20260720-libipa-algorithms-v6-0-ececb73f97cb@ideasonboard.com>","In-Reply-To":"<20260720-libipa-algorithms-v6-0-ececb73f97cb@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.14.3","X-Developer-Signature":"v=1; a=openpgp-sha256; l=6809;\n\ti=jacopo.mondi@ideasonboard.com; h=from:subject:message-id;\n\tbh=dlfNgaxvpp06Pxl62gjETV+oyFIXVXC6rt1lzcZEpYo=;\n\tb=owEBbQKS/ZANAwAKAXI0Bo8WoVY8AcsmYgBqXjfap9BmU1yuAc8zHJbfjCem5Bbqplt9GgcTC\n\tp3rSAbANH2JAjMEAAEKAB0WIQS1xD1IgJogio9YOMByNAaPFqFWPAUCal432gAKCRByNAaPFqFW\n\tPHW5D/92cTnLNYkHcsM8jwT3I881kfdRWTW1qJ4EBqduADra+BhV+lhYMZy9oyZF84IqLSxiZCB\n\tAUZCXDThq46QuBRM7r05g8Y/PN71TwJdlu4pkW5dfKoH5CX4Cg9tPi7phAAn4iviowGNIBSGoHO\n\tX48CKdtorsQgJlIu76Ty1wyGRgT0ArEtd3DQTwVlE7cDT52z3rWIHaPZhrkN0btQV+Mcvq+yvR1\n\tEY9Ef9Rzi8hs7lMcNQPHIL0UzToF9l+NU5cEePw+P6kZbKm88JvhHRZMhWrhw/6UUADbjydt+XG\n\tjNe0b0jY1N4vcVgn1L/hLwWWLGobfDZzssk0nqzYvCTBoUedcbUf49ahWhcu8ikD6ianWnD+t6A\n\tjpZV8xisjVcN5GXGZMlMRMM4D6C0GRuszFo8XN0lDNFtYTEt7enSfM0aBcCNPFaqoNXUf/2VN3Z\n\tVI3eFFlvMG3XCOAWEag8w8p5hL+QLfQi6xRa/QPKu/LuM6wvIR4L6fBM4e6gd6oLPE5dqQTveOZ\n\tg8dY8c5UqxHrsku5oyHnNDr0Ua5ejXQBzX+MIQdRbi8Iqw83bGlyU4LNv4e2W6fsalxZw3PmKwa\n\t0PKbypLNhQ6jVxRy7kuNwybPmHQ0L5oW9vt8CEu7wj8tFqatnQ32FBDd3YW+5fJYDykZ2bDUOBd\n\tR3HXq53IaMtYadQ==","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\nMove the register quantization to the plaltform IPA and use 'floats'\nas the exchange format for the table-based and polynomial-based\nLSC implementations.\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      |  6 +++---\n src/ipa/libipa/lsc_table.h        |  2 +-\n src/ipa/rkisp1/algorithms/lsc.cpp | 34 ++++++++++++++++++++++++++--------\n 6 files changed, 40 insertions(+), 29 deletions(-)","diff":"diff --git a/src/ipa/libipa/lsc_base.h b/src/ipa/libipa/lsc_base.h\nindex b0b9b3b0712d..386cac55be34 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>>;\n+using Components = std::map<std::string, std::vector<float>>;\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 c61c85fbf2b2..e31a63e06cc8 100644\n--- a/src/ipa/libipa/lsc_polynomial.cpp\n+++ b/src/ipa/libipa/lsc_polynomial.cpp\n@@ -218,7 +218,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@@ -229,7 +229,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@@ -237,16 +237,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 6d19e46a50b7..8d526531075b 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 92e24bc5160a..33f6182de1bd 100644\n--- a/src/ipa/libipa/lsc_table.cpp\n+++ b/src/ipa/libipa/lsc_table.cpp\n@@ -82,15 +82,15 @@ int LscTable::parseLscComponent(const ValueNode &yamlSet,\n \treturn 0;\n }\n \n-std::vector<uint16_t> LscTable::parseTable(const ValueNode &tuningData,\n+std::vector<float> 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 {\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+\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..dba1a01118ee 100644\n--- a/src/ipa/rkisp1/algorithms/lsc.cpp\n+++ b/src/ipa/rkisp1/algorithms/lsc.cpp\n@@ -177,14 +177,32 @@ 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+\tstd::vector<uint16_t> regs;\n+\tregs.reserve(RKISP1_CIF_ISP_LSC_SAMPLES_MAX *\n+\t\t     RKISP1_CIF_ISP_LSC_SAMPLES_MAX);\n+\n+\tfor (const float &f : set.at(\"r\"))\n+\t\tregs.emplace_back(std::clamp(static_cast<int>(f * 1024), 1024, 4095));\n+\tstd::copy(regs.begin(), regs.end(), &config.r_data_tbl[0][0]);\n+\n+\tregs = {};\n+\tfor (const float &f : set.at(\"gr\"))\n+\t\tregs.emplace_back(std::clamp(static_cast<int>(f * 1024), 1024, 4095));\n+\tstd::copy(regs.begin(), regs.end(), &config.gr_data_tbl[0][0]);\n+\n+\tregs = {};\n+\tfor (const float &f : set.at(\"gb\"))\n+\t\tregs.emplace_back(std::clamp(static_cast<int>(f * 1024), 1024, 4095));\n+\tstd::copy(regs.begin(), regs.end(), &config.gb_data_tbl[0][0]);\n+\n+\tregs = {};\n+\tfor (const float &f : set.at(\"b\"))\n+\t\tregs.emplace_back(std::clamp(static_cast<int>(f * 1024), 1024, 4095));\n+\tstd::copy(regs.begin(), regs.end(), &config.b_data_tbl[0][0]);\n }\n \n /**\n","prefixes":["v6","27/31"]}