[{"id":39815,"web_url":"https://patchwork.libcamera.org/comment/39815/","msgid":"<3586a8ec-5a93-4b0d-a01f-a1b83ae4ba76@ideasonboard.com>","date":"2026-07-24T08:43:28","subject":"Re: [PATCH v6 27/31] ipa: libipa: lsc: Quantize lsc gains in IPA","submitter":{"id":216,"url":"https://patchwork.libcamera.org/api/people/216/","name":"Barnabás Pőcze","email":"barnabas.pocze@ideasonboard.com"},"content":"2026. 07. 20. 16:59 keltezéssel, Jacopo Mondi írta:\n> The libIPA Lsc algorithm currently assumes the register format of\n> the RkISP1 platform (16 bits Q2.10 format), as that's where the\n> implementation has been derived from.\n> \n> Move the register quantization to the plaltform IPA and use 'floats'\n> as the exchange format for the table-based and polynomial-based\n> LSC implementations.\n> \n> Signed-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(-)\n> \n> diff --git a/src/ipa/libipa/lsc_base.h b/src/ipa/libipa/lsc_base.h\n> index 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 */\n> diff --git a/src/ipa/libipa/lsc_polynomial.cpp b/src/ipa/libipa/lsc_polynomial.cpp\n> index 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;\n> diff --git a/src/ipa/libipa/lsc_polynomial.h b/src/ipa/libipa/lsc_polynomial.h\n> index 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>   \n> diff --git a/src/ipa/libipa/lsc_table.cpp b/src/ipa/libipa/lsc_table.cpp\n> index 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 \"\n> diff --git a/src/ipa/libipa/lsc_table.h b/src/ipa/libipa/lsc_table.h\n> index 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);\n> diff --git a/src/ipa/rkisp1/algorithms/lsc.cpp b/src/ipa/rkisp1/algorithms/lsc.cpp\n> index 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\nI think this can be done without `regs` at all, e.g.:\n\n   const auto quantizeSet = [&](std::string_view key, float *dst) {\n     const auto &s = set.at(key);\n     std::transform(s.begin(), s.end(), dst, [](float f) {\n       return std::clamp<uint16_t>(f * 1024, 1024, 4095);\n     });\n   };\n\n   quantizeSet(\"r\", &config.r_data_tbl[0][0]);\n   // etc.\n\nor maybe even `quantize(key, dst)` could be added as a member function to `lsc::Components`.\n\n\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>","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 D365BBDE17\n\tfor <parsemail@patchwork.libcamera.org>;\n\tFri, 24 Jul 2026 08:43:34 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 8668D67EDD;\n\tFri, 24 Jul 2026 10:43:34 +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 323F167E8A\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tFri, 24 Jul 2026 10:43:32 +0200 (CEST)","from [192.168.33.42] (185.182.215.156.nat.pool.zt.hu\n\t[185.182.215.156])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id 9C56A153F;\n\tFri, 24 Jul 2026 10:42:30 +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=\"urG5zU1E\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1784882550;\n\tbh=7+9K+lL7eMoqu6Vr+xLcgYatUJ+pLBk8Di/DqOgm4oU=;\n\th=Date:Subject:To:References:From:In-Reply-To:From;\n\tb=urG5zU1EfEYA1AcweXkUInnbI5ogXcPioXGXGWQjNB2iFChG9welrAITvZ1qfu2Xe\n\tV5penfzjGXLXXAcs/OXbybRf6wyDGMxAtiqBlVUvdjM762mZjcD9lf/5a6F2M5q+qk\n\tQywkUp7rigq+xqJ0tJIctPArVspsYSw3hqSg0m3A=","Message-ID":"<3586a8ec-5a93-4b0d-a01f-a1b83ae4ba76@ideasonboard.com>","Date":"Fri, 24 Jul 2026 10:43:28 +0200","MIME-Version":"1.0","User-Agent":"Mozilla Thunderbird","Subject":"Re: [PATCH v6 27/31] ipa: libipa: lsc: Quantize lsc gains in IPA","To":"Jacopo Mondi <jacopo.mondi@ideasonboard.com>,\n\tlibcamera-devel@lists.libcamera.org,\n\tStefan Klug <stefan.klug@ideasonboard.com>,\n\tMilan Zamazal <mzamazal@redhat.com>","References":"<20260720-libipa-algorithms-v6-0-ececb73f97cb@ideasonboard.com>\n\t<20260720-libipa-algorithms-v6-27-ececb73f97cb@ideasonboard.com>","From":"=?utf-8?q?Barnab=C3=A1s_P=C5=91cze?= <barnabas.pocze@ideasonboard.com>","Content-Language":"en-US, hu-HU","In-Reply-To":"<20260720-libipa-algorithms-v6-27-ececb73f97cb@ideasonboard.com>","Content-Type":"text/plain; charset=UTF-8; format=flowed","Content-Transfer-Encoding":"8bit","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>"}}]