Show a patch.

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

{
    "id": 22130,
    "url": "https://patchwork.libcamera.org/api/1.1/patches/22130/?format=api",
    "web_url": "https://patchwork.libcamera.org/patch/22130/",
    "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": "<20241128125226.683249-4-mzamazal@redhat.com>",
    "date": "2024-11-28T12:52:24",
    "name": "[v5,3/4] libcamera: software_isp: Add support for contrast control",
    "commit_ref": "9135aacff1fa8aa6af1a44493c6ec8abfbde31a3",
    "pull_url": null,
    "state": "accepted",
    "archived": false,
    "hash": "4ce39a27550ee7cacfd0e4c4a6c014e95e067231",
    "submitter": {
        "id": 177,
        "url": "https://patchwork.libcamera.org/api/1.1/people/177/?format=api",
        "name": "Milan Zamazal",
        "email": "mzamazal@redhat.com"
    },
    "delegate": null,
    "mbox": "https://patchwork.libcamera.org/patch/22130/mbox/",
    "series": [
        {
            "id": 4831,
            "url": "https://patchwork.libcamera.org/api/1.1/series/4831/?format=api",
            "web_url": "https://patchwork.libcamera.org/project/libcamera/list/?series=4831",
            "date": "2024-11-28T12:52:21",
            "name": "Add contrast control to software ISP",
            "version": 5,
            "mbox": "https://patchwork.libcamera.org/series/4831/mbox/"
        }
    ],
    "comments": "https://patchwork.libcamera.org/api/patches/22130/comments/",
    "check": "pending",
    "checks": "https://patchwork.libcamera.org/api/patches/22130/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 04E2ABD78E\n\tfor <parsemail@patchwork.libcamera.org>;\n\tThu, 28 Nov 2024 12:52:49 +0000 (UTC)",
            "from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id AE06D65F78;\n\tThu, 28 Nov 2024 13:52:48 +0100 (CET)",
            "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 E1F1C65F6B\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tThu, 28 Nov 2024 13:52:46 +0100 (CET)",
            "from mx-prod-mc-03.mail-002.prod.us-west-2.aws.redhat.com\n\t(ec2-54-186-198-63.us-west-2.compute.amazonaws.com [54.186.198.63])\n\tby relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3,\n\tcipher=TLS_AES_256_GCM_SHA384) id us-mta-121-RJlQERaWN3a6eiW-vHHV_w-1;\n\tThu, 28 Nov 2024 07:52:42 -0500",
            "from mx-prod-int-03.mail-002.prod.us-west-2.aws.redhat.com\n\t(mx-prod-int-03.mail-002.prod.us-west-2.aws.redhat.com\n\t[10.30.177.12])\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-03.mail-002.prod.us-west-2.aws.redhat.com (Postfix)\n\twith ESMTPS id 6AB191955D93; Thu, 28 Nov 2024 12:52:41 +0000 (UTC)",
            "from nuthatch.redhat.com (unknown [10.45.225.146])\n\tby mx-prod-int-03.mail-002.prod.us-west-2.aws.redhat.com (Postfix)\n\twith ESMTP id 3E8C91955D45; Thu, 28 Nov 2024 12:52:38 +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=\"PoSZxC4I\"; dkim-atps=neutral",
        "DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com;\n\ts=mimecast20190719; t=1732798365;\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=uTkw9PWyTPx5Ng8ADEbF/wxDXHCVkNyvdHIk1SnZRh4=;\n\tb=PoSZxC4IrLR8DAE41zPrRJ3PYONPL9UAcNAsYG0nr3l2/kvDmSVcTtpoo//VtaSzCKtTCb\n\th8deN+9AT/0Hj9B/8iPWptAWdYkpj+1mfayQ7Wl92Q5PIWS7MKHXSgnvjp8qwYIMDWYExV\n\tUknTg5n607vSHEHRwezZOlikRsutd7E=",
        "X-MC-Unique": "RJlQERaWN3a6eiW-vHHV_w-1",
        "X-Mimecast-MFC-AGG-ID": "RJlQERaWN3a6eiW-vHHV_w",
        "From": "Milan Zamazal <mzamazal@redhat.com>",
        "To": "libcamera-devel@lists.libcamera.org",
        "Cc": "Milan Zamazal <mzamazal@redhat.com>,\n\tKieran Bingham <kieran.bingham@ideasonboard.com>,\n\tLaurent Pinchart <laurent.pinchart@ideasonboard.com>,\n\tStefan Klug <stefan.klug@ideasonboard.com>",
        "Subject": "[PATCH v5 3/4] libcamera: software_isp: Add support for contrast\n\tcontrol",
        "Date": "Thu, 28 Nov 2024 13:52:24 +0100",
        "Message-ID": "<20241128125226.683249-4-mzamazal@redhat.com>",
        "In-Reply-To": "<20241128125226.683249-1-mzamazal@redhat.com>",
        "References": "<20241128125226.683249-1-mzamazal@redhat.com>",
        "MIME-Version": "1.0",
        "X-Scanned-By": "MIMEDefang 3.0 on 10.30.177.12",
        "X-Mimecast-Spam-Score": "0",
        "X-Mimecast-MFC-PROC-ID": "c-TQjBbsOfshynd7qQ3LACwLiHJAzjamTZCn4arkzbk_1732798361",
        "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": "Software ISP is currently fully automatic and doesn't allow image\nmodifications by explicitly set control values.  The user has no means\nto make the image looking better.\n\nThis patch introduces support for contrast control, which can improve\ne.g. a flat looking image.  Based on the provided contrast value, it\napplies a simple S-curve modification to the image.\n\nThe contrast algorithm just handles the provided values, while the\nS-curve is applied in the gamma algorithm on the computed gamma curve\nwhenever the contrast value changes.  Since the algorithm is applied\nonly on the lookup table already present, its overhead is negligible.\n\nThe contrast value range is 0..2 and corresponds to the whole range from\na completely flat contrast to an infinite contrast, 1.0 being the normal\nvalue.  This makes the user visible range intuitive and easy to use in\nGUI sliders, while complying with Contrast control definition.  There is\nno unified range in the hardware pipelines, for example rkisp1 uses\n0..1.993 range while rpi uses 0..10 range.\n\nThis is a preparation patch without actually providing the control\nitself, which is done in the following patch.\n\nSigned-off-by: Milan Zamazal <mzamazal@redhat.com>\n---\n src/ipa/simple/algorithms/lut.cpp | 40 +++++++++++++++++++++++++++----\n src/ipa/simple/algorithms/lut.h   |  5 ++++\n src/ipa/simple/ipa_context.h      |  7 ++++++\n 3 files changed, 47 insertions(+), 5 deletions(-)",
    "diff": "diff --git a/src/ipa/simple/algorithms/lut.cpp b/src/ipa/simple/algorithms/lut.cpp\nindex 9744e773..dd76e117 100644\n--- a/src/ipa/simple/algorithms/lut.cpp\n+++ b/src/ipa/simple/algorithms/lut.cpp\n@@ -9,14 +9,19 @@\n \n #include <algorithm>\n #include <cmath>\n+#include <optional>\n #include <stdint.h>\n \n #include <libcamera/base/log.h>\n \n #include \"simple/ipa_context.h\"\n \n+#include \"control_ids.h\"\n+\n namespace libcamera {\n \n+LOG_DEFINE_CATEGORY(IPASoftLut)\n+\n namespace ipa::soft::algorithms {\n \n int Lut::configure(IPAContext &context,\n@@ -24,24 +29,48 @@ int Lut::configure(IPAContext &context,\n {\n \t/* Gamma value is fixed */\n \tcontext.configuration.gamma = 0.5;\n+\tcontext.activeState.knobs.contrast = std::optional<double>();\n \tupdateGammaTable(context);\n \n \treturn 0;\n }\n \n+void Lut::queueRequest(typename Module::Context &context,\n+\t\t       [[maybe_unused]] const uint32_t frame,\n+\t\t       [[maybe_unused]] typename Module::FrameContext &frameContext,\n+\t\t       const ControlList &controls)\n+{\n+\tconst auto &contrast = controls.get(controls::Contrast);\n+\tif (contrast.has_value()) {\n+\t\tcontext.activeState.knobs.contrast = contrast;\n+\t\tLOG(IPASoftLut, Debug) << \"Setting contrast to \" << contrast.value();\n+\t}\n+}\n+\n void Lut::updateGammaTable(IPAContext &context)\n {\n \tauto &gammaTable = context.activeState.gamma.gammaTable;\n-\tauto blackLevel = context.activeState.blc.level;\n+\tconst auto blackLevel = context.activeState.blc.level;\n \tconst unsigned int blackIndex = blackLevel * gammaTable.size() / 256;\n+\tconst auto contrast = context.activeState.knobs.contrast.value_or(1.0);\n \n \tstd::fill(gammaTable.begin(), gammaTable.begin() + blackIndex, 0);\n \tconst float divisor = gammaTable.size() - blackIndex - 1.0;\n-\tfor (unsigned int i = blackIndex; i < gammaTable.size(); i++)\n-\t\tgammaTable[i] = UINT8_MAX * std::pow((i - blackIndex) / divisor,\n-\t\t\t\t\t\t     context.configuration.gamma);\n+\tfor (unsigned int i = blackIndex; i < gammaTable.size(); i++) {\n+\t\tdouble normalized = (i - blackIndex) / divisor;\n+\t\t/* Convert 0..2 to 0..infinity; avoid actual inifinity at tan(pi/2) */\n+\t\tdouble contrastExp = tan(std::clamp(contrast * M_PI_4, 0.0, M_PI_2 - 0.00001));\n+\t\t/* Apply simple S-curve */\n+\t\tif (normalized < 0.5)\n+\t\t\tnormalized = 0.5 * std::pow(normalized / 0.5, contrastExp);\n+\t\telse\n+\t\t\tnormalized = 1.0 - 0.5 * std::pow((1.0 - normalized) / 0.5, contrastExp);\n+\t\tgammaTable[i] = UINT8_MAX *\n+\t\t\t\tstd::pow(normalized, context.configuration.gamma);\n+\t}\n \n \tcontext.activeState.gamma.blackLevel = blackLevel;\n+\tcontext.activeState.gamma.contrast = contrast;\n }\n \n void Lut::prepare(IPAContext &context,\n@@ -55,7 +84,8 @@ void Lut::prepare(IPAContext &context,\n \t * observed, it's not permanently prone to minor fluctuations or\n \t * rounding errors.\n \t */\n-\tif (context.activeState.gamma.blackLevel != context.activeState.blc.level)\n+\tif (context.activeState.gamma.blackLevel != context.activeState.blc.level ||\n+\t    context.activeState.gamma.contrast != context.activeState.knobs.contrast)\n \t\tupdateGammaTable(context);\n \n \tauto &gains = context.activeState.gains;\ndiff --git a/src/ipa/simple/algorithms/lut.h b/src/ipa/simple/algorithms/lut.h\nindex b635987d..ef2df147 100644\n--- a/src/ipa/simple/algorithms/lut.h\n+++ b/src/ipa/simple/algorithms/lut.h\n@@ -20,6 +20,11 @@ public:\n \t~Lut() = default;\n \n \tint configure(IPAContext &context, const IPAConfigInfo &configInfo) override;\n+\tvoid queueRequest(typename Module::Context &context,\n+\t\t\t  const uint32_t frame,\n+\t\t\t  typename Module::FrameContext &frameContext,\n+\t\t\t  const ControlList &controls)\n+\t\toverride;\n \tvoid prepare(IPAContext &context,\n \t\t     const uint32_t frame,\n \t\t     IPAFrameContext &frameContext,\ndiff --git a/src/ipa/simple/ipa_context.h b/src/ipa/simple/ipa_context.h\nindex fd7343e9..0c2f7021 100644\n--- a/src/ipa/simple/ipa_context.h\n+++ b/src/ipa/simple/ipa_context.h\n@@ -11,6 +11,8 @@\n #include <optional>\n #include <stdint.h>\n \n+#include <libcamera/controls.h>\n+\n #include <libipa/fc_queue.h>\n \n namespace libcamera {\n@@ -48,7 +50,12 @@ struct IPAActiveState {\n \tstruct {\n \t\tstd::array<double, kGammaLookupSize> gammaTable;\n \t\tuint8_t blackLevel;\n+\t\tdouble contrast;\n \t} gamma;\n+\tstruct {\n+\t\t/* 0..2 range, 1.0 = normal */\n+\t\tstd::optional<double> contrast;\n+\t} knobs;\n };\n \n struct IPAFrameContext : public FrameContext {\n",
    "prefixes": [
        "v5",
        "3/4"
    ]
}