Patch Detail
Show a patch.
GET /api/patches/24046/?format=api
{ "id": 24046, "url": "https://patchwork.libcamera.org/api/patches/24046/?format=api", "web_url": "https://patchwork.libcamera.org/patch/24046/", "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": "<20250804144804.16965-1-david.plowman@raspberrypi.com>", "date": "2025-08-04T14:48:04", "name": "[v2] ipa: rpi: ccm: Implement \"manual\" CCM mode", "commit_ref": null, "pull_url": null, "state": "accepted", "archived": false, "hash": "f96f99bd4f02d4b56d82a369172c0b93294c960c", "submitter": { "id": 42, "url": "https://patchwork.libcamera.org/api/people/42/?format=api", "name": "David Plowman", "email": "david.plowman@raspberrypi.com" }, "delegate": null, "mbox": "https://patchwork.libcamera.org/patch/24046/mbox/", "series": [ { "id": 5354, "url": "https://patchwork.libcamera.org/api/series/5354/?format=api", "web_url": "https://patchwork.libcamera.org/project/libcamera/list/?series=5354", "date": "2025-08-04T14:48:04", "name": "[v2] ipa: rpi: ccm: Implement \"manual\" CCM mode", "version": 2, "mbox": "https://patchwork.libcamera.org/series/5354/mbox/" } ], "comments": "https://patchwork.libcamera.org/api/patches/24046/comments/", "check": "pending", "checks": "https://patchwork.libcamera.org/api/patches/24046/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 CCE49BDCC1\n\tfor <parsemail@patchwork.libcamera.org>;\n\tMon, 4 Aug 2025 14:48:10 +0000 (UTC)", "from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id DCB866921D;\n\tMon, 4 Aug 2025 16:48:09 +0200 (CEST)", "from mail-wm1-x336.google.com (mail-wm1-x336.google.com\n\t[IPv6:2a00:1450:4864:20::336])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 2B42F6920D\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tMon, 4 Aug 2025 16:48:08 +0200 (CEST)", "by mail-wm1-x336.google.com with SMTP id\n\t5b1f17b1804b1-459d62184c9so8330215e9.1\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tMon, 04 Aug 2025 07:48:08 -0700 (PDT)", "from raspberrypi.pitowers.org\n\t([2a00:1098:3142:1f:ffc9:aff6:7f7f:893b])\n\tby smtp.gmail.com with ESMTPSA id\n\tffacd0b85a97d-3b79c3abf33sm15816078f8f.7.2025.08.04.07.48.06\n\t(version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256);\n\tMon, 04 Aug 2025 07:48:06 -0700 (PDT)" ], "Authentication-Results": "lancelot.ideasonboard.com; dkim=pass (2048-bit key;\n\tunprotected) header.d=raspberrypi.com header.i=@raspberrypi.com\n\theader.b=\"sSrHV5Rk\"; dkim-atps=neutral", "DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/relaxed;\n\td=raspberrypi.com; s=google; t=1754318887; x=1754923687;\n\tdarn=lists.libcamera.org; \n\th=content-transfer-encoding:mime-version:message-id:date:subject:cc\n\t:to:from:from:to:cc:subject:date:message-id:reply-to;\n\tbh=xt6eCgrJgA37PRqJWve1gvQQlzRL6jql6nFUe3pl1qE=;\n\tb=sSrHV5RkXYM/MoIHlHl7kvqPmszfCibYPS0Ll/2CsPjd69iq3qZ4SILiSD0QYcXEhf\n\tk+WOALwt2CHe9Uo4Cbn5pf5O9knzk2h6sOFM+JMR/QUvXV9D3oWa7t4jpbHrsLMiSd9y\n\t+s7fP7Q4Zt17hNSHZ4EyWbQlNOf3GKWuLTei+jco21VDTkYd/1j1oWKXpJZt18f7XMpR\n\tbb1Opplkqd5uZ55fmBS/sJdVgVTFuw5L9t+xKFDD7IQr+qQNqyLvxKBy0t224vH97kP5\n\tik4kJElFOgKtociELzCbdHgFOuzT/AOoVCKSVZnV87K56pPKy+RIYTx6vJWnQm4d6AAd\n\thPfA==", "X-Google-DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/relaxed;\n\td=1e100.net; s=20230601; t=1754318887; x=1754923687;\n\th=content-transfer-encoding:mime-version:message-id:date:subject:cc\n\t:to:from:x-gm-message-state:from:to:cc:subject:date:message-id\n\t:reply-to;\n\tbh=xt6eCgrJgA37PRqJWve1gvQQlzRL6jql6nFUe3pl1qE=;\n\tb=EvEMbhshWbtvc4V8U5/yO7IspKmwKr10VCAZSP6K5kTZrRrqh0tNZ7Iig+Ha5fs5D0\n\tX3AanN90/lLM/4DEm4JH89vbmouoi+qOlj7Rs6sVIegZOXW1IUxER5GKshS2NOJr+nlB\n\tyDihsj32ZxZedAfHIBpPJZv+IFIOJrrGNSMzjo+x2dd3niqaruv60YNBkvHpQyBShWbA\n\tlo0/W8obCrspunwVNgG3/oZ0i3PoEgzBz/VJ8OZcS9uDoxS+kURH86ElAjkFJeFjjvRD\n\talRIo3jjBGFYWTl+JPpXPWY6CO1hq5x1pMXt6q/U+ykf9xa9KgjP1fYZGcwO/yUsSocV\n\tMabg==", "X-Gm-Message-State": "AOJu0YzlKrCy7RtcpyH5yGufoDdTQbC6aoZfmZqotGfq1ywiTCAl3NUh\n\tCEHZ3BNdlHh8y11AhACMZNUV+GCNsFg3pv8AZ2N6BK9OHzFsJVlwDRxYyjmLMbj0N2wmkcRuV8u\n\tmwrac4hk=", "X-Gm-Gg": "ASbGncstFiFgeowE7NLxwHJGWwQTBGatqVmZqlc6LheoFOj3nmBMyIMzMAhkXPj1RoD\n\t51kd7QQBoTt/VZLo5xDH/TO8XfmmDkwgzKS+VnnDse+aBQj54YcEEZk8LfN/89htyCEDel1XEjP\n\ttGPz0IzqYrJuYNRh5KkTZaP7wAlUwwM2o/UrnX+8+Ue/Nr5e1aFG7uP/9rdQHnxMoia7ql/AY7S\n\tPV3b/d0PSsv5XMLyPxqCQlb5R0y5WzP2hfmvCyAZpwUklc1EkmkddvdJ5qUEBqEKumA0UEMDhZP\n\t/FcQyb94FEmSPOg9VgewQ2W8M9S+kHYpgndEgb81JKgRTNMzfqNcyDDb2z9Yg0NeM9Jt7nr5w+i\n\tSHs/cX7IfUCfjMGNthtQ7yj+o6JgVNl+AdRG6gutURpdScXnAAH1G4aBQbMMUbWWV", "X-Google-Smtp-Source": "AGHT+IHFYMz8qmevcSytjW7leMJCP28iHiKr86lYmrWIZoeHCdMGN9qMvkw+7EGDZ3CzIb3j+mBYyQ==", "X-Received": "by 2002:a05:600c:1391:b0:453:23fe:ca86 with SMTP id\n\t5b1f17b1804b1-458b69c7d66mr75436195e9.4.1754318886872; \n\tMon, 04 Aug 2025 07:48:06 -0700 (PDT)", "From": "David Plowman <david.plowman@raspberrypi.com>", "To": "libcamera-devel@lists.libcamera.org", "Cc": "David Plowman <david.plowman@raspberrypi.com>", "Subject": "[PATCH v2] ipa: rpi: ccm: Implement \"manual\" CCM mode", "Date": "Mon, 4 Aug 2025 15:48:04 +0100", "Message-Id": "<20250804144804.16965-1-david.plowman@raspberrypi.com>", "X-Mailer": "git-send-email 2.39.5", "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": "The CCM algorithm will now let an explicit colour matrix be set when\nAWB is in manual mode.\n\nWe must handle any controls that can cause the AWB to be enabled or\ndisabled first, so that we know the AWB's state correctly when we come\nto set the CCM.\n\nSigned-off-by: David Plowman <david.plowman@raspberrypi.com>\n---\n src/ipa/rpi/common/ipa_base.cpp | 180 +++++++++++++++++++------\n src/ipa/rpi/common/ipa_base.h | 2 +\n src/ipa/rpi/controller/ccm_algorithm.h | 6 +\n src/ipa/rpi/controller/rpi/ccm.cpp | 24 +++-\n src/ipa/rpi/controller/rpi/ccm.h | 5 +-\n 5 files changed, 170 insertions(+), 47 deletions(-)", "diff": "diff --git a/src/ipa/rpi/common/ipa_base.cpp b/src/ipa/rpi/common/ipa_base.cpp\nindex ce2343e9..6448e6ab 100644\n--- a/src/ipa/rpi/common/ipa_base.cpp\n+++ b/src/ipa/rpi/common/ipa_base.cpp\n@@ -94,6 +94,7 @@ const ControlInfoMap::Map ipaColourControls{\n \t{ &controls::AwbEnable, ControlInfo(false, true) },\n \t{ &controls::AwbMode, ControlInfo(controls::AwbModeValues) },\n \t{ &controls::ColourGains, ControlInfo(0.0f, 32.0f) },\n+\t{ &controls::ColourCorrectionMatrix, ControlInfo(0.0f, 8.0f) },\n \t{ &controls::ColourTemperature, ControlInfo(100, 100000) },\n \t{ &controls::Saturation, ControlInfo(0.0f, 32.0f, 1.0f) },\n };\n@@ -126,7 +127,7 @@ namespace ipa::RPi {\n IpaBase::IpaBase()\n \t: controller_(), frameLengths_(FrameLengthsQueueSize, 0s), statsMetadataOutput_(false),\n \t stitchSwapBuffers_(false), frameCount_(0), mistrustCount_(0), lastRunTimestamp_(0),\n-\t firstStart_(true), flickerState_({ 0, 0s })\n+\t firstStart_(true), flickerState_({ 0, 0s }), awbEnabled_(true)\n {\n }\n \n@@ -821,6 +822,102 @@ void IpaBase::applyControls(const ControlList &controls)\n \t\t}\n \t}\n \n+\t/*\n+\t * We must also handle any AWB on/off changes first, so that the CCM algorithm\n+\t * knows its state correctly.\n+\t */\n+\tconst auto awbEnable = controls.get(controls::AwbEnable);\n+\tif (awbEnable)\n+\t\tdo {\n+\t\t\t/* Silently ignore this control for a mono sensor. */\n+\t\t\tif (monoSensor_)\n+\t\t\t\tbreak;\n+\n+\t\t\tRPiController::AwbAlgorithm *awb = dynamic_cast<RPiController::AwbAlgorithm *>(\n+\t\t\t\tcontroller_.getAlgorithm(\"awb\"));\n+\t\t\tif (!awb) {\n+\t\t\t\tLOG(IPARPI, Warning)\n+\t\t\t\t\t<< \"Could not set AWB_ENABLE - no AWB algorithm\";\n+\t\t\t\tbreak;\n+\t\t\t}\n+\n+\t\t\tawbEnabled_ = *awbEnable;\n+\n+\t\t\tif (!awbEnabled_)\n+\t\t\t\tawb->disableAuto();\n+\t\t\telse {\n+\t\t\t\tawb->enableAuto();\n+\n+\t\t\t\t/* The CCM algorithm must go back to auto as well. */\n+\t\t\t\tRPiController::CcmAlgorithm *ccm = dynamic_cast<RPiController::CcmAlgorithm *>(\n+\t\t\t\t\tcontroller_.getAlgorithm(\"ccm\"));\n+\t\t\t\tif (ccm)\n+\t\t\t\t\tccm->enableAuto();\n+\t\t\t}\n+\n+\t\t\tlibcameraMetadata_.set(controls::AwbEnable, awbEnabled_);\n+\t\t} while (false);\n+\n+\tconst auto colourGains = controls.get(controls::ColourGains);\n+\tif (colourGains)\n+\t\tdo {\n+\t\t\t/* Silently ignore this control for a mono sensor. */\n+\t\t\tif (monoSensor_)\n+\t\t\t\tbreak;\n+\n+\t\t\tauto gains = *colourGains;\n+\t\t\tRPiController::AwbAlgorithm *awb = dynamic_cast<RPiController::AwbAlgorithm *>(\n+\t\t\t\tcontroller_.getAlgorithm(\"awb\"));\n+\t\t\tif (!awb) {\n+\t\t\t\tLOG(IPARPI, Warning)\n+\t\t\t\t\t<< \"Could not set COLOUR_GAINS - no AWB algorithm\";\n+\t\t\t\tbreak;\n+\t\t\t}\n+\n+\t\t\tawb->setManualGains(gains[0], gains[1]);\n+\t\t\tif (gains[0] != 0.0f && gains[1] != 0.0f) {\n+\t\t\t\t/* A gain of 0.0f will switch back to auto mode. */\n+\t\t\t\tlibcameraMetadata_.set(controls::ColourGains,\n+\t\t\t\t\t\t { gains[0], gains[1] });\n+\n+\t\t\t\tawbEnabled_ = false; /* doing this puts AWB into manual mode */\n+\t\t\t} else {\n+\t\t\t\tawbEnabled_ = true; /* doing this puts AWB into auto mode */\n+\n+\t\t\t\t/* The CCM algorithm must go back to auto as well. */\n+\t\t\t\tRPiController::CcmAlgorithm *ccm = dynamic_cast<RPiController::CcmAlgorithm *>(\n+\t\t\t\t\tcontroller_.getAlgorithm(\"ccm\"));\n+\t\t\t\tif (ccm)\n+\t\t\t\t\tccm->enableAuto();\n+\t\t\t}\n+\n+\t\t\t/* This metadata will get reported back automatically. */\n+\t\t\tbreak;\n+\t\t} while (false);\n+\n+\tconst auto colourTemperature = controls.get(controls::ColourTemperature);\n+\tif (colourTemperature)\n+\t\tdo {\n+\t\t\t/* Silently ignore this control for a mono sensor. */\n+\t\t\tif (monoSensor_)\n+\t\t\t\tbreak;\n+\n+\t\t\tauto temperatureK = *colourTemperature;\n+\t\t\tRPiController::AwbAlgorithm *awb = dynamic_cast<RPiController::AwbAlgorithm *>(\n+\t\t\t\tcontroller_.getAlgorithm(\"awb\"));\n+\t\t\tif (!awb) {\n+\t\t\t\tLOG(IPARPI, Warning)\n+\t\t\t\t\t<< \"Could not set COLOUR_TEMPERATURE - no AWB algorithm\";\n+\t\t\t\tbreak;\n+\t\t\t}\n+\n+\t\t\tawb->setColourTemperature(temperatureK);\n+\t\t\tawbEnabled_ = false; /* doing this puts AWB into manual mode */\n+\n+\t\t\t/* This metadata will get reported back automatically. */\n+\t\t\tbreak;\n+\t\t} while (false);\n+\n \t/* Iterate over controls */\n \tfor (auto const &ctrl : controls) {\n \t\tLOG(IPARPI, Debug) << \"Request ctrl: \"\n@@ -1037,25 +1134,7 @@ void IpaBase::applyControls(const ControlList &controls)\n \t\t}\n \n \t\tcase controls::AWB_ENABLE: {\n-\t\t\t/* Silently ignore this control for a mono sensor. */\n-\t\t\tif (monoSensor_)\n-\t\t\t\tbreak;\n-\n-\t\t\tRPiController::AwbAlgorithm *awb = dynamic_cast<RPiController::AwbAlgorithm *>(\n-\t\t\t\tcontroller_.getAlgorithm(\"awb\"));\n-\t\t\tif (!awb) {\n-\t\t\t\tLOG(IPARPI, Warning)\n-\t\t\t\t\t<< \"Could not set AWB_ENABLE - no AWB algorithm\";\n-\t\t\t\tbreak;\n-\t\t\t}\n-\n-\t\t\tif (ctrl.second.get<bool>() == false)\n-\t\t\t\tawb->disableAuto();\n-\t\t\telse\n-\t\t\t\tawb->enableAuto();\n-\n-\t\t\tlibcameraMetadata_.set(controls::AwbEnable,\n-\t\t\t\t\t ctrl.second.get<bool>());\n+\t\t\t/* We handled this one above. */\n \t\t\tbreak;\n \t\t}\n \n@@ -1080,47 +1159,60 @@ void IpaBase::applyControls(const ControlList &controls)\n \t\t\t\tLOG(IPARPI, Error) << \"AWB mode \" << idx\n \t\t\t\t\t\t << \" not recognised\";\n \t\t\t}\n+\n \t\t\tbreak;\n \t\t}\n \n \t\tcase controls::COLOUR_GAINS: {\n-\t\t\t/* Silently ignore this control for a mono sensor. */\n-\t\t\tif (monoSensor_)\n-\t\t\t\tbreak;\n-\n-\t\t\tauto gains = ctrl.second.get<Span<const float>>();\n-\t\t\tRPiController::AwbAlgorithm *awb = dynamic_cast<RPiController::AwbAlgorithm *>(\n-\t\t\t\tcontroller_.getAlgorithm(\"awb\"));\n-\t\t\tif (!awb) {\n-\t\t\t\tLOG(IPARPI, Warning)\n-\t\t\t\t\t<< \"Could not set COLOUR_GAINS - no AWB algorithm\";\n-\t\t\t\tbreak;\n-\t\t\t}\n-\n-\t\t\tawb->setManualGains(gains[0], gains[1]);\n-\t\t\tif (gains[0] != 0.0f && gains[1] != 0.0f)\n-\t\t\t\t/* A gain of 0.0f will switch back to auto mode. */\n-\t\t\t\tlibcameraMetadata_.set(controls::ColourGains,\n-\t\t\t\t\t\t { gains[0], gains[1] });\n+\t\t\t/* We handled this one above. */\n \t\t\tbreak;\n \t\t}\n \n \t\tcase controls::COLOUR_TEMPERATURE: {\n-\t\t\t/* Silently ignore this control for a mono sensor. */\n+\t\t\t/* We handled this one above. */\n+\t\t\tbreak;\n+\t\t}\n+\n+\t\tcase controls::COLOUR_CORRECTION_MATRIX: {\n \t\t\tif (monoSensor_)\n \t\t\t\tbreak;\n \n-\t\t\tauto temperatureK = ctrl.second.get<int32_t>();\n+\t\t\tauto floats = ctrl.second.get<Span<const float>>();\n+\t\t\tRPiController::CcmAlgorithm *ccm = dynamic_cast<RPiController::CcmAlgorithm *>(\n+\t\t\t\tcontroller_.getAlgorithm(\"ccm\"));\n+\t\t\tif (!ccm) {\n+\t\t\t\tLOG(IPARPI, Warning)\n+\t\t\t\t\t<< \"Could not set COLOUR_CORRECTION_MATRIX - no CCM algorithm\";\n+\t\t\t\tbreak;\n+\t\t\t}\n+\n \t\t\tRPiController::AwbAlgorithm *awb = dynamic_cast<RPiController::AwbAlgorithm *>(\n \t\t\t\tcontroller_.getAlgorithm(\"awb\"));\n-\t\t\tif (!awb) {\n+\t\t\tif (awb && awbEnabled_) {\n \t\t\t\tLOG(IPARPI, Warning)\n-\t\t\t\t\t<< \"Could not set COLOUR_TEMPERATURE - no AWB algorithm\";\n+\t\t\t\t\t<< \"Could not set COLOUR_CORRECTION_MATRIX - AWB is active\";\n \t\t\t\tbreak;\n \t\t\t}\n \n-\t\t\tawb->setColourTemperature(temperatureK);\n-\t\t\t/* This metadata will get reported back automatically. */\n+\t\t\t/* We are guaranteed this control contains 9 values. Nevertheless: */\n+\t\t\tassert(floats.size() == 9);\n+\n+\t\t\tMatrix<double, 3, 3> matrix;\n+\t\t\tfor (std::size_t i = 0; i < 3; ++i)\n+\t\t\t\tfor (std::size_t j = 0; j < 3; ++j)\n+\t\t\t\t\tmatrix[i][j] = static_cast<double>(floats[i * 3 + j]);\n+\n+\t\t\tccm->setCcm(matrix);\n+\n+\t\t\t/*\n+\t\t\t * But if AWB is running, go back to auto mode. The CCM gets remembered,\n+\t\t\t * which avoids the race between setting the CCM and disabling AWB in\n+\t\t\t * the same set of controls.\n+\t\t\t */\n+\t\t\tif (awbEnabled_)\n+\t\t\t\tccm->enableAuto();\n+\n+\t\t\t/* This metadata will be reported back automatically. */\n \t\t\tbreak;\n \t\t}\n \ndiff --git a/src/ipa/rpi/common/ipa_base.h b/src/ipa/rpi/common/ipa_base.h\nindex e2f6e330..5348f2ea 100644\n--- a/src/ipa/rpi/common/ipa_base.h\n+++ b/src/ipa/rpi/common/ipa_base.h\n@@ -140,6 +140,8 @@ private:\n \t\tint32_t mode;\n \t\tutils::Duration manualPeriod;\n \t} flickerState_;\n+\n+\tbool awbEnabled_;\n };\n \n } /* namespace ipa::RPi */\ndiff --git a/src/ipa/rpi/controller/ccm_algorithm.h b/src/ipa/rpi/controller/ccm_algorithm.h\nindex 6678ba75..785c408a 100644\n--- a/src/ipa/rpi/controller/ccm_algorithm.h\n+++ b/src/ipa/rpi/controller/ccm_algorithm.h\n@@ -6,16 +6,22 @@\n */\n #pragma once\n \n+#include \"libcamera/internal/matrix.h\"\n+\n #include \"algorithm.h\"\n \n namespace RPiController {\n \n+using Matrix3x3 = libcamera::Matrix<double, 3, 3>;\n+\n class CcmAlgorithm : public Algorithm\n {\n public:\n \tCcmAlgorithm(Controller *controller) : Algorithm(controller) {}\n \t/* A CCM algorithm must provide the following: */\n+\tvirtual void enableAuto() = 0;\n \tvirtual void setSaturation(double saturation) = 0;\n+\tvirtual void setCcm(Matrix3x3 const &matrix) = 0;\n };\n \n } /* namespace RPiController */\ndiff --git a/src/ipa/rpi/controller/rpi/ccm.cpp b/src/ipa/rpi/controller/rpi/ccm.cpp\nindex 8607f152..c42c0141 100644\n--- a/src/ipa/rpi/controller/rpi/ccm.cpp\n+++ b/src/ipa/rpi/controller/rpi/ccm.cpp\n@@ -32,7 +32,10 @@ LOG_DEFINE_CATEGORY(RPiCcm)\n using Matrix3x3 = Matrix<double, 3, 3>;\n \n Ccm::Ccm(Controller *controller)\n-\t: CcmAlgorithm(controller), saturation_(1.0) {}\n+\t: CcmAlgorithm(controller), enableAuto_(true), saturation_(1.0),\n+\t manualCcm_({ 1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0 })\n+{\n+}\n \n char const *Ccm::name() const\n {\n@@ -78,11 +81,22 @@ int Ccm::read(const libcamera::YamlObject ¶ms)\n \treturn 0;\n }\n \n+void Ccm::enableAuto()\n+{\n+\tenableAuto_ = true;\n+}\n+\n void Ccm::setSaturation(double saturation)\n {\n \tsaturation_ = saturation;\n }\n \n+void Ccm::setCcm(Matrix3x3 const &matrix)\n+{\n+\tenableAuto_ = false;\n+\tmanualCcm_ = matrix;\n+}\n+\n void Ccm::initialise()\n {\n }\n@@ -151,7 +165,13 @@ void Ccm::prepare(Metadata *imageMetadata)\n \t\tLOG(RPiCcm, Warning) << \"no colour temperature found\";\n \tif (!luxOk)\n \t\tLOG(RPiCcm, Warning) << \"no lux value found\";\n-\tMatrix3x3 ccm = calculateCcm(config_.ccms, awb.temperatureK);\n+\n+\tMatrix3x3 ccm;\n+\tif (enableAuto_)\n+\t\tccm = calculateCcm(config_.ccms, awb.temperatureK);\n+\telse\n+\t\tccm = manualCcm_;\n+\n \tdouble saturation = saturation_;\n \tstruct CcmStatus ccmStatus;\n \tccmStatus.saturation = saturation;\ndiff --git a/src/ipa/rpi/controller/rpi/ccm.h b/src/ipa/rpi/controller/rpi/ccm.h\nindex c05dbb17..70f28ed3 100644\n--- a/src/ipa/rpi/controller/rpi/ccm.h\n+++ b/src/ipa/rpi/controller/rpi/ccm.h\n@@ -8,7 +8,6 @@\n \n #include <vector>\n \n-#include \"libcamera/internal/matrix.h\"\n #include <libipa/pwl.h>\n \n #include \"../ccm_algorithm.h\"\n@@ -33,13 +32,17 @@ public:\n \tCcm(Controller *controller = NULL);\n \tchar const *name() const override;\n \tint read(const libcamera::YamlObject ¶ms) override;\n+\tvoid enableAuto() override;\n \tvoid setSaturation(double saturation) override;\n+\tvoid setCcm(Matrix3x3 const &matrix) override;\n \tvoid initialise() override;\n \tvoid prepare(Metadata *imageMetadata) override;\n \n private:\n \tCcmConfig config_;\n+\tbool enableAuto_;\n \tdouble saturation_;\n+\tMatrix3x3 manualCcm_;\n };\n \n } /* namespace RPiController */\n", "prefixes": [ "v2" ] }