From patchwork Wed Jan 14 11:30:03 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Milan Zamazal X-Patchwork-Id: 25766 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 A7D73C3226 for ; Wed, 14 Jan 2026 11:30:44 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 5621D61FC0; Wed, 14 Jan 2026 12:30:44 +0100 (CET) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=redhat.com header.i=@redhat.com header.b="g1qpiN4O"; 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 2BF8261FB9 for ; Wed, 14 Jan 2026 12:30:43 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1768390242; 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=i8UGhkmLt6QS6rqX1oJshBUPoYg/mDS32mL7uD/INFs=; b=g1qpiN4OW54bC1G5QXElh9sIYL6uCCspkULoRRp8/JTfToLh6TkpQyt0HBYioRe02bqmyB aF3FciYAAjpppLGqaQSYbHLnpD6RrSTo++5Ec2c/b/hb5eowtVphJ11wrC2847S+pKT2jG N4Yk964hwiARA1V8qGzqOP2AdABAFZY= 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-554-eYXXGYhFMge6Lj2OMMPgYA-1; Wed, 14 Jan 2026 06:30:38 -0500 X-MC-Unique: eYXXGYhFMge6Lj2OMMPgYA-1 X-Mimecast-MFC-AGG-ID: eYXXGYhFMge6Lj2OMMPgYA_1768390237 Received: from mx-prod-int-06.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-06.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.93]) (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 822661800451; Wed, 14 Jan 2026 11:30:37 +0000 (UTC) Received: from mzamazal-thinkpadp1gen7.tpbc.com (unknown [10.44.32.217]) by mx-prod-int-06.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id 4B54B180009E; Wed, 14 Jan 2026 11:30:35 +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 v3 01/14] libcamera: ipa: simple: Remove an unused include from awb.cpp Date: Wed, 14 Jan 2026 12:30:03 +0100 Message-ID: <20260114113016.25162-2-mzamazal@redhat.com> In-Reply-To: <20260114113016.25162-1-mzamazal@redhat.com> References: <20260114113016.25162-1-mzamazal@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.4.1 on 10.30.177.93 X-Mimecast-Spam-Score: 0 X-Mimecast-MFC-PROC-ID: 6msG_yp5eBbz2GcKE4_q1D4FgA9pYyato-6Ekeau-d8_1768390237 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 0080865aa..a391359fb 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 14 11:30:04 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Milan Zamazal X-Patchwork-Id: 25767 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 41C85C3226 for ; Wed, 14 Jan 2026 11:30:48 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id E962961FC0; Wed, 14 Jan 2026 12:30:47 +0100 (CET) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=redhat.com header.i=@redhat.com header.b="iFdWxJal"; 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 941BF61FB9 for ; Wed, 14 Jan 2026 12:30:45 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1768390244; 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=iFdWxJalo/Mcf8q+cCfY8lC+zwKvZ9S3V3RNnoYx8mmPvXvza17SrS3lcqxjrcXIpBV5ua SCPWfu+qRP/cWavEgWRVBtShhPnQI+Uk66tIn5kolt7e6X2QDLidmO9/xGDumz0h1ORgX6 otRnKt2WzKNGY6aSqzS/iLxKjMMJmFA= 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-564-HaiHmLmONdaFUQD5sIwKPg-1; Wed, 14 Jan 2026 06:30:41 -0500 X-MC-Unique: HaiHmLmONdaFUQD5sIwKPg-1 X-Mimecast-MFC-AGG-ID: HaiHmLmONdaFUQD5sIwKPg_1768390240 Received: from mx-prod-int-06.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-06.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.93]) (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 07439180044D; Wed, 14 Jan 2026 11:30:40 +0000 (UTC) Received: from mzamazal-thinkpadp1gen7.tpbc.com (unknown [10.44.32.217]) by mx-prod-int-06.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id 1614218004D8; Wed, 14 Jan 2026 11:30: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?= Subject: [PATCH v3 02/14] libcamera: ipa: simple: Unwrap IPAFrameContext::ccm Date: Wed, 14 Jan 2026 12:30:04 +0100 Message-ID: <20260114113016.25162-3-mzamazal@redhat.com> In-Reply-To: <20260114113016.25162-1-mzamazal@redhat.com> References: <20260114113016.25162-1-mzamazal@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.4.1 on 10.30.177.93 X-Mimecast-Spam-Score: 0 X-Mimecast-MFC-PROC-ID: HTt8XBhPImg0-a7jwsIz493zKa8tN48QW-TzQaWLnIM_1768390240 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 14 11:30:05 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Milan Zamazal X-Patchwork-Id: 25768 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 C769FC3274 for ; Wed, 14 Jan 2026 11:30:49 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 6FE6261FC7; Wed, 14 Jan 2026 12:30:49 +0100 (CET) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=redhat.com header.i=@redhat.com header.b="eOKoUNAb"; 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 B8D8C61FB9 for ; Wed, 14 Jan 2026 12:30:47 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1768390246; 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=7s8kUBGb/ewwAhYOgqJBuZjOjjtQgQvsgbnAUqWXVdk=; b=eOKoUNAbIVeOjzWM3v2WJuojwljBhfR8HgttIV/Yrcf7QAWcgqkX+TZRW19hvYNu/QDxCv OHtQz1dbburTrlhZzWsWtEuTfvPlJvNxdvbpImEhUGWm5P7DlMPTNYeabh7HsPU4C3EP1O DKTsAfp+1dBSakfuuD+6O6IoOts4W24= 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-192-WJfJFir2Nranby7z8gRb0Q-1; Wed, 14 Jan 2026 06:30:43 -0500 X-MC-Unique: WJfJFir2Nranby7z8gRb0Q-1 X-Mimecast-MFC-AGG-ID: WJfJFir2Nranby7z8gRb0Q_1768390242 Received: from mx-prod-int-06.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-06.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.93]) (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 75CD7195609D; Wed, 14 Jan 2026 11:30:42 +0000 (UTC) Received: from mzamazal-thinkpadp1gen7.tpbc.com (unknown [10.44.32.217]) by mx-prod-int-06.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id 969AB1800665; Wed, 14 Jan 2026 11:30:40 +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 v3 03/14] libcamera: ipa: simple: Generalise tracking matrix changes Date: Wed, 14 Jan 2026 12:30:05 +0100 Message-ID: <20260114113016.25162-4-mzamazal@redhat.com> In-Reply-To: <20260114113016.25162-1-mzamazal@redhat.com> References: <20260114113016.25162-1-mzamazal@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.4.1 on 10.30.177.93 X-Mimecast-Spam-Score: 0 X-Mimecast-MFC-PROC-ID: _NjZtxjuIxmZeFHOiVWBiE9h6KrESPrpDZHrQiHn_7k_1768390242 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" 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. 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 14 11:30:06 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Milan Zamazal X-Patchwork-Id: 25769 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 E463FC3226 for ; Wed, 14 Jan 2026 11:30:51 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 8906E61FCE; Wed, 14 Jan 2026 12:30:51 +0100 (CET) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=redhat.com header.i=@redhat.com header.b="IL3kGbW6"; 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 698F461FC6 for ; Wed, 14 Jan 2026 12:30:49 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1768390248; 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=tZOp3CsI562qtIBQ0NQKng4KLDkYMn95J0crTZ2gjew=; b=IL3kGbW6bhZlfJ/seete3GJ/tRYx4xg3XXS4xhWr8PjrxLO4xssuqmpk5bskIHl9pfil9w 4T/y70tjKu1X2CzBx3HQZvAcrZyhsOIz6inPYCmMeo6g2FFFEgljckN9Akwxzj6SGJVoPh gcytCS+mshpZOd92Qcvszu53OFPMInU= 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-495-ywmWCMmOMaOjd2xz25-PmQ-1; Wed, 14 Jan 2026 06:30:45 -0500 X-MC-Unique: ywmWCMmOMaOjd2xz25-PmQ-1 X-Mimecast-MFC-AGG-ID: ywmWCMmOMaOjd2xz25-PmQ_1768390245 Received: from mx-prod-int-06.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-06.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.93]) (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 0A17518005B2; Wed, 14 Jan 2026 11:30:45 +0000 (UTC) Received: from mzamazal-thinkpadp1gen7.tpbc.com (unknown [10.44.32.217]) by mx-prod-int-06.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id 11C68180009E; Wed, 14 Jan 2026 11:30: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?= Subject: [PATCH v3 04/14] libcamera: ipa: simple: Rename "ccm" identifiers not specific to CCM Date: Wed, 14 Jan 2026 12:30:06 +0100 Message-ID: <20260114113016.25162-5-mzamazal@redhat.com> In-Reply-To: <20260114113016.25162-1-mzamazal@redhat.com> References: <20260114113016.25162-1-mzamazal@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.4.1 on 10.30.177.93 X-Mimecast-Spam-Score: 0 X-Mimecast-MFC-PROC-ID: in1yl85la2zEYsxSv_rJsXPgDxz-hn-8S4PGfYa7T54_1768390245 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" 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. 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 14 11:30:07 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Milan Zamazal X-Patchwork-Id: 25770 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 58F02C3274 for ; Wed, 14 Jan 2026 11:30:53 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 1C07261FC6; Wed, 14 Jan 2026 12:30:53 +0100 (CET) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=redhat.com header.i=@redhat.com header.b="Aq4AJErk"; 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 D50EB61FBC for ; Wed, 14 Jan 2026 12:30:50 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1768390249; 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=GogNUcizGWkqY1wGn5jwu0XlCeZ9LCdwytV7K+BS068=; b=Aq4AJErkNXBhXsw6ZD3kOg6UCPxp+EiV/kj0GZsTCxojXmO1+I/VOitnWEKuvYByR2FLEJ 62ZHaC9qnFa7a/sgdqjjMmUMT6GJux9QtB7rzSeTPeHWn+y6IkxqDmGQROZMmpIMkQ/Rh+ 5MGkAOwPXybanTf5sJYaiOXJ5bauoKM= 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-448-5zOTvLRXOQ6T8IVCYJsWsA-1; Wed, 14 Jan 2026 06:30:48 -0500 X-MC-Unique: 5zOTvLRXOQ6T8IVCYJsWsA-1 X-Mimecast-MFC-AGG-ID: 5zOTvLRXOQ6T8IVCYJsWsA_1768390247 Received: from mx-prod-int-06.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-06.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.93]) (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 2F6CE19560A3; Wed, 14 Jan 2026 11:30:47 +0000 (UTC) Received: from mzamazal-thinkpadp1gen7.tpbc.com (unknown [10.44.32.217]) by mx-prod-int-06.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id 81C9718004D8; Wed, 14 Jan 2026 11:30:45 +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 v3 05/14] libcamera: ipa: simple: Introduce a general correction matrix Date: Wed, 14 Jan 2026 12:30:07 +0100 Message-ID: <20260114113016.25162-6-mzamazal@redhat.com> In-Reply-To: <20260114113016.25162-1-mzamazal@redhat.com> References: <20260114113016.25162-1-mzamazal@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.4.1 on 10.30.177.93 X-Mimecast-Spam-Score: 0 X-Mimecast-MFC-PROC-ID: XScWL9v7rJkHp8pmpwULUUZzzJ8K5qzt_COJPOJVm1Q_1768390247 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" 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 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 14 11:30:08 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Milan Zamazal X-Patchwork-Id: 25771 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 EB847C3226 for ; Wed, 14 Jan 2026 11:30:57 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 9A81B61FCA; Wed, 14 Jan 2026 12:30:57 +0100 (CET) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=redhat.com header.i=@redhat.com header.b="aMpoibBb"; 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 1846661FBF for ; Wed, 14 Jan 2026 12:30:56 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1768390255; 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=JQLC/YW80//VznTjTZIJs0LSPXvvL2b3e4i8lkrNR9M=; b=aMpoibBbug/ZchcaCIEjBD/imyHyHo66kCPpVyTP2hCuJ6NlzgKkNH3kvmMsKlRpHPEy73 +3KPjRQZURoFQKUR///Rbo3m6oVXYe5anNjhWQufaASf631dRctarHV4YEUYEAhn+hikbE dxXqSm5ta8bYjBPI7bbh3TXxkvtpJLI= 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-690-EeEqqUSLOc-O2tOBZioL5Q-1; Wed, 14 Jan 2026 06:30:50 -0500 X-MC-Unique: EeEqqUSLOc-O2tOBZioL5Q-1 X-Mimecast-MFC-AGG-ID: EeEqqUSLOc-O2tOBZioL5Q_1768390249 Received: from mx-prod-int-06.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-06.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.93]) (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 9E2C118003FC; Wed, 14 Jan 2026 11:30:49 +0000 (UTC) Received: from mzamazal-thinkpadp1gen7.tpbc.com (unknown [10.44.32.217]) by mx-prod-int-06.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id BF2B81800665; Wed, 14 Jan 2026 11:30:47 +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 v3 06/14] libcamera: ipa: simple: Initialise the general correction matrix Date: Wed, 14 Jan 2026 12:30:08 +0100 Message-ID: <20260114113016.25162-7-mzamazal@redhat.com> In-Reply-To: <20260114113016.25162-1-mzamazal@redhat.com> References: <20260114113016.25162-1-mzamazal@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.4.1 on 10.30.177.93 X-Mimecast-Spam-Score: 0 X-Mimecast-MFC-PROC-ID: t4Wwr0S26gOIB7Ke5Dofzaj_m6qbi3BC51ZRFqCHKg0_1768390249 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. Signed-off-by: Milan Zamazal --- src/ipa/simple/algorithms/ccm.cpp | 31 +++++++++++++++---------------- src/ipa/simple/algorithms/ccm.h | 2 ++ src/ipa/simple/soft_simple.cpp | 2 ++ 3 files changed, 19 insertions(+), 16 deletions(-) diff --git a/src/ipa/simple/algorithms/ccm.cpp b/src/ipa/simple/algorithms/ccm.cpp index a3e8cd6c4..8992a8ee1 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,23 @@ 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 (!ccmAssigned_ || + utils::abs_diff(ct, lastCt_) >= kTemperatureThreshold || + saturation != lastSaturation_) { + currentCcm_ = ccm_.getInterpolated(ct); + ccmAssigned_ = true; + if (saturation) + applySaturation(currentCcm_, 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_ * context.activeState.combinedMatrix; + context.activeState.ccm = currentCcm_; frameContext.saturation = saturation; - context.activeState.matrixChanged = true; - frameContext.ccm = ccm; + frameContext.ccm = currentCcm_; } 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..19bf283af 100644 --- a/src/ipa/simple/algorithms/ccm.h +++ b/src/ipa/simple/algorithms/ccm.h @@ -47,6 +47,8 @@ private: unsigned int lastCt_; std::optional lastSaturation_; Interpolator> ccm_; + Matrix currentCcm_; + bool ccmAssigned_ = false; }; } /* namespace ipa::soft::algorithms */ 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 14 11:30:09 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Milan Zamazal X-Patchwork-Id: 25772 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 75AD4C3226 for ; Wed, 14 Jan 2026 11:31:00 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 220C461FC3; Wed, 14 Jan 2026 12:31:00 +0100 (CET) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=redhat.com header.i=@redhat.com header.b="gtjw1IaM"; 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 E538D61FBF for ; Wed, 14 Jan 2026 12:30:56 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1768390256; 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=2Yy0fw8VfTVLNbHji1K0yrA439XKw8Ne3cOSiUOGLic=; b=gtjw1IaMrUPJZKqy5bbR2ADrY/6rkosCUXHcxzNvuLoIQ4vJQPcFPCd5FhYWF9DwJ4L4u4 S8bm91MVi9ufEifiEL9KsuSbGW0gqnL4w2CGJ3QkGWO5RTDC7KPGPS5ErgO8UYo0FtL5FN VERDKYxHSOZbVeXLiIRT3REPpl1EXNQ= 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-349-lhMQdrqfM8quPkQoXRJ8Ng-1; Wed, 14 Jan 2026 06:30:52 -0500 X-MC-Unique: lhMQdrqfM8quPkQoXRJ8Ng-1 X-Mimecast-MFC-AGG-ID: lhMQdrqfM8quPkQoXRJ8Ng_1768390251 Received: from mx-prod-int-06.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-06.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.93]) (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 CA79218003FD; Wed, 14 Jan 2026 11:30:51 +0000 (UTC) Received: from mzamazal-thinkpadp1gen7.tpbc.com (unknown [10.44.32.217]) by mx-prod-int-06.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id 4366D18004D8; Wed, 14 Jan 2026 11:30:49 +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 v3 07/14] libcamera: ipa: simple: Separate saturation from CCM Date: Wed, 14 Jan 2026 12:30:09 +0100 Message-ID: <20260114113016.25162-8-mzamazal@redhat.com> In-Reply-To: <20260114113016.25162-1-mzamazal@redhat.com> References: <20260114113016.25162-1-mzamazal@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.4.1 on 10.30.177.93 X-Mimecast-Spam-Score: 0 X-Mimecast-MFC-PROC-ID: SHDCrj2mhIqAokKbfJXDWdg3bh5mQ6aK25YcftYr3-U_1768390251 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 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 | 58 +------------- src/ipa/simple/algorithms/ccm.h | 9 --- src/ipa/simple/algorithms/meson.build | 1 + src/ipa/simple/data/uncalibrated.yaml | 1 + 6 files changed, 163 insertions(+), 64 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 8992a8ee1..1b2c6eb3a 100644 --- a/src/ipa/simple/algorithms/ccm.cpp +++ b/src/ipa/simple/algorithms/ccm.cpp @@ -3,7 +3,7 @@ * Copyright (C) 2024, Ideas On Board * Copyright (C) 2024-2025, Red Hat Inc. * - * Color correction matrix + saturation + * Color correction matrix */ #include "ccm.h" @@ -37,76 +37,27 @@ 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 (!ccmAssigned_ || - utils::abs_diff(ct, lastCt_) >= kTemperatureThreshold || - saturation != lastSaturation_) { + utils::abs_diff(ct, lastCt_) >= kTemperatureThreshold) { currentCcm_ = ccm_.getInterpolated(ct); ccmAssigned_ = true; - if (saturation) - applySaturation(currentCcm_, saturation.value()); lastCt_ = ct; - lastSaturation_ = saturation; context.activeState.matrixChanged = true; } context.activeState.combinedMatrix = currentCcm_ * context.activeState.combinedMatrix; context.activeState.ccm = currentCcm_; - frameContext.saturation = saturation; frameContext.ccm = currentCcm_; } @@ -117,9 +68,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 19bf283af..261c2c371 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_; Matrix currentCcm_; bool ccmAssigned_ = false; 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 14 11:30:10 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Milan Zamazal X-Patchwork-Id: 25773 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 D9B30C3274 for ; Wed, 14 Jan 2026 11:31:00 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 9BED961FD1; Wed, 14 Jan 2026 12:31:00 +0100 (CET) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=redhat.com header.i=@redhat.com header.b="A7QZqgC9"; 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 DF19C61FBF for ; Wed, 14 Jan 2026 12:30:58 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1768390258; 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=dmscOUWItN5n0Nh/oIFRVuEgC1AVhu6NtJk52F6nMFA=; b=A7QZqgC9GwIFbBF9zrGsPkdLTDc89I2jVSsxe7YZ9wN/EgIwNv3UFVJ5p5jAxdzWTl4JN9 o/VeUXgNSZSnCkjjRQHSJn87t6U7kOhO/VhCkYQYIzlcZcwDVLtrXH770fyJMBR26UHw7o cZ4ZmAhU//BMRM+1T1jYd1Nibvtgv/g= 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-477-7P4O-awKMgqvdRReP8Dygg-1; Wed, 14 Jan 2026 06:30:54 -0500 X-MC-Unique: 7P4O-awKMgqvdRReP8Dygg-1 X-Mimecast-MFC-AGG-ID: 7P4O-awKMgqvdRReP8Dygg_1768390253 Received: from mx-prod-int-06.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-06.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.93]) (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 CB9F21956094; Wed, 14 Jan 2026 11:30:53 +0000 (UTC) Received: from mzamazal-thinkpadp1gen7.tpbc.com (unknown [10.44.32.217]) by mx-prod-int-06.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id 3D91F1800665; Wed, 14 Jan 2026 11:30:52 +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 v3 08/14] libcamera: ipa: simple: Move contrast settings to adjust.cpp Date: Wed, 14 Jan 2026 12:30:10 +0100 Message-ID: <20260114113016.25162-9-mzamazal@redhat.com> In-Reply-To: <20260114113016.25162-1-mzamazal@redhat.com> References: <20260114113016.25162-1-mzamazal@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.4.1 on 10.30.177.93 X-Mimecast-Spam-Score: 0 X-Mimecast-MFC-PROC-ID: mhpLWf3-oc5jkdC0KEYd6IfNr8081qvGi9xxJ6VBAlI_1768390253 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" Let's move the contrast settings from lut.cpp to adjust.cpp, where they belong, similarly to saturation. 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 14 11:30:11 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Milan Zamazal X-Patchwork-Id: 25774 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 97367C3226 for ; Wed, 14 Jan 2026 11:31:03 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 2F55361FC6; Wed, 14 Jan 2026 12:31:03 +0100 (CET) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=redhat.com header.i=@redhat.com header.b="QYO8Zp83"; 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 9027A61FC6 for ; Wed, 14 Jan 2026 12:31:01 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1768390260; 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=IBQX3gI6o/n48huuNLu3pgz9+U7xLw/KPKHprbM8bJ8=; b=QYO8Zp83ifjhZjikAeOww/19J0RDcdhlcQl3ldHu0FJmU90EjPApywZHb2GUzac6rixNR6 z5esSdPmma3kWxmRhiU71CsQcjKmDVLW3zJHZ+3zGceyetD9HbVgKVCvNxQQUmRDMYWN5Q IP57WDeF65rD0PkdjWmbYNDBBBn7vlQ= 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-213-F0VpMSI_OOChQC9w14xviA-1; Wed, 14 Jan 2026 06:30:56 -0500 X-MC-Unique: F0VpMSI_OOChQC9w14xviA-1 X-Mimecast-MFC-AGG-ID: F0VpMSI_OOChQC9w14xviA_1768390256 Received: from mx-prod-int-06.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-06.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.93]) (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 F2C1118003FC; Wed, 14 Jan 2026 11:30:55 +0000 (UTC) Received: from mzamazal-thinkpadp1gen7.tpbc.com (unknown [10.44.32.217]) by mx-prod-int-06.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id 5256C1800665; Wed, 14 Jan 2026 11:30:54 +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 v3 09/14] libcamera: ipa: simple: Make gamma adjustable Date: Wed, 14 Jan 2026 12:30:11 +0100 Message-ID: <20260114113016.25162-10-mzamazal@redhat.com> In-Reply-To: <20260114113016.25162-1-mzamazal@redhat.com> References: <20260114113016.25162-1-mzamazal@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.4.1 on 10.30.177.93 X-Mimecast-Spam-Score: 0 X-Mimecast-MFC-PROC-ID: YuVWsza5DuW78UgBtA1aBjTpwYO_B3czZRb8jV59OOw_1768390256 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 gamma value is fixed in software ISP. Let's make it adjustable, similarly to contrast and saturation. Signed-off-by: Milan Zamazal --- src/ipa/simple/algorithms/adjust.cpp | 8 ++++++++ src/ipa/simple/algorithms/adjust.h | 2 ++ src/ipa/simple/algorithms/lut.cpp | 12 +++++++----- src/ipa/simple/ipa_context.h | 4 +++- 4 files changed, 20 insertions(+), 6 deletions(-) diff --git a/src/ipa/simple/algorithms/adjust.cpp b/src/ipa/simple/algorithms/adjust.cpp index 27ae2a53a..cfbc39610 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 = std::optional(); 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; + LOG(IPASoftAdjust, Debug) << "Setting gamma to " << gamma.value(); + } + const auto &contrast = controls.get(controls::Contrast); if (contrast.has_value()) { context.activeState.knobs.contrast = contrast; diff --git a/src/ipa/simple/algorithms/adjust.h b/src/ipa/simple/algorithms/adjust.h index c4baa2503..190d2079f 100644 --- a/src/ipa/simple/algorithms/adjust.h +++ b/src/ipa/simple/algorithms/adjust.h @@ -19,6 +19,8 @@ namespace libcamera { namespace ipa::soft::algorithms { +const 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..25b188b91 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,8 @@ 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.value_or(kDefaultGamma); 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 +54,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 +66,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 +133,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 58dcad290..680b72eb9 100644 --- a/src/ipa/simple/ipa_context.h +++ b/src/ipa/simple/ipa_context.h @@ -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; @@ -67,6 +67,7 @@ struct IPAActiveState { bool matrixChanged = false; struct { + std::optional gamma; /* 0..2 range, 1.0 = normal */ std::optional contrast; std::optional saturation; @@ -86,6 +87,7 @@ struct IPAFrameContext : public FrameContext { double blue; } gains; + std::optional gamma; std::optional contrast; std::optional saturation; }; From patchwork Wed Jan 14 11:30:12 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Milan Zamazal X-Patchwork-Id: 25775 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 1229CC3274 for ; Wed, 14 Jan 2026 11:31:05 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id C840161FD4; Wed, 14 Jan 2026 12:31:04 +0100 (CET) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=redhat.com header.i=@redhat.com header.b="CuGSq7+K"; 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 4130461FCE for ; Wed, 14 Jan 2026 12:31:03 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1768390262; 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=4RQlDUyu5erKeHtd6wHMuUlJ4XSv280COTY96bnwlBU=; b=CuGSq7+KQbIho5Wypg5ZQAIvhswok43oP3sYk7SnE7eNCj8ci1qxPU5gVTSEghF229OLWP S9YM/vp5dMTUrqYYFysT2gz6Oxs2OABS9yrAuXaBSs0NJdi88WMaYjjVZ3vZ045fy58ASL iZt9N3s+AT9cLf7sS0YasFNpy9TxodQ= 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-167-XPjcgNCTPJWTaVirKHSABg-1; Wed, 14 Jan 2026 06:30:59 -0500 X-MC-Unique: XPjcgNCTPJWTaVirKHSABg-1 X-Mimecast-MFC-AGG-ID: XPjcgNCTPJWTaVirKHSABg_1768390258 Received: from mx-prod-int-06.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-06.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.93]) (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 57185180034F; Wed, 14 Jan 2026 11:30:58 +0000 (UTC) Received: from mzamazal-thinkpadp1gen7.tpbc.com (unknown [10.44.32.217]) by mx-prod-int-06.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id 9001A1800665; Wed, 14 Jan 2026 11:30:56 +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 v3 10/14] libcamera: ipa: simple: Apply gain matrix in awb Date: Wed, 14 Jan 2026 12:30:12 +0100 Message-ID: <20260114113016.25162-11-mzamazal@redhat.com> In-Reply-To: <20260114113016.25162-1-mzamazal@redhat.com> References: <20260114113016.25162-1-mzamazal@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.4.1 on 10.30.177.93 X-Mimecast-Spam-Score: 0 X-Mimecast-MFC-PROC-ID: 9QT3n0UoTnc9_snS0FSBAgA3jguEjgjCQEEOZh_6Oq0_1768390258 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. Signed-off-by: Milan Zamazal --- src/ipa/simple/algorithms/awb.cpp | 7 ++++++- src/ipa/simple/algorithms/lut.cpp | 5 +---- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/ipa/simple/algorithms/awb.cpp b/src/ipa/simple/algorithms/awb.cpp index a391359fb..4d2f1df15 100644 --- a/src/ipa/simple/algorithms/awb.cpp +++ b/src/ipa/simple/algorithms/awb.cpp @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ /* - * Copyright (C) 2024, Red Hat Inc. + * Copyright (C) 2024-2025 Red Hat Inc. * * Auto white balance */ @@ -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 25b188b91..a6dbd7b1d 100644 --- a/src/ipa/simple/algorithms/lut.cpp +++ b/src/ipa/simple/algorithms/lut.cpp @@ -108,10 +108,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 14 11:30:13 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Milan Zamazal X-Patchwork-Id: 25776 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 333D3C3226 for ; Wed, 14 Jan 2026 11:31:07 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id CA71861FDA; Wed, 14 Jan 2026 12:31:06 +0100 (CET) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=redhat.com header.i=@redhat.com header.b="TEGWzxCC"; 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 85FA161FDC for ; Wed, 14 Jan 2026 12:31:05 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1768390264; 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=yQ/EME8k3Sd4zv5VogG4YMeC7fzR90geKLGDwhN8Wo0=; b=TEGWzxCCfELCYQznBmwMiYWymKym8KhKimU/9Fdfyr/pddoHCJnkOPGZXGFP/Vy6DkON4o AYoNBYq7D46ym7BpPO/XYsFgOywsmAB2Hu7J9xNkc55hXHaOZEyXGkGfWfmNifbiz0vFGd oSvZuVXYuQFxbCRlyEbMsmIyRUzi3qs= 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-286-tp8vVzFjOzupsrlcDkUEhw-1; Wed, 14 Jan 2026 06:31:01 -0500 X-MC-Unique: tp8vVzFjOzupsrlcDkUEhw-1 X-Mimecast-MFC-AGG-ID: tp8vVzFjOzupsrlcDkUEhw_1768390260 Received: from mx-prod-int-06.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-06.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.93]) (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 7BC73180034A; Wed, 14 Jan 2026 11:31:00 +0000 (UTC) Received: from mzamazal-thinkpadp1gen7.tpbc.com (unknown [10.44.32.217]) by mx-prod-int-06.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id D16C4180009E; Wed, 14 Jan 2026 11:30:58 +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 v3 11/14] libcamera: ipa: simple: Use float type for adjustment controls Date: Wed, 14 Jan 2026 12:30:13 +0100 Message-ID: <20260114113016.25162-12-mzamazal@redhat.com> In-Reply-To: <20260114113016.25162-1-mzamazal@redhat.com> References: <20260114113016.25162-1-mzamazal@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.4.1 on 10.30.177.93 X-Mimecast-Spam-Score: 0 X-Mimecast-MFC-PROC-ID: MqpF3KV1YuZPQfZhaCKuPUr5TJFOUqRTC1C2gefPavg_1768390260 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" 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, let's use float for the knobs too. Signed-off-by: Milan Zamazal --- include/libcamera/internal/software_isp/debayer_params.h | 2 +- src/ipa/simple/algorithms/adjust.cpp | 6 +++--- src/ipa/simple/algorithms/lut.cpp | 2 +- src/ipa/simple/ipa_context.h | 8 ++++---- 4 files changed, 9 insertions(+), 9 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 cfbc39610..95032799f 100644 --- a/src/ipa/simple/algorithms/adjust.cpp +++ b/src/ipa/simple/algorithms/adjust.cpp @@ -33,9 +33,9 @@ int Adjust::init(IPAContext &context, [[maybe_unused]] const YamlObject &tuningD int Adjust::configure(IPAContext &context, [[maybe_unused]] const IPAConfigInfo &configInfo) { - context.activeState.knobs.gamma = std::optional(); - context.activeState.knobs.contrast = std::optional(); - context.activeState.knobs.saturation = std::optional(); + context.activeState.knobs.gamma = 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 a6dbd7b1d..f31a7e631 100644 --- a/src/ipa/simple/algorithms/lut.cpp +++ b/src/ipa/simple/algorithms/lut.cpp @@ -41,7 +41,7 @@ void Lut::updateGammaTable(IPAContext &context) 1.0 / context.activeState.knobs.gamma.value_or(kDefaultGamma); 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 680b72eb9..85455bbfa 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 ccm; @@ -69,7 +69,7 @@ struct IPAActiveState { struct { std::optional gamma; /* 0..2 range, 1.0 = normal */ - std::optional contrast; + std::optional contrast; std::optional saturation; } knobs; }; @@ -88,7 +88,7 @@ struct IPAFrameContext : public FrameContext { } gains; std::optional gamma; - std::optional contrast; + std::optional contrast; std::optional saturation; }; From patchwork Wed Jan 14 11:30:14 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Milan Zamazal X-Patchwork-Id: 25777 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 DBACAC3226 for ; Wed, 14 Jan 2026 11:31:10 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 9075361FC6; Wed, 14 Jan 2026 12:31:10 +0100 (CET) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=redhat.com header.i=@redhat.com header.b="N+V3PXHL"; 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 002BF61FC6 for ; Wed, 14 Jan 2026 12:31:07 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1768390266; 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=qJgJTB2HiOkuixBdsTyHC2h6ZWieXCDCI2ZpXS5airY=; b=N+V3PXHLPPaI80oRd7iIvQD/K/gW/KcmwYaOpcaOzp6mptPFTaNQo1qTtWWzX/CXnqTzz1 GlvItlFjeQr+en/lDxg64rZ8jBUU5m9xCtDHLLadUxZUfsFcoX0etGu1X0vlJQTNPCsQ/m KQt35hJjZnNzEmwqYserKwaco7snr6Y= 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-519-J_Fm8qYmP-GtKwQ25SoczQ-1; Wed, 14 Jan 2026 06:31:03 -0500 X-MC-Unique: J_Fm8qYmP-GtKwQ25SoczQ-1 X-Mimecast-MFC-AGG-ID: J_Fm8qYmP-GtKwQ25SoczQ_1768390262 Received: from mx-prod-int-06.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-06.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.93]) (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 B89881954B06; Wed, 14 Jan 2026 11:31:02 +0000 (UTC) Received: from mzamazal-thinkpadp1gen7.tpbc.com (unknown [10.44.32.217]) by mx-prod-int-06.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id F3DD11800665; Wed, 14 Jan 2026 11:31:00 +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 v3 12/14] libcamera: ipa: simple: Use symbolic constants for adjust defaults Date: Wed, 14 Jan 2026 12:30:14 +0100 Message-ID: <20260114113016.25162-13-mzamazal@redhat.com> In-Reply-To: <20260114113016.25162-1-mzamazal@redhat.com> References: <20260114113016.25162-1-mzamazal@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.4.1 on 10.30.177.93 X-Mimecast-Spam-Score: 0 X-Mimecast-MFC-PROC-ID: mUiEOv_YGasniyMI8mQDiuXwRnU6bqWfHUrP-dWuobI_1768390262 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 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. Signed-off-by: Milan Zamazal --- src/ipa/simple/algorithms/adjust.cpp | 11 +++++++---- src/ipa/simple/algorithms/adjust.h | 2 ++ 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/src/ipa/simple/algorithms/adjust.cpp b/src/ipa/simple/algorithms/adjust.cpp index 95032799f..60a191380 100644 --- a/src/ipa/simple/algorithms/adjust.cpp +++ b/src/ipa/simple/algorithms/adjust.cpp @@ -23,10 +23,13 @@ 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; } @@ -118,7 +121,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") diff --git a/src/ipa/simple/algorithms/adjust.h b/src/ipa/simple/algorithms/adjust.h index 190d2079f..11d8297ca 100644 --- a/src/ipa/simple/algorithms/adjust.h +++ b/src/ipa/simple/algorithms/adjust.h @@ -20,6 +20,8 @@ namespace libcamera { namespace ipa::soft::algorithms { const float kDefaultGamma = 2.2f; +const float kDefaultContrast = 1.0f; +const float kDefaultSaturation = 1.0f; class Adjust : public Algorithm { From patchwork Wed Jan 14 11:30:15 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Milan Zamazal X-Patchwork-Id: 25778 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 A8CE4C3274 for ; Wed, 14 Jan 2026 11:31:12 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 30B5961FD8; Wed, 14 Jan 2026 12:31:12 +0100 (CET) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=redhat.com header.i=@redhat.com header.b="OEQ8ngQX"; 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 2413661FC6 for ; Wed, 14 Jan 2026 12:31:09 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1768390268; 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=9f3NWipm0WF5lD8vhDbSLyE7PPWkwzCvLusYOLJoplM=; b=OEQ8ngQXOuxKSjNsKkk3MJ2vXYEUob3+gZz7sFjDM2UJnqS87eqlBMh2y7kflkO6uaevs2 pMXb63XEYDRrSaYe8Fm/7ua9AE2MnTAD63LB62d0VGl9hvhosx9t+vGE44NGKU1UjJlY85 mZCVOPxjN05d9BOHmWWXkcq6FokHS3A= 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-633-wWPPd8eDPCqwWQ_vOMP8mg-1; Wed, 14 Jan 2026 06:31:06 -0500 X-MC-Unique: wWPPd8eDPCqwWQ_vOMP8mg-1 X-Mimecast-MFC-AGG-ID: wWPPd8eDPCqwWQ_vOMP8mg_1768390265 Received: from mx-prod-int-06.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-06.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.93]) (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 B4BBB1800378; Wed, 14 Jan 2026 11:31:05 +0000 (UTC) Received: from mzamazal-thinkpadp1gen7.tpbc.com (unknown [10.44.32.217]) by mx-prod-int-06.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id 4E4511800665; Wed, 14 Jan 2026 11:31:03 +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 v3 13/14] libcamera: ipa: simple: Remove Lut algorithm Date: Wed, 14 Jan 2026 12:30:15 +0100 Message-ID: <20260114113016.25162-14-mzamazal@redhat.com> In-Reply-To: <20260114113016.25162-1-mzamazal@redhat.com> References: <20260114113016.25162-1-mzamazal@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.4.1 on 10.30.177.93 X-Mimecast-Spam-Score: 0 X-Mimecast-MFC-PROC-ID: wPxU0SKsEmEQPVenI0jOdKzEZpO9CQxXSIU1bewiY60_1768390265 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. Signed-off-by: Milan Zamazal --- .../internal/software_isp/debayer_params.h | 43 +---- src/ipa/simple/algorithms/adjust.cpp | 20 +- src/ipa/simple/algorithms/adjust.h | 2 - src/ipa/simple/algorithms/awb.cpp | 7 +- src/ipa/simple/algorithms/ccm.cpp | 4 +- src/ipa/simple/algorithms/lut.cpp | 141 -------------- 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 | 10 - src/ipa/simple/soft_simple.cpp | 10 +- src/libcamera/software_isp/debayer.cpp | 172 +----------------- src/libcamera/software_isp/debayer.h | 8 - src/libcamera/software_isp/debayer_cpu.cpp | 141 ++++++++++++-- src/libcamera/software_isp/debayer_cpu.h | 26 ++- src/libcamera/software_isp/debayer_egl.cpp | 22 +-- src/libcamera/software_isp/software_isp.cpp | 17 -- 17 files changed, 195 insertions(+), 465 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/src/ipa/simple/algorithms/adjust.cpp b/src/ipa/simple/algorithms/adjust.cpp index 60a191380..8e2230164 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 */ @@ -92,22 +92,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.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.value_or(kDefaultGamma); + 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)); + params->combinedMatrix = context.activeState.combinedMatrix; } void Adjust::process([[maybe_unused]] IPAContext &context, diff --git a/src/ipa/simple/algorithms/adjust.h b/src/ipa/simple/algorithms/adjust.h index 11d8297ca..02e469a50 100644 --- a/src/ipa/simple/algorithms/adjust.h +++ b/src/ipa/simple/algorithms/adjust.h @@ -47,8 +47,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 4d2f1df15..d361cd6c5 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,12 @@ 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; + params->combinedMatrix = context.activeState.combinedMatrix; } void Awb::process(IPAContext &context, diff --git a/src/ipa/simple/algorithms/ccm.cpp b/src/ipa/simple/algorithms/ccm.cpp index 1b2c6eb3a..aa06b5274 100644 --- a/src/ipa/simple/algorithms/ccm.cpp +++ b/src/ipa/simple/algorithms/ccm.cpp @@ -42,7 +42,7 @@ int Ccm::init([[maybe_unused]] IPAContext &context, const YamlObject &tuningData } void Ccm::prepare(IPAContext &context, [[maybe_unused]] const uint32_t frame, - IPAFrameContext &frameContext, [[maybe_unused]] DebayerParams *params) + IPAFrameContext &frameContext, DebayerParams *params) { const unsigned int ct = context.activeState.awb.temperatureK; @@ -52,13 +52,13 @@ void Ccm::prepare(IPAContext &context, [[maybe_unused]] const uint32_t frame, currentCcm_ = ccm_.getInterpolated(ct); ccmAssigned_ = true; lastCt_ = ct; - context.activeState.matrixChanged = true; } context.activeState.combinedMatrix = currentCcm_ * context.activeState.combinedMatrix; context.activeState.ccm = currentCcm_; frameContext.ccm = currentCcm_; + params->combinedMatrix = context.activeState.combinedMatrix; } void Ccm::process([[maybe_unused]] IPAContext &context, diff --git a/src/ipa/simple/algorithms/lut.cpp b/src/ipa/simple/algorithms/lut.cpp deleted file mode 100644 index f31a7e631..000000000 --- a/src/ipa/simple/algorithms/lut.cpp +++ /dev/null @@ -1,141 +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.value_or(kDefaultGamma); - 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 85455bbfa..aec864d02 100644 --- a/src/ipa/simple/ipa_context.h +++ b/src/ipa/simple/ipa_context.h @@ -53,18 +53,8 @@ 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 ccm; Matrix combinedMatrix; - bool matrixChanged = false; struct { std::optional gamma; diff --git a/src/ipa/simple/soft_simple.cpp b/src/ipa/simple/soft_simple.cpp index 732e82510..b85e09501 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" @@ -161,6 +162,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. */ } { @@ -282,7 +288,9 @@ void IPASoftSimple::queueRequest(const uint32_t frame, const ControlList &contro void IPASoftSimple::computeParams(const uint32_t frame) { - context_.activeState.combinedMatrix = Matrix::identity(); + Matrix combinedMatrix = Matrix::identity(); + context_.activeState.combinedMatrix = combinedMatrix; + params_->combinedMatrix = combinedMatrix; IPAFrameContext &frameContext = context_.frameContexts.get(frame); for (auto const &algo : algorithms()) 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..dcd931d2d 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,103 @@ 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 = [](Matrix m1, Matrix m2) -> bool { + for (unsigned int i = 0; i < 3; i++) + for (unsigned int j = 0; j < 3; j++) + if (m1[i][j] != m2[i][j]) + return true; + return false; + }; + + 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 +855,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 8e0890323..ddf03ad4b 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..ebdf5b2c6 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; From patchwork Wed Jan 14 11:30:16 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Milan Zamazal X-Patchwork-Id: 25779 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 67055C3226 for ; Wed, 14 Jan 2026 11:31:16 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 1C0DE61FD7; Wed, 14 Jan 2026 12:31:16 +0100 (CET) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=redhat.com header.i=@redhat.com header.b="QKtxZ6Pd"; 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 CEFBD61FC6 for ; Wed, 14 Jan 2026 12:31:14 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1768390273; 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=b+Z6EBDWWahsE034X9Fogftd//pSeUogq83V6JYZG7A=; b=QKtxZ6PdlaW+xc4ijFiT6UtxD8cD6nQrcdjjACqGlk3OEtESyxaIoShJt8MeHglRuefh8U 9WpBuVjJJTe1ByoMFVM/XZqrbrkC1Bvc8lbbQvRgTw7MP9TK2hh+57yN+4skWhOZqiIm6i OHf4QQXrRLM4d68zfENNL3AEW6noc7w= 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-338-_U0ywzi1NjqaNA63_xROHQ-1; Wed, 14 Jan 2026 06:31:08 -0500 X-MC-Unique: _U0ywzi1NjqaNA63_xROHQ-1 X-Mimecast-MFC-AGG-ID: _U0ywzi1NjqaNA63_xROHQ_1768390267 Received: from mx-prod-int-06.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-06.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.93]) (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 A6836180057E; Wed, 14 Jan 2026 11:31:07 +0000 (UTC) Received: from mzamazal-thinkpadp1gen7.tpbc.com (unknown [10.44.32.217]) by mx-prod-int-06.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id 34C1E18004D8; Wed, 14 Jan 2026 11:31:05 +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 v3 14/14] libcamera: ipa: simple: Disable Ccm algorithm by default again Date: Wed, 14 Jan 2026 12:30:16 +0100 Message-ID: <20260114113016.25162-15-mzamazal@redhat.com> In-Reply-To: <20260114113016.25162-1-mzamazal@redhat.com> References: <20260114113016.25162-1-mzamazal@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.4.1 on 10.30.177.93 X-Mimecast-Spam-Score: 0 X-Mimecast-MFC-PROC-ID: MUit8RG4WCUZXRvrGfdexTqv9-zinjj6Q8_oI8T_rVY_1768390267 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 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. 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 ddf03ad4b..b3994c29e 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) {