Patch Detail
Show a patch.
GET /api/1.1/patches/19113/?format=api
{ "id": 19113, "url": "https://patchwork.libcamera.org/api/1.1/patches/19113/?format=api", "web_url": "https://patchwork.libcamera.org/patch/19113/", "project": { "id": 1, "url": "https://patchwork.libcamera.org/api/1.1/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": "<20231006132000.23504-12-naush@raspberrypi.com>", "date": "2023-10-06T13:19:51", "name": "[libcamera-devel,11/20] ipa: rpi: Add new algorithms for PiSP", "commit_ref": null, "pull_url": null, "state": "superseded", "archived": false, "hash": "9ba71e96ce0497b90fb85370878196656081a13c", "submitter": { "id": 34, "url": "https://patchwork.libcamera.org/api/1.1/people/34/?format=api", "name": "Naushir Patuck", "email": "naush@raspberrypi.com" }, "delegate": null, "mbox": "https://patchwork.libcamera.org/patch/19113/mbox/", "series": [ { "id": 4047, "url": "https://patchwork.libcamera.org/api/1.1/series/4047/?format=api", "web_url": "https://patchwork.libcamera.org/project/libcamera/list/?series=4047", "date": "2023-10-06T13:19:40", "name": "Raspberry Pi: Preliminary PiSP support", "version": 1, "mbox": "https://patchwork.libcamera.org/series/4047/mbox/" } ], "comments": "https://patchwork.libcamera.org/api/patches/19113/comments/", "check": "pending", "checks": "https://patchwork.libcamera.org/api/patches/19113/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 5C5EFC32B6\n\tfor <parsemail@patchwork.libcamera.org>;\n\tFri, 6 Oct 2023 13:20:14 +0000 (UTC)", "from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id DB23262976;\n\tFri, 6 Oct 2023 15:20:13 +0200 (CEST)", "from mail-wm1-x330.google.com (mail-wm1-x330.google.com\n\t[IPv6:2a00:1450:4864:20::330])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 3797262970\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tFri, 6 Oct 2023 15:20:12 +0200 (CEST)", "by mail-wm1-x330.google.com with SMTP id\n\t5b1f17b1804b1-406609df1a6so19907575e9.3\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tFri, 06 Oct 2023 06:20:12 -0700 (PDT)", "from localhost.localdomain ([93.93.133.154])\n\tby smtp.gmail.com with ESMTPSA id\n\tm8-20020a056000180800b00321773bb933sm1639478wrh.77.2023.10.06.06.20.09\n\t(version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256);\n\tFri, 06 Oct 2023 06:20:09 -0700 (PDT)" ], "DKIM-Signature": [ "v=1; a=rsa-sha256; c=relaxed/simple; d=libcamera.org;\n\ts=mail; t=1696598413;\n\tbh=InJk5nE/LHRBOTGRWjRFoKav2SdmaIyuGdMs8XQP2Ek=;\n\th=To:Date:In-Reply-To:References:Subject:List-Id:List-Unsubscribe:\n\tList-Archive:List-Post:List-Help:List-Subscribe:From:Reply-To:\n\tFrom;\n\tb=GAd+q10Kul7j5I82kXpkiOcLCDUo+5lOTU+DZhhv1fCxNRdZyLqPySSlgeKYVY8hk\n\tMkfowTKmZDh7xwtiWwbgAqmdOzqhW0rGUfsmRVQ4RrhMiTN5Gu0R9HbEEeu/s6prZZ\n\ttyaXuZ56dtktuLThdpC7Mq9bsQxM2gSH4cmQBDqPH0jYTP9tQ299Ye/fxULimL7O5s\n\tsC9ZuTL4gOIsI8bSvr90UqOiqVKJd4BzSi9onsMPB2slTx4ds/B9S979nz9AX9B8dM\n\tOOBHcXLhFuN8d2+cQ1aEoK9ZB4UwgcrTOX0n1SBuREhD//j9fE/DM31QPb2d5QhxWz\n\tg1rhO7J55VjzA==", "v=1; a=rsa-sha256; c=relaxed/relaxed;\n\td=raspberrypi.com; s=google; t=1696598411; x=1697203211;\n\tdarn=lists.libcamera.org; \n\th=content-transfer-encoding:mime-version:references:in-reply-to\n\t:message-id:date:subject:cc:to:from:from:to:cc:subject:date\n\t:message-id:reply-to;\n\tbh=VR+P5mQIrApbu0Xnw/5rpn4ma5izTxpcrx2OEmGcuNM=;\n\tb=B1Yd6/u6pIqJvT4usCYEc+pFGWV7ktyPiU/ewh7speCnAPryWU1kCULpPYfIaLv7E1\n\t4+P+sfbSXC1B8edr2lQ3/XrGSgNd94sgAF4x3WEZKANaiDxwqifs68lXpD+MBQLYk6hK\n\tX7XBqeLzyNJAL1zmkmM5wqYm3sNfHi56OMNN4393q3Yzt5lxP41R/5l2fl5Yl1vfcOWr\n\tzl1yGq+WHLBZq/UJwpJymNLNQzz/rfp2OjcH28FBPuZfF5ZB0KcxHqjP2EdSNtL/58ht\n\tUoaEbWoTabSmtPci5DdMhtSkHNUfMpIXoHoeOKIos1I9OYJOLruB9ARscLruLuUahSEq\n\tn4Lw==" ], "Authentication-Results": "lancelot.ideasonboard.com; dkim=pass (2048-bit key; \n\tunprotected) header.d=raspberrypi.com\n\theader.i=@raspberrypi.com\n\theader.b=\"B1Yd6/u6\"; dkim-atps=neutral", "X-Google-DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/relaxed;\n\td=1e100.net; s=20230601; t=1696598411; x=1697203211;\n\th=content-transfer-encoding:mime-version:references:in-reply-to\n\t:message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc\n\t:subject:date:message-id:reply-to;\n\tbh=VR+P5mQIrApbu0Xnw/5rpn4ma5izTxpcrx2OEmGcuNM=;\n\tb=Xi9Rig2IoULYcnfr49F9Iz+qRJg8f+2Yp/40IA1fexOMagTHYtuMdJ5gzc6+kL6puu\n\t0bmin6kvHg0ektsVROPTH6K5fvR3XEE7nbFhOtoQaZTPeaVxZvJe5vfRO5SQszTZvJGe\n\tj8aBz1DfmBRI0XEB34Cejo3RQuQ37JS3sUfvwPZopMYxImmsYdhORl6W3cYDpFJz0jes\n\t3lKarmgjrOQ6Y8dme2YAj4fSYq6hlqpB/rLURtyHYfb2fx2CUM8pRB0HDc2PX3S5f1xJ\n\tThoZeAmbSH/mQUOACZrlXEfGkmtn2BtlIyZH5l1M1mXAJQyiQma8BtX+Rbl3dp7Ea0Ib\n\tzWTg==", "X-Gm-Message-State": "AOJu0YxDaWO741T/3KeycR0rlWbyr/wtsPgacb9PtFFoDvJROyf3EJ9a\n\t9cwOwRwbini5CdoNZ4frlnVy6mKyj0ZMkf8Zlrf0aw==", "X-Google-Smtp-Source": "AGHT+IHe3T1B85IMv7V7UNbA5ZsHNg4nzoHAlXXTuU0e7mJ3LQoAsu6m2LfZXQuRjyIRsDov86Ji8g==", "X-Received": "by 2002:a05:600c:164a:b0:402:ea83:45cf with SMTP id\n\to10-20020a05600c164a00b00402ea8345cfmr7977569wmn.2.1696598410281; \n\tFri, 06 Oct 2023 06:20:10 -0700 (PDT)", "To": "libcamera-devel@lists.libcamera.org", "Date": "Fri, 6 Oct 2023 14:19:51 +0100", "Message-Id": "<20231006132000.23504-12-naush@raspberrypi.com>", "X-Mailer": "git-send-email 2.34.1", "In-Reply-To": "<20231006132000.23504-1-naush@raspberrypi.com>", "References": "<20231006132000.23504-1-naush@raspberrypi.com>", "MIME-Version": "1.0", "Content-Transfer-Encoding": "8bit", "Subject": "[libcamera-devel] [PATCH 11/20] ipa: rpi: Add new algorithms for\n\tPiSP", "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>", "From": "Naushir Patuck via libcamera-devel\n\t<libcamera-devel@lists.libcamera.org>", "Reply-To": "Naushir Patuck <naush@raspberrypi.com>", "Errors-To": "libcamera-devel-bounces@lists.libcamera.org", "Sender": "\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>" }, "content": "Add new CAC, HDR, Saturation and Tonemapping algorithms.\n\nAdd a new Denoise algorithm that handles spatial/temporal/colour denoise\nthrough one interface. With this change, the old SDN algorithm is now\nconsidered deprecated and a warning message will be displayed if it is\nenabled.\n\nSigned-off-by: Naushir Patuck <naush@raspberrypi.com>\nReviewed-by: David Plowman <david.plowman@raspberrypi.com>\n---\n src/ipa/rpi/controller/agc_status.h | 3 +\n src/ipa/rpi/controller/cac_status.h | 16 ++\n src/ipa/rpi/controller/denoise_status.h | 19 ++\n src/ipa/rpi/controller/hdr_algorithm.h | 25 ++\n src/ipa/rpi/controller/hdr_status.h | 19 ++\n src/ipa/rpi/controller/meson.build | 5 +\n src/ipa/rpi/controller/rpi/cac.cpp | 81 +++++++\n src/ipa/rpi/controller/rpi/cac.h | 38 +++\n src/ipa/rpi/controller/rpi/denoise.cpp | 156 ++++++++++++\n src/ipa/rpi/controller/rpi/denoise.h | 49 ++++\n src/ipa/rpi/controller/rpi/hdr.cpp | 270 +++++++++++++++++++++\n src/ipa/rpi/controller/rpi/hdr.h | 72 ++++++\n src/ipa/rpi/controller/rpi/saturation.cpp | 57 +++++\n src/ipa/rpi/controller/rpi/saturation.h | 32 +++\n src/ipa/rpi/controller/rpi/sdn.cpp | 2 +\n src/ipa/rpi/controller/rpi/tonemap.cpp | 61 +++++\n src/ipa/rpi/controller/rpi/tonemap.h | 35 +++\n src/ipa/rpi/controller/saturation_status.h | 13 +\n src/ipa/rpi/controller/stitch_status.h | 17 ++\n src/ipa/rpi/controller/tonemap_status.h | 17 ++\n 20 files changed, 987 insertions(+)\n create mode 100644 src/ipa/rpi/controller/cac_status.h\n create mode 100644 src/ipa/rpi/controller/hdr_algorithm.h\n create mode 100644 src/ipa/rpi/controller/hdr_status.h\n create mode 100644 src/ipa/rpi/controller/rpi/cac.cpp\n create mode 100644 src/ipa/rpi/controller/rpi/cac.h\n create mode 100644 src/ipa/rpi/controller/rpi/denoise.cpp\n create mode 100644 src/ipa/rpi/controller/rpi/denoise.h\n create mode 100644 src/ipa/rpi/controller/rpi/hdr.cpp\n create mode 100644 src/ipa/rpi/controller/rpi/hdr.h\n create mode 100644 src/ipa/rpi/controller/rpi/saturation.cpp\n create mode 100644 src/ipa/rpi/controller/rpi/saturation.h\n create mode 100644 src/ipa/rpi/controller/rpi/tonemap.cpp\n create mode 100644 src/ipa/rpi/controller/rpi/tonemap.h\n create mode 100644 src/ipa/rpi/controller/saturation_status.h\n create mode 100644 src/ipa/rpi/controller/stitch_status.h\n create mode 100644 src/ipa/rpi/controller/tonemap_status.h", "diff": "diff --git a/src/ipa/rpi/controller/agc_status.h b/src/ipa/rpi/controller/agc_status.h\nindex e5c4ee2239d9..68f899585740 100644\n--- a/src/ipa/rpi/controller/agc_status.h\n+++ b/src/ipa/rpi/controller/agc_status.h\n@@ -10,6 +10,8 @@\n \n #include <libcamera/base/utils.h>\n \n+#include \"hdr_status.h\"\n+\n /*\n * The AGC algorithm process method should post an AgcStatus into the image\n * metadata under the tag \"agc.status\".\n@@ -37,6 +39,7 @@ struct AgcStatus {\n \tlibcamera::utils::Duration fixedShutter;\n \tdouble fixedAnalogueGain;\n \tunsigned int channel;\n+\tHdrStatus hdr;\n };\n \n struct AgcPrepareStatus {\ndiff --git a/src/ipa/rpi/controller/cac_status.h b/src/ipa/rpi/controller/cac_status.h\nnew file mode 100644\nindex 000000000000..475d4c5cc734\n--- /dev/null\n+++ b/src/ipa/rpi/controller/cac_status.h\n@@ -0,0 +1,16 @@\n+/* SPDX-License-Identifier: BSD-2-Clause */\n+/*\n+ * Copyright (C) 2023 Raspberry Pi Ltd\n+ *\n+ * CAC (Chromatic Abberation Correction) algorithm status\n+ */\n+#pragma once\n+\n+#include \"pwl.h\"\n+\n+struct CacStatus {\n+\tstd::vector<double> lutRx;\n+\tstd::vector<double> lutRy;\n+\tstd::vector<double> lutBx;\n+\tstd::vector<double> lutBy;\n+};\ndiff --git a/src/ipa/rpi/controller/denoise_status.h b/src/ipa/rpi/controller/denoise_status.h\nindex f6b9ee29dad6..4d2bd291f2f1 100644\n--- a/src/ipa/rpi/controller/denoise_status.h\n+++ b/src/ipa/rpi/controller/denoise_status.h\n@@ -14,3 +14,22 @@ struct DenoiseStatus {\n \tdouble strength;\n \tunsigned int mode;\n };\n+\n+struct SdnStatus {\n+\tdouble noiseConstant;\n+\tdouble noiseSlope;\n+\tdouble noiseConstant2;\n+\tdouble noiseSlope2;\n+\tdouble strength;\n+};\n+\n+struct CdnStatus {\n+\tdouble strength;\n+\tdouble threshold;\n+};\n+\n+struct TdnStatus {\n+\tdouble noiseConstant;\n+\tdouble noiseSlope;\n+\tdouble threshold;\n+};\ndiff --git a/src/ipa/rpi/controller/hdr_algorithm.h b/src/ipa/rpi/controller/hdr_algorithm.h\nnew file mode 100644\nindex 000000000000..f622e099b6f5\n--- /dev/null\n+++ b/src/ipa/rpi/controller/hdr_algorithm.h\n@@ -0,0 +1,25 @@\n+/* SPDX-License-Identifier: BSD-2-Clause */\n+/*\n+ * Copyright (C) 2023, Raspberry Pi Ltd\n+ *\n+ * hdr_algorithm.h - HDR control algorithm interface\n+ */\n+#pragma once\n+\n+#include <vector>\n+\n+#include \"algorithm.h\"\n+\n+namespace RPiController {\n+\n+class HdrAlgorithm : public Algorithm\n+{\n+public:\n+\tHdrAlgorithm(Controller *controller)\n+\t\t: Algorithm(controller) {}\n+\t/* An HDR algorithm must provide the following: */\n+\tvirtual int setMode(std::string const &modeName) = 0;\n+\tvirtual std::vector<unsigned int> getChannels() const = 0;\n+};\n+\n+} /* namespace RPiController */\ndiff --git a/src/ipa/rpi/controller/hdr_status.h b/src/ipa/rpi/controller/hdr_status.h\nnew file mode 100644\nindex 000000000000..24b1a9358871\n--- /dev/null\n+++ b/src/ipa/rpi/controller/hdr_status.h\n@@ -0,0 +1,19 @@\n+/* SPDX-License-Identifier: BSD-2-Clause */\n+/*\n+ * Copyright (C) 2023 Raspberry Pi Ltd\n+ *\n+ * hdr_status.h - HDR control algorithm status\n+ */\n+#pragma once\n+\n+#include <string>\n+\n+/*\n+ * The HDR algorithm process method should post an HdrStatus into the image\n+ * metadata under the tag \"hdr.status\".\n+ */\n+\n+struct HdrStatus {\n+\tstd::string mode;\n+\tstd::string channel;\n+};\ndiff --git a/src/ipa/rpi/controller/meson.build b/src/ipa/rpi/controller/meson.build\nindex 20b9cda93661..32a4d31cfada 100644\n--- a/src/ipa/rpi/controller/meson.build\n+++ b/src/ipa/rpi/controller/meson.build\n@@ -12,14 +12,19 @@ rpi_ipa_controller_sources = files([\n 'rpi/alsc.cpp',\n 'rpi/awb.cpp',\n 'rpi/black_level.cpp',\n+ 'rpi/cac.cpp',\n 'rpi/ccm.cpp',\n 'rpi/contrast.cpp',\n+ 'rpi/denoise.cpp',\n 'rpi/dpc.cpp',\n 'rpi/geq.cpp',\n+ 'rpi/hdr.cpp',\n 'rpi/lux.cpp',\n 'rpi/noise.cpp',\n+ 'rpi/saturation.cpp',\n 'rpi/sdn.cpp',\n 'rpi/sharpen.cpp',\n+ 'rpi/tonemap.cpp',\n ])\n \n rpi_ipa_controller_deps = [\ndiff --git a/src/ipa/rpi/controller/rpi/cac.cpp b/src/ipa/rpi/controller/rpi/cac.cpp\nnew file mode 100644\nindex 000000000000..7c123da1530a\n--- /dev/null\n+++ b/src/ipa/rpi/controller/rpi/cac.cpp\n@@ -0,0 +1,81 @@\n+/* SPDX-License-Identifier: BSD-2-Clause */\n+/*\n+ * Copyright (C) 2023 Raspberry Pi Ltd\n+ *\n+ * cac.cpp - Chromatic Aberration Correction algorithm\n+ */\n+#include \"cac.h\"\n+\n+#include <libcamera/base/log.h>\n+\n+#include \"cac_status.h\"\n+\n+using namespace RPiController;\n+using namespace libcamera;\n+\n+LOG_DEFINE_CATEGORY(RPiCac)\n+\n+#define NAME \"rpi.cac\"\n+\n+Cac::Cac(Controller *controller)\n+\t: Algorithm(controller)\n+{\n+}\n+\n+char const *Cac::name() const\n+{\n+\treturn NAME;\n+}\n+\n+int Cac::read(const libcamera::YamlObject ¶ms)\n+{\n+\tarrayToSet(params[\"lut_rx\"], config_.lutRx);\n+\tarrayToSet(params[\"lut_ry\"], config_.lutRy);\n+\tarrayToSet(params[\"lut_bx\"], config_.lutBx);\n+\tarrayToSet(params[\"lut_by\"], config_.lutBy);\n+\tcacStatus_.lutRx = config_.lutRx;\n+\tcacStatus_.lutRy = config_.lutRy;\n+\tcacStatus_.lutBx = config_.lutBx;\n+\tcacStatus_.lutBy = config_.lutBy;\n+\tdouble strength = params[\"strength\"].get<double>(1);\n+\tsetStrength(config_.lutRx, cacStatus_.lutRx, strength);\n+\tsetStrength(config_.lutBx, cacStatus_.lutBx, strength);\n+\tsetStrength(config_.lutRy, cacStatus_.lutRy, strength);\n+\tsetStrength(config_.lutBy, cacStatus_.lutBy, strength);\n+\treturn 0;\n+}\n+\n+void Cac::initialise()\n+{\n+}\n+\n+void Cac::arrayToSet(const libcamera::YamlObject ¶ms, std::vector<double> &inputArray)\n+{\n+\tint num = 0;\n+\tconst Size &size = getHardwareConfig().cacRegions;\n+\tinputArray.resize((size.width + 1) * (size.height + 1));\n+\tfor (const auto &p : params.asList()) {\n+\t\tinputArray[num++] = p.get<double>(0);\n+\t}\n+}\n+\n+void Cac::setStrength(std::vector<double> &inputArray, std::vector<double> &outputArray,\n+\t\t double strengthFactor)\n+{\n+\tint num = 0;\n+\tfor (const auto &p : inputArray) {\n+\t\toutputArray[num++] = p * strengthFactor;\n+\t}\n+}\n+\n+void Cac::prepare(Metadata *imageMetadata)\n+{\n+\timageMetadata->set(\"cac.status\", cacStatus_);\n+}\n+\n+// Register algorithm with the system.\n+static Algorithm *Create(Controller *controller)\n+{\n+\treturn (Algorithm *)new Cac(controller);\n+}\n+static RegisterAlgorithm reg(NAME, &Create);\ndiff --git a/src/ipa/rpi/controller/rpi/cac.h b/src/ipa/rpi/controller/rpi/cac.h\nnew file mode 100644\nindex 000000000000..419180ab7d29\n--- /dev/null\n+++ b/src/ipa/rpi/controller/rpi/cac.h\n@@ -0,0 +1,38 @@\n+/* SPDX-License-Identifier: BSD-2-Clause */\n+/*\n+ * Copyright (C) 2023, Raspberry Pi Ltd\n+ *\n+ * cac.hpp - CAC control algorithm\n+ */\n+#pragma once\n+\n+#include \"algorithm.h\"\n+#include \"cac_status.h\"\n+\n+namespace RPiController {\n+\n+struct CacConfig {\n+\tstd::vector<double> lutRx;\n+\tstd::vector<double> lutRy;\n+\tstd::vector<double> lutBx;\n+\tstd::vector<double> lutBy;\n+};\n+\n+class Cac : public Algorithm\n+{\n+public:\n+\tCac(Controller *controller = NULL);\n+\tchar const *name() const override;\n+\tint read(const libcamera::YamlObject ¶ms) override;\n+\tvoid initialise() override;\n+\tvoid prepare(Metadata *imageMetadata) override;\n+\tvoid setStrength(std::vector<double> &inputArray, std::vector<double> &outputArray,\n+\t\t\t double strengthFactor);\n+\n+private:\n+\tCacConfig config_;\n+\tCacStatus cacStatus_;\n+\tvoid arrayToSet(const libcamera::YamlObject ¶ms, std::vector<double> &inputArray);\n+};\n+\n+} // namespace RPiController\ndiff --git a/src/ipa/rpi/controller/rpi/denoise.cpp b/src/ipa/rpi/controller/rpi/denoise.cpp\nnew file mode 100644\nindex 000000000000..440ee4425534\n--- /dev/null\n+++ b/src/ipa/rpi/controller/rpi/denoise.cpp\n@@ -0,0 +1,156 @@\n+/* SPDX-License-Identifier: BSD-2-Clause */\n+/*\n+ * Copyright (C) 2022 Raspberry Pi Ltd\n+ *\n+ * Denoise.cpp - Denoise (spatial, colour, temporal) control algorithm\n+ */\n+#include \"denoise.h\"\n+\n+#include <libcamera/base/log.h>\n+\n+#include \"denoise_status.h\"\n+#include \"noise_status.h\"\n+\n+using namespace RPiController;\n+using namespace libcamera;\n+\n+LOG_DEFINE_CATEGORY(RPiDenoise)\n+\n+// Calculate settings for the denoise blocks using the noise profile in\n+// the image metadata.\n+\n+#define NAME \"rpi.denoise\"\n+\n+Denoise::Denoise(Controller *controller)\n+\t: DenoiseAlgorithm(controller), mode_(DenoiseMode::ColourHighQuality)\n+{\n+}\n+\n+char const *Denoise::name() const\n+{\n+\treturn NAME;\n+}\n+\n+int Denoise::read(const libcamera::YamlObject ¶ms)\n+{\n+\tsdnEnable_ = params.contains(\"sdn\");\n+\tif (sdnEnable_) {\n+\t\tauto &sdnParams = params[\"sdn\"];\n+\t\tsdnDeviation_ = sdnParams[\"deviation\"].get<double>(3.2);\n+\t\tsdnStrength_ = sdnParams[\"strength\"].get<double>(0.25);\n+\t\tsdnDeviation2_ = sdnParams[\"deviation2\"].get<double>(sdnDeviation_);\n+\t\tsdnDeviationNoTdn_ = sdnParams[\"deviation_no_tdn\"].get<double>(sdnDeviation_);\n+\t\tsdnStrengthNoTdn_ = sdnParams[\"strength_no_tdn\"].get<double>(sdnStrength_);\n+\t\tsdnTdnBackoff_ = sdnParams[\"backoff\"].get<double>(0.75);\n+\t}\n+\n+\tcdnEnable_ = params.contains(\"cdn\");\n+\tif (cdnEnable_) {\n+\t\tauto &cdnParams = params[\"cdn\"];\n+\t\tcdnDeviation_ = cdnParams[\"deviation\"].get<double>(120);\n+\t\tcdnStrength_ = cdnParams[\"strength\"].get<double>(0.2);\n+\t}\n+\n+\ttdnEnable_ = params.contains(\"tdn\");\n+\tif (tdnEnable_) {\n+\t\tauto &tdnParams = params[\"tdn\"];\n+\t\ttdnDeviation_ = tdnParams[\"deviation\"].get<double>(0.5);\n+\t\ttdnThreshold_ = tdnParams[\"threshold\"].get<double>(0.75);\n+\t} else if (sdnEnable_) {\n+\t\t/*\n+\t\t * If SDN is enabled but TDN isn't, overwrite all the SDN settings\n+\t\t * with the \"no TDN\" versions. This makes it easier to enable or\n+\t\t * disable TDN in the tuning file without editing all the other\n+\t\t * parameters.\n+\t\t */\n+\t\tsdnDeviation_ = sdnDeviation2_ = sdnDeviationNoTdn_;\n+\t\tsdnStrength_ = sdnStrengthNoTdn_;\n+\t}\n+\n+\treturn 0;\n+}\n+\n+void Denoise::initialise()\n+{\n+}\n+\n+void Denoise::switchMode([[maybe_unused]] CameraMode const &cameraMode,\n+\t\t\t [[maybe_unused]] Metadata *metadata)\n+{\n+\t/* A mode switch effectively resets temporal denoise and it has to start over. */\n+\tcurrentSdnDeviation_ = sdnDeviationNoTdn_;\n+\tcurrentSdnStrength_ = sdnStrengthNoTdn_;\n+\tcurrentSdnDeviation2_ = sdnDeviationNoTdn_;\n+}\n+\n+void Denoise::prepare(Metadata *imageMetadata)\n+{\n+\tstruct NoiseStatus noiseStatus = {};\n+\tnoiseStatus.noiseSlope = 3.0; // in case no metadata\n+\tif (imageMetadata->get(\"noise.status\", noiseStatus) != 0)\n+\t\tLOG(RPiDenoise, Warning) << \"no noise profile found\";\n+\n+\tLOG(RPiDenoise, Debug)\n+\t\t<< \"Noise profile: constant \" << noiseStatus.noiseConstant\n+\t\t<< \" slope \" << noiseStatus.noiseSlope;\n+\n+\tif (mode_ == DenoiseMode::Off)\n+\t\treturn;\n+\n+\tif (sdnEnable_) {\n+\t\tstruct SdnStatus sdn;\n+\t\tsdn.noiseConstant = noiseStatus.noiseConstant * currentSdnDeviation_;\n+\t\tsdn.noiseSlope = noiseStatus.noiseSlope * currentSdnDeviation_;\n+\t\tsdn.noiseConstant2 = noiseStatus.noiseConstant * sdnDeviation2_;\n+\t\tsdn.noiseSlope2 = noiseStatus.noiseSlope * currentSdnDeviation2_;\n+\t\tsdn.strength = currentSdnStrength_;\n+\t\timageMetadata->set(\"sdn.status\", sdn);\n+\t\tLOG(RPiDenoise, Debug)\n+\t\t\t<< \"const \" << sdn.noiseConstant\n+\t\t\t<< \" slope \" << sdn.noiseSlope\n+\t\t\t<< \" str \" << sdn.strength\n+\t\t\t<< \" const2 \" << sdn.noiseConstant2\n+\t\t\t<< \" slope2 \" << sdn.noiseSlope2;\n+\n+\t\t/* For the next frame, we back off the SDN parameters as TDN ramps up. */\n+\t\tdouble f = sdnTdnBackoff_;\n+\t\tcurrentSdnDeviation_ = f * currentSdnDeviation_ + (1 - f) * sdnDeviation_;\n+\t\tcurrentSdnStrength_ = f * currentSdnStrength_ + (1 - f) * sdnStrength_;\n+\t\tcurrentSdnDeviation2_ = f * currentSdnDeviation2_ + (1 - f) * sdnDeviation2_;\n+\t}\n+\n+\tif (tdnEnable_) {\n+\t\tstruct TdnStatus tdn;\n+\t\ttdn.noiseConstant = noiseStatus.noiseConstant * tdnDeviation_;\n+\t\ttdn.noiseSlope = noiseStatus.noiseSlope * tdnDeviation_;\n+\t\ttdn.threshold = tdnThreshold_;\n+\t\timageMetadata->set(\"tdn.status\", tdn);\n+\t\tLOG(RPiDenoise, Debug)\n+\t\t\t<< \"programmed tdn threshold \" << tdn.threshold\n+\t\t\t<< \" constant \" << tdn.noiseConstant\n+\t\t\t<< \" slope \" << tdn.noiseSlope;\n+\t}\n+\n+\tif (cdnEnable_ && mode_ != DenoiseMode::ColourOff) {\n+\t\tstruct CdnStatus cdn;\n+\t\tcdn.threshold = cdnDeviation_ * noiseStatus.noiseSlope + noiseStatus.noiseConstant;\n+\t\tcdn.strength = cdnStrength_;\n+\t\timageMetadata->set(\"cdn.status\", cdn);\n+\t\tLOG(RPiDenoise, Debug)\n+\t\t\t<< \"programmed cdn threshold \" << cdn.threshold\n+\t\t\t<< \" strength \" << cdn.strength;\n+\t}\n+}\n+\n+void Denoise::setMode(DenoiseMode mode)\n+{\n+\t// We only distinguish between off and all other modes.\n+\tmode_ = mode;\n+}\n+\n+// Register algorithm with the system.\n+static Algorithm *Create(Controller *controller)\n+{\n+\treturn (Algorithm *)new Denoise(controller);\n+}\n+static RegisterAlgorithm reg(NAME, &Create);\ndiff --git a/src/ipa/rpi/controller/rpi/denoise.h b/src/ipa/rpi/controller/rpi/denoise.h\nnew file mode 100644\nindex 000000000000..88b37663e569\n--- /dev/null\n+++ b/src/ipa/rpi/controller/rpi/denoise.h\n@@ -0,0 +1,49 @@\n+/* SPDX-License-Identifier: BSD-2-Clause */\n+/*\n+ * Copyright (C) 2022, Raspberry Pi Ltd\n+ *\n+ * denoise.hpp - Denoise (spatial, colour, temporal) control algorithm\n+ */\n+#pragma once\n+\n+#include \"algorithm.h\"\n+#include \"denoise_algorithm.h\"\n+\n+namespace RPiController {\n+\n+// Algorithm to calculate correct denoise settings.\n+\n+class Denoise : public DenoiseAlgorithm\n+{\n+public:\n+\tDenoise(Controller *controller);\n+\tchar const *name() const override;\n+\tint read(const libcamera::YamlObject ¶ms) override;\n+\tvoid initialise() override;\n+\tvoid switchMode(CameraMode const &cameraMode, Metadata *metadata) override;\n+\tvoid prepare(Metadata *imageMetadata) override;\n+\tvoid setMode(DenoiseMode mode) override;\n+\n+private:\n+\tdouble sdnDeviation_;\n+\tdouble sdnStrength_;\n+\tdouble sdnDeviation2_;\n+\tdouble sdnDeviationNoTdn_;\n+\tdouble sdnStrengthNoTdn_;\n+\tdouble sdnTdnBackoff_;\n+\tdouble cdnDeviation_;\n+\tdouble cdnStrength_;\n+\tdouble tdnDeviation_;\n+\tdouble tdnThreshold_;\n+\tDenoiseMode mode_;\n+\tbool tdnEnable_;\n+\tbool sdnEnable_;\n+\tbool cdnEnable_;\n+\n+\t/* SDN parameters attenuate over time if TDN is running. */\n+\tdouble currentSdnDeviation_;\n+\tdouble currentSdnStrength_;\n+\tdouble currentSdnDeviation2_;\n+};\n+\n+} // namespace RPiController\ndiff --git a/src/ipa/rpi/controller/rpi/hdr.cpp b/src/ipa/rpi/controller/rpi/hdr.cpp\nnew file mode 100644\nindex 000000000000..295e4c5f1c0a\n--- /dev/null\n+++ b/src/ipa/rpi/controller/rpi/hdr.cpp\n@@ -0,0 +1,270 @@\n+/* SPDX-License-Identifier: BSD-2-Clause */\n+/*\n+ * Copyright (C) 2023 Raspberry Pi Ltd\n+ *\n+ * hdr.cpp - HDR control algorithm\n+ */\n+\n+#include \"hdr.h\"\n+\n+#include <libcamera/base/log.h>\n+\n+#include \"../agc_status.h\"\n+#include \"../stitch_status.h\"\n+#include \"../tonemap_status.h\"\n+\n+using namespace RPiController;\n+using namespace libcamera;\n+\n+LOG_DEFINE_CATEGORY(RPiHdr)\n+\n+#define NAME \"rpi.hdr\"\n+\n+void HdrConfig::read(const libcamera::YamlObject ¶ms, const std::string &modeName)\n+{\n+\tname = modeName;\n+\n+\tif (!params.contains(\"cadence\"))\n+\t\tLOG(RPiHdr, Fatal) << \"No cadence for HDR mode \" << name;\n+\tcadence = params[\"cadence\"].getList<unsigned int>().value();\n+\tif (cadence.empty())\n+\t\tLOG(RPiHdr, Fatal) << \"Empty cadence in HDR mode \" << name;\n+\n+\t/*\n+\t * In the JSON file it's easier to use the channel name as the key, but\n+\t * for us it's convenient to swap them over.\n+\t */\n+\tfor (const auto &[k, v] : params[\"channel_map\"].asDict())\n+\t\tchannelMap[v.get<unsigned int>().value()] = k;\n+\n+\t/* Read any tonemap parameters. */\n+\ttonemapEnable = params[\"tonemap_enable\"].get<int>(0);\n+\tdetailConstant = params[\"detail_constant\"].get<uint16_t>(50);\n+\tdetailSlope = params[\"detail_slope\"].get<double>(8.0);\n+\tiirStrength = params[\"iir_strength\"].get<double>(8.0);\n+\tstrength = params[\"strength\"].get<double>(1.5);\n+\n+\tif (tonemapEnable) {\n+\t\t/* We need either an explicit tonemap, or the information to build them dynamically. */\n+\t\tif (params.contains(\"tonemap\")) {\n+\t\t\tif (tonemap.read(params[\"tonemap\"]))\n+\t\t\t\tLOG(RPiHdr, Fatal) << \"Failed to read tonemap in HDR mode \" << name;\n+\t\t} else {\n+\t\t\tif (target.read(params[\"target\"]))\n+\t\t\t\tLOG(RPiHdr, Fatal) << \"Failed to read target in HDR mode \" << name;\n+\t\t\tif (maxSlope.read(params[\"max_slope\"]))\n+\t\t\t\tLOG(RPiHdr, Fatal) << \"Failed to read max_slope in HDR mode \" << name;\n+\t\t\tminSlope = params[\"min_slope\"].get<double>(1.0);\n+\t\t\tmaxGain = params[\"max_gain\"].get<double>(64.0);\n+\t\t\tstep = params[\"step\"].get<double>(0.05);\n+\t\t\tspeed = params[\"speed\"].get<double>(0.5);\n+\t\t}\n+\t}\n+\n+\t/* Read any stitch parameters. */\n+\tstitchEnable = params[\"stitch_enable\"].get<int>(0);\n+\tthresholdLo = params[\"threshold_lo\"].get<uint16_t>(50000);\n+\tmotionThreshold = params[\"motion_threshold\"].get<double>(0.005);\n+\tdiffPower = params[\"diff_power\"].get<uint8_t>(13);\n+\tif (diffPower > 15)\n+\t\tLOG(RPiHdr, Fatal) << \"Bad diff_power value in HDR mode \" << name;\n+}\n+\n+Hdr::Hdr(Controller *controller)\n+\t: HdrAlgorithm(controller)\n+{\n+}\n+\n+char const *Hdr::name() const\n+{\n+\treturn NAME;\n+}\n+\n+int Hdr::read(const libcamera::YamlObject ¶ms)\n+{\n+\t/* Make an \"HDR off\" mode by default so that tuning files don't have to. */\n+\tHdrConfig &offMode = config_[\"Off\"];\n+\toffMode.name = \"Off\";\n+\toffMode.cadence = { 0 };\n+\toffMode.channelMap[0] = \"None\";\n+\tstatus_.mode = offMode.name;\n+\tdelayedStatus_.mode = offMode.name;\n+\n+\t/*\n+\t * But we still allow the tuning file to override the \"Off\" mode if it wants.\n+\t * For example, maybe an application will make channel 0 be the \"short\"\n+\t * channel, in order to apply other AGC controls to it.\n+\t */\n+\tfor (const auto &[key, value] : params.asDict())\n+\t\tconfig_[key].read(value, key);\n+\n+\treturn 0;\n+}\n+\n+int Hdr::setMode(std::string const &mode)\n+{\n+\t/* Always validate the mode, so it can be used later without checking. */\n+\tauto it = config_.find(mode);\n+\tif (it == config_.end()) {\n+\t\tLOG(RPiHdr, Warning) << \"No such HDR mode \" << mode;\n+\t\treturn -1;\n+\t}\n+\n+\tstatus_.mode = it->second.name;\n+\n+\treturn 0;\n+}\n+\n+std::vector<unsigned int> Hdr::getChannels() const\n+{\n+\treturn config_.at(status_.mode).cadence;\n+}\n+\n+void Hdr::updateAgcStatus(Metadata *metadata)\n+{\n+\tstd::scoped_lock lock(*metadata);\n+\tAgcStatus *agcStatus = metadata->getLocked<AgcStatus>(\"agc.status\");\n+\tif (agcStatus) {\n+\t\tHdrConfig &hdrConfig = config_[status_.mode];\n+\t\tauto it = hdrConfig.channelMap.find(agcStatus->channel);\n+\t\tif (it != hdrConfig.channelMap.end()) {\n+\t\t\tstatus_.channel = it->second;\n+\t\t\tagcStatus->hdr = status_;\n+\t\t} else\n+\t\t\tLOG(RPiHdr, Warning) << \"Channel \" << agcStatus->channel\n+\t\t\t\t\t << \" not found in mode \" << status_.mode;\n+\t} else\n+\t\tLOG(RPiHdr, Warning) << \"No agc.status found\";\n+}\n+\n+void Hdr::switchMode([[maybe_unused]] CameraMode const &cameraMode, Metadata *metadata)\n+{\n+\tupdateAgcStatus(metadata);\n+\tdelayedStatus_ = status_;\n+}\n+\n+bool Hdr::updateTonemap(StatisticsPtr &stats, HdrConfig &config)\n+{\n+\t/* When there's a change of HDR mode we start over with a new tonemap curve. */\n+\tif (delayedStatus_.mode != previousMode_) {\n+\t\tpreviousMode_ = delayedStatus_.mode;\n+\t\ttonemap_ = Pwl();\n+\t}\n+\n+\t/* No tonemapping. No need to output a tonemap.status. */\n+\tif (!config.tonemapEnable)\n+\t\treturn false;\n+\n+\t/* If an explicit tonemap was given, use it. */\n+\tif (!config.tonemap.empty()) {\n+\t\ttonemap_ = config.tonemap;\n+\t\treturn true;\n+\t}\n+\n+\t/*\n+\t * We only update the tonemap on short frames when in multi-exposure mode. But\n+\t * we still need to output the most recent tonemap. Possibly we should make the\n+\t * config indicate the channels for which we should update the tonemap?\n+\t */\n+\tif (delayedStatus_.mode == \"MultiExposure\" && delayedStatus_.channel != \"short\")\n+\t\treturn true;\n+\n+\t/* Build the tonemap dynamically using the image histogram. */\n+\tPwl tonemap;\n+\ttonemap.append(0, 0);\n+\n+\tdouble prev_input_val = 0;\n+\tdouble prev_output_val = 0;\n+\tconst double step2 = config.step / 2;\n+\tfor (double q = config.step; q < 1.0 - step2; q += config.step) {\n+\t\tdouble q_lo = std::max(0.0, q - step2);\n+\t\tdouble q_hi = std::min(1.0, q + step2);\n+\t\tdouble iqm = stats->yHist.interQuantileMean(q_lo, q_hi);\n+\t\tdouble input_val = std::min(iqm * 64, 65535.0);\n+\n+\t\tif (input_val > prev_input_val + 1) {\n+\t\t\t/* We're going to calcualte a Pwl to map input_val to this output_val. */\n+\t\t\tdouble want_output_val = config.target.eval(q) * 65535;\n+\t\t\t/* But we must ensure we aren't applying too small or too great a local gain. */\n+\t\t\tdouble want_slope = (want_output_val - prev_output_val) / (input_val - prev_input_val);\n+\t\t\tdouble slope = std::clamp(want_slope, config.minSlope,\n+\t\t\t\t\t\t config.maxSlope.eval(q));\n+\t\t\tdouble output_val = prev_output_val + slope * (input_val - prev_input_val);\n+\t\t\toutput_val = std::min(output_val, config.maxGain * input_val);\n+\t\t\toutput_val = std::clamp(output_val, 0.0, 65535.0);\n+\t\t\t/* Let the tonemap adapte slightly more gently from frame to frame. */\n+\t\t\tif (!tonemap_.empty()) {\n+\t\t\t\tdouble old_output_val = tonemap_.eval(input_val);\n+\t\t\t\toutput_val = config.speed * output_val +\n+\t\t\t\t\t (1 - config.speed) * old_output_val;\n+\t\t\t}\n+\t\t\tLOG(RPiHdr, Debug) << \"q \" << q << \" input \" << input_val\n+\t\t\t\t\t << \" output \" << want_output_val << \" slope \" << want_slope\n+\t\t\t\t\t << \" slope \" << slope << \" output \" << output_val;\n+\t\t\ttonemap.append(input_val, output_val);\n+\t\t\tprev_input_val = input_val;\n+\t\t\tprev_output_val = output_val;\n+\t\t}\n+\t}\n+\n+\ttonemap.append(65535, 65535);\n+\t/* tonemap.debug(); */\n+\ttonemap_ = tonemap;\n+\n+\treturn true;\n+}\n+\n+void Hdr::process(StatisticsPtr &stats, Metadata *imageMetadata)\n+{\n+\t/* Note what HDR channel this frame will be once it comes back to us. */\n+\tupdateAgcStatus(imageMetadata);\n+\n+\t/*\n+\t * Now figure out what HDR channel this frame is. It should be available in the\n+\t * agc.delayed_status, unless this is an early frame after a mode switch, in which\n+\t * case delayedStatus_ should be right.\n+\t */\n+\tAgcStatus agcStatus;\n+\tif (!imageMetadata->get<AgcStatus>(\"agc.delayed_status\", agcStatus))\n+\t\tdelayedStatus_ = agcStatus.hdr;\n+\n+\tauto it = config_.find(delayedStatus_.mode);\n+\tif (it == config_.end()) {\n+\t\t/* Shouldn't be possible. There would be nothing we could do. */\n+\t\tLOG(RPiHdr, Warning) << \"Unexpected HDR mode \" << delayedStatus_.mode;\n+\t\treturn;\n+\t}\n+\n+\tHdrConfig &config = it->second;\n+\n+\tif (updateTonemap(stats, config)) {\n+\t\t/* Add tonemap.status metadata. */\n+\t\tTonemapStatus tonemapStatus;\n+\n+\t\ttonemapStatus.detailConstant = config.detailConstant;\n+\t\ttonemapStatus.detailSlope = config.detailSlope;\n+\t\ttonemapStatus.iirStrength = config.iirStrength;\n+\t\ttonemapStatus.strength = config.strength;\n+\t\ttonemapStatus.tonemap = tonemap_;\n+\n+\t\timageMetadata->set(\"tonemap.status\", tonemapStatus);\n+\t}\n+\n+\tif (config.stitchEnable) {\n+\t\t/* Add stitch.status metadata. */\n+\t\tStitchStatus stitchStatus;\n+\n+\t\tstitchStatus.diffPower = config.diffPower;\n+\t\tstitchStatus.motionThreshold = config.motionThreshold;\n+\t\tstitchStatus.thresholdLo = config.thresholdLo;\n+\n+\t\timageMetadata->set(\"stitch.status\", stitchStatus);\n+\t}\n+}\n+\n+/* Register algorithm with the system. */\n+static Algorithm *create(Controller *controller)\n+{\n+\treturn (Algorithm *)new Hdr(controller);\n+}\n+static RegisterAlgorithm reg(NAME, &create);\ndiff --git a/src/ipa/rpi/controller/rpi/hdr.h b/src/ipa/rpi/controller/rpi/hdr.h\nnew file mode 100644\nindex 000000000000..01ba45f1d3dc\n--- /dev/null\n+++ b/src/ipa/rpi/controller/rpi/hdr.h\n@@ -0,0 +1,72 @@\n+/* SPDX-License-Identifier: BSD-2-Clause */\n+/*\n+ * Copyright (C) 2023, Raspberry Pi Ltd\n+ *\n+ * hdr.h - HDR control algorithm\n+ */\n+#pragma once\n+\n+#include <map>\n+#include <string>\n+#include <vector>\n+\n+#include \"../hdr_algorithm.h\"\n+#include \"../hdr_status.h\"\n+#include \"../pwl.h\"\n+\n+/* This is our implementation of an HDR algorithm. */\n+\n+namespace RPiController {\n+\n+struct HdrConfig {\n+\tstd::string name;\n+\tstd::vector<unsigned int> cadence;\n+\tstd::map<unsigned int, std::string> channelMap;\n+\n+\t/* Tonemap related parameters. */\n+\tbool tonemapEnable;\n+\tuint16_t detailConstant;\n+\tdouble detailSlope;\n+\tdouble iirStrength;\n+\tdouble strength;\n+\t/* We must have either an explicit tonemap curve, or the other parameters. */\n+\tPwl tonemap;\n+\tPwl target; /* maps histogram quatile to desired target output value */\n+\tPwl maxSlope; /* the maximum slope allowed at each point in the mapping */\n+\tdouble minSlope; /* the minimum allowed slope */\n+\tdouble maxGain; /* limit to the max absolute gain */\n+\tdouble step; /* the histogram granularity for building the mapping */\n+\tdouble speed; /* rate at which tonemap is updated */\n+\n+\t/* Stitch related parameters. */\n+\tbool stitchEnable;\n+\tuint16_t thresholdLo;\n+\tuint8_t diffPower;\n+\tdouble motionThreshold;\n+\n+\tvoid read(const libcamera::YamlObject ¶ms, const std::string &name);\n+};\n+\n+class Hdr : public HdrAlgorithm\n+{\n+public:\n+\tHdr(Controller *controller);\n+\tchar const *name() const override;\n+\tvoid switchMode(CameraMode const &cameraMode, Metadata *metadata) override;\n+\tint read(const libcamera::YamlObject ¶ms) override;\n+\tvoid process(StatisticsPtr &stats, Metadata *imageMetadata) override;\n+\tint setMode(std::string const &mode) override;\n+\tstd::vector<unsigned int> getChannels() const override;\n+\n+private:\n+\tvoid updateAgcStatus(Metadata *metadata);\n+\tbool updateTonemap(StatisticsPtr &stats, HdrConfig &config);\n+\n+\tstd::map<std::string, HdrConfig> config_;\n+\tHdrStatus status_; /* track the current HDR mode and channel */\n+\tHdrStatus delayedStatus_; /* track the delayed HDR mode and channel */\n+\tstd::string previousMode_;\n+\tPwl tonemap_;\n+};\n+\n+} /* namespace RPiController */\ndiff --git a/src/ipa/rpi/controller/rpi/saturation.cpp b/src/ipa/rpi/controller/rpi/saturation.cpp\nnew file mode 100644\nindex 000000000000..813540e5154d\n--- /dev/null\n+++ b/src/ipa/rpi/controller/rpi/saturation.cpp\n@@ -0,0 +1,57 @@\n+/* SPDX-License-Identifier: BSD-2-Clause */\n+/*\n+ * Copyright (C) 2022 Raspberry Pi Ltd\n+ *\n+ * saturation.cpp - Saturation control algorithm\n+ */\n+#include \"saturation.h\"\n+\n+#include <libcamera/base/log.h>\n+\n+#include \"saturation_status.h\"\n+\n+using namespace RPiController;\n+using namespace libcamera;\n+\n+LOG_DEFINE_CATEGORY(RPiSaturation)\n+\n+#define NAME \"rpi.saturation\"\n+\n+Saturation::Saturation(Controller *controller)\n+\t: Algorithm(controller)\n+{\n+}\n+\n+char const *Saturation::name() const\n+{\n+\treturn NAME;\n+}\n+\n+int Saturation::read(const libcamera::YamlObject ¶ms)\n+{\n+\tconfig_.shiftR = params[\"shift_r\"].get<uint8_t>(0);\n+\tconfig_.shiftG = params[\"shift_g\"].get<uint8_t>(0);\n+\tconfig_.shiftB = params[\"shift_b\"].get<uint8_t>(0);\n+\treturn 0;\n+}\n+\n+void Saturation::initialise()\n+{\n+}\n+\n+void Saturation::prepare(Metadata *imageMetadata)\n+{\n+\tSaturationStatus saturation;\n+\n+\tsaturation.shiftR = config_.shiftR;\n+\tsaturation.shiftG = config_.shiftG;\n+\tsaturation.shiftB = config_.shiftB;\n+\timageMetadata->set(\"saturation.status\", saturation);\n+}\n+\n+// Register algorithm with the system.\n+static Algorithm *Create(Controller *controller)\n+{\n+\treturn (Algorithm *)new Saturation(controller);\n+}\n+static RegisterAlgorithm reg(NAME, &Create);\ndiff --git a/src/ipa/rpi/controller/rpi/saturation.h b/src/ipa/rpi/controller/rpi/saturation.h\nnew file mode 100644\nindex 000000000000..97da412ad59a\n--- /dev/null\n+++ b/src/ipa/rpi/controller/rpi/saturation.h\n@@ -0,0 +1,32 @@\n+/* SPDX-License-Identifier: BSD-2-Clause */\n+/*\n+ * Copyright (C) 2022, Raspberry Pi Ltd\n+ *\n+ * saturation.hpp - Saturation control algorithm\n+ */\n+#pragma once\n+\n+#include \"algorithm.h\"\n+\n+namespace RPiController {\n+\n+struct SaturationConfig {\n+\tuint8_t shiftR;\n+\tuint8_t shiftG;\n+\tuint8_t shiftB;\n+};\n+\n+class Saturation : public Algorithm\n+{\n+public:\n+\tSaturation(Controller *controller = NULL);\n+\tchar const *name() const override;\n+\tint read(const libcamera::YamlObject ¶ms) override;\n+\tvoid initialise() override;\n+\tvoid prepare(Metadata *imageMetadata) override;\n+\n+private:\n+\tSaturationConfig config_;\n+};\n+\n+} // namespace RPiController\ndiff --git a/src/ipa/rpi/controller/rpi/sdn.cpp b/src/ipa/rpi/controller/rpi/sdn.cpp\nindex b6b662518f2c..6743919e6b36 100644\n--- a/src/ipa/rpi/controller/rpi/sdn.cpp\n+++ b/src/ipa/rpi/controller/rpi/sdn.cpp\n@@ -36,6 +36,8 @@ char const *Sdn::name() const\n \n int Sdn::read(const libcamera::YamlObject ¶ms)\n {\n+\tLOG(RPiSdn, Warning)\n+\t\t<< \"Using legacy SDN tuning - please consider moving SDN inside rpi.denoise\";\n \tdeviation_ = params[\"deviation\"].get<double>(3.2);\n \tstrength_ = params[\"strength\"].get<double>(0.75);\n \treturn 0;\ndiff --git a/src/ipa/rpi/controller/rpi/tonemap.cpp b/src/ipa/rpi/controller/rpi/tonemap.cpp\nnew file mode 100644\nindex 000000000000..5f8b2bf25aeb\n--- /dev/null\n+++ b/src/ipa/rpi/controller/rpi/tonemap.cpp\n@@ -0,0 +1,61 @@\n+/* SPDX-License-Identifier: BSD-2-Clause */\n+/*\n+ * Copyright (C) 2022 Raspberry Pi Ltd\n+ *\n+ * tonemap.cpp - Tonemap control algorithm\n+ */\n+#include \"tonemap.h\"\n+\n+#include <libcamera/base/log.h>\n+\n+#include \"tonemap_status.h\"\n+\n+using namespace RPiController;\n+using namespace libcamera;\n+\n+LOG_DEFINE_CATEGORY(RPiTonemap)\n+\n+#define NAME \"rpi.tonemap\"\n+\n+Tonemap::Tonemap(Controller *controller)\n+\t: Algorithm(controller)\n+{\n+}\n+\n+char const *Tonemap::name() const\n+{\n+\treturn NAME;\n+}\n+\n+int Tonemap::read(const libcamera::YamlObject ¶ms)\n+{\n+\tconfig_.detailConstant = params[\"detail_constant\"].get<uint16_t>(0);\n+\tconfig_.detailSlope = params[\"detail_slope\"].get<double>(0.1);\n+\tconfig_.iirStrength = params[\"iir_strength\"].get<double>(1.0);\n+\tconfig_.strength = params[\"strength\"].get<double>(1.0);\n+\tconfig_.tonemap.read(params[\"tone_curve\"]);\n+\treturn 0;\n+}\n+\n+void Tonemap::initialise()\n+{\n+}\n+\n+void Tonemap::prepare(Metadata *imageMetadata)\n+{\n+\tTonemapStatus tonemapStatus;\n+\n+\ttonemapStatus.detailConstant = config_.detailConstant;\n+\ttonemapStatus.detailSlope = config_.detailSlope;\n+\ttonemapStatus.iirStrength = config_.iirStrength;\n+\ttonemapStatus.strength = config_.strength;\n+\ttonemapStatus.tonemap = config_.tonemap;\n+\timageMetadata->set(\"tonemap.status\", tonemapStatus);\n+}\n+\n+// Register algorithm with the system.\n+static Algorithm *Create(Controller *controller)\n+{\n+\treturn (Algorithm *)new Tonemap(controller);\n+}\n+static RegisterAlgorithm reg(NAME, &Create);\ndiff --git a/src/ipa/rpi/controller/rpi/tonemap.h b/src/ipa/rpi/controller/rpi/tonemap.h\nnew file mode 100644\nindex 000000000000..f25aa47f86c2\n--- /dev/null\n+++ b/src/ipa/rpi/controller/rpi/tonemap.h\n@@ -0,0 +1,35 @@\n+/* SPDX-License-Identifier: BSD-2-Clause */\n+/*\n+ * Copyright (C) 2022, Raspberry Pi Ltd\n+ *\n+ * tonemap.hpp - Tonemap control algorithm\n+ */\n+#pragma once\n+\n+#include \"algorithm.h\"\n+#include \"pwl.h\"\n+\n+namespace RPiController {\n+\n+struct TonemapConfig {\n+\tuint16_t detailConstant;\n+\tdouble detailSlope;\n+\tdouble iirStrength;\n+\tdouble strength;\n+\tPwl tonemap;\n+};\n+\n+class Tonemap : public Algorithm\n+{\n+public:\n+\tTonemap(Controller *controller = NULL);\n+\tchar const *name() const override;\n+\tint read(const libcamera::YamlObject ¶ms) override;\n+\tvoid initialise() override;\n+\tvoid prepare(Metadata *imageMetadata) override;\n+\n+private:\n+\tTonemapConfig config_;\n+};\n+\n+} // namespace RPiController\ndiff --git a/src/ipa/rpi/controller/saturation_status.h b/src/ipa/rpi/controller/saturation_status.h\nnew file mode 100644\nindex 000000000000..337b66a3e91e\n--- /dev/null\n+++ b/src/ipa/rpi/controller/saturation_status.h\n@@ -0,0 +1,13 @@\n+/* SPDX-License-Identifier: BSD-2-Clause */\n+/*\n+ * Copyright (C) 2022 Raspberry Pi Ltd\n+ *\n+ * saturation_status.h - Saturation control algorithm status\n+ */\n+#pragma once\n+\n+struct SaturationStatus {\n+\tuint8_t shiftR;\n+\tuint8_t shiftG;\n+\tuint8_t shiftB;\n+};\ndiff --git a/src/ipa/rpi/controller/stitch_status.h b/src/ipa/rpi/controller/stitch_status.h\nnew file mode 100644\nindex 000000000000..b17800ed6697\n--- /dev/null\n+++ b/src/ipa/rpi/controller/stitch_status.h\n@@ -0,0 +1,17 @@\n+/* SPDX-License-Identifier: BSD-2-Clause */\n+/*\n+ * Copyright (C) 2023 Raspberry Pi Ltd\n+ *\n+ * stitch_status.h - stitch control algorithm status\n+ */\n+#pragma once\n+\n+/*\n+ * Parameters for the stitch block.\n+ */\n+\n+struct StitchStatus {\n+\tuint16_t thresholdLo;\n+\tuint8_t diffPower;\n+\tdouble motionThreshold;\n+};\ndiff --git a/src/ipa/rpi/controller/tonemap_status.h b/src/ipa/rpi/controller/tonemap_status.h\nnew file mode 100644\nindex 000000000000..0e6399467869\n--- /dev/null\n+++ b/src/ipa/rpi/controller/tonemap_status.h\n@@ -0,0 +1,17 @@\n+/* SPDX-License-Identifier: BSD-2-Clause */\n+/*\n+ * Copyright (C) 2022 Raspberry Pi Ltd\n+ *\n+ * hdr.h - Tonemap control algorithm status\n+ */\n+#pragma once\n+\n+#include \"pwl.h\"\n+\n+struct TonemapStatus {\n+\tuint16_t detailConstant;\n+\tdouble detailSlope;\n+\tdouble iirStrength;\n+\tdouble strength;\n+\tRPiController::Pwl tonemap;\n+};\n", "prefixes": [ "libcamera-devel", "11/20" ] }