Patch Detail
Show a patch.
GET /api/patches/26896/?format=api
{ "id": 26896, "url": "https://patchwork.libcamera.org/api/patches/26896/?format=api", "web_url": "https://patchwork.libcamera.org/patch/26896/", "project": { "id": 1, "url": "https://patchwork.libcamera.org/api/projects/1/?format=api", "name": "libcamera", "link_name": "libcamera", "list_id": "libcamera_core", "list_email": "libcamera-devel@lists.libcamera.org", "web_url": "", "scm_url": "", "webscm_url": "" }, "msgid": "<20260616-ipu3-libipa-rework-v1-5-d4448b54f1d8@ideasonboard.com>", "date": "2026-06-16T06:41:39", "name": "[05/10] ipa: libipa: Add GammaAlgorithm class", "commit_ref": null, "pull_url": null, "state": "superseded", "archived": false, "hash": "0bbceb6276fd44c43cb4f03f298b9b8ac6a729e7", "submitter": { "id": 156, "url": "https://patchwork.libcamera.org/api/people/156/?format=api", "name": "Dan Scally", "email": "dan.scally@ideasonboard.com" }, "delegate": null, "mbox": "https://patchwork.libcamera.org/patch/26896/mbox/", "series": [ { "id": 5995, "url": "https://patchwork.libcamera.org/api/series/5995/?format=api", "web_url": "https://patchwork.libcamera.org/project/libcamera/list/?series=5995", "date": "2026-06-16T06:41:34", "name": "libipa: Re-work IPU3 IPA to use libipa algorithms", "version": 1, "mbox": "https://patchwork.libcamera.org/series/5995/mbox/" } ], "comments": "https://patchwork.libcamera.org/api/patches/26896/comments/", "check": "pending", "checks": "https://patchwork.libcamera.org/api/patches/26896/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 0A84FC3301\n\tfor <parsemail@patchwork.libcamera.org>;\n\tTue, 16 Jun 2026 06:42:03 +0000 (UTC)", "from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 2D731625A1;\n\tTue, 16 Jun 2026 08:41:56 +0200 (CEST)", "from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[213.167.242.64])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id D9A6C62401\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tTue, 16 Jun 2026 08:41:49 +0200 (CEST)", "from [127.0.1.1]\n\t(chfd-03-b2-v4wan-176392-cust229.vm15.cable.virginm.net\n\t[82.19.20.230])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id 977A4217B;\n\tTue, 16 Jun 2026 08:41:16 +0200 (CEST)" ], "Authentication-Results": "lancelot.ideasonboard.com; dkim=pass (1024-bit key;\n\tunprotected) header.d=ideasonboard.com header.i=@ideasonboard.com\n\theader.b=\"BlM8gDMn\"; dkim-atps=neutral", "DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1781592076;\n\tbh=Y0ZLtyoOLcv4vmrV9hPoS/xsNd9dtxzJlb5kWBqmzBo=;\n\th=From:Date:Subject:References:In-Reply-To:To:Cc:From;\n\tb=BlM8gDMnjXThDiChj+dICzYm2GJCpFJYeVsKSN/6i4hGGmfOeaW9ahs9PBiGfgTan\n\t6j+0U7fvNBkLDfkPJerwjdr0jVK38SKNTL1xLXle+voj8cN+AraeHPuR4ibypudFTx\n\tmbpOswPRutK/OqDArE0TCXT/FGXignkn7jXaP/EM=", "From": "Daniel Scally <dan.scally@ideasonboard.com>", "Date": "Tue, 16 Jun 2026 07:41:39 +0100", "Subject": "[PATCH 05/10] ipa: libipa: Add GammaAlgorithm class", "MIME-Version": "1.0", "Content-Type": "text/plain; charset=\"utf-8\"", "Content-Transfer-Encoding": "7bit", "Message-Id": "<20260616-ipu3-libipa-rework-v1-5-d4448b54f1d8@ideasonboard.com>", "References": "<20260616-ipu3-libipa-rework-v1-0-d4448b54f1d8@ideasonboard.com>", "In-Reply-To": "<20260616-ipu3-libipa-rework-v1-0-d4448b54f1d8@ideasonboard.com>", "To": "libcamera-devel@lists.libcamera.org", "Cc": "Daniel Scally <dan.scally@ideasonboard.com>", "X-Mailer": "b4 0.14.2", "X-Developer-Signature": "v=1; a=openpgp-sha256; l=11753;\n\ti=dan.scally@ideasonboard.com; h=from:subject:message-id;\n\tbh=Y0ZLtyoOLcv4vmrV9hPoS/xsNd9dtxzJlb5kWBqmzBo=;\n\tb=owEBbQKS/ZANAwAKAchJV3psRXUyAcsmYgBqMPApaOrgoJUVlQLIGjxdg/LCTIODPR0RA9lad\n\tgfwS/rZxL6JAjMEAAEKAB0WIQQqyuwyDnZdb+mxmm/ISVd6bEV1MgUCajDwKQAKCRDISVd6bEV1\n\tMqiBD/0Su+//Jr0g2xbxxNUQCshEUQG/PK6953VhX7xg/E15uDgvc3RjEAOKWk8cyW4npzeq9up\n\t6Qput34hu0ZWlGAU850r7CEXos/wr8aOiOr51KNXEdRyBsT+NcEQ/R/2koLrv5qS3Bp7B3MZBIz\n\tfgyRk2TG07VNlrAiZb10jQwUNkHxwLHLNy4QoUSky551mF7BV1AjMm9CaYnaFrlFakbGE0JfGDE\n\tNtaGN370sgfi/x8JaTPsPMeIX/o8MeQjXN7JKSLdyhpKDIIbu++OMjzdJEY2hxz0fBa9lbcP+Io\n\tECVziX0OOOUDrmAdHxJD0rb9vd5sa0YNdT59BR0RqJG7d5gUdYI6mD2ez0fuLkHaMfVSr8c9Y6T\n\tn+IyTFaRpxl0762E92AwEr9kK65KIeoME5zuEkK1OJDBnwXaFaig7JkWcEwmREubdafwIcv3iB5\n\tP8as6PUIrEf/nGQp3jKAHWOb3AkoNkaYu3sdC3XPpWNCB7cUF+3G1JGDWmMOIShRxPTsLpb8iFG\n\tEC/g0mhip6rFHmqQFGKAFcZo6xUGy5z10VyZSoRXRAdItKTtI46bZDFzFTsWGsAAHbckJElZEmO\n\tLWS7v8ec3PpeVTFcRxUde+LJyarWI+iBKLAb8EDe1uJa58oDrr04Ir3S+s/KCZoRpHq/7u7NnWO\n\tV+s2DaWRgnbnAdA==", "X-Developer-Key": "i=dan.scally@ideasonboard.com; a=openpgp;\n\tfpr=EEC699ACA1B7CB5D31330C0BBD501C2A3546CCF6", "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": "Add a base GammaAlgorithm class that can be used by IPA specific\ngamma algorithms to reduce the amount of work that they need to\nimplement.\n\nSigned-off-by: Daniel Scally <dan.scally@ideasonboard.com>\n---\n src/ipa/libipa/gamma.cpp | 248 +++++++++++++++++++++++++++++++++++++++++++++\n src/ipa/libipa/gamma.h | 93 +++++++++++++++++\n src/ipa/libipa/meson.build | 2 +\n 3 files changed, 343 insertions(+)", "diff": "diff --git a/src/ipa/libipa/gamma.cpp b/src/ipa/libipa/gamma.cpp\nnew file mode 100644\nindex 0000000000000000000000000000000000000000..f84f4cf7049f695f6a4ea21e0bcf851aef0f2c70\n--- /dev/null\n+++ b/src/ipa/libipa/gamma.cpp\n@@ -0,0 +1,248 @@\n+/* SPDX-License-Identifier: LGPL-2.1-or-later */\n+/*\n+ * Copyright (C) 2026 Ideas on Board Oy\n+ *\n+ * libIPA Gamma correction algorithm\n+ */\n+\n+#include \"gamma.h\"\n+\n+#include <numeric>\n+\n+#include <libcamera/controls.h>\n+\n+#include \"libcamera/internal/value_node.h\"\n+\n+/**\n+ * \\file gamma.h\n+ * \\brief libipa implementation of a gamma curve correction algorithm\n+ */\n+\n+namespace libcamera {\n+\n+namespace ipa {\n+\n+LOG_DEFINE_CATEGORY(Gamma)\n+\n+namespace gamma {\n+\n+/**\n+ * \\struct ActiveState\n+ * \\brief Active gamma correction algorithm state\n+ *\n+ * \\var ActiveState::gamma\n+ * \\brief The gamma correction value applied as 1.0/gamma\n+ */\n+\n+/**\n+ * \\struct FrameContext\n+ * \\brief Per-frame gamma correction settings\n+ *\n+ * \\var FrameContext::gamma\n+ * \\brief The gamma correction value applied for this frame\n+ *\n+ * \\var FrameContext::update\n+ * \\brief A flag instructing the algorithm to push an update to the hardware\n+ */\n+\n+} /* namespace gamma */\n+\n+/**\n+ * \\brief The default gamma correction value\n+ */\n+const float kDefaultGamma = 2.2f;\n+\n+/**\n+ * \\class GammaAlgorithmBase\n+ * \\brief Base class for GammaAlgorithm to implement non-templated functions\n+ *\n+ * This base class for GammeaAlgorithm allows us to implement non templated\n+ * functions. IPA specific implementations shall derive from GammaAlgorithm and\n+ * not this class.\n+ */\n+\n+/**\n+ * \\fn GammaAlgorithmBase::GammaAlgorithmBase\n+ * \\brief Construct an instance of the class\n+ * \\param[in] nLutNodes Set the number of function knee-points expected by the\n+ * IPA algorithm\n+ */\n+\n+/**\n+ * \\brief Initialise the algorithm with the given tuning data\n+ * \\param[out] controls The ControlList into which this algorithm's supported\n+ * controls will be emplaced.\n+ * \\param[in] tuningData The tuning data to use with the algorithm\n+ * \\param[in] segments\tA vector of segment spacings to define a custom\n+ * X coordinate system for the curve\n+ *\n+ * Parse \\a tuningData and \\a segments to initialize the gamma correction curve.\n+ * The tuning data may contain a default gamma value to use; otherwise the value\n+ * of \\a kDefaultGamma will be taken as the default. \\a segments may provide a\n+ * view into an array of segment spacings, which can be used to vary the X\n+ * co-ordinate of the gamma correction curve. For example, if the piecewise\n+ * linear function of the correction curve is expected to have 16 knee-points, a\n+ * \\a segments array like so:\n+ *\n+ * [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]\n+ *\n+ * would result in evenly spaced knee-points along the X-axis. Hardware may\n+ * expect the knee-points to be spaced more densely towards the start of the\n+ * curve and more sparsely towards the end, in which case an alternative array\n+ * might be:\n+ *\n+ * [1, 1, 1, 1, 2, 2, 2, 2, 4, 4, 4, 8, 8, 8, 8, 8]\n+ *\n+ * As the values in \\a segments represent the distance between two knee-points\n+ * relative to the total distance between the first and last point, the length\n+ * of \\a segments should be equal to the number of knee-points minus one. As a\n+ * convenience, a hardware-specific algorithm deriving from this class may omit\n+ * \\a segments, in which case an evenly-spaced default will be constructed.\n+ *\n+ * IPA modules are expected to call this function as part of their\n+ * implementation of Algorithm::init()\n+ *\n+ * @return 0 on success, a negative error code otherwise\n+ */\n+int GammaAlgorithmBase::init(ControlInfoMap::Map &controls, const ValueNode &tuningData,\n+\t\t\t Span<unsigned int> segments)\n+{\n+\t/*\n+\t * If the caller doesn't pass in a segment list we simply construct one\n+\t * with equally spaced segments. We need one less segment than we have\n+\t * LUT nodes.\n+\t */\n+\tunsigned int expectedNSegments = nLutNodes_ - 1;\n+\n+\tif (segments.empty())\n+\t\tfor (unsigned int i = 0; i < expectedNSegments; i++)\n+\t\t\tsegments_.push_back(1);\n+\telse\n+\t\tsegments_.assign(segments.begin(), segments.end());\n+\n+\tif (segments_.size() != expectedNSegments)\n+\t\treturn -EINVAL;\n+\n+\tsegmentsSum_ = std::accumulate(segments_.begin(), segments_.end(), 0.0f);\n+\n+\tdefaultGamma_ = tuningData[\"gamma\"].get<double>(kDefaultGamma);\n+\tcontrols[&controls::Gamma] = ControlInfo(0.1f, 10.0f, defaultGamma_);\n+\n+\treturn 0;\n+}\n+\n+/**\n+ * \\brief Configure the gamma correction algorithm\n+ * \\param[out] state The gamma correction algorithm's active state\n+ *\n+ * Reset to the default gamma correction value.\n+ *\n+ * IPA modules are expected to call this function as part of their\n+ * implementation of Algorithm::configure()\n+ */\n+void GammaAlgorithmBase::configure(gamma::ActiveState &state)\n+{\n+\tstate.gamma = defaultGamma_;\n+}\n+\n+/**\n+ * \\brief Queue a request to the gamma correction algorithm\n+ * \\param[in] state The algorithm's active state\n+ * \\param[in] frame The current frame number\n+ * \\param[in] context The algorithm's frame context\n+ * \\param[in] controls The ControlList that was queued with the request\n+ *\n+ * Queue a new request to the gamma correction algorithm and handle any relevant\n+ * controls that were queued. The only control currently handled is:\n+ *\n+ * - controls::Gamma\n+ *\n+ * If a control with that ID is queued the value is stored in \\a state and\n+ * \\a context.\n+ *\n+ * IPA modules are expected to call this function as part of their\n+ * implementation of Algorithm::queueRequest()\n+ */\n+void GammaAlgorithmBase::queueRequest(gamma::ActiveState &state,\n+\t\t\t\t const uint32_t frame,\n+\t\t\t\t gamma::FrameContext &context,\n+\t\t\t\t const ControlList &controls)\n+{\n+\tif (frame == 0)\n+\t\tcontext.update = true;\n+\n+\tconst auto &gamma = controls.get(controls::Gamma);\n+\tif (gamma) {\n+\t\tstate.gamma = *gamma;\n+\t\tcontext.update = true;\n+\t\tLOG(Gamma, Info) << \"Set gamma to \" << *gamma;\n+\t}\n+\n+\tcontext.gamma = state.gamma;\n+}\n+\n+/**\n+ * \\brief Populate metadata with the gamma correction values for a frame\n+ * \\param[in] context The frame context\n+ * \\param[out] metadata The ControlList of metadata for a frame\n+ *\n+ * Report the gamma value used to calculate the correction curve that was\n+ * applied to a frame.\n+ */\n+void GammaAlgorithmBase::process(gamma::FrameContext &context, ControlList &metadata)\n+{\n+\tmetadata.set(controls::Gamma, context.gamma);\n+}\n+\n+/**\n+ * \\var GammaAlgorithmBase::nLutNodes_\n+ * \\brief The number of knee-points in the gamma correction curve\n+ */\n+\n+/**\n+ * \\var GammaAlgorithmBase::defaultGamma_\n+ * \\brief The default gamma parameter used at stream start\n+ */\n+\n+/**\n+ * \\var GammaAlgorithmBase::segments_\n+ * \\brief The vector of segment sizes describing the space between knee-points\n+ */\n+\n+/**\n+ * \\var GammaAlgorithmBase::segmentsSum_\n+ * \\brief The sum of \\a GammaAlgorithmBase::segments_\n+ */\n+\n+/**\n+ * \\class GammaAlgorithm\n+ * \\brief The libipa gamma correction algorithm\n+ * \\tparam nLutNodes The number of knee-points in the algorithm's function\n+ * \\tparam UQ The fixedpoint representation of the function's values\n+ *\n+ * Gamma correction adjusts for the differences in the way light is perceived\n+ * by a camera and the human eye by applying a function to the input values.\n+ * The GammaAlgorithm class facilitates this by building a piecewise linear\n+ * function from a gamma parameter and supplying it in the hardware-specific\n+ * formats defined by the IPA algorithms.\n+ *\n+ * IPA modules are expected to store an instance of GammaAlgorithm as a class\n+ * member, templated with the format and number of knee-points in the PWL\n+ * expected by their hardware and then call its functions in their overload of\n+ * the Algorithm class's function.\n+ *\n+ * When an application queues a new value for the gamma parameter with a\n+ * Request, the GammaAlgorithm will recalculate and populate the new LUT to be\n+ * sent to the ISP.\n+ */\n+\n+/**\n+ * \\fn GammaAlgorithm::prepare()\n+ * \\tparam T The type of data expected by the hardware's look-up table\n+ * \\param[in] context The frame context\n+ * \\param[out] lut The Span into which to place the calculated look-up table\n+ */\n+\n+} /* namespace ipa */\n+\n+} /* namespace libcamera */\ndiff --git a/src/ipa/libipa/gamma.h b/src/ipa/libipa/gamma.h\nnew file mode 100644\nindex 0000000000000000000000000000000000000000..2b449d9ec41bc96f576e664afca33acb8267a8e6\n--- /dev/null\n+++ b/src/ipa/libipa/gamma.h\n@@ -0,0 +1,93 @@\n+/* SPDX-License-Identifier: LGPL-2.1-or-later */\n+/*\n+ * Copyright (C) 2026 Ideas on Board Oy\n+ *\n+ * libIPA Gamma correction algorithm\n+ */\n+\n+#pragma once\n+\n+#include <cmath>\n+#include <vector>\n+\n+#include <libcamera/base/log.h>\n+#include <libcamera/base/span.h>\n+\n+#include <libcamera/control_ids.h>\n+\n+#include \"libcamera/internal/value_node.h\"\n+\n+#include \"fixedpoint.h\"\n+\n+namespace libcamera {\n+\n+namespace ipa {\n+\n+LOG_DECLARE_CATEGORY(Gamma)\n+\n+namespace gamma {\n+\n+struct ActiveState {\n+\tdouble gamma;\n+};\n+\n+struct FrameContext {\n+\tdouble gamma;\n+\tbool update;\n+};\n+\n+} /* namespace gamma */\n+\n+class GammaAlgorithmBase\n+{\n+public:\n+\tGammaAlgorithmBase(unsigned int nLutNodes)\n+\t\t: nLutNodes_(nLutNodes)\n+\t{\n+\t}\n+\n+\tint init(ControlInfoMap::Map &controls, const ValueNode &tuningData,\n+\t\t Span<unsigned int> segments = {});\n+\n+\tvoid configure(gamma::ActiveState &state);\n+\tvoid queueRequest(gamma::ActiveState &state, const uint32_t frame,\n+\t\t\t gamma::FrameContext &context, const ControlList &controls);\n+\tvoid process(gamma::FrameContext &context, ControlList &metadata);\n+\n+protected:\n+\tunsigned int nLutNodes_;\n+\tfloat defaultGamma_;\n+\tstd::vector<unsigned int> segments_;\n+\tunsigned int segmentsSum_;\n+};\n+\n+template<unsigned int nLutNodes, typename UQ>\n+class GammaAlgorithm : public GammaAlgorithmBase\n+{\n+public:\n+\tGammaAlgorithm()\n+\t\t: GammaAlgorithmBase(nLutNodes)\n+\t{\n+\t}\n+\n+\ttemplate<typename T>\n+\tvoid prepare(gamma::FrameContext &context, Span<T> lut)\n+\t{\n+\t\tfloat x = 0;\n+\n+\t\tfor (unsigned int i = 0; i < nLutNodes_; i++) {\n+\t\t\tfloat gamma = std::pow(x / segmentsSum_,\n+\t\t\t\t\t 1.0 / context.gamma);\n+\t\t\tlut[i] = UQ(gamma).quantized();\n+\n+\t\t\tLOG(Gamma, Debug) << \"LUT[\" << i << \"]=\" << gamma << \"(\" << lut[i] << \")\";\n+\n+\t\t\tif (i < segments_.size())\n+\t\t\t\tx += segments_[i];\n+\t\t}\n+\t}\n+};\n+\n+} /* namespace ipa */\n+\n+} /* namespace libcamera */\ndiff --git a/src/ipa/libipa/meson.build b/src/ipa/libipa/meson.build\nindex da6ea0c5e13000f78b2196c7334610c350f1ad13..565da9be9059f2167d107061d643e58202e655ef 100644\n--- a/src/ipa/libipa/meson.build\n+++ b/src/ipa/libipa/meson.build\n@@ -12,6 +12,7 @@ libipa_headers = files([\n 'exposure_mode_helper.h',\n 'fc_queue.h',\n 'fixedpoint.h',\n+ 'gamma.h',\n 'histogram.h',\n 'interpolator.h',\n 'lsc.h',\n@@ -37,6 +38,7 @@ libipa_sources = files([\n 'exposure_mode_helper.cpp',\n 'fc_queue.cpp',\n 'fixedpoint.cpp',\n+ 'gamma.cpp',\n 'histogram.cpp',\n 'interpolator.cpp',\n 'lsc.cpp',\n", "prefixes": [ "05/10" ] }