From patchwork Wed Aug 5 16:13:09 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jacopo Mondi X-Patchwork-Id: 27652 Return-Path: X-Original-To: parsemail@patchwork.libcamera.org Delivered-To: parsemail@patchwork.libcamera.org Received: from lancelot.ideasonboard.com (lancelot.ideasonboard.com [92.243.16.209]) by patchwork.libcamera.org (Postfix) with ESMTPS id 87C5CC334D for ; Wed, 5 Aug 2026 16:14:01 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 140706816D; Wed, 5 Aug 2026 18:14:01 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="KYDKyGBA"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [IPv6:2001:4b98:dc2:55:216:3eff:fef7:d647]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 4C61E68163 for ; Wed, 5 Aug 2026 18:13:42 +0200 (CEST) Received: from [192.168.125.177] (mob-109-113-25-35.net.vodafone.it [109.113.25.35]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 88C4B981; Wed, 5 Aug 2026 18:12:31 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1785946351; bh=vDonS8TQg8+qGGtDqSm4kxg7JZ/OvVkZjmA0BUQhIOs=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=KYDKyGBAl70pEvS31HZBJmCtNqL0sRubx3GR/VDK5p+zzvCvW5W2qnZzp0Wr1mWFJ XPgkCPZaDdD1MUKdg6qpwy/aQbwyIyVrXG7+KKmI3T5SDJbJ5jkm+LGKoxlqYJSUWZ ErtR6nwbVIYQ0P9WBA0h3SlNQ6UeqalhGsBqcqws= From: Jacopo Mondi 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 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, Stefan Klug , Milan Zamazal Cc: Jacopo Mondi X-Mailer: b4 0.16-dev X-Developer-Signature: v=1; a=openpgp-sha256; l=7176; i=jacopo.mondi@ideasonboard.com; h=from:subject:message-id; bh=vDonS8TQg8+qGGtDqSm4kxg7JZ/OvVkZjmA0BUQhIOs=; b=owEBbQKS/ZANAwAKAXI0Bo8WoVY8AcsmYgBqc2EiZ1l3BVAans4LNGssd6KgWb1lspup9Sv3j g2lhWWXZ0GJAjMEAAEKAB0WIQS1xD1IgJogio9YOMByNAaPFqFWPAUCanNhIgAKCRByNAaPFqFW PAKvD/9f+X0r1VKpnuXVzfWJY5Ubgr4G5Ez1rKKOalN8SJgxL0ZmCPwYHAKTKsoL4/yUQdWkmRE Nx2TJMTCM/nHmK9GYpOowQMLt6dh8DiEMoDR3X8lsjlyrSui11J8dTuKSkZaZ8uumQ0mjXMMefJ 8CTT5Tw4e6m4gHR+w4FpVX65VSpl3oIUUChZzPLbyqRwX7UsyFFEQIQXtBVo3WvNRGOL/fIbHUF AsO6ZCCqyPXm6j92cPEGvJbRXKoUbWj34YjjKq9902qmvg2GuiKXVcfoWZKmlPMf8f70r6ZzUR3 CbztqLBnsm9vlCMDogSy1W/PwdzHUod5x11T+SVikcIH4YcpU0sX05WfGerdCpw1DEh1uU1PlGy 7rIfG/a7CZg9XW1iTJjFl4ZccvcyNlsQzL/b1L7a+GOCd+iokB8unA6STv579K3vrWeZqPSxLsS m2O+41YbBo6jwsJ1+QRlz+Gdu/e+3rFRhDAH7FjahqthDw6vLBFLIMZs3Iz/u7w4NMoEkNtKrEB AAzqjgntsajGzOnZvFx63kLDbX750gyHAHZhWsjuagNmXTi16RZsg5j+LUo37PMoXUjWB3vRuFM /WcyQdrxtYE7lKw4pG7f92xZQFR8DqMnrGBZjiSNvIX3qMMTMOKDaVqjX7zw9gayf+ig4NMpqeg zRBDfamU+1fSisQ== X-Developer-Key: i=jacopo.mondi@ideasonboard.com; a=openpgp; fpr=72392EDC88144A65C701EA9BA5826A2587AD026B X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" The libIPA Lsc algorithm currently assumes the register format of the RkISP1 platform (16 bits Q2.10 format), as that's where the implementation has been derived from. Make lsc::Components use floats as the default exchange type between the LscAlgorithm and the IPA module and move the gains quantization to the plaltform IPA module. Signed-off-by: Jacopo Mondi Reviewed-by: Kieran Bingham --- src/ipa/libipa/lsc_base.h | 2 +- src/ipa/libipa/lsc_polynomial.cpp | 17 +++++------------ src/ipa/libipa/lsc_polynomial.h | 8 ++++---- src/ipa/libipa/lsc_table.cpp | 22 ++++++++++++++++------ src/ipa/libipa/lsc_table.h | 2 +- src/ipa/rkisp1/algorithms/lsc.cpp | 23 +++++++++++++++-------- 6 files changed, 42 insertions(+), 32 deletions(-) diff --git a/src/ipa/libipa/lsc_base.h b/src/ipa/libipa/lsc_base.h index 4ad754b3727a..2cff34b32a2b 100644 --- a/src/ipa/libipa/lsc_base.h +++ b/src/ipa/libipa/lsc_base.h @@ -26,7 +26,7 @@ namespace ipa { namespace lsc { -using Components = std::map, std::less<>>; +using Components = std::map, std::less<>>; using ComponentsMap = std::map; } /* namespace lsc */ diff --git a/src/ipa/libipa/lsc_polynomial.cpp b/src/ipa/libipa/lsc_polynomial.cpp index 0eb017de6b50..419de2c12cf0 100644 --- a/src/ipa/libipa/lsc_polynomial.cpp +++ b/src/ipa/libipa/lsc_polynomial.cpp @@ -212,7 +212,7 @@ LscPolynomial::sampleForCrop(const Rectangle &cropRectangle, return components; } -std::vector +std::vector LscPolynomial::samplePolynomial(const lsc::Polynomial &poly, Span xPositions, Span yPositions, @@ -223,7 +223,7 @@ LscPolynomial::samplePolynomial(const lsc::Polynomial &poly, double y0 = cropRectangle.y / m; double w = cropRectangle.width / m; double h = cropRectangle.height / m; - std::vector samples; + std::vector samples; samples.reserve(xPositions.size() * yPositions.size()); @@ -231,16 +231,9 @@ LscPolynomial::samplePolynomial(const lsc::Polynomial &poly, for (double x : xPositions) { double xp = x0 + x * w; double yp = y0 + y * h; - /* - * The hardware uses 2.10 fixed point format and limits - * the legal values to [1..3.999]. Scale and clamp the - * sampled value accordingly. - */ - int v = static_cast( - poly.sampleAtNormalizedPixelPos(xp, yp) * - 1024); - v = std::clamp(v, 1024, 4095); - samples.push_back(v); + + samples.push_back(static_cast + (poly.sampleAtNormalizedPixelPos(xp, yp))); } } return samples; diff --git a/src/ipa/libipa/lsc_polynomial.h b/src/ipa/libipa/lsc_polynomial.h index 548ed94b2461..24b216a1aa25 100644 --- a/src/ipa/libipa/lsc_polynomial.h +++ b/src/ipa/libipa/lsc_polynomial.h @@ -65,10 +65,10 @@ public: std::vector xPos, std::vector yPos) override; private: - std::vector samplePolynomial(const lsc::Polynomial &poly, - Span xPositions, - Span yPositions, - const Rectangle &cropRectangle); + std::vector samplePolynomial(const lsc::Polynomial &poly, + Span xPositions, + Span yPositions, + const Rectangle &cropRectangle); PolynomialComponentsMap lscData_; }; diff --git a/src/ipa/libipa/lsc_table.cpp b/src/ipa/libipa/lsc_table.cpp index 639f4f6aa8b0..3e8b1a060ce7 100644 --- a/src/ipa/libipa/lsc_table.cpp +++ b/src/ipa/libipa/lsc_table.cpp @@ -30,6 +30,11 @@ namespace ipa { * * Parse the LSC data in tabular form from the \a sets tuning data. * + * \todo Currently the gain values parsed from tuning file are expressed in the + * platform fixed-point register format. Use gains in floating point + * representation for tabular LSC data in order to facilitate re-use of LSC + * tuning data across different platforms. + * * \return 0 on success or a negative error number otherwise */ int LscTable::parseLscData(const ValueNode &sets, @@ -82,15 +87,20 @@ int LscTable::parseLscComponent(const ValueNode &yamlSet, return 0; } -std::vector LscTable::parseTable(const ValueNode &tuningData, - const char *prop, - unsigned int numHSamples, - unsigned int numVSamples) +std::vector LscTable::parseTable(const ValueNode &tuningData, + const char *prop, + unsigned int numHSamples, + unsigned int numVSamples) { unsigned int lscNumSamples = numHSamples * numVSamples; - std::vector table = - tuningData[prop].get>().value_or(utils::defopt); + /* + * Cast to float even if gains are expressed as fixed-point + * representations. This prepares to express gains in floating point + * formats in tuning files. + */ + std::vector table = + tuningData[prop].get>().value_or(utils::defopt); if (table.size() != lscNumSamples) { LOG(LscTable, Error) << "Invalid '" << prop << "' values: expected " diff --git a/src/ipa/libipa/lsc_table.h b/src/ipa/libipa/lsc_table.h index a33208761c15..6d9b0c692b7f 100644 --- a/src/ipa/libipa/lsc_table.h +++ b/src/ipa/libipa/lsc_table.h @@ -42,7 +42,7 @@ public: private: int parseLscComponent(const ValueNode &yamlSet, unsigned int ct, const LscDescriptor &descriptor); - std::vector parseTable(const ValueNode &tuningData, + std::vector parseTable(const ValueNode &tuningData, const char *prop, unsigned int numHSamples, unsigned int numVSamples); diff --git a/src/ipa/rkisp1/algorithms/lsc.cpp b/src/ipa/rkisp1/algorithms/lsc.cpp index 7a95bfcfb93a..911ac6895a14 100644 --- a/src/ipa/rkisp1/algorithms/lsc.cpp +++ b/src/ipa/rkisp1/algorithms/lsc.cpp @@ -177,14 +177,21 @@ void LensShadingCorrection::setParameters(rkisp1_cif_isp_lsc_config &config) void LensShadingCorrection::copyTable(rkisp1_cif_isp_lsc_config &config, const lsc::Components &set) { - const auto &r = set.at("r"); - std::copy(r.begin(), r.end(), &config.r_data_tbl[0][0]); - const auto &gr = set.at("gr"); - std::copy(gr.begin(), gr.end(), &config.gr_data_tbl[0][0]); - const auto &gb = set.at("gb"); - std::copy(gb.begin(), gb.end(), &config.gb_data_tbl[0][0]); - const auto &b = set.at("b"); - std::copy(b.begin(), b.end(), &config.b_data_tbl[0][0]); + /* + * The hardware uses 2.10 fixed point format and limits the legal values + * to [1..3.999]. Scale and clamp the sampled values accordingly. + */ + const auto quantizeSet = [&](const std::string &key, uint16_t *dst) { + const auto &s = set.at(key); + std::transform(s.begin(), s.end(), dst, [](float f) { + return std::clamp(f * 1024, 1024, 4095); + }); + }; + + quantizeSet("r", &config.r_data_tbl[0][0]); + quantizeSet("gr", &config.gr_data_tbl[0][0]); + quantizeSet("gb", &config.gb_data_tbl[0][0]); + quantizeSet("b", &config.b_data_tbl[0][0]); } /**