From patchwork Wed Jan 28 11:43:48 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Milan Zamazal X-Patchwork-Id: 25994 Return-Path: X-Original-To: parsemail@patchwork.libcamera.org Delivered-To: parsemail@patchwork.libcamera.org Received: from lancelot.ideasonboard.com (lancelot.ideasonboard.com [92.243.16.209]) by patchwork.libcamera.org (Postfix) with ESMTPS id CA27CC3200 for ; Wed, 28 Jan 2026 11:44:18 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 8A4C361FCB; Wed, 28 Jan 2026 12:44:18 +0100 (CET) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=redhat.com header.i=@redhat.com header.b="WrdI3K5R"; dkim-atps=neutral Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 3EFB261FC4 for ; Wed, 28 Jan 2026 12:44:16 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1769600654; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=C/8LgjF6Mwfb/KDKE4GrtzMaCOLnS+XsLTr8F4Yeg9U=; b=WrdI3K5Rpq5kMrnjw16dyqU0rSK88ki+2yxFCtfLXyKZucRrH2VT5aL6C73kWhNZKOFhkh 41Cq1+x5B3buwJPop84CC9oysHM5yLy8M8QA9dp3ShQrsBX4he4al+HHo47uYsySuUgVpO 5c0pYTBGJmxtkaDjCoMhI1W7qGY4Zs0= Received: from mx-prod-mc-01.mail-002.prod.us-west-2.aws.redhat.com (ec2-54-186-198-63.us-west-2.compute.amazonaws.com [54.186.198.63]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-574-GtgIOb6VO7OKblOd7Lv8ow-1; Wed, 28 Jan 2026 06:44:11 -0500 X-MC-Unique: GtgIOb6VO7OKblOd7Lv8ow-1 X-Mimecast-MFC-AGG-ID: GtgIOb6VO7OKblOd7Lv8ow_1769600650 Received: from mx-prod-int-03.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-03.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.12]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mx-prod-mc-01.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id 595791944F12; Wed, 28 Jan 2026 11:44:10 +0000 (UTC) Received: from mzamazal-thinkpadp1gen7.tpbc.com (unknown [10.44.34.236]) by mx-prod-int-03.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id 739E319560B2; Wed, 28 Jan 2026 11:44:08 +0000 (UTC) From: Milan Zamazal To: libcamera-devel@lists.libcamera.org Cc: Milan Zamazal , Kieran Bingham , =?utf-8?q?Barnab=C3=A1s_P=C5=91cze?= Subject: [PATCH v5 01/15] libcamera: ipa: simple: Remove an unused include from awb.cpp Date: Wed, 28 Jan 2026 12:43:48 +0100 Message-ID: <20260128114402.31570-2-mzamazal@redhat.com> In-Reply-To: <20260128114402.31570-1-mzamazal@redhat.com> References: <20260128114402.31570-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: ZSAzUZdIcOigNVpTGzwkgYybLtomvwGmKavwhVevxGQ_1769600650 X-Mimecast-Originator: redhat.com X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" is not used any more. Reviewed-by: Kieran Bingham Reviewed-by: Barnabás Pőcze Signed-off-by: Milan Zamazal --- src/ipa/simple/algorithms/awb.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/ipa/simple/algorithms/awb.cpp b/src/ipa/simple/algorithms/awb.cpp index 50565fe8a..18d97f707 100644 --- a/src/ipa/simple/algorithms/awb.cpp +++ b/src/ipa/simple/algorithms/awb.cpp @@ -7,7 +7,6 @@ #include "awb.h" -#include #include #include From patchwork Wed Jan 28 11:43:49 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Milan Zamazal X-Patchwork-Id: 25995 Return-Path: X-Original-To: parsemail@patchwork.libcamera.org Delivered-To: parsemail@patchwork.libcamera.org Received: from lancelot.ideasonboard.com (lancelot.ideasonboard.com [92.243.16.209]) by patchwork.libcamera.org (Postfix) with ESMTPS id CF9E3C3200 for ; Wed, 28 Jan 2026 11:44:21 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 3AFC061FCC; Wed, 28 Jan 2026 12:44:21 +0100 (CET) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=redhat.com header.i=@redhat.com header.b="cCTn0sSm"; dkim-atps=neutral Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 99B7F61FCC for ; Wed, 28 Jan 2026 12:44:18 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1769600657; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=ecGQpeqL+0q1o7SA6sjZ4G4MfTbDuFJ/73Hvra+WDYc=; b=cCTn0sSmTXn4ysWg130X5Aq8uMccqwxaMf/RhDrGRVOVxhoFoDKtHZ+/0lOgM0x63QQkja tk4Gkfat34vpnj42bZlFxW/O9slrMipXxSUekj/NiUrggC2b3RTpGD2a/HiRXSXgfvLOO7 FU6T567kXaEIyOA8qe/Qd9X1UOUpL6w= Received: from mx-prod-mc-01.mail-002.prod.us-west-2.aws.redhat.com (ec2-54-186-198-63.us-west-2.compute.amazonaws.com [54.186.198.63]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-590-mnubTlvQM4qfOxegWK84rA-1; Wed, 28 Jan 2026 06:44:14 -0500 X-MC-Unique: mnubTlvQM4qfOxegWK84rA-1 X-Mimecast-MFC-AGG-ID: mnubTlvQM4qfOxegWK84rA_1769600653 Received: from mx-prod-int-03.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-03.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.12]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mx-prod-mc-01.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id 8A5A11944EB7; Wed, 28 Jan 2026 11:44:12 +0000 (UTC) Received: from mzamazal-thinkpadp1gen7.tpbc.com (unknown [10.44.34.236]) by mx-prod-int-03.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id DC9E319560B2; Wed, 28 Jan 2026 11:44:10 +0000 (UTC) From: Milan Zamazal To: libcamera-devel@lists.libcamera.org Cc: Milan Zamazal , Kieran Bingham , =?utf-8?q?Barnab=C3=A1s_P=C5=91cze?= Subject: [PATCH v5 02/15] libcamera: ipa: simple: Unwrap IPAFrameContext::ccm Date: Wed, 28 Jan 2026 12:43:49 +0100 Message-ID: <20260128114402.31570-3-mzamazal@redhat.com> In-Reply-To: <20260128114402.31570-1-mzamazal@redhat.com> References: <20260128114402.31570-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: D0lMclc0-zBLclYGcEh-qUHK2Jl8PyRqXN1RrqlcGwk_1769600653 X-Mimecast-Originator: redhat.com X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" The struct has only one member and there is no immediate need to add more. Let's use the member directly, to make things a bit simpler. Reviewed-by: Kieran Bingham Reviewed-by: Barnabás Pőcze Signed-off-by: Milan Zamazal --- src/ipa/simple/algorithms/ccm.cpp | 6 +++--- src/ipa/simple/ipa_context.h | 4 +--- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/src/ipa/simple/algorithms/ccm.cpp b/src/ipa/simple/algorithms/ccm.cpp index 0a98406c1..d7d3dda76 100644 --- a/src/ipa/simple/algorithms/ccm.cpp +++ b/src/ipa/simple/algorithms/ccm.cpp @@ -94,7 +94,7 @@ void Ccm::prepare(IPAContext &context, const uint32_t frame, if (frame > 0 && utils::abs_diff(ct, lastCt_) < kTemperatureThreshold && saturation == lastSaturation_) { - frameContext.ccm.ccm = context.activeState.ccm.ccm; + frameContext.ccm = context.activeState.ccm.ccm; context.activeState.ccm.changed = false; return; } @@ -106,9 +106,9 @@ void Ccm::prepare(IPAContext &context, const uint32_t frame, applySaturation(ccm, saturation.value()); context.activeState.ccm.ccm = ccm; - frameContext.ccm.ccm = ccm; frameContext.saturation = saturation; context.activeState.ccm.changed = true; + frameContext.ccm = ccm; } void Ccm::process([[maybe_unused]] IPAContext &context, @@ -117,7 +117,7 @@ void Ccm::process([[maybe_unused]] IPAContext &context, [[maybe_unused]] const SwIspStats *stats, ControlList &metadata) { - metadata.set(controls::ColourCorrectionMatrix, frameContext.ccm.ccm.data()); + metadata.set(controls::ColourCorrectionMatrix, frameContext.ccm.data()); const auto &saturation = frameContext.saturation; metadata.set(controls::Saturation, saturation.value_or(1.0)); diff --git a/src/ipa/simple/ipa_context.h b/src/ipa/simple/ipa_context.h index 26db92e93..74e77c841 100644 --- a/src/ipa/simple/ipa_context.h +++ b/src/ipa/simple/ipa_context.h @@ -75,9 +75,7 @@ struct IPAActiveState { }; struct IPAFrameContext : public FrameContext { - struct { - Matrix ccm; - } ccm; + Matrix ccm; struct { int32_t exposure; From patchwork Wed Jan 28 11:43:50 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Milan Zamazal X-Patchwork-Id: 25996 Return-Path: X-Original-To: parsemail@patchwork.libcamera.org Delivered-To: parsemail@patchwork.libcamera.org Received: from lancelot.ideasonboard.com (lancelot.ideasonboard.com [92.243.16.209]) by patchwork.libcamera.org (Postfix) with ESMTPS id DA535C3200 for ; Wed, 28 Jan 2026 11:44:24 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 77BA561FD7; Wed, 28 Jan 2026 12:44:24 +0100 (CET) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=redhat.com header.i=@redhat.com header.b="OIEJFfVb"; dkim-atps=neutral Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.129.124]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 3130D61FC4 for ; Wed, 28 Jan 2026 12:44:23 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1769600662; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=IEP7X+EchYL0d/Sa5J5GJDOjq0bnXvAMifj9sbuOl6c=; b=OIEJFfVbiQ/VQra3Yh+80MBXzE5HD1vxdwCl9AwUnD57nAl+xnyNojxGMxOeLy8GoVNY/e s502pdzG5Q/pKXUS9C8FyROds2qzf/KHDk3TclnnMyJssv7WZ/jWIdxSWwbS0WdNOTADG6 pyPrVf5Yikxe1J7+QTW6+9ZiJG7x2jk= Received: from mx-prod-mc-03.mail-002.prod.us-west-2.aws.redhat.com (ec2-54-186-198-63.us-west-2.compute.amazonaws.com [54.186.198.63]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-497-ZvNy_nF5MAOy5wbq7GyXAA-1; Wed, 28 Jan 2026 06:44:16 -0500 X-MC-Unique: ZvNy_nF5MAOy5wbq7GyXAA-1 X-Mimecast-MFC-AGG-ID: ZvNy_nF5MAOy5wbq7GyXAA_1769600655 Received: from mx-prod-int-03.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-03.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.12]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mx-prod-mc-03.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id A1FE61955D80; Wed, 28 Jan 2026 11:44:15 +0000 (UTC) Received: from mzamazal-thinkpadp1gen7.tpbc.com (unknown [10.44.34.236]) by mx-prod-int-03.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id 2654E19560B4; Wed, 28 Jan 2026 11:44:12 +0000 (UTC) From: Milan Zamazal To: libcamera-devel@lists.libcamera.org Cc: Milan Zamazal , Kieran Bingham , Bryan O'Donoghue , =?utf-8?b?QmFybmFiw6FzIFDFkWN6?= =?utf-8?q?e?= Subject: [PATCH v5 03/15] libcamera: ipa: simple: Generalize tracking matrix changes Date: Wed, 28 Jan 2026 12:43:50 +0100 Message-ID: <20260128114402.31570-4-mzamazal@redhat.com> In-Reply-To: <20260128114402.31570-1-mzamazal@redhat.com> References: <20260128114402.31570-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: BHZzJZyWta36_IuYPT-3JCHBZWQQE_Zb_3H69sHrXjk_1769600655 X-Mimecast-Originator: redhat.com X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" IPAActiveState::ccm stores the colour correction matrix (CCM) and whether it has been changed. The change flag is later used when recomputing or not the lookup tables. But the CCM may include other corrections than just the sensor colour correction, for example white balance and saturation adjustments. These things should be separated and IPAActiveState::ccm should represent just the CCM itself. As the first step towards that cleanup, let's separate the change flag from the CCM. And wrap the only remaining member of IPAActiveState::ccm. Also, let's reset the separated change flag in the lookup tables; it'll be no longer tied to just CCM handling. This patch doesn't change actual behaviour and it still reports the combined matrix as CCM in metadata. This is addressed in the followup patches. Reviewed-by: Bryan O'Donoghue Reviewed-by: Barnabás Pőcze Signed-off-by: Milan Zamazal --- src/ipa/simple/algorithms/ccm.cpp | 7 +++---- src/ipa/simple/algorithms/lut.cpp | 5 +++-- src/ipa/simple/ipa_context.h | 6 ++---- 3 files changed, 8 insertions(+), 10 deletions(-) diff --git a/src/ipa/simple/algorithms/ccm.cpp b/src/ipa/simple/algorithms/ccm.cpp index d7d3dda76..e05e5bc28 100644 --- a/src/ipa/simple/algorithms/ccm.cpp +++ b/src/ipa/simple/algorithms/ccm.cpp @@ -94,8 +94,7 @@ void Ccm::prepare(IPAContext &context, const uint32_t frame, if (frame > 0 && utils::abs_diff(ct, lastCt_) < kTemperatureThreshold && saturation == lastSaturation_) { - frameContext.ccm = context.activeState.ccm.ccm; - context.activeState.ccm.changed = false; + frameContext.ccm = context.activeState.ccm; return; } @@ -105,9 +104,9 @@ void Ccm::prepare(IPAContext &context, const uint32_t frame, if (saturation) applySaturation(ccm, saturation.value()); - context.activeState.ccm.ccm = ccm; + context.activeState.ccm = ccm; frameContext.saturation = saturation; - context.activeState.ccm.changed = true; + context.activeState.matrixChanged = true; frameContext.ccm = ccm; } diff --git a/src/ipa/simple/algorithms/lut.cpp b/src/ipa/simple/algorithms/lut.cpp index 54cb804e7..c19b34482 100644 --- a/src/ipa/simple/algorithms/lut.cpp +++ b/src/ipa/simple/algorithms/lut.cpp @@ -127,11 +127,11 @@ void Lut::prepare(IPAContext &context, params->green[i] = gammaTable[static_cast(lutGains.g())]; params->blue[i] = gammaTable[static_cast(lutGains.b())]; } - } else if (context.activeState.ccm.changed || gammaUpdateNeeded) { + } else if (context.activeState.matrixChanged || gammaUpdateNeeded) { Matrix gainCcm = { { gains.r(), 0, 0, 0, gains.g(), 0, 0, 0, gains.b() } }; - auto ccm = context.activeState.ccm.ccm * gainCcm; + auto ccm = context.activeState.ccm * gainCcm; auto &red = params->redCcm; auto &green = params->greenCcm; auto &blue = params->blueCcm; @@ -150,6 +150,7 @@ void Lut::prepare(IPAContext &context, params->gammaLut[i] = gammaTable[i / div]; } } + context.activeState.matrixChanged = false; } params->gamma = context.configuration.gamma; diff --git a/src/ipa/simple/ipa_context.h b/src/ipa/simple/ipa_context.h index 74e77c841..ea1852cd4 100644 --- a/src/ipa/simple/ipa_context.h +++ b/src/ipa/simple/ipa_context.h @@ -62,10 +62,8 @@ struct IPAActiveState { double contrastExp; } gamma; - struct { - Matrix ccm; - bool changed; - } ccm; + Matrix ccm; + bool matrixChanged = false; struct { /* 0..2 range, 1.0 = normal */ From patchwork Wed Jan 28 11:43:51 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Milan Zamazal X-Patchwork-Id: 25997 Return-Path: X-Original-To: parsemail@patchwork.libcamera.org Delivered-To: parsemail@patchwork.libcamera.org Received: from lancelot.ideasonboard.com (lancelot.ideasonboard.com [92.243.16.209]) by patchwork.libcamera.org (Postfix) with ESMTPS id 74BEDC3200 for ; Wed, 28 Jan 2026 11:44:27 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 00D2061FDA; Wed, 28 Jan 2026 12:44:26 +0100 (CET) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=redhat.com header.i=@redhat.com header.b="h3hDNIz2"; dkim-atps=neutral Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id EE21661FC4 for ; Wed, 28 Jan 2026 12:44:23 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1769600663; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=ZVyUWhDya57rwrjPpLGWVabMQ8b1lvVJBYg49JSMlIk=; b=h3hDNIz2oXUSIALn87/jLDgmyBz0ajJ28ULUxq+W72O4f8Nprl95KBoX9jCrABuTAQmISR DQiinit5uUUcStBgAjHqtAQQ7zFA/30rBgM32RSBke/QNHLX3mMTxOiPLVAngWuJAE0GCQ ruKsV1vIlWofrvm/0LwuZy581+3Ivb8= Received: from mx-prod-mc-03.mail-002.prod.us-west-2.aws.redhat.com (ec2-54-186-198-63.us-west-2.compute.amazonaws.com [54.186.198.63]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-474-BMkY8sHQMrKIOGk71hoWsw-1; Wed, 28 Jan 2026 06:44:19 -0500 X-MC-Unique: BMkY8sHQMrKIOGk71hoWsw-1 X-Mimecast-MFC-AGG-ID: BMkY8sHQMrKIOGk71hoWsw_1769600658 Received: from mx-prod-int-03.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-03.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.12]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mx-prod-mc-03.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id 978341955D88; Wed, 28 Jan 2026 11:44:18 +0000 (UTC) Received: from mzamazal-thinkpadp1gen7.tpbc.com (unknown [10.44.34.236]) by mx-prod-int-03.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id 3817B19560B2; Wed, 28 Jan 2026 11:44:15 +0000 (UTC) From: Milan Zamazal To: libcamera-devel@lists.libcamera.org Cc: Milan Zamazal , Kieran Bingham , =?utf-8?q?Barnab=C3=A1s_P=C5=91cze?= , Robert Mader Subject: [PATCH v5 04/15] libcamera: ipa: simple: Rename "ccm" identifiers not specific to CCM Date: Wed, 28 Jan 2026 12:43:51 +0100 Message-ID: <20260128114402.31570-5-mzamazal@redhat.com> In-Reply-To: <20260128114402.31570-1-mzamazal@redhat.com> References: <20260128114402.31570-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: -hvxTAOQjw54f_OX0Hrq1F0f_sigZWvmExS0u6UE_eg_1769600658 X-Mimecast-Originator: redhat.com X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" Let's rename the identifiers that are related to general colour corrections applied by matrix operations, rather than directly to the sensor colour correction matrix. Reviewed-by: Barnabás Pőcze Reviewed-by: Robert Mader Signed-off-by: Milan Zamazal --- src/ipa/simple/algorithms/lut.cpp | 32 +++++++++++++++---------------- src/ipa/simple/algorithms/lut.h | 2 +- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/src/ipa/simple/algorithms/lut.cpp b/src/ipa/simple/algorithms/lut.cpp index c19b34482..5c4683fd8 100644 --- a/src/ipa/simple/algorithms/lut.cpp +++ b/src/ipa/simple/algorithms/lut.cpp @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ /* - * Copyright (C) 2024-2025, Red Hat Inc. + * Copyright (C) 2024-2026, Red Hat Inc. * * Color lookup tables construction */ @@ -89,7 +89,7 @@ void Lut::updateGammaTable(IPAContext &context) context.activeState.gamma.contrastExp = contrastExp; } -int16_t Lut::ccmValue(unsigned int i, float ccm) const +int16_t Lut::matrixValue(unsigned int i, float ccm) const { return std::round(i * ccm); } @@ -128,25 +128,25 @@ void Lut::prepare(IPAContext &context, params->blue[i] = gammaTable[static_cast(lutGains.b())]; } } else if (context.activeState.matrixChanged || gammaUpdateNeeded) { - Matrix gainCcm = { { gains.r(), 0, 0, - 0, gains.g(), 0, - 0, 0, gains.b() } }; - auto ccm = context.activeState.ccm * gainCcm; + Matrix gainMatrix = { { gains.r(), 0, 0, + 0, gains.g(), 0, + 0, 0, gains.b() } }; + auto matrix = context.activeState.ccm * gainMatrix; auto &red = params->redCcm; auto &green = params->greenCcm; auto &blue = params->blueCcm; - params->ccm = ccm; + params->ccm = matrix; if (!context.gpuIspEnabled) { for (unsigned int i = 0; i < DebayerParams::kRGBLookupSize; i++) { - red[i].r = ccmValue(i, ccm[0][0]); - red[i].g = ccmValue(i, ccm[1][0]); - red[i].b = ccmValue(i, ccm[2][0]); - green[i].r = ccmValue(i, ccm[0][1]); - green[i].g = ccmValue(i, ccm[1][1]); - green[i].b = ccmValue(i, ccm[2][1]); - blue[i].r = ccmValue(i, ccm[0][2]); - blue[i].g = ccmValue(i, ccm[1][2]); - blue[i].b = ccmValue(i, ccm[2][2]); + red[i].r = matrixValue(i, matrix[0][0]); + red[i].g = matrixValue(i, matrix[1][0]); + red[i].b = matrixValue(i, matrix[2][0]); + green[i].r = matrixValue(i, matrix[0][1]); + green[i].g = matrixValue(i, matrix[1][1]); + green[i].b = matrixValue(i, matrix[2][1]); + blue[i].r = matrixValue(i, matrix[0][2]); + blue[i].g = matrixValue(i, matrix[1][2]); + blue[i].b = matrixValue(i, matrix[2][2]); params->gammaLut[i] = gammaTable[i / div]; } } diff --git a/src/ipa/simple/algorithms/lut.h b/src/ipa/simple/algorithms/lut.h index ba8b9021b..0eafd0695 100644 --- a/src/ipa/simple/algorithms/lut.h +++ b/src/ipa/simple/algorithms/lut.h @@ -38,7 +38,7 @@ public: private: void updateGammaTable(IPAContext &context); - int16_t ccmValue(unsigned int i, float ccm) const; + int16_t matrixValue(unsigned int i, float ccm) const; }; } /* namespace ipa::soft::algorithms */ From patchwork Wed Jan 28 11:43:52 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Milan Zamazal X-Patchwork-Id: 25998 Return-Path: X-Original-To: parsemail@patchwork.libcamera.org Delivered-To: parsemail@patchwork.libcamera.org Received: from lancelot.ideasonboard.com (lancelot.ideasonboard.com [92.243.16.209]) by patchwork.libcamera.org (Postfix) with ESMTPS id C669BC3200 for ; Wed, 28 Jan 2026 11:44:30 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 7B85561FD2; Wed, 28 Jan 2026 12:44:30 +0100 (CET) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=redhat.com header.i=@redhat.com header.b="OkPVBlz3"; dkim-atps=neutral Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.129.124]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 5E30461FCC for ; Wed, 28 Jan 2026 12:44:26 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1769600665; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=yHt8hEx7L7x6m8Giz/E0YsIRoOVuMDJUld2I1itRJUs=; b=OkPVBlz3jhqS4sQXE7tnTOp4J04RpAZ03+pYmaeASSwMa8eaIhZKotsruNwhWSMVXYUkAj Cn4T955Srj6Zkg3Gab+URmz+gKdBahVE6eMUvlLLSoqj0ZrycTPImSuZTC0VFGqDNRUd4f vyhm5T95vOl18vmOiy4vLf4v0ARPiaw= Received: from mx-prod-mc-08.mail-002.prod.us-west-2.aws.redhat.com (ec2-35-165-154-97.us-west-2.compute.amazonaws.com [35.165.154.97]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-159-ieMpqr8RP8WpA-lujRZO_A-1; Wed, 28 Jan 2026 06:44:21 -0500 X-MC-Unique: ieMpqr8RP8WpA-lujRZO_A-1 X-Mimecast-MFC-AGG-ID: ieMpqr8RP8WpA-lujRZO_A_1769600660 Received: from mx-prod-int-03.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-03.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.12]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mx-prod-mc-08.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id D2BF618002C4; Wed, 28 Jan 2026 11:44:20 +0000 (UTC) Received: from mzamazal-thinkpadp1gen7.tpbc.com (unknown [10.44.34.236]) by mx-prod-int-03.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id 1765A19560B2; Wed, 28 Jan 2026 11:44:18 +0000 (UTC) From: Milan Zamazal To: libcamera-devel@lists.libcamera.org Cc: Milan Zamazal , Kieran Bingham , =?utf-8?q?Barnab=C3=A1s_P=C5=91cze?= Subject: [PATCH v5 05/15] libcamera: ipa: simple: Introduce a general correction matrix Date: Wed, 28 Jan 2026 12:43:52 +0100 Message-ID: <20260128114402.31570-6-mzamazal@redhat.com> In-Reply-To: <20260128114402.31570-1-mzamazal@redhat.com> References: <20260128114402.31570-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: _3HIPnHCh1N5YE_2KtaUUMKnulFmGX78FwHsdRvHM4o_1769600660 X-Mimecast-Originator: redhat.com X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" Let's introduce IPAActiveState::combinedMatrix that is separate from IPAActiveState::ccm and represents the overall correction matrix, not only the sensor colour correction matrix. IPAActiveState::ccm still includes everything; this is changed in the followup patch. Reviewed-by: Kieran Bingham Reviewed-by: Barnabás Pőcze Signed-off-by: Milan Zamazal --- src/ipa/simple/algorithms/ccm.cpp | 1 + src/ipa/simple/algorithms/lut.cpp | 2 +- src/ipa/simple/ipa_context.h | 1 + 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/ipa/simple/algorithms/ccm.cpp b/src/ipa/simple/algorithms/ccm.cpp index e05e5bc28..a3e8cd6c4 100644 --- a/src/ipa/simple/algorithms/ccm.cpp +++ b/src/ipa/simple/algorithms/ccm.cpp @@ -104,6 +104,7 @@ void Ccm::prepare(IPAContext &context, const uint32_t frame, if (saturation) applySaturation(ccm, saturation.value()); + context.activeState.combinedMatrix = ccm; context.activeState.ccm = ccm; frameContext.saturation = saturation; context.activeState.matrixChanged = true; diff --git a/src/ipa/simple/algorithms/lut.cpp b/src/ipa/simple/algorithms/lut.cpp index 5c4683fd8..141ea17fa 100644 --- a/src/ipa/simple/algorithms/lut.cpp +++ b/src/ipa/simple/algorithms/lut.cpp @@ -131,7 +131,7 @@ void Lut::prepare(IPAContext &context, Matrix gainMatrix = { { gains.r(), 0, 0, 0, gains.g(), 0, 0, 0, gains.b() } }; - auto matrix = context.activeState.ccm * gainMatrix; + auto matrix = context.activeState.combinedMatrix * gainMatrix; auto &red = params->redCcm; auto &green = params->greenCcm; auto &blue = params->blueCcm; diff --git a/src/ipa/simple/ipa_context.h b/src/ipa/simple/ipa_context.h index ea1852cd4..58dcad290 100644 --- a/src/ipa/simple/ipa_context.h +++ b/src/ipa/simple/ipa_context.h @@ -63,6 +63,7 @@ struct IPAActiveState { } gamma; Matrix ccm; + Matrix combinedMatrix; bool matrixChanged = false; struct { From patchwork Wed Jan 28 11:43:53 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Milan Zamazal X-Patchwork-Id: 26000 Return-Path: X-Original-To: parsemail@patchwork.libcamera.org Delivered-To: parsemail@patchwork.libcamera.org Received: from lancelot.ideasonboard.com (lancelot.ideasonboard.com [92.243.16.209]) by patchwork.libcamera.org (Postfix) with ESMTPS id 50A5FC3200 for ; Wed, 28 Jan 2026 11:44:36 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id B972861FE8; Wed, 28 Jan 2026 12:44:35 +0100 (CET) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=redhat.com header.i=@redhat.com header.b="IDf4NrLm"; dkim-atps=neutral Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.129.124]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 909F261FDA for ; Wed, 28 Jan 2026 12:44:30 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1769600669; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=RbcfjV2aZVIq9RQLpoQQem6jLjKghnJI6DW2x5psyh8=; b=IDf4NrLm992vtVJyCfmD/0GS+aScrSWNkrRBaKlPiVKU45oCnDK1f0Oxu6AiG8PPldVPxb SB/jy7FFw387hhGAqOGb4eqRsRiPFZYhCxQHv7sgeof8NcIxjsR35V4j7O7mrOxAHx0sP6 uF/2tslCAd6lhXUswUHgrXyTZLOUlm0= Received: from mx-prod-mc-03.mail-002.prod.us-west-2.aws.redhat.com (ec2-54-186-198-63.us-west-2.compute.amazonaws.com [54.186.198.63]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-15-mI6c1zOUO9K876D7D8iP2g-1; Wed, 28 Jan 2026 06:44:24 -0500 X-MC-Unique: mI6c1zOUO9K876D7D8iP2g-1 X-Mimecast-MFC-AGG-ID: mI6c1zOUO9K876D7D8iP2g_1769600663 Received: from mx-prod-int-03.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-03.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.12]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mx-prod-mc-03.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id 1D1031955D8E; Wed, 28 Jan 2026 11:44:23 +0000 (UTC) Received: from mzamazal-thinkpadp1gen7.tpbc.com (unknown [10.44.34.236]) by mx-prod-int-03.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id 6E54F19560B4; Wed, 28 Jan 2026 11:44:21 +0000 (UTC) From: Milan Zamazal To: libcamera-devel@lists.libcamera.org Cc: Milan Zamazal , Kieran Bingham , Robert Mader Subject: [PATCH v5 06/15] libcamera: ipa: simple: Initialise the general correction matrix Date: Wed, 28 Jan 2026 12:43:53 +0100 Message-ID: <20260128114402.31570-7-mzamazal@redhat.com> In-Reply-To: <20260128114402.31570-1-mzamazal@redhat.com> References: <20260128114402.31570-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: 5R8jhOPgufvMmzo-baNTfy4JFYg8kszQ_jkmINLYOmM_1769600663 X-Mimecast-Originator: redhat.com 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: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" The combined matrix must be reset to the initial value before each frame is prepared. This must be done outside algorithms because any of the algorithms may be disabled while the matrix must be always initialised. Let's initialise the combined matrix to the identity matrix (which keeps the pixel values unchanged) in software ISP just before calling `prepare' on the algorithms. Matrix updates can no longer be skipped in ccm.cpp, otherwise the CCM won't be applied if there is no temperature or saturation change. We explicitly track whether the CCM has been set up completely rather than relying on the frame number, to avoid missing the initialisation in case the first frame is skipped due to some error. Reviewed-by: Robert Mader Signed-off-by: Milan Zamazal Acked-by: Kieran Bingham --- src/ipa/simple/algorithms/ccm.cpp | 29 +++++++++++++---------------- src/ipa/simple/algorithms/ccm.h | 3 ++- src/ipa/simple/ipa_context.h | 1 - src/ipa/simple/soft_simple.cpp | 2 ++ 4 files changed, 17 insertions(+), 18 deletions(-) diff --git a/src/ipa/simple/algorithms/ccm.cpp b/src/ipa/simple/algorithms/ccm.cpp index a3e8cd6c4..85643645b 100644 --- a/src/ipa/simple/algorithms/ccm.cpp +++ b/src/ipa/simple/algorithms/ccm.cpp @@ -83,7 +83,7 @@ void Ccm::applySaturation(Matrix &ccm, float saturation) ccm = ycbcr2rgb * saturationMatrix * rgb2ycbcr * ccm; } -void Ccm::prepare(IPAContext &context, const uint32_t frame, +void Ccm::prepare(IPAContext &context, [[maybe_unused]] const uint32_t frame, IPAFrameContext &frameContext, [[maybe_unused]] DebayerParams *params) { auto &saturation = context.activeState.knobs.saturation; @@ -91,24 +91,21 @@ void Ccm::prepare(IPAContext &context, const uint32_t frame, const unsigned int ct = context.activeState.awb.temperatureK; /* Change CCM only on saturation or bigger temperature changes. */ - if (frame > 0 && - utils::abs_diff(ct, lastCt_) < kTemperatureThreshold && - saturation == lastSaturation_) { - frameContext.ccm = context.activeState.ccm; - return; + if (!currentCcm_ || + utils::abs_diff(ct, lastCt_) >= kTemperatureThreshold || + saturation != lastSaturation_) { + currentCcm_ = ccm_.getInterpolated(ct); + if (saturation) + applySaturation(currentCcm_.value(), saturation.value()); + lastCt_ = ct; + lastSaturation_ = saturation; + context.activeState.matrixChanged = true; } - lastCt_ = ct; - lastSaturation_ = saturation; - Matrix ccm = ccm_.getInterpolated(ct); - if (saturation) - applySaturation(ccm, saturation.value()); - - context.activeState.combinedMatrix = ccm; - context.activeState.ccm = ccm; + context.activeState.combinedMatrix = + currentCcm_.value() * context.activeState.combinedMatrix; frameContext.saturation = saturation; - context.activeState.matrixChanged = true; - frameContext.ccm = ccm; + frameContext.ccm = currentCcm_.value(); } void Ccm::process([[maybe_unused]] IPAContext &context, diff --git a/src/ipa/simple/algorithms/ccm.h b/src/ipa/simple/algorithms/ccm.h index 8279a3d59..867a680c3 100644 --- a/src/ipa/simple/algorithms/ccm.h +++ b/src/ipa/simple/algorithms/ccm.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ /* - * Copyright (C) 2024-2025, Red Hat Inc. + * Copyright (C) 2024-2026, Red Hat Inc. * * Color correction matrix */ @@ -47,6 +47,7 @@ private: unsigned int lastCt_; std::optional lastSaturation_; Interpolator> ccm_; + std::optional> currentCcm_; }; } /* namespace ipa::soft::algorithms */ diff --git a/src/ipa/simple/ipa_context.h b/src/ipa/simple/ipa_context.h index 58dcad290..a3ff3d038 100644 --- a/src/ipa/simple/ipa_context.h +++ b/src/ipa/simple/ipa_context.h @@ -62,7 +62,6 @@ struct IPAActiveState { double contrastExp; } gamma; - Matrix ccm; Matrix combinedMatrix; bool matrixChanged = false; diff --git a/src/ipa/simple/soft_simple.cpp b/src/ipa/simple/soft_simple.cpp index 57836c73c..732e82510 100644 --- a/src/ipa/simple/soft_simple.cpp +++ b/src/ipa/simple/soft_simple.cpp @@ -282,6 +282,8 @@ void IPASoftSimple::queueRequest(const uint32_t frame, const ControlList &contro void IPASoftSimple::computeParams(const uint32_t frame) { + context_.activeState.combinedMatrix = Matrix::identity(); + IPAFrameContext &frameContext = context_.frameContexts.get(frame); for (auto const &algo : algorithms()) algo->prepare(context_, frame, frameContext, params_); From patchwork Wed Jan 28 11:43:54 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Milan Zamazal X-Patchwork-Id: 25999 Return-Path: X-Original-To: parsemail@patchwork.libcamera.org Delivered-To: parsemail@patchwork.libcamera.org Received: from lancelot.ideasonboard.com (lancelot.ideasonboard.com [92.243.16.209]) by patchwork.libcamera.org (Postfix) with ESMTPS id A434DC3200 for ; Wed, 28 Jan 2026 11:44:34 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 536BE61FD9; Wed, 28 Jan 2026 12:44:34 +0100 (CET) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=redhat.com header.i=@redhat.com header.b="WmJ4eoL3"; dkim-atps=neutral Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.129.124]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 9CA0161FDC for ; Wed, 28 Jan 2026 12:44:30 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1769600669; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=9bwusOCfXMmNTTY0gYPt9Hhu8b5+Z56GLw6j/gYGxho=; b=WmJ4eoL3rjk/KWnX8KuNe5lY6033BfEgQuX4GYbSHMqjn6+4iFf8si4IkXoQJN5JXB4Jae V/I0S96BeRtBIsSEcdHivVFKZaDhwP+AQfRpUV+QBGVFiX8RscD/y3gOhGREWM1gZZZ2rp tEZQhzz1z9Hfy3fLBHmVOhoRyU2jljQ= Received: from mx-prod-mc-06.mail-002.prod.us-west-2.aws.redhat.com (ec2-35-165-154-97.us-west-2.compute.amazonaws.com [35.165.154.97]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-297-z9VMQIUVPUK3SQ97q0QZEw-1; Wed, 28 Jan 2026 06:44:26 -0500 X-MC-Unique: z9VMQIUVPUK3SQ97q0QZEw-1 X-Mimecast-MFC-AGG-ID: z9VMQIUVPUK3SQ97q0QZEw_1769600665 Received: from mx-prod-int-03.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-03.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.12]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mx-prod-mc-06.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id 2A0341800365; Wed, 28 Jan 2026 11:44:25 +0000 (UTC) Received: from mzamazal-thinkpadp1gen7.tpbc.com (unknown [10.44.34.236]) by mx-prod-int-03.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id 9DC4B19560B2; Wed, 28 Jan 2026 11:44:23 +0000 (UTC) From: Milan Zamazal To: libcamera-devel@lists.libcamera.org Cc: Milan Zamazal , Kieran Bingham , Robert Mader Subject: [PATCH v5 07/15] libcamera: ipa: simple: Separate saturation from CCM Date: Wed, 28 Jan 2026 12:43:54 +0100 Message-ID: <20260128114402.31570-8-mzamazal@redhat.com> In-Reply-To: <20260128114402.31570-1-mzamazal@redhat.com> References: <20260128114402.31570-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: 9vF7j9g84_1SZequdC3YLZ1pCMsR9cikpy_ittGBKRc_1769600665 X-Mimecast-Originator: redhat.com 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: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" Saturation adjustments are implemented using matrix operations. They are currently applied to the colour correction matrix. Let's move them to a newly introduced separate "Adjust" algorithm. This separation has the following advantages: - It allows disabling general colour adjustments algorithms without disabling the CCM algorithm. - It keeps the CCM separated from other corrections. - It's generally cleaner. Reviewed-by: Kieran Bingham Reviewed-by: Robert Mader Signed-off-by: Milan Zamazal --- src/ipa/simple/algorithms/adjust.cpp | 106 ++++++++++++++++++++++++++ src/ipa/simple/algorithms/adjust.h | 52 +++++++++++++ src/ipa/simple/algorithms/ccm.cpp | 60 +-------------- src/ipa/simple/algorithms/ccm.h | 9 --- src/ipa/simple/algorithms/meson.build | 1 + src/ipa/simple/data/uncalibrated.yaml | 1 + 6 files changed, 164 insertions(+), 65 deletions(-) create mode 100644 src/ipa/simple/algorithms/adjust.cpp create mode 100644 src/ipa/simple/algorithms/adjust.h diff --git a/src/ipa/simple/algorithms/adjust.cpp b/src/ipa/simple/algorithms/adjust.cpp new file mode 100644 index 000000000..8ce0631e4 --- /dev/null +++ b/src/ipa/simple/algorithms/adjust.cpp @@ -0,0 +1,106 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2024, Ideas On Board + * Copyright (C) 2024-2025, Red Hat Inc. + * + * Common image adjustments + */ + +#include "adjust.h" + +#include +#include + +#include + +#include "libcamera/internal/matrix.h" + +namespace libcamera { + +namespace ipa::soft::algorithms { + +LOG_DEFINE_CATEGORY(IPASoftAdjust) + +int Adjust::init(IPAContext &context, [[maybe_unused]] const YamlObject &tuningData) +{ + if (context.ccmEnabled) + context.ctrlMap[&controls::Saturation] = ControlInfo(0.0f, 2.0f, 1.0f); + return 0; +} + +int Adjust::configure(IPAContext &context, + [[maybe_unused]] const IPAConfigInfo &configInfo) +{ + context.activeState.knobs.saturation = std::optional(); + + return 0; +} + +void Adjust::queueRequest(typename Module::Context &context, + [[maybe_unused]] const uint32_t frame, + [[maybe_unused]] typename Module::FrameContext &frameContext, + const ControlList &controls) +{ + const auto &saturation = controls.get(controls::Saturation); + if (saturation.has_value()) { + context.activeState.knobs.saturation = saturation; + LOG(IPASoftAdjust, Debug) << "Setting saturation to " << saturation.value(); + } +} + +void Adjust::applySaturation(Matrix &matrix, float saturation) +{ + /* https://en.wikipedia.org/wiki/YCbCr#ITU-R_BT.601_conversion */ + const Matrix rgb2ycbcr{ + { 0.256788235294, 0.504129411765, 0.0979058823529, + -0.148223529412, -0.290992156863, 0.439215686275, + 0.439215686275, -0.367788235294, -0.0714274509804 } + }; + const Matrix ycbcr2rgb{ + { 1.16438356164, 0, 1.59602678571, + 1.16438356164, -0.391762290094, -0.812967647235, + 1.16438356164, 2.01723214285, 0 } + }; + const Matrix saturationMatrix{ + { 1, 0, 0, + 0, saturation, 0, + 0, 0, saturation } + }; + matrix = + ycbcr2rgb * saturationMatrix * rgb2ycbcr * matrix; +} + +void Adjust::prepare(IPAContext &context, + [[maybe_unused]] const uint32_t frame, + IPAFrameContext &frameContext, + [[maybe_unused]] DebayerParams *params) +{ + if (!context.ccmEnabled) + return; + + auto &saturation = context.activeState.knobs.saturation; + frameContext.saturation = saturation; + if (saturation) + applySaturation(context.activeState.combinedMatrix, saturation.value()); + + if (saturation != lastSaturation_) { + context.activeState.matrixChanged = true; + lastSaturation_ = saturation; + } +} + +void Adjust::process([[maybe_unused]] IPAContext &context, + [[maybe_unused]] const uint32_t frame, + IPAFrameContext &frameContext, + [[maybe_unused]] const SwIspStats *stats, + ControlList &metadata) +{ + const auto &saturation = frameContext.saturation; + metadata.set(controls::Saturation, saturation.value_or(1.0)); +} + +REGISTER_IPA_ALGORITHM(Adjust, "Adjust") + +} /* namespace ipa::soft::algorithms */ + +} /* namespace libcamera */ diff --git a/src/ipa/simple/algorithms/adjust.h b/src/ipa/simple/algorithms/adjust.h new file mode 100644 index 000000000..c4baa2503 --- /dev/null +++ b/src/ipa/simple/algorithms/adjust.h @@ -0,0 +1,52 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2024-2025, Red Hat Inc. + * + * Color correction matrix + */ + +#pragma once + +#include + +#include "libcamera/internal/matrix.h" + +#include + +#include "algorithm.h" + +namespace libcamera { + +namespace ipa::soft::algorithms { + +class Adjust : public Algorithm +{ +public: + Adjust() = default; + ~Adjust() = default; + + int init(IPAContext &context, const YamlObject &tuningData) override; + int configure(IPAContext &context, + const IPAConfigInfo &configInfo) override; + void queueRequest(typename Module::Context &context, + const uint32_t frame, + typename Module::FrameContext &frameContext, + const ControlList &controls) override; + void prepare(IPAContext &context, + const uint32_t frame, + IPAFrameContext &frameContext, + DebayerParams *params) override; + void process(IPAContext &context, const uint32_t frame, + IPAFrameContext &frameContext, + const SwIspStats *stats, + ControlList &metadata) override; + +private: + void applySaturation(Matrix &ccm, float saturation); + + std::optional lastSaturation_; +}; + +} /* namespace ipa::soft::algorithms */ + +} /* namespace libcamera */ diff --git a/src/ipa/simple/algorithms/ccm.cpp b/src/ipa/simple/algorithms/ccm.cpp index 85643645b..5576a301f 100644 --- a/src/ipa/simple/algorithms/ccm.cpp +++ b/src/ipa/simple/algorithms/ccm.cpp @@ -1,9 +1,9 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ /* * Copyright (C) 2024, Ideas On Board - * Copyright (C) 2024-2025, Red Hat Inc. + * Copyright (C) 2024-2026, Red Hat Inc. * - * Color correction matrix + saturation + * Color correction matrix */ #include "ccm.h" @@ -37,74 +37,25 @@ int Ccm::init([[maybe_unused]] IPAContext &context, const YamlObject &tuningData } context.ccmEnabled = true; - context.ctrlMap[&controls::Saturation] = ControlInfo(0.0f, 2.0f, 1.0f); return 0; } -int Ccm::configure(IPAContext &context, - [[maybe_unused]] const IPAConfigInfo &configInfo) -{ - context.activeState.knobs.saturation = std::optional(); - - return 0; -} - -void Ccm::queueRequest(typename Module::Context &context, - [[maybe_unused]] const uint32_t frame, - [[maybe_unused]] typename Module::FrameContext &frameContext, - const ControlList &controls) -{ - const auto &saturation = controls.get(controls::Saturation); - if (saturation.has_value()) { - context.activeState.knobs.saturation = saturation; - LOG(IPASoftCcm, Debug) << "Setting saturation to " << saturation.value(); - } -} - -void Ccm::applySaturation(Matrix &ccm, float saturation) -{ - /* https://en.wikipedia.org/wiki/YCbCr#ITU-R_BT.601_conversion */ - const Matrix rgb2ycbcr{ - { 0.256788235294, 0.504129411765, 0.0979058823529, - -0.148223529412, -0.290992156863, 0.439215686275, - 0.439215686275, -0.367788235294, -0.0714274509804 } - }; - const Matrix ycbcr2rgb{ - { 1.16438356164, 0, 1.59602678571, - 1.16438356164, -0.391762290094, -0.812967647235, - 1.16438356164, 2.01723214285, 0 } - }; - const Matrix saturationMatrix{ - { 1, 0, 0, - 0, saturation, 0, - 0, 0, saturation } - }; - ccm = ycbcr2rgb * saturationMatrix * rgb2ycbcr * ccm; -} - void Ccm::prepare(IPAContext &context, [[maybe_unused]] const uint32_t frame, IPAFrameContext &frameContext, [[maybe_unused]] DebayerParams *params) { - auto &saturation = context.activeState.knobs.saturation; - const unsigned int ct = context.activeState.awb.temperatureK; - /* Change CCM only on saturation or bigger temperature changes. */ + /* Change CCM only on bigger temperature changes. */ if (!currentCcm_ || - utils::abs_diff(ct, lastCt_) >= kTemperatureThreshold || - saturation != lastSaturation_) { + utils::abs_diff(ct, lastCt_) >= kTemperatureThreshold) { currentCcm_ = ccm_.getInterpolated(ct); - if (saturation) - applySaturation(currentCcm_.value(), saturation.value()); lastCt_ = ct; - lastSaturation_ = saturation; context.activeState.matrixChanged = true; } context.activeState.combinedMatrix = currentCcm_.value() * context.activeState.combinedMatrix; - frameContext.saturation = saturation; frameContext.ccm = currentCcm_.value(); } @@ -115,9 +66,6 @@ void Ccm::process([[maybe_unused]] IPAContext &context, ControlList &metadata) { metadata.set(controls::ColourCorrectionMatrix, frameContext.ccm.data()); - - const auto &saturation = frameContext.saturation; - metadata.set(controls::Saturation, saturation.value_or(1.0)); } REGISTER_IPA_ALGORITHM(Ccm, "Ccm") diff --git a/src/ipa/simple/algorithms/ccm.h b/src/ipa/simple/algorithms/ccm.h index 867a680c3..62009a375 100644 --- a/src/ipa/simple/algorithms/ccm.h +++ b/src/ipa/simple/algorithms/ccm.h @@ -26,12 +26,6 @@ public: ~Ccm() = default; int init(IPAContext &context, const YamlObject &tuningData) override; - int configure(IPAContext &context, - const IPAConfigInfo &configInfo) override; - void queueRequest(typename Module::Context &context, - const uint32_t frame, - typename Module::FrameContext &frameContext, - const ControlList &controls) override; void prepare(IPAContext &context, const uint32_t frame, IPAFrameContext &frameContext, @@ -42,10 +36,7 @@ public: ControlList &metadata) override; private: - void applySaturation(Matrix &ccm, float saturation); - unsigned int lastCt_; - std::optional lastSaturation_; Interpolator> ccm_; std::optional> currentCcm_; }; diff --git a/src/ipa/simple/algorithms/meson.build b/src/ipa/simple/algorithms/meson.build index 2d0adb059..ebe9f20dd 100644 --- a/src/ipa/simple/algorithms/meson.build +++ b/src/ipa/simple/algorithms/meson.build @@ -1,6 +1,7 @@ # SPDX-License-Identifier: CC0-1.0 soft_simple_ipa_algorithms = files([ + 'adjust.cpp', 'awb.cpp', 'agc.cpp', 'blc.cpp', diff --git a/src/ipa/simple/data/uncalibrated.yaml b/src/ipa/simple/data/uncalibrated.yaml index 8b6df9afc..e389e0588 100644 --- a/src/ipa/simple/data/uncalibrated.yaml +++ b/src/ipa/simple/data/uncalibrated.yaml @@ -14,6 +14,7 @@ algorithms: ccm: [ 1, 0, 0, 0, 1, 0, 0, 0, 1] + - Adjust: - Lut: - Agc: ... From patchwork Wed Jan 28 11:43:55 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Milan Zamazal X-Patchwork-Id: 26001 Return-Path: X-Original-To: parsemail@patchwork.libcamera.org Delivered-To: parsemail@patchwork.libcamera.org Received: from lancelot.ideasonboard.com (lancelot.ideasonboard.com [92.243.16.209]) by patchwork.libcamera.org (Postfix) with ESMTPS id 19954C3200 for ; Wed, 28 Jan 2026 11:44:38 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 73C6361FD9; Wed, 28 Jan 2026 12:44:37 +0100 (CET) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=redhat.com header.i=@redhat.com header.b="QyhU1AYr"; dkim-atps=neutral Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.129.124]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id DEF1E61FD0 for ; Wed, 28 Jan 2026 12:44:30 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1769600669; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=iVS9VFGkuoeeiE6e0/fQyz9Mtv0pIkrBSDb8NCUYkwM=; b=QyhU1AYrAKcR39l//FPfK/XCmSdNIFtkg1UnTPzPrwiNRVRpAXtQGoqyobZ3oUFLqodzFz a3JQhg0AoNbpTCPpqXOygZgZeejjjgPIfvxyKUGtFJICQiwXBxUUAg9UtTuRRamSlZkkJI wUyW5/+mE3rhvj+sROAKJ/JH4TTq3ss= Received: from mx-prod-mc-06.mail-002.prod.us-west-2.aws.redhat.com (ec2-35-165-154-97.us-west-2.compute.amazonaws.com [35.165.154.97]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-417-AgLo-u-SNYCniMHZTITDrg-1; Wed, 28 Jan 2026 06:44:28 -0500 X-MC-Unique: AgLo-u-SNYCniMHZTITDrg-1 X-Mimecast-MFC-AGG-ID: AgLo-u-SNYCniMHZTITDrg_1769600667 Received: from mx-prod-int-03.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-03.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.12]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mx-prod-mc-06.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id 7EB9B18005B6; Wed, 28 Jan 2026 11:44:27 +0000 (UTC) Received: from mzamazal-thinkpadp1gen7.tpbc.com (unknown [10.44.34.236]) by mx-prod-int-03.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id 79D8F19560B2; Wed, 28 Jan 2026 11:44:25 +0000 (UTC) From: Milan Zamazal To: libcamera-devel@lists.libcamera.org Cc: Milan Zamazal , Kieran Bingham , =?utf-8?q?Barnab=C3=A1s_P=C5=91cze?= , Robert Mader Subject: [PATCH v5 08/15] libcamera: ipa: simple: Move contrast settings to adjust.cpp Date: Wed, 28 Jan 2026 12:43:55 +0100 Message-ID: <20260128114402.31570-9-mzamazal@redhat.com> In-Reply-To: <20260128114402.31570-1-mzamazal@redhat.com> References: <20260128114402.31570-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: KUXgD2ui69lMm4Gv5ffM39r8KJ5Kn8p-t4xnc5y46Fc_1769600667 X-Mimecast-Originator: redhat.com X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" Let's move the contrast settings from lut.cpp to adjust.cpp, where they belong, similarly to saturation. Reviewed-by: Barnabás Pőcze Reviewed-by: Robert Mader Signed-off-by: Milan Zamazal --- src/ipa/simple/algorithms/adjust.cpp | 14 +++++++++++ src/ipa/simple/algorithms/lut.cpp | 35 +--------------------------- src/ipa/simple/algorithms/lut.h | 11 --------- 3 files changed, 15 insertions(+), 45 deletions(-) diff --git a/src/ipa/simple/algorithms/adjust.cpp b/src/ipa/simple/algorithms/adjust.cpp index 8ce0631e4..27ae2a53a 100644 --- a/src/ipa/simple/algorithms/adjust.cpp +++ b/src/ipa/simple/algorithms/adjust.cpp @@ -23,6 +23,7 @@ LOG_DEFINE_CATEGORY(IPASoftAdjust) int Adjust::init(IPAContext &context, [[maybe_unused]] const YamlObject &tuningData) { + context.ctrlMap[&controls::Contrast] = ControlInfo(0.0f, 2.0f, 1.0f); if (context.ccmEnabled) context.ctrlMap[&controls::Saturation] = ControlInfo(0.0f, 2.0f, 1.0f); return 0; @@ -31,6 +32,7 @@ int Adjust::init(IPAContext &context, [[maybe_unused]] const YamlObject &tuningD int Adjust::configure(IPAContext &context, [[maybe_unused]] const IPAConfigInfo &configInfo) { + context.activeState.knobs.contrast = std::optional(); context.activeState.knobs.saturation = std::optional(); return 0; @@ -41,6 +43,12 @@ void Adjust::queueRequest(typename Module::Context &context, [[maybe_unused]] typename Module::FrameContext &frameContext, const ControlList &controls) { + const auto &contrast = controls.get(controls::Contrast); + if (contrast.has_value()) { + context.activeState.knobs.contrast = contrast; + LOG(IPASoftAdjust, Debug) << "Setting contrast to " << contrast.value(); + } + const auto &saturation = controls.get(controls::Saturation); if (saturation.has_value()) { context.activeState.knobs.saturation = saturation; @@ -75,6 +83,8 @@ void Adjust::prepare(IPAContext &context, IPAFrameContext &frameContext, [[maybe_unused]] DebayerParams *params) { + frameContext.contrast = context.activeState.knobs.contrast; + if (!context.ccmEnabled) return; @@ -95,6 +105,10 @@ void Adjust::process([[maybe_unused]] IPAContext &context, [[maybe_unused]] const SwIspStats *stats, ControlList &metadata) { + const auto &contrast = frameContext.contrast; + if (contrast) + metadata.set(controls::Contrast, contrast.value()); + const auto &saturation = frameContext.saturation; metadata.set(controls::Saturation, saturation.value_or(1.0)); } diff --git a/src/ipa/simple/algorithms/lut.cpp b/src/ipa/simple/algorithms/lut.cpp index 141ea17fa..3a00bf4ee 100644 --- a/src/ipa/simple/algorithms/lut.cpp +++ b/src/ipa/simple/algorithms/lut.cpp @@ -24,36 +24,16 @@ LOG_DEFINE_CATEGORY(IPASoftLut) namespace ipa::soft::algorithms { -int Lut::init(IPAContext &context, - [[maybe_unused]] const YamlObject &tuningData) -{ - context.ctrlMap[&controls::Contrast] = ControlInfo(0.0f, 2.0f, 1.0f); - return 0; -} - int Lut::configure(IPAContext &context, [[maybe_unused]] const IPAConfigInfo &configInfo) { /* Gamma value is fixed */ context.configuration.gamma = 1.0 / 2.2; - context.activeState.knobs.contrast = std::optional(); updateGammaTable(context); return 0; } -void Lut::queueRequest(typename Module::Context &context, - [[maybe_unused]] const uint32_t frame, - [[maybe_unused]] typename Module::FrameContext &frameContext, - const ControlList &controls) -{ - const auto &contrast = controls.get(controls::Contrast); - if (contrast.has_value()) { - context.activeState.knobs.contrast = contrast; - LOG(IPASoftLut, Debug) << "Setting contrast to " << contrast.value(); - } -} - void Lut::updateGammaTable(IPAContext &context) { const auto blackLevel = context.activeState.blc.level; @@ -96,11 +76,9 @@ int16_t Lut::matrixValue(unsigned int i, float ccm) const void Lut::prepare(IPAContext &context, [[maybe_unused]] const uint32_t frame, - IPAFrameContext &frameContext, + [[maybe_unused]] IPAFrameContext &frameContext, DebayerParams *params) { - frameContext.contrast = context.activeState.knobs.contrast; - /* * Update the gamma table if needed. This means if black level changes * and since the black level gets updated only if a lower value is @@ -157,17 +135,6 @@ void Lut::prepare(IPAContext &context, params->contrastExp = context.activeState.gamma.contrastExp; } -void Lut::process([[maybe_unused]] IPAContext &context, - [[maybe_unused]] const uint32_t frame, - [[maybe_unused]] IPAFrameContext &frameContext, - [[maybe_unused]] const SwIspStats *stats, - ControlList &metadata) -{ - const auto &contrast = frameContext.contrast; - if (contrast) - metadata.set(controls::Contrast, contrast.value()); -} - REGISTER_IPA_ALGORITHM(Lut, "Lut") } /* namespace ipa::soft::algorithms */ diff --git a/src/ipa/simple/algorithms/lut.h b/src/ipa/simple/algorithms/lut.h index 0eafd0695..ad16d1e8e 100644 --- a/src/ipa/simple/algorithms/lut.h +++ b/src/ipa/simple/algorithms/lut.h @@ -19,22 +19,11 @@ public: Lut() = default; ~Lut() = default; - int init(IPAContext &context, const YamlObject &tuningData) override; int configure(IPAContext &context, const IPAConfigInfo &configInfo) override; - void queueRequest(typename Module::Context &context, - const uint32_t frame, - typename Module::FrameContext &frameContext, - const ControlList &controls) - override; void prepare(IPAContext &context, const uint32_t frame, IPAFrameContext &frameContext, DebayerParams *params) override; - void process(IPAContext &context, - const uint32_t frame, - IPAFrameContext &frameContext, - const SwIspStats *stats, - ControlList &metadata) override; private: void updateGammaTable(IPAContext &context); From patchwork Wed Jan 28 11:43:56 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Milan Zamazal X-Patchwork-Id: 26002 Return-Path: X-Original-To: parsemail@patchwork.libcamera.org Delivered-To: parsemail@patchwork.libcamera.org Received: from lancelot.ideasonboard.com (lancelot.ideasonboard.com [92.243.16.209]) by patchwork.libcamera.org (Postfix) with ESMTPS id D3E1DC3200 for ; Wed, 28 Jan 2026 11:44:41 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 5D7A461FF3; Wed, 28 Jan 2026 12:44:41 +0100 (CET) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=redhat.com header.i=@redhat.com header.b="PfaHRys/"; dkim-atps=neutral Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id A964961FE4 for ; Wed, 28 Jan 2026 12:44:36 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1769600675; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=B8b9P/jOysXNgAcGTUf2CVjuo3yT7LK/GQDxrlEUCQ8=; b=PfaHRys/PdGMTB2Hq2IYOnXPewfNepu6w6RIFGMlkB6rRsKjHAUg+SIqIpSWJoFbfu8r/F 8D2omw8ky4XA/AG5nKzRlN4wO+MfXOmUiSDLdC9wxmx4af+sKE09vhwFa6axLO3OaS3wRw HDqzecthg0GdcxIBz+jFeoRd59lESQk= Received: from mx-prod-mc-05.mail-002.prod.us-west-2.aws.redhat.com (ec2-54-186-198-63.us-west-2.compute.amazonaws.com [54.186.198.63]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-114-vNC20Hk9Np6Ohz1jtbBI3g-1; Wed, 28 Jan 2026 06:44:31 -0500 X-MC-Unique: vNC20Hk9Np6Ohz1jtbBI3g-1 X-Mimecast-MFC-AGG-ID: vNC20Hk9Np6Ohz1jtbBI3g_1769600670 Received: from mx-prod-int-03.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-03.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.12]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mx-prod-mc-05.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id 43C041956094; Wed, 28 Jan 2026 11:44:30 +0000 (UTC) Received: from mzamazal-thinkpadp1gen7.tpbc.com (unknown [10.44.34.236]) by mx-prod-int-03.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id 1A59219560B2; Wed, 28 Jan 2026 11:44:27 +0000 (UTC) From: Milan Zamazal To: libcamera-devel@lists.libcamera.org Cc: Milan Zamazal , Kieran Bingham , Robert Mader , =?utf-8?q?Barnab=C3=A1s_P=C5=91cze?= Subject: [PATCH v5 09/15] libcamera: ipa: simple: Make gamma adjustable Date: Wed, 28 Jan 2026 12:43:56 +0100 Message-ID: <20260128114402.31570-10-mzamazal@redhat.com> In-Reply-To: <20260128114402.31570-1-mzamazal@redhat.com> References: <20260128114402.31570-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: 6Cib53E1CObkh9eLNPLR1__pZuCktdQMD-UU0poBJDc_1769600670 X-Mimecast-Originator: redhat.com X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" The gamma value is fixed in software ISP. Let's make it adjustable, similarly to contrast and saturation, and report it in metadata. Reviewed-by: Robert Mader Reviewed-by: Barnabás Pőcze Signed-off-by: Milan Zamazal --- src/ipa/simple/algorithms/adjust.cpp | 12 ++++++++++++ src/ipa/simple/algorithms/adjust.h | 4 +++- src/ipa/simple/algorithms/lut.cpp | 11 ++++++----- src/ipa/simple/ipa_context.h | 6 ++++-- 4 files changed, 25 insertions(+), 8 deletions(-) diff --git a/src/ipa/simple/algorithms/adjust.cpp b/src/ipa/simple/algorithms/adjust.cpp index 27ae2a53a..a067f7b84 100644 --- a/src/ipa/simple/algorithms/adjust.cpp +++ b/src/ipa/simple/algorithms/adjust.cpp @@ -23,6 +23,7 @@ LOG_DEFINE_CATEGORY(IPASoftAdjust) int Adjust::init(IPAContext &context, [[maybe_unused]] const YamlObject &tuningData) { + context.ctrlMap[&controls::Gamma] = ControlInfo(0.1f, 10.0f, kDefaultGamma); context.ctrlMap[&controls::Contrast] = ControlInfo(0.0f, 2.0f, 1.0f); if (context.ccmEnabled) context.ctrlMap[&controls::Saturation] = ControlInfo(0.0f, 2.0f, 1.0f); @@ -32,6 +33,7 @@ int Adjust::init(IPAContext &context, [[maybe_unused]] const YamlObject &tuningD int Adjust::configure(IPAContext &context, [[maybe_unused]] const IPAConfigInfo &configInfo) { + context.activeState.knobs.gamma = kDefaultGamma; context.activeState.knobs.contrast = std::optional(); context.activeState.knobs.saturation = std::optional(); @@ -43,6 +45,12 @@ void Adjust::queueRequest(typename Module::Context &context, [[maybe_unused]] typename Module::FrameContext &frameContext, const ControlList &controls) { + const auto &gamma = controls.get(controls::Gamma); + if (gamma.has_value()) { + context.activeState.knobs.gamma = gamma.value(); + LOG(IPASoftAdjust, Debug) << "Setting gamma to " << gamma.value(); + } + const auto &contrast = controls.get(controls::Contrast); if (contrast.has_value()) { context.activeState.knobs.contrast = contrast; @@ -83,6 +91,7 @@ void Adjust::prepare(IPAContext &context, IPAFrameContext &frameContext, [[maybe_unused]] DebayerParams *params) { + frameContext.gamma = context.activeState.knobs.gamma; frameContext.contrast = context.activeState.knobs.contrast; if (!context.ccmEnabled) @@ -105,6 +114,9 @@ void Adjust::process([[maybe_unused]] IPAContext &context, [[maybe_unused]] const SwIspStats *stats, ControlList &metadata) { + const auto &gamma = frameContext.gamma; + metadata.set(controls::Gamma, gamma); + const auto &contrast = frameContext.contrast; if (contrast) metadata.set(controls::Contrast, contrast.value()); diff --git a/src/ipa/simple/algorithms/adjust.h b/src/ipa/simple/algorithms/adjust.h index c4baa2503..7644138ff 100644 --- a/src/ipa/simple/algorithms/adjust.h +++ b/src/ipa/simple/algorithms/adjust.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ /* - * Copyright (C) 2024-2025, Red Hat Inc. + * Copyright (C) 2024-2026, Red Hat Inc. * * Color correction matrix */ @@ -19,6 +19,8 @@ namespace libcamera { namespace ipa::soft::algorithms { +constexpr float kDefaultGamma = 2.2f; + class Adjust : public Algorithm { public: diff --git a/src/ipa/simple/algorithms/lut.cpp b/src/ipa/simple/algorithms/lut.cpp index 3a00bf4ee..9740f8c8d 100644 --- a/src/ipa/simple/algorithms/lut.cpp +++ b/src/ipa/simple/algorithms/lut.cpp @@ -18,6 +18,8 @@ #include "simple/ipa_context.h" +#include "adjust.h" + namespace libcamera { LOG_DEFINE_CATEGORY(IPASoftLut) @@ -27,8 +29,6 @@ namespace ipa::soft::algorithms { int Lut::configure(IPAContext &context, [[maybe_unused]] const IPAConfigInfo &configInfo) { - /* Gamma value is fixed */ - context.configuration.gamma = 1.0 / 2.2; updateGammaTable(context); return 0; @@ -37,6 +37,7 @@ int Lut::configure(IPAContext &context, void Lut::updateGammaTable(IPAContext &context) { const auto blackLevel = context.activeState.blc.level; + const auto gamma = 1.0 / context.activeState.knobs.gamma; const auto contrast = context.activeState.knobs.contrast.value_or(1.0); /* Convert 0..2 to 0..infinity; avoid actual inifinity at tan(pi/2) */ double contrastExp = tan(std::clamp(contrast * M_PI_4, 0.0, M_PI_2 - 0.00001)); @@ -52,8 +53,7 @@ void Lut::updateGammaTable(IPAContext &context) normalized = 0.5 * std::pow(normalized / 0.5, contrastExp); else normalized = 1.0 - 0.5 * std::pow((1.0 - normalized) / 0.5, contrastExp); - gammaTable[i] = UINT8_MAX * - std::pow(normalized, context.configuration.gamma); + gammaTable[i] = UINT8_MAX * std::pow(normalized, gamma); } /* * Due to CCM operations, the table lookup may reach indices below the black @@ -65,6 +65,7 @@ void Lut::updateGammaTable(IPAContext &context) gammaTable[blackIndex]); } + context.activeState.gamma.gamma = gamma; context.activeState.gamma.blackLevel = blackLevel; context.activeState.gamma.contrastExp = contrastExp; } @@ -131,7 +132,7 @@ void Lut::prepare(IPAContext &context, context.activeState.matrixChanged = false; } - params->gamma = context.configuration.gamma; + params->gamma = context.activeState.gamma.gamma; params->contrastExp = context.activeState.gamma.contrastExp; } diff --git a/src/ipa/simple/ipa_context.h b/src/ipa/simple/ipa_context.h index a3ff3d038..eeade9d35 100644 --- a/src/ipa/simple/ipa_context.h +++ b/src/ipa/simple/ipa_context.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ /* - * Copyright (C) 2024-2025 Red Hat, Inc. + * Copyright (C) 2024-2026 Red Hat, Inc. * * Simple pipeline IPA Context */ @@ -25,7 +25,6 @@ namespace libcamera { namespace ipa::soft { struct IPASessionConfiguration { - float gamma; struct { int32_t exposureMin, exposureMax; double againMin, againMax, again10, againMinStep; @@ -58,6 +57,7 @@ struct IPAActiveState { struct { std::array gammaTable; uint8_t blackLevel; + float gamma; double contrast; double contrastExp; } gamma; @@ -66,6 +66,7 @@ struct IPAActiveState { bool matrixChanged = false; struct { + float gamma; /* 0..2 range, 1.0 = normal */ std::optional contrast; std::optional saturation; @@ -85,6 +86,7 @@ struct IPAFrameContext : public FrameContext { double blue; } gains; + float gamma; std::optional contrast; std::optional saturation; }; From patchwork Wed Jan 28 11:43:57 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Milan Zamazal X-Patchwork-Id: 26003 Return-Path: X-Original-To: parsemail@patchwork.libcamera.org Delivered-To: parsemail@patchwork.libcamera.org Received: from lancelot.ideasonboard.com (lancelot.ideasonboard.com [92.243.16.209]) by patchwork.libcamera.org (Postfix) with ESMTPS id 7CBD4C3200 for ; Wed, 28 Jan 2026 11:44:43 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id E143F61FF5; Wed, 28 Jan 2026 12:44:42 +0100 (CET) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=redhat.com header.i=@redhat.com header.b="fWyfLU0F"; dkim-atps=neutral Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.129.124]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 432B361FD0 for ; Wed, 28 Jan 2026 12:44:38 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1769600677; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=tXFgft+XYVjSa8lYqVP+B3fdcH9cDd+ABLYCY2ZVagY=; b=fWyfLU0Fm7iIGmKCUGUSJOjJbOY1dT6COMMZt/o4ggouK1VNbqM4V8hG18V1zolW6ngNXd IJvAwd/8TlabbCxehVSzsI9nUwnTQBeBiMtQH5kTM7Hga/a5o+hDYIO9jdB6dt3UWNDUri RADDK8xJsXWib4qSJ5eAwZ+9gCW4Qdk= Received: from mx-prod-mc-03.mail-002.prod.us-west-2.aws.redhat.com (ec2-54-186-198-63.us-west-2.compute.amazonaws.com [54.186.198.63]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-107-BUlrdB8lPtOFaR51RXB70A-1; Wed, 28 Jan 2026 06:44:33 -0500 X-MC-Unique: BUlrdB8lPtOFaR51RXB70A-1 X-Mimecast-MFC-AGG-ID: BUlrdB8lPtOFaR51RXB70A_1769600672 Received: from mx-prod-int-03.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-03.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.12]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mx-prod-mc-03.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id 80EE51955D98; Wed, 28 Jan 2026 11:44:32 +0000 (UTC) Received: from mzamazal-thinkpadp1gen7.tpbc.com (unknown [10.44.34.236]) by mx-prod-int-03.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id A315A19560B2; Wed, 28 Jan 2026 11:44:30 +0000 (UTC) From: Milan Zamazal To: libcamera-devel@lists.libcamera.org Cc: Milan Zamazal , Kieran Bingham , Robert Mader Subject: [PATCH v5 10/15] libcamera: ipa: simple: Apply gain matrix in awb Date: Wed, 28 Jan 2026 12:43:57 +0100 Message-ID: <20260128114402.31570-11-mzamazal@redhat.com> In-Reply-To: <20260128114402.31570-1-mzamazal@redhat.com> References: <20260128114402.31570-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: mcPPp43coS6v17ZFt7bYTfiGt6cr7TKxkDIRiz_n5HU_1769600672 X-Mimecast-Originator: redhat.com 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: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" Now, when we have a combined matrix, we can apply AWB gains to it directly in awb.cpp. Reviewed-by: Robert Mader Signed-off-by: Milan Zamazal Reviewed-by: Kieran Bingham --- src/ipa/simple/algorithms/awb.cpp | 5 +++++ src/ipa/simple/algorithms/lut.cpp | 5 +---- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/ipa/simple/algorithms/awb.cpp b/src/ipa/simple/algorithms/awb.cpp index 18d97f707..31726658a 100644 --- a/src/ipa/simple/algorithms/awb.cpp +++ b/src/ipa/simple/algorithms/awb.cpp @@ -40,6 +40,11 @@ void Awb::prepare(IPAContext &context, [[maybe_unused]] DebayerParams *params) { auto &gains = context.activeState.awb.gains; + Matrix gainMatrix = { { gains.r(), 0, 0, + 0, gains.g(), 0, + 0, 0, gains.b() } }; + context.activeState.combinedMatrix = + context.activeState.combinedMatrix * gainMatrix; /* Just report, the gains are applied in LUT algorithm. */ frameContext.gains.red = gains.r(); frameContext.gains.blue = gains.b(); diff --git a/src/ipa/simple/algorithms/lut.cpp b/src/ipa/simple/algorithms/lut.cpp index 9740f8c8d..d8e92c613 100644 --- a/src/ipa/simple/algorithms/lut.cpp +++ b/src/ipa/simple/algorithms/lut.cpp @@ -107,10 +107,7 @@ void Lut::prepare(IPAContext &context, params->blue[i] = gammaTable[static_cast(lutGains.b())]; } } else if (context.activeState.matrixChanged || gammaUpdateNeeded) { - Matrix gainMatrix = { { gains.r(), 0, 0, - 0, gains.g(), 0, - 0, 0, gains.b() } }; - auto matrix = context.activeState.combinedMatrix * gainMatrix; + auto &matrix = context.activeState.combinedMatrix; auto &red = params->redCcm; auto &green = params->greenCcm; auto &blue = params->blueCcm; From patchwork Wed Jan 28 11:43:58 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Milan Zamazal X-Patchwork-Id: 26004 Return-Path: X-Original-To: parsemail@patchwork.libcamera.org Delivered-To: parsemail@patchwork.libcamera.org Received: from lancelot.ideasonboard.com (lancelot.ideasonboard.com [92.243.16.209]) by patchwork.libcamera.org (Postfix) with ESMTPS id AD99CC3200 for ; Wed, 28 Jan 2026 11:44:46 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 5A3D561FE8; Wed, 28 Jan 2026 12:44:46 +0100 (CET) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=redhat.com header.i=@redhat.com header.b="NViHXuMY"; dkim-atps=neutral Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 6866061FC9 for ; Wed, 28 Jan 2026 12:44:40 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1769600679; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=HnF5trqWh4bDvEDa7fhd6lKrtjjkabTJkPGY/KeAxQo=; b=NViHXuMY6aTmYvf30MatGmsOlNwBNaa1rYgFi70LbSq4B3NHY5Om1irxvqVwGxTBbTXtAx q+NwdKivTPLtsOLO4qV7dy3Q6KMQ77tU5aj5r9n4TLTDYsc1E+v8O1uIlF68BqiEyKMhWc uYxIP8ZcKVPMBWDEd4O5UMtI7EhNbSM= Received: from mx-prod-mc-08.mail-002.prod.us-west-2.aws.redhat.com (ec2-35-165-154-97.us-west-2.compute.amazonaws.com [35.165.154.97]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-62-HHQd9VidPQG-Gj8lMV36Yg-1; Wed, 28 Jan 2026 06:44:35 -0500 X-MC-Unique: HHQd9VidPQG-Gj8lMV36Yg-1 X-Mimecast-MFC-AGG-ID: HHQd9VidPQG-Gj8lMV36Yg_1769600674 Received: from mx-prod-int-03.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-03.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.12]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mx-prod-mc-08.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id 8E70F1800371; Wed, 28 Jan 2026 11:44:34 +0000 (UTC) Received: from mzamazal-thinkpadp1gen7.tpbc.com (unknown [10.44.34.236]) by mx-prod-int-03.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id E424A19560B4; Wed, 28 Jan 2026 11:44:32 +0000 (UTC) From: Milan Zamazal To: libcamera-devel@lists.libcamera.org Cc: Milan Zamazal , Kieran Bingham , =?utf-8?q?Barnab=C3=A1s_P=C5=91cze?= , Robert Mader Subject: [PATCH v5 11/15] libcamera: ipa: simple: Use float type for adjustment controls Date: Wed, 28 Jan 2026 12:43:58 +0100 Message-ID: <20260128114402.31570-12-mzamazal@redhat.com> In-Reply-To: <20260128114402.31570-1-mzamazal@redhat.com> References: <20260128114402.31570-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: -USwjvYnHGPn3zcWDgi5NuybpprOdB4U_r98dUFAyvY_1769600674 X-Mimecast-Originator: redhat.com X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" control_ids.h defines the contrast type as float, let's use the same in simple IPA, instead of double. Saturation and gamma already use float, except for the knobs initializers, let's use float for the knobs too. Reviewed-by: Barnabás Pőcze Reviewed-by: Robert Mader Signed-off-by: Milan Zamazal --- include/libcamera/internal/software_isp/debayer_params.h | 2 +- src/ipa/simple/algorithms/adjust.cpp | 4 ++-- src/ipa/simple/algorithms/lut.cpp | 2 +- src/ipa/simple/ipa_context.h | 8 ++++---- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/include/libcamera/internal/software_isp/debayer_params.h b/include/libcamera/internal/software_isp/debayer_params.h index 256c7d43d..2d69bd295 100644 --- a/include/libcamera/internal/software_isp/debayer_params.h +++ b/include/libcamera/internal/software_isp/debayer_params.h @@ -59,7 +59,7 @@ struct DebayerParams { Matrix ccm; RGB blackLevel; float gamma; - double contrastExp; + float contrastExp; }; } /* namespace libcamera */ diff --git a/src/ipa/simple/algorithms/adjust.cpp b/src/ipa/simple/algorithms/adjust.cpp index a067f7b84..a8b7c5ab6 100644 --- a/src/ipa/simple/algorithms/adjust.cpp +++ b/src/ipa/simple/algorithms/adjust.cpp @@ -34,8 +34,8 @@ int Adjust::configure(IPAContext &context, [[maybe_unused]] const IPAConfigInfo &configInfo) { context.activeState.knobs.gamma = kDefaultGamma; - context.activeState.knobs.contrast = std::optional(); - context.activeState.knobs.saturation = std::optional(); + context.activeState.knobs.contrast = std::optional(); + context.activeState.knobs.saturation = std::optional(); return 0; } diff --git a/src/ipa/simple/algorithms/lut.cpp b/src/ipa/simple/algorithms/lut.cpp index d8e92c613..fd442259a 100644 --- a/src/ipa/simple/algorithms/lut.cpp +++ b/src/ipa/simple/algorithms/lut.cpp @@ -40,7 +40,7 @@ void Lut::updateGammaTable(IPAContext &context) const auto gamma = 1.0 / context.activeState.knobs.gamma; const auto contrast = context.activeState.knobs.contrast.value_or(1.0); /* Convert 0..2 to 0..infinity; avoid actual inifinity at tan(pi/2) */ - double contrastExp = tan(std::clamp(contrast * M_PI_4, 0.0, M_PI_2 - 0.00001)); + float contrastExp = tan(std::clamp(contrast * M_PI_4, 0.0, M_PI_2 - 0.00001)); if (!context.gpuIspEnabled) { auto &gammaTable = context.activeState.gamma.gammaTable; diff --git a/src/ipa/simple/ipa_context.h b/src/ipa/simple/ipa_context.h index eeade9d35..293e35b71 100644 --- a/src/ipa/simple/ipa_context.h +++ b/src/ipa/simple/ipa_context.h @@ -58,8 +58,8 @@ struct IPAActiveState { std::array gammaTable; uint8_t blackLevel; float gamma; - double contrast; - double contrastExp; + float contrast; + float contrastExp; } gamma; Matrix combinedMatrix; @@ -68,7 +68,7 @@ struct IPAActiveState { struct { float gamma; /* 0..2 range, 1.0 = normal */ - std::optional contrast; + std::optional contrast; std::optional saturation; } knobs; }; @@ -87,7 +87,7 @@ struct IPAFrameContext : public FrameContext { } gains; float gamma; - std::optional contrast; + std::optional contrast; std::optional saturation; }; From patchwork Wed Jan 28 11:43:59 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Milan Zamazal X-Patchwork-Id: 26005 Return-Path: X-Original-To: parsemail@patchwork.libcamera.org Delivered-To: parsemail@patchwork.libcamera.org Received: from lancelot.ideasonboard.com (lancelot.ideasonboard.com [92.243.16.209]) by patchwork.libcamera.org (Postfix) with ESMTPS id 35534C3200 for ; Wed, 28 Jan 2026 11:44:48 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 90D0261FFC; Wed, 28 Jan 2026 12:44:47 +0100 (CET) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=redhat.com header.i=@redhat.com header.b="ZFg8TaIt"; dkim-atps=neutral Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 88D4261FD3 for ; Wed, 28 Jan 2026 12:44:40 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1769600679; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=qwc3MDgveHRMPpmSPMcogDqaqKS6nMiSM5Cb1HbqB1M=; b=ZFg8TaItzIOslu3GODmNb5F2PK99ypGqT6mDDophK4nD/g76mBLDVNPdKitWkPg9d/RnG+ ydM1vHjLXMMdNGe28GepkroWi8Y3OcD56G/dI2EZREhqTsPXBcYjn/SneMZLBC/RzRE2JS m+TSQYxQ3ECthQoGv1My+7xwABR2olo= Received: from mx-prod-mc-05.mail-002.prod.us-west-2.aws.redhat.com (ec2-54-186-198-63.us-west-2.compute.amazonaws.com [54.186.198.63]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-106-PRxmicG6PGi7bjIyVTo_fQ-1; Wed, 28 Jan 2026 06:44:38 -0500 X-MC-Unique: PRxmicG6PGi7bjIyVTo_fQ-1 X-Mimecast-MFC-AGG-ID: PRxmicG6PGi7bjIyVTo_fQ_1769600677 Received: from mx-prod-int-03.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-03.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.12]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mx-prod-mc-05.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id 2346C19775A3; Wed, 28 Jan 2026 11:44:37 +0000 (UTC) Received: from mzamazal-thinkpadp1gen7.tpbc.com (unknown [10.44.34.236]) by mx-prod-int-03.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id 11AE319560B2; Wed, 28 Jan 2026 11:44:34 +0000 (UTC) From: Milan Zamazal To: libcamera-devel@lists.libcamera.org Cc: Milan Zamazal , Kieran Bingham , =?utf-8?q?Barnab=C3=A1s_P=C5=91cze?= , Robert Mader Subject: [PATCH v5 12/15] libcamera: ipa: simple: Use symbolic constants for adjust defaults Date: Wed, 28 Jan 2026 12:43:59 +0100 Message-ID: <20260128114402.31570-13-mzamazal@redhat.com> In-Reply-To: <20260128114402.31570-1-mzamazal@redhat.com> References: <20260128114402.31570-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: sKACZM_Vam2TvpAK1q7Twqh7zygpyYL3ho7X4-Xykco_1769600677 X-Mimecast-Originator: redhat.com X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" The adjust algorithm already uses a symbolic constant for gamma. Let's introduce similar constants for contrast and saturation to prevent copying the numeric defaults to multiple places. Reviewed-by: Barnabás Pőcze Reviewed-by: Robert Mader Signed-off-by: Milan Zamazal --- src/ipa/simple/algorithms/adjust.cpp | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/src/ipa/simple/algorithms/adjust.cpp b/src/ipa/simple/algorithms/adjust.cpp index a8b7c5ab6..23f6f6ad7 100644 --- a/src/ipa/simple/algorithms/adjust.cpp +++ b/src/ipa/simple/algorithms/adjust.cpp @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ /* * Copyright (C) 2024, Ideas On Board - * Copyright (C) 2024-2025, Red Hat Inc. + * Copyright (C) 2024-2026, Red Hat Inc. * * Common image adjustments */ @@ -19,14 +19,20 @@ namespace libcamera { namespace ipa::soft::algorithms { +constexpr float kDefaultContrast = 1.0f; +constexpr float kDefaultSaturation = 1.0f; + LOG_DEFINE_CATEGORY(IPASoftAdjust) int Adjust::init(IPAContext &context, [[maybe_unused]] const YamlObject &tuningData) { - context.ctrlMap[&controls::Gamma] = ControlInfo(0.1f, 10.0f, kDefaultGamma); - context.ctrlMap[&controls::Contrast] = ControlInfo(0.0f, 2.0f, 1.0f); + context.ctrlMap[&controls::Gamma] = + ControlInfo(0.1f, 10.0f, kDefaultGamma); + context.ctrlMap[&controls::Contrast] = + ControlInfo(0.0f, 2.0f, kDefaultContrast); if (context.ccmEnabled) - context.ctrlMap[&controls::Saturation] = ControlInfo(0.0f, 2.0f, 1.0f); + context.ctrlMap[&controls::Saturation] = + ControlInfo(0.0f, 2.0f, kDefaultSaturation); return 0; } @@ -122,7 +128,7 @@ void Adjust::process([[maybe_unused]] IPAContext &context, metadata.set(controls::Contrast, contrast.value()); const auto &saturation = frameContext.saturation; - metadata.set(controls::Saturation, saturation.value_or(1.0)); + metadata.set(controls::Saturation, saturation.value_or(kDefaultSaturation)); } REGISTER_IPA_ALGORITHM(Adjust, "Adjust") From patchwork Wed Jan 28 11:44:00 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Milan Zamazal X-Patchwork-Id: 26006 Return-Path: X-Original-To: parsemail@patchwork.libcamera.org Delivered-To: parsemail@patchwork.libcamera.org Received: from lancelot.ideasonboard.com (lancelot.ideasonboard.com [92.243.16.209]) by patchwork.libcamera.org (Postfix) with ESMTPS id E1893C3200 for ; Wed, 28 Jan 2026 11:44:49 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 4E27B61FE8; Wed, 28 Jan 2026 12:44:49 +0100 (CET) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=redhat.com header.i=@redhat.com header.b="FeXCFqjx"; dkim-atps=neutral Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 5E1C061FD0 for ; Wed, 28 Jan 2026 12:44:43 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1769600682; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=2k9B05U/io3wxXZ1qBvWXjn6uBX2Bf69MxM3QNtNULg=; b=FeXCFqjxyihsjSWDJPeRh135Ag9fr/YvqNrYji0IFHS6C3N5AinCEhhMr/ijdaXu3sH3ZL wMD6Ue4DHgbBcNQtuXcRkWHmB9t9ROfpC5LWTPPRB1pKHxRhfN4p4uIHqF7dVyJ28fPPvx e1ZyURHbSCZ3dgbKsBZNDMZA3cUS2VU= Received: from mx-prod-mc-01.mail-002.prod.us-west-2.aws.redhat.com (ec2-54-186-198-63.us-west-2.compute.amazonaws.com [54.186.198.63]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-90-PrxF4spXPpu7g_QDc2WRZQ-1; Wed, 28 Jan 2026 06:44:41 -0500 X-MC-Unique: PrxF4spXPpu7g_QDc2WRZQ-1 X-Mimecast-MFC-AGG-ID: PrxF4spXPpu7g_QDc2WRZQ_1769600680 Received: from mx-prod-int-03.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-03.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.12]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mx-prod-mc-01.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id DF13D1944F12; Wed, 28 Jan 2026 11:44:39 +0000 (UTC) Received: from mzamazal-thinkpadp1gen7.tpbc.com (unknown [10.44.34.236]) by mx-prod-int-03.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id B2D2719560B2; Wed, 28 Jan 2026 11:44:37 +0000 (UTC) From: Milan Zamazal To: libcamera-devel@lists.libcamera.org Cc: Milan Zamazal , Kieran Bingham , =?utf-8?q?Barnab=C3=A1s_P=C5=91cze?= , Robert Mader Subject: [PATCH v5 13/15] libcamera: ipa: simple: Set contrast metadata unconditionally Date: Wed, 28 Jan 2026 12:44:00 +0100 Message-ID: <20260128114402.31570-14-mzamazal@redhat.com> In-Reply-To: <20260128114402.31570-1-mzamazal@redhat.com> References: <20260128114402.31570-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_-jTULAiNJxzVqY_KVRbVU4xVZNLlqUuRZAJTQAZss_1769600680 X-Mimecast-Originator: redhat.com X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" Let's do the same with contrast as with other metadata. Reviewed-by: Barnabás Pőcze Reviewed-by: Robert Mader Signed-off-by: Milan Zamazal --- src/ipa/simple/algorithms/adjust.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/ipa/simple/algorithms/adjust.cpp b/src/ipa/simple/algorithms/adjust.cpp index 23f6f6ad7..acdd3f741 100644 --- a/src/ipa/simple/algorithms/adjust.cpp +++ b/src/ipa/simple/algorithms/adjust.cpp @@ -124,8 +124,7 @@ void Adjust::process([[maybe_unused]] IPAContext &context, metadata.set(controls::Gamma, gamma); const auto &contrast = frameContext.contrast; - if (contrast) - metadata.set(controls::Contrast, contrast.value()); + metadata.set(controls::Contrast, contrast.value_or(kDefaultContrast)); const auto &saturation = frameContext.saturation; metadata.set(controls::Saturation, saturation.value_or(kDefaultSaturation)); From patchwork Wed Jan 28 11:44:01 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Milan Zamazal X-Patchwork-Id: 26007 Return-Path: X-Original-To: parsemail@patchwork.libcamera.org Delivered-To: parsemail@patchwork.libcamera.org Received: from lancelot.ideasonboard.com (lancelot.ideasonboard.com [92.243.16.209]) by patchwork.libcamera.org (Postfix) with ESMTPS id 9AC79C3200 for ; Wed, 28 Jan 2026 11:44:54 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 4FE4261FE4; Wed, 28 Jan 2026 12:44:54 +0100 (CET) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=redhat.com header.i=@redhat.com header.b="ALKQHCFw"; dkim-atps=neutral Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.129.124]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id E303561FFB for ; Wed, 28 Jan 2026 12:44:46 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1769600685; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=k+iCQjV2NUF1iF+ojwzfN+nc3aZP+GAJTW2/8L9QxWo=; b=ALKQHCFwQc9W7Hj5V1qaOtrrPDOLXuY9oAAwYkFhakup2x/alWmRStfi7bxRC4+zvJjNj2 0A3rSQ8htbei0u26qE2mHB/wibkTKBHpYPe135gLuhNzO6lshf26OBwmzKcPUhsitqyeVW AKM+AMIN+KbPv9nJB+nwO4O4g2mb4AA= Received: from mx-prod-mc-01.mail-002.prod.us-west-2.aws.redhat.com (ec2-54-186-198-63.us-west-2.compute.amazonaws.com [54.186.198.63]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-425-TsbViuJJP6evFxi1I6Nyiw-1; Wed, 28 Jan 2026 06:44:43 -0500 X-MC-Unique: TsbViuJJP6evFxi1I6Nyiw-1 X-Mimecast-MFC-AGG-ID: TsbViuJJP6evFxi1I6Nyiw_1769600682 Received: from mx-prod-int-03.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-03.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.12]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mx-prod-mc-01.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id 8BD27194510A; Wed, 28 Jan 2026 11:44:42 +0000 (UTC) Received: from mzamazal-thinkpadp1gen7.tpbc.com (unknown [10.44.34.236]) by mx-prod-int-03.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id 646FC19560B2; Wed, 28 Jan 2026 11:44:40 +0000 (UTC) From: Milan Zamazal To: libcamera-devel@lists.libcamera.org Cc: Milan Zamazal , Kieran Bingham , Robert Mader Subject: [PATCH v5 14/15] libcamera: ipa: simple: Remove Lut algorithm Date: Wed, 28 Jan 2026 12:44:01 +0100 Message-ID: <20260128114402.31570-15-mzamazal@redhat.com> In-Reply-To: <20260128114402.31570-1-mzamazal@redhat.com> References: <20260128114402.31570-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: mejhFOHA_x0JOlDxeVCNau4hUgyWLNwwHKNQiobrEaU_1769600682 X-Mimecast-Originator: redhat.com 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: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" The Lut algorithm is not really an algorithm. Moreover, algorithms may be enabled or disabled but with Lut disabled, nothing will work. Let's move the construction of lookup tables to CPU debayering, where it is used. The implied and related changes are: - DebayerParams is changed to contain the real params rather than lookup tables. - contrastExp parameter introduced by GPU ISP is used for CPU ISP too. - The params must be initialised so that debayering gets meaningful parameter values even when some algorithms are disabled. - combinedMatrix must be put to params everywhere where it is modified. - Matrix changes needn't be tracked in the algorithms any more. - CPU debayering must watch for changes of the corresponding parameters to update the lookup tables when and only when needed. - Swapping red and blue is integrated into lookup table constructions. - gpuIspEnabled flags are removed as they are not needed any more. Reviewed-by: Robert Mader Signed-off-by: Milan Zamazal Acked-by: Kieran Bingham --- .../internal/software_isp/debayer_params.h | 43 +---- include/libcamera/ipa/soft.mojom | 3 +- src/ipa/simple/algorithms/adjust.cpp | 17 +- src/ipa/simple/algorithms/adjust.h | 4 - src/ipa/simple/algorithms/awb.cpp | 6 +- src/ipa/simple/algorithms/ccm.cpp | 1 - src/ipa/simple/algorithms/lut.cpp | 140 -------------- src/ipa/simple/algorithms/lut.h | 35 ---- src/ipa/simple/algorithms/meson.build | 1 - src/ipa/simple/data/uncalibrated.yaml | 1 - src/ipa/simple/ipa_context.h | 11 -- src/ipa/simple/soft_simple.cpp | 11 +- src/libcamera/software_isp/debayer.cpp | 172 +----------------- src/libcamera/software_isp/debayer.h | 8 - src/libcamera/software_isp/debayer_cpu.cpp | 136 ++++++++++++-- src/libcamera/software_isp/debayer_cpu.h | 26 ++- src/libcamera/software_isp/debayer_egl.cpp | 22 +-- src/libcamera/software_isp/software_isp.cpp | 29 +-- 18 files changed, 188 insertions(+), 478 deletions(-) delete mode 100644 src/ipa/simple/algorithms/lut.cpp delete mode 100644 src/ipa/simple/algorithms/lut.h diff --git a/include/libcamera/internal/software_isp/debayer_params.h b/include/libcamera/internal/software_isp/debayer_params.h index 2d69bd295..1c0412d75 100644 --- a/include/libcamera/internal/software_isp/debayer_params.h +++ b/include/libcamera/internal/software_isp/debayer_params.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ /* - * Copyright (C) 2023-2025 Red Hat Inc. + * Copyright (C) 2023-2026 Red Hat Inc. * * Authors: * Hans de Goede @@ -10,7 +10,6 @@ #pragma once -#include #include #include "libcamera/internal/matrix.h" @@ -19,47 +18,11 @@ namespace libcamera { struct DebayerParams { - static constexpr unsigned int kRGBLookupSize = 256; - - struct CcmColumn { - int16_t r; - int16_t g; - int16_t b; - }; - - using LookupTable = std::array; - using CcmLookupTable = std::array; - - /* - * Color lookup tables when CCM is not used. - * - * Each color of a debayered pixel is amended by the corresponding - * value in the given table. - */ - LookupTable red; - LookupTable green; - LookupTable blue; - - /* - * Color and gamma lookup tables when CCM is used. - * - * Each of the CcmLookupTable's corresponds to a CCM column; together they - * make a complete 3x3 CCM lookup table. The CCM is applied on debayered - * pixels and then the gamma lookup table is used to set the resulting - * values of all the three colors. - */ - CcmLookupTable redCcm; - CcmLookupTable greenCcm; - CcmLookupTable blueCcm; - LookupTable gammaLut; - - /* - * Per frame corrections as calculated by the IPA - */ - Matrix ccm; + Matrix combinedMatrix; RGB blackLevel; float gamma; float contrastExp; + RGB gains; }; } /* namespace libcamera */ diff --git a/include/libcamera/ipa/soft.mojom b/include/libcamera/ipa/soft.mojom index aff8fcbd3..77328c5fd 100644 --- a/include/libcamera/ipa/soft.mojom +++ b/include/libcamera/ipa/soft.mojom @@ -17,8 +17,7 @@ interface IPASoftInterface { libcamera.SharedFD fdStats, libcamera.SharedFD fdParams, libcamera.IPACameraSensorInfo sensorInfo, - libcamera.ControlInfoMap sensorControls, - bool gpuIspEnabled) + libcamera.ControlInfoMap sensorControls) => (int32 ret, libcamera.ControlInfoMap ipaControls, bool ccmEnabled); start() => (int32 ret); stop(); diff --git a/src/ipa/simple/algorithms/adjust.cpp b/src/ipa/simple/algorithms/adjust.cpp index acdd3f741..068e98404 100644 --- a/src/ipa/simple/algorithms/adjust.cpp +++ b/src/ipa/simple/algorithms/adjust.cpp @@ -95,23 +95,20 @@ void Adjust::applySaturation(Matrix &matrix, float saturation) void Adjust::prepare(IPAContext &context, [[maybe_unused]] const uint32_t frame, IPAFrameContext &frameContext, - [[maybe_unused]] DebayerParams *params) + DebayerParams *params) { frameContext.gamma = context.activeState.knobs.gamma; frameContext.contrast = context.activeState.knobs.contrast; - if (!context.ccmEnabled) - return; - auto &saturation = context.activeState.knobs.saturation; - frameContext.saturation = saturation; - if (saturation) + if (context.ccmEnabled && saturation) { applySaturation(context.activeState.combinedMatrix, saturation.value()); - - if (saturation != lastSaturation_) { - context.activeState.matrixChanged = true; - lastSaturation_ = saturation; + frameContext.saturation = saturation; } + + params->gamma = 1.0 / context.activeState.knobs.gamma; + const float contrast = context.activeState.knobs.contrast.value_or(kDefaultContrast); + params->contrastExp = tan(std::clamp(contrast * M_PI_4, 0.0, M_PI_2 - 0.00001)); } void Adjust::process([[maybe_unused]] IPAContext &context, diff --git a/src/ipa/simple/algorithms/adjust.h b/src/ipa/simple/algorithms/adjust.h index 7644138ff..fb133b140 100644 --- a/src/ipa/simple/algorithms/adjust.h +++ b/src/ipa/simple/algorithms/adjust.h @@ -7,8 +7,6 @@ #pragma once -#include - #include "libcamera/internal/matrix.h" #include @@ -45,8 +43,6 @@ public: private: void applySaturation(Matrix &ccm, float saturation); - - std::optional lastSaturation_; }; } /* namespace ipa::soft::algorithms */ diff --git a/src/ipa/simple/algorithms/awb.cpp b/src/ipa/simple/algorithms/awb.cpp index 31726658a..6fdaacaba 100644 --- a/src/ipa/simple/algorithms/awb.cpp +++ b/src/ipa/simple/algorithms/awb.cpp @@ -37,7 +37,7 @@ int Awb::configure(IPAContext &context, void Awb::prepare(IPAContext &context, [[maybe_unused]] const uint32_t frame, IPAFrameContext &frameContext, - [[maybe_unused]] DebayerParams *params) + DebayerParams *params) { auto &gains = context.activeState.awb.gains; Matrix gainMatrix = { { gains.r(), 0, 0, @@ -45,9 +45,11 @@ void Awb::prepare(IPAContext &context, 0, 0, gains.b() } }; context.activeState.combinedMatrix = context.activeState.combinedMatrix * gainMatrix; - /* Just report, the gains are applied in LUT algorithm. */ + frameContext.gains.red = gains.r(); frameContext.gains.blue = gains.b(); + + params->gains = gains; } void Awb::process(IPAContext &context, diff --git a/src/ipa/simple/algorithms/ccm.cpp b/src/ipa/simple/algorithms/ccm.cpp index 5576a301f..911a5af2c 100644 --- a/src/ipa/simple/algorithms/ccm.cpp +++ b/src/ipa/simple/algorithms/ccm.cpp @@ -51,7 +51,6 @@ void Ccm::prepare(IPAContext &context, [[maybe_unused]] const uint32_t frame, utils::abs_diff(ct, lastCt_) >= kTemperatureThreshold) { currentCcm_ = ccm_.getInterpolated(ct); lastCt_ = ct; - context.activeState.matrixChanged = true; } context.activeState.combinedMatrix = diff --git a/src/ipa/simple/algorithms/lut.cpp b/src/ipa/simple/algorithms/lut.cpp deleted file mode 100644 index fd442259a..000000000 --- a/src/ipa/simple/algorithms/lut.cpp +++ /dev/null @@ -1,140 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ -/* - * Copyright (C) 2024-2026, Red Hat Inc. - * - * Color lookup tables construction - */ - -#include "lut.h" - -#include -#include -#include -#include - -#include - -#include - -#include "simple/ipa_context.h" - -#include "adjust.h" - -namespace libcamera { - -LOG_DEFINE_CATEGORY(IPASoftLut) - -namespace ipa::soft::algorithms { - -int Lut::configure(IPAContext &context, - [[maybe_unused]] const IPAConfigInfo &configInfo) -{ - updateGammaTable(context); - - return 0; -} - -void Lut::updateGammaTable(IPAContext &context) -{ - const auto blackLevel = context.activeState.blc.level; - const auto gamma = 1.0 / context.activeState.knobs.gamma; - const auto contrast = context.activeState.knobs.contrast.value_or(1.0); - /* Convert 0..2 to 0..infinity; avoid actual inifinity at tan(pi/2) */ - float contrastExp = tan(std::clamp(contrast * M_PI_4, 0.0, M_PI_2 - 0.00001)); - - if (!context.gpuIspEnabled) { - auto &gammaTable = context.activeState.gamma.gammaTable; - const unsigned int blackIndex = blackLevel * gammaTable.size() / 256; - const float divisor = gammaTable.size() - blackIndex - 1.0; - for (unsigned int i = blackIndex; i < gammaTable.size(); i++) { - double normalized = (i - blackIndex) / divisor; - /* Apply simple S-curve */ - if (normalized < 0.5) - normalized = 0.5 * std::pow(normalized / 0.5, contrastExp); - else - normalized = 1.0 - 0.5 * std::pow((1.0 - normalized) / 0.5, contrastExp); - gammaTable[i] = UINT8_MAX * std::pow(normalized, gamma); - } - /* - * Due to CCM operations, the table lookup may reach indices below the black - * level. Let's set the table values below black level to the minimum - * non-black value to prevent problems when the minimum value is - * significantly non-zero (for example, when the image should be all grey). - */ - std::fill(gammaTable.begin(), gammaTable.begin() + blackIndex, - gammaTable[blackIndex]); - } - - context.activeState.gamma.gamma = gamma; - context.activeState.gamma.blackLevel = blackLevel; - context.activeState.gamma.contrastExp = contrastExp; -} - -int16_t Lut::matrixValue(unsigned int i, float ccm) const -{ - return std::round(i * ccm); -} - -void Lut::prepare(IPAContext &context, - [[maybe_unused]] const uint32_t frame, - [[maybe_unused]] IPAFrameContext &frameContext, - DebayerParams *params) -{ - /* - * Update the gamma table if needed. This means if black level changes - * and since the black level gets updated only if a lower value is - * observed, it's not permanently prone to minor fluctuations or - * rounding errors. - */ - const bool gammaUpdateNeeded = - context.activeState.gamma.blackLevel != context.activeState.blc.level || - context.activeState.gamma.contrast != context.activeState.knobs.contrast; - if (gammaUpdateNeeded) - updateGammaTable(context); - - auto &gains = context.activeState.awb.gains; - auto &gammaTable = context.activeState.gamma.gammaTable; - const unsigned int gammaTableSize = gammaTable.size(); - const double div = static_cast(DebayerParams::kRGBLookupSize) / - gammaTableSize; - - if (!context.ccmEnabled) { - for (unsigned int i = 0; i < DebayerParams::kRGBLookupSize; i++) { - /* Apply gamma after gain! */ - const RGB lutGains = (gains * i / div).min(gammaTableSize - 1); - params->red[i] = gammaTable[static_cast(lutGains.r())]; - params->green[i] = gammaTable[static_cast(lutGains.g())]; - params->blue[i] = gammaTable[static_cast(lutGains.b())]; - } - } else if (context.activeState.matrixChanged || gammaUpdateNeeded) { - auto &matrix = context.activeState.combinedMatrix; - auto &red = params->redCcm; - auto &green = params->greenCcm; - auto &blue = params->blueCcm; - params->ccm = matrix; - if (!context.gpuIspEnabled) { - for (unsigned int i = 0; i < DebayerParams::kRGBLookupSize; i++) { - red[i].r = matrixValue(i, matrix[0][0]); - red[i].g = matrixValue(i, matrix[1][0]); - red[i].b = matrixValue(i, matrix[2][0]); - green[i].r = matrixValue(i, matrix[0][1]); - green[i].g = matrixValue(i, matrix[1][1]); - green[i].b = matrixValue(i, matrix[2][1]); - blue[i].r = matrixValue(i, matrix[0][2]); - blue[i].g = matrixValue(i, matrix[1][2]); - blue[i].b = matrixValue(i, matrix[2][2]); - params->gammaLut[i] = gammaTable[i / div]; - } - } - context.activeState.matrixChanged = false; - } - - params->gamma = context.activeState.gamma.gamma; - params->contrastExp = context.activeState.gamma.contrastExp; -} - -REGISTER_IPA_ALGORITHM(Lut, "Lut") - -} /* namespace ipa::soft::algorithms */ - -} /* namespace libcamera */ diff --git a/src/ipa/simple/algorithms/lut.h b/src/ipa/simple/algorithms/lut.h deleted file mode 100644 index ad16d1e8e..000000000 --- a/src/ipa/simple/algorithms/lut.h +++ /dev/null @@ -1,35 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ -/* - * Copyright (C) 2024, Red Hat Inc. - * - * Color lookup tables construction - */ - -#pragma once - -#include "algorithm.h" - -namespace libcamera { - -namespace ipa::soft::algorithms { - -class Lut : public Algorithm -{ -public: - Lut() = default; - ~Lut() = default; - - int configure(IPAContext &context, const IPAConfigInfo &configInfo) override; - void prepare(IPAContext &context, - const uint32_t frame, - IPAFrameContext &frameContext, - DebayerParams *params) override; - -private: - void updateGammaTable(IPAContext &context); - int16_t matrixValue(unsigned int i, float ccm) const; -}; - -} /* namespace ipa::soft::algorithms */ - -} /* namespace libcamera */ diff --git a/src/ipa/simple/algorithms/meson.build b/src/ipa/simple/algorithms/meson.build index ebe9f20dd..73c637220 100644 --- a/src/ipa/simple/algorithms/meson.build +++ b/src/ipa/simple/algorithms/meson.build @@ -6,5 +6,4 @@ soft_simple_ipa_algorithms = files([ 'agc.cpp', 'blc.cpp', 'ccm.cpp', - 'lut.cpp', ]) diff --git a/src/ipa/simple/data/uncalibrated.yaml b/src/ipa/simple/data/uncalibrated.yaml index e389e0588..c6feda36d 100644 --- a/src/ipa/simple/data/uncalibrated.yaml +++ b/src/ipa/simple/data/uncalibrated.yaml @@ -15,6 +15,5 @@ algorithms: 0, 1, 0, 0, 0, 1] - Adjust: - - Lut: - Agc: ... diff --git a/src/ipa/simple/ipa_context.h b/src/ipa/simple/ipa_context.h index 293e35b71..34f7403a4 100644 --- a/src/ipa/simple/ipa_context.h +++ b/src/ipa/simple/ipa_context.h @@ -53,17 +53,7 @@ struct IPAActiveState { unsigned int temperatureK; } awb; - static constexpr unsigned int kGammaLookupSize = 1024; - struct { - std::array gammaTable; - uint8_t blackLevel; - float gamma; - float contrast; - float contrastExp; - } gamma; - Matrix combinedMatrix; - bool matrixChanged = false; struct { float gamma; @@ -103,7 +93,6 @@ struct IPAContext { FCQueue frameContexts; ControlInfoMap::Map ctrlMap; bool ccmEnabled = false; - bool gpuIspEnabled = false; }; } /* namespace ipa::soft */ diff --git a/src/ipa/simple/soft_simple.cpp b/src/ipa/simple/soft_simple.cpp index 732e82510..6bef597c8 100644 --- a/src/ipa/simple/soft_simple.cpp +++ b/src/ipa/simple/soft_simple.cpp @@ -26,6 +26,7 @@ #include "libcamera/internal/software_isp/swisp_stats.h" #include "libcamera/internal/yaml_parser.h" +#include "algorithms/adjust.h" #include "libipa/camera_sensor_helper.h" #include "module.h" @@ -55,7 +56,6 @@ public: const SharedFD &fdParams, const IPACameraSensorInfo &sensorInfo, const ControlInfoMap &sensorControls, - bool gpuIspEnabled, ControlInfoMap *ipaControls, bool *ccmEnabled) override; int configure(const IPAConfigInfo &configInfo) override; @@ -96,7 +96,6 @@ int IPASoftSimple::init(const IPASettings &settings, const SharedFD &fdParams, const IPACameraSensorInfo &sensorInfo, const ControlInfoMap &sensorControls, - bool gpuIspEnabled, ControlInfoMap *ipaControls, bool *ccmEnabled) { @@ -108,7 +107,6 @@ int IPASoftSimple::init(const IPASettings &settings, } context_.sensorInfo = sensorInfo; - context_.gpuIspEnabled = gpuIspEnabled; /* Load the tuning data file */ File file(settings.configurationFile); @@ -161,6 +159,11 @@ int IPASoftSimple::init(const IPASettings &settings, } params_ = static_cast(mem); + params_->blackLevel = { { 0.0, 0.0, 0.0 } }; + params_->gamma = 1.0 / algorithms::kDefaultGamma; + params_->contrastExp = 1.0; + params_->gains = { { 1.0, 1.0, 1.0 } }; + /* combinedMatrix is reset for each frame. */ } { @@ -287,6 +290,8 @@ void IPASoftSimple::computeParams(const uint32_t frame) IPAFrameContext &frameContext = context_.frameContexts.get(frame); for (auto const &algo : algorithms()) algo->prepare(context_, frame, frameContext, params_); + params_->combinedMatrix = context_.activeState.combinedMatrix; + setIspParams.emit(); } diff --git a/src/libcamera/software_isp/debayer.cpp b/src/libcamera/software_isp/debayer.cpp index 65a1762dd..dccdd86b4 100644 --- a/src/libcamera/software_isp/debayer.cpp +++ b/src/libcamera/software_isp/debayer.cpp @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ /* * Copyright (C) 2023, Linaro Ltd - * Copyright (C) 2023-2025 Red Hat Inc. + * Copyright (C) 2023-2026 Red Hat Inc. * * Authors: * Hans de Goede @@ -25,99 +25,28 @@ namespace libcamera { */ /** - * \var DebayerParams::kRGBLookupSize - * \brief Size of a color lookup table + * \var DebayerParams::gains + * \brief Colour channel gains */ /** - * \struct DebayerParams::CcmColumn - * \brief Type of a single column of a color correction matrix (CCM) - * - * When multiplying an input pixel, columns in the CCM correspond to the red, - * green or blue component of input pixel values, while rows correspond to the - * red, green or blue components of the output pixel values. The members of the - * CcmColumn structure are named after the colour components of the output pixel - * values they correspond to. - */ - -/** - * \var DebayerParams::CcmColumn::r - * \brief Red (first) component of a CCM column - */ - -/** - * \var DebayerParams::CcmColumn::g - * \brief Green (second) component of a CCM column - */ - -/** - * \var DebayerParams::CcmColumn::b - * \brief Blue (third) component of a CCM column - */ - -/** - * \typedef DebayerParams::LookupTable - * \brief Type of the lookup tables for single lookup values - */ - -/** - * \typedef DebayerParams::CcmLookupTable - * \brief Type of the CCM lookup tables for red, green, blue values - */ - -/** - * \var DebayerParams::red - * \brief Lookup table for red color, mapping input values to output values - */ - -/** - * \var DebayerParams::green - * \brief Lookup table for green color, mapping input values to output values - */ - -/** - * \var DebayerParams::blue - * \brief Lookup table for blue color, mapping input values to output values - */ - -/** - * \var DebayerParams::redCcm - * \brief Lookup table for the CCM red column, mapping input values to output values - */ - -/** - * \var DebayerParams::greenCcm - * \brief Lookup table for the CCM green column, mapping input values to output values - */ - -/** - * \var DebayerParams::blueCcm - * \brief Lookup table for the CCM blue column, mapping input values to output values - */ - -/** - * \var DebayerParams::gammaLut - * \brief Gamma lookup table used with color correction matrix - */ - -/** - * \var DebayerParams::ccm - * \brief Per frame colour correction matrix for GPUISP + * \var DebayerParams::combinedMatrix + * \brief Colour correction matrix, including other adjustments */ /** * \var DebayerParams::blackLevel - * \brief Blacklevel gains for the GPUISP + * \brief Black level values */ /** * \var DebayerParams::gamma - * \brief Gamma value for the GPUISP + * \brief Gamma value, e.g. 1/2.2 */ /** * \var DebayerParams::contrastExp - * \brief Contrast value for GPUISP + * \brief Contrast value to be used as an exponent */ /** @@ -131,13 +60,6 @@ LOG_DEFINE_CATEGORY(Debayer) Debayer::Debayer(const GlobalConfiguration &configuration) : bench_(configuration) { - /* Initialize color lookup tables */ - for (unsigned int i = 0; i < DebayerParams::kRGBLookupSize; i++) { - red_[i] = green_[i] = blue_[i] = i; - redCcm_[i] = { static_cast(i), 0, 0 }; - greenCcm_[i] = { 0, static_cast(i), 0 }; - blueCcm_[i] = { 0, 0, static_cast(i) }; - } } Debayer::~Debayer() @@ -305,56 +227,6 @@ Debayer::~Debayer() * \brief Output size object */ -/** - * \var Debayer::red_ - * \brief Lookup table for red channel gain and correction values - * - * This table provides precomputed per-pixel or per-intensity - * correction values for the red color channel used during debayering. - */ - -/** - * \var Debayer::green_ - * \brief Lookup table for green channel gain and correction values - * - * This table provides precomputed per-pixel or per-intensity - * correction values for the green color channel used during debayering. - */ - -/** - * \var Debayer::blue_ - * \brief Lookup table for blue channel gain and correction values - * - * This table provides precomputed per-pixel or per-intensity - * correction values for the blue color channel used during debayering. - */ - -/** - * \var Debayer::redCcm_ - * \brief Red channel Color Correction Matrix (CCM) lookup table - * - * Contains coefficients for green channel color correction. - */ - -/** - * \var Debayer::greenCcm_ - * \brief Green channel Color Correction Matrix (CCM) lookup table - * - * Contains coefficients for green channel color correction. - */ - -/** - * \var Debayer::blueCcm_ - * \brief Blue channel Color Correction Matrix (CCM) lookup table - * - * Contains coefficients for blue channel color correction. - */ - -/** - * \var Debayer::gammaLut_ - * \brief Gamma correction lookup table - */ - /** * \var Debayer::swapRedBlueGains_ * \brief Flag indicating whether red and blue channel gains should be swapped @@ -396,34 +268,6 @@ Debayer::~Debayer() * DebayerEGL::start. */ -/** - * \fn void Debayer::setParams(DebayerParams ¶ms) - * \brief Select the bayer params to use for the next frame debayer - * \param[in] params The parameters to be used in debayering - */ -void Debayer::setParams(DebayerParams ¶ms) -{ - green_ = params.green; - greenCcm_ = params.greenCcm; - if (swapRedBlueGains_) { - red_ = params.blue; - blue_ = params.red; - redCcm_ = params.blueCcm; - blueCcm_ = params.redCcm; - for (unsigned int i = 0; i < 256; i++) { - std::swap(redCcm_[i].r, redCcm_[i].b); - std::swap(greenCcm_[i].r, greenCcm_[i].b); - std::swap(blueCcm_[i].r, blueCcm_[i].b); - } - } else { - red_ = params.red; - blue_ = params.blue; - redCcm_ = params.redCcm; - blueCcm_ = params.blueCcm; - } - gammaLut_ = params.gammaLut; -} - /** * \fn void Debayer::dmaSyncBegin(DebayerParams ¶ms) * \brief Common CPU/GPU Dma Sync Buffer begin diff --git a/src/libcamera/software_isp/debayer.h b/src/libcamera/software_isp/debayer.h index cd2db9930..652cff4cc 100644 --- a/src/libcamera/software_isp/debayer.h +++ b/src/libcamera/software_isp/debayer.h @@ -78,13 +78,6 @@ public: Size outputSize_; PixelFormat inputPixelFormat_; PixelFormat outputPixelFormat_; - DebayerParams::LookupTable red_; - DebayerParams::LookupTable green_; - DebayerParams::LookupTable blue_; - DebayerParams::CcmLookupTable redCcm_; - DebayerParams::CcmLookupTable greenCcm_; - DebayerParams::CcmLookupTable blueCcm_; - DebayerParams::LookupTable gammaLut_; bool swapRedBlueGains_; Benchmark bench_; @@ -92,7 +85,6 @@ private: virtual Size patternSize(PixelFormat inputFormat) = 0; protected: - void setParams(DebayerParams ¶ms); void dmaSyncBegin(std::vector &dmaSyncers, FrameBuffer *input, FrameBuffer *output); static bool isStandardBayerOrder(BayerFormat::Order order); }; diff --git a/src/libcamera/software_isp/debayer_cpu.cpp b/src/libcamera/software_isp/debayer_cpu.cpp index 00738c56b..af7af0a8d 100644 --- a/src/libcamera/software_isp/debayer_cpu.cpp +++ b/src/libcamera/software_isp/debayer_cpu.cpp @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ /* * Copyright (C) 2023, Linaro Ltd - * Copyright (C) 2023-2025 Red Hat Inc. + * Copyright (C) 2023-2026 Red Hat Inc. * * Authors: * Hans de Goede @@ -68,21 +68,21 @@ DebayerCpu::~DebayerCpu() = default; #define GAMMA(value) \ *dst++ = gammaLut_[std::clamp(value, 0, static_cast(gammaLut_.size()) - 1)] -#define STORE_PIXEL(b_, g_, r_) \ - if constexpr (ccmEnabled) { \ - const DebayerParams::CcmColumn &blue = blueCcm_[b_]; \ - const DebayerParams::CcmColumn &green = greenCcm_[g_]; \ - const DebayerParams::CcmColumn &red = redCcm_[r_]; \ - GAMMA(blue.b + green.b + red.b); \ - GAMMA(blue.g + green.g + red.g); \ - GAMMA(blue.r + green.r + red.r); \ - } else { \ - *dst++ = blue_[b_]; \ - *dst++ = green_[g_]; \ - *dst++ = red_[r_]; \ - } \ - if constexpr (addAlphaByte) \ - *dst++ = 255; \ +#define STORE_PIXEL(b_, g_, r_) \ + if constexpr (ccmEnabled) { \ + const CcmColumn &blue = blueCcm_[b_]; \ + const CcmColumn &green = greenCcm_[g_]; \ + const CcmColumn &red = redCcm_[r_]; \ + GAMMA(blue.b + green.b + red.b); \ + GAMMA(blue.g + green.g + red.g); \ + GAMMA(blue.r + green.r + red.r); \ + } else { \ + *dst++ = blue_[b_]; \ + *dst++ = green_[g_]; \ + *dst++ = red_[r_]; \ + } \ + if constexpr (addAlphaByte) \ + *dst++ = 255; \ x++; /* @@ -525,6 +525,16 @@ int DebayerCpu::configure(const StreamConfiguration &inputCfg, if (ret != 0) return -EINVAL; + ccmEnabled_ = ccmEnabled; + + /* + * Lookup tables must be initialized because the initial value is used for + * the first two frames, i.e. until stats processing starts providing its + * own parameters. Let's enforce recomputing lookup tables by setting the + * stored last used gamma to an out-of-range value. + */ + params_.gamma = 1.0; + window_.x = ((inputCfg.size.width - outputCfg.size.width) / 2) & ~(inputConfig_.patternSize.width - 1); window_.y = ((inputCfg.size.height - outputCfg.size.height) / 2) & @@ -740,6 +750,98 @@ void DebayerCpu::process4(uint32_t frame, const uint8_t *src, uint8_t *dst) } } +void DebayerCpu::updateGammaTable(DebayerParams ¶ms) +{ + const RGB blackLevel = params.blackLevel; + /* Take let's say the green channel black level */ + const unsigned int blackIndex = blackLevel[1] * gammaTable_.size(); + const float gamma = params.gamma; + const float contrastExp = params.contrastExp; + + const float divisor = gammaTable_.size() - blackIndex - 1.0; + for (unsigned int i = blackIndex; i < gammaTable_.size(); i++) { + float normalized = (i - blackIndex) / divisor; + /* Convert 0..2 to 0..infinity; avoid actual inifinity at tan(pi/2) */ + /* Apply simple S-curve */ + if (normalized < 0.5) + normalized = 0.5 * std::pow(normalized / 0.5, contrastExp); + else + normalized = 1.0 - 0.5 * std::pow((1.0 - normalized) / 0.5, contrastExp); + gammaTable_[i] = UINT8_MAX * + std::pow(normalized, gamma); + } + /* + * Due to CCM operations, the table lookup may reach indices below the black + * level. Let's set the table values below black level to the minimum + * non-black value to prevent problems when the minimum value is + * significantly non-zero (for example, when the image should be all grey). + */ + std::fill(gammaTable_.begin(), gammaTable_.begin() + blackIndex, + gammaTable_[blackIndex]); +} + +void DebayerCpu::updateLookupTables(DebayerParams ¶ms) +{ + const bool gammaUpdateNeeded = + params.gamma != params_.gamma || + params.blackLevel != params_.blackLevel || + params.contrastExp != params_.contrastExp; + if (gammaUpdateNeeded) + updateGammaTable(params); + + auto matrixChanged = [](const Matrix &m1, const Matrix &m2) -> bool { + return !std::equal(m1.data().begin(), m1.data().end(), m2.data().begin()); + }; + const unsigned int gammaTableSize = gammaTable_.size(); + const double div = static_cast(kRGBLookupSize) / gammaTableSize; + if (ccmEnabled_) { + if (gammaUpdateNeeded || + matrixChanged(params.combinedMatrix, params_.combinedMatrix)) { + auto &red = swapRedBlueGains_ ? blueCcm_ : redCcm_; + auto &green = greenCcm_; + auto &blue = swapRedBlueGains_ ? redCcm_ : blueCcm_; + const unsigned int redIndex = swapRedBlueGains_ ? 2 : 0; + const unsigned int greenIndex = 1; + const unsigned int blueIndex = swapRedBlueGains_ ? 0 : 2; + for (unsigned int i = 0; i < kRGBLookupSize; i++) { + red[i].r = std::round(i * params.combinedMatrix[redIndex][0]); + red[i].g = std::round(i * params.combinedMatrix[greenIndex][0]); + red[i].b = std::round(i * params.combinedMatrix[blueIndex][0]); + green[i].r = std::round(i * params.combinedMatrix[redIndex][1]); + green[i].g = std::round(i * params.combinedMatrix[greenIndex][1]); + green[i].b = std::round(i * params.combinedMatrix[blueIndex][1]); + blue[i].r = std::round(i * params.combinedMatrix[redIndex][2]); + blue[i].g = std::round(i * params.combinedMatrix[greenIndex][2]); + blue[i].b = std::round(i * params.combinedMatrix[blueIndex][2]); + gammaLut_[i] = gammaTable_[i / div]; + } + } + } else { + if (gammaUpdateNeeded || params.gains != params_.gains) { + auto &gains = params.gains; + auto &red = swapRedBlueGains_ ? blue_ : red_; + auto &green = green_; + auto &blue = swapRedBlueGains_ ? red_ : blue_; + for (unsigned int i = 0; i < kRGBLookupSize; i++) { + /* Apply gamma after gain! */ + const RGB lutGains = (gains * i / div).min(gammaTableSize - 1); + red[i] = gammaTable_[static_cast(lutGains.r())]; + green[i] = gammaTable_[static_cast(lutGains.g())]; + blue[i] = gammaTable_[static_cast(lutGains.b())]; + } + } + } + + LOG(Debayer, Debug) + << "Debayer parameters: blackLevel=" << params.blackLevel + << "; gamma=" << params.gamma + << "; contrastExp=" << params.contrastExp + << "; gains=" << params.gains + << "; matrix=" << params.combinedMatrix; + + params_ = params; +} + void DebayerCpu::process(uint32_t frame, FrameBuffer *input, FrameBuffer *output, DebayerParams params) { bench_.startFrame(); @@ -748,7 +850,7 @@ void DebayerCpu::process(uint32_t frame, FrameBuffer *input, FrameBuffer *output dmaSyncBegin(dmaSyncers, input, output); - setParams(params); + updateLookupTables(params); /* Copy metadata from the input buffer */ FrameMetadata &metadata = output->_d()->metadata(); diff --git a/src/libcamera/software_isp/debayer_cpu.h b/src/libcamera/software_isp/debayer_cpu.h index 67df2b93a..b5cbb5bd2 100644 --- a/src/libcamera/software_isp/debayer_cpu.h +++ b/src/libcamera/software_isp/debayer_cpu.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ /* * Copyright (C) 2023, Linaro Ltd - * Copyright (C) 2023-2025 Red Hat Inc. + * Copyright (C) 2023-2026 Red Hat Inc. * * Authors: * Hans de Goede @@ -18,6 +18,8 @@ #include #include "libcamera/internal/bayer_format.h" +#include "libcamera/internal/global_configuration.h" +#include "libcamera/internal/software_isp/debayer_params.h" #include "libcamera/internal/software_isp/swstats_cpu.h" #include "debayer.h" @@ -108,10 +110,32 @@ private: void memcpyNextLine(const uint8_t *linePointers[]); void process2(uint32_t frame, const uint8_t *src, uint8_t *dst); void process4(uint32_t frame, const uint8_t *src, uint8_t *dst); + void updateGammaTable(DebayerParams ¶ms); + void updateLookupTables(DebayerParams ¶ms); /* Max. supported Bayer pattern height is 4, debayering this requires 5 lines */ static constexpr unsigned int kMaxLineBuffers = 5; + static constexpr unsigned int kRGBLookupSize = 256; + static constexpr unsigned int kGammaLookupSize = 1024; + struct CcmColumn { + int16_t r; + int16_t g; + int16_t b; + }; + using LookupTable = std::array; + using CcmLookupTable = std::array; + LookupTable red_; + LookupTable green_; + LookupTable blue_; + CcmLookupTable redCcm_; + CcmLookupTable greenCcm_; + CcmLookupTable blueCcm_; + std::array gammaTable_; + LookupTable gammaLut_; + bool ccmEnabled_; + DebayerParams params_; + debayerFn debayer0_; debayerFn debayer1_; debayerFn debayer2_; diff --git a/src/libcamera/software_isp/debayer_egl.cpp b/src/libcamera/software_isp/debayer_egl.cpp index 37c44e8b1..722778103 100644 --- a/src/libcamera/software_isp/debayer_egl.cpp +++ b/src/libcamera/software_isp/debayer_egl.cpp @@ -475,18 +475,18 @@ void DebayerEGL::setShaderVariableValues(DebayerParams ¶ms) << " textureUniformProjMatrix_ " << textureUniformProjMatrix_; GLfloat ccm[9] = { - params.ccm[0][0], - params.ccm[0][1], - params.ccm[0][2], - params.ccm[1][0], - params.ccm[1][1], - params.ccm[1][2], - params.ccm[2][0], - params.ccm[2][1], - params.ccm[2][2], + params.combinedMatrix[0][0], + params.combinedMatrix[0][1], + params.combinedMatrix[0][2], + params.combinedMatrix[1][0], + params.combinedMatrix[1][1], + params.combinedMatrix[1][2], + params.combinedMatrix[2][0], + params.combinedMatrix[2][1], + params.combinedMatrix[2][2], }; glUniformMatrix3fv(ccmUniformDataIn_, 1, GL_FALSE, ccm); - LOG(Debayer, Debug) << " ccmUniformDataIn_ " << ccmUniformDataIn_ << " data " << params.ccm; + LOG(Debayer, Debug) << " ccmUniformDataIn_ " << ccmUniformDataIn_ << " data " << params.combinedMatrix; /* * 0 = Red, 1 = Green, 2 = Blue @@ -544,8 +544,6 @@ void DebayerEGL::process(uint32_t frame, FrameBuffer *input, FrameBuffer *output dmaSyncBegin(dmaSyncers, input, nullptr); - setParams(params); - /* Copy metadata from the input buffer */ FrameMetadata &metadata = output->_d()->metadata(); metadata.status = input->metadata().status; diff --git a/src/libcamera/software_isp/software_isp.cpp b/src/libcamera/software_isp/software_isp.cpp index 7ad3511db..a83986b78 100644 --- a/src/libcamera/software_isp/software_isp.cpp +++ b/src/libcamera/software_isp/software_isp.cpp @@ -84,23 +84,6 @@ SoftwareIsp::SoftwareIsp(PipelineHandler *pipe, const CameraSensor *sensor, DmaBufAllocator::DmaBufAllocatorFlag::SystemHeap | DmaBufAllocator::DmaBufAllocatorFlag::UDmaBuf) { - /* - * debayerParams_ must be initialized because the initial value is used for - * the first two frames, i.e. until stats processing starts providing its - * own parameters. - * - * \todo This should be handled in the same place as the related - * operations, in the IPA module. - */ - std::array gammaTable; - for (unsigned int i = 0; i < 256; i++) - gammaTable[i] = UINT8_MAX * std::pow(i / 256.0, 0.5); - for (unsigned int i = 0; i < DebayerParams::kRGBLookupSize; i++) { - debayerParams_.red[i] = gammaTable[i]; - debayerParams_.green[i] = gammaTable[i]; - debayerParams_.blue[i] = gammaTable[i]; - } - if (!dmaHeap_.isValid()) { LOG(SoftwareIsp, Error) << "Failed to create DmaBufAllocator object"; return; @@ -121,8 +104,6 @@ SoftwareIsp::SoftwareIsp(PipelineHandler *pipe, const CameraSensor *sensor, } stats->statsReady.connect(this, &SoftwareIsp::statsReady); - bool gpuIspEnabled; - #if HAVE_DEBAYER_EGL std::optional softISPMode = configuration.envOption("LIBCAMERA_SOFTISP_MODE", { "software_isp", "mode" }); if (softISPMode) { @@ -133,15 +114,12 @@ SoftwareIsp::SoftwareIsp(PipelineHandler *pipe, const CameraSensor *sensor, } } - if (!softISPMode || softISPMode == "gpu") { + if (!softISPMode || softISPMode == "gpu") debayer_ = std::make_unique(std::move(stats), configuration); - gpuIspEnabled = true; - } + #endif - if (!debayer_) { + if (!debayer_) debayer_ = std::make_unique(std::move(stats), configuration); - gpuIspEnabled = false; - } debayer_->inputBufferReady.connect(this, &SoftwareIsp::inputReady); debayer_->outputBufferReady.connect(this, &SoftwareIsp::outputReady); @@ -173,7 +151,6 @@ SoftwareIsp::SoftwareIsp(PipelineHandler *pipe, const CameraSensor *sensor, sharedParams_.fd(), sensorInfo, sensor->controls(), - gpuIspEnabled, ipaControls, &ccmEnabled_); if (ret) { From patchwork Wed Jan 28 11:44:02 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Milan Zamazal X-Patchwork-Id: 26008 Return-Path: X-Original-To: parsemail@patchwork.libcamera.org Delivered-To: parsemail@patchwork.libcamera.org Received: from lancelot.ideasonboard.com (lancelot.ideasonboard.com [92.243.16.209]) by patchwork.libcamera.org (Postfix) with ESMTPS id 141DCC3200 for ; Wed, 28 Jan 2026 11:44:56 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 8147562000; Wed, 28 Jan 2026 12:44:55 +0100 (CET) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=redhat.com header.i=@redhat.com header.b="hIGCOJk6"; dkim-atps=neutral Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 8902661FCB for ; Wed, 28 Jan 2026 12:44:50 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1769600689; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=vlyMaPVkPiRoxMTcp+RNi7WZm5uBO/L6zWBKZuwBYF8=; b=hIGCOJk6T6C4W7N/w9PkbYpfOqAMsoxcZjTG7Wtviueu7MoM+6NEBoPnYJbu2le638MN8I 9cq9bPoLQEtN9hy3f/OMFYunZ2lpaxhCsnczzmtDuGG8sLU9x+RwMiFwKGIxWqFMpHzHDy c8ykKmdlvUwgObILwhN0fXX1SUuqTN4= Received: from mx-prod-mc-06.mail-002.prod.us-west-2.aws.redhat.com (ec2-35-165-154-97.us-west-2.compute.amazonaws.com [35.165.154.97]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-584-X4iOO7ZSOdqK6YLWONfK1Q-1; Wed, 28 Jan 2026 06:44:46 -0500 X-MC-Unique: X4iOO7ZSOdqK6YLWONfK1Q-1 X-Mimecast-MFC-AGG-ID: X4iOO7ZSOdqK6YLWONfK1Q_1769600685 Received: from mx-prod-int-03.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-03.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.12]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mx-prod-mc-06.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id 1E8BF18005AD; Wed, 28 Jan 2026 11:44:45 +0000 (UTC) Received: from mzamazal-thinkpadp1gen7.tpbc.com (unknown [10.44.34.236]) by mx-prod-int-03.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id 26FAD19560B2; Wed, 28 Jan 2026 11:44:42 +0000 (UTC) From: Milan Zamazal To: libcamera-devel@lists.libcamera.org Cc: Milan Zamazal , Kieran Bingham , =?utf-8?q?Barnab=C3=A1s_P=C5=91cze?= , Robert Mader Subject: [PATCH v5 15/15] libcamera: ipa: simple: Disable Ccm algorithm by default again Date: Wed, 28 Jan 2026 12:44:02 +0100 Message-ID: <20260128114402.31570-16-mzamazal@redhat.com> In-Reply-To: <20260128114402.31570-1-mzamazal@redhat.com> References: <20260128114402.31570-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: yA4PgvtCqzC6Z-jwcLnyvFZLR_W5OIwobtNq56I6a_0_1769600685 X-Mimecast-Originator: redhat.com X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" The default CCM in uncalibrated.yaml is just an identity transformation and has been enabled by default only to always provide a correction matrix to GPU ISP. It slows down CPU ISP when CCM is not used. Now, when a default correction matrix is always provided to GPU ISP, we can disable the Ccm algorithm in uncalibrated.yaml again. The check for ccmEnabled in GPU ISP is no longer needed and it must be removed in order not to fail when Ccm algorithm is not enabled. ccmEnabled flag is still needed in CPU ISP where the processing differs based on whether CCM is present or not. Reviewed-by: Barnabás Pőcze Reviewed-by: Robert Mader Signed-off-by: Milan Zamazal --- src/ipa/simple/data/uncalibrated.yaml | 12 ++++++------ src/libcamera/software_isp/debayer_egl.cpp | 5 +---- 2 files changed, 7 insertions(+), 10 deletions(-) diff --git a/src/ipa/simple/data/uncalibrated.yaml b/src/ipa/simple/data/uncalibrated.yaml index c6feda36d..fc90ca526 100644 --- a/src/ipa/simple/data/uncalibrated.yaml +++ b/src/ipa/simple/data/uncalibrated.yaml @@ -8,12 +8,12 @@ algorithms: # Color correction matrices can be defined here. The CCM algorithm # has a significant performance impact, and should only be enabled # if tuned. - - Ccm: - ccms: - - ct: 6500 - ccm: [ 1, 0, 0, - 0, 1, 0, - 0, 0, 1] + # - Ccm: + # ccms: + # - ct: 6500 + # ccm: [ 1, 0, 0, + # 0, 1, 0, + # 0, 0, 1] - Adjust: - Agc: ... diff --git a/src/libcamera/software_isp/debayer_egl.cpp b/src/libcamera/software_isp/debayer_egl.cpp index 722778103..4d0caa6ef 100644 --- a/src/libcamera/software_isp/debayer_egl.cpp +++ b/src/libcamera/software_isp/debayer_egl.cpp @@ -288,7 +288,7 @@ unsigned int DebayerEGL::frameSize() int DebayerEGL::configure(const StreamConfiguration &inputCfg, const std::vector> &outputCfgs, - bool ccmEnabled) + [[maybe_unused]] bool ccmEnabled) { if (getInputConfig(inputCfg.pixelFormat, inputConfig_) != 0) return -EINVAL; @@ -296,9 +296,6 @@ int DebayerEGL::configure(const StreamConfiguration &inputCfg, if (stats_->configure(inputCfg) != 0) return -EINVAL; - if (!ccmEnabled) - return -EINVAL; - const Size &stats_pattern_size = stats_->patternSize(); if (inputConfig_.patternSize.width != stats_pattern_size.width || inputConfig_.patternSize.height != stats_pattern_size.height) {