From patchwork Thu Nov 20 10:45:36 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Milan Zamazal X-Patchwork-Id: 25087 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 6DCC6C3330 for ; Thu, 20 Nov 2025 10:46:10 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 07E4260A81; Thu, 20 Nov 2025 11:46:10 +0100 (CET) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=redhat.com header.i=@redhat.com header.b="YpMWHtKg"; 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 33817609D8 for ; Thu, 20 Nov 2025 11:46:09 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1763635568; 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=dbYblhY/zMn3ab68e8f/1os5oxcOeoKmTpXA4VFVfuM=; b=YpMWHtKg1wAvGloH8hPZclEDacqJo2wXrV4Xz2Hned1FHZF5oU1gCUWUNVKm4A57Yp8Jzo EENWHDTXB5B27DSpDZK8col98ASoxrtGW/05AtCvLQTbbX4AllYV1P2sf1h4Lw9OeCBD/M JMap43Mq90yFB6WxVlWMkDTr2L9GTuE= Received: from mx-prod-mc-03.mail-002.prod.us-west-2.aws.redhat.com (ec2-54-186-198-63.us-west-2.compute.amazonaws.com [54.186.198.63]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-568-xqpvXZQnPKKejHWLn1fgIg-1; Thu, 20 Nov 2025 05:46:04 -0500 X-MC-Unique: xqpvXZQnPKKejHWLn1fgIg-1 X-Mimecast-MFC-AGG-ID: xqpvXZQnPKKejHWLn1fgIg_1763635563 Received: from mx-prod-int-08.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-08.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.111]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mx-prod-mc-03.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id C4308195605B; Thu, 20 Nov 2025 10:46:02 +0000 (UTC) Received: from mzamazal-thinkpadp1gen7.tpbc.com (unknown [10.44.34.39]) by mx-prod-int-08.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id C5BE21800451; Thu, 20 Nov 2025 10:46: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: [RFC PATCH v2 01/13] libcamera: ipa: simple: Remove an unused include from awb.cpp Date: Thu, 20 Nov 2025 11:45:36 +0100 Message-ID: <20251120104548.80268-2-mzamazal@redhat.com> In-Reply-To: <20251120104548.80268-1-mzamazal@redhat.com> References: <20251120104548.80268-1-mzamazal@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.4.1 on 10.30.177.111 X-Mimecast-Spam-Score: 0 X-Mimecast-MFC-PROC-ID: -fdpKax_6b4NE4WkXjXVroe-nNM0e_xdkq-xE6JZ0Dg_1763635563 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. Signed-off-by: Milan Zamazal 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 cf78e9800..9de422519 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 Thu Nov 20 10:45:37 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Milan Zamazal X-Patchwork-Id: 25088 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 11956C3330 for ; Thu, 20 Nov 2025 10:46:14 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id AD30D60AA3; Thu, 20 Nov 2025 11:46:13 +0100 (CET) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=redhat.com header.i=@redhat.com header.b="NIx8VIw1"; 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 14B3260AA0 for ; Thu, 20 Nov 2025 11:46:11 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1763635570; 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=YbSidXJqGj9tLUBOBK+dVGBxBATb0YOxjrEeIEAP/Zw=; b=NIx8VIw1NqmK5TSHtw0QDztU9MGHO0FoziaZDOOAqPhiZlm443bRXKVQD4cXgABrB3DI7X 5yiZQmCbv1GB+k7CUZCGhdwi8dAy+UnDMQMaW5Dy+oCHyOQKXBWbsJ4JCsEBTpmJd+f9S/ hIMfoU47fYoDZ1ZNi62mIxh3AMaYBEs= 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-619-JkU-S3SnMzeWJxy0T9jT1A-1; Thu, 20 Nov 2025 05:46:06 -0500 X-MC-Unique: JkU-S3SnMzeWJxy0T9jT1A-1 X-Mimecast-MFC-AGG-ID: JkU-S3SnMzeWJxy0T9jT1A_1763635565 Received: from mx-prod-int-08.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-08.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.111]) (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 2712719560B3; Thu, 20 Nov 2025 10:46:05 +0000 (UTC) Received: from mzamazal-thinkpadp1gen7.tpbc.com (unknown [10.44.34.39]) by mx-prod-int-08.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id 4F5AC1800451; Thu, 20 Nov 2025 10:46: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: [RFC PATCH v2 02/13] libcamera: ipa: simple: Unwrap IPAFrameContext::ccm Date: Thu, 20 Nov 2025 11:45:37 +0100 Message-ID: <20251120104548.80268-3-mzamazal@redhat.com> In-Reply-To: <20251120104548.80268-1-mzamazal@redhat.com> References: <20251120104548.80268-1-mzamazal@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.4.1 on 10.30.177.111 X-Mimecast-Spam-Score: 0 X-Mimecast-MFC-PROC-ID: XYpPpi3jpLuYuNL1ZTzqf6aai2c5G4ULgK9BLJZl8mc_1763635565 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. Signed-off-by: Milan Zamazal 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 c3081e306..330fe39cb 100644 --- a/src/ipa/simple/ipa_context.h +++ b/src/ipa/simple/ipa_context.h @@ -73,9 +73,7 @@ struct IPAActiveState { }; struct IPAFrameContext : public FrameContext { - struct { - Matrix ccm; - } ccm; + Matrix ccm; struct { int32_t exposure; From patchwork Thu Nov 20 10:45:38 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Milan Zamazal X-Patchwork-Id: 25089 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 B788AC3330 for ; Thu, 20 Nov 2025 10:46:15 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 6683760A81; Thu, 20 Nov 2025 11:46:15 +0100 (CET) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=redhat.com header.i=@redhat.com header.b="X6EsWQpn"; 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 63CFF60AA1 for ; Thu, 20 Nov 2025 11:46:13 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1763635572; 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=4edCUQmK8Wq8zdYgI7wKO9gtoQsozmvbFwxYfwtEcrc=; b=X6EsWQpnSIiYFz3+Tj9YZIlqbxqF3vgkGDDC9F/SDZNJuPiVB6pNn86roUp6DMtJYSJWSm MhMNjo1dY1EPAxnKtqS87DfiBeS0Ym1BjT9C0sCUjP2uzYeR0AMH0+t7z6emVmGO1wiNwr 72sz0adC9ck49cUmXeS4Z4RpT4l8Gvc= 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-bozH9i00NZ2qUXvSj2rrNw-1; Thu, 20 Nov 2025 05:46:08 -0500 X-MC-Unique: bozH9i00NZ2qUXvSj2rrNw-1 X-Mimecast-MFC-AGG-ID: bozH9i00NZ2qUXvSj2rrNw_1763635567 Received: from mx-prod-int-08.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-08.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.111]) (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 5011C180047F; Thu, 20 Nov 2025 10:46:07 +0000 (UTC) Received: from mzamazal-thinkpadp1gen7.tpbc.com (unknown [10.44.34.39]) by mx-prod-int-08.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id A2AA6180087B; Thu, 20 Nov 2025 10:46: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: [RFC PATCH v2 03/13] libcamera: ipa: simple: Generalise tracking matrix changes Date: Thu, 20 Nov 2025 11:45:38 +0100 Message-ID: <20251120104548.80268-4-mzamazal@redhat.com> In-Reply-To: <20251120104548.80268-1-mzamazal@redhat.com> References: <20251120104548.80268-1-mzamazal@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.4.1 on 10.30.177.111 X-Mimecast-Spam-Score: 0 X-Mimecast-MFC-PROC-ID: ZAb1-dAqxRB7crFcvfS_51bOjyjyoYEmRfFgssE_0vA_1763635567 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 d4a79e101..1cd7ef92e 100644 --- a/src/ipa/simple/algorithms/lut.cpp +++ b/src/ipa/simple/algorithms/lut.cpp @@ -125,11 +125,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; @@ -145,6 +145,7 @@ void Lut::prepare(IPAContext &context, blue[i].b = ccmValue(i, ccm[2][2]); params->gammaLut[i] = gammaTable[i / div]; } + context.activeState.matrixChanged = false; } } diff --git a/src/ipa/simple/ipa_context.h b/src/ipa/simple/ipa_context.h index 330fe39cb..b6b3cbfed 100644 --- a/src/ipa/simple/ipa_context.h +++ b/src/ipa/simple/ipa_context.h @@ -60,10 +60,8 @@ struct IPAActiveState { double contrast; } gamma; - struct { - Matrix ccm; - bool changed; - } ccm; + Matrix ccm; + bool matrixChanged = false; struct { /* 0..2 range, 1.0 = normal */ From patchwork Thu Nov 20 10:45:39 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Milan Zamazal X-Patchwork-Id: 25090 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 BA67EC3330 for ; Thu, 20 Nov 2025 10:46:18 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 6907D60AA1; Thu, 20 Nov 2025 11:46:18 +0100 (CET) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=redhat.com header.i=@redhat.com header.b="ddY58Qk3"; 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 CE81E60AA4 for ; Thu, 20 Nov 2025 11:46:15 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1763635574; 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=uUNVsOkuEdWJt6qvUcutuM1g7bn7nYqqke8eSWJ01G4=; b=ddY58Qk3ZbE8A31Xn4rnI7MsY0ADCaezmDyy2D1S10sdJN2YnKvwV1sGf18IHWU6UfdsRs xw4j8Zd9zyko2wSb8lJm8rpPoFPU+i8AXATviBKF/DzWjFS9ZefPoHqMYuqp/5IsrjIMPu 7ENctTcYVwyTHGNpLm0btY5/wGf+rQM= Received: from mx-prod-mc-03.mail-002.prod.us-west-2.aws.redhat.com (ec2-54-186-198-63.us-west-2.compute.amazonaws.com [54.186.198.63]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-80-bfwlGw95MBivy_whXL3p4g-1; Thu, 20 Nov 2025 05:46:10 -0500 X-MC-Unique: bfwlGw95MBivy_whXL3p4g-1 X-Mimecast-MFC-AGG-ID: bfwlGw95MBivy_whXL3p4g_1763635569 Received: from mx-prod-int-08.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-08.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.111]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mx-prod-mc-03.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id BC0041956058; Thu, 20 Nov 2025 10:46:09 +0000 (UTC) Received: from mzamazal-thinkpadp1gen7.tpbc.com (unknown [10.44.34.39]) by mx-prod-int-08.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id E5580180087B; Thu, 20 Nov 2025 10:46:07 +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: [RFC PATCH v2 04/13] libcamera: ipa: simple: Rename "ccm" identifiers not specific to CCM Date: Thu, 20 Nov 2025 11:45:39 +0100 Message-ID: <20251120104548.80268-5-mzamazal@redhat.com> In-Reply-To: <20251120104548.80268-1-mzamazal@redhat.com> References: <20251120104548.80268-1-mzamazal@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.4.1 on 10.30.177.111 X-Mimecast-Spam-Score: 0 X-Mimecast-MFC-PROC-ID: oYTxfV5edpcbRaKD-xU8AsdPlezVyIS2HkAUEQA6gO0_1763635569 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 | 28 ++++++++++++++-------------- src/ipa/simple/algorithms/lut.h | 2 +- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/src/ipa/simple/algorithms/lut.cpp b/src/ipa/simple/algorithms/lut.cpp index 1cd7ef92e..39d8cca1d 100644 --- a/src/ipa/simple/algorithms/lut.cpp +++ b/src/ipa/simple/algorithms/lut.cpp @@ -87,7 +87,7 @@ void Lut::updateGammaTable(IPAContext &context) context.activeState.gamma.contrast = contrast; } -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); } @@ -126,23 +126,23 @@ 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; 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]; } context.activeState.matrixChanged = false; 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 Thu Nov 20 10:45:40 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Milan Zamazal X-Patchwork-Id: 25091 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 DDB32C3330 for ; Thu, 20 Nov 2025 10:46:20 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 9039D60AA9; Thu, 20 Nov 2025 11:46:20 +0100 (CET) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=redhat.com header.i=@redhat.com header.b="X+GUUcEJ"; 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 37AF560A80 for ; Thu, 20 Nov 2025 11:46:18 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1763635577; 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=1r4Gdf/Ifv1lTNDi1Xu/fJaMrna3jVBWRIRlDI/gZ+Q=; b=X+GUUcEJQpSJSK/KD7IDTnUkKZN9I3mDnLYPkk9sndp8r+U23PZq72Tsuvp7tg0Y7Dd5xU XmezLRVOW/GF0mourVomKSHkDFW0JapgfhhFGtFH5F5mc8wtOoY5Smv4qdPuGGWRY87hV4 nakwOIiw5En7Ebqx1Q7YqrmhctJVnZw= 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-225-cTjaeBgfMpyShzmYvDTazw-1; Thu, 20 Nov 2025 05:46:13 -0500 X-MC-Unique: cTjaeBgfMpyShzmYvDTazw-1 X-Mimecast-MFC-AGG-ID: cTjaeBgfMpyShzmYvDTazw_1763635572 Received: from mx-prod-int-08.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-08.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.111]) (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 39570180034F; Thu, 20 Nov 2025 10:46:12 +0000 (UTC) Received: from mzamazal-thinkpadp1gen7.tpbc.com (unknown [10.44.34.39]) by mx-prod-int-08.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id 57DFD1800947; Thu, 20 Nov 2025 10:46:10 +0000 (UTC) From: Milan Zamazal To: libcamera-devel@lists.libcamera.org Cc: Milan Zamazal , Kieran Bingham , =?utf-8?q?Barnab=C3=A1s_P=C5=91cze?= Subject: [RFC PATCH v2 05/13] libcamera: ipa: simple: Introduce a general correction matrix Date: Thu, 20 Nov 2025 11:45:40 +0100 Message-ID: <20251120104548.80268-6-mzamazal@redhat.com> In-Reply-To: <20251120104548.80268-1-mzamazal@redhat.com> References: <20251120104548.80268-1-mzamazal@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.4.1 on 10.30.177.111 X-Mimecast-Spam-Score: 0 X-Mimecast-MFC-PROC-ID: 3Nq4_arwJu24nwtlfWnHaFj0J7t50Adg6k2tf00I_MU_1763635572 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. Signed-off-by: Milan Zamazal 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 39d8cca1d..fe8db65ea 100644 --- a/src/ipa/simple/algorithms/lut.cpp +++ b/src/ipa/simple/algorithms/lut.cpp @@ -129,7 +129,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 b6b3cbfed..c69d59780 100644 --- a/src/ipa/simple/ipa_context.h +++ b/src/ipa/simple/ipa_context.h @@ -61,6 +61,7 @@ struct IPAActiveState { } gamma; Matrix ccm; + Matrix combinedMatrix; bool matrixChanged = false; struct { From patchwork Thu Nov 20 10:45:41 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Milan Zamazal X-Patchwork-Id: 25092 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 E9675C3334 for ; Thu, 20 Nov 2025 10:46:21 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 6A1E560A8A; Thu, 20 Nov 2025 11:46:21 +0100 (CET) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=redhat.com header.i=@redhat.com header.b="Jy+Uox/n"; 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 27D5660AA0 for ; Thu, 20 Nov 2025 11:46:19 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1763635577; 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=y+wNNCi9XyNQtDmcHXH++PVnjmltzN0lcLIrkG+xR9g=; b=Jy+Uox/nJ8jDBtH8jZhZvMHvsXC1N8eQGBDN26oqU632YU1HpzyC7B3xVadR/C9yRb0YNp EKeFnkSaMij/nk2ki+tPNaa9jaKxidwyzz+iOVrt1vq6fxlMA17/eu/pZrKUtcmzyAqoqe Ul7vM3WWSAix6uRkVemiZwh+byKw/JQ= 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-178-6d_zshY2PmalJRJcHYcr_w-1; Thu, 20 Nov 2025 05:46:15 -0500 X-MC-Unique: 6d_zshY2PmalJRJcHYcr_w-1 X-Mimecast-MFC-AGG-ID: 6d_zshY2PmalJRJcHYcr_w_1763635574 Received: from mx-prod-int-08.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-08.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.111]) (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 2F2D818002CA; Thu, 20 Nov 2025 10:46:14 +0000 (UTC) Received: from mzamazal-thinkpadp1gen7.tpbc.com (unknown [10.44.34.39]) by mx-prod-int-08.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id A1B8D1800451; Thu, 20 Nov 2025 10:46:12 +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: [RFC PATCH v2 06/13] libcamera: ipa: simple: Initialise the general correction matrix Date: Thu, 20 Nov 2025 11:45:41 +0100 Message-ID: <20251120104548.80268-7-mzamazal@redhat.com> In-Reply-To: <20251120104548.80268-1-mzamazal@redhat.com> References: <20251120104548.80268-1-mzamazal@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.4.1 on 10.30.177.111 X-Mimecast-Spam-Score: 0 X-Mimecast-MFC-PROC-ID: G6IQS2tIG0wurzfLZKmd81xH2OvHz3xzu_A4UYrPPAs_1763635574 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. Signed-off-by: Milan Zamazal --- src/ipa/simple/algorithms/ccm.cpp | 28 +++++++++++++--------------- src/ipa/simple/algorithms/ccm.h | 1 + src/ipa/simple/soft_simple.cpp | 2 ++ 3 files changed, 16 insertions(+), 15 deletions(-) diff --git a/src/ipa/simple/algorithms/ccm.cpp b/src/ipa/simple/algorithms/ccm.cpp index a3e8cd6c4..52dd11aaa 100644 --- a/src/ipa/simple/algorithms/ccm.cpp +++ b/src/ipa/simple/algorithms/ccm.cpp @@ -91,24 +91,22 @@ 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 (frame == 0 || + utils::abs_diff(ct, lastCt_) >= kTemperatureThreshold || + saturation != lastSaturation_) { + currentCcm_ = ccm_.getInterpolated(ct); + 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..c29f394f0 100644 --- a/src/ipa/simple/algorithms/ccm.h +++ b/src/ipa/simple/algorithms/ccm.h @@ -47,6 +47,7 @@ private: unsigned int lastCt_; std::optional lastSaturation_; Interpolator> ccm_; + Matrix currentCcm_; }; } /* namespace ipa::soft::algorithms */ diff --git a/src/ipa/simple/soft_simple.cpp b/src/ipa/simple/soft_simple.cpp index b147aca2e..98582587c 100644 --- a/src/ipa/simple/soft_simple.cpp +++ b/src/ipa/simple/soft_simple.cpp @@ -296,6 +296,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 Thu Nov 20 10:45:42 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Milan Zamazal X-Patchwork-Id: 25093 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 7E253C3330 for ; Thu, 20 Nov 2025 10:46:24 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 3294260AAE; Thu, 20 Nov 2025 11:46:24 +0100 (CET) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=redhat.com header.i=@redhat.com header.b="KdBqqX8p"; 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 AAC9D60AAB for ; Thu, 20 Nov 2025 11:46:20 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1763635579; 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=nzU1JruVmpvV/SjwbmJAKlBGk479eMSAy9Kc0Vk3tLM=; b=KdBqqX8phevXWiGdqkLKwVl7rSB6W0E2OupOxhkk0qHuIcFM/aPY1MVXoTdSeUXnpd0aM/ Cn9w86Dd+mcCUxTjzJ0pgaRUn7NtLwgAeO2AdJGVvLDcIBIofMiaUZnVTMhUvqulQ20DuO vHLr2H7L38JQTAVJnxukY61hXABXXHU= Received: from mx-prod-mc-03.mail-002.prod.us-west-2.aws.redhat.com (ec2-54-186-198-63.us-west-2.compute.amazonaws.com [54.186.198.63]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-137-e5Wv2KO_N7qk3maF7r1bqQ-1; Thu, 20 Nov 2025 05:46:17 -0500 X-MC-Unique: e5Wv2KO_N7qk3maF7r1bqQ-1 X-Mimecast-MFC-AGG-ID: e5Wv2KO_N7qk3maF7r1bqQ_1763635576 Received: from mx-prod-int-08.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-08.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.111]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mx-prod-mc-03.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id 80754195606B; Thu, 20 Nov 2025 10:46:16 +0000 (UTC) Received: from mzamazal-thinkpadp1gen7.tpbc.com (unknown [10.44.34.39]) by mx-prod-int-08.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id A58DE1800451; Thu, 20 Nov 2025 10:46:14 +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: [RFC PATCH v2 07/13] libcamera: ipa: simple: Separate saturation from CCM Date: Thu, 20 Nov 2025 11:45:42 +0100 Message-ID: <20251120104548.80268-8-mzamazal@redhat.com> In-Reply-To: <20251120104548.80268-1-mzamazal@redhat.com> References: <20251120104548.80268-1-mzamazal@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.4.1 on 10.30.177.111 X-Mimecast-Spam-Score: 0 X-Mimecast-MFC-PROC-ID: O8-Yyx59ZQ7qOq3_Cee8Tc3TLG_e-F-mssGMQZ5Y4Hs_1763635576 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..b9a7864ad --- /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, + 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 (frame == 0 || 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 52dd11aaa..50bb3164f 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,75 +37,26 @@ 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, 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 (frame == 0 || - utils::abs_diff(ct, lastCt_) >= kTemperatureThreshold || - saturation != lastSaturation_) { + utils::abs_diff(ct, lastCt_) >= kTemperatureThreshold) { currentCcm_ = ccm_.getInterpolated(ct); - 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_; } @@ -116,9 +67,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 c29f394f0..a7613f0f9 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_; }; 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 5508e6686..f0410fe61 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 Thu Nov 20 10:45:43 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Milan Zamazal X-Patchwork-Id: 25097 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 6690FC3330 for ; Thu, 20 Nov 2025 10:46:35 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 1A87160AB8; Thu, 20 Nov 2025 11:46:35 +0100 (CET) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=redhat.com header.i=@redhat.com header.b="ZiTEtpz3"; 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 73CF560A8B for ; Thu, 20 Nov 2025 11:46:32 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1763635591; 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=vbvwMbPLpDlfI1nqfd6hyT/MirXk7jPP0MwKST2EToY=; b=ZiTEtpz3KOxbmbY5xqcJtWbiKQRgJDMr0PIlw9NL0gP0AK++EqJtBXuD9hn2Q1aMSO96tm ecnV84ZL4Ji3NUvoX8VhZcKdzY2iHTBSmeYbjzsvVL/jTr47a0TQ3GpcdBa6Nz9GvvwQc0 KvWggUlMND9k3555JRF8053SlVyw1AQ= 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-578-9On3-vLEOOS-wQil57XdMw-1; Thu, 20 Nov 2025 05:46:19 -0500 X-MC-Unique: 9On3-vLEOOS-wQil57XdMw-1 X-Mimecast-MFC-AGG-ID: 9On3-vLEOOS-wQil57XdMw_1763635579 Received: from mx-prod-int-08.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-08.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.111]) (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 EEB4D180130B; Thu, 20 Nov 2025 10:46:18 +0000 (UTC) Received: from mzamazal-thinkpadp1gen7.tpbc.com (unknown [10.44.34.39]) by mx-prod-int-08.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id 1C9E51800451; Thu, 20 Nov 2025 10:46:16 +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: [RFC PATCH v2 08/13] libcamera: ipa: simple: Move contrast settings to adjust.cpp Date: Thu, 20 Nov 2025 11:45:43 +0100 Message-ID: <20251120104548.80268-9-mzamazal@redhat.com> In-Reply-To: <20251120104548.80268-1-mzamazal@redhat.com> References: <20251120104548.80268-1-mzamazal@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.4.1 on 10.30.177.111 X-Mimecast-Spam-Score: 0 X-Mimecast-MFC-PROC-ID: tOt_qhuVaakn3vb6ANyO3y_qwShbkNQuvDXenAAL_44_1763635579 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 b9a7864ad..49fa98c9d 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 fe8db65ea..a4cb14673 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 = 0.5; - 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) { auto &gammaTable = context.activeState.gamma.gammaTable; @@ -94,11 +74,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 @@ -149,17 +127,6 @@ void Lut::prepare(IPAContext &context, } } -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 Thu Nov 20 10:45:44 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Milan Zamazal X-Patchwork-Id: 25095 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 C7DAAC3330 for ; Thu, 20 Nov 2025 10:46:30 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 4F69760AB1; Thu, 20 Nov 2025 11:46:30 +0100 (CET) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=redhat.com header.i=@redhat.com header.b="fsCZUs3s"; 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 5721260A8A for ; Thu, 20 Nov 2025 11:46:27 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1763635586; 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=vVfx98L7QettwdI59zv0fnwQbGjLUrVmHFrOP7RjXBA=; b=fsCZUs3snPqwaAlKcG2qmKp7hP8xha9ZArKDBydEqB9I9UxJ37jEc/byvsKgp3LNRJbSlX AR08Ha3dXSVg96PjiQfqxOXk+QB44LY4TKqFfxyI5CljKy4wWPOSL2E606qM1AnRmFUhXk CDvv5Y2ohqSheNI9CpV2KMjdwkSA8Z8= 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-134-c_p03WtVM12ZMRe5E7KDhg-1; Thu, 20 Nov 2025 05:46:22 -0500 X-MC-Unique: c_p03WtVM12ZMRe5E7KDhg-1 X-Mimecast-MFC-AGG-ID: c_p03WtVM12ZMRe5E7KDhg_1763635581 Received: from mx-prod-int-08.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-08.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.111]) (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 3A6DC19560A3; Thu, 20 Nov 2025 10:46:21 +0000 (UTC) Received: from mzamazal-thinkpadp1gen7.tpbc.com (unknown [10.44.34.39]) by mx-prod-int-08.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id 8CA161800451; Thu, 20 Nov 2025 10:46:19 +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: [RFC PATCH v2 09/13] libcamera: ipa: simple: Make gamma adjustable Date: Thu, 20 Nov 2025 11:45:44 +0100 Message-ID: <20251120104548.80268-10-mzamazal@redhat.com> In-Reply-To: <20251120104548.80268-1-mzamazal@redhat.com> References: <20251120104548.80268-1-mzamazal@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.4.1 on 10.30.177.111 X-Mimecast-Spam-Score: 0 X-Mimecast-MFC-PROC-ID: bRTtSDe0w5trTb4AYU0VWzZS1XoXce9TkIb49AL1lJE_1763635581 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. The default gamma value is changed from 1/2.0 to a more common value 1/2.2, which is used in GPU ISP. 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 | 9 ++++++--- src/ipa/simple/ipa_context.h | 3 +++ 4 files changed, 19 insertions(+), 3 deletions(-) diff --git a/src/ipa/simple/algorithms/adjust.cpp b/src/ipa/simple/algorithms/adjust.cpp index 49fa98c9d..600fd3c7b 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 a4cb14673..a17a24c9e 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 = 0.5; updateGammaTable(context); return 0; @@ -39,6 +39,8 @@ void Lut::updateGammaTable(IPAContext &context) auto &gammaTable = context.activeState.gamma.gammaTable; const auto blackLevel = context.activeState.blc.level; const unsigned int blackIndex = blackLevel * gammaTable.size() / 256; + const auto gamma = + 1.0 / context.activeState.knobs.gamma.value_or(kDefaultGamma); const auto contrast = context.activeState.knobs.contrast.value_or(1.0); const float divisor = gammaTable.size() - blackIndex - 1.0; @@ -52,7 +54,7 @@ void Lut::updateGammaTable(IPAContext &context) else normalized = 1.0 - 0.5 * std::pow((1.0 - normalized) / 0.5, contrastExp); gammaTable[i] = UINT8_MAX * - std::pow(normalized, context.configuration.gamma); + std::pow(normalized, gamma); } /* * Due to CCM operations, the table lookup may reach indices below the black @@ -63,6 +65,7 @@ void Lut::updateGammaTable(IPAContext &context) std::fill(gammaTable.begin(), gammaTable.begin() + blackIndex, gammaTable[blackIndex]); + context.activeState.gamma.gamma = gamma; context.activeState.gamma.blackLevel = blackLevel; context.activeState.gamma.contrast = contrast; } diff --git a/src/ipa/simple/ipa_context.h b/src/ipa/simple/ipa_context.h index c69d59780..8f56c25bf 100644 --- a/src/ipa/simple/ipa_context.h +++ b/src/ipa/simple/ipa_context.h @@ -57,6 +57,7 @@ struct IPAActiveState { struct { std::array gammaTable; uint8_t blackLevel; + float gamma; double contrast; } gamma; @@ -65,6 +66,7 @@ struct IPAActiveState { bool matrixChanged = false; struct { + std::optional gamma; /* 0..2 range, 1.0 = normal */ std::optional contrast; std::optional saturation; @@ -84,6 +86,7 @@ struct IPAFrameContext : public FrameContext { double blue; } gains; + std::optional gamma; std::optional contrast; std::optional saturation; }; From patchwork Thu Nov 20 10:45:45 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Milan Zamazal X-Patchwork-Id: 25094 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 A7349C3330 for ; Thu, 20 Nov 2025 10:46:28 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 1E75A60AAE; Thu, 20 Nov 2025 11:46:28 +0100 (CET) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=redhat.com header.i=@redhat.com header.b="QPK20ef/"; 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 7635860A8A for ; Thu, 20 Nov 2025 11:46:26 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1763635585; 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=YYuPtkA2BoPPCcF1lZCupzLLtPpMr0tsXnypIKRRbL4=; b=QPK20ef/zSG1YuXbFU5hbdnQE0P6OeALznMaOmAjf+xL/MMs9DOa1DcMfq5ELMathqG0NP 8tOK26tFgabbDdFMpwLUiQpSkhJtqsf5KSj0qSWgwgucd2KD1gtuKeU4twPnpbMXw61Uqo taxUhzmpYaR3WXrVHVxfRpxRrKvxYSA= 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-640-TFBuK8EXM7ukkGO-8XeZKA-1; Thu, 20 Nov 2025 05:46:24 -0500 X-MC-Unique: TFBuK8EXM7ukkGO-8XeZKA-1 X-Mimecast-MFC-AGG-ID: TFBuK8EXM7ukkGO-8XeZKA_1763635583 Received: from mx-prod-int-08.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-08.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.111]) (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 533331954B0D; Thu, 20 Nov 2025 10:46:23 +0000 (UTC) Received: from mzamazal-thinkpadp1gen7.tpbc.com (unknown [10.44.34.39]) by mx-prod-int-08.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id CA486180087B; Thu, 20 Nov 2025 10:46:21 +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: [RFC PATCH v2 10/13] libcamera: ipa: simple: Apply gain matrix in awb Date: Thu, 20 Nov 2025 11:45:45 +0100 Message-ID: <20251120104548.80268-11-mzamazal@redhat.com> In-Reply-To: <20251120104548.80268-1-mzamazal@redhat.com> References: <20251120104548.80268-1-mzamazal@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.4.1 on 10.30.177.111 X-Mimecast-Spam-Score: 0 X-Mimecast-MFC-PROC-ID: cUrLYfMywLji0x00O5Hnjh-RnODFeTIy-JRB7FJ7luI_1763635583 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 9de422519..a0af8a603 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 a17a24c9e..bbf7f168a 100644 --- a/src/ipa/simple/algorithms/lut.cpp +++ b/src/ipa/simple/algorithms/lut.cpp @@ -107,10 +107,7 @@ void Lut::prepare(IPAContext &context, params->blue[i] = gammaTable[static_cast(lutGains.b())]; } } else if (context.activeState.matrixChanged || gammaUpdateNeeded) { - Matrix gainMatrix = { { gains.r(), 0, 0, - 0, gains.g(), 0, - 0, 0, gains.b() } }; - auto matrix = context.activeState.combinedMatrix * gainMatrix; + auto &matrix = context.activeState.combinedMatrix; auto &red = params->redCcm; auto &green = params->greenCcm; auto &blue = params->blueCcm; From patchwork Thu Nov 20 10:45:46 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Milan Zamazal X-Patchwork-Id: 25096 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 CFC4FC3330 for ; Thu, 20 Nov 2025 10:46:33 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 8083960AA0; Thu, 20 Nov 2025 11:46:33 +0100 (CET) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=redhat.com header.i=@redhat.com header.b="Q39QMaLh"; 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 80D3860A8B for ; Thu, 20 Nov 2025 11:46:30 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1763635589; 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=7X2EsoUBq3VvXhpFXn51rajAnZvLH5Poc3KFf+qB3Mo=; b=Q39QMaLhEQXFciV0DwbuBdhbIhrj1mtksM7DzHo0vu5YhPYyF3pevFMvwtsX+hYALqKuW2 tMav8hClQMVIJkReBiUD4/G8sfpMxGN4sRm4jx/4yaEmyRys0g47mq9ZvLdUXqbF7Sb3bO 3Udt8AGzMVISE1vZt7QhkIhYQza5gK0= 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-306-95axWgThPGeEAI6s4UZfZg-1; Thu, 20 Nov 2025 05:46:26 -0500 X-MC-Unique: 95axWgThPGeEAI6s4UZfZg-1 X-Mimecast-MFC-AGG-ID: 95axWgThPGeEAI6s4UZfZg_1763635585 Received: from mx-prod-int-08.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-08.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.111]) (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 3BBD4180123B; Thu, 20 Nov 2025 10:46:25 +0000 (UTC) Received: from mzamazal-thinkpadp1gen7.tpbc.com (unknown [10.44.34.39]) by mx-prod-int-08.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id BAAAD180087B; Thu, 20 Nov 2025 10:46:23 +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: [RFC PATCH v2 11/13] libcamera: ipa: simple: Use float type for adjustment controls Date: Thu, 20 Nov 2025 11:45:46 +0100 Message-ID: <20251120104548.80268-12-mzamazal@redhat.com> In-Reply-To: <20251120104548.80268-1-mzamazal@redhat.com> References: <20251120104548.80268-1-mzamazal@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.4.1 on 10.30.177.111 X-Mimecast-Spam-Score: 0 X-Mimecast-MFC-PROC-ID: _iEygRH7wMRcN7FrYLo1Af9bnhYFrU0XFeC0SZ10pFQ_1763635585 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 --- src/ipa/simple/algorithms/adjust.cpp | 6 +++--- src/ipa/simple/ipa_context.h | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/ipa/simple/algorithms/adjust.cpp b/src/ipa/simple/algorithms/adjust.cpp index 600fd3c7b..57ed4f6bf 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/ipa_context.h b/src/ipa/simple/ipa_context.h index 8f56c25bf..fb063b091 100644 --- a/src/ipa/simple/ipa_context.h +++ b/src/ipa/simple/ipa_context.h @@ -58,7 +58,7 @@ struct IPAActiveState { std::array gammaTable; uint8_t blackLevel; float gamma; - double contrast; + float contrast; } gamma; Matrix ccm; @@ -68,7 +68,7 @@ struct IPAActiveState { struct { std::optional gamma; /* 0..2 range, 1.0 = normal */ - std::optional contrast; + std::optional contrast; std::optional saturation; } knobs; }; @@ -87,7 +87,7 @@ struct IPAFrameContext : public FrameContext { } gains; std::optional gamma; - std::optional contrast; + std::optional contrast; std::optional saturation; }; From patchwork Thu Nov 20 10:45:47 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Milan Zamazal X-Patchwork-Id: 25098 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 B0062C3334 for ; Thu, 20 Nov 2025 10:46:36 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 5792260AA0; Thu, 20 Nov 2025 11:46:36 +0100 (CET) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=redhat.com header.i=@redhat.com header.b="e6PxP8Mg"; 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 B34EB60AA0 for ; Thu, 20 Nov 2025 11:46:32 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1763635591; 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=/qa56mmG/1DgtFXVlsEmljH2308w/osuFl9CNcfayS8=; b=e6PxP8MgrHSMDVDOe1SYK2q2U8x7Ia9iZz/HpKmC9LNy8wA575pFDe+vVRfKyCEC2jVDC5 sBBeJilPcS3bcLtw3xQ5NvmvvkTOu7QxcqmcZXBUNJB9aMunfSXx09DV/ZowmQ/X0i0tC6 BPI6s4DCQfkw1IQYgYKd+68D4h+dDyk= 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-662-O4aD6rFfPx6GC-0V4Pnr8Q-1; Thu, 20 Nov 2025 05:46:28 -0500 X-MC-Unique: O4aD6rFfPx6GC-0V4Pnr8Q-1 X-Mimecast-MFC-AGG-ID: O4aD6rFfPx6GC-0V4Pnr8Q_1763635587 Received: from mx-prod-int-08.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-08.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.111]) (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 593D519560A7; Thu, 20 Nov 2025 10:46:27 +0000 (UTC) Received: from mzamazal-thinkpadp1gen7.tpbc.com (unknown [10.44.34.39]) by mx-prod-int-08.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id 9F2C0180094C; Thu, 20 Nov 2025 10:46:25 +0000 (UTC) From: Milan Zamazal To: libcamera-devel@lists.libcamera.org Cc: Milan Zamazal , Kieran Bingham , =?utf-8?q?Barnab=C3=A1s_P=C5=91cze?= Subject: [RFC PATCH v2 12/13] libcamera: ipa: simple: Use symbolic constants for adjust defaults Date: Thu, 20 Nov 2025 11:45:47 +0100 Message-ID: <20251120104548.80268-13-mzamazal@redhat.com> In-Reply-To: <20251120104548.80268-1-mzamazal@redhat.com> References: <20251120104548.80268-1-mzamazal@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.4.1 on 10.30.177.111 X-Mimecast-Spam-Score: 0 X-Mimecast-MFC-PROC-ID: _F4xo03WUMaq_1719CSG5Mm55wy4FfHo9dTUTNg7tTI_1763635587 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 57ed4f6bf..96c0bde0a 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 Thu Nov 20 10:45:48 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Milan Zamazal X-Patchwork-Id: 25099 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 B5AC9C3330 for ; Thu, 20 Nov 2025 10:46:37 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 3B99860AAE; Thu, 20 Nov 2025 11:46:37 +0100 (CET) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=redhat.com header.i=@redhat.com header.b="V2PLB+u8"; 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 870BE60AB5 for ; Thu, 20 Nov 2025 11:46:33 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1763635592; 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=WEe0dQuM8uQflGfUsVvTSuaPJ9If8Vzqz7dkDyx+yQ8=; b=V2PLB+u8mJtMlq3jZjFSoozx3/3oUOg31rZMYGtnoLCapJt86Pz6E8p9THGXd/UhsBid9H mY3CI0XQE0f32oeQ8XsMGrZvSf1tksy3e+mBBQ//Vi0nY6eQJNdQiighuxqBt4eYXpGVCk OFqmPoxVMhP66GfQnDOoszUI/Chfz0Q= Received: from mx-prod-mc-03.mail-002.prod.us-west-2.aws.redhat.com (ec2-54-186-198-63.us-west-2.compute.amazonaws.com [54.186.198.63]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-339-zDYgTBBhMAivND3Mb8DPww-1; Thu, 20 Nov 2025 05:46:30 -0500 X-MC-Unique: zDYgTBBhMAivND3Mb8DPww-1 X-Mimecast-MFC-AGG-ID: zDYgTBBhMAivND3Mb8DPww_1763635589 Received: from mx-prod-int-08.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-08.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.111]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mx-prod-mc-03.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id CC7611956048; Thu, 20 Nov 2025 10:46:29 +0000 (UTC) Received: from mzamazal-thinkpadp1gen7.tpbc.com (unknown [10.44.34.39]) by mx-prod-int-08.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id BFAC11800451; Thu, 20 Nov 2025 10:46:27 +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: [RFC PATCH v2 13/13] libcamera: ipa: simple: Remove Lut algorithm Date: Thu, 20 Nov 2025 11:45:48 +0100 Message-ID: <20251120104548.80268-14-mzamazal@redhat.com> In-Reply-To: <20251120104548.80268-1-mzamazal@redhat.com> References: <20251120104548.80268-1-mzamazal@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.4.1 on 10.30.177.111 X-Mimecast-Spam-Score: 0 X-Mimecast-MFC-PROC-ID: 0s4j3XLeE9OKvMdoVpWKoF6XTxs7ZovdcJyiDDsSIOs_1763635589 X-Mimecast-Originator: redhat.com content-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 debayering, where it is used. The implied and related changes are: - DebayerParams is changed to contain the real params rather than lookup tables. - 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. - 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 | 42 +---- 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/blc.cpp | 8 + src/ipa/simple/algorithms/blc.h | 6 +- src/ipa/simple/algorithms/ccm.cpp | 4 +- src/ipa/simple/algorithms/lut.cpp | 134 -------------- 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 | 9 - src/ipa/simple/soft_simple.cpp | 10 +- src/libcamera/software_isp/debayer.cpp | 71 ++------ src/libcamera/software_isp/debayer_cpu.cpp | 165 +++++++++++++----- src/libcamera/software_isp/debayer_cpu.h | 30 +++- src/libcamera/software_isp/software_isp.cpp | 17 -- 17 files changed, 202 insertions(+), 360 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 217cd5d92..e10f7f9e9 100644 --- a/include/libcamera/internal/software_isp/debayer_params.h +++ b/include/libcamera/internal/software_isp/debayer_params.h @@ -10,45 +10,19 @@ #pragma once -#include #include +#include "libcamera/internal/matrix.h" +#include "libcamera/internal/vector.h" + 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; + uint8_t blackLevel; + float gamma; + float contrast; + RGB gains; + Matrix combinedMatrix; }; } /* namespace libcamera */ diff --git a/src/ipa/simple/algorithms/adjust.cpp b/src/ipa/simple/algorithms/adjust.cpp index 96c0bde0a..81f97feeb 100644 --- a/src/ipa/simple/algorithms/adjust.cpp +++ b/src/ipa/simple/algorithms/adjust.cpp @@ -90,24 +90,22 @@ void Adjust::applySaturation(Matrix &matrix, float saturation) } void Adjust::prepare(IPAContext &context, - const uint32_t frame, + [[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 (frame == 0 || saturation != lastSaturation_) { - context.activeState.matrixChanged = true; - lastSaturation_ = saturation; + frameContext.saturation = saturation; } + + params->gamma = context.activeState.knobs.gamma.value_or(kDefaultGamma); + params->contrast = + context.activeState.knobs.contrast.value_or(kDefaultContrast); + 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 a0af8a603..b743a69cc 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/blc.cpp b/src/ipa/simple/algorithms/blc.cpp index 370385afc..365360f47 100644 --- a/src/ipa/simple/algorithms/blc.cpp +++ b/src/ipa/simple/algorithms/blc.cpp @@ -47,6 +47,14 @@ int BlackLevel::configure(IPAContext &context, return 0; } +void BlackLevel::prepare(IPAContext &context, + [[maybe_unused]] const uint32_t frame, + [[maybe_unused]] IPAFrameContext &frameContext, + DebayerParams *params) +{ + params->blackLevel = context.activeState.blc.level; +} + void BlackLevel::process(IPAContext &context, [[maybe_unused]] const uint32_t frame, IPAFrameContext &frameContext, diff --git a/src/ipa/simple/algorithms/blc.h b/src/ipa/simple/algorithms/blc.h index db9e6d639..8c919452f 100644 --- a/src/ipa/simple/algorithms/blc.h +++ b/src/ipa/simple/algorithms/blc.h @@ -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. * * Black level handling */ @@ -24,6 +24,10 @@ public: int init(IPAContext &context, const YamlObject &tuningData) override; int configure(IPAContext &context, const IPAConfigInfo &configInfo) 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, diff --git a/src/ipa/simple/algorithms/ccm.cpp b/src/ipa/simple/algorithms/ccm.cpp index 50bb3164f..561c6e123 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, const uint32_t frame, - IPAFrameContext &frameContext, [[maybe_unused]] DebayerParams *params) + IPAFrameContext &frameContext, DebayerParams *params) { const unsigned int ct = context.activeState.awb.temperatureK; @@ -51,13 +51,13 @@ void Ccm::prepare(IPAContext &context, const uint32_t frame, utils::abs_diff(ct, lastCt_) >= kTemperatureThreshold) { currentCcm_ = ccm_.getInterpolated(ct); 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 bbf7f168a..000000000 --- a/src/ipa/simple/algorithms/lut.cpp +++ /dev/null @@ -1,134 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ -/* - * Copyright (C) 2024-2025, 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) -{ - auto &gammaTable = context.activeState.gamma.gammaTable; - const auto blackLevel = context.activeState.blc.level; - const unsigned int blackIndex = blackLevel * gammaTable.size() / 256; - const auto gamma = - 1.0 / context.activeState.knobs.gamma.value_or(kDefaultGamma); - const auto contrast = context.activeState.knobs.contrast.value_or(1.0); - - const float divisor = gammaTable.size() - blackIndex - 1.0; - for (unsigned int i = blackIndex; i < gammaTable.size(); i++) { - double normalized = (i - blackIndex) / divisor; - /* 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)); - /* 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.contrast = contrast; -} - -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; - 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; - } -} - -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 f0410fe61..fc90ca526 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 fb063b091..c59e684f8 100644 --- a/src/ipa/simple/ipa_context.h +++ b/src/ipa/simple/ipa_context.h @@ -53,17 +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; - } 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 98582587c..574b972e5 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" @@ -158,6 +159,11 @@ int IPASoftSimple::init(const IPASettings &settings, } params_ = static_cast(mem); + params_->blackLevel = 0; + params_->gamma = algorithms::kDefaultGamma; + params_->contrast = algorithms::kDefaultContrast; + params_->gains = { { 1.0, 1.0, 1.0 } }; + /* combinedMatrix is reset for each frame. */ } { @@ -296,7 +302,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 e9e18c488..298875384 100644 --- a/src/libcamera/software_isp/debayer.cpp +++ b/src/libcamera/software_isp/debayer.cpp @@ -19,79 +19,28 @@ namespace libcamera { */ /** - * \var DebayerParams::kRGBLookupSize - * \brief Size of a color lookup table + * \var DebayerParams::blackLevel + * \brief 8-bit black level value */ /** - * \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::gamma + * \brief Gamma value */ /** - * \var DebayerParams::greenCcm - * \brief Lookup table for the CCM green column, mapping input values to output values + * \var DebayerParams::contrast + * \brief Contrast to be applied */ /** - * \var DebayerParams::blueCcm - * \brief Lookup table for the CCM blue column, mapping input values to output values + * \var DebayerParams::gains + * \brief Colour channel gains */ /** - * \var DebayerParams::gammaLut - * \brief Gamma lookup table used with color correction matrix + * \var DebayerParams::combinedMatrix + * \brief Colour correction matrix, including other adjustments */ /** diff --git a/src/libcamera/software_isp/debayer_cpu.cpp b/src/libcamera/software_isp/debayer_cpu.cpp index 67a303c65..a0e845f67 100644 --- a/src/libcamera/software_isp/debayer_cpu.cpp +++ b/src/libcamera/software_isp/debayer_cpu.cpp @@ -64,14 +64,6 @@ DebayerCpu::DebayerCpu(std::unique_ptr stats, const GlobalConfigurat framesToMeasure_ = configuration.option( { "software_isp", "measure", "number" }) .value_or(framesToMeasure_); - - /* 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) }; - } } DebayerCpu::~DebayerCpu() = default; @@ -84,21 +76,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++; /* @@ -547,6 +539,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 = 0.0; + window_.x = ((inputCfg.size.width - outputCfg.size.width) / 2) & ~(inputConfig_.patternSize.width - 1); window_.y = ((inputCfg.size.height - outputCfg.size.height) / 2) & @@ -775,6 +777,103 @@ inline int64_t timeDiff(timespec &after, timespec &before) } /* namespace */ +void DebayerCpu::updateGammaTable(DebayerParams ¶ms) +{ + const uint8_t blackLevel = params.blackLevel; + const unsigned int blackIndex = blackLevel * gammaTable_.size() / 256; + const double gamma = 1.0 / params.gamma; + const double contrast = params.contrast; + + const float divisor = gammaTable_.size() - blackIndex - 1.0; + for (unsigned int i = blackIndex; i < gammaTable_.size(); i++) { + double normalized = (i - blackIndex) / divisor; + /* 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)); + /* 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.contrast != params_.contrast; + 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=" << static_cast(params.blackLevel) + << "; gamma=" << params.gamma + << "; contrast=" << params.contrast + << "; gains=" << params.gains + << "; matrix=" << params.combinedMatrix; + + params_ = params; +} + void DebayerCpu::process(uint32_t frame, FrameBuffer *input, FrameBuffer *output, DebayerParams params) { timespec frameStartTime; @@ -794,25 +893,7 @@ void DebayerCpu::process(uint32_t frame, FrameBuffer *input, FrameBuffer *output for (const FrameBuffer::Plane &plane : output->planes()) dmaSyncers.emplace_back(plane.fd, DmaSyncer::SyncType::Write); - 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; + 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 03e0d7843..941f08b7b 100644 --- a/src/libcamera/software_isp/debayer_cpu.h +++ b/src/libcamera/software_isp/debayer_cpu.h @@ -19,6 +19,7 @@ #include "libcamera/internal/bayer_format.h" #include "libcamera/internal/global_configuration.h" +#include "libcamera/internal/software_isp/debayer_params.h" #include "debayer.h" #include "swstats_cpu.h" @@ -135,17 +136,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; - DebayerParams::LookupTable red_; - DebayerParams::LookupTable green_; - DebayerParams::LookupTable blue_; - DebayerParams::CcmLookupTable redCcm_; - DebayerParams::CcmLookupTable greenCcm_; - DebayerParams::CcmLookupTable blueCcm_; - DebayerParams::LookupTable gammaLut_; + 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/software_isp.cpp b/src/libcamera/software_isp/software_isp.cpp index 341c0352c..ad40471d3 100644 --- a/src/libcamera/software_isp/software_isp.cpp +++ b/src/libcamera/software_isp/software_isp.cpp @@ -80,23 +80,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;