From patchwork Thu Jul 16 22:25:10 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Milan Zamazal X-Patchwork-Id: 27371 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 7670FC3306 for ; Thu, 16 Jul 2026 22:25:43 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 0D6A267E33; Fri, 17 Jul 2026 00:25:43 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=redhat.com header.i=@redhat.com header.b="gcb1SJtP"; dkim-atps=neutral Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 3523F67E2B for ; Fri, 17 Jul 2026 00:25:42 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1784240741; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=ore5MPZZ7iH/6SDbc5PyDfdC9drP5MRoACXAN9sK+xc=; b=gcb1SJtPjfo0aBKJU3aK8vFfiLvXW5/z1kn74SfU7Kv5jr7xpm4VNfeWRr1bbqk69KRN+Y dzx2xQKLv+QtH5dsW9unFs2uTyQqGvpD4Vwui2dKWWU14gEf+QOLSPkYz9Vy5Y3PuGEn++ XbKhrVoWp8mVwjhylTvOzi89vbDiprI= Received: from mx-prod-mc-01.mail-002.prod.us-west-2.aws.redhat.com (ec2-54-186-198-63.us-west-2.compute.amazonaws.com [54.186.198.63]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-596-Cmefd7iONmCiuIx4k_ZWxA-1; Thu, 16 Jul 2026 18:25:37 -0400 X-MC-Unique: Cmefd7iONmCiuIx4k_ZWxA-1 X-Mimecast-MFC-AGG-ID: Cmefd7iONmCiuIx4k_ZWxA_1784240736 Received: from mx-prod-int-08.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-08.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.111]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mx-prod-mc-01.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id F1C53195606A; Thu, 16 Jul 2026 22:25:35 +0000 (UTC) Received: from mzamazal-thinkpadp1gen7.tpbc.com (unknown [10.44.32.11]) by mx-prod-int-08.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id A79771800361; Thu, 16 Jul 2026 22:25:32 +0000 (UTC) From: Milan Zamazal To: libcamera-devel@lists.libcamera.org Cc: Xander Pronk , Bryan O'Donoghue , Hans de Goede , Laurent Pinchart , Rick ten Wolde , Milan Zamazal Subject: [RFC PATCH v8 3/6] ipa: simple: Add LSC algorithm Date: Fri, 17 Jul 2026 00:25:10 +0200 Message-ID: <20260716222515.251464-4-mzamazal@redhat.com> In-Reply-To: <20260716222515.251464-1-mzamazal@redhat.com> References: <20260716222515.251464-1-mzamazal@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.4.1 on 10.30.177.111 X-Mimecast-Spam-Score: 0 X-Mimecast-MFC-PROC-ID: oGi6GZDM6pH5UT5k-55HN2TbtyOJc63N-r3XM5Qla6o_1784240736 X-Mimecast-Originator: redhat.com content-type: text/plain; charset="US-ASCII"; x-default=true 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" From: Xander Pronk The algorithm is based on the common libipa lens shading correction implementation. The grid values obtained from the libipa algorithm are passed to the debayer algorithm as an array and used as an an RGB texture. Notes on the implementation: - The overall idea is to keep things simple, to not make the LSC computation unnecessarily expensive. - 16 equally spaced grid positions are used for LscAlgorithm; for no particular reason other than that we already use the same number for the whole grid sizes. - LscAlgorithm accepts only quantised types. UQ<2,6> is used, to be converted to float for debayering (i.e. divided by 64.0). - The limit of 100 degrees to consider a temperature change noticeable is arbitrary. Co-developed-by: Rick ten Wolde Signed-off-by: Rick ten Wolde Signed-off-by: Xander Pronk Signed-off-by: Milan Zamazal --- src/ipa/simple/algorithms/lsc.cpp | 91 +++++++++++++++++++++++++++ src/ipa/simple/algorithms/lsc.h | 50 +++++++++++++++ src/ipa/simple/algorithms/meson.build | 1 + src/ipa/simple/ipa_context.h | 5 ++ 4 files changed, 147 insertions(+) create mode 100644 src/ipa/simple/algorithms/lsc.cpp create mode 100644 src/ipa/simple/algorithms/lsc.h diff --git a/src/ipa/simple/algorithms/lsc.cpp b/src/ipa/simple/algorithms/lsc.cpp new file mode 100644 index 000000000..f38eee4db --- /dev/null +++ b/src/ipa/simple/algorithms/lsc.cpp @@ -0,0 +1,91 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Lens shading correction + */ + +#include "lsc.h" + +#include + +namespace libcamera { + +namespace ipa::soft::algorithms { + +LOG_DEFINE_CATEGORY(IPASoftLsc) + +int Lsc::init(IPAContext &context, const ValueNode &tuningData) +{ + static constexpr unsigned int kGridSize = DebayerParams::kLscGridSize; + + for (unsigned int i = 0; i < kGridSize; i++) + gridPos_.push_back(static_cast(i) / (kGridSize - 1)); + + return lscAlgo_.init(tuningData, context.ctrlMap, + { .keys = { "r", "g", "b" }, + .numHCells = kGridSize, + .numVCells = kGridSize, + .sensorSize = context.sensorInfo.activeAreaSize }); +} + +int Lsc::configure(IPAContext &context, + [[maybe_unused]] const IPAConfigInfo &configInfo) +{ + return lscAlgo_.configure(context.activeState.lsc, + context.sensorInfo.analogCrop, + gridPos_, gridPos_); +} + +void Lsc::prepare([[maybe_unused]] IPAContext &context, + [[maybe_unused]] const uint32_t frame, + IPAFrameContext &frameContext, + DebayerParams *params) +{ + params->lscEnabled = frameContext.lsc.enabled; + + unsigned int ct = frameContext.awb.colourTemperature; + constexpr unsigned int minTemperatureChange = 100; + + if (!frameContext.lsc.enabled || + utils::abs_diff(ct, lastAppliedCt_) < minTemperatureChange) + return; + + const lsc::Components &set = lscAlgo_.interpolateComponents(ct); + + const auto &red = set.at("r"); + const auto &green = set.at("g"); + const auto &blue = set.at("b"); + + DebayerParams::LscLookupTable lut; + constexpr unsigned int gridSize = DebayerParams::kLscGridSize; + for (unsigned int i = 0, j = 0; i < gridSize * gridSize; i++) { + /* Convert UQ<2, 6> to float 0.0..4.0 */ + lut[j++] = red[i] / 64.0; + lut[j++] = green[i] / 64.0; + lut[j++] = blue[i] / 64.0; + } + params->lscLut = lut; + + lastAppliedCt_ = ct; +} + +void Lsc::queueRequest(IPAContext &context, [[maybe_unused]] const uint32_t frame, + IPAFrameContext &frameContext, const ControlList &controls) +{ + lscAlgo_.queueRequest(context.activeState.lsc, frameContext.lsc, + controls); +} + +void Lsc::process([[maybe_unused]] IPAContext &context, + [[maybe_unused]] const uint32_t frame, + IPAFrameContext &frameContext, + [[maybe_unused]] const SwIspStats *stats, + ControlList &metadata) +{ + lscAlgo_.process(frameContext.lsc, metadata); +} + +REGISTER_IPA_ALGORITHM(Lsc, "Lsc") + +} /* namespace ipa::soft::algorithms */ + +} /* namespace libcamera */ diff --git a/src/ipa/simple/algorithms/lsc.h b/src/ipa/simple/algorithms/lsc.h new file mode 100644 index 000000000..9418fac39 --- /dev/null +++ b/src/ipa/simple/algorithms/lsc.h @@ -0,0 +1,50 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Lens shading correction + */ + +#pragma once + +#include + +#include "libipa/fixedpoint.h" +#include "libipa/lsc.h" + +#include "algorithm.h" +#include "ipa_context.h" + +namespace libcamera { + +namespace ipa::soft::algorithms { + +class Lsc : public Algorithm +{ +public: + Lsc() = default; + ~Lsc() = default; + + int init(IPAContext &context, const ValueNode &tuningData) override; + int configure(IPAContext &context, + const IPAConfigInfo &configInfo) override; + void queueRequest(IPAContext &context, [[maybe_unused]] const uint32_t frame, + IPAFrameContext &frameContext, const ControlList &controls) override; + void prepare(IPAContext &context, + const uint32_t frame, + IPAFrameContext &frameContext, + DebayerParams *params) override; + void process([[maybe_unused]] IPAContext &context, + [[maybe_unused]] const uint32_t frame, + IPAFrameContext &frameContext, + [[maybe_unused]] const SwIspStats *stats, + ControlList &metadata) override; + +private: + LscAlgorithm> lscAlgo_; + std::vector gridPos_; + + unsigned int lastAppliedCt_ = 0; +}; + +} /* namespace ipa::soft::algorithms */ + +} /* namespace libcamera */ diff --git a/src/ipa/simple/algorithms/meson.build b/src/ipa/simple/algorithms/meson.build index 73c637220..c9f6e5590 100644 --- a/src/ipa/simple/algorithms/meson.build +++ b/src/ipa/simple/algorithms/meson.build @@ -6,4 +6,5 @@ soft_simple_ipa_algorithms = files([ 'agc.cpp', 'blc.cpp', 'ccm.cpp', + 'lsc.cpp', ]) diff --git a/src/ipa/simple/ipa_context.h b/src/ipa/simple/ipa_context.h index ff312ae8f..23c2cfd0a 100644 --- a/src/ipa/simple/ipa_context.h +++ b/src/ipa/simple/ipa_context.h @@ -19,6 +19,7 @@ #include #include #include +#include "libipa/lsc.h" #include "core_ipa_interface.h" @@ -61,6 +62,8 @@ struct IPAActiveState { std::optional contrast; std::optional saturation; } knobs; + + ipa::lsc::ActiveState lsc; }; struct IPAFrameContext : public FrameContext { @@ -75,6 +78,7 @@ struct IPAFrameContext : public FrameContext { float gamma; std::optional contrast; std::optional saturation; + ipa::lsc::FrameContext lsc; }; struct IPAContext { @@ -89,6 +93,7 @@ struct IPAContext { FCQueue frameContexts; ControlInfoMap::Map ctrlMap; bool ccmEnabled = false; + ipa::lsc::ActiveState lsc; }; } /* namespace ipa::soft */