From patchwork Wed Jul 8 09:24:56 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jacopo Mondi X-Patchwork-Id: 27274 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 8B816C3318 for ; Wed, 8 Jul 2026 09:25:27 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 741C666073; Wed, 8 Jul 2026 11:25:18 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="H/17L4lb"; 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 DAC5C66075 for ; Wed, 8 Jul 2026 11:25:07 +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 3A0741D03; Wed, 8 Jul 2026 11:24:18 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1783502658; bh=/YB+sHNbmPImz60FaygKFvKtjE2RV5KVOUMkfOU9+mY=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=H/17L4lbhaA1Tc+RBWkRdqf9WdLk7EyI+9Ff2E6t0uVZP0RV+z+Hs+Aedj/qEtIkF ybkP5Yemivr/nXeXaC2x/5IzGpmqcIET2Ry1FzdKFwjJpM0aTTdBzebNGLBWWlNeDL 54wXdFIOcv0IRI0WmV7UR7WxTVajT9fW1bcJCk7s= From: Jacopo Mondi Date: Wed, 08 Jul 2026 11:24:56 +0200 Subject: [PATCH v4 09/35] ipa: rkisp1: lsc: Re-architecture Lsc support MIME-Version: 1.0 Message-Id: <20260708-libipa-algorithms-v4-9-f0e99f035294@ideasonboard.com> References: <20260708-libipa-algorithms-v4-0-f0e99f035294@ideasonboard.com> In-Reply-To: <20260708-libipa-algorithms-v4-0-f0e99f035294@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=15591; i=jacopo.mondi@ideasonboard.com; h=from:subject:message-id; bh=/YB+sHNbmPImz60FaygKFvKtjE2RV5KVOUMkfOU9+mY=; b=owEBbQKS/ZANAwAKAXI0Bo8WoVY8AcsmYgBqThdt81hUOpush5EbJ/Cu9JU0oYLXKJemdMZeM wD3zDvKogOJAjMEAAEKAB0WIQS1xD1IgJogio9YOMByNAaPFqFWPAUCak4XbQAKCRByNAaPFqFW PBl9D/97pPP+grLwcJL2RDgzf7pkRcbMKrfVDn4swWdW+jG1o7++S1rdgIEgSqrAqL44i/eaTa8 oYaEU7B5Q5xenoSK9Hco4bNnTwsHXjjWbjWuS7g9NZZsdyM3U9P5kOJqKqWiic6jU2vppAjOP6y KlNaIJ2ustmCAi0zP89fuW9FvAOIXsEHuq7onNONo0RaVTx395A/0ihuwlKuNPCx0YvOjlOqOQe ETQ8gSMiT1l53+B5OGkywR/oj9J0OUn2bnBuoYODhH7vPUer3++k84ORYeJMy/pNgF7Js6dpAJ/ +P7IxZvftlr2UB7Ft3i/AovzmSbWEayTC2D6zasJCX3TeFOYHjhol4np9qTDuztKqyYQJhlp5GI +qGBEB2BEDp5pNdeouOeVJm7TPft14sK06PnLtuO0rZAkHfkMDv4VMf//Y+XiUbHNPDaw8phvoT xbTNHxjmHZ26rDGl+jBsSxC6JT2gFppwGt9VlpWX7rk1OIFyZ1audgifFpfahr2MWRGBY0zwpJd Cc6v5XqHgLLyUpSSgSbXzwI9QJ/C5kZ2pAR8m1q92s5MoSiIT+t8CPKiqESl2huEgoixwCfR4ah /JhBgefSLDwbjCFOc7ard1HkcuHnYbx71w0JkLmqOREGX4t/M8wEUPXj5sNpeTtC/sHELV610VW jieHkYWNipcO0WQ== 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 RkISP1 LSC implementation is realized by the following components: - 2 "tuning file" parser classes: - LscPolynomialLoader - LscTableLoader which implement the "parseLscData" function that loads from tuning file a polynomial representation of the lsc gains or a table of gains respectively. - A hierachy of "ShadingDescriptors" - LscPolynomialShadingDescriptor - LscTableShadingDescriptor that implement the "sampleForCrop" interface which expands a polynomial to a list of gains for the LscPolynomialShadingDescriptor and simply return the data parsed from tuning file for LscTableShadingDescriptor. Unify the two functionalities in a single class hierarchy whose interface is defined by the LscImplementation pure virtual class implemented by LscPolynomialImpl and LscTableImpl. The LensShadingCorrection algorithm class instantiates the correct sub-class and uses the file parsing and table expansion functionalities by implementing the composition pattern. Signed-off-by: Jacopo Mondi --- src/ipa/rkisp1/algorithms/lsc.cpp | 247 +++++++++++++++++--------------------- src/ipa/rkisp1/algorithms/lsc.h | 49 ++++---- 2 files changed, 141 insertions(+), 155 deletions(-) diff --git a/src/ipa/rkisp1/algorithms/lsc.cpp b/src/ipa/rkisp1/algorithms/lsc.cpp index 06f5913c5750..38380aa839c6 100644 --- a/src/ipa/rkisp1/algorithms/lsc.cpp +++ b/src/ipa/rkisp1/algorithms/lsc.cpp @@ -54,17 +54,6 @@ void Interpolator:: namespace ipa::rkisp1::algorithms { -/** - * \class LensShadingCorrection - * \brief RkISP1 Lens Shading Correction control - * - * Due to the optical characteristics of the lens, the light intensity received - * by the sensor is not uniform. - * - * The Lens Shading Correction algorithm applies multipliers to all pixels to - * compensate for the lens shading effect. The coefficients are specified in a - * downscaled table in the tuning data. - */ LOG_DEFINE_CATEGORY(RkISP1Lsc) @@ -72,54 +61,110 @@ namespace { constexpr int kColourTemperatureQuantization = 10; -class LscPolynomialShadingDescriptor : public LensShadingCorrection::ShadingDescriptor +class LscPolynomialImpl : public LscImplementation { +private: + struct PolynomialComponents { + LscPolynomial pr; + LscPolynomial pgr; + LscPolynomial pgb; + LscPolynomial pb; + }; + using PolynomialComponentsMap = std::map; + public: - LscPolynomialShadingDescriptor(const LscPolynomial &pr, const LscPolynomial &pgr, - const LscPolynomial &pgb, const LscPolynomial &pb) - : pr_(pr), pgr_(pgr), pgb_(pgb), pb_(pb) + LscPolynomialImpl(const Size &sensorSize) + : sensorSize_(sensorSize) { } - LensShadingCorrection::Components sampleForCrop(const Rectangle &cropRectangle, - Span xSizes, - Span ySizes) override; + int parseLscData(const ValueNode &sets) override; + + LensShadingCorrection::ComponentsMap + sampleForCrop(const Rectangle &cropRectangle, + Span xSizes, + Span ySizes) override; private: + std::vector sizesListToPositions(Span sizes); std::vector samplePolynomial(const LscPolynomial &poly, Span xPositions, Span yPositions, const Rectangle &cropRectangle); + PolynomialComponentsMap lscData_; + Size sensorSize_; +}; - std::vector sizesListToPositions(Span sizes); +int LscPolynomialImpl::parseLscData(const ValueNode &sets) +{ + for (const auto &set : sets.asList()) { + std::optional pr, pgr, pgb, pb; + uint32_t ct = set["ct"].get(0); - LscPolynomial pr_; - LscPolynomial pgr_; - LscPolynomial pgb_; - LscPolynomial pb_; -}; + if (lscData_.count(ct)) { + LOG(RkISP1Lsc, Error) + << "Multiple sets found for " + << "color temperature " << ct; + return -EINVAL; + } + + pr = set["r"].get(); + pgr = set["gr"].get(); + pgb = set["gb"].get(); + pb = set["b"].get(); + + if (!(pr || pgr || pgb || pb)) { + LOG(RkISP1Lsc, Error) + << "Failed to parse polynomial for " + << "colour temperature " << ct; + return -EINVAL; + } -LensShadingCorrection::Components -LscPolynomialShadingDescriptor::sampleForCrop(const Rectangle &cropRectangle, - Span xSizes, - Span ySizes) + pr->setReferenceImageSize(sensorSize_); + pgr->setReferenceImageSize(sensorSize_); + pgb->setReferenceImageSize(sensorSize_); + pb->setReferenceImageSize(sensorSize_); + + lscData_.emplace(std::piecewise_construct, + std::forward_as_tuple(ct), + std::forward_as_tuple(PolynomialComponents{ *pr, *pgr, *pgb, *pb })); + } + + if (lscData_.empty()) { + LOG(RkISP1Lsc, Error) << "Failed to load any sets"; + return -EINVAL; + } + + return 0; +} + +LensShadingCorrection::ComponentsMap +LscPolynomialImpl::sampleForCrop(const Rectangle &cropRectangle, + Span xSizes, + Span ySizes) { std::vector xPos = sizesListToPositions(xSizes); std::vector yPos = sizesListToPositions(ySizes); - return { - .r = samplePolynomial(pr_, xPos, yPos, cropRectangle), - .gr = samplePolynomial(pgr_, xPos, yPos, cropRectangle), - .gb = samplePolynomial(pgb_, xPos, yPos, cropRectangle), - .b = samplePolynomial(pb_, xPos, yPos, cropRectangle) - }; + LensShadingCorrection::ComponentsMap components; + + for (const auto &[k, p] : lscData_) { + components[k] = { + samplePolynomial(p.pr, xPos, yPos, cropRectangle), + samplePolynomial(p.pgr, xPos, yPos, cropRectangle), + samplePolynomial(p.pgb, xPos, yPos, cropRectangle), + samplePolynomial(p.pb, xPos, yPos, cropRectangle) + }; + } + + return components; } std::vector -LscPolynomialShadingDescriptor::samplePolynomial(const LscPolynomial &poly, - Span xPositions, - Span yPositions, - const Rectangle &cropRectangle) +LscPolynomialImpl::samplePolynomial(const LscPolynomial &poly, + Span xPositions, + Span yPositions, + const Rectangle &cropRectangle) { double m = poly.getM(); double x0 = cropRectangle.x / m; @@ -160,7 +205,7 @@ LscPolynomialShadingDescriptor::samplePolynomial(const LscPolynomial &poly, * output: 0.0 0.2 0.5 0.8 1.0 */ std::vector -LscPolynomialShadingDescriptor::sizesListToPositions(Span sizes) +LscPolynomialImpl::sizesListToPositions(Span sizes) { const int half = sizes.size(); std::vector positions(half * 2 + 1); @@ -176,74 +221,12 @@ LscPolynomialShadingDescriptor::sizesListToPositions(Span sizes) return positions; } -class LscPolynomialLoader +class LscTableImpl : public LscImplementation { public: - LscPolynomialLoader(const Size &sensorSize) - : sensorSize_(sensorSize) - { - } - - int parseLscData(const ValueNode &sets, - LensShadingCorrection::ShadingDescriptorMap &lscData); + int parseLscData(const ValueNode &sets) override; -private: - Size sensorSize_; -}; - -int LscPolynomialLoader::parseLscData(const ValueNode &sets, - LensShadingCorrection::ShadingDescriptorMap &lscData) -{ - for (const auto &set : sets.asList()) { - std::optional pr, pgr, pgb, pb; - uint32_t ct = set["ct"].get(0); - - if (lscData.count(ct)) { - LOG(RkISP1Lsc, Error) - << "Multiple sets found for " - << "color temperature " << ct; - return -EINVAL; - } - - pr = set["r"].get(); - pgr = set["gr"].get(); - pgb = set["gb"].get(); - pb = set["b"].get(); - - if (!(pr || pgr || pgb || pb)) { - LOG(RkISP1Lsc, Error) - << "Failed to parse polynomial for " - << "colour temperature " << ct; - return -EINVAL; - } - - pr->setReferenceImageSize(sensorSize_); - pgr->setReferenceImageSize(sensorSize_); - pgb->setReferenceImageSize(sensorSize_); - pb->setReferenceImageSize(sensorSize_); - - lscData.emplace( - ct, std::make_unique( - *pr, *pgr, *pgb, *pb)); - } - - if (lscData.empty()) { - LOG(RkISP1Lsc, Error) << "Failed to load any sets"; - return -EINVAL; - } - - return 0; -} - -class LscTableShadingDescriptor : public LensShadingCorrection::ShadingDescriptor -{ -public: - LscTableShadingDescriptor(LensShadingCorrection::Components components) - : lscData_(std::move(components)) - { - } - - LensShadingCorrection::Components + LensShadingCorrection::ComponentsMap sampleForCrop([[maybe_unused]] const Rectangle &cropRectangle, [[maybe_unused]] Span xSizes, [[maybe_unused]] Span ySizes) override @@ -253,28 +236,19 @@ public: return lscData_; } -private: - LensShadingCorrection::Components lscData_; -}; - -class LscTableLoader -{ -public: - int parseLscData(const ValueNode &sets, - LensShadingCorrection::ShadingDescriptorMap &lscData); - private: std::vector parseTable(const ValueNode &tuningData, const char *prop); + + LensShadingCorrection::ComponentsMap lscData_; }; -int LscTableLoader::parseLscData(const ValueNode &sets, - LensShadingCorrection::ShadingDescriptorMap &lscData) +int LscTableImpl::parseLscData(const ValueNode &sets) { for (const auto &set : sets.asList()) { uint32_t ct = set["ct"].get(0); - if (lscData.count(ct)) { + if (lscData_.count(ct)) { LOG(RkISP1Lsc, Error) << "Multiple sets found for color temperature " << ct; @@ -295,11 +269,10 @@ int LscTableLoader::parseLscData(const ValueNode &sets, return -EINVAL; } - lscData.emplace( - ct, std::make_unique(std::move(components))); + lscData_.emplace(ct, std::move(components)); } - if (lscData.empty()) { + if (lscData_.empty()) { LOG(RkISP1Lsc, Error) << "Failed to load any sets"; return -EINVAL; } @@ -307,7 +280,7 @@ int LscTableLoader::parseLscData(const ValueNode &sets, return 0; } -std::vector LscTableLoader::parseTable(const ValueNode &tuningData, +std::vector LscTableImpl::parseTable(const ValueNode &tuningData, const char *prop) { static constexpr unsigned int kLscNumSamples = @@ -363,6 +336,18 @@ unsigned int quantize(unsigned int value, unsigned int step) } /* namespace */ +/** + * \class LensShadingCorrection + * \brief RkISP1 Lens Shading Correction control + * + * Due to the optical characteristics of the lens, the light intensity received + * by the sensor is not uniform. + * + * The Lens Shading Correction algorithm applies multipliers to all pixels to + * compensate for the lens shading effect. The coefficients are specified in a + * downscaled table in the tuning data. + */ + LensShadingCorrection::LensShadingCorrection() : lastAppliedCt_(0), lastAppliedQuantizedCt_(0) { @@ -388,22 +373,21 @@ int LensShadingCorrection::init([[maybe_unused]] IPAContext &context, return -EINVAL; } - ShadingDescriptorMap lscData; - int ret = 0; + int ret; std::string type = tuningData["type"].get("table"); if (type == "table") { LOG(RkISP1Lsc, Debug) << "Loading tabular LSC data."; - auto loader = LscTableLoader(); - ret = loader.parseLscData(sets, lscData); + algo_ = std::make_unique(); + ret = algo_->parseLscData(sets); } else if (type == "polynomial") { LOG(RkISP1Lsc, Debug) << "Loading polynomial LSC data."; /* * \todo: Most likely the reference frame should be native_size. * Let's wait how the internal discussions progress. */ - auto loader = LscPolynomialLoader(context.sensorInfo.activeAreaSize); - ret = loader.parseLscData(sets, lscData); + algo_ = std::make_unique(context.sensorInfo.activeAreaSize); + ret = algo_->parseLscData(sets); } else { LOG(RkISP1Lsc, Error) << "Unsupported LSC data type '" << type << "'"; @@ -416,8 +400,6 @@ int LensShadingCorrection::init([[maybe_unused]] IPAContext &context, context.ctrlMap[&controls::LensShadingCorrectionEnable] = ControlInfo(false, true, true); - shadingDescriptors_ = std::move(lscData); - return 0; } @@ -453,11 +435,8 @@ int LensShadingCorrection::configure(IPAContext &context, } LOG(RkISP1Lsc, Debug) << "Sample LSC data for " << configInfo.analogCrop; - std::map shadingData; - for (const auto &[t, descriptor] : shadingDescriptors_) - shadingData[t] = descriptor->sampleForCrop(configInfo.analogCrop, - xSize_, ySize_); - + ComponentsMap shadingData = algo_->sampleForCrop(configInfo.analogCrop, + xSize_, ySize_); sets_.setData(std::move(shadingData)); context.activeState.lsc.enabled = true; diff --git a/src/ipa/rkisp1/algorithms/lsc.h b/src/ipa/rkisp1/algorithms/lsc.h index 0a256e225327..3463531f2ab2 100644 --- a/src/ipa/rkisp1/algorithms/lsc.h +++ b/src/ipa/rkisp1/algorithms/lsc.h @@ -18,9 +18,20 @@ namespace libcamera { namespace ipa::rkisp1::algorithms { +class LscImplementation; + class LensShadingCorrection : public Algorithm { public: + struct Components { + std::vector r; + std::vector gr; + std::vector gb; + std::vector b; + }; + + using ComponentsMap = std::map; + LensShadingCorrection(); ~LensShadingCorrection() = default; @@ -36,31 +47,10 @@ public: IPAFrameContext &frameContext, const rkisp1_stat_buffer *stats, ControlList &metadata) override; - - struct Components { - std::vector r; - std::vector gr; - std::vector gb; - std::vector b; - }; - - class ShadingDescriptor - { - public: - virtual ~ShadingDescriptor() = default; - virtual Components sampleForCrop(const Rectangle &cropRectangle, - Span xSizes, - Span ySizes) = 0; - }; - - using ShadingDescriptorMap = std::map>; - private: void setParameters(rkisp1_cif_isp_lsc_config &config); void copyTable(rkisp1_cif_isp_lsc_config &config, const Components &set0); - ShadingDescriptorMap shadingDescriptors_; - ipa::Interpolator sets_; std::vector xSize_; std::vector ySize_; uint16_t xGrad_[RKISP1_CIF_ISP_LSC_SECTORS_TBL_SIZE]; @@ -70,6 +60,23 @@ private: unsigned int lastAppliedCt_; unsigned int lastAppliedQuantizedCt_; + + ipa::Interpolator sets_; + + std::unique_ptr algo_; +}; + +class LscImplementation +{ +public: + virtual ~LscImplementation() {} + + virtual int parseLscData(const ValueNode &sets) = 0; + + virtual LensShadingCorrection::ComponentsMap + sampleForCrop(const Rectangle &cropRectangle, + Span xSizes, + Span ySizes) = 0; }; } /* namespace ipa::rkisp1::algorithms */