Show a patch.

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

{
    "id": 21070,
    "url": "https://patchwork.libcamera.org/api/1.1/patches/21070/?format=api",
    "web_url": "https://patchwork.libcamera.org/patch/21070/",
    "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": "<20240830072554.180672-15-mzamazal@redhat.com>",
    "date": "2024-08-30T07:25:50",
    "name": "[v5,14/18] libcamera: software_isp: Move color handling to an algorithm module",
    "commit_ref": null,
    "pull_url": null,
    "state": "superseded",
    "archived": false,
    "hash": "08d42bf4f19780108575e2f7ed3fc4fe4bc5c0be",
    "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/21070/mbox/",
    "series": [
        {
            "id": 4548,
            "url": "https://patchwork.libcamera.org/api/1.1/series/4548/?format=api",
            "web_url": "https://patchwork.libcamera.org/project/libcamera/list/?series=4548",
            "date": "2024-08-30T07:25:36",
            "name": "Software ISP refactoring",
            "version": 5,
            "mbox": "https://patchwork.libcamera.org/series/4548/mbox/"
        }
    ],
    "comments": "https://patchwork.libcamera.org/api/patches/21070/comments/",
    "check": "pending",
    "checks": "https://patchwork.libcamera.org/api/patches/21070/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 42BE2C324C\n\tfor <parsemail@patchwork.libcamera.org>;\n\tFri, 30 Aug 2024 07:26:52 +0000 (UTC)",
            "from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 9FD1E634E7;\n\tFri, 30 Aug 2024 09:26:51 +0200 (CEST)",
            "from us-smtp-delivery-124.mimecast.com\n\t(us-smtp-delivery-124.mimecast.com [170.10.133.124])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id C4B716341E\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tFri, 30 Aug 2024 09:26:47 +0200 (CEST)",
            "from mx-prod-mc-02.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-534-ccVu-sqPMGydhGAcqYjwEw-1;\n\tFri, 30 Aug 2024 03:26:42 -0400",
            "from mx-prod-int-04.mail-002.prod.us-west-2.aws.redhat.com\n\t(mx-prod-int-04.mail-002.prod.us-west-2.aws.redhat.com\n\t[10.30.177.40])\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-02.mail-002.prod.us-west-2.aws.redhat.com (Postfix)\n\twith ESMTPS id E38231955D48; Fri, 30 Aug 2024 07:26:40 +0000 (UTC)",
            "from nuthatch.redhat.com (unknown [10.45.225.65])\n\tby mx-prod-int-04.mail-002.prod.us-west-2.aws.redhat.com (Postfix)\n\twith ESMTP id 4ECCF19560AA; Fri, 30 Aug 2024 07:26: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=\"jPnqVhLU\"; dkim-atps=neutral",
        "DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com;\n\ts=mimecast20190719; t=1725002806;\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=8B202D3YmiI9zxvPcauUXitgE2tZBKhGaVNdjiB2mS8=;\n\tb=jPnqVhLU1lbKiI6PVoPQGWHzjT8HMCDXvQWfMzQsYyXYnW9VtNifJ7jzDhpL/+Kg/+4Tkq\n\t2XmeD1/QTjc/dHfQFG+K31FCC0Et64GxbBKcYRpgtsvkRUFxJWWoOTjY6VC1jlnKPzhq8e\n\tz7j77V7apQmWED9egxmEIHqxGBIEBEs=",
        "X-MC-Unique": "ccVu-sqPMGydhGAcqYjwEw-1",
        "From": "Milan Zamazal <mzamazal@redhat.com>",
        "To": "libcamera-devel@lists.libcamera.org",
        "Cc": "Milan Zamazal <mzamazal@redhat.com>,\n\tUmang Jain <umang.jain@ideasonboard.com>,\n\tLaurent Pinchart <laurent.pinchart@ideasonboard.com>,\n\tDaniel Scally <dan.scally@ideasonboard.com>",
        "Subject": "[PATCH v5 14/18] libcamera: software_isp: Move color handling to an\n\talgorithm module",
        "Date": "Fri, 30 Aug 2024 09:25:50 +0200",
        "Message-ID": "<20240830072554.180672-15-mzamazal@redhat.com>",
        "In-Reply-To": "<20240830072554.180672-1-mzamazal@redhat.com>",
        "References": "<20240830072554.180672-1-mzamazal@redhat.com>",
        "MIME-Version": "1.0",
        "X-Scanned-By": "MIMEDefang 3.0 on 10.30.177.40",
        "X-Mimecast-Spam-Score": "0",
        "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": "After black level handling has been moved to an algorithm module, white\nbalance and the construction of color tables can be moved to algorithm\nmodules too.\n\nThis time, the moved code is split between stats processing and\nparameter construction methods.  It is also split to three algorithm\nmodules:\n\n- Gamma table computation.  This is actually independent of color\n  handling.\n\n- White balance computation.\n\n- Color lookup tables construction.  While this applies the color gains\n  computed by the white balance algorithm, it is not directly related to\n  white balance.  We may want to modify the color lookup tables in\n  future according to other parameters than just gamma and white balance\n  gains.\n\nThis is the only part of the software ISP algorithms that sets the\nparameters so emitting setIspParams can be moved to prepare() method.\n\nA more natural representation of the gains (and black level) would be\nfloating point numbers.  This is not done here in order to minimize\nchanges in code movements.  It will be addressed in a followup patch.\n\nSigned-off-by: Milan Zamazal <mzamazal@redhat.com>\n---\n src/ipa/simple/algorithms/awb.cpp     | 70 +++++++++++++++++++++++++++\n src/ipa/simple/algorithms/awb.h       | 38 +++++++++++++++\n src/ipa/simple/algorithms/gamma.cpp   | 64 ++++++++++++++++++++++++\n src/ipa/simple/algorithms/gamma.h     | 42 ++++++++++++++++\n src/ipa/simple/algorithms/lut.cpp     | 57 ++++++++++++++++++++++\n src/ipa/simple/algorithms/lut.h       | 37 ++++++++++++++\n src/ipa/simple/algorithms/meson.build |  3 ++\n src/ipa/simple/data/uncalibrated.yaml |  3 ++\n src/ipa/simple/ipa_context.cpp        | 30 ++++++++++++\n src/ipa/simple/ipa_context.h          | 12 +++++\n src/ipa/simple/soft_simple.cpp        | 66 ++-----------------------\n 11 files changed, 360 insertions(+), 62 deletions(-)\n create mode 100644 src/ipa/simple/algorithms/awb.cpp\n create mode 100644 src/ipa/simple/algorithms/awb.h\n create mode 100644 src/ipa/simple/algorithms/gamma.cpp\n create mode 100644 src/ipa/simple/algorithms/gamma.h\n create mode 100644 src/ipa/simple/algorithms/lut.cpp\n create mode 100644 src/ipa/simple/algorithms/lut.h",
    "diff": "diff --git a/src/ipa/simple/algorithms/awb.cpp b/src/ipa/simple/algorithms/awb.cpp\nnew file mode 100644\nindex 00000000..4b49996a\n--- /dev/null\n+++ b/src/ipa/simple/algorithms/awb.cpp\n@@ -0,0 +1,70 @@\n+/* SPDX-License-Identifier: LGPL-2.1-or-later */\n+/*\n+ * Copyright (C) 2024, Red Hat Inc.\n+ *\n+ * Auto white balance\n+ */\n+\n+#include \"awb.h\"\n+\n+#include <numeric>\n+#include <stdint.h>\n+\n+#include <libcamera/base/log.h>\n+\n+#include \"simple/ipa_context.h\"\n+\n+namespace libcamera {\n+\n+LOG_DEFINE_CATEGORY(IPASoftAwb)\n+\n+namespace ipa::soft::algorithms {\n+\n+int Awb::init(IPAContext &context,\n+\t      [[maybe_unused]] const YamlObject &tuningData)\n+{\n+\tauto &gains = context.activeState.gains;\n+\tgains.red = gains.green = gains.blue = 256;\n+\n+\treturn 0;\n+}\n+\n+void Awb::process(IPAContext &context,\n+\t\t  [[maybe_unused]] const uint32_t frame,\n+\t\t  [[maybe_unused]] IPAFrameContext &frameContext,\n+\t\t  const SwIspStats *stats,\n+\t\t  [[maybe_unused]] ControlList &metadata)\n+{\n+\tconst SwIspStats::Histogram &histogram = stats->yHistogram;\n+\tconst uint8_t blackLevel = context.activeState.black.level;\n+\n+\t/*\n+\t * Black level must be subtracted to get the correct AWB ratios, they\n+\t * would be off if they were computed from the whole brightness range\n+\t * rather than from the sensor range.\n+\t */\n+\tconst uint64_t nPixels = std::accumulate(\n+\t\thistogram.begin(), histogram.end(), 0);\n+\tconst uint64_t offset = blackLevel * nPixels;\n+\tconst uint64_t sumR = stats->sumR_ - offset / 4;\n+\tconst uint64_t sumG = stats->sumG_ - offset / 2;\n+\tconst uint64_t sumB = stats->sumB_ - offset / 4;\n+\n+\t/*\n+\t * Calculate red and blue gains for AWB.\n+\t * Clamp max gain at 4.0, this also avoids 0 division.\n+\t * Gain: 128 = 0.5, 256 = 1.0, 512 = 2.0, etc.\n+\t */\n+\tauto &gains = context.activeState.gains;\n+\tgains.red = sumR <= sumG / 4 ? 1024 : 256 * sumG / sumR;\n+\tgains.blue = sumB <= sumG / 4 ? 1024 : 256 * sumG / sumB;\n+\t/* Green gain is fixed to 256 */\n+\n+\tLOG(IPASoftAwb, Debug) << \"gain R/B \" << gains.red << \"/\" << gains.blue;\n+}\n+\n+REGISTER_IPA_ALGORITHM(Awb, \"Awb\")\n+\n+} /* namespace ipa::soft::algorithms */\n+\n+} /* namespace libcamera */\ndiff --git a/src/ipa/simple/algorithms/awb.h b/src/ipa/simple/algorithms/awb.h\nnew file mode 100644\nindex 00000000..c0b88bec\n--- /dev/null\n+++ b/src/ipa/simple/algorithms/awb.h\n@@ -0,0 +1,38 @@\n+/* SPDX-License-Identifier: LGPL-2.1-or-later */\n+/*\n+ * Copyright (C) 2024, Red Hat Inc.\n+ *\n+ * Auto white balance\n+ */\n+\n+#pragma once\n+\n+#include <libcamera/controls.h>\n+\n+#include \"libcamera/internal/software_isp/swisp_stats.h\"\n+\n+#include \"algorithm.h\"\n+#include \"ipa_context.h\"\n+\n+namespace libcamera {\n+\n+namespace ipa::soft::algorithms {\n+\n+class Awb : public Algorithm\n+{\n+public:\n+\tAwb() = default;\n+\t~Awb() = default;\n+\n+\tint init(IPAContext &context, const YamlObject &tuningData)\n+\t\toverride;\n+\tvoid process(IPAContext &context,\n+\t\t     const uint32_t frame,\n+\t\t     IPAFrameContext &frameContext,\n+\t\t     const SwIspStats *stats,\n+\t\t     ControlList &metadata) override;\n+};\n+\n+} /* namespace ipa::soft::algorithms */\n+\n+} /* namespace libcamera */\ndiff --git a/src/ipa/simple/algorithms/gamma.cpp b/src/ipa/simple/algorithms/gamma.cpp\nnew file mode 100644\nindex 00000000..0b8ec5ee\n--- /dev/null\n+++ b/src/ipa/simple/algorithms/gamma.cpp\n@@ -0,0 +1,64 @@\n+/* SPDX-License-Identifier: LGPL-2.1-or-later */\n+/*\n+ * Copyright (C) 2024, Red Hat Inc.\n+ *\n+ * Gamma table construction\n+ */\n+\n+#include \"gamma.h\"\n+\n+#include <algorithm>\n+#include <cmath>\n+#include <stdint.h>\n+\n+#include <libcamera/base/log.h>\n+\n+#include \"simple/ipa_context.h\"\n+\n+namespace libcamera {\n+\n+namespace ipa::soft::algorithms {\n+\n+int Gamma::init(IPAContext &context,\n+\t\t[[maybe_unused]] const YamlObject &tuningData)\n+{\n+\t/* Gamma value is fixed */\n+\tcontext.configuration.gamma = 0.5;\n+\tupdateGammaTable(context);\n+\n+\treturn 0;\n+}\n+\n+void Gamma::updateGammaTable(IPAContext &context)\n+{\n+\tauto &gammaTable = context.activeState.gamma.gammaTable;\n+\tauto blackLevel = context.activeState.black.level;\n+\tconst unsigned int blackIndex =\n+\t\tblackLevel * IPAActiveState::kGammaLookupSize / 256;\n+\tstd::fill(gammaTable.begin(), gammaTable.begin() + blackIndex, 0);\n+\tconst float divisor = kGammaLookupSize - blackIndex - 1.0;\n+\tfor (unsigned int i = blackIndex; i < kGammaLookupSize; i++)\n+\t\tgammaTable[i] = UINT8_MAX * powf((i - blackIndex) / divisor,\n+\t\t\t\t\t\t context.configuration.gamma);\n+\tcontext.activeState.gamma.blackLevel = blackLevel;\n+}\n+\n+void Gamma::prepare(IPAContext &context,\n+\t\t    [[maybe_unused]] const uint32_t frame,\n+\t\t    [[maybe_unused]] IPAFrameContext &frameContext,\n+\t\t    [[maybe_unused]] DebayerParams *params)\n+{\n+\t/*\n+\t * Update the gamma table if needed. This means if black level changes and\n+\t * since the black level gets updated only if a lower value is observed,\n+\t * it's not permanently prone to minor fluctuations or rounding errors.\n+\t */\n+\tif (context.activeState.gamma.blackLevel != context.activeState.black.level)\n+\t\tupdateGammaTable(context);\n+}\n+\n+REGISTER_IPA_ALGORITHM(Gamma, \"Gamma\")\n+\n+} /* namespace ipa::soft::algorithms */\n+\n+} /* namespace libcamera */\ndiff --git a/src/ipa/simple/algorithms/gamma.h b/src/ipa/simple/algorithms/gamma.h\nnew file mode 100644\nindex 00000000..43e88e87\n--- /dev/null\n+++ b/src/ipa/simple/algorithms/gamma.h\n@@ -0,0 +1,42 @@\n+/* SPDX-License-Identifier: LGPL-2.1-or-later */\n+/*\n+ * Copyright (C) 2024, Red Hat Inc.\n+ *\n+ * Gamma table construction\n+ */\n+\n+#pragma once\n+\n+#include <libcamera/controls.h>\n+\n+#include \"libcamera/internal/software_isp/debayer_params.h\"\n+\n+#include \"algorithm.h\"\n+#include \"ipa_context.h\"\n+\n+namespace libcamera {\n+\n+namespace ipa::soft::algorithms {\n+\n+static constexpr unsigned int kGammaLookupSize = 1024;\n+\n+class Gamma : public Algorithm\n+{\n+public:\n+\tGamma() = default;\n+\t~Gamma() = default;\n+\n+\tint init(IPAContext &context, const YamlObject &tuningData)\n+\t\toverride;\n+\tvoid prepare(IPAContext &context,\n+\t\t     const uint32_t frame,\n+\t\t     IPAFrameContext &frameContext,\n+\t\t     DebayerParams *params) override;\n+\n+private:\n+\tvoid updateGammaTable(IPAContext &context);\n+};\n+\n+} /* namespace ipa::soft::algorithms */\n+\n+} /* namespace libcamera */\ndiff --git a/src/ipa/simple/algorithms/lut.cpp b/src/ipa/simple/algorithms/lut.cpp\nnew file mode 100644\nindex 00000000..748f10aa\n--- /dev/null\n+++ b/src/ipa/simple/algorithms/lut.cpp\n@@ -0,0 +1,57 @@\n+/* SPDX-License-Identifier: LGPL-2.1-or-later */\n+/*\n+ * Copyright (C) 2024, Red Hat Inc.\n+ *\n+ * Lookup table construction\n+ */\n+\n+#include \"lut.h\"\n+\n+#include <algorithm>\n+#include <stdint.h>\n+\n+#include <libcamera/base/log.h>\n+\n+#include \"simple/ipa_context.h\"\n+\n+#include \"gamma.h\"\n+\n+namespace libcamera {\n+\n+namespace ipa::soft::algorithms {\n+\n+int Lut::init(IPAContext &context,\n+\t      [[maybe_unused]] const YamlObject &tuningData)\n+{\n+\tauto &gains = context.activeState.gains;\n+\tgains.red = gains.green = gains.blue = 1.0;\n+\n+\treturn 0;\n+}\n+\n+void Lut::prepare(IPAContext &context,\n+\t\t  [[maybe_unused]] const uint32_t frame,\n+\t\t  [[maybe_unused]] IPAFrameContext &frameContext,\n+\t\t  DebayerParams *params)\n+{\n+\tauto &gains = context.activeState.gains;\n+\tauto &gammaTable = context.activeState.gamma.gammaTable;\n+\tfor (unsigned int i = 0; i < DebayerParams::kRGBLookupSize; i++) {\n+\t\tconstexpr unsigned int div =\n+\t\t\tstatic_cast<double>(DebayerParams::kRGBLookupSize) * 256 / kGammaLookupSize;\n+\t\t/* Apply gamma after gain! */\n+\t\tunsigned int idx;\n+\t\tidx = std::min({ i * gains.red / div, kGammaLookupSize - 1 });\n+\t\tparams->red[i] = gammaTable[idx];\n+\t\tidx = std::min({ i * gains.green / div, kGammaLookupSize - 1 });\n+\t\tparams->green[i] = gammaTable[idx];\n+\t\tidx = std::min({ i * gains.blue / div, kGammaLookupSize - 1 });\n+\t\tparams->blue[i] = gammaTable[idx];\n+\t}\n+}\n+\n+REGISTER_IPA_ALGORITHM(Lut, \"Lut\")\n+\n+} /* namespace ipa::soft::algorithms */\n+\n+} /* namespace libcamera */\ndiff --git a/src/ipa/simple/algorithms/lut.h b/src/ipa/simple/algorithms/lut.h\nnew file mode 100644\nindex 00000000..5b5e554e\n--- /dev/null\n+++ b/src/ipa/simple/algorithms/lut.h\n@@ -0,0 +1,37 @@\n+/* SPDX-License-Identifier: LGPL-2.1-or-later */\n+/*\n+ * Copyright (C) 2024, Red Hat Inc.\n+ *\n+ * Lookup table construction\n+ */\n+\n+#pragma once\n+\n+#include <libcamera/controls.h>\n+\n+#include \"libcamera/internal/software_isp/debayer_params.h\"\n+\n+#include \"algorithm.h\"\n+#include \"ipa_context.h\"\n+\n+namespace libcamera {\n+\n+namespace ipa::soft::algorithms {\n+\n+class Lut : public Algorithm\n+{\n+public:\n+\tLut() = default;\n+\t~Lut() = default;\n+\n+\tint init(IPAContext &context, const YamlObject &tuningData)\n+\t\toverride;\n+\tvoid prepare(IPAContext &context,\n+\t\t     const uint32_t frame,\n+\t\t     IPAFrameContext &frameContext,\n+\t\t     DebayerParams *params) override;\n+};\n+\n+} /* namespace ipa::soft::algorithms */\n+\n+} /* namespace libcamera */\ndiff --git a/src/ipa/simple/algorithms/meson.build b/src/ipa/simple/algorithms/meson.build\nindex 1f63c220..68e5aa58 100644\n--- a/src/ipa/simple/algorithms/meson.build\n+++ b/src/ipa/simple/algorithms/meson.build\n@@ -1,5 +1,8 @@\n # SPDX-License-Identifier: CC0-1.0\n \n soft_simple_ipa_algorithms = files([\n+    'awb.cpp',\n     'blc.cpp',\n+    'gamma.cpp',\n+    'lut.cpp',\n ])\ndiff --git a/src/ipa/simple/data/uncalibrated.yaml b/src/ipa/simple/data/uncalibrated.yaml\nindex e0d03d96..deaea6b1 100644\n--- a/src/ipa/simple/data/uncalibrated.yaml\n+++ b/src/ipa/simple/data/uncalibrated.yaml\n@@ -4,4 +4,7 @@\n version: 1\n algorithms:\n   - BlackLevel:\n+  - Gamma:\n+  - Awb:\n+  - Lut:\n ...\ndiff --git a/src/ipa/simple/ipa_context.cpp b/src/ipa/simple/ipa_context.cpp\nindex 268cff32..5fa492cb 100644\n--- a/src/ipa/simple/ipa_context.cpp\n+++ b/src/ipa/simple/ipa_context.cpp\n@@ -50,6 +50,11 @@ namespace libcamera::ipa::soft {\n  * \\brief The current state of IPA algorithms\n  */\n \n+/**\n+ * \\var IPASessionConfiguration::gamma\n+ * \\brief Gamma value to be used in the raw image processing\n+ */\n+\n /**\n  * \\var IPAActiveState::black\n  * \\brief Context for the Black Level algorithm\n@@ -58,4 +63,29 @@ namespace libcamera::ipa::soft {\n  * \\brief Current determined black level\n  */\n \n+/**\n+ * \\var IPAActiveState::gains\n+ * \\brief Context for gains in the Colors algorithm\n+ *\n+ * \\var IPAActiveState::gains.red\n+ * \\brief Gain of red color\n+ *\n+ * \\var IPAActiveState::gains.green\n+ * \\brief Gain of green color\n+ *\n+ * \\var IPAActiveState::gains.blue\n+ * \\brief Gain of blue color\n+ */\n+\n+/**\n+ * \\var IPAActiveState::gamma\n+ * \\brief Context for gamma in the Colors algorithm\n+ *\n+ * \\var IPAActiveState::gamma.gammaTable\n+ * \\brief Computed gamma table\n+ *\n+ * \\var IPAActiveState::gamma.blackLevel\n+ * \\brief Black level used for the gamma table computation\n+ */\n+\n } /* namespace libcamera::ipa::soft */\ndiff --git a/src/ipa/simple/ipa_context.h b/src/ipa/simple/ipa_context.h\nindex 22156569..f8b8834d 100644\n--- a/src/ipa/simple/ipa_context.h\n+++ b/src/ipa/simple/ipa_context.h\n@@ -7,6 +7,7 @@\n \n #pragma once\n \n+#include <array>\n #include <stdint.h>\n \n #include <libipa/fc_queue.h>\n@@ -16,12 +17,23 @@ namespace libcamera {\n namespace ipa::soft {\n \n struct IPASessionConfiguration {\n+\tfloat gamma;\n };\n \n struct IPAActiveState {\n \tstruct {\n \t\tuint8_t level;\n \t} black;\n+\tstruct {\n+\t\tunsigned int red;\n+\t\tunsigned int green;\n+\t\tunsigned int blue;\n+\t} gains;\n+\tstatic constexpr unsigned int kGammaLookupSize = 1024;\n+\tstruct {\n+\t\tstd::array<double, kGammaLookupSize> gammaTable;\n+\t\tuint8_t blackLevel;\n+\t} gamma;\n };\n \n struct IPAFrameContext : public FrameContext {\ndiff --git a/src/ipa/simple/soft_simple.cpp b/src/ipa/simple/soft_simple.cpp\nindex a1bd2f0c..8f01bf7d 100644\n--- a/src/ipa/simple/soft_simple.cpp\n+++ b/src/ipa/simple/soft_simple.cpp\n@@ -5,8 +5,6 @@\n  * Simple Software Image Processing Algorithm module\n  */\n \n-#include <cmath>\n-#include <numeric>\n #include <stdint.h>\n #include <sys/mman.h>\n \n@@ -93,9 +91,6 @@ private:\n \tstd::unique_ptr<CameraSensorHelper> camHelper_;\n \tControlInfoMap sensorInfoMap_;\n \n-\tstatic constexpr unsigned int kGammaLookupSize = 1024;\n-\tstd::array<uint8_t, kGammaLookupSize> gammaTable_;\n-\tint lastBlackLevel_ = -1;\n \t/* Local parameter storage */\n \tstruct IPAContext context_;\n \n@@ -283,6 +278,7 @@ void IPASoftSimple::prepare(const uint32_t frame)\n \tIPAFrameContext &frameContext = context_.frameContexts.get(frame);\n \tfor (auto const &algo : algorithms())\n \t\talgo->prepare(context_, frame, frameContext, params_);\n+\tsetIspParams.emit();\n }\n \n void IPASoftSimple::processStats(const uint32_t frame,\n@@ -300,62 +296,6 @@ void IPASoftSimple::processStats(const uint32_t frame,\n \tfor (auto const &algo : algorithms())\n \t\talgo->process(context_, frame, frameContext, stats_, metadata);\n \n-\tSwIspStats::Histogram histogram = stats_->yHistogram;\n-\tconst uint8_t blackLevel = context_.activeState.black.level;\n-\n-\t/*\n-\t * Black level must be subtracted to get the correct AWB ratios, they\n-\t * would be off if they were computed from the whole brightness range\n-\t * rather than from the sensor range.\n-\t */\n-\tconst uint64_t nPixels = std::accumulate(\n-\t\thistogram.begin(), histogram.end(), 0);\n-\tconst uint64_t offset = blackLevel * nPixels;\n-\tconst uint64_t sumR = stats_->sumR_ - offset / 4;\n-\tconst uint64_t sumG = stats_->sumG_ - offset / 2;\n-\tconst uint64_t sumB = stats_->sumB_ - offset / 4;\n-\n-\t/*\n-\t * Calculate red and blue gains for AWB.\n-\t * Clamp max gain at 4.0, this also avoids 0 division.\n-\t * Gain: 128 = 0.5, 256 = 1.0, 512 = 2.0, etc.\n-\t */\n-\tconst unsigned int gainR = sumR <= sumG / 4 ? 1024 : 256 * sumG / sumR;\n-\tconst unsigned int gainB = sumB <= sumG / 4 ? 1024 : 256 * sumG / sumB;\n-\t/* Green gain and gamma values are fixed */\n-\tconstexpr unsigned int gainG = 256;\n-\n-\t/* Update the gamma table if needed */\n-\tif (blackLevel != lastBlackLevel_) {\n-\t\tconstexpr float gamma = 0.5;\n-\t\tconst unsigned int blackIndex = blackLevel * kGammaLookupSize / 256;\n-\t\tstd::fill(gammaTable_.begin(), gammaTable_.begin() + blackIndex, 0);\n-\t\tconst float divisor = kGammaLookupSize - blackIndex - 1.0;\n-\t\tfor (unsigned int i = blackIndex; i < kGammaLookupSize; i++)\n-\t\t\tgammaTable_[i] = UINT8_MAX *\n-\t\t\t\t\t std::pow((i - blackIndex) / divisor, gamma);\n-\n-\t\tlastBlackLevel_ = blackLevel;\n-\t}\n-\n-\tfor (unsigned int i = 0; i < DebayerParams::kRGBLookupSize; i++) {\n-\t\tconstexpr unsigned int div =\n-\t\t\tDebayerParams::kRGBLookupSize * 256 / kGammaLookupSize;\n-\t\tunsigned int idx;\n-\n-\t\t/* Apply gamma after gain! */\n-\t\tidx = std::min({ i * gainR / div, (kGammaLookupSize - 1) });\n-\t\tparams_->red[i] = gammaTable_[idx];\n-\n-\t\tidx = std::min({ i * gainG / div, (kGammaLookupSize - 1) });\n-\t\tparams_->green[i] = gammaTable_[idx];\n-\n-\t\tidx = std::min({ i * gainB / div, (kGammaLookupSize - 1) });\n-\t\tparams_->blue[i] = gammaTable_[idx];\n-\t}\n-\n-\tsetIspParams.emit();\n-\n \t/* \\todo Switch to the libipa/algorithm.h API someday. */\n \n \t/*\n@@ -372,6 +312,7 @@ void IPASoftSimple::processStats(const uint32_t frame,\n \t * Calculate Mean Sample Value (MSV) according to formula from:\n \t * https://www.araa.asn.au/acra/acra2007/papers/paper84final.pdf\n \t */\n+\tconst uint8_t blackLevel = context_.activeState.black.level;\n \tconst unsigned int blackLevelHistIdx =\n \t\tblackLevel / (256 / SwIspStats::kYHistogramSize);\n \tconst unsigned int histogramSize =\n@@ -421,7 +362,8 @@ void IPASoftSimple::processStats(const uint32_t frame,\n \n \tLOG(IPASoft, Debug) << \"exposureMSV \" << exposureMSV\n \t\t\t    << \" exp \" << exposure_ << \" again \" << again_\n-\t\t\t    << \" gain R/B \" << gainR << \"/\" << gainB\n+\t\t\t    << \" gain R/B \" << context_.activeState.gains.red\n+\t\t\t    << \"/\" << context_.activeState.gains.blue\n \t\t\t    << \" black level \" << static_cast<unsigned int>(blackLevel);\n }\n \n",
    "prefixes": [
        "v5",
        "14/18"
    ]
}