From patchwork Wed Jul 8 15:51:14 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jacopo Mondi X-Patchwork-Id: 27341 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 8C2ACC3339 for ; Wed, 8 Jul 2026 15:51:58 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 34D626612B; Wed, 8 Jul 2026 17:51:58 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="fzHUVSct"; 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 A89D06608D for ; Wed, 8 Jul 2026 17:51:22 +0200 (CEST) Received: from [192.168.1.182] (93-46-82-201.ip106.fastwebnet.it [93.46.82.201]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id ADF30401E; Wed, 8 Jul 2026 17:50:32 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1783525832; bh=GY5ogt0BidgoFAt5zKvDZ5pfXZ8jQRBTkReJfnAGbZ0=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=fzHUVSctbMQo3YugRs+tTPdzyryl6ksK6aKMNDVWmwBV9LvrgywHGIJZKSDSn58YG QqG6P3bajJBS+qucAZczNAWcutAT59uh9J/FBLLQ7mx2e+vU4x06zXj6jC4873ndNa 7lmLMeXZXvHMJ4zJ+Ym64XzgKb5jCb/o3OpSdqzU= From: Jacopo Mondi Date: Wed, 08 Jul 2026 17:51:14 +0200 Subject: [PATCH v5 32/36] ipa: libipa: lsc: Template lsc::Components MIME-Version: 1.0 Message-Id: <20260708-libipa-algorithms-v5-32-0759d0359f52@ideasonboard.com> References: <20260708-libipa-algorithms-v5-0-0759d0359f52@ideasonboard.com> In-Reply-To: <20260708-libipa-algorithms-v5-0-0759d0359f52@ideasonboard.com> To: libcamera-devel@lists.libcamera.org, Stefan Klug Cc: Jacopo Mondi X-Mailer: b4 0.14.3 X-Developer-Signature: v=1; a=openpgp-sha256; l=16122; i=jacopo.mondi@ideasonboard.com; h=from:subject:message-id; bh=GY5ogt0BidgoFAt5zKvDZ5pfXZ8jQRBTkReJfnAGbZ0=; b=owEBbQKS/ZANAwAKAXI0Bo8WoVY8AcsmYgBqTnHtSJLeR/DKR/NNx1oBBROrZ9PvPMfGleLfd Pq5Bk6koduJAjMEAAEKAB0WIQS1xD1IgJogio9YOMByNAaPFqFWPAUCak5x7QAKCRByNAaPFqFW PBxpD/9IqZQ7tIBqBbGRttX0qx1Qm+Mur7CEfi81atxZpCSSDkCAHbwmr7KZkDOKvS4ruoOA2i1 rUtjRwSApy9+xH+jh3/NqZqCe/yo433lPCXUREVvNr3tYZwhZJH3nlSHpYgg3MV54e81BySJJz/ uj9MNRmUQoIOTmyOvzdMNNR599dYvjpwQWipW32CjQbQmbtkSN2h7fo4uZFN1MrTXMxzI4bJ3Ql 3J4Nf/0Aa7tROZ0pI8OXX96RLW8M/ecqvGP4SM9C/iyqcjELCcYhhaSZ3ZTWl5ugSqZ3mgR4fSY /ve7dBoD+BUQPk6e/CNbUQM79QPnRFe3k3OzEHIhbF27ACZ8dZoUTWCzNYxu2KqJTRAB649KM1N lMGs24GwLPlbh4ClNM1DV7eySfJ3tJ5m0o3I2w4Yzv58pDICMtOSO69kR/IwnG5YvmZ0p6MQ2nk NcrdfzYKet7VeDjy6BqlVXQ3u/QbLJAeIH/Sb4DiZO6x12CXMyTBEAbgSZYy+8jWi6g9y9xgyNK fZ9u0GoIISWTcBhD3gmXL6iSWwb2OvVdfMqWYpCTEzTRIK18G+Hjjt7pkRoyDd9o6vpqbmF9iNp GPGY1PUpS0IGP3j6PN8m0k2VZIa1TjOjGNDc137Hptfu7+AGgibob9K9ClH2CtNvTInf/QknsMk /ul664JLaOdC/ZQ== 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 lsc::Component type maps a string that identifies a colour channel to a vector of gains expressed in register format. The current implementation assumes gains in u16 format which matches the RkISP1 register format. As lsc::Components is used both to store gains as loaded from tuning file and the result of the radial polynomial expansion, different ISP use different bit widths to represent the gain values. Template the lsc::Components and lsc::ComponentsMap with the type used to store the gain values. Use the Quantized::QuantizedType typedef which expresses the integer type required to store all bits of the fixed point number used to instantiate LscAlgorithm<> by the IPA module to template Components<>. The LscTable class now requires to inline all tuning file parsing helpers, so we can drop the just introduced LscTableBase. Specialize Interpolator> for uint16_t as this is the format used by RkISP1. Signed-off-by: Jacopo Mondi --- src/ipa/libipa/lsc.h | 13 +++-- src/ipa/libipa/lsc_base.cpp | 19 +++++--- src/ipa/libipa/lsc_base.h | 21 +++++--- src/ipa/libipa/lsc_polynomial.h | 19 +++++--- src/ipa/libipa/lsc_table.cpp | 100 +++----------------------------------- src/ipa/libipa/lsc_table.h | 90 ++++++++++++++++++++++++++-------- src/ipa/rkisp1/algorithms/lsc.cpp | 4 +- src/ipa/rkisp1/algorithms/lsc.h | 3 +- 8 files changed, 125 insertions(+), 144 deletions(-) diff --git a/src/ipa/libipa/lsc.h b/src/ipa/libipa/lsc.h index d48336b169f4..6dc76b6572b3 100644 --- a/src/ipa/libipa/lsc.h +++ b/src/ipa/libipa/lsc.h @@ -53,6 +53,9 @@ public: template class LscAlgorithm : public LscAlgorithmBase { +private: + using T = typename U::QuantizedType; + public: int init(const ValueNode &tuningData, ControlInfoMap::Map &controls, const LscDescriptor &descriptor) @@ -94,7 +97,7 @@ public: const std::vector &yPos) { LOG(Lsc, Debug) << "Sample Lsc data for " << analogCrop; - lsc::ComponentsMap lscData = + lsc::ComponentsMap lscData = impl_->sampleForCrop(analogCrop, xPos, yPos); /* @@ -112,20 +115,20 @@ public: return 0; } - const lsc::Components interpolateComponents(unsigned int ct) + const lsc::Components interpolateComponents(unsigned int ct) { return sets_.getInterpolated(ct); } - const lsc::ComponentsMap getComponents() + const lsc::ComponentsMap getComponents() { return lscData_; } private: std::unique_ptr> impl_; - Interpolator sets_; - lsc::ComponentsMap lscData_; + Interpolator> sets_; + lsc::ComponentsMap lscData_; bool polynomial_; }; diff --git a/src/ipa/libipa/lsc_base.cpp b/src/ipa/libipa/lsc_base.cpp index dd2e1128a546..235f11e8c53b 100644 --- a/src/ipa/libipa/lsc_base.cpp +++ b/src/ipa/libipa/lsc_base.cpp @@ -32,25 +32,28 @@ namespace ipa { namespace lsc { /** - * \typedef Components + * \class Components * \brief Associate a colour components with a list of gains + * \tparam T The type used to store the gain values when loaded from tuning file * * LSC tables are defined as a list of gain values associated to a colour * component. * * As different ISP support different colour components (usually 'r', 'gr', * 'gb', 'b' or just 'r', 'g', 'b') this class associates a string - * identifier for the colour component to a list of gains. + * identifier for the colour component to a list of gains of type \a T. * - * Each key name shall match an entry in the tuning file. + * Each key name shall match an entry in the tuning file and the type \a T + * shall match the size of the registers where gains are stored. * * The list of keys is provided to the LscAlgorithm class using \a * LscDescriptor::keys. */ /** - * \typedef ComponentsMap + * \class ComponentsMap * \brief Associate a colour temperature to a LSC table + * \tparam T The type used to store the gain values when loaded from tuning file * * An LSC table is generated during the tuning phase for a specific light * temperature, and a tuning file usually contains LSC tables generated for @@ -61,10 +64,10 @@ namespace lsc { #ifndef __DOXYGEN__ template<> -void Interpolator:: - interpolate(const lsc::Components &a, - const lsc::Components &b, - lsc::Components &dest, +void Interpolator>:: + interpolate(const lsc::Components &a, + const lsc::Components &b, + lsc::Components &dest, double lambda) { for (auto const &[k, v] : a) diff --git a/src/ipa/libipa/lsc_base.h b/src/ipa/libipa/lsc_base.h index 721fd137ef21..4edcb2637d1b 100644 --- a/src/ipa/libipa/lsc_base.h +++ b/src/ipa/libipa/lsc_base.h @@ -26,8 +26,15 @@ namespace ipa { namespace lsc { -using Components = std::map>; -using ComponentsMap = std::map; +template +class Components : public std::map> +{ +}; + +template +class ComponentsMap : public std::map> +{ +}; } /* namespace lsc */ @@ -43,10 +50,10 @@ void interpolateVector(const std::vector &a, const std::vector &b, } template<> -void Interpolator:: - interpolate(const lsc::Components &a, - const lsc::Components &b, - lsc::Components &dest, +void Interpolator>:: + interpolate(const lsc::Components &a, + const lsc::Components &b, + lsc::Components &dest, double lambda); #endif /* __DOXYGEN__ */ @@ -66,7 +73,7 @@ public: virtual int parseLscData(const ValueNode &tuningData, const LscDescriptor &descriptor) = 0; - virtual lsc::ComponentsMap + virtual lsc::ComponentsMap sampleForCrop(const Rectangle &cropRectangle, std::vector xPos, std::vector yPos); }; diff --git a/src/ipa/libipa/lsc_polynomial.h b/src/ipa/libipa/lsc_polynomial.h index 04d7465b3af0..a84a736734e1 100644 --- a/src/ipa/libipa/lsc_polynomial.h +++ b/src/ipa/libipa/lsc_polynomial.h @@ -67,6 +67,9 @@ protected: template class LscPolynomial : public LscPolynomialBase, public LscImplementation { +private: + using T = typename U::QuantizedType; + public: int parseLscData(const ValueNode &yamlSets, const LscDescriptor &descriptor) override @@ -74,14 +77,14 @@ public: return LscPolynomialBase::parseLscData(yamlSets, descriptor); } - lsc::ComponentsMap + lsc::ComponentsMap sampleForCrop(const Rectangle &cropRectangle, std::vector xPos, std::vector yPos) override { - lsc::ComponentsMap components; + lsc::ComponentsMap components; for (const auto &[t, c] : lscData_) { - lsc::Components comp; + lsc::Components comp; for (const auto &[k, p] : c) { comp.emplace(std::piecewise_construct, @@ -97,17 +100,17 @@ public: } 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) { double m = poly.getM(); double x0 = cropRectangle.x / m; 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()); diff --git a/src/ipa/libipa/lsc_table.cpp b/src/ipa/libipa/lsc_table.cpp index 0e8f144cb873..4c9ab587d014 100644 --- a/src/ipa/libipa/lsc_table.cpp +++ b/src/ipa/libipa/lsc_table.cpp @@ -13,99 +13,6 @@ LOG_DEFINE_CATEGORY(LscTable) namespace ipa { -/** - * \class LscTableBase - * \brief Base class for LscTable - * - * Base class for LscTable for non-templated functions. - */ - -/** - * \brief Parse tabular LSC data - * \param[in] sets The tuning file content - * \param[in] descriptor The LSC engine descriptor - * - * Parse the LSC data in tabular form from the \a sets tuning data. - * - * \return 0 on success or a negative error number otherwise - */ -int LscTableBase::parseLscData(const ValueNode &sets, - const LscDescriptor &descriptor) -{ - for (const auto &set : sets.asList()) { - uint32_t ct = set["ct"].get(0); - - int ret = parseLscComponent(set, ct, descriptor); - if (ret) - return ret; - } - - if (lscData_.empty()) { - LOG(LscTable, Error) << "Failed to load any sets"; - return -EINVAL; - } - - return 0; -} - -int LscTableBase::parseLscComponent(const ValueNode &yamlSet, - unsigned int ct, const LscDescriptor &descriptor) -{ - lsc::Components component; - for (auto &k : descriptor.keys) { - auto [it, inserted] = component.emplace( - std::piecewise_construct, - std::forward_as_tuple(k.c_str()), - std::forward_as_tuple(parseTable(yamlSet, - k.c_str(), - descriptor.numHCells, - descriptor.numVCells))); - if (!inserted || it->second.empty()) { - LOG(LscTable, Error) - << "Set " << k << " for color temperature " - << ct << " is missing"; - return -EINVAL; - } - } - - auto [it, inserted] = lscData_.emplace(ct, component); - if (!inserted) { - LOG(LscTable, Error) - << "Multiple sets found for color temperature " - << ct; - return -EINVAL; - } - - return 0; -} - -std::vector LscTableBase::parseTable(const ValueNode &tuningData, - const char *prop, - unsigned int numHCells, - unsigned int numVCells) -{ - unsigned int lscNumSamples = numHCells * numVCells; - - std::vector table = - tuningData[prop].get>().value_or(utils::defopt); - if (table.size() != lscNumSamples) { - LOG(LscTable, Error) - << "Invalid '" << prop << "' values: expected " - << lscNumSamples - << " elements, got " << table.size(); - return {}; - } - - return table; -} - -/** - * \var LscTableBase::lscData_ - * \brief The tabular LSC data - * - * Maps colour temperatures to per-colour channel vector of gains - */ - /** * \class LscTable * \brief Table based LSC algorithm implementation @@ -119,8 +26,13 @@ std::vector LscTableBase::parseTable(const ValueNode &tuningData, /** * \fn LscTable::parseLscData() + * \brief Parse tabular LSC data + * \param[in] sets The tuning file content + * \param[in] descriptor The LSC engine descriptor + * + * Parse the LSC data in tabular form from the \a sets tuning data. * - * \copydoc LscTableBase::parseLscData() + * \return 0 on success or a negative error number otherwise */ } /* namespace ipa */ diff --git a/src/ipa/libipa/lsc_table.h b/src/ipa/libipa/lsc_table.h index 65e04b09c3ff..fe4e133060a6 100644 --- a/src/ipa/libipa/lsc_table.h +++ b/src/ipa/libipa/lsc_table.h @@ -23,34 +23,33 @@ LOG_DECLARE_CATEGORY(LscTable) namespace ipa { -class LscTableBase +template +class LscTable : public LscImplementation { -protected: - int parseLscData(const ValueNode &sets, - const LscDescriptor &descriptor); - private: - int parseLscComponent(const ValueNode &yamlSet, - unsigned int ct, const LscDescriptor &descriptor); - std::vector parseTable(const ValueNode &tuningData, - const char *prop, - unsigned int numHCells, - unsigned int numVCells); -protected: - lsc::ComponentsMap lscData_; -}; + using T = typename U::QuantizedType; -template -class LscTable : public LscTableBase, public LscImplementation -{ public: int parseLscData(const ValueNode &sets, const LscDescriptor &descriptor) override { - return LscTableBase::parseLscData(sets, descriptor); + for (const auto &set : sets.asList()) { + uint32_t ct = set["ct"].get(0); + + int ret = parseLscComponent(set, ct, descriptor); + if (ret) + return ret; + } + + if (lscData_.empty()) { + LOG(LscTable, Error) << "Failed to load any sets"; + return -EINVAL; + } + + return 0; } - lsc::ComponentsMap + lsc::ComponentsMap sampleForCrop([[maybe_unused]] const Rectangle &cropRectangle, [[maybe_unused]] std::vector xPos, [[maybe_unused]] std::vector yPos) override @@ -59,6 +58,59 @@ public: << "Tabular LSC data doesn't support resampling"; return lscData_; } + +private: + int parseLscComponent(const ValueNode &yamlSet, + unsigned int ct, const LscDescriptor &descriptor) + { + lsc::Components component; + for (auto &k : descriptor.keys) { + auto [it, inserted] = component.emplace( + std::piecewise_construct, + std::forward_as_tuple(k.c_str()), + std::forward_as_tuple(parseTable(yamlSet, + k.c_str(), + descriptor.numHCells, + descriptor.numVCells))); + if (!inserted || it->second.empty()) { + LOG(LscTable, Error) + << "Set " << k << " for color temperature " + << ct << " is missing"; + return -EINVAL; + } + } + + auto [it, inserted] = lscData_.emplace(ct, component); + if (!inserted) { + LOG(LscTable, Error) + << "Multiple sets found for color temperature " + << ct; + return -EINVAL; + } + + return 0; + } + + std::vector parseTable(const ValueNode &tuningData, + const char *prop, unsigned int numHCells, + unsigned int numVCells) + { + unsigned int kLscNumSamples = numHCells * numVCells; + + std::vector table = + tuningData[prop].get>().value_or(utils::defopt); + if (table.size() != kLscNumSamples) { + LOG(LscTable, Error) + << "Invalid '" << prop << "' values: expected " + << kLscNumSamples + << " elements, got " << table.size(); + return {}; + } + + return table; + } + + lsc::ComponentsMap lscData_; }; } /* namespace ipa */ diff --git a/src/ipa/rkisp1/algorithms/lsc.cpp b/src/ipa/rkisp1/algorithms/lsc.cpp index 9db67197ad64..7fa495acf10a 100644 --- a/src/ipa/rkisp1/algorithms/lsc.cpp +++ b/src/ipa/rkisp1/algorithms/lsc.cpp @@ -176,7 +176,7 @@ void LensShadingCorrection::setParameters(rkisp1_cif_isp_lsc_config &config) } void LensShadingCorrection::copyTable(rkisp1_cif_isp_lsc_config &config, - const lsc::Components &set) + const lsc::Components &set) { const auto &r = set.at("r"); std::copy(r.begin(), r.end(), &config.r_data_tbl[0][0]); @@ -235,7 +235,7 @@ void LensShadingCorrection::prepare([[maybe_unused]] IPAContext &context, setParameters(*config); - const lsc::Components &set = lscAlgo_.interpolateComponents(quantizedCt); + const lsc::Components &set = lscAlgo_.interpolateComponents(quantizedCt); copyTable(*config, set); lastAppliedCt_ = ct; diff --git a/src/ipa/rkisp1/algorithms/lsc.h b/src/ipa/rkisp1/algorithms/lsc.h index 31156f56cd21..eb32997d64bc 100644 --- a/src/ipa/rkisp1/algorithms/lsc.h +++ b/src/ipa/rkisp1/algorithms/lsc.h @@ -42,7 +42,8 @@ public: ControlList &metadata) override; private: void setParameters(rkisp1_cif_isp_lsc_config &config); - void copyTable(rkisp1_cif_isp_lsc_config &config, const lsc::Components &set0); + void copyTable(rkisp1_cif_isp_lsc_config &config, + const lsc::Components &set0); std::vector parseSizes(const ValueNode &tuningData, const char *prop); std::vector sizesListToPositions(Span sizes);