Show a patch.

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

{
    "id": 24393,
    "url": "https://patchwork.libcamera.org/api/patches/24393/?format=api",
    "web_url": "https://patchwork.libcamera.org/patch/24393/",
    "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": "<20250918144333.108695-5-stefan.klug@ideasonboard.com>",
    "date": "2025-09-18T14:43:13",
    "name": "[v4,04/19] libipa: exposure_mode_helper: Take exposure/gain quantization into account",
    "commit_ref": null,
    "pull_url": null,
    "state": "accepted",
    "archived": false,
    "hash": "654d991aadadaa05be6c88a47b7d74e6985c10ef",
    "submitter": {
        "id": 184,
        "url": "https://patchwork.libcamera.org/api/people/184/?format=api",
        "name": "Stefan Klug",
        "email": "stefan.klug@ideasonboard.com"
    },
    "delegate": null,
    "mbox": "https://patchwork.libcamera.org/patch/24393/mbox/",
    "series": [
        {
            "id": 5449,
            "url": "https://patchwork.libcamera.org/api/series/5449/?format=api",
            "web_url": "https://patchwork.libcamera.org/project/libcamera/list/?series=5449",
            "date": "2025-09-18T14:43:09",
            "name": "Implement WDR algorithm",
            "version": 4,
            "mbox": "https://patchwork.libcamera.org/series/5449/mbox/"
        }
    ],
    "comments": "https://patchwork.libcamera.org/api/patches/24393/comments/",
    "check": "pending",
    "checks": "https://patchwork.libcamera.org/api/patches/24393/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 A60E3C328C\n\tfor <parsemail@patchwork.libcamera.org>;\n\tThu, 18 Sep 2025 14:44:01 +0000 (UTC)",
            "from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id D273B6937D;\n\tThu, 18 Sep 2025 16:44:00 +0200 (CEST)",
            "from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[IPv6:2001:4b98:dc2:55:216:3eff:fef7:d647])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id EAEE76936D\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tThu, 18 Sep 2025 16:43:57 +0200 (CEST)",
            "from ideasonboard.com (unknown\n\t[IPv6:2a00:6020:448c:6c00:ace3:d2c2:5eff:9cc5])\n\tby perceval.ideasonboard.com (Postfix) with UTF8SMTPSA id 4FBAE9FC;\n\tThu, 18 Sep 2025 16:42:38 +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=\"G0lZllxa\"; dkim-atps=neutral",
        "DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1758206558;\n\tbh=xDVZ9ggp9ojc3L+LlFL01S76YA2IRaSQUNBafWCExyc=;\n\th=From:To:Cc:Subject:Date:In-Reply-To:References:From;\n\tb=G0lZllxa/pjqWrgED19/hrkNBcrEY+gIX1Y0T1uCEh+ezLVaMSrx2ZFkOcYDNrwh5\n\tioSiw75gaJXnz9CjPjhE47T+2SSUJUtTaex3E8EOOf4tbt7xcFaVv+PC6o6fLdVziP\n\tyUDgxNvaLB+spPybfC3uyLSb+tETzSoCzRcR7Uko=",
        "From": "Stefan Klug <stefan.klug@ideasonboard.com>",
        "To": "libcamera-devel@lists.libcamera.org",
        "Cc": "Stefan Klug <stefan.klug@ideasonboard.com>,\n\tKieran Bingham <kieran.bingham@ideasonboard.com>,\n\tDaniel Scally <dan.scally@ideasonboard.com>",
        "Subject": "[PATCH v4 04/19] libipa: exposure_mode_helper: Take exposure/gain\n\tquantization into account",
        "Date": "Thu, 18 Sep 2025 16:43:13 +0200",
        "Message-ID": "<20250918144333.108695-5-stefan.klug@ideasonboard.com>",
        "X-Mailer": "git-send-email 2.48.1",
        "In-Reply-To": "<20250918144333.108695-1-stefan.klug@ideasonboard.com>",
        "References": "<20250918144333.108695-1-stefan.klug@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": "In ExposureModeHelper::splitExposure() the quantization of exposure time\nand gain is not taken into account. This can lead to visible flicker\nwhen the quantization steps are too big. As a preparation to fixing\nthat, add a function to set the sensor line length and the current\nsensor mode helper and extend the clampXXX functions to return the\nquantization error.\n\nBy default the exposure time quantization is assumed to be 1us and gain\nis assumed to not be quantized at all.\n\nSigned-off-by: Stefan Klug <stefan.klug@ideasonboard.com>\nReviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>\nReviewed-by: Daniel Scally <dan.scally@ideasonboard.com>\n\n---\n\nChanges in v4:\n- Improved documentation\n\nChanges in v3:\n- Collected tags\n- Renamed lineLength to lineDuration\n- Remove the \"no functional changes\" sentence, as the returned\n  exposureTime/gain are now quantized, which happened outside of this\nclass before. So that is actually a functional change.\n---\n src/ipa/libipa/exposure_mode_helper.cpp | 49 ++++++++++++++++++++-----\n src/ipa/libipa/exposure_mode_helper.h   | 10 ++++-\n 2 files changed, 48 insertions(+), 11 deletions(-)",
    "diff": "diff --git a/src/ipa/libipa/exposure_mode_helper.cpp b/src/ipa/libipa/exposure_mode_helper.cpp\nindex 0c1e99e31a47..b776a031b441 100644\n--- a/src/ipa/libipa/exposure_mode_helper.cpp\n+++ b/src/ipa/libipa/exposure_mode_helper.cpp\n@@ -70,18 +70,37 @@ namespace ipa {\n  * the runtime limits set through setLimits() instead.\n  */\n ExposureModeHelper::ExposureModeHelper(const Span<std::pair<utils::Duration, double>> stages)\n+\t: lineDuration_(1us), minExposureTime_(0us), maxExposureTime_(0us),\n+\t  minGain_(0), maxGain_(0), sensorHelper_(nullptr)\n {\n-\tminExposureTime_ = 0us;\n-\tmaxExposureTime_ = 0us;\n-\tminGain_ = 0;\n-\tmaxGain_ = 0;\n-\n \tfor (const auto &[s, g] : stages) {\n \t\texposureTimes_.push_back(s);\n \t\tgains_.push_back(g);\n \t}\n }\n \n+/**\n+ * \\brief Configure sensor details\n+ * \\param[in] lineDuration The current line length of the sensor\n+ * \\param[in] sensorHelper The sensor helper\n+ *\n+ * This function sets the line length and sensor helper. These are used in\n+ * splitExposure() to take the quantization of the exposure and gain into\n+ * account.\n+ *\n+ * When this has not been called, it is assumed that exposure is in micro second\n+ * granularity and gain has no quantization at all.\n+ *\n+ * ExposureModeHelper keeps a pointer to the CameraSensorHelper, so the caller\n+ * has to ensure that sensorHelper is valid until the next call to configure().\n+ */\n+void ExposureModeHelper::configure(utils::Duration lineDuration,\n+\t\t\t\t   const CameraSensorHelper *sensorHelper)\n+{\n+\tlineDuration_ = lineDuration;\n+\tsensorHelper_ = sensorHelper;\n+}\n+\n /**\n  * \\brief Set the exposure time and gain limits\n  * \\param[in] minExposureTime The minimum exposure time supported\n@@ -108,14 +127,26 @@ void ExposureModeHelper::setLimits(utils::Duration minExposureTime,\n \tmaxGain_ = maxGain;\n }\n \n-utils::Duration ExposureModeHelper::clampExposureTime(utils::Duration exposureTime) const\n+utils::Duration ExposureModeHelper::clampExposureTime(utils::Duration exposureTime,\n+\t\t\t\t\t\t      double *quantizationGain) const\n {\n-\treturn std::clamp(exposureTime, minExposureTime_, maxExposureTime_);\n+\tutils::Duration clamped;\n+\tutils::Duration exp;\n+\n+\tclamped = std::clamp(exposureTime, minExposureTime_, maxExposureTime_);\n+\texp = static_cast<long>(clamped / lineDuration_) * lineDuration_;\n+\tif (quantizationGain)\n+\t\t*quantizationGain = clamped / exp;\n+\n+\treturn exp;\n }\n \n-double ExposureModeHelper::clampGain(double gain) const\n+double ExposureModeHelper::clampGain(double gain, double *quantizationGain) const\n {\n-\treturn std::clamp(gain, minGain_, maxGain_);\n+\tdouble clamped = std::clamp(gain, minGain_, maxGain_);\n+\tif (!sensorHelper_)\n+\t\treturn clamped;\n+\treturn sensorHelper_->quantizeGain(clamped, quantizationGain);\n }\n \n /**\ndiff --git a/src/ipa/libipa/exposure_mode_helper.h b/src/ipa/libipa/exposure_mode_helper.h\nindex c5be1b6703a8..ac7e8da95c6c 100644\n--- a/src/ipa/libipa/exposure_mode_helper.h\n+++ b/src/ipa/libipa/exposure_mode_helper.h\n@@ -14,6 +14,8 @@\n #include <libcamera/base/span.h>\n #include <libcamera/base/utils.h>\n \n+#include \"camera_sensor_helper.h\"\n+\n namespace libcamera {\n \n namespace ipa {\n@@ -24,6 +26,7 @@ public:\n \tExposureModeHelper(const Span<std::pair<utils::Duration, double>> stages);\n \t~ExposureModeHelper() = default;\n \n+\tvoid configure(utils::Duration lineLength, const CameraSensorHelper *sensorHelper);\n \tvoid setLimits(utils::Duration minExposureTime, utils::Duration maxExposureTime,\n \t\t       double minGain, double maxGain);\n \n@@ -36,16 +39,19 @@ public:\n \tdouble maxGain() const { return maxGain_; }\n \n private:\n-\tutils::Duration clampExposureTime(utils::Duration exposureTime) const;\n-\tdouble clampGain(double gain) const;\n+\tutils::Duration clampExposureTime(utils::Duration exposureTime,\n+\t\t\t\t\t  double *quantizationGain = nullptr) const;\n+\tdouble clampGain(double gain, double *quantizationGain = nullptr) const;\n \n \tstd::vector<utils::Duration> exposureTimes_;\n \tstd::vector<double> gains_;\n \n+\tutils::Duration lineDuration_;\n \tutils::Duration minExposureTime_;\n \tutils::Duration maxExposureTime_;\n \tdouble minGain_;\n \tdouble maxGain_;\n+\tconst CameraSensorHelper *sensorHelper_;\n };\n \n } /* namespace ipa */\n",
    "prefixes": [
        "v4",
        "04/19"
    ]
}