Show a patch.

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

{
    "id": 25099,
    "url": "https://patchwork.libcamera.org/api/1.1/patches/25099/?format=api",
    "web_url": "https://patchwork.libcamera.org/patch/25099/",
    "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": "<20251120104548.80268-14-mzamazal@redhat.com>",
    "date": "2025-11-20T10:45:48",
    "name": "[RFC,v2,13/13] libcamera: ipa: simple: Remove Lut algorithm",
    "commit_ref": null,
    "pull_url": null,
    "state": "superseded",
    "archived": false,
    "hash": "602f8678c62ce4e1cb72bea92f73739b92eedc7e",
    "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/25099/mbox/",
    "series": [
        {
            "id": 5597,
            "url": "https://patchwork.libcamera.org/api/1.1/series/5597/?format=api",
            "web_url": "https://patchwork.libcamera.org/project/libcamera/list/?series=5597",
            "date": "2025-11-20T10:45:35",
            "name": "Simple pipeline IPA cleanup",
            "version": 2,
            "mbox": "https://patchwork.libcamera.org/series/5597/mbox/"
        }
    ],
    "comments": "https://patchwork.libcamera.org/api/patches/25099/comments/",
    "check": "pending",
    "checks": "https://patchwork.libcamera.org/api/patches/25099/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 B5AC9C3330\n\tfor <parsemail@patchwork.libcamera.org>;\n\tThu, 20 Nov 2025 10:46:37 +0000 (UTC)",
            "from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 3B99860AAE;\n\tThu, 20 Nov 2025 11:46:37 +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 870BE60AB5\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tThu, 20 Nov 2025 11:46:33 +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-339-zDYgTBBhMAivND3Mb8DPww-1;\n\tThu, 20 Nov 2025 05:46:30 -0500",
            "from mx-prod-int-08.mail-002.prod.us-west-2.aws.redhat.com\n\t(mx-prod-int-08.mail-002.prod.us-west-2.aws.redhat.com\n\t[10.30.177.111])\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 CC7611956048; Thu, 20 Nov 2025 10:46:29 +0000 (UTC)",
            "from mzamazal-thinkpadp1gen7.tpbc.com (unknown [10.44.34.39])\n\tby mx-prod-int-08.mail-002.prod.us-west-2.aws.redhat.com (Postfix)\n\twith ESMTP id BFAC11800451; Thu, 20 Nov 2025 10:46:27 +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=\"V2PLB+u8\"; dkim-atps=neutral",
        "DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com;\n\ts=mimecast20190719; t=1763635592;\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=WEe0dQuM8uQflGfUsVvTSuaPJ9If8Vzqz7dkDyx+yQ8=;\n\tb=V2PLB+u8mJtMlq3jZjFSoozx3/3oUOg31rZMYGtnoLCapJt86Pz6E8p9THGXd/UhsBid9H\n\tmY3CI0XQE0f32oeQ8XsMGrZvSf1tksy3e+mBBQ//Vi0nY6eQJNdQiighuxqBt4eYXpGVCk\n\tOFqmPoxVMhP66GfQnDOoszUI/Chfz0Q=",
        "X-MC-Unique": "zDYgTBBhMAivND3Mb8DPww-1",
        "X-Mimecast-MFC-AGG-ID": "zDYgTBBhMAivND3Mb8DPww_1763635589",
        "From": "Milan Zamazal <mzamazal@redhat.com>",
        "To": "libcamera-devel@lists.libcamera.org",
        "Cc": "Milan Zamazal <mzamazal@redhat.com>, Kieran Bingham\n\t<kieran.bingham@ideasonboard.com>, =?utf-8?q?Barnab=C3=A1s_P=C5=91cze?=\n\t<barnabas.pocze@ideasonboard.com>",
        "Subject": "[RFC PATCH v2 13/13] libcamera: ipa: simple: Remove Lut algorithm",
        "Date": "Thu, 20 Nov 2025 11:45:48 +0100",
        "Message-ID": "<20251120104548.80268-14-mzamazal@redhat.com>",
        "In-Reply-To": "<20251120104548.80268-1-mzamazal@redhat.com>",
        "References": "<20251120104548.80268-1-mzamazal@redhat.com>",
        "MIME-Version": "1.0",
        "X-Scanned-By": "MIMEDefang 3.4.1 on 10.30.177.111",
        "X-Mimecast-Spam-Score": "0",
        "X-Mimecast-MFC-PROC-ID": "0s4j3XLeE9OKvMdoVpWKoF6XTxs7ZovdcJyiDDsSIOs_1763635589",
        "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": "The Lut algorithm is not really an algorithm.  Moreover, algorithms may\nbe enabled or disabled but with Lut disabled, nothing will work.\n\nLet's move the construction of lookup tables to debayering, where it is\nused.  The implied and related changes are:\n\n- DebayerParams is changed to contain the real params rather than lookup\n  tables.\n- The params must be initialised so that debayering gets meaningful\n  parameter values even when some algorithms are disabled.\n- combinedMatrix must be put to params everywhere where it is modified.\n- Matrix changes needn't be tracked in the algorithms any more.\n- Debayering must watch for changes of the corresponding parameters to\n  update the lookup tables when and only when needed.\n- Swapping red and blue is integrated into lookup table constructions.\n\nSigned-off-by: Milan Zamazal <mzamazal@redhat.com>\n---\n .../internal/software_isp/debayer_params.h    |  42 +----\n src/ipa/simple/algorithms/adjust.cpp          |  20 +--\n src/ipa/simple/algorithms/adjust.h            |   2 -\n src/ipa/simple/algorithms/awb.cpp             |   7 +-\n src/ipa/simple/algorithms/blc.cpp             |   8 +\n src/ipa/simple/algorithms/blc.h               |   6 +-\n src/ipa/simple/algorithms/ccm.cpp             |   4 +-\n src/ipa/simple/algorithms/lut.cpp             | 134 --------------\n src/ipa/simple/algorithms/lut.h               |  35 ----\n src/ipa/simple/algorithms/meson.build         |   1 -\n src/ipa/simple/data/uncalibrated.yaml         |   1 -\n src/ipa/simple/ipa_context.h                  |   9 -\n src/ipa/simple/soft_simple.cpp                |  10 +-\n src/libcamera/software_isp/debayer.cpp        |  71 ++------\n src/libcamera/software_isp/debayer_cpu.cpp    | 165 +++++++++++++-----\n src/libcamera/software_isp/debayer_cpu.h      |  30 +++-\n src/libcamera/software_isp/software_isp.cpp   |  17 --\n 17 files changed, 202 insertions(+), 360 deletions(-)\n delete mode 100644 src/ipa/simple/algorithms/lut.cpp\n delete mode 100644 src/ipa/simple/algorithms/lut.h",
    "diff": "diff --git a/include/libcamera/internal/software_isp/debayer_params.h b/include/libcamera/internal/software_isp/debayer_params.h\nindex 217cd5d92..e10f7f9e9 100644\n--- a/include/libcamera/internal/software_isp/debayer_params.h\n+++ b/include/libcamera/internal/software_isp/debayer_params.h\n@@ -10,45 +10,19 @@\n \n #pragma once\n \n-#include <array>\n #include <stdint.h>\n \n+#include \"libcamera/internal/matrix.h\"\n+#include \"libcamera/internal/vector.h\"\n+\n namespace libcamera {\n \n struct DebayerParams {\n-\tstatic constexpr unsigned int kRGBLookupSize = 256;\n-\n-\tstruct CcmColumn {\n-\t\tint16_t r;\n-\t\tint16_t g;\n-\t\tint16_t b;\n-\t};\n-\n-\tusing LookupTable = std::array<uint8_t, kRGBLookupSize>;\n-\tusing CcmLookupTable = std::array<CcmColumn, kRGBLookupSize>;\n-\n-\t/*\n-\t * Color lookup tables when CCM is not used.\n-\t *\n-\t * Each color of a debayered pixel is amended by the corresponding\n-\t * value in the given table.\n-\t */\n-\tLookupTable red;\n-\tLookupTable green;\n-\tLookupTable blue;\n-\n-\t/*\n-\t * Color and gamma lookup tables when CCM is used.\n-\t *\n-\t * Each of the CcmLookupTable's corresponds to a CCM column; together they\n-\t * make a complete 3x3 CCM lookup table. The CCM is applied on debayered\n-\t * pixels and then the gamma lookup table is used to set the resulting\n-\t * values of all the three colors.\n-\t */\n-\tCcmLookupTable redCcm;\n-\tCcmLookupTable greenCcm;\n-\tCcmLookupTable blueCcm;\n-\tLookupTable gammaLut;\n+\tuint8_t blackLevel;\n+\tfloat gamma;\n+\tfloat contrast;\n+\tRGB<float> gains;\n+\tMatrix<float, 3, 3> combinedMatrix;\n };\n \n } /* namespace libcamera */\ndiff --git a/src/ipa/simple/algorithms/adjust.cpp b/src/ipa/simple/algorithms/adjust.cpp\nindex 96c0bde0a..81f97feeb 100644\n--- a/src/ipa/simple/algorithms/adjust.cpp\n+++ b/src/ipa/simple/algorithms/adjust.cpp\n@@ -90,24 +90,22 @@ void Adjust::applySaturation(Matrix<float, 3, 3> &matrix, float saturation)\n }\n \n void Adjust::prepare(IPAContext &context,\n-\t\t     const uint32_t frame,\n+\t\t     [[maybe_unused]] const uint32_t frame,\n \t\t     IPAFrameContext &frameContext,\n-\t\t     [[maybe_unused]] DebayerParams *params)\n+\t\t     DebayerParams *params)\n {\n \tframeContext.contrast = context.activeState.knobs.contrast;\n \n-\tif (!context.ccmEnabled)\n-\t\treturn;\n-\n \tauto &saturation = context.activeState.knobs.saturation;\n-\tframeContext.saturation = saturation;\n-\tif (saturation)\n+\tif (context.ccmEnabled && saturation) {\n \t\tapplySaturation(context.activeState.combinedMatrix, saturation.value());\n-\n-\tif (frame == 0 || saturation != lastSaturation_) {\n-\t\tcontext.activeState.matrixChanged = true;\n-\t\tlastSaturation_ = saturation;\n+\t\tframeContext.saturation = saturation;\n \t}\n+\n+\tparams->gamma = context.activeState.knobs.gamma.value_or(kDefaultGamma);\n+\tparams->contrast =\n+\t\tcontext.activeState.knobs.contrast.value_or(kDefaultContrast);\n+\tparams->combinedMatrix = context.activeState.combinedMatrix;\n }\n \n void Adjust::process([[maybe_unused]] IPAContext &context,\ndiff --git a/src/ipa/simple/algorithms/adjust.h b/src/ipa/simple/algorithms/adjust.h\nindex 11d8297ca..02e469a50 100644\n--- a/src/ipa/simple/algorithms/adjust.h\n+++ b/src/ipa/simple/algorithms/adjust.h\n@@ -47,8 +47,6 @@ public:\n \n private:\n \tvoid applySaturation(Matrix<float, 3, 3> &ccm, float saturation);\n-\n-\tstd::optional<float> lastSaturation_;\n };\n \n } /* namespace ipa::soft::algorithms */\ndiff --git a/src/ipa/simple/algorithms/awb.cpp b/src/ipa/simple/algorithms/awb.cpp\nindex a0af8a603..b743a69cc 100644\n--- a/src/ipa/simple/algorithms/awb.cpp\n+++ b/src/ipa/simple/algorithms/awb.cpp\n@@ -37,7 +37,7 @@ int Awb::configure(IPAContext &context,\n void Awb::prepare(IPAContext &context,\n \t\t  [[maybe_unused]] const uint32_t frame,\n \t\t  IPAFrameContext &frameContext,\n-\t\t  [[maybe_unused]] DebayerParams *params)\n+\t\t  DebayerParams *params)\n {\n \tauto &gains = context.activeState.awb.gains;\n \tMatrix<float, 3, 3> gainMatrix = { { gains.r(), 0, 0,\n@@ -45,9 +45,12 @@ void Awb::prepare(IPAContext &context,\n \t\t\t\t\t     0, 0, gains.b() } };\n \tcontext.activeState.combinedMatrix =\n \t\tcontext.activeState.combinedMatrix * gainMatrix;\n-\t/* Just report, the gains are applied in LUT algorithm. */\n+\n \tframeContext.gains.red = gains.r();\n \tframeContext.gains.blue = gains.b();\n+\n+\tparams->gains = gains;\n+\tparams->combinedMatrix = context.activeState.combinedMatrix;\n }\n \n void Awb::process(IPAContext &context,\ndiff --git a/src/ipa/simple/algorithms/blc.cpp b/src/ipa/simple/algorithms/blc.cpp\nindex 370385afc..365360f47 100644\n--- a/src/ipa/simple/algorithms/blc.cpp\n+++ b/src/ipa/simple/algorithms/blc.cpp\n@@ -47,6 +47,14 @@ int BlackLevel::configure(IPAContext &context,\n \treturn 0;\n }\n \n+void BlackLevel::prepare(IPAContext &context,\n+\t\t\t [[maybe_unused]] const uint32_t frame,\n+\t\t\t [[maybe_unused]] IPAFrameContext &frameContext,\n+\t\t\t DebayerParams *params)\n+{\n+\tparams->blackLevel = context.activeState.blc.level;\n+}\n+\n void BlackLevel::process(IPAContext &context,\n \t\t\t [[maybe_unused]] const uint32_t frame,\n \t\t\t IPAFrameContext &frameContext,\ndiff --git a/src/ipa/simple/algorithms/blc.h b/src/ipa/simple/algorithms/blc.h\nindex db9e6d639..8c919452f 100644\n--- a/src/ipa/simple/algorithms/blc.h\n+++ b/src/ipa/simple/algorithms/blc.h\n@@ -1,6 +1,6 @@\n /* SPDX-License-Identifier: LGPL-2.1-or-later */\n /*\n- * Copyright (C) 2024, Red Hat Inc.\n+ * Copyright (C) 2024-2025 Red Hat Inc.\n  *\n  * Black level handling\n  */\n@@ -24,6 +24,10 @@ public:\n \n \tint init(IPAContext &context, const YamlObject &tuningData) override;\n \tint configure(IPAContext &context, const IPAConfigInfo &configInfo) override;\n+\tvoid prepare(IPAContext &context,\n+\t\t     const uint32_t frame,\n+\t\t     IPAFrameContext &frameContext,\n+\t\t     DebayerParams *params) override;\n \tvoid process(IPAContext &context, const uint32_t frame,\n \t\t     IPAFrameContext &frameContext,\n \t\t     const SwIspStats *stats,\ndiff --git a/src/ipa/simple/algorithms/ccm.cpp b/src/ipa/simple/algorithms/ccm.cpp\nindex 50bb3164f..561c6e123 100644\n--- a/src/ipa/simple/algorithms/ccm.cpp\n+++ b/src/ipa/simple/algorithms/ccm.cpp\n@@ -42,7 +42,7 @@ int Ccm::init([[maybe_unused]] IPAContext &context, const YamlObject &tuningData\n }\n \n void Ccm::prepare(IPAContext &context, const uint32_t frame,\n-\t\t  IPAFrameContext &frameContext, [[maybe_unused]] DebayerParams *params)\n+\t\t  IPAFrameContext &frameContext, DebayerParams *params)\n {\n \tconst unsigned int ct = context.activeState.awb.temperatureK;\n \n@@ -51,13 +51,13 @@ void Ccm::prepare(IPAContext &context, const uint32_t frame,\n \t    utils::abs_diff(ct, lastCt_) >= kTemperatureThreshold) {\n \t\tcurrentCcm_ = ccm_.getInterpolated(ct);\n \t\tlastCt_ = ct;\n-\t\tcontext.activeState.matrixChanged = true;\n \t}\n \n \tcontext.activeState.combinedMatrix =\n \t\tcurrentCcm_ * context.activeState.combinedMatrix;\n \tcontext.activeState.ccm = currentCcm_;\n \tframeContext.ccm = currentCcm_;\n+\tparams->combinedMatrix = context.activeState.combinedMatrix;\n }\n \n void Ccm::process([[maybe_unused]] IPAContext &context,\ndiff --git a/src/ipa/simple/algorithms/lut.cpp b/src/ipa/simple/algorithms/lut.cpp\ndeleted file mode 100644\nindex bbf7f168a..000000000\n--- a/src/ipa/simple/algorithms/lut.cpp\n+++ /dev/null\n@@ -1,134 +0,0 @@\n-/* SPDX-License-Identifier: LGPL-2.1-or-later */\n-/*\n- * Copyright (C) 2024-2025, Red Hat Inc.\n- *\n- * Color lookup tables construction\n- */\n-\n-#include \"lut.h\"\n-\n-#include <algorithm>\n-#include <cmath>\n-#include <optional>\n-#include <stdint.h>\n-\n-#include <libcamera/base/log.h>\n-\n-#include <libcamera/control_ids.h>\n-\n-#include \"simple/ipa_context.h\"\n-\n-#include \"adjust.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-\t\t   [[maybe_unused]] const IPAConfigInfo &configInfo)\n-{\n-\tupdateGammaTable(context);\n-\n-\treturn 0;\n-}\n-\n-void Lut::updateGammaTable(IPAContext &context)\n-{\n-\tauto &gammaTable = context.activeState.gamma.gammaTable;\n-\tconst auto blackLevel = context.activeState.blc.level;\n-\tconst unsigned int blackIndex = blackLevel * gammaTable.size() / 256;\n-\tconst auto gamma =\n-\t\t1.0 / context.activeState.knobs.gamma.value_or(kDefaultGamma);\n-\tconst auto contrast = context.activeState.knobs.contrast.value_or(1.0);\n-\n-\tconst float divisor = gammaTable.size() - blackIndex - 1.0;\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, gamma);\n-\t}\n-\t/*\n-\t * Due to CCM operations, the table lookup may reach indices below the black\n-\t * level. Let's set the table values below black level to the minimum\n-\t * non-black value to prevent problems when the minimum value is\n-\t * significantly non-zero (for example, when the image should be all grey).\n-\t */\n-\tstd::fill(gammaTable.begin(), gammaTable.begin() + blackIndex,\n-\t\t  gammaTable[blackIndex]);\n-\n-\tcontext.activeState.gamma.gamma = gamma;\n-\tcontext.activeState.gamma.blackLevel = blackLevel;\n-\tcontext.activeState.gamma.contrast = contrast;\n-}\n-\n-int16_t Lut::matrixValue(unsigned int i, float ccm) const\n-{\n-\treturn std::round(i * ccm);\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-\t/*\n-\t * Update the gamma table if needed. This means if black level changes\n-\t * and since the black level gets updated only if a lower value is\n-\t * observed, it's not permanently prone to minor fluctuations or\n-\t * rounding errors.\n-\t */\n-\tconst bool gammaUpdateNeeded =\n-\t\tcontext.activeState.gamma.blackLevel != context.activeState.blc.level ||\n-\t\tcontext.activeState.gamma.contrast != context.activeState.knobs.contrast;\n-\tif (gammaUpdateNeeded)\n-\t\tupdateGammaTable(context);\n-\n-\tauto &gains = context.activeState.awb.gains;\n-\tauto &gammaTable = context.activeState.gamma.gammaTable;\n-\tconst unsigned int gammaTableSize = gammaTable.size();\n-\tconst double div = static_cast<double>(DebayerParams::kRGBLookupSize) /\n-\t\t\t   gammaTableSize;\n-\n-\tif (!context.ccmEnabled) {\n-\t\tfor (unsigned int i = 0; i < DebayerParams::kRGBLookupSize; i++) {\n-\t\t\t/* Apply gamma after gain! */\n-\t\t\tconst RGB<float> lutGains = (gains * i / div).min(gammaTableSize - 1);\n-\t\t\tparams->red[i] = gammaTable[static_cast<unsigned int>(lutGains.r())];\n-\t\t\tparams->green[i] = gammaTable[static_cast<unsigned int>(lutGains.g())];\n-\t\t\tparams->blue[i] = gammaTable[static_cast<unsigned int>(lutGains.b())];\n-\t\t}\n-\t} else if (context.activeState.matrixChanged || gammaUpdateNeeded) {\n-\t\tauto &matrix = context.activeState.combinedMatrix;\n-\t\tauto &red = params->redCcm;\n-\t\tauto &green = params->greenCcm;\n-\t\tauto &blue = params->blueCcm;\n-\t\tfor (unsigned int i = 0; i < DebayerParams::kRGBLookupSize; i++) {\n-\t\t\tred[i].r = matrixValue(i, matrix[0][0]);\n-\t\t\tred[i].g = matrixValue(i, matrix[1][0]);\n-\t\t\tred[i].b = matrixValue(i, matrix[2][0]);\n-\t\t\tgreen[i].r = matrixValue(i, matrix[0][1]);\n-\t\t\tgreen[i].g = matrixValue(i, matrix[1][1]);\n-\t\t\tgreen[i].b = matrixValue(i, matrix[2][1]);\n-\t\t\tblue[i].r = matrixValue(i, matrix[0][2]);\n-\t\t\tblue[i].g = matrixValue(i, matrix[1][2]);\n-\t\t\tblue[i].b = matrixValue(i, matrix[2][2]);\n-\t\t\tparams->gammaLut[i] = gammaTable[i / div];\n-\t\t}\n-\t\tcontext.activeState.matrixChanged = false;\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\ndeleted file mode 100644\nindex ad16d1e8e..000000000\n--- a/src/ipa/simple/algorithms/lut.h\n+++ /dev/null\n@@ -1,35 +0,0 @@\n-/* SPDX-License-Identifier: LGPL-2.1-or-later */\n-/*\n- * Copyright (C) 2024, Red Hat Inc.\n- *\n- * Color lookup tables construction\n- */\n-\n-#pragma once\n-\n-#include \"algorithm.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 configure(IPAContext &context, const IPAConfigInfo &configInfo) override;\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-\tint16_t matrixValue(unsigned int i, float ccm) const;\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 ebe9f20dd..73c637220 100644\n--- a/src/ipa/simple/algorithms/meson.build\n+++ b/src/ipa/simple/algorithms/meson.build\n@@ -6,5 +6,4 @@ soft_simple_ipa_algorithms = files([\n     'agc.cpp',\n     'blc.cpp',\n     'ccm.cpp',\n-    'lut.cpp',\n ])\ndiff --git a/src/ipa/simple/data/uncalibrated.yaml b/src/ipa/simple/data/uncalibrated.yaml\nindex f0410fe61..fc90ca526 100644\n--- a/src/ipa/simple/data/uncalibrated.yaml\n+++ b/src/ipa/simple/data/uncalibrated.yaml\n@@ -15,6 +15,5 @@ algorithms:\n   #                0, 1, 0,\n   #                0, 0, 1]\n   - Adjust:\n-  - Lut:\n   - Agc:\n ...\ndiff --git a/src/ipa/simple/ipa_context.h b/src/ipa/simple/ipa_context.h\nindex fb063b091..c59e684f8 100644\n--- a/src/ipa/simple/ipa_context.h\n+++ b/src/ipa/simple/ipa_context.h\n@@ -53,17 +53,8 @@ struct IPAActiveState {\n \t\tunsigned int temperatureK;\n \t} awb;\n \n-\tstatic constexpr unsigned int kGammaLookupSize = 1024;\n-\tstruct {\n-\t\tstd::array<double, kGammaLookupSize> gammaTable;\n-\t\tuint8_t blackLevel;\n-\t\tfloat gamma;\n-\t\tfloat contrast;\n-\t} gamma;\n-\n \tMatrix<float, 3, 3> ccm;\n \tMatrix<float, 3, 3> combinedMatrix;\n-\tbool matrixChanged = false;\n \n \tstruct {\n \t\tstd::optional<float> gamma;\ndiff --git a/src/ipa/simple/soft_simple.cpp b/src/ipa/simple/soft_simple.cpp\nindex 98582587c..574b972e5 100644\n--- a/src/ipa/simple/soft_simple.cpp\n+++ b/src/ipa/simple/soft_simple.cpp\n@@ -26,6 +26,7 @@\n #include \"libcamera/internal/software_isp/swisp_stats.h\"\n #include \"libcamera/internal/yaml_parser.h\"\n \n+#include \"algorithms/adjust.h\"\n #include \"libipa/camera_sensor_helper.h\"\n \n #include \"module.h\"\n@@ -158,6 +159,11 @@ int IPASoftSimple::init(const IPASettings &settings,\n \t\t}\n \n \t\tparams_ = static_cast<DebayerParams *>(mem);\n+\t\tparams_->blackLevel = 0;\n+\t\tparams_->gamma = algorithms::kDefaultGamma;\n+\t\tparams_->contrast = algorithms::kDefaultContrast;\n+\t\tparams_->gains = { { 1.0, 1.0, 1.0 } };\n+\t\t/* combinedMatrix is reset for each frame. */\n \t}\n \n \t{\n@@ -296,7 +302,9 @@ void IPASoftSimple::queueRequest(const uint32_t frame, const ControlList &contro\n \n void IPASoftSimple::computeParams(const uint32_t frame)\n {\n-\tcontext_.activeState.combinedMatrix = Matrix<float, 3, 3>::identity();\n+\tMatrix<float, 3, 3> combinedMatrix = Matrix<float, 3, 3>::identity();\n+\tcontext_.activeState.combinedMatrix = combinedMatrix;\n+\tparams_->combinedMatrix = combinedMatrix;\n \n \tIPAFrameContext &frameContext = context_.frameContexts.get(frame);\n \tfor (auto const &algo : algorithms())\ndiff --git a/src/libcamera/software_isp/debayer.cpp b/src/libcamera/software_isp/debayer.cpp\nindex e9e18c488..298875384 100644\n--- a/src/libcamera/software_isp/debayer.cpp\n+++ b/src/libcamera/software_isp/debayer.cpp\n@@ -19,79 +19,28 @@ namespace libcamera {\n  */\n \n /**\n- * \\var DebayerParams::kRGBLookupSize\n- * \\brief Size of a color lookup table\n+ * \\var DebayerParams::blackLevel\n+ * \\brief 8-bit black level value\n  */\n \n /**\n- * \\struct DebayerParams::CcmColumn\n- * \\brief Type of a single column of a color correction matrix (CCM)\n- *\n- * When multiplying an input pixel, columns in the CCM correspond to the red,\n- * green or blue component of input pixel values, while rows correspond to the\n- * red, green or blue components of the output pixel values. The members of the\n- * CcmColumn structure are named after the colour components of the output pixel\n- * values they correspond to.\n- */\n-\n-/**\n- * \\var DebayerParams::CcmColumn::r\n- * \\brief Red (first) component of a CCM column\n- */\n-\n-/**\n- * \\var DebayerParams::CcmColumn::g\n- * \\brief Green (second) component of a CCM column\n- */\n-\n-/**\n- * \\var DebayerParams::CcmColumn::b\n- * \\brief Blue (third) component of a CCM column\n- */\n-\n-/**\n- * \\typedef DebayerParams::LookupTable\n- * \\brief Type of the lookup tables for single lookup values\n- */\n-\n-/**\n- * \\typedef DebayerParams::CcmLookupTable\n- * \\brief Type of the CCM lookup tables for red, green, blue values\n- */\n-\n-/**\n- * \\var DebayerParams::red\n- * \\brief Lookup table for red color, mapping input values to output values\n- */\n-\n-/**\n- * \\var DebayerParams::green\n- * \\brief Lookup table for green color, mapping input values to output values\n- */\n-\n-/**\n- * \\var DebayerParams::blue\n- * \\brief Lookup table for blue color, mapping input values to output values\n- */\n-\n-/**\n- * \\var DebayerParams::redCcm\n- * \\brief Lookup table for the CCM red column, mapping input values to output values\n+ * \\var DebayerParams::gamma\n+ * \\brief Gamma value\n  */\n \n /**\n- * \\var DebayerParams::greenCcm\n- * \\brief Lookup table for the CCM green column, mapping input values to output values\n+ * \\var DebayerParams::contrast\n+ * \\brief Contrast to be applied\n  */\n \n /**\n- * \\var DebayerParams::blueCcm\n- * \\brief Lookup table for the CCM blue column, mapping input values to output values\n+ * \\var DebayerParams::gains\n+ * \\brief Colour channel gains\n  */\n \n /**\n- * \\var DebayerParams::gammaLut\n- * \\brief Gamma lookup table used with color correction matrix\n+ * \\var DebayerParams::combinedMatrix\n+ * \\brief Colour correction matrix, including other adjustments\n  */\n \n /**\ndiff --git a/src/libcamera/software_isp/debayer_cpu.cpp b/src/libcamera/software_isp/debayer_cpu.cpp\nindex 67a303c65..a0e845f67 100644\n--- a/src/libcamera/software_isp/debayer_cpu.cpp\n+++ b/src/libcamera/software_isp/debayer_cpu.cpp\n@@ -64,14 +64,6 @@ DebayerCpu::DebayerCpu(std::unique_ptr<SwStatsCpu> stats, const GlobalConfigurat\n \tframesToMeasure_ = configuration.option<unsigned int>(\n \t\t\t\t\t\t{ \"software_isp\", \"measure\", \"number\" })\n \t\t\t\t   .value_or(framesToMeasure_);\n-\n-\t/* Initialize color lookup tables */\n-\tfor (unsigned int i = 0; i < DebayerParams::kRGBLookupSize; i++) {\n-\t\tred_[i] = green_[i] = blue_[i] = i;\n-\t\tredCcm_[i] = { static_cast<int16_t>(i), 0, 0 };\n-\t\tgreenCcm_[i] = { 0, static_cast<int16_t>(i), 0 };\n-\t\tblueCcm_[i] = { 0, 0, static_cast<int16_t>(i) };\n-\t}\n }\n \n DebayerCpu::~DebayerCpu() = default;\n@@ -84,21 +76,21 @@ DebayerCpu::~DebayerCpu() = default;\n #define GAMMA(value) \\\n \t*dst++ = gammaLut_[std::clamp(value, 0, static_cast<int>(gammaLut_.size()) - 1)]\n \n-#define STORE_PIXEL(b_, g_, r_)                                        \\\n-\tif constexpr (ccmEnabled) {                                    \\\n-\t\tconst DebayerParams::CcmColumn &blue = blueCcm_[b_];   \\\n-\t\tconst DebayerParams::CcmColumn &green = greenCcm_[g_]; \\\n-\t\tconst DebayerParams::CcmColumn &red = redCcm_[r_];     \\\n-\t\tGAMMA(blue.b + green.b + red.b);                       \\\n-\t\tGAMMA(blue.g + green.g + red.g);                       \\\n-\t\tGAMMA(blue.r + green.r + red.r);                       \\\n-\t} else {                                                       \\\n-\t\t*dst++ = blue_[b_];                                    \\\n-\t\t*dst++ = green_[g_];                                   \\\n-\t\t*dst++ = red_[r_];                                     \\\n-\t}                                                              \\\n-\tif constexpr (addAlphaByte)                                    \\\n-\t\t*dst++ = 255;                                          \\\n+#define STORE_PIXEL(b_, g_, r_)                         \\\n+\tif constexpr (ccmEnabled) {                     \\\n+\t\tconst CcmColumn &blue = blueCcm_[b_];   \\\n+\t\tconst CcmColumn &green = greenCcm_[g_]; \\\n+\t\tconst CcmColumn &red = redCcm_[r_];     \\\n+\t\tGAMMA(blue.b + green.b + red.b);        \\\n+\t\tGAMMA(blue.g + green.g + red.g);        \\\n+\t\tGAMMA(blue.r + green.r + red.r);        \\\n+\t} else {                                        \\\n+\t\t*dst++ = blue_[b_];                     \\\n+\t\t*dst++ = green_[g_];                    \\\n+\t\t*dst++ = red_[r_];                      \\\n+\t}                                               \\\n+\tif constexpr (addAlphaByte)                     \\\n+\t\t*dst++ = 255;                           \\\n \tx++;\n \n /*\n@@ -547,6 +539,16 @@ int DebayerCpu::configure(const StreamConfiguration &inputCfg,\n \tif (ret != 0)\n \t\treturn -EINVAL;\n \n+\tccmEnabled_ = ccmEnabled;\n+\n+\t/*\n+\t * Lookup tables must be initialized because the initial value is used for\n+\t * the first two frames, i.e. until stats processing starts providing its\n+\t * own parameters. Let's enforce recomputing lookup tables by setting the\n+\t * stored last used gamma to an out-of-range value.\n+\t */\n+\tparams_.gamma = 0.0;\n+\n \twindow_.x = ((inputCfg.size.width - outputCfg.size.width) / 2) &\n \t\t    ~(inputConfig_.patternSize.width - 1);\n \twindow_.y = ((inputCfg.size.height - outputCfg.size.height) / 2) &\n@@ -775,6 +777,103 @@ inline int64_t timeDiff(timespec &after, timespec &before)\n \n } /* namespace */\n \n+void DebayerCpu::updateGammaTable(DebayerParams &params)\n+{\n+\tconst uint8_t blackLevel = params.blackLevel;\n+\tconst unsigned int blackIndex = blackLevel * gammaTable_.size() / 256;\n+\tconst double gamma = 1.0 / params.gamma;\n+\tconst double contrast = params.contrast;\n+\n+\tconst float divisor = gammaTable_.size() - blackIndex - 1.0;\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\t std::pow(normalized, gamma);\n+\t}\n+\t/*\n+\t * Due to CCM operations, the table lookup may reach indices below the black\n+\t * level. Let's set the table values below black level to the minimum\n+\t * non-black value to prevent problems when the minimum value is\n+\t * significantly non-zero (for example, when the image should be all grey).\n+\t */\n+\tstd::fill(gammaTable_.begin(), gammaTable_.begin() + blackIndex,\n+\t\t  gammaTable_[blackIndex]);\n+}\n+\n+void DebayerCpu::updateLookupTables(DebayerParams &params)\n+{\n+\tconst bool gammaUpdateNeeded =\n+\t\tparams.gamma != params_.gamma ||\n+\t\tparams.blackLevel != params_.blackLevel ||\n+\t\tparams.contrast != params_.contrast;\n+\tif (gammaUpdateNeeded)\n+\t\tupdateGammaTable(params);\n+\n+\tauto matrixChanged = [](Matrix<float, 3, 3> m1, Matrix<float, 3, 3> m2) -> bool {\n+\t\tfor (unsigned int i = 0; i < 3; i++)\n+\t\t\tfor (unsigned int j = 0; j < 3; j++)\n+\t\t\t\tif (m1[i][j] != m2[i][j])\n+\t\t\t\t\treturn true;\n+\t\treturn false;\n+\t};\n+\n+\tconst unsigned int gammaTableSize = gammaTable_.size();\n+\tconst double div = static_cast<double>(kRGBLookupSize) / gammaTableSize;\n+\tif (ccmEnabled_) {\n+\t\tif (gammaUpdateNeeded ||\n+\t\t    matrixChanged(params.combinedMatrix, params_.combinedMatrix)) {\n+\t\t\tauto &red = swapRedBlueGains_ ? blueCcm_ : redCcm_;\n+\t\t\tauto &green = greenCcm_;\n+\t\t\tauto &blue = swapRedBlueGains_ ? redCcm_ : blueCcm_;\n+\t\t\tconst unsigned int redIndex = swapRedBlueGains_ ? 2 : 0;\n+\t\t\tconst unsigned int greenIndex = 1;\n+\t\t\tconst unsigned int blueIndex = swapRedBlueGains_ ? 0 : 2;\n+\t\t\tfor (unsigned int i = 0; i < kRGBLookupSize; i++) {\n+\t\t\t\tred[i].r = std::round(i * params.combinedMatrix[redIndex][0]);\n+\t\t\t\tred[i].g = std::round(i * params.combinedMatrix[greenIndex][0]);\n+\t\t\t\tred[i].b = std::round(i * params.combinedMatrix[blueIndex][0]);\n+\t\t\t\tgreen[i].r = std::round(i * params.combinedMatrix[redIndex][1]);\n+\t\t\t\tgreen[i].g = std::round(i * params.combinedMatrix[greenIndex][1]);\n+\t\t\t\tgreen[i].b = std::round(i * params.combinedMatrix[blueIndex][1]);\n+\t\t\t\tblue[i].r = std::round(i * params.combinedMatrix[redIndex][2]);\n+\t\t\t\tblue[i].g = std::round(i * params.combinedMatrix[greenIndex][2]);\n+\t\t\t\tblue[i].b = std::round(i * params.combinedMatrix[blueIndex][2]);\n+\t\t\t\tgammaLut_[i] = gammaTable_[i / div];\n+\t\t\t}\n+\t\t}\n+\t} else {\n+\t\tif (gammaUpdateNeeded || params.gains != params_.gains) {\n+\t\t\tauto &gains = params.gains;\n+\t\t\tauto &red = swapRedBlueGains_ ? blue_ : red_;\n+\t\t\tauto &green = green_;\n+\t\t\tauto &blue = swapRedBlueGains_ ? red_ : blue_;\n+\t\t\tfor (unsigned int i = 0; i < kRGBLookupSize; i++) {\n+\t\t\t\t/* Apply gamma after gain! */\n+\t\t\t\tconst RGB<float> lutGains = (gains * i / div).min(gammaTableSize - 1);\n+\t\t\t\tred[i] = gammaTable_[static_cast<unsigned int>(lutGains.r())];\n+\t\t\t\tgreen[i] = gammaTable_[static_cast<unsigned int>(lutGains.g())];\n+\t\t\t\tblue[i] = gammaTable_[static_cast<unsigned int>(lutGains.b())];\n+\t\t\t}\n+\t\t}\n+\t}\n+\n+\tLOG(Debayer, Debug)\n+\t\t<< \"Debayer parameters: blackLevel=\" << static_cast<unsigned int>(params.blackLevel)\n+\t\t<< \"; gamma=\" << params.gamma\n+\t\t<< \"; contrast=\" << params.contrast\n+\t\t<< \"; gains=\" << params.gains\n+\t\t<< \"; matrix=\" << params.combinedMatrix;\n+\n+\tparams_ = params;\n+}\n+\n void DebayerCpu::process(uint32_t frame, FrameBuffer *input, FrameBuffer *output, DebayerParams params)\n {\n \ttimespec frameStartTime;\n@@ -794,25 +893,7 @@ void DebayerCpu::process(uint32_t frame, FrameBuffer *input, FrameBuffer *output\n \tfor (const FrameBuffer::Plane &plane : output->planes())\n \t\tdmaSyncers.emplace_back(plane.fd, DmaSyncer::SyncType::Write);\n \n-\tgreen_ = params.green;\n-\tgreenCcm_ = params.greenCcm;\n-\tif (swapRedBlueGains_) {\n-\t\tred_ = params.blue;\n-\t\tblue_ = params.red;\n-\t\tredCcm_ = params.blueCcm;\n-\t\tblueCcm_ = params.redCcm;\n-\t\tfor (unsigned int i = 0; i < 256; i++) {\n-\t\t\tstd::swap(redCcm_[i].r, redCcm_[i].b);\n-\t\t\tstd::swap(greenCcm_[i].r, greenCcm_[i].b);\n-\t\t\tstd::swap(blueCcm_[i].r, blueCcm_[i].b);\n-\t\t}\n-\t} else {\n-\t\tred_ = params.red;\n-\t\tblue_ = params.blue;\n-\t\tredCcm_ = params.redCcm;\n-\t\tblueCcm_ = params.blueCcm;\n-\t}\n-\tgammaLut_ = params.gammaLut;\n+\tupdateLookupTables(params);\n \n \t/* Copy metadata from the input buffer */\n \tFrameMetadata &metadata = output->_d()->metadata();\ndiff --git a/src/libcamera/software_isp/debayer_cpu.h b/src/libcamera/software_isp/debayer_cpu.h\nindex 03e0d7843..941f08b7b 100644\n--- a/src/libcamera/software_isp/debayer_cpu.h\n+++ b/src/libcamera/software_isp/debayer_cpu.h\n@@ -19,6 +19,7 @@\n \n #include \"libcamera/internal/bayer_format.h\"\n #include \"libcamera/internal/global_configuration.h\"\n+#include \"libcamera/internal/software_isp/debayer_params.h\"\n \n #include \"debayer.h\"\n #include \"swstats_cpu.h\"\n@@ -135,17 +136,32 @@ private:\n \tvoid memcpyNextLine(const uint8_t *linePointers[]);\n \tvoid process2(uint32_t frame, const uint8_t *src, uint8_t *dst);\n \tvoid process4(uint32_t frame, const uint8_t *src, uint8_t *dst);\n+\tvoid updateGammaTable(DebayerParams &params);\n+\tvoid updateLookupTables(DebayerParams &params);\n \n \t/* Max. supported Bayer pattern height is 4, debayering this requires 5 lines */\n \tstatic constexpr unsigned int kMaxLineBuffers = 5;\n \n-\tDebayerParams::LookupTable red_;\n-\tDebayerParams::LookupTable green_;\n-\tDebayerParams::LookupTable blue_;\n-\tDebayerParams::CcmLookupTable redCcm_;\n-\tDebayerParams::CcmLookupTable greenCcm_;\n-\tDebayerParams::CcmLookupTable blueCcm_;\n-\tDebayerParams::LookupTable gammaLut_;\n+\tstatic constexpr unsigned int kRGBLookupSize = 256;\n+\tstatic constexpr unsigned int kGammaLookupSize = 1024;\n+\tstruct CcmColumn {\n+\t\tint16_t r;\n+\t\tint16_t g;\n+\t\tint16_t b;\n+\t};\n+\tusing LookupTable = std::array<uint8_t, kRGBLookupSize>;\n+\tusing CcmLookupTable = std::array<CcmColumn, kRGBLookupSize>;\n+\tLookupTable red_;\n+\tLookupTable green_;\n+\tLookupTable blue_;\n+\tCcmLookupTable redCcm_;\n+\tCcmLookupTable greenCcm_;\n+\tCcmLookupTable blueCcm_;\n+\tstd::array<double, kGammaLookupSize> gammaTable_;\n+\tLookupTable gammaLut_;\n+\tbool ccmEnabled_;\n+\tDebayerParams params_;\n+\n \tdebayerFn debayer0_;\n \tdebayerFn debayer1_;\n \tdebayerFn debayer2_;\ndiff --git a/src/libcamera/software_isp/software_isp.cpp b/src/libcamera/software_isp/software_isp.cpp\nindex 341c0352c..ad40471d3 100644\n--- a/src/libcamera/software_isp/software_isp.cpp\n+++ b/src/libcamera/software_isp/software_isp.cpp\n@@ -80,23 +80,6 @@ SoftwareIsp::SoftwareIsp(PipelineHandler *pipe, const CameraSensor *sensor,\n \t\t   DmaBufAllocator::DmaBufAllocatorFlag::SystemHeap |\n \t\t   DmaBufAllocator::DmaBufAllocatorFlag::UDmaBuf)\n {\n-\t/*\n-\t * debayerParams_ must be initialized because the initial value is used for\n-\t * the first two frames, i.e. until stats processing starts providing its\n-\t * own parameters.\n-\t *\n-\t * \\todo This should be handled in the same place as the related\n-\t * operations, in the IPA module.\n-\t */\n-\tstd::array<uint8_t, 256> gammaTable;\n-\tfor (unsigned int i = 0; i < 256; i++)\n-\t\tgammaTable[i] = UINT8_MAX * std::pow(i / 256.0, 0.5);\n-\tfor (unsigned int i = 0; i < DebayerParams::kRGBLookupSize; i++) {\n-\t\tdebayerParams_.red[i] = gammaTable[i];\n-\t\tdebayerParams_.green[i] = gammaTable[i];\n-\t\tdebayerParams_.blue[i] = gammaTable[i];\n-\t}\n-\n \tif (!dmaHeap_.isValid()) {\n \t\tLOG(SoftwareIsp, Error) << \"Failed to create DmaBufAllocator object\";\n \t\treturn;\n",
    "prefixes": [
        "RFC",
        "v2",
        "13/13"
    ]
}