Show a patch.

GET /api/1.1/patches/22389/?format=api
HTTP 200 OK
Allow: GET, PUT, PATCH, HEAD, OPTIONS
Content-Type: application/json
Vary: Accept

{
    "id": 22389,
    "url": "https://patchwork.libcamera.org/api/1.1/patches/22389/?format=api",
    "web_url": "https://patchwork.libcamera.org/patch/22389/",
    "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": "<20241218074601.3552093-2-paul.elder@ideasonboard.com>",
    "date": "2024-12-18T07:46:00",
    "name": "[v3,1/2] ipa: libipa: Add Lux helper",
    "commit_ref": null,
    "pull_url": null,
    "state": "accepted",
    "archived": false,
    "hash": "6e77172097cc87256156a7b5d656af0e92f0df74",
    "submitter": {
        "id": 17,
        "url": "https://patchwork.libcamera.org/api/1.1/people/17/?format=api",
        "name": "Paul Elder",
        "email": "paul.elder@ideasonboard.com"
    },
    "delegate": null,
    "mbox": "https://patchwork.libcamera.org/patch/22389/mbox/",
    "series": [
        {
            "id": 4909,
            "url": "https://patchwork.libcamera.org/api/1.1/series/4909/?format=api",
            "web_url": "https://patchwork.libcamera.org/project/libcamera/list/?series=4909",
            "date": "2024-12-18T07:45:59",
            "name": "ipa: rkisp1: Add lux estimation",
            "version": 3,
            "mbox": "https://patchwork.libcamera.org/series/4909/mbox/"
        }
    ],
    "comments": "https://patchwork.libcamera.org/api/patches/22389/comments/",
    "check": "pending",
    "checks": "https://patchwork.libcamera.org/api/patches/22389/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 2D6A4C3305\n\tfor <parsemail@patchwork.libcamera.org>;\n\tWed, 18 Dec 2024 07:46:25 +0000 (UTC)",
            "from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id BE58C6807F;\n\tWed, 18 Dec 2024 08:46:24 +0100 (CET)",
            "from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[213.167.242.64])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 95C1968085\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tWed, 18 Dec 2024 08:46:22 +0100 (CET)",
            "from neptunite.hamster-moth.ts.net (unknown\n\t[IPv6:2404:7a81:160:2100:6d4:c4ff:fe49:f0b3])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id 4459759D;\n\tWed, 18 Dec 2024 08:45:43 +0100 (CET)"
        ],
        "Authentication-Results": "lancelot.ideasonboard.com; dkim=pass (1024-bit key;\n\tunprotected) header.d=ideasonboard.com header.i=@ideasonboard.com\n\theader.b=\"r4LZpHOM\"; dkim-atps=neutral",
        "DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1734507944;\n\tbh=ywYKR4I7Ki0ObB5BaSmuAO7d86grQy4lNS3Pll7wmto=;\n\th=From:To:Cc:Subject:Date:In-Reply-To:References:From;\n\tb=r4LZpHOMcMYQPVEadJ7y6J7UoRFYP8kPdFr7pOg8uKdC83uThlwXRPPPOw/OxxZ6f\n\t+cQqohUlSLiQQ5Qbw0uXXGV9ww9Rg2jadRS9ETYPHjPXwSdc69WhOVO/dEf9m4ywGc\n\tl2L4bE4GC9E0Fyxw3iauu4jEg5uWB86O39FSlS3Y=",
        "From": "Paul Elder <paul.elder@ideasonboard.com>",
        "To": "libcamera-devel@lists.libcamera.org, laurent.pinchart@ideasonboard.com, \n\tstefan.klug@ideasonboard.com",
        "Cc": "Paul Elder <paul.elder@ideasonboard.com>",
        "Subject": "[PATCH v3 1/2] ipa: libipa: Add Lux helper",
        "Date": "Wed, 18 Dec 2024 16:46:00 +0900",
        "Message-Id": "<20241218074601.3552093-2-paul.elder@ideasonboard.com>",
        "X-Mailer": "git-send-email 2.39.2",
        "In-Reply-To": "<20241218074601.3552093-1-paul.elder@ideasonboard.com>",
        "References": "<20241218074601.3552093-1-paul.elder@ideasonboard.com>",
        "MIME-Version": "1.0",
        "Content-Transfer-Encoding": "8bit",
        "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 Lux helper to libipa that does the estimation of the lux level\ngiven gain, exposure, and luminance histogram. The helper also\nhandles reading the reference values from the tuning file. These are\nexpected to be common operations of lux algorithm modules in IPAs, and\nis modeled/copied from Raspberry Pi.\n\nSigned-off-by: Paul Elder <paul.elder@ideasonboard.com>\nReviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>\n\n---\nChanges in v3:\n- improve documentation\n- minor docs formatting fixes\n- replace setBinSize() with constructor parameter\n- s/readYaml/parseTuningData/\n- remove unnecessary includes\n\nChanges in v2:\n- improve documentation\n- add binSize member variable and corresponding setter\n- remove aperture\n- split gain into analogue and digital\n- change tuning file names into camel case\n---\n src/ipa/libipa/lux.cpp     | 181 +++++++++++++++++++++++++++++++++++++\n src/ipa/libipa/lux.h       |  42 +++++++++\n src/ipa/libipa/meson.build |   2 +\n 3 files changed, 225 insertions(+)\n create mode 100644 src/ipa/libipa/lux.cpp\n create mode 100644 src/ipa/libipa/lux.h",
    "diff": "diff --git a/src/ipa/libipa/lux.cpp b/src/ipa/libipa/lux.cpp\nnew file mode 100644\nindex 000000000000..b60060e21803\n--- /dev/null\n+++ b/src/ipa/libipa/lux.cpp\n@@ -0,0 +1,181 @@\n+/* SPDX-License-Identifier: BSD-2-Clause */\n+/*\n+ * Copyright (C) 2019, Raspberry Pi Ltd\n+ * Copyright (C) 2024, Paul Elder <paul.elder@ideasonboard.com>\n+ *\n+ * Helper class that implements lux estimation\n+ */\n+#include \"lux.h\"\n+\n+#include <algorithm>\n+#include <chrono>\n+\n+#include <libcamera/base/log.h>\n+\n+#include \"libcamera/internal/yaml_parser.h\"\n+\n+#include \"histogram.h\"\n+\n+/**\n+ * \\file lux.h\n+ * \\brief Helper class that implements lux estimation\n+ *\n+ * Estimating the lux level of an image is a common operation that can for\n+ * instance be used to adjust the target Y value in AGC or for Bayesian AWB\n+ * estimation.\n+ */\n+\n+namespace libcamera {\n+\n+using namespace std::literals::chrono_literals;\n+\n+LOG_DEFINE_CATEGORY(Lux)\n+\n+namespace ipa {\n+\n+/**\n+ * \\class Lux\n+ * \\brief Class that implements lux estimation\n+ *\n+ * IPAs that wish to use lux esimation should create a Lux algorithm module\n+ * that lightly wraps this module by providing the platform-specific luminance\n+ * histogram. The Lux entry in the tuning file must then precede the algorithms\n+ * that depend on the estimated lux value.\n+ */\n+\n+/**\n+ * \\var Lux::binSize_\n+ * \\brief The maximum count of each bin\n+ */\n+\n+/**\n+ * \\var Lux::referenceExposureTime_\n+ * \\brief The exposure time of the reference image, in microseconds\n+ */\n+\n+/**\n+ * \\var Lux::referenceAnalogueGain_\n+ * \\brief The analogue gain of the reference image\n+ */\n+\n+/**\n+ * \\var Lux::referenceDigitalGain_\n+ * \\brief The analogue gain of the reference image\n+ */\n+\n+/**\n+ * \\var Lux::referenceY_\n+ * \\brief The measured luminance of the reference image, out of the bin size\n+ *\n+ * \\sa binSize_\n+ */\n+\n+/**\n+ * \\var Lux::referenceLux_\n+ * \\brief The estimated lux level of the reference image\n+ */\n+\n+/**\n+  * \\brief Construct the Lux helper module\n+  * \\param[in] binSize The maximum count of each bin\n+  */\n+Lux::Lux(unsigned int binSize)\n+\t: binSize_(binSize)\n+{\n+}\n+\n+/**\n+ * \\brief Parse tuning data\n+ * \\param[in] tuningData The YamlObject representing the tuning data\n+ *\n+ * This function parses yaml tuning data for the common Lux module. It requires\n+ * reference exposure time, analogue gain, digital gain, and lux values.\n+ *\n+ * \\code{.unparsed}\n+ * algorithms:\n+ *   - Lux:\n+ *       referenceExposureTime: 10000\n+ *       referenceAnalogueGain: 4.0\n+ *       referenceDigitalGain: 1.0\n+ *       referenceY: 12000\n+ *       referenceLux: 1000\n+ * \\endcode\n+ *\n+ * \\return 0 on success or a negative error code\n+ */\n+int Lux::parseTuningData(const YamlObject &tuningData)\n+{\n+\tauto value = tuningData[\"referenceExposureTime\"].get<double>();\n+\tif (!value) {\n+\t\tLOG(Lux, Error) << \"Missing tuning parameter: \"\n+\t\t\t\t<< \"'referenceExposureTime'\";\n+\t\treturn -EINVAL;\n+\t}\n+\treferenceExposureTime_ = *value * 1.0us;\n+\n+\tvalue = tuningData[\"referenceAnalogueGain\"].get<double>();\n+\tif (!value) {\n+\t\tLOG(Lux, Error) << \"Missing tuning parameter: \"\n+\t\t\t\t<< \"'referenceAnalogueGain'\";\n+\t\treturn -EINVAL;\n+\t}\n+\treferenceAnalogueGain_ = *value;\n+\n+\tvalue = tuningData[\"referenceDigitalGain\"].get<double>();\n+\tif (!value) {\n+\t\tLOG(Lux, Error) << \"Missing tuning parameter: \"\n+\t\t\t\t<< \"'referenceDigitalGain'\";\n+\t\treturn -EINVAL;\n+\t}\n+\treferenceDigitalGain_ = *value;\n+\n+\tvalue = tuningData[\"referenceY\"].get<double>();\n+\tif (!value) {\n+\t\tLOG(Lux, Error) << \"Missing tuning parameter: \"\n+\t\t\t\t<< \"'referenceY'\";\n+\t\treturn -EINVAL;\n+\t}\n+\treferenceY_ = *value;\n+\n+\tvalue = tuningData[\"referenceLux\"].get<double>();\n+\tif (!value) {\n+\t\tLOG(Lux, Error) << \"Missing tuning parameter: \"\n+\t\t\t\t<< \"'referenceLux'\";\n+\t\treturn -EINVAL;\n+\t}\n+\treferenceLux_ = *value;\n+\n+\treturn 0;\n+}\n+\n+/**\n+ * \\brief Estimate lux given runtime values\n+ * \\param[in] exposureTime Exposure time applied to the frame\n+ * \\param[in] aGain Analogue gain applied to the frame\n+ * \\param[in] dGain Digital gain applied to the frame\n+ * \\param[in] yHist Histogram from the ISP statistics\n+ *\n+ * Estimate the lux given the exposure time, gain, and histogram.\n+ *\n+ * \\return Estimated lux value\n+ */\n+double Lux::estimateLux(utils::Duration exposureTime,\n+\t\t\tdouble aGain, double dGain,\n+\t\t\tconst Histogram &yHist) const\n+{\n+\tdouble currentY = yHist.interQuantileMean(0, 1);\n+\tdouble exposureTimeRatio = referenceExposureTime_ / exposureTime;\n+\tdouble aGainRatio = referenceAnalogueGain_ / aGain;\n+\tdouble dGainRatio = referenceDigitalGain_ / dGain;\n+\tdouble yRatio = currentY * (binSize_ / yHist.bins()) / referenceY_;\n+\n+\tdouble estimatedLux = exposureTimeRatio * aGainRatio * dGainRatio *\n+\t\t\t      yRatio * referenceLux_;\n+\n+\tLOG(Lux, Debug) << \"Estimated lux \" << estimatedLux;\n+\treturn estimatedLux;\n+}\n+\n+} /* namespace ipa */\n+\n+} /* namespace libcamera */\ndiff --git a/src/ipa/libipa/lux.h b/src/ipa/libipa/lux.h\nnew file mode 100644\nindex 000000000000..93ca64795803\n--- /dev/null\n+++ b/src/ipa/libipa/lux.h\n@@ -0,0 +1,42 @@\n+/* SPDX-License-Identifier: BSD-2-Clause */\n+/*\n+ * Copyright (C) 2019, Raspberry Pi Ltd\n+ * Copyright (C) 2024, Paul Elder <paul.elder@ideasonboard.com>\n+ *\n+ * Helper class that implements lux estimation\n+ */\n+\n+#pragma once\n+\n+#include <libcamera/base/utils.h>\n+\n+namespace libcamera {\n+\n+class YamlObject;\n+\n+namespace ipa {\n+\n+class Histogram;\n+\n+class Lux\n+{\n+public:\n+\tLux(unsigned int binSize);\n+\n+\tint parseTuningData(const YamlObject &tuningData);\n+\tdouble estimateLux(utils::Duration exposureTime,\n+\t\t\t   double aGain, double dGain,\n+\t\t\t   const Histogram &yHist) const;\n+\n+private:\n+\tunsigned int binSize_;\n+\tutils::Duration referenceExposureTime_;\n+\tdouble referenceAnalogueGain_;\n+\tdouble referenceDigitalGain_;\n+\tdouble referenceY_;\n+\tdouble referenceLux_;\n+};\n+\n+} /* namespace ipa */\n+\n+} /* namespace libcamera */\ndiff --git a/src/ipa/libipa/meson.build b/src/ipa/libipa/meson.build\nindex a7f16ff63079..f2b2f4be50db 100644\n--- a/src/ipa/libipa/meson.build\n+++ b/src/ipa/libipa/meson.build\n@@ -11,6 +11,7 @@ libipa_headers = files([\n     'histogram.h',\n     'interpolator.h',\n     'lsc_polynomial.h',\n+    'lux.h',\n     'module.h',\n     'pwl.h',\n     'vector.h',\n@@ -27,6 +28,7 @@ libipa_sources = files([\n     'histogram.cpp',\n     'interpolator.cpp',\n     'lsc_polynomial.cpp',\n+    'lux.cpp',\n     'module.cpp',\n     'pwl.cpp',\n     'vector.cpp',\n",
    "prefixes": [
        "v3",
        "1/2"
    ]
}