{"id":27382,"url":"https://patchwork.libcamera.org/api/patches/27382/?format=json","web_url":"https://patchwork.libcamera.org/patch/27382/","project":{"id":1,"url":"https://patchwork.libcamera.org/api/projects/1/?format=json","name":"libcamera","link_name":"libcamera","list_id":"libcamera_core","list_email":"libcamera-devel@lists.libcamera.org","web_url":"","scm_url":"","webscm_url":""},"msgid":"<20260720095951.49979-4-mzamazal@redhat.com>","date":"2026-07-20T09:59:47","name":"[RFC,v9,3/6] ipa: simple: Add LSC algorithm","commit_ref":null,"pull_url":null,"state":"new","archived":false,"hash":"341b4a013f060da6ffd88e16df0e968be0de9492","submitter":{"id":177,"url":"https://patchwork.libcamera.org/api/people/177/?format=json","name":"Milan Zamazal","email":"mzamazal@redhat.com"},"delegate":null,"mbox":"https://patchwork.libcamera.org/patch/27382/mbox/","series":[{"id":6062,"url":"https://patchwork.libcamera.org/api/series/6062/?format=json","web_url":"https://patchwork.libcamera.org/project/libcamera/list/?series=6062","date":"2026-07-20T09:59:44","name":"LSC for SoftISP simple pipeline","version":9,"mbox":"https://patchwork.libcamera.org/series/6062/mbox/"}],"comments":"https://patchwork.libcamera.org/api/patches/27382/comments/","check":"pending","checks":"https://patchwork.libcamera.org/api/patches/27382/checks/","tags":{},"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 35803BF415\n\tfor <parsemail@patchwork.libcamera.org>;\n\tMon, 20 Jul 2026 10:00:21 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 0338967E37;\n\tMon, 20 Jul 2026 12:00:21 +0200 (CEST)","from us-smtp-delivery-124.mimecast.com\n\t(us-smtp-delivery-124.mimecast.com [170.10.129.124])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id C79EB66149\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tMon, 20 Jul 2026 12:00:17 +0200 (CEST)","from mx-prod-mc-06.mail-002.prod.us-west-2.aws.redhat.com\n\t(ec2-35-165-154-97.us-west-2.compute.amazonaws.com [35.165.154.97])\n\tby relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3,\n\tcipher=TLS_AES_256_GCM_SHA384) id us-mta-649-_w8siRrCNDWftJC_oOYYrw-1;\n\tMon, 20 Jul 2026 06:00:11 -0400","from mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com\n\t(mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.4])\n\t(using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)\n\tkey-exchange X25519 server-signature RSA-PSS (2048 bits)\n\tserver-digest SHA256) (No client certificate requested)\n\tby mx-prod-mc-06.mail-002.prod.us-west-2.aws.redhat.com (Postfix)\n\twith ESMTPS id 82C041802543; Mon, 20 Jul 2026 10:00:09 +0000 (UTC)","from mzamazal-thinkpadp1gen7.tpbc.com (unknown [10.44.33.73])\n\tby mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com (Postfix)\n\twith ESMTP id A2662300418D; Mon, 20 Jul 2026 10:00:06 +0000 (UTC)"],"Authentication-Results":"lancelot.ideasonboard.com; dkim=pass (1024-bit key;\n\tunprotected) header.d=redhat.com header.i=@redhat.com\n\theader.b=\"HaoeBMx0\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com;\n\ts=mimecast20190719; t=1784541616;\n\th=from:from:reply-to:subject:subject:date:date:message-id:message-id:\n\tto:to:cc:cc:mime-version:mime-version:content-type:content-type:\n\tcontent-transfer-encoding:content-transfer-encoding:\n\tin-reply-to:in-reply-to:references:references;\n\tbh=ore5MPZZ7iH/6SDbc5PyDfdC9drP5MRoACXAN9sK+xc=;\n\tb=HaoeBMx0p3C99oEHUNOCicZ4pcuyRpEZdhuSR0OW57eBDtrKpjyjw2VE+PO6TTLz8Q0UgY\n\tAEJRy6V7+MEEwuP5MJOW8BAWZUD4qJTAbB1XOewYFsDu2RKJF9ghZn8rTmVzoRr8x4uYfB\n\t7hBJQThtGaKeVr04qflcRX0cgRj1ypg=","X-MC-Unique":"_w8siRrCNDWftJC_oOYYrw-1","X-Mimecast-MFC-AGG-ID":"_w8siRrCNDWftJC_oOYYrw_1784541609","From":"Milan Zamazal <mzamazal@redhat.com>","To":"libcamera-devel@lists.libcamera.org","Cc":"Xander Pronk <xander.c.pronk@gmail.com>,\n\tBryan O'Donoghue <bod.linux@nxsw.ie>,\n\tHans de Goede <johannes.goede@oss.qualcomm.com>,\n\tLaurent Pinchart <laurent.pinchart@ideasonboard.com>,\n\tRick ten Wolde <rick_libcamera@wolde.info>,\n\tMilan Zamazal <mzamazal@redhat.com>","Subject":"[RFC PATCH v9 3/6] ipa: simple: Add LSC algorithm","Date":"Mon, 20 Jul 2026 11:59:47 +0200","Message-ID":"<20260720095951.49979-4-mzamazal@redhat.com>","In-Reply-To":"<20260720095951.49979-1-mzamazal@redhat.com>","References":"<20260720095951.49979-1-mzamazal@redhat.com>","MIME-Version":"1.0","X-Scanned-By":"MIMEDefang 3.4.1 on 10.30.177.4","X-Mimecast-Spam-Score":"0","X-Mimecast-MFC-PROC-ID":"TwPCFyVZC18UJGOoprsUIHS4ALFp3NUkpzk-gebxqzo_1784541609","X-Mimecast-Originator":"redhat.com","Content-Transfer-Encoding":"8bit","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":"<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>"},"content":"From: Xander Pronk <xander.c.pronk@gmail.com>\n\nThe algorithm is based on the common libipa lens shading correction\nimplementation.  The grid values obtained from the libipa algorithm are\npassed to the debayer algorithm as an array and used as an an RGB\ntexture.\n\nNotes on the implementation:\n\n- The overall idea is to keep things simple, to not make the LSC\n  computation unnecessarily expensive.\n\n- 16 equally spaced grid positions are used for LscAlgorithm; for no\n  particular reason other than that we already use the same number for\n  the whole grid sizes.\n\n- LscAlgorithm accepts only quantised types.  UQ<2,6> is used, to be\n  converted to float for debayering (i.e. divided by 64.0).\n\n- The limit of 100 degrees to consider a temperature change noticeable\n  is arbitrary.\n\nCo-developed-by: Rick ten Wolde <rick_libcamera@wolde.info>\nSigned-off-by: Rick ten Wolde <rick_libcamera@wolde.info>\nSigned-off-by: Xander Pronk <xander.c.pronk@gmail.com>\nSigned-off-by: Milan Zamazal <mzamazal@redhat.com>\n---\n src/ipa/simple/algorithms/lsc.cpp     | 91 +++++++++++++++++++++++++++\n src/ipa/simple/algorithms/lsc.h       | 50 +++++++++++++++\n src/ipa/simple/algorithms/meson.build |  1 +\n src/ipa/simple/ipa_context.h          |  5 ++\n 4 files changed, 147 insertions(+)\n create mode 100644 src/ipa/simple/algorithms/lsc.cpp\n create mode 100644 src/ipa/simple/algorithms/lsc.h","diff":"diff --git a/src/ipa/simple/algorithms/lsc.cpp b/src/ipa/simple/algorithms/lsc.cpp\nnew file mode 100644\nindex 000000000..f38eee4db\n--- /dev/null\n+++ b/src/ipa/simple/algorithms/lsc.cpp\n@@ -0,0 +1,91 @@\n+/* SPDX-License-Identifier: LGPL-2.1-or-later */\n+/*\n+ * Lens shading correction\n+ */\n+\n+#include \"lsc.h\"\n+\n+#include <libcamera/base/log.h>\n+\n+namespace libcamera {\n+\n+namespace ipa::soft::algorithms {\n+\n+LOG_DEFINE_CATEGORY(IPASoftLsc)\n+\n+int Lsc::init(IPAContext &context, const ValueNode &tuningData)\n+{\n+\tstatic constexpr unsigned int kGridSize = DebayerParams::kLscGridSize;\n+\n+\tfor (unsigned int i = 0; i < kGridSize; i++)\n+\t\tgridPos_.push_back(static_cast<double>(i) / (kGridSize - 1));\n+\n+\treturn lscAlgo_.init(tuningData, context.ctrlMap,\n+\t\t\t     { .keys = { \"r\", \"g\", \"b\" },\n+\t\t\t       .numHCells = kGridSize,\n+\t\t\t       .numVCells = kGridSize,\n+\t\t\t       .sensorSize = context.sensorInfo.activeAreaSize });\n+}\n+\n+int Lsc::configure(IPAContext &context,\n+\t\t   [[maybe_unused]] const IPAConfigInfo &configInfo)\n+{\n+\treturn lscAlgo_.configure(context.activeState.lsc,\n+\t\t\t\t  context.sensorInfo.analogCrop,\n+\t\t\t\t  gridPos_, gridPos_);\n+}\n+\n+void Lsc::prepare([[maybe_unused]] IPAContext &context,\n+\t\t  [[maybe_unused]] const uint32_t frame,\n+\t\t  IPAFrameContext &frameContext,\n+\t\t  DebayerParams *params)\n+{\n+\tparams->lscEnabled = frameContext.lsc.enabled;\n+\n+\tunsigned int ct = frameContext.awb.colourTemperature;\n+\tconstexpr unsigned int minTemperatureChange = 100;\n+\n+\tif (!frameContext.lsc.enabled ||\n+\t    utils::abs_diff(ct, lastAppliedCt_) < minTemperatureChange)\n+\t\treturn;\n+\n+\tconst lsc::Components<uint8_t> &set = lscAlgo_.interpolateComponents(ct);\n+\n+\tconst auto &red = set.at(\"r\");\n+\tconst auto &green = set.at(\"g\");\n+\tconst auto &blue = set.at(\"b\");\n+\n+\tDebayerParams::LscLookupTable lut;\n+\tconstexpr unsigned int gridSize = DebayerParams::kLscGridSize;\n+\tfor (unsigned int i = 0, j = 0; i < gridSize * gridSize; i++) {\n+\t\t/* Convert UQ<2, 6> to float 0.0..4.0 */\n+\t\tlut[j++] = red[i] / 64.0;\n+\t\tlut[j++] = green[i] / 64.0;\n+\t\tlut[j++] = blue[i] / 64.0;\n+\t}\n+\tparams->lscLut = lut;\n+\n+\tlastAppliedCt_ = ct;\n+}\n+\n+void Lsc::queueRequest(IPAContext &context, [[maybe_unused]] const uint32_t frame,\n+\t\t       IPAFrameContext &frameContext, const ControlList &controls)\n+{\n+\tlscAlgo_.queueRequest(context.activeState.lsc, frameContext.lsc,\n+\t\t\t      controls);\n+}\n+\n+void Lsc::process([[maybe_unused]] IPAContext &context,\n+\t\t  [[maybe_unused]] const uint32_t frame,\n+\t\t  IPAFrameContext &frameContext,\n+\t\t  [[maybe_unused]] const SwIspStats *stats,\n+\t\t  ControlList &metadata)\n+{\n+\tlscAlgo_.process(frameContext.lsc, metadata);\n+}\n+\n+REGISTER_IPA_ALGORITHM(Lsc, \"Lsc\")\n+\n+} /* namespace ipa::soft::algorithms */\n+\n+} /* namespace libcamera */\ndiff --git a/src/ipa/simple/algorithms/lsc.h b/src/ipa/simple/algorithms/lsc.h\nnew file mode 100644\nindex 000000000..9418fac39\n--- /dev/null\n+++ b/src/ipa/simple/algorithms/lsc.h\n@@ -0,0 +1,50 @@\n+/* SPDX-License-Identifier: LGPL-2.1-or-later */\n+/*\n+ * Lens shading correction\n+ */\n+\n+#pragma once\n+\n+#include <libipa/interpolator.h>\n+\n+#include \"libipa/fixedpoint.h\"\n+#include \"libipa/lsc.h\"\n+\n+#include \"algorithm.h\"\n+#include \"ipa_context.h\"\n+\n+namespace libcamera {\n+\n+namespace ipa::soft::algorithms {\n+\n+class Lsc : public Algorithm\n+{\n+public:\n+\tLsc() = default;\n+\t~Lsc() = default;\n+\n+\tint init(IPAContext &context, const ValueNode &tuningData) override;\n+\tint configure(IPAContext &context,\n+\t\t      const IPAConfigInfo &configInfo) override;\n+\tvoid queueRequest(IPAContext &context, [[maybe_unused]] const uint32_t frame,\n+\t\t\t  IPAFrameContext &frameContext, const ControlList &controls) override;\n+\tvoid prepare(IPAContext &context,\n+\t\t     const uint32_t frame,\n+\t\t     IPAFrameContext &frameContext,\n+\t\t     DebayerParams *params) override;\n+\tvoid process([[maybe_unused]] IPAContext &context,\n+\t\t     [[maybe_unused]] const uint32_t frame,\n+\t\t     IPAFrameContext &frameContext,\n+\t\t     [[maybe_unused]] const SwIspStats *stats,\n+\t\t     ControlList &metadata) override;\n+\n+private:\n+\tLscAlgorithm<UQ<2, 6>> lscAlgo_;\n+\tstd::vector<double> gridPos_;\n+\n+\tunsigned int lastAppliedCt_ = 0;\n+};\n+\n+} /* namespace ipa::soft::algorithms */\n+\n+} /* namespace libcamera */\ndiff --git a/src/ipa/simple/algorithms/meson.build b/src/ipa/simple/algorithms/meson.build\nindex 73c637220..c9f6e5590 100644\n--- a/src/ipa/simple/algorithms/meson.build\n+++ b/src/ipa/simple/algorithms/meson.build\n@@ -6,4 +6,5 @@ soft_simple_ipa_algorithms = files([\n     'agc.cpp',\n     'blc.cpp',\n     'ccm.cpp',\n+    'lsc.cpp',\n ])\ndiff --git a/src/ipa/simple/ipa_context.h b/src/ipa/simple/ipa_context.h\nindex ff312ae8f..23c2cfd0a 100644\n--- a/src/ipa/simple/ipa_context.h\n+++ b/src/ipa/simple/ipa_context.h\n@@ -19,6 +19,7 @@\n #include <libipa/awb.h>\n #include <libipa/ccm.h>\n #include <libipa/fc_queue.h>\n+#include \"libipa/lsc.h\"\n \n #include \"core_ipa_interface.h\"\n \n@@ -61,6 +62,8 @@ struct IPAActiveState {\n \t\tstd::optional<float> contrast;\n \t\tstd::optional<float> saturation;\n \t} knobs;\n+\n+\tipa::lsc::ActiveState lsc;\n };\n \n struct IPAFrameContext : public FrameContext {\n@@ -75,6 +78,7 @@ struct IPAFrameContext : public FrameContext {\n \tfloat gamma;\n \tstd::optional<float> contrast;\n \tstd::optional<float> saturation;\n+\tipa::lsc::FrameContext lsc;\n };\n \n struct IPAContext {\n@@ -89,6 +93,7 @@ struct IPAContext {\n \tFCQueue<IPAFrameContext> frameContexts;\n \tControlInfoMap::Map ctrlMap;\n \tbool ccmEnabled = false;\n+\tipa::lsc::ActiveState lsc;\n };\n \n } /* namespace ipa::soft */\n","prefixes":["RFC","v9","3/6"]}