From patchwork Thu Jan 22 16:19:21 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Milan Zamazal X-Patchwork-Id: 25927 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 8B76FBDCBF for ; Thu, 22 Jan 2026 16:20:17 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 28CAC61FCC; Thu, 22 Jan 2026 17:20:17 +0100 (CET) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=redhat.com header.i=@redhat.com header.b="UrexPxA0"; 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 597AB61F84 for ; Thu, 22 Jan 2026 17:20:14 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1769098813; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=i8UGhkmLt6QS6rqX1oJshBUPoYg/mDS32mL7uD/INFs=; b=UrexPxA0JtPL4SsP7ob5vG6XoCUKnRp8VHJFiMUtBbzr2zpbA8zuaW5iey9z8nG3gNLsFG gnN5cbSWP5RRk77l9f2YPM/hS484DjLMPW4MRyQ7dAmrPo8HlNo81loTI+mOzniFJl8/1+ C4uii1Yko4Y63DRw9pl6cj7yUdMCz9s= 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-212-GcWIDLwkN2m1IVNSTBWDRw-1; Thu, 22 Jan 2026 11:20:09 -0500 X-MC-Unique: GcWIDLwkN2m1IVNSTBWDRw-1 X-Mimecast-MFC-AGG-ID: GcWIDLwkN2m1IVNSTBWDRw_1769098809 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 D35C61955D9F; Thu, 22 Jan 2026 16:20:08 +0000 (UTC) Received: from mzamazal-thinkpadp1gen7.tpbc.com (unknown [10.45.224.97]) by mx-prod-int-08.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id C83A31801760; Thu, 22 Jan 2026 16:20:06 +0000 (UTC) From: Milan Zamazal To: libcamera-devel@lists.libcamera.org Cc: Milan Zamazal , Kieran Bingham , =?utf-8?q?Barnab=C3=A1s_P=C5=91cze?= Subject: [PATCH v4 01/15] libcamera: ipa: simple: Remove an unused include from awb.cpp Date: Thu, 22 Jan 2026 17:19:21 +0100 Message-ID: <20260122161935.208562-2-mzamazal@redhat.com> In-Reply-To: <20260122161935.208562-1-mzamazal@redhat.com> References: <20260122161935.208562-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: 1GsI8Iw1dJ2MIq74BXgyaFT2oG2EV1cnUo40HNcZFeQ_1769098809 X-Mimecast-Originator: redhat.com X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" is not used any more. Reviewed-by: Kieran Bingham Reviewed-by: Barnabás Pőcze Signed-off-by: Milan Zamazal --- src/ipa/simple/algorithms/awb.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/ipa/simple/algorithms/awb.cpp b/src/ipa/simple/algorithms/awb.cpp index 0080865aa..a391359fb 100644 --- a/src/ipa/simple/algorithms/awb.cpp +++ b/src/ipa/simple/algorithms/awb.cpp @@ -7,7 +7,6 @@ #include "awb.h" -#include #include #include From patchwork Thu Jan 22 16:19:22 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Milan Zamazal X-Patchwork-Id: 25928 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 7EC79C327D for ; Thu, 22 Jan 2026 16:20:20 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 1FCE761FD1; Thu, 22 Jan 2026 17:20:20 +0100 (CET) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=redhat.com header.i=@redhat.com header.b="jM1UUqGH"; 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 B6E6261F84 for ; Thu, 22 Jan 2026 17:20:15 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1769098814; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=ecGQpeqL+0q1o7SA6sjZ4G4MfTbDuFJ/73Hvra+WDYc=; b=jM1UUqGHVUhNJelJkOJtRHPx2mvgSmpXg19NxcsCzdfa+fmgsxfd3djLVBYivFCgzWuLGr ui2hPJUTCeLCZHm63rjXsx5VZa7MBqwsAoYl9Le+gnJHt78kmeXpiO7SzFvpzYdpjjWm9L +BfiYe+J0B1B3JIvzFCZapje85ARbBI= 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-563-zLeN4x6_Nvmjpk39qhRV0g-1; Thu, 22 Jan 2026 11:20:12 -0500 X-MC-Unique: zLeN4x6_Nvmjpk39qhRV0g-1 X-Mimecast-MFC-AGG-ID: zLeN4x6_Nvmjpk39qhRV0g_1769098811 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 56A0A18005BA; Thu, 22 Jan 2026 16:20:11 +0000 (UTC) Received: from mzamazal-thinkpadp1gen7.tpbc.com (unknown [10.45.224.97]) by mx-prod-int-08.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id 776DE1800240; Thu, 22 Jan 2026 16:20:09 +0000 (UTC) From: Milan Zamazal To: libcamera-devel@lists.libcamera.org Cc: Milan Zamazal , Kieran Bingham , =?utf-8?q?Barnab=C3=A1s_P=C5=91cze?= Subject: [PATCH v4 02/15] libcamera: ipa: simple: Unwrap IPAFrameContext::ccm Date: Thu, 22 Jan 2026 17:19:22 +0100 Message-ID: <20260122161935.208562-3-mzamazal@redhat.com> In-Reply-To: <20260122161935.208562-1-mzamazal@redhat.com> References: <20260122161935.208562-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: KgAhDP4DYwa1rSrG5jtCCpYlCI3Xa0rWOlh9O-i3bgU_1769098811 X-Mimecast-Originator: redhat.com X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" The struct has only one member and there is no immediate need to add more. Let's use the member directly, to make things a bit simpler. Reviewed-by: Kieran Bingham Reviewed-by: Barnabás Pőcze Signed-off-by: Milan Zamazal --- src/ipa/simple/algorithms/ccm.cpp | 6 +++--- src/ipa/simple/ipa_context.h | 4 +--- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/src/ipa/simple/algorithms/ccm.cpp b/src/ipa/simple/algorithms/ccm.cpp index 0a98406c1..d7d3dda76 100644 --- a/src/ipa/simple/algorithms/ccm.cpp +++ b/src/ipa/simple/algorithms/ccm.cpp @@ -94,7 +94,7 @@ void Ccm::prepare(IPAContext &context, const uint32_t frame, if (frame > 0 && utils::abs_diff(ct, lastCt_) < kTemperatureThreshold && saturation == lastSaturation_) { - frameContext.ccm.ccm = context.activeState.ccm.ccm; + frameContext.ccm = context.activeState.ccm.ccm; context.activeState.ccm.changed = false; return; } @@ -106,9 +106,9 @@ void Ccm::prepare(IPAContext &context, const uint32_t frame, applySaturation(ccm, saturation.value()); context.activeState.ccm.ccm = ccm; - frameContext.ccm.ccm = ccm; frameContext.saturation = saturation; context.activeState.ccm.changed = true; + frameContext.ccm = ccm; } void Ccm::process([[maybe_unused]] IPAContext &context, @@ -117,7 +117,7 @@ void Ccm::process([[maybe_unused]] IPAContext &context, [[maybe_unused]] const SwIspStats *stats, ControlList &metadata) { - metadata.set(controls::ColourCorrectionMatrix, frameContext.ccm.ccm.data()); + metadata.set(controls::ColourCorrectionMatrix, frameContext.ccm.data()); const auto &saturation = frameContext.saturation; metadata.set(controls::Saturation, saturation.value_or(1.0)); diff --git a/src/ipa/simple/ipa_context.h b/src/ipa/simple/ipa_context.h index 26db92e93..74e77c841 100644 --- a/src/ipa/simple/ipa_context.h +++ b/src/ipa/simple/ipa_context.h @@ -75,9 +75,7 @@ struct IPAActiveState { }; struct IPAFrameContext : public FrameContext { - struct { - Matrix ccm; - } ccm; + Matrix ccm; struct { int32_t exposure; From patchwork Thu Jan 22 16:19:23 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Milan Zamazal X-Patchwork-Id: 25929 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 B19ADBDCBF for ; Thu, 22 Jan 2026 16:20:21 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 2823E61FCF; Thu, 22 Jan 2026 17:20:21 +0100 (CET) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=redhat.com header.i=@redhat.com header.b="MuXrqwe4"; 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 A173F61FCF for ; Thu, 22 Jan 2026 17:20:17 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1769098816; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=IEP7X+EchYL0d/Sa5J5GJDOjq0bnXvAMifj9sbuOl6c=; b=MuXrqwe4wRQlr360x6HDm+49perWxzvr2FboCKG1AiktxuSkpT4u7fOpzGn87G6K8TL3yO QT1YvERCjvp3m+i9ia7YrVEdTEiuYZzIEal7zZNz1Wct3ZWm2+W1yYCkvhSIn44g3dfG7M yiN/wHZs5m/E4K+qHSqsd1vndFqqVOQ= 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-548-RDqJBJ_-PSa1paeHtY90fg-1; Thu, 22 Jan 2026 11:20:15 -0500 X-MC-Unique: RDqJBJ_-PSa1paeHtY90fg-1 X-Mimecast-MFC-AGG-ID: RDqJBJ_-PSa1paeHtY90fg_1769098814 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 2F2AE1944EA8; Thu, 22 Jan 2026 16:20:14 +0000 (UTC) Received: from mzamazal-thinkpadp1gen7.tpbc.com (unknown [10.45.224.97]) by mx-prod-int-08.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id ACF1E1800577; Thu, 22 Jan 2026 16:20:11 +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?= , Bryan O'Donoghue Subject: [PATCH v4 03/15] libcamera: ipa: simple: Generalize tracking matrix changes Date: Thu, 22 Jan 2026 17:19:23 +0100 Message-ID: <20260122161935.208562-4-mzamazal@redhat.com> In-Reply-To: <20260122161935.208562-1-mzamazal@redhat.com> References: <20260122161935.208562-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: c2xh-wIrrSWjr8184HnX_9L3S-1QUcW4ZNBh-2c4PY4_1769098814 X-Mimecast-Originator: redhat.com X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" IPAActiveState::ccm stores the colour correction matrix (CCM) and whether it has been changed. The change flag is later used when recomputing or not the lookup tables. But the CCM may include other corrections than just the sensor colour correction, for example white balance and saturation adjustments. These things should be separated and IPAActiveState::ccm should represent just the CCM itself. As the first step towards that cleanup, let's separate the change flag from the CCM. And wrap the only remaining member of IPAActiveState::ccm. Also, let's reset the separated change flag in the lookup tables; it'll be no longer tied to just CCM handling. This patch doesn't change actual behaviour and it still reports the combined matrix as CCM in metadata. This is addressed in the followup patches. Reviewed-by: Bryan O'Donoghue Reviewed-by: Barnabás Pőcze Signed-off-by: Milan Zamazal --- src/ipa/simple/algorithms/ccm.cpp | 7 +++---- src/ipa/simple/algorithms/lut.cpp | 5 +++-- src/ipa/simple/ipa_context.h | 6 ++---- 3 files changed, 8 insertions(+), 10 deletions(-) diff --git a/src/ipa/simple/algorithms/ccm.cpp b/src/ipa/simple/algorithms/ccm.cpp index d7d3dda76..e05e5bc28 100644 --- a/src/ipa/simple/algorithms/ccm.cpp +++ b/src/ipa/simple/algorithms/ccm.cpp @@ -94,8 +94,7 @@ void Ccm::prepare(IPAContext &context, const uint32_t frame, if (frame > 0 && utils::abs_diff(ct, lastCt_) < kTemperatureThreshold && saturation == lastSaturation_) { - frameContext.ccm = context.activeState.ccm.ccm; - context.activeState.ccm.changed = false; + frameContext.ccm = context.activeState.ccm; return; } @@ -105,9 +104,9 @@ void Ccm::prepare(IPAContext &context, const uint32_t frame, if (saturation) applySaturation(ccm, saturation.value()); - context.activeState.ccm.ccm = ccm; + context.activeState.ccm = ccm; frameContext.saturation = saturation; - context.activeState.ccm.changed = true; + context.activeState.matrixChanged = true; frameContext.ccm = ccm; } diff --git a/src/ipa/simple/algorithms/lut.cpp b/src/ipa/simple/algorithms/lut.cpp index 54cb804e7..c19b34482 100644 --- a/src/ipa/simple/algorithms/lut.cpp +++ b/src/ipa/simple/algorithms/lut.cpp @@ -127,11 +127,11 @@ void Lut::prepare(IPAContext &context, params->green[i] = gammaTable[static_cast(lutGains.g())]; params->blue[i] = gammaTable[static_cast(lutGains.b())]; } - } else if (context.activeState.ccm.changed || gammaUpdateNeeded) { + } else if (context.activeState.matrixChanged || gammaUpdateNeeded) { Matrix gainCcm = { { gains.r(), 0, 0, 0, gains.g(), 0, 0, 0, gains.b() } }; - auto ccm = context.activeState.ccm.ccm * gainCcm; + auto ccm = context.activeState.ccm * gainCcm; auto &red = params->redCcm; auto &green = params->greenCcm; auto &blue = params->blueCcm; @@ -150,6 +150,7 @@ void Lut::prepare(IPAContext &context, params->gammaLut[i] = gammaTable[i / div]; } } + context.activeState.matrixChanged = false; } params->gamma = context.configuration.gamma; diff --git a/src/ipa/simple/ipa_context.h b/src/ipa/simple/ipa_context.h index 74e77c841..ea1852cd4 100644 --- a/src/ipa/simple/ipa_context.h +++ b/src/ipa/simple/ipa_context.h @@ -62,10 +62,8 @@ struct IPAActiveState { double contrastExp; } gamma; - struct { - Matrix ccm; - bool changed; - } ccm; + Matrix ccm; + bool matrixChanged = false; struct { /* 0..2 range, 1.0 = normal */ From patchwork Thu Jan 22 16:19:24 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Milan Zamazal X-Patchwork-Id: 25930 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 3765ABDCBF for ; Thu, 22 Jan 2026 16:20:26 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id DE0EE61FD3; Thu, 22 Jan 2026 17:20:25 +0100 (CET) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=redhat.com header.i=@redhat.com header.b="QpsSxyZ0"; 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 7264061FC4 for ; Thu, 22 Jan 2026 17:20:24 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1769098823; 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=UA0HsVOkrg7ZUYEaw/vP8z0T6FwkjQRAWfkDsfQm0tU=; b=QpsSxyZ0C3f2flZs6SaXeq1FeBYKx73e25gyOpiJX4pUff5dq0l7lNO2Mjd/sawqBdEIga Upd+CYNVYb8b0cIrorc9i70jfZ9v0XKxkiZWzjIQDuPDsDVjqBv5qUjRxF71tjdiKBBuMG rCMt/XCsxy8cteU3AxWAlT3IcrYkJXA= 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-39-hkmWUw2nM5aCyNd5xOx_qA-1; Thu, 22 Jan 2026 11:20:17 -0500 X-MC-Unique: hkmWUw2nM5aCyNd5xOx_qA-1 X-Mimecast-MFC-AGG-ID: hkmWUw2nM5aCyNd5xOx_qA_1769098816 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 B83A0180034F; Thu, 22 Jan 2026 16:20:16 +0000 (UTC) Received: from mzamazal-thinkpadp1gen7.tpbc.com (unknown [10.45.224.97]) by mx-prod-int-08.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id AEADC1800577; Thu, 22 Jan 2026 16:20: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: [PATCH v4 04/15] libcamera: ipa: simple: Rename "ccm" identifiers not specific to CCM Date: Thu, 22 Jan 2026 17:19:24 +0100 Message-ID: <20260122161935.208562-5-mzamazal@redhat.com> In-Reply-To: <20260122161935.208562-1-mzamazal@redhat.com> References: <20260122161935.208562-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: U6RKnuskLLVNga8EPkcdg90M_hASxbSK-AiBTJqpdyw_1769098816 X-Mimecast-Originator: redhat.com X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" Let's rename the identifiers that are related to general colour corrections applied by matrix operations, rather than directly to the sensor colour correction matrix. Reviewed-by: Barnabás Pőcze Signed-off-by: Milan Zamazal --- src/ipa/simple/algorithms/lut.cpp | 32 +++++++++++++++---------------- src/ipa/simple/algorithms/lut.h | 2 +- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/src/ipa/simple/algorithms/lut.cpp b/src/ipa/simple/algorithms/lut.cpp index c19b34482..5c4683fd8 100644 --- a/src/ipa/simple/algorithms/lut.cpp +++ b/src/ipa/simple/algorithms/lut.cpp @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ /* - * Copyright (C) 2024-2025, Red Hat Inc. + * Copyright (C) 2024-2026, Red Hat Inc. * * Color lookup tables construction */ @@ -89,7 +89,7 @@ void Lut::updateGammaTable(IPAContext &context) context.activeState.gamma.contrastExp = contrastExp; } -int16_t Lut::ccmValue(unsigned int i, float ccm) const +int16_t Lut::matrixValue(unsigned int i, float ccm) const { return std::round(i * ccm); } @@ -128,25 +128,25 @@ void Lut::prepare(IPAContext &context, params->blue[i] = gammaTable[static_cast(lutGains.b())]; } } else if (context.activeState.matrixChanged || gammaUpdateNeeded) { - Matrix gainCcm = { { gains.r(), 0, 0, - 0, gains.g(), 0, - 0, 0, gains.b() } }; - auto ccm = context.activeState.ccm * gainCcm; + Matrix gainMatrix = { { gains.r(), 0, 0, + 0, gains.g(), 0, + 0, 0, gains.b() } }; + auto matrix = context.activeState.ccm * gainMatrix; auto &red = params->redCcm; auto &green = params->greenCcm; auto &blue = params->blueCcm; - params->ccm = ccm; + params->ccm = matrix; if (!context.gpuIspEnabled) { for (unsigned int i = 0; i < DebayerParams::kRGBLookupSize; i++) { - red[i].r = ccmValue(i, ccm[0][0]); - red[i].g = ccmValue(i, ccm[1][0]); - red[i].b = ccmValue(i, ccm[2][0]); - green[i].r = ccmValue(i, ccm[0][1]); - green[i].g = ccmValue(i, ccm[1][1]); - green[i].b = ccmValue(i, ccm[2][1]); - blue[i].r = ccmValue(i, ccm[0][2]); - blue[i].g = ccmValue(i, ccm[1][2]); - blue[i].b = ccmValue(i, ccm[2][2]); + red[i].r = matrixValue(i, matrix[0][0]); + red[i].g = matrixValue(i, matrix[1][0]); + red[i].b = matrixValue(i, matrix[2][0]); + green[i].r = matrixValue(i, matrix[0][1]); + green[i].g = matrixValue(i, matrix[1][1]); + green[i].b = matrixValue(i, matrix[2][1]); + blue[i].r = matrixValue(i, matrix[0][2]); + blue[i].g = matrixValue(i, matrix[1][2]); + blue[i].b = matrixValue(i, matrix[2][2]); params->gammaLut[i] = gammaTable[i / div]; } } diff --git a/src/ipa/simple/algorithms/lut.h b/src/ipa/simple/algorithms/lut.h index ba8b9021b..0eafd0695 100644 --- a/src/ipa/simple/algorithms/lut.h +++ b/src/ipa/simple/algorithms/lut.h @@ -38,7 +38,7 @@ public: private: void updateGammaTable(IPAContext &context); - int16_t ccmValue(unsigned int i, float ccm) const; + int16_t matrixValue(unsigned int i, float ccm) const; }; } /* namespace ipa::soft::algorithms */ From patchwork Thu Jan 22 16:19:25 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Milan Zamazal X-Patchwork-Id: 25931 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 B3A69C327D for ; Thu, 22 Jan 2026 16:20:26 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 743A861FC7; Thu, 22 Jan 2026 17:20:26 +0100 (CET) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=redhat.com header.i=@redhat.com header.b="Dk1PtG8S"; 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 1370561FC4 for ; Thu, 22 Jan 2026 17:20:24 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1769098824; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=yHt8hEx7L7x6m8Giz/E0YsIRoOVuMDJUld2I1itRJUs=; b=Dk1PtG8SkTAdG06shYvin2Bb+ivDMAQEa0NKBCoqy6wZTFXgyAnee6aDB6SQqku2+oSzwW GfMqP1f8iuEF9vycEc8AyEuIO5jvzQHWLG+0JU1UnTlYUH9UJXgRMYwOwLoyD6fLEWAi4g sdyvm64zwiJfl/SOTCU4iXNcH/S4BCQ= 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-189-g1hOB1ohPISHuWMdZsww_Q-1; Thu, 22 Jan 2026 11:20:20 -0500 X-MC-Unique: g1hOB1ohPISHuWMdZsww_Q-1 X-Mimecast-MFC-AGG-ID: g1hOB1ohPISHuWMdZsww_Q_1769098819 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 647E8180045C; Thu, 22 Jan 2026 16:20:19 +0000 (UTC) Received: from mzamazal-thinkpadp1gen7.tpbc.com (unknown [10.45.224.97]) by mx-prod-int-08.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id 52E4D1801760; Thu, 22 Jan 2026 16:20:17 +0000 (UTC) From: Milan Zamazal To: libcamera-devel@lists.libcamera.org Cc: Milan Zamazal , Kieran Bingham , =?utf-8?q?Barnab=C3=A1s_P=C5=91cze?= Subject: [PATCH v4 05/15] libcamera: ipa: simple: Introduce a general correction matrix Date: Thu, 22 Jan 2026 17:19:25 +0100 Message-ID: <20260122161935.208562-6-mzamazal@redhat.com> In-Reply-To: <20260122161935.208562-1-mzamazal@redhat.com> References: <20260122161935.208562-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: z97TwdX4EhRXWoyptgNxh0m5K7BHgOd-hCp9jWdtVU0_1769098819 X-Mimecast-Originator: redhat.com X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" Let's introduce IPAActiveState::combinedMatrix that is separate from IPAActiveState::ccm and represents the overall correction matrix, not only the sensor colour correction matrix. IPAActiveState::ccm still includes everything; this is changed in the followup patch. Reviewed-by: Kieran Bingham Reviewed-by: Barnabás Pőcze Signed-off-by: Milan Zamazal --- src/ipa/simple/algorithms/ccm.cpp | 1 + src/ipa/simple/algorithms/lut.cpp | 2 +- src/ipa/simple/ipa_context.h | 1 + 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/ipa/simple/algorithms/ccm.cpp b/src/ipa/simple/algorithms/ccm.cpp index e05e5bc28..a3e8cd6c4 100644 --- a/src/ipa/simple/algorithms/ccm.cpp +++ b/src/ipa/simple/algorithms/ccm.cpp @@ -104,6 +104,7 @@ void Ccm::prepare(IPAContext &context, const uint32_t frame, if (saturation) applySaturation(ccm, saturation.value()); + context.activeState.combinedMatrix = ccm; context.activeState.ccm = ccm; frameContext.saturation = saturation; context.activeState.matrixChanged = true; diff --git a/src/ipa/simple/algorithms/lut.cpp b/src/ipa/simple/algorithms/lut.cpp index 5c4683fd8..141ea17fa 100644 --- a/src/ipa/simple/algorithms/lut.cpp +++ b/src/ipa/simple/algorithms/lut.cpp @@ -131,7 +131,7 @@ void Lut::prepare(IPAContext &context, Matrix gainMatrix = { { gains.r(), 0, 0, 0, gains.g(), 0, 0, 0, gains.b() } }; - auto matrix = context.activeState.ccm * gainMatrix; + auto matrix = context.activeState.combinedMatrix * gainMatrix; auto &red = params->redCcm; auto &green = params->greenCcm; auto &blue = params->blueCcm; diff --git a/src/ipa/simple/ipa_context.h b/src/ipa/simple/ipa_context.h index ea1852cd4..58dcad290 100644 --- a/src/ipa/simple/ipa_context.h +++ b/src/ipa/simple/ipa_context.h @@ -63,6 +63,7 @@ struct IPAActiveState { } gamma; Matrix ccm; + Matrix combinedMatrix; bool matrixChanged = false; struct { From patchwork Thu Jan 22 16:19:26 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Milan Zamazal X-Patchwork-Id: 25932 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 6CC1FBDCBF for ; Thu, 22 Jan 2026 16:20:29 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 0C2A661FD6; Thu, 22 Jan 2026 17:20:29 +0100 (CET) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=redhat.com header.i=@redhat.com header.b="gpc6cyyp"; 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 52F9D61FD6 for ; Thu, 22 Jan 2026 17:20:27 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1769098826; 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=+wgL+P6kMkDZ81o4zsJ9WkocXgMiqwVZYegIbyGVed4=; b=gpc6cyypXVL4cpeeLhQVEe/EJp/QZSYXQoJ2TpD7OygKAE4MVd943iMe71xK4zwMmOrUTe VG+YopHGiB1XsRn8/qKjCGF5QLNYaEY7wH7BKE0kqpImPy86jkZcRGwsVP9t/iqLlCAhl8 m2ncTNq2Tixiv3cmNJlSCuMM/SryVoE= 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-658--UyLs7OSMGWdOajm0RKPpw-1; Thu, 22 Jan 2026 11:20:22 -0500 X-MC-Unique: -UyLs7OSMGWdOajm0RKPpw-1 X-Mimecast-MFC-AGG-ID: -UyLs7OSMGWdOajm0RKPpw_1769098822 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 EB6D41800359; Thu, 22 Jan 2026 16:20:21 +0000 (UTC) Received: from mzamazal-thinkpadp1gen7.tpbc.com (unknown [10.45.224.97]) by mx-prod-int-08.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id EFDB61801A45; Thu, 22 Jan 2026 16:20: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: [PATCH v4 06/15] libcamera: ipa: simple: Initialise the general correction matrix Date: Thu, 22 Jan 2026 17:19:26 +0100 Message-ID: <20260122161935.208562-7-mzamazal@redhat.com> In-Reply-To: <20260122161935.208562-1-mzamazal@redhat.com> References: <20260122161935.208562-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: pwDJSZR47NBlO6JZ1pSMr2PnbgYnC55T6epLo1i85Fo_1769098822 X-Mimecast-Originator: redhat.com content-type: text/plain; charset="US-ASCII"; x-default=true X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" The combined matrix must be reset to the initial value before each frame is prepared. This must be done outside algorithms because any of the algorithms may be disabled while the matrix must be always initialised. Let's initialise the combined matrix to the identity matrix (which keeps the pixel values unchanged) in software ISP just before calling `prepare' on the algorithms. Matrix updates can no longer be skipped in ccm.cpp, otherwise the CCM won't be applied if there is no temperature or saturation change. We explicitly track whether the CCM has been set up completely rather than relying on the frame number, to avoid missing the initialisation in case the first frame is skipped due to some error. Signed-off-by: Milan Zamazal --- src/ipa/simple/algorithms/ccm.cpp | 29 +++++++++++++---------------- src/ipa/simple/algorithms/ccm.h | 3 ++- src/ipa/simple/ipa_context.h | 1 - src/ipa/simple/soft_simple.cpp | 2 ++ 4 files changed, 17 insertions(+), 18 deletions(-) diff --git a/src/ipa/simple/algorithms/ccm.cpp b/src/ipa/simple/algorithms/ccm.cpp index a3e8cd6c4..85643645b 100644 --- a/src/ipa/simple/algorithms/ccm.cpp +++ b/src/ipa/simple/algorithms/ccm.cpp @@ -83,7 +83,7 @@ void Ccm::applySaturation(Matrix &ccm, float saturation) ccm = ycbcr2rgb * saturationMatrix * rgb2ycbcr * ccm; } -void Ccm::prepare(IPAContext &context, const uint32_t frame, +void Ccm::prepare(IPAContext &context, [[maybe_unused]] const uint32_t frame, IPAFrameContext &frameContext, [[maybe_unused]] DebayerParams *params) { auto &saturation = context.activeState.knobs.saturation; @@ -91,24 +91,21 @@ void Ccm::prepare(IPAContext &context, const uint32_t frame, const unsigned int ct = context.activeState.awb.temperatureK; /* Change CCM only on saturation or bigger temperature changes. */ - if (frame > 0 && - utils::abs_diff(ct, lastCt_) < kTemperatureThreshold && - saturation == lastSaturation_) { - frameContext.ccm = context.activeState.ccm; - return; + if (!currentCcm_ || + utils::abs_diff(ct, lastCt_) >= kTemperatureThreshold || + saturation != lastSaturation_) { + currentCcm_ = ccm_.getInterpolated(ct); + if (saturation) + applySaturation(currentCcm_.value(), saturation.value()); + lastCt_ = ct; + lastSaturation_ = saturation; + context.activeState.matrixChanged = true; } - lastCt_ = ct; - lastSaturation_ = saturation; - Matrix ccm = ccm_.getInterpolated(ct); - if (saturation) - applySaturation(ccm, saturation.value()); - - context.activeState.combinedMatrix = ccm; - context.activeState.ccm = ccm; + context.activeState.combinedMatrix = + currentCcm_.value() * context.activeState.combinedMatrix; frameContext.saturation = saturation; - context.activeState.matrixChanged = true; - frameContext.ccm = ccm; + frameContext.ccm = currentCcm_.value(); } void Ccm::process([[maybe_unused]] IPAContext &context, diff --git a/src/ipa/simple/algorithms/ccm.h b/src/ipa/simple/algorithms/ccm.h index 8279a3d59..867a680c3 100644 --- a/src/ipa/simple/algorithms/ccm.h +++ b/src/ipa/simple/algorithms/ccm.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ /* - * Copyright (C) 2024-2025, Red Hat Inc. + * Copyright (C) 2024-2026, Red Hat Inc. * * Color correction matrix */ @@ -47,6 +47,7 @@ private: unsigned int lastCt_; std::optional lastSaturation_; Interpolator> ccm_; + std::optional> currentCcm_; }; } /* namespace ipa::soft::algorithms */ diff --git a/src/ipa/simple/ipa_context.h b/src/ipa/simple/ipa_context.h index 58dcad290..a3ff3d038 100644 --- a/src/ipa/simple/ipa_context.h +++ b/src/ipa/simple/ipa_context.h @@ -62,7 +62,6 @@ struct IPAActiveState { double contrastExp; } gamma; - Matrix ccm; Matrix combinedMatrix; bool matrixChanged = false; diff --git a/src/ipa/simple/soft_simple.cpp b/src/ipa/simple/soft_simple.cpp index 57836c73c..732e82510 100644 --- a/src/ipa/simple/soft_simple.cpp +++ b/src/ipa/simple/soft_simple.cpp @@ -282,6 +282,8 @@ void IPASoftSimple::queueRequest(const uint32_t frame, const ControlList &contro void IPASoftSimple::computeParams(const uint32_t frame) { + context_.activeState.combinedMatrix = Matrix::identity(); + IPAFrameContext &frameContext = context_.frameContexts.get(frame); for (auto const &algo : algorithms()) algo->prepare(context_, frame, frameContext, params_); From patchwork Thu Jan 22 16:19:27 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Milan Zamazal X-Patchwork-Id: 25933 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 3F6C6C327D for ; Thu, 22 Jan 2026 16:20:31 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id ED19161FD9; Thu, 22 Jan 2026 17:20:30 +0100 (CET) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=redhat.com header.i=@redhat.com header.b="QP3blrb6"; 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 9373261FD8 for ; Thu, 22 Jan 2026 17:20:29 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1769098828; 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=pmz99rAzwyKtGcrvg6/L0jG/wSOxiMwvwFfmiwrck54=; b=QP3blrb6KT0JRJKqaL6KJmvv3r8fJpmaA/eNE3RQ3/HccxaFshJbnqfhHcVlliDbwElH7H TDV8++Db3aGP7fVPBxu5SmUYZQgJzb0i0idbTWvpSeekViT5YHhuXB6CGia5N29KSDY7aB PJBzJCOao6YWimurJoU+8uWQZvnR5Cg= 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-479-RNBfqSrRML2jMnCsfIIskQ-1; Thu, 22 Jan 2026 11:20:25 -0500 X-MC-Unique: RNBfqSrRML2jMnCsfIIskQ-1 X-Mimecast-MFC-AGG-ID: RNBfqSrRML2jMnCsfIIskQ_1769098824 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 36FEA1955D9B; Thu, 22 Jan 2026 16:20:24 +0000 (UTC) Received: from mzamazal-thinkpadp1gen7.tpbc.com (unknown [10.45.224.97]) by mx-prod-int-08.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id 57C7D1800240; Thu, 22 Jan 2026 16:20:22 +0000 (UTC) From: Milan Zamazal To: libcamera-devel@lists.libcamera.org Cc: Milan Zamazal , Kieran Bingham , =?utf-8?q?Barnab=C3=A1s_P=C5=91cze?= Subject: [PATCH v4 07/15] libcamera: ipa: simple: Separate saturation from CCM Date: Thu, 22 Jan 2026 17:19:27 +0100 Message-ID: <20260122161935.208562-8-mzamazal@redhat.com> In-Reply-To: <20260122161935.208562-1-mzamazal@redhat.com> References: <20260122161935.208562-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: 8SIdXtoT9gpo5ovqBv_VQVe_RpVXKDg_AEoz8fywobo_1769098824 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 | 60 +-------------- src/ipa/simple/algorithms/ccm.h | 9 --- src/ipa/simple/algorithms/meson.build | 1 + src/ipa/simple/data/uncalibrated.yaml | 1 + 6 files changed, 164 insertions(+), 65 deletions(-) create mode 100644 src/ipa/simple/algorithms/adjust.cpp create mode 100644 src/ipa/simple/algorithms/adjust.h diff --git a/src/ipa/simple/algorithms/adjust.cpp b/src/ipa/simple/algorithms/adjust.cpp new file mode 100644 index 000000000..8ce0631e4 --- /dev/null +++ b/src/ipa/simple/algorithms/adjust.cpp @@ -0,0 +1,106 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2024, Ideas On Board + * Copyright (C) 2024-2025, Red Hat Inc. + * + * Common image adjustments + */ + +#include "adjust.h" + +#include +#include + +#include + +#include "libcamera/internal/matrix.h" + +namespace libcamera { + +namespace ipa::soft::algorithms { + +LOG_DEFINE_CATEGORY(IPASoftAdjust) + +int Adjust::init(IPAContext &context, [[maybe_unused]] const YamlObject &tuningData) +{ + if (context.ccmEnabled) + context.ctrlMap[&controls::Saturation] = ControlInfo(0.0f, 2.0f, 1.0f); + return 0; +} + +int Adjust::configure(IPAContext &context, + [[maybe_unused]] const IPAConfigInfo &configInfo) +{ + context.activeState.knobs.saturation = std::optional(); + + return 0; +} + +void Adjust::queueRequest(typename Module::Context &context, + [[maybe_unused]] const uint32_t frame, + [[maybe_unused]] typename Module::FrameContext &frameContext, + const ControlList &controls) +{ + const auto &saturation = controls.get(controls::Saturation); + if (saturation.has_value()) { + context.activeState.knobs.saturation = saturation; + LOG(IPASoftAdjust, Debug) << "Setting saturation to " << saturation.value(); + } +} + +void Adjust::applySaturation(Matrix &matrix, float saturation) +{ + /* https://en.wikipedia.org/wiki/YCbCr#ITU-R_BT.601_conversion */ + const Matrix rgb2ycbcr{ + { 0.256788235294, 0.504129411765, 0.0979058823529, + -0.148223529412, -0.290992156863, 0.439215686275, + 0.439215686275, -0.367788235294, -0.0714274509804 } + }; + const Matrix ycbcr2rgb{ + { 1.16438356164, 0, 1.59602678571, + 1.16438356164, -0.391762290094, -0.812967647235, + 1.16438356164, 2.01723214285, 0 } + }; + const Matrix saturationMatrix{ + { 1, 0, 0, + 0, saturation, 0, + 0, 0, saturation } + }; + matrix = + ycbcr2rgb * saturationMatrix * rgb2ycbcr * matrix; +} + +void Adjust::prepare(IPAContext &context, + [[maybe_unused]] const uint32_t frame, + IPAFrameContext &frameContext, + [[maybe_unused]] DebayerParams *params) +{ + if (!context.ccmEnabled) + return; + + auto &saturation = context.activeState.knobs.saturation; + frameContext.saturation = saturation; + if (saturation) + applySaturation(context.activeState.combinedMatrix, saturation.value()); + + if (saturation != lastSaturation_) { + context.activeState.matrixChanged = true; + lastSaturation_ = saturation; + } +} + +void Adjust::process([[maybe_unused]] IPAContext &context, + [[maybe_unused]] const uint32_t frame, + IPAFrameContext &frameContext, + [[maybe_unused]] const SwIspStats *stats, + ControlList &metadata) +{ + const auto &saturation = frameContext.saturation; + metadata.set(controls::Saturation, saturation.value_or(1.0)); +} + +REGISTER_IPA_ALGORITHM(Adjust, "Adjust") + +} /* namespace ipa::soft::algorithms */ + +} /* namespace libcamera */ diff --git a/src/ipa/simple/algorithms/adjust.h b/src/ipa/simple/algorithms/adjust.h new file mode 100644 index 000000000..c4baa2503 --- /dev/null +++ b/src/ipa/simple/algorithms/adjust.h @@ -0,0 +1,52 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2024-2025, Red Hat Inc. + * + * Color correction matrix + */ + +#pragma once + +#include + +#include "libcamera/internal/matrix.h" + +#include + +#include "algorithm.h" + +namespace libcamera { + +namespace ipa::soft::algorithms { + +class Adjust : public Algorithm +{ +public: + Adjust() = default; + ~Adjust() = default; + + int init(IPAContext &context, const YamlObject &tuningData) override; + int configure(IPAContext &context, + const IPAConfigInfo &configInfo) override; + void queueRequest(typename Module::Context &context, + const uint32_t frame, + typename Module::FrameContext &frameContext, + const ControlList &controls) override; + void prepare(IPAContext &context, + const uint32_t frame, + IPAFrameContext &frameContext, + DebayerParams *params) override; + void process(IPAContext &context, const uint32_t frame, + IPAFrameContext &frameContext, + const SwIspStats *stats, + ControlList &metadata) override; + +private: + void applySaturation(Matrix &ccm, float saturation); + + std::optional lastSaturation_; +}; + +} /* namespace ipa::soft::algorithms */ + +} /* namespace libcamera */ diff --git a/src/ipa/simple/algorithms/ccm.cpp b/src/ipa/simple/algorithms/ccm.cpp index 85643645b..5576a301f 100644 --- a/src/ipa/simple/algorithms/ccm.cpp +++ b/src/ipa/simple/algorithms/ccm.cpp @@ -1,9 +1,9 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ /* * Copyright (C) 2024, Ideas On Board - * Copyright (C) 2024-2025, Red Hat Inc. + * Copyright (C) 2024-2026, Red Hat Inc. * - * Color correction matrix + saturation + * Color correction matrix */ #include "ccm.h" @@ -37,74 +37,25 @@ int Ccm::init([[maybe_unused]] IPAContext &context, const YamlObject &tuningData } context.ccmEnabled = true; - context.ctrlMap[&controls::Saturation] = ControlInfo(0.0f, 2.0f, 1.0f); return 0; } -int Ccm::configure(IPAContext &context, - [[maybe_unused]] const IPAConfigInfo &configInfo) -{ - context.activeState.knobs.saturation = std::optional(); - - return 0; -} - -void Ccm::queueRequest(typename Module::Context &context, - [[maybe_unused]] const uint32_t frame, - [[maybe_unused]] typename Module::FrameContext &frameContext, - const ControlList &controls) -{ - const auto &saturation = controls.get(controls::Saturation); - if (saturation.has_value()) { - context.activeState.knobs.saturation = saturation; - LOG(IPASoftCcm, Debug) << "Setting saturation to " << saturation.value(); - } -} - -void Ccm::applySaturation(Matrix &ccm, float saturation) -{ - /* https://en.wikipedia.org/wiki/YCbCr#ITU-R_BT.601_conversion */ - const Matrix rgb2ycbcr{ - { 0.256788235294, 0.504129411765, 0.0979058823529, - -0.148223529412, -0.290992156863, 0.439215686275, - 0.439215686275, -0.367788235294, -0.0714274509804 } - }; - const Matrix ycbcr2rgb{ - { 1.16438356164, 0, 1.59602678571, - 1.16438356164, -0.391762290094, -0.812967647235, - 1.16438356164, 2.01723214285, 0 } - }; - const Matrix saturationMatrix{ - { 1, 0, 0, - 0, saturation, 0, - 0, 0, saturation } - }; - ccm = ycbcr2rgb * saturationMatrix * rgb2ycbcr * ccm; -} - void Ccm::prepare(IPAContext &context, [[maybe_unused]] const uint32_t frame, IPAFrameContext &frameContext, [[maybe_unused]] DebayerParams *params) { - auto &saturation = context.activeState.knobs.saturation; - const unsigned int ct = context.activeState.awb.temperatureK; - /* Change CCM only on saturation or bigger temperature changes. */ + /* Change CCM only on bigger temperature changes. */ if (!currentCcm_ || - utils::abs_diff(ct, lastCt_) >= kTemperatureThreshold || - saturation != lastSaturation_) { + utils::abs_diff(ct, lastCt_) >= kTemperatureThreshold) { currentCcm_ = ccm_.getInterpolated(ct); - if (saturation) - applySaturation(currentCcm_.value(), saturation.value()); lastCt_ = ct; - lastSaturation_ = saturation; context.activeState.matrixChanged = true; } context.activeState.combinedMatrix = currentCcm_.value() * context.activeState.combinedMatrix; - frameContext.saturation = saturation; frameContext.ccm = currentCcm_.value(); } @@ -115,9 +66,6 @@ void Ccm::process([[maybe_unused]] IPAContext &context, ControlList &metadata) { metadata.set(controls::ColourCorrectionMatrix, frameContext.ccm.data()); - - const auto &saturation = frameContext.saturation; - metadata.set(controls::Saturation, saturation.value_or(1.0)); } REGISTER_IPA_ALGORITHM(Ccm, "Ccm") diff --git a/src/ipa/simple/algorithms/ccm.h b/src/ipa/simple/algorithms/ccm.h index 867a680c3..62009a375 100644 --- a/src/ipa/simple/algorithms/ccm.h +++ b/src/ipa/simple/algorithms/ccm.h @@ -26,12 +26,6 @@ public: ~Ccm() = default; int init(IPAContext &context, const YamlObject &tuningData) override; - int configure(IPAContext &context, - const IPAConfigInfo &configInfo) override; - void queueRequest(typename Module::Context &context, - const uint32_t frame, - typename Module::FrameContext &frameContext, - const ControlList &controls) override; void prepare(IPAContext &context, const uint32_t frame, IPAFrameContext &frameContext, @@ -42,10 +36,7 @@ public: ControlList &metadata) override; private: - void applySaturation(Matrix &ccm, float saturation); - unsigned int lastCt_; - std::optional lastSaturation_; Interpolator> ccm_; std::optional> currentCcm_; }; diff --git a/src/ipa/simple/algorithms/meson.build b/src/ipa/simple/algorithms/meson.build index 2d0adb059..ebe9f20dd 100644 --- a/src/ipa/simple/algorithms/meson.build +++ b/src/ipa/simple/algorithms/meson.build @@ -1,6 +1,7 @@ # SPDX-License-Identifier: CC0-1.0 soft_simple_ipa_algorithms = files([ + 'adjust.cpp', 'awb.cpp', 'agc.cpp', 'blc.cpp', diff --git a/src/ipa/simple/data/uncalibrated.yaml b/src/ipa/simple/data/uncalibrated.yaml index 8b6df9afc..e389e0588 100644 --- a/src/ipa/simple/data/uncalibrated.yaml +++ b/src/ipa/simple/data/uncalibrated.yaml @@ -14,6 +14,7 @@ algorithms: ccm: [ 1, 0, 0, 0, 1, 0, 0, 0, 1] + - Adjust: - Lut: - Agc: ... From patchwork Thu Jan 22 16:19:28 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Milan Zamazal X-Patchwork-Id: 25934 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 4117ABDCBF for ; Thu, 22 Jan 2026 16:20:34 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id C2B4B61FDB; Thu, 22 Jan 2026 17:20:33 +0100 (CET) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=redhat.com header.i=@redhat.com header.b="aWdec1IM"; 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 612C861FC4 for ; Thu, 22 Jan 2026 17:20:32 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1769098831; 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=4fYe7/byNE/+dhrkrJ0rBP0B9LszNlSxL+WQjH5JV+Q=; b=aWdec1IMGH4zUrNcG+BfgNflhXe4bKwZjAcol2T1kRwrwyuqmIUw4KpSlqpGghhMXPKeWP V4SynnKsthZmIyre5hc327A5Xvaan+7tjKu3PYrva9sNOE3NHFxyuUYpOWWbaS0VlRTI9u uNFstdfo0f36yvgfAL+UhzdovrjfF30= 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-316-ggYp0HE-NE60OhJKhQ421Q-1; Thu, 22 Jan 2026 11:20:27 -0500 X-MC-Unique: ggYp0HE-NE60OhJKhQ421Q-1 X-Mimecast-MFC-AGG-ID: ggYp0HE-NE60OhJKhQ421Q_1769098826 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 2ADB21800359; Thu, 22 Jan 2026 16:20:26 +0000 (UTC) Received: from mzamazal-thinkpadp1gen7.tpbc.com (unknown [10.45.224.97]) by mx-prod-int-08.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id 9D6901800577; Thu, 22 Jan 2026 16:20:24 +0000 (UTC) From: Milan Zamazal To: libcamera-devel@lists.libcamera.org Cc: Milan Zamazal , Kieran Bingham , =?utf-8?q?Barnab=C3=A1s_P=C5=91cze?= Subject: [PATCH v4 08/15] libcamera: ipa: simple: Move contrast settings to adjust.cpp Date: Thu, 22 Jan 2026 17:19:28 +0100 Message-ID: <20260122161935.208562-9-mzamazal@redhat.com> In-Reply-To: <20260122161935.208562-1-mzamazal@redhat.com> References: <20260122161935.208562-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: eqBR2gcKr6Wns2jZJJPt3_cVw5xMjLPxE3n9cEJdTj8_1769098826 X-Mimecast-Originator: redhat.com X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" Let's move the contrast settings from lut.cpp to adjust.cpp, where they belong, similarly to saturation. Reviewed-by: Barnabás Pőcze Signed-off-by: Milan Zamazal --- src/ipa/simple/algorithms/adjust.cpp | 14 +++++++++++ src/ipa/simple/algorithms/lut.cpp | 35 +--------------------------- src/ipa/simple/algorithms/lut.h | 11 --------- 3 files changed, 15 insertions(+), 45 deletions(-) diff --git a/src/ipa/simple/algorithms/adjust.cpp b/src/ipa/simple/algorithms/adjust.cpp index 8ce0631e4..27ae2a53a 100644 --- a/src/ipa/simple/algorithms/adjust.cpp +++ b/src/ipa/simple/algorithms/adjust.cpp @@ -23,6 +23,7 @@ LOG_DEFINE_CATEGORY(IPASoftAdjust) int Adjust::init(IPAContext &context, [[maybe_unused]] const YamlObject &tuningData) { + context.ctrlMap[&controls::Contrast] = ControlInfo(0.0f, 2.0f, 1.0f); if (context.ccmEnabled) context.ctrlMap[&controls::Saturation] = ControlInfo(0.0f, 2.0f, 1.0f); return 0; @@ -31,6 +32,7 @@ int Adjust::init(IPAContext &context, [[maybe_unused]] const YamlObject &tuningD int Adjust::configure(IPAContext &context, [[maybe_unused]] const IPAConfigInfo &configInfo) { + context.activeState.knobs.contrast = std::optional(); context.activeState.knobs.saturation = std::optional(); return 0; @@ -41,6 +43,12 @@ void Adjust::queueRequest(typename Module::Context &context, [[maybe_unused]] typename Module::FrameContext &frameContext, const ControlList &controls) { + const auto &contrast = controls.get(controls::Contrast); + if (contrast.has_value()) { + context.activeState.knobs.contrast = contrast; + LOG(IPASoftAdjust, Debug) << "Setting contrast to " << contrast.value(); + } + const auto &saturation = controls.get(controls::Saturation); if (saturation.has_value()) { context.activeState.knobs.saturation = saturation; @@ -75,6 +83,8 @@ void Adjust::prepare(IPAContext &context, IPAFrameContext &frameContext, [[maybe_unused]] DebayerParams *params) { + frameContext.contrast = context.activeState.knobs.contrast; + if (!context.ccmEnabled) return; @@ -95,6 +105,10 @@ void Adjust::process([[maybe_unused]] IPAContext &context, [[maybe_unused]] const SwIspStats *stats, ControlList &metadata) { + const auto &contrast = frameContext.contrast; + if (contrast) + metadata.set(controls::Contrast, contrast.value()); + const auto &saturation = frameContext.saturation; metadata.set(controls::Saturation, saturation.value_or(1.0)); } diff --git a/src/ipa/simple/algorithms/lut.cpp b/src/ipa/simple/algorithms/lut.cpp index 141ea17fa..3a00bf4ee 100644 --- a/src/ipa/simple/algorithms/lut.cpp +++ b/src/ipa/simple/algorithms/lut.cpp @@ -24,36 +24,16 @@ LOG_DEFINE_CATEGORY(IPASoftLut) namespace ipa::soft::algorithms { -int Lut::init(IPAContext &context, - [[maybe_unused]] const YamlObject &tuningData) -{ - context.ctrlMap[&controls::Contrast] = ControlInfo(0.0f, 2.0f, 1.0f); - return 0; -} - int Lut::configure(IPAContext &context, [[maybe_unused]] const IPAConfigInfo &configInfo) { /* Gamma value is fixed */ context.configuration.gamma = 1.0 / 2.2; - context.activeState.knobs.contrast = std::optional(); updateGammaTable(context); return 0; } -void Lut::queueRequest(typename Module::Context &context, - [[maybe_unused]] const uint32_t frame, - [[maybe_unused]] typename Module::FrameContext &frameContext, - const ControlList &controls) -{ - const auto &contrast = controls.get(controls::Contrast); - if (contrast.has_value()) { - context.activeState.knobs.contrast = contrast; - LOG(IPASoftLut, Debug) << "Setting contrast to " << contrast.value(); - } -} - void Lut::updateGammaTable(IPAContext &context) { const auto blackLevel = context.activeState.blc.level; @@ -96,11 +76,9 @@ int16_t Lut::matrixValue(unsigned int i, float ccm) const void Lut::prepare(IPAContext &context, [[maybe_unused]] const uint32_t frame, - IPAFrameContext &frameContext, + [[maybe_unused]] IPAFrameContext &frameContext, DebayerParams *params) { - frameContext.contrast = context.activeState.knobs.contrast; - /* * Update the gamma table if needed. This means if black level changes * and since the black level gets updated only if a lower value is @@ -157,17 +135,6 @@ void Lut::prepare(IPAContext &context, params->contrastExp = context.activeState.gamma.contrastExp; } -void Lut::process([[maybe_unused]] IPAContext &context, - [[maybe_unused]] const uint32_t frame, - [[maybe_unused]] IPAFrameContext &frameContext, - [[maybe_unused]] const SwIspStats *stats, - ControlList &metadata) -{ - const auto &contrast = frameContext.contrast; - if (contrast) - metadata.set(controls::Contrast, contrast.value()); -} - REGISTER_IPA_ALGORITHM(Lut, "Lut") } /* namespace ipa::soft::algorithms */ diff --git a/src/ipa/simple/algorithms/lut.h b/src/ipa/simple/algorithms/lut.h index 0eafd0695..ad16d1e8e 100644 --- a/src/ipa/simple/algorithms/lut.h +++ b/src/ipa/simple/algorithms/lut.h @@ -19,22 +19,11 @@ public: Lut() = default; ~Lut() = default; - int init(IPAContext &context, const YamlObject &tuningData) override; int configure(IPAContext &context, const IPAConfigInfo &configInfo) override; - void queueRequest(typename Module::Context &context, - const uint32_t frame, - typename Module::FrameContext &frameContext, - const ControlList &controls) - override; void prepare(IPAContext &context, const uint32_t frame, IPAFrameContext &frameContext, DebayerParams *params) override; - void process(IPAContext &context, - const uint32_t frame, - IPAFrameContext &frameContext, - const SwIspStats *stats, - ControlList &metadata) override; private: void updateGammaTable(IPAContext &context); From patchwork Thu Jan 22 16:19:29 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Milan Zamazal X-Patchwork-Id: 25935 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 1C080C327D for ; Thu, 22 Jan 2026 16:20:36 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id A7D5F61FDE; Thu, 22 Jan 2026 17:20:35 +0100 (CET) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=redhat.com header.i=@redhat.com header.b="BWspU5Er"; 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 0A58C61FDE for ; Thu, 22 Jan 2026 17:20:33 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1769098832; 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=19dB+gIIyZNgSWoN8M2d5Kc7AHinkW6/uwzoVDE7jA4=; b=BWspU5Ert/48ZFLqFmrD9RfR5So249LnYigIXvWA7ezweL5mbgP9O/eTtRa/gg1UGAziKx DV5R/GjPNw52EudDTHzS0gXGkTC4fnsNyV/veWcRtLBc9Z8je0/hAHKJMyIdW3M8X/EUuM +mRcjorfQp/rWVZc+/1Ffa1JtCSzg+w= 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-120-6nYziSl4PB2CIZlGWMXCQA-1; Thu, 22 Jan 2026 11:20:30 -0500 X-MC-Unique: 6nYziSl4PB2CIZlGWMXCQA-1 X-Mimecast-MFC-AGG-ID: 6nYziSl4PB2CIZlGWMXCQA_1769098828 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 7343F1955D9B; Thu, 22 Jan 2026 16:20:28 +0000 (UTC) Received: from mzamazal-thinkpadp1gen7.tpbc.com (unknown [10.45.224.97]) by mx-prod-int-08.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id B6A001800240; Thu, 22 Jan 2026 16:20:26 +0000 (UTC) From: Milan Zamazal To: libcamera-devel@lists.libcamera.org Cc: Milan Zamazal , Kieran Bingham , =?utf-8?q?Barnab=C3=A1s_P=C5=91cze?= Subject: [PATCH v4 09/15] libcamera: ipa: simple: Make gamma adjustable Date: Thu, 22 Jan 2026 17:19:29 +0100 Message-ID: <20260122161935.208562-10-mzamazal@redhat.com> In-Reply-To: <20260122161935.208562-1-mzamazal@redhat.com> References: <20260122161935.208562-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: qaVMY_MsjyoegQdJOA1owYoxnsj-L31l0xPOWyop4aw_1769098828 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, and report it in metadata. Signed-off-by: Milan Zamazal --- src/ipa/simple/algorithms/adjust.cpp | 12 ++++++++++++ src/ipa/simple/algorithms/adjust.h | 4 +++- src/ipa/simple/algorithms/lut.cpp | 11 ++++++----- src/ipa/simple/ipa_context.h | 6 ++++-- 4 files changed, 25 insertions(+), 8 deletions(-) diff --git a/src/ipa/simple/algorithms/adjust.cpp b/src/ipa/simple/algorithms/adjust.cpp index 27ae2a53a..a067f7b84 100644 --- a/src/ipa/simple/algorithms/adjust.cpp +++ b/src/ipa/simple/algorithms/adjust.cpp @@ -23,6 +23,7 @@ LOG_DEFINE_CATEGORY(IPASoftAdjust) int Adjust::init(IPAContext &context, [[maybe_unused]] const YamlObject &tuningData) { + context.ctrlMap[&controls::Gamma] = ControlInfo(0.1f, 10.0f, kDefaultGamma); context.ctrlMap[&controls::Contrast] = ControlInfo(0.0f, 2.0f, 1.0f); if (context.ccmEnabled) context.ctrlMap[&controls::Saturation] = ControlInfo(0.0f, 2.0f, 1.0f); @@ -32,6 +33,7 @@ int Adjust::init(IPAContext &context, [[maybe_unused]] const YamlObject &tuningD int Adjust::configure(IPAContext &context, [[maybe_unused]] const IPAConfigInfo &configInfo) { + context.activeState.knobs.gamma = kDefaultGamma; context.activeState.knobs.contrast = std::optional(); context.activeState.knobs.saturation = std::optional(); @@ -43,6 +45,12 @@ void Adjust::queueRequest(typename Module::Context &context, [[maybe_unused]] typename Module::FrameContext &frameContext, const ControlList &controls) { + const auto &gamma = controls.get(controls::Gamma); + if (gamma.has_value()) { + context.activeState.knobs.gamma = gamma.value(); + LOG(IPASoftAdjust, Debug) << "Setting gamma to " << gamma.value(); + } + const auto &contrast = controls.get(controls::Contrast); if (contrast.has_value()) { context.activeState.knobs.contrast = contrast; @@ -83,6 +91,7 @@ void Adjust::prepare(IPAContext &context, IPAFrameContext &frameContext, [[maybe_unused]] DebayerParams *params) { + frameContext.gamma = context.activeState.knobs.gamma; frameContext.contrast = context.activeState.knobs.contrast; if (!context.ccmEnabled) @@ -105,6 +114,9 @@ void Adjust::process([[maybe_unused]] IPAContext &context, [[maybe_unused]] const SwIspStats *stats, ControlList &metadata) { + const auto &gamma = frameContext.gamma; + metadata.set(controls::Gamma, gamma); + const auto &contrast = frameContext.contrast; if (contrast) metadata.set(controls::Contrast, contrast.value()); diff --git a/src/ipa/simple/algorithms/adjust.h b/src/ipa/simple/algorithms/adjust.h index c4baa2503..7644138ff 100644 --- a/src/ipa/simple/algorithms/adjust.h +++ b/src/ipa/simple/algorithms/adjust.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ /* - * Copyright (C) 2024-2025, Red Hat Inc. + * Copyright (C) 2024-2026, Red Hat Inc. * * Color correction matrix */ @@ -19,6 +19,8 @@ namespace libcamera { namespace ipa::soft::algorithms { +constexpr float kDefaultGamma = 2.2f; + class Adjust : public Algorithm { public: diff --git a/src/ipa/simple/algorithms/lut.cpp b/src/ipa/simple/algorithms/lut.cpp index 3a00bf4ee..9740f8c8d 100644 --- a/src/ipa/simple/algorithms/lut.cpp +++ b/src/ipa/simple/algorithms/lut.cpp @@ -18,6 +18,8 @@ #include "simple/ipa_context.h" +#include "adjust.h" + namespace libcamera { LOG_DEFINE_CATEGORY(IPASoftLut) @@ -27,8 +29,6 @@ namespace ipa::soft::algorithms { int Lut::configure(IPAContext &context, [[maybe_unused]] const IPAConfigInfo &configInfo) { - /* Gamma value is fixed */ - context.configuration.gamma = 1.0 / 2.2; updateGammaTable(context); return 0; @@ -37,6 +37,7 @@ int Lut::configure(IPAContext &context, void Lut::updateGammaTable(IPAContext &context) { const auto blackLevel = context.activeState.blc.level; + const auto gamma = 1.0 / context.activeState.knobs.gamma; const auto contrast = context.activeState.knobs.contrast.value_or(1.0); /* Convert 0..2 to 0..infinity; avoid actual inifinity at tan(pi/2) */ double contrastExp = tan(std::clamp(contrast * M_PI_4, 0.0, M_PI_2 - 0.00001)); @@ -52,8 +53,7 @@ void Lut::updateGammaTable(IPAContext &context) normalized = 0.5 * std::pow(normalized / 0.5, contrastExp); else normalized = 1.0 - 0.5 * std::pow((1.0 - normalized) / 0.5, contrastExp); - gammaTable[i] = UINT8_MAX * - std::pow(normalized, context.configuration.gamma); + gammaTable[i] = UINT8_MAX * std::pow(normalized, gamma); } /* * Due to CCM operations, the table lookup may reach indices below the black @@ -65,6 +65,7 @@ void Lut::updateGammaTable(IPAContext &context) gammaTable[blackIndex]); } + context.activeState.gamma.gamma = gamma; context.activeState.gamma.blackLevel = blackLevel; context.activeState.gamma.contrastExp = contrastExp; } @@ -131,7 +132,7 @@ void Lut::prepare(IPAContext &context, context.activeState.matrixChanged = false; } - params->gamma = context.configuration.gamma; + params->gamma = context.activeState.gamma.gamma; params->contrastExp = context.activeState.gamma.contrastExp; } diff --git a/src/ipa/simple/ipa_context.h b/src/ipa/simple/ipa_context.h index a3ff3d038..eeade9d35 100644 --- a/src/ipa/simple/ipa_context.h +++ b/src/ipa/simple/ipa_context.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ /* - * Copyright (C) 2024-2025 Red Hat, Inc. + * Copyright (C) 2024-2026 Red Hat, Inc. * * Simple pipeline IPA Context */ @@ -25,7 +25,6 @@ namespace libcamera { namespace ipa::soft { struct IPASessionConfiguration { - float gamma; struct { int32_t exposureMin, exposureMax; double againMin, againMax, again10, againMinStep; @@ -58,6 +57,7 @@ struct IPAActiveState { struct { std::array gammaTable; uint8_t blackLevel; + float gamma; double contrast; double contrastExp; } gamma; @@ -66,6 +66,7 @@ struct IPAActiveState { bool matrixChanged = false; struct { + float gamma; /* 0..2 range, 1.0 = normal */ std::optional contrast; std::optional saturation; @@ -85,6 +86,7 @@ struct IPAFrameContext : public FrameContext { double blue; } gains; + float gamma; std::optional contrast; std::optional saturation; }; From patchwork Thu Jan 22 16:19:30 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Milan Zamazal X-Patchwork-Id: 25937 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 88B4FC327D for ; Thu, 22 Jan 2026 16:20:42 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 1EC7761FD4; Thu, 22 Jan 2026 17:20:42 +0100 (CET) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=redhat.com header.i=@redhat.com header.b="OkA/Domx"; 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 CAE3161FD4 for ; Thu, 22 Jan 2026 17:20:39 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1769098838; 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=TjD7mM/VrSDQn03xL75WPVgkICDm+csPILtj7ABoVuI=; b=OkA/Domx6d+1aNsiB6gTxxgS7GpV5nPaGMf6gIfl2WxsT2AOzvE4AIjZEah18XumOzwHc7 Qkf3sESCamxMJx7HwBydHu0QpkOvfPxAZ/TZVMLBzDuikd30fcLh+OgUgMufCOjawF0Ptf F5noGOfS1nDmORCKrPMs1IcQ7zRd7CM= 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-94-7cutVTFNNaG9enp1l6KcTw-1; Thu, 22 Jan 2026 11:20:32 -0500 X-MC-Unique: 7cutVTFNNaG9enp1l6KcTw-1 X-Mimecast-MFC-AGG-ID: 7cutVTFNNaG9enp1l6KcTw_1769098830 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 93B5A1955D8E; Thu, 22 Jan 2026 16:20:30 +0000 (UTC) Received: from mzamazal-thinkpadp1gen7.tpbc.com (unknown [10.45.224.97]) by mx-prod-int-08.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id 03D6A1800240; Thu, 22 Jan 2026 16:20:28 +0000 (UTC) From: Milan Zamazal To: libcamera-devel@lists.libcamera.org Cc: Milan Zamazal , Kieran Bingham , =?utf-8?q?Barnab=C3=A1s_P=C5=91cze?= Subject: [PATCH v4 10/15] libcamera: ipa: simple: Apply gain matrix in awb Date: Thu, 22 Jan 2026 17:19:30 +0100 Message-ID: <20260122161935.208562-11-mzamazal@redhat.com> In-Reply-To: <20260122161935.208562-1-mzamazal@redhat.com> References: <20260122161935.208562-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: ZmJIyXxhNCQkVg_5T-muG6FW-qsT8LDJEllXweVM8YM_1769098830 X-Mimecast-Originator: redhat.com content-type: text/plain; charset="US-ASCII"; x-default=true X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" Now, when we have a combined matrix, we can apply AWB gains to it directly in awb.cpp. Signed-off-by: Milan Zamazal --- src/ipa/simple/algorithms/awb.cpp | 7 ++++++- src/ipa/simple/algorithms/lut.cpp | 5 +---- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/ipa/simple/algorithms/awb.cpp b/src/ipa/simple/algorithms/awb.cpp index a391359fb..4d2f1df15 100644 --- a/src/ipa/simple/algorithms/awb.cpp +++ b/src/ipa/simple/algorithms/awb.cpp @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ /* - * Copyright (C) 2024, Red Hat Inc. + * Copyright (C) 2024-2025 Red Hat Inc. * * Auto white balance */ @@ -40,6 +40,11 @@ void Awb::prepare(IPAContext &context, [[maybe_unused]] DebayerParams *params) { auto &gains = context.activeState.awb.gains; + Matrix gainMatrix = { { gains.r(), 0, 0, + 0, gains.g(), 0, + 0, 0, gains.b() } }; + context.activeState.combinedMatrix = + context.activeState.combinedMatrix * gainMatrix; /* Just report, the gains are applied in LUT algorithm. */ frameContext.gains.red = gains.r(); frameContext.gains.blue = gains.b(); diff --git a/src/ipa/simple/algorithms/lut.cpp b/src/ipa/simple/algorithms/lut.cpp index 9740f8c8d..d8e92c613 100644 --- a/src/ipa/simple/algorithms/lut.cpp +++ b/src/ipa/simple/algorithms/lut.cpp @@ -107,10 +107,7 @@ void Lut::prepare(IPAContext &context, params->blue[i] = gammaTable[static_cast(lutGains.b())]; } } else if (context.activeState.matrixChanged || gammaUpdateNeeded) { - Matrix gainMatrix = { { gains.r(), 0, 0, - 0, gains.g(), 0, - 0, 0, gains.b() } }; - auto matrix = context.activeState.combinedMatrix * gainMatrix; + auto &matrix = context.activeState.combinedMatrix; auto &red = params->redCcm; auto &green = params->greenCcm; auto &blue = params->blueCcm; From patchwork Thu Jan 22 16:19:31 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Milan Zamazal X-Patchwork-Id: 25936 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 D3298BDCBF for ; Thu, 22 Jan 2026 16:20:39 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 8DE7061FD1; Thu, 22 Jan 2026 17:20:39 +0100 (CET) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=redhat.com header.i=@redhat.com header.b="CJH00/Hi"; 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 47AD761FC4 for ; Thu, 22 Jan 2026 17:20:38 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1769098837; 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=cl8ga13jeNy1VyVUMmG0mXproifV0Y4zXuXjfejjGLk=; b=CJH00/Hi8FfYlWF0Wnvn8WfNdBq4pmzU30+AqTsFd6lzn+gvvGzUFN91cLSo3sfP/HbJ0I EM1x7JdqkIssQU7h5mBBzcItbY+Giz46SpngBvjdAOra2q7QmceMvuBJNEdwEOsRGJVKqW HDGoTG2awXFlk3IfcFQmNqWNp6vnr/I= 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-376-nQx9i2Y2P1CuaQf8bZFOWQ-1; Thu, 22 Jan 2026 11:20:34 -0500 X-MC-Unique: nQx9i2Y2P1CuaQf8bZFOWQ-1 X-Mimecast-MFC-AGG-ID: nQx9i2Y2P1CuaQf8bZFOWQ_1769098833 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 E246F180044D; Thu, 22 Jan 2026 16:20:32 +0000 (UTC) Received: from mzamazal-thinkpadp1gen7.tpbc.com (unknown [10.45.224.97]) by mx-prod-int-08.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id 0F3DA1800577; Thu, 22 Jan 2026 16:20:30 +0000 (UTC) From: Milan Zamazal To: libcamera-devel@lists.libcamera.org Cc: Milan Zamazal , Kieran Bingham , =?utf-8?q?Barnab=C3=A1s_P=C5=91cze?= Subject: [PATCH v4 11/15] libcamera: ipa: simple: Use float type for adjustment controls Date: Thu, 22 Jan 2026 17:19:31 +0100 Message-ID: <20260122161935.208562-12-mzamazal@redhat.com> In-Reply-To: <20260122161935.208562-1-mzamazal@redhat.com> References: <20260122161935.208562-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: SAa2WTtr8kZwf61fN0NtwyDZQqM1N21lSdO93Lm5Ow8_1769098833 X-Mimecast-Originator: redhat.com X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" control_ids.h defines the contrast type as float, let's use the same in simple IPA, instead of double. Saturation and gamma already use float, except for the knobs initializers, let's use float for the knobs too. Reviewed-by: Barnabás Pőcze Signed-off-by: Milan Zamazal --- include/libcamera/internal/software_isp/debayer_params.h | 2 +- src/ipa/simple/algorithms/adjust.cpp | 4 ++-- src/ipa/simple/algorithms/lut.cpp | 2 +- src/ipa/simple/ipa_context.h | 8 ++++---- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/include/libcamera/internal/software_isp/debayer_params.h b/include/libcamera/internal/software_isp/debayer_params.h index 256c7d43d..2d69bd295 100644 --- a/include/libcamera/internal/software_isp/debayer_params.h +++ b/include/libcamera/internal/software_isp/debayer_params.h @@ -59,7 +59,7 @@ struct DebayerParams { Matrix ccm; RGB blackLevel; float gamma; - double contrastExp; + float contrastExp; }; } /* namespace libcamera */ diff --git a/src/ipa/simple/algorithms/adjust.cpp b/src/ipa/simple/algorithms/adjust.cpp index a067f7b84..a8b7c5ab6 100644 --- a/src/ipa/simple/algorithms/adjust.cpp +++ b/src/ipa/simple/algorithms/adjust.cpp @@ -34,8 +34,8 @@ int Adjust::configure(IPAContext &context, [[maybe_unused]] const IPAConfigInfo &configInfo) { context.activeState.knobs.gamma = kDefaultGamma; - context.activeState.knobs.contrast = std::optional(); - context.activeState.knobs.saturation = std::optional(); + context.activeState.knobs.contrast = std::optional(); + context.activeState.knobs.saturation = std::optional(); return 0; } diff --git a/src/ipa/simple/algorithms/lut.cpp b/src/ipa/simple/algorithms/lut.cpp index d8e92c613..fd442259a 100644 --- a/src/ipa/simple/algorithms/lut.cpp +++ b/src/ipa/simple/algorithms/lut.cpp @@ -40,7 +40,7 @@ void Lut::updateGammaTable(IPAContext &context) const auto gamma = 1.0 / context.activeState.knobs.gamma; const auto contrast = context.activeState.knobs.contrast.value_or(1.0); /* Convert 0..2 to 0..infinity; avoid actual inifinity at tan(pi/2) */ - double contrastExp = tan(std::clamp(contrast * M_PI_4, 0.0, M_PI_2 - 0.00001)); + float contrastExp = tan(std::clamp(contrast * M_PI_4, 0.0, M_PI_2 - 0.00001)); if (!context.gpuIspEnabled) { auto &gammaTable = context.activeState.gamma.gammaTable; diff --git a/src/ipa/simple/ipa_context.h b/src/ipa/simple/ipa_context.h index eeade9d35..293e35b71 100644 --- a/src/ipa/simple/ipa_context.h +++ b/src/ipa/simple/ipa_context.h @@ -58,8 +58,8 @@ struct IPAActiveState { std::array gammaTable; uint8_t blackLevel; float gamma; - double contrast; - double contrastExp; + float contrast; + float contrastExp; } gamma; Matrix combinedMatrix; @@ -68,7 +68,7 @@ struct IPAActiveState { struct { float gamma; /* 0..2 range, 1.0 = normal */ - std::optional contrast; + std::optional contrast; std::optional saturation; } knobs; }; @@ -87,7 +87,7 @@ struct IPAFrameContext : public FrameContext { } gains; float gamma; - std::optional contrast; + std::optional contrast; std::optional saturation; }; From patchwork Thu Jan 22 16:19:32 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Milan Zamazal X-Patchwork-Id: 25938 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 51D5DBDCBF for ; Thu, 22 Jan 2026 16:20:44 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id E4A8A61FE2; Thu, 22 Jan 2026 17:20:43 +0100 (CET) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=redhat.com header.i=@redhat.com header.b="dPB0kleF"; 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 948C961FE4 for ; Thu, 22 Jan 2026 17:20:40 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1769098839; 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=sDLvUusc5OkFfSeMpHOZCQS+ZyxZNundjfzXfadkPRA=; b=dPB0kleFj+6CGzcLRLjWzKQHcIzvEc2/bpiJnvEUXvp5cWH42qR86nQ2BZ0T7ldwn+HeCa I8HAhew3KXoozR8lcbuOuTrg4YKOJ42FnhLlQtO9AIzDO9gWHZcTNN3U145kAEXLQhydpe Xu3BVDDXyYuXztTRIz4hWT25LBfp0Yk= 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-534-d8NSjxbhOZ-VF9qi2XuBqQ-1; Thu, 22 Jan 2026 11:20:36 -0500 X-MC-Unique: d8NSjxbhOZ-VF9qi2XuBqQ-1 X-Mimecast-MFC-AGG-ID: d8NSjxbhOZ-VF9qi2XuBqQ_1769098835 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 46CA81955D84; Thu, 22 Jan 2026 16:20:35 +0000 (UTC) Received: from mzamazal-thinkpadp1gen7.tpbc.com (unknown [10.45.224.97]) by mx-prod-int-08.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id 7EB1F1801AC9; Thu, 22 Jan 2026 16:20:33 +0000 (UTC) From: Milan Zamazal To: libcamera-devel@lists.libcamera.org Cc: Milan Zamazal , Kieran Bingham , =?utf-8?q?Barnab=C3=A1s_P=C5=91cze?= Subject: [PATCH v4 12/15] libcamera: ipa: simple: Use symbolic constants for adjust defaults Date: Thu, 22 Jan 2026 17:19:32 +0100 Message-ID: <20260122161935.208562-13-mzamazal@redhat.com> In-Reply-To: <20260122161935.208562-1-mzamazal@redhat.com> References: <20260122161935.208562-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: EAqtZ_EJPxZTaHNS3sc0hV22wckpRS-XKzjNr2BZ7YQ_1769098835 X-Mimecast-Originator: redhat.com X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" The adjust algorithm already uses a symbolic constant for gamma. Let's introduce similar constants for contrast and saturation to prevent copying the numeric defaults to multiple places. Reviewed-by: Barnabás Pőcze Signed-off-by: Milan Zamazal --- src/ipa/simple/algorithms/adjust.cpp | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/src/ipa/simple/algorithms/adjust.cpp b/src/ipa/simple/algorithms/adjust.cpp index a8b7c5ab6..23f6f6ad7 100644 --- a/src/ipa/simple/algorithms/adjust.cpp +++ b/src/ipa/simple/algorithms/adjust.cpp @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ /* * Copyright (C) 2024, Ideas On Board - * Copyright (C) 2024-2025, Red Hat Inc. + * Copyright (C) 2024-2026, Red Hat Inc. * * Common image adjustments */ @@ -19,14 +19,20 @@ namespace libcamera { namespace ipa::soft::algorithms { +constexpr float kDefaultContrast = 1.0f; +constexpr float kDefaultSaturation = 1.0f; + LOG_DEFINE_CATEGORY(IPASoftAdjust) int Adjust::init(IPAContext &context, [[maybe_unused]] const YamlObject &tuningData) { - context.ctrlMap[&controls::Gamma] = ControlInfo(0.1f, 10.0f, kDefaultGamma); - context.ctrlMap[&controls::Contrast] = ControlInfo(0.0f, 2.0f, 1.0f); + context.ctrlMap[&controls::Gamma] = + ControlInfo(0.1f, 10.0f, kDefaultGamma); + context.ctrlMap[&controls::Contrast] = + ControlInfo(0.0f, 2.0f, kDefaultContrast); if (context.ccmEnabled) - context.ctrlMap[&controls::Saturation] = ControlInfo(0.0f, 2.0f, 1.0f); + context.ctrlMap[&controls::Saturation] = + ControlInfo(0.0f, 2.0f, kDefaultSaturation); return 0; } @@ -122,7 +128,7 @@ void Adjust::process([[maybe_unused]] IPAContext &context, metadata.set(controls::Contrast, contrast.value()); const auto &saturation = frameContext.saturation; - metadata.set(controls::Saturation, saturation.value_or(1.0)); + metadata.set(controls::Saturation, saturation.value_or(kDefaultSaturation)); } REGISTER_IPA_ALGORITHM(Adjust, "Adjust") From patchwork Thu Jan 22 16:19:33 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Milan Zamazal X-Patchwork-Id: 25939 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 57561C32E7 for ; Thu, 22 Jan 2026 16:20:45 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id E459661FD4; Thu, 22 Jan 2026 17:20:44 +0100 (CET) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=redhat.com header.i=@redhat.com header.b="OuzN7tWq"; 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 C9B5C61FD4 for ; Thu, 22 Jan 2026 17:20:40 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1769098839; 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=TUhFj8SRsM46CjnXWoH94NdCUQQqhOFuMXl9BYm8uKQ=; b=OuzN7tWqjIPXlMvTPsWdJFlqPvGE+cWSXRaYeMFLU+hXTvFDQgiYr4rWJvzccSJjhr5DX6 BG2FO4koiITbitAC1cTocjAtFCg36nctmPMpLnflLQ21Rq6vcYghSTZtJdb6sf+N2dR5Ip sXx7cd1LBYzHKLVn453oDXhOas5TiEY= 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-592-gznZBotmPUq7EZA1Xx_wSA-1; Thu, 22 Jan 2026 11:20:38 -0500 X-MC-Unique: gznZBotmPUq7EZA1Xx_wSA-1 X-Mimecast-MFC-AGG-ID: gznZBotmPUq7EZA1Xx_wSA_1769098837 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 B3F8118005B9; Thu, 22 Jan 2026 16:20:37 +0000 (UTC) Received: from mzamazal-thinkpadp1gen7.tpbc.com (unknown [10.45.224.97]) by mx-prod-int-08.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id D5CC81800577; Thu, 22 Jan 2026 16:20:35 +0000 (UTC) From: Milan Zamazal To: libcamera-devel@lists.libcamera.org Cc: Milan Zamazal , Kieran Bingham , =?utf-8?q?Barnab=C3=A1s_P=C5=91cze?= Subject: [PATCH v4 13/15] libcamera: ipa: simple: Set contrast metadata unconditionally Date: Thu, 22 Jan 2026 17:19:33 +0100 Message-ID: <20260122161935.208562-14-mzamazal@redhat.com> In-Reply-To: <20260122161935.208562-1-mzamazal@redhat.com> References: <20260122161935.208562-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: nHANZ44jPcP5SJoIBdPrzuSRu-x3nbrXVH73bV_W-8k_1769098837 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 do the same with contrast as with other metadata. Signed-off-by: Milan Zamazal --- src/ipa/simple/algorithms/adjust.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/ipa/simple/algorithms/adjust.cpp b/src/ipa/simple/algorithms/adjust.cpp index 23f6f6ad7..acdd3f741 100644 --- a/src/ipa/simple/algorithms/adjust.cpp +++ b/src/ipa/simple/algorithms/adjust.cpp @@ -124,8 +124,7 @@ void Adjust::process([[maybe_unused]] IPAContext &context, metadata.set(controls::Gamma, gamma); const auto &contrast = frameContext.contrast; - if (contrast) - metadata.set(controls::Contrast, contrast.value()); + metadata.set(controls::Contrast, contrast.value_or(kDefaultContrast)); const auto &saturation = frameContext.saturation; metadata.set(controls::Saturation, saturation.value_or(kDefaultSaturation)); From patchwork Thu Jan 22 16:19:34 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Milan Zamazal X-Patchwork-Id: 25940 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 688F1C327D for ; Thu, 22 Jan 2026 16:20:47 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id F310461FE2; Thu, 22 Jan 2026 17:20:46 +0100 (CET) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=redhat.com header.i=@redhat.com header.b="drzCZRv/"; 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 24AC061FF3 for ; Thu, 22 Jan 2026 17:20:45 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1769098844; 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=hZzokgXHXY26J0etNVW2qQTo7buLW9MhTrhhlsxF6hg=; b=drzCZRv/zLbsPyTTpaoaan/3n1jpRXOZsxabywReYmUdSg+htFVuWnehhCQhuqbL5QCSnC PVH+QZHvFoFnMrdtXKe9O/4KpXH6NCtB+V15V6fvo4Vp7o1DhnNqIprdYqtIrQQliMU4FT psRu+pHrc0OWUPWnMNYbhXHiMDAamCs= 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-629-pndBP0ifM-qzUFNwiJESvA-1; Thu, 22 Jan 2026 11:20:42 -0500 X-MC-Unique: pndBP0ifM-qzUFNwiJESvA-1 X-Mimecast-MFC-AGG-ID: pndBP0ifM-qzUFNwiJESvA_1769098841 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 1E5EF1955DB7; Thu, 22 Jan 2026 16:20:41 +0000 (UTC) Received: from mzamazal-thinkpadp1gen7.tpbc.com (unknown [10.45.224.97]) by mx-prod-int-08.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id 513751800240; Thu, 22 Jan 2026 16:20:38 +0000 (UTC) From: Milan Zamazal To: libcamera-devel@lists.libcamera.org Cc: Milan Zamazal , Kieran Bingham , =?utf-8?q?Barnab=C3=A1s_P=C5=91cze?= Subject: [PATCH v4 14/15] libcamera: ipa: simple: Remove Lut algorithm Date: Thu, 22 Jan 2026 17:19:34 +0100 Message-ID: <20260122161935.208562-15-mzamazal@redhat.com> In-Reply-To: <20260122161935.208562-1-mzamazal@redhat.com> References: <20260122161935.208562-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: jtX9wVs4SAenA9JA23LHKcJdOgwjubCga-XNJgl-mcw_1769098841 X-Mimecast-Originator: redhat.com content-type: text/plain; charset="US-ASCII"; x-default=true X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" The Lut algorithm is not really an algorithm. Moreover, algorithms may be enabled or disabled but with Lut disabled, nothing will work. Let's move the construction of lookup tables to CPU debayering, where it is used. The implied and related changes are: - DebayerParams is changed to contain the real params rather than lookup tables. - contrastExp parameter introduced by GPU ISP is used for CPU ISP too. - The params must be initialised so that debayering gets meaningful parameter values even when some algorithms are disabled. - combinedMatrix must be put to params everywhere where it is modified. - Matrix changes needn't be tracked in the algorithms any more. - CPU debayering must watch for changes of the corresponding parameters to update the lookup tables when and only when needed. - Swapping red and blue is integrated into lookup table constructions. - gpuIspEnabled flags are removed as they are not needed any more. Signed-off-by: Milan Zamazal --- .../internal/software_isp/debayer_params.h | 43 +---- include/libcamera/ipa/soft.mojom | 3 +- src/ipa/simple/algorithms/adjust.cpp | 17 +- src/ipa/simple/algorithms/adjust.h | 4 - src/ipa/simple/algorithms/awb.cpp | 6 +- src/ipa/simple/algorithms/ccm.cpp | 1 - src/ipa/simple/algorithms/lut.cpp | 140 -------------- src/ipa/simple/algorithms/lut.h | 35 ---- src/ipa/simple/algorithms/meson.build | 1 - src/ipa/simple/data/uncalibrated.yaml | 1 - src/ipa/simple/ipa_context.h | 11 -- src/ipa/simple/soft_simple.cpp | 11 +- src/libcamera/software_isp/debayer.cpp | 172 +----------------- src/libcamera/software_isp/debayer.h | 8 - src/libcamera/software_isp/debayer_cpu.cpp | 136 ++++++++++++-- src/libcamera/software_isp/debayer_cpu.h | 26 ++- src/libcamera/software_isp/debayer_egl.cpp | 22 +-- src/libcamera/software_isp/software_isp.cpp | 29 +-- 18 files changed, 188 insertions(+), 478 deletions(-) delete mode 100644 src/ipa/simple/algorithms/lut.cpp delete mode 100644 src/ipa/simple/algorithms/lut.h diff --git a/include/libcamera/internal/software_isp/debayer_params.h b/include/libcamera/internal/software_isp/debayer_params.h index 2d69bd295..1c0412d75 100644 --- a/include/libcamera/internal/software_isp/debayer_params.h +++ b/include/libcamera/internal/software_isp/debayer_params.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ /* - * Copyright (C) 2023-2025 Red Hat Inc. + * Copyright (C) 2023-2026 Red Hat Inc. * * Authors: * Hans de Goede @@ -10,7 +10,6 @@ #pragma once -#include #include #include "libcamera/internal/matrix.h" @@ -19,47 +18,11 @@ namespace libcamera { struct DebayerParams { - static constexpr unsigned int kRGBLookupSize = 256; - - struct CcmColumn { - int16_t r; - int16_t g; - int16_t b; - }; - - using LookupTable = std::array; - using CcmLookupTable = std::array; - - /* - * Color lookup tables when CCM is not used. - * - * Each color of a debayered pixel is amended by the corresponding - * value in the given table. - */ - LookupTable red; - LookupTable green; - LookupTable blue; - - /* - * Color and gamma lookup tables when CCM is used. - * - * Each of the CcmLookupTable's corresponds to a CCM column; together they - * make a complete 3x3 CCM lookup table. The CCM is applied on debayered - * pixels and then the gamma lookup table is used to set the resulting - * values of all the three colors. - */ - CcmLookupTable redCcm; - CcmLookupTable greenCcm; - CcmLookupTable blueCcm; - LookupTable gammaLut; - - /* - * Per frame corrections as calculated by the IPA - */ - Matrix ccm; + Matrix combinedMatrix; RGB blackLevel; float gamma; float contrastExp; + RGB gains; }; } /* namespace libcamera */ diff --git a/include/libcamera/ipa/soft.mojom b/include/libcamera/ipa/soft.mojom index aff8fcbd3..77328c5fd 100644 --- a/include/libcamera/ipa/soft.mojom +++ b/include/libcamera/ipa/soft.mojom @@ -17,8 +17,7 @@ interface IPASoftInterface { libcamera.SharedFD fdStats, libcamera.SharedFD fdParams, libcamera.IPACameraSensorInfo sensorInfo, - libcamera.ControlInfoMap sensorControls, - bool gpuIspEnabled) + libcamera.ControlInfoMap sensorControls) => (int32 ret, libcamera.ControlInfoMap ipaControls, bool ccmEnabled); start() => (int32 ret); stop(); diff --git a/src/ipa/simple/algorithms/adjust.cpp b/src/ipa/simple/algorithms/adjust.cpp index acdd3f741..068e98404 100644 --- a/src/ipa/simple/algorithms/adjust.cpp +++ b/src/ipa/simple/algorithms/adjust.cpp @@ -95,23 +95,20 @@ void Adjust::applySaturation(Matrix &matrix, float saturation) void Adjust::prepare(IPAContext &context, [[maybe_unused]] const uint32_t frame, IPAFrameContext &frameContext, - [[maybe_unused]] DebayerParams *params) + DebayerParams *params) { frameContext.gamma = context.activeState.knobs.gamma; frameContext.contrast = context.activeState.knobs.contrast; - if (!context.ccmEnabled) - return; - auto &saturation = context.activeState.knobs.saturation; - frameContext.saturation = saturation; - if (saturation) + if (context.ccmEnabled && saturation) { applySaturation(context.activeState.combinedMatrix, saturation.value()); - - if (saturation != lastSaturation_) { - context.activeState.matrixChanged = true; - lastSaturation_ = saturation; + frameContext.saturation = saturation; } + + params->gamma = 1.0 / context.activeState.knobs.gamma; + const float contrast = context.activeState.knobs.contrast.value_or(kDefaultContrast); + params->contrastExp = tan(std::clamp(contrast * M_PI_4, 0.0, M_PI_2 - 0.00001)); } void Adjust::process([[maybe_unused]] IPAContext &context, diff --git a/src/ipa/simple/algorithms/adjust.h b/src/ipa/simple/algorithms/adjust.h index 7644138ff..fb133b140 100644 --- a/src/ipa/simple/algorithms/adjust.h +++ b/src/ipa/simple/algorithms/adjust.h @@ -7,8 +7,6 @@ #pragma once -#include - #include "libcamera/internal/matrix.h" #include @@ -45,8 +43,6 @@ public: private: void applySaturation(Matrix &ccm, float saturation); - - std::optional lastSaturation_; }; } /* namespace ipa::soft::algorithms */ diff --git a/src/ipa/simple/algorithms/awb.cpp b/src/ipa/simple/algorithms/awb.cpp index 4d2f1df15..d2c1b87f0 100644 --- a/src/ipa/simple/algorithms/awb.cpp +++ b/src/ipa/simple/algorithms/awb.cpp @@ -37,7 +37,7 @@ int Awb::configure(IPAContext &context, void Awb::prepare(IPAContext &context, [[maybe_unused]] const uint32_t frame, IPAFrameContext &frameContext, - [[maybe_unused]] DebayerParams *params) + DebayerParams *params) { auto &gains = context.activeState.awb.gains; Matrix gainMatrix = { { gains.r(), 0, 0, @@ -45,9 +45,11 @@ void Awb::prepare(IPAContext &context, 0, 0, gains.b() } }; context.activeState.combinedMatrix = context.activeState.combinedMatrix * gainMatrix; - /* Just report, the gains are applied in LUT algorithm. */ + frameContext.gains.red = gains.r(); frameContext.gains.blue = gains.b(); + + params->gains = gains; } void Awb::process(IPAContext &context, diff --git a/src/ipa/simple/algorithms/ccm.cpp b/src/ipa/simple/algorithms/ccm.cpp index 5576a301f..911a5af2c 100644 --- a/src/ipa/simple/algorithms/ccm.cpp +++ b/src/ipa/simple/algorithms/ccm.cpp @@ -51,7 +51,6 @@ void Ccm::prepare(IPAContext &context, [[maybe_unused]] const uint32_t frame, utils::abs_diff(ct, lastCt_) >= kTemperatureThreshold) { currentCcm_ = ccm_.getInterpolated(ct); lastCt_ = ct; - context.activeState.matrixChanged = true; } context.activeState.combinedMatrix = diff --git a/src/ipa/simple/algorithms/lut.cpp b/src/ipa/simple/algorithms/lut.cpp deleted file mode 100644 index fd442259a..000000000 --- a/src/ipa/simple/algorithms/lut.cpp +++ /dev/null @@ -1,140 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ -/* - * Copyright (C) 2024-2026, Red Hat Inc. - * - * Color lookup tables construction - */ - -#include "lut.h" - -#include -#include -#include -#include - -#include - -#include - -#include "simple/ipa_context.h" - -#include "adjust.h" - -namespace libcamera { - -LOG_DEFINE_CATEGORY(IPASoftLut) - -namespace ipa::soft::algorithms { - -int Lut::configure(IPAContext &context, - [[maybe_unused]] const IPAConfigInfo &configInfo) -{ - updateGammaTable(context); - - return 0; -} - -void Lut::updateGammaTable(IPAContext &context) -{ - const auto blackLevel = context.activeState.blc.level; - const auto gamma = 1.0 / context.activeState.knobs.gamma; - const auto contrast = context.activeState.knobs.contrast.value_or(1.0); - /* Convert 0..2 to 0..infinity; avoid actual inifinity at tan(pi/2) */ - float contrastExp = tan(std::clamp(contrast * M_PI_4, 0.0, M_PI_2 - 0.00001)); - - if (!context.gpuIspEnabled) { - auto &gammaTable = context.activeState.gamma.gammaTable; - const unsigned int blackIndex = blackLevel * gammaTable.size() / 256; - const float divisor = gammaTable.size() - blackIndex - 1.0; - for (unsigned int i = blackIndex; i < gammaTable.size(); i++) { - double normalized = (i - blackIndex) / divisor; - /* Apply simple S-curve */ - if (normalized < 0.5) - normalized = 0.5 * std::pow(normalized / 0.5, contrastExp); - else - normalized = 1.0 - 0.5 * std::pow((1.0 - normalized) / 0.5, contrastExp); - gammaTable[i] = UINT8_MAX * std::pow(normalized, gamma); - } - /* - * Due to CCM operations, the table lookup may reach indices below the black - * level. Let's set the table values below black level to the minimum - * non-black value to prevent problems when the minimum value is - * significantly non-zero (for example, when the image should be all grey). - */ - std::fill(gammaTable.begin(), gammaTable.begin() + blackIndex, - gammaTable[blackIndex]); - } - - context.activeState.gamma.gamma = gamma; - context.activeState.gamma.blackLevel = blackLevel; - context.activeState.gamma.contrastExp = contrastExp; -} - -int16_t Lut::matrixValue(unsigned int i, float ccm) const -{ - return std::round(i * ccm); -} - -void Lut::prepare(IPAContext &context, - [[maybe_unused]] const uint32_t frame, - [[maybe_unused]] IPAFrameContext &frameContext, - DebayerParams *params) -{ - /* - * Update the gamma table if needed. This means if black level changes - * and since the black level gets updated only if a lower value is - * observed, it's not permanently prone to minor fluctuations or - * rounding errors. - */ - const bool gammaUpdateNeeded = - context.activeState.gamma.blackLevel != context.activeState.blc.level || - context.activeState.gamma.contrast != context.activeState.knobs.contrast; - if (gammaUpdateNeeded) - updateGammaTable(context); - - auto &gains = context.activeState.awb.gains; - auto &gammaTable = context.activeState.gamma.gammaTable; - const unsigned int gammaTableSize = gammaTable.size(); - const double div = static_cast(DebayerParams::kRGBLookupSize) / - gammaTableSize; - - if (!context.ccmEnabled) { - for (unsigned int i = 0; i < DebayerParams::kRGBLookupSize; i++) { - /* Apply gamma after gain! */ - const RGB lutGains = (gains * i / div).min(gammaTableSize - 1); - params->red[i] = gammaTable[static_cast(lutGains.r())]; - params->green[i] = gammaTable[static_cast(lutGains.g())]; - params->blue[i] = gammaTable[static_cast(lutGains.b())]; - } - } else if (context.activeState.matrixChanged || gammaUpdateNeeded) { - auto &matrix = context.activeState.combinedMatrix; - auto &red = params->redCcm; - auto &green = params->greenCcm; - auto &blue = params->blueCcm; - params->ccm = matrix; - if (!context.gpuIspEnabled) { - for (unsigned int i = 0; i < DebayerParams::kRGBLookupSize; i++) { - red[i].r = matrixValue(i, matrix[0][0]); - red[i].g = matrixValue(i, matrix[1][0]); - red[i].b = matrixValue(i, matrix[2][0]); - green[i].r = matrixValue(i, matrix[0][1]); - green[i].g = matrixValue(i, matrix[1][1]); - green[i].b = matrixValue(i, matrix[2][1]); - blue[i].r = matrixValue(i, matrix[0][2]); - blue[i].g = matrixValue(i, matrix[1][2]); - blue[i].b = matrixValue(i, matrix[2][2]); - params->gammaLut[i] = gammaTable[i / div]; - } - } - context.activeState.matrixChanged = false; - } - - params->gamma = context.activeState.gamma.gamma; - params->contrastExp = context.activeState.gamma.contrastExp; -} - -REGISTER_IPA_ALGORITHM(Lut, "Lut") - -} /* namespace ipa::soft::algorithms */ - -} /* namespace libcamera */ diff --git a/src/ipa/simple/algorithms/lut.h b/src/ipa/simple/algorithms/lut.h deleted file mode 100644 index ad16d1e8e..000000000 --- a/src/ipa/simple/algorithms/lut.h +++ /dev/null @@ -1,35 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ -/* - * Copyright (C) 2024, Red Hat Inc. - * - * Color lookup tables construction - */ - -#pragma once - -#include "algorithm.h" - -namespace libcamera { - -namespace ipa::soft::algorithms { - -class Lut : public Algorithm -{ -public: - Lut() = default; - ~Lut() = default; - - int configure(IPAContext &context, const IPAConfigInfo &configInfo) override; - void prepare(IPAContext &context, - const uint32_t frame, - IPAFrameContext &frameContext, - DebayerParams *params) override; - -private: - void updateGammaTable(IPAContext &context); - int16_t matrixValue(unsigned int i, float ccm) const; -}; - -} /* namespace ipa::soft::algorithms */ - -} /* namespace libcamera */ diff --git a/src/ipa/simple/algorithms/meson.build b/src/ipa/simple/algorithms/meson.build index ebe9f20dd..73c637220 100644 --- a/src/ipa/simple/algorithms/meson.build +++ b/src/ipa/simple/algorithms/meson.build @@ -6,5 +6,4 @@ soft_simple_ipa_algorithms = files([ 'agc.cpp', 'blc.cpp', 'ccm.cpp', - 'lut.cpp', ]) diff --git a/src/ipa/simple/data/uncalibrated.yaml b/src/ipa/simple/data/uncalibrated.yaml index e389e0588..c6feda36d 100644 --- a/src/ipa/simple/data/uncalibrated.yaml +++ b/src/ipa/simple/data/uncalibrated.yaml @@ -15,6 +15,5 @@ algorithms: 0, 1, 0, 0, 0, 1] - Adjust: - - Lut: - Agc: ... diff --git a/src/ipa/simple/ipa_context.h b/src/ipa/simple/ipa_context.h index 293e35b71..34f7403a4 100644 --- a/src/ipa/simple/ipa_context.h +++ b/src/ipa/simple/ipa_context.h @@ -53,17 +53,7 @@ struct IPAActiveState { unsigned int temperatureK; } awb; - static constexpr unsigned int kGammaLookupSize = 1024; - struct { - std::array gammaTable; - uint8_t blackLevel; - float gamma; - float contrast; - float contrastExp; - } gamma; - Matrix combinedMatrix; - bool matrixChanged = false; struct { float gamma; @@ -103,7 +93,6 @@ struct IPAContext { FCQueue frameContexts; ControlInfoMap::Map ctrlMap; bool ccmEnabled = false; - bool gpuIspEnabled = false; }; } /* namespace ipa::soft */ diff --git a/src/ipa/simple/soft_simple.cpp b/src/ipa/simple/soft_simple.cpp index 732e82510..6bef597c8 100644 --- a/src/ipa/simple/soft_simple.cpp +++ b/src/ipa/simple/soft_simple.cpp @@ -26,6 +26,7 @@ #include "libcamera/internal/software_isp/swisp_stats.h" #include "libcamera/internal/yaml_parser.h" +#include "algorithms/adjust.h" #include "libipa/camera_sensor_helper.h" #include "module.h" @@ -55,7 +56,6 @@ public: const SharedFD &fdParams, const IPACameraSensorInfo &sensorInfo, const ControlInfoMap &sensorControls, - bool gpuIspEnabled, ControlInfoMap *ipaControls, bool *ccmEnabled) override; int configure(const IPAConfigInfo &configInfo) override; @@ -96,7 +96,6 @@ int IPASoftSimple::init(const IPASettings &settings, const SharedFD &fdParams, const IPACameraSensorInfo &sensorInfo, const ControlInfoMap &sensorControls, - bool gpuIspEnabled, ControlInfoMap *ipaControls, bool *ccmEnabled) { @@ -108,7 +107,6 @@ int IPASoftSimple::init(const IPASettings &settings, } context_.sensorInfo = sensorInfo; - context_.gpuIspEnabled = gpuIspEnabled; /* Load the tuning data file */ File file(settings.configurationFile); @@ -161,6 +159,11 @@ int IPASoftSimple::init(const IPASettings &settings, } params_ = static_cast(mem); + params_->blackLevel = { { 0.0, 0.0, 0.0 } }; + params_->gamma = 1.0 / algorithms::kDefaultGamma; + params_->contrastExp = 1.0; + params_->gains = { { 1.0, 1.0, 1.0 } }; + /* combinedMatrix is reset for each frame. */ } { @@ -287,6 +290,8 @@ void IPASoftSimple::computeParams(const uint32_t frame) IPAFrameContext &frameContext = context_.frameContexts.get(frame); for (auto const &algo : algorithms()) algo->prepare(context_, frame, frameContext, params_); + params_->combinedMatrix = context_.activeState.combinedMatrix; + setIspParams.emit(); } diff --git a/src/libcamera/software_isp/debayer.cpp b/src/libcamera/software_isp/debayer.cpp index 65a1762dd..dccdd86b4 100644 --- a/src/libcamera/software_isp/debayer.cpp +++ b/src/libcamera/software_isp/debayer.cpp @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ /* * Copyright (C) 2023, Linaro Ltd - * Copyright (C) 2023-2025 Red Hat Inc. + * Copyright (C) 2023-2026 Red Hat Inc. * * Authors: * Hans de Goede @@ -25,99 +25,28 @@ namespace libcamera { */ /** - * \var DebayerParams::kRGBLookupSize - * \brief Size of a color lookup table + * \var DebayerParams::gains + * \brief Colour channel gains */ /** - * \struct DebayerParams::CcmColumn - * \brief Type of a single column of a color correction matrix (CCM) - * - * When multiplying an input pixel, columns in the CCM correspond to the red, - * green or blue component of input pixel values, while rows correspond to the - * red, green or blue components of the output pixel values. The members of the - * CcmColumn structure are named after the colour components of the output pixel - * values they correspond to. - */ - -/** - * \var DebayerParams::CcmColumn::r - * \brief Red (first) component of a CCM column - */ - -/** - * \var DebayerParams::CcmColumn::g - * \brief Green (second) component of a CCM column - */ - -/** - * \var DebayerParams::CcmColumn::b - * \brief Blue (third) component of a CCM column - */ - -/** - * \typedef DebayerParams::LookupTable - * \brief Type of the lookup tables for single lookup values - */ - -/** - * \typedef DebayerParams::CcmLookupTable - * \brief Type of the CCM lookup tables for red, green, blue values - */ - -/** - * \var DebayerParams::red - * \brief Lookup table for red color, mapping input values to output values - */ - -/** - * \var DebayerParams::green - * \brief Lookup table for green color, mapping input values to output values - */ - -/** - * \var DebayerParams::blue - * \brief Lookup table for blue color, mapping input values to output values - */ - -/** - * \var DebayerParams::redCcm - * \brief Lookup table for the CCM red column, mapping input values to output values - */ - -/** - * \var DebayerParams::greenCcm - * \brief Lookup table for the CCM green column, mapping input values to output values - */ - -/** - * \var DebayerParams::blueCcm - * \brief Lookup table for the CCM blue column, mapping input values to output values - */ - -/** - * \var DebayerParams::gammaLut - * \brief Gamma lookup table used with color correction matrix - */ - -/** - * \var DebayerParams::ccm - * \brief Per frame colour correction matrix for GPUISP + * \var DebayerParams::combinedMatrix + * \brief Colour correction matrix, including other adjustments */ /** * \var DebayerParams::blackLevel - * \brief Blacklevel gains for the GPUISP + * \brief Black level values */ /** * \var DebayerParams::gamma - * \brief Gamma value for the GPUISP + * \brief Gamma value, e.g. 1/2.2 */ /** * \var DebayerParams::contrastExp - * \brief Contrast value for GPUISP + * \brief Contrast value to be used as an exponent */ /** @@ -131,13 +60,6 @@ LOG_DEFINE_CATEGORY(Debayer) Debayer::Debayer(const GlobalConfiguration &configuration) : bench_(configuration) { - /* Initialize color lookup tables */ - for (unsigned int i = 0; i < DebayerParams::kRGBLookupSize; i++) { - red_[i] = green_[i] = blue_[i] = i; - redCcm_[i] = { static_cast(i), 0, 0 }; - greenCcm_[i] = { 0, static_cast(i), 0 }; - blueCcm_[i] = { 0, 0, static_cast(i) }; - } } Debayer::~Debayer() @@ -305,56 +227,6 @@ Debayer::~Debayer() * \brief Output size object */ -/** - * \var Debayer::red_ - * \brief Lookup table for red channel gain and correction values - * - * This table provides precomputed per-pixel or per-intensity - * correction values for the red color channel used during debayering. - */ - -/** - * \var Debayer::green_ - * \brief Lookup table for green channel gain and correction values - * - * This table provides precomputed per-pixel or per-intensity - * correction values for the green color channel used during debayering. - */ - -/** - * \var Debayer::blue_ - * \brief Lookup table for blue channel gain and correction values - * - * This table provides precomputed per-pixel or per-intensity - * correction values for the blue color channel used during debayering. - */ - -/** - * \var Debayer::redCcm_ - * \brief Red channel Color Correction Matrix (CCM) lookup table - * - * Contains coefficients for green channel color correction. - */ - -/** - * \var Debayer::greenCcm_ - * \brief Green channel Color Correction Matrix (CCM) lookup table - * - * Contains coefficients for green channel color correction. - */ - -/** - * \var Debayer::blueCcm_ - * \brief Blue channel Color Correction Matrix (CCM) lookup table - * - * Contains coefficients for blue channel color correction. - */ - -/** - * \var Debayer::gammaLut_ - * \brief Gamma correction lookup table - */ - /** * \var Debayer::swapRedBlueGains_ * \brief Flag indicating whether red and blue channel gains should be swapped @@ -396,34 +268,6 @@ Debayer::~Debayer() * DebayerEGL::start. */ -/** - * \fn void Debayer::setParams(DebayerParams ¶ms) - * \brief Select the bayer params to use for the next frame debayer - * \param[in] params The parameters to be used in debayering - */ -void Debayer::setParams(DebayerParams ¶ms) -{ - green_ = params.green; - greenCcm_ = params.greenCcm; - if (swapRedBlueGains_) { - red_ = params.blue; - blue_ = params.red; - redCcm_ = params.blueCcm; - blueCcm_ = params.redCcm; - for (unsigned int i = 0; i < 256; i++) { - std::swap(redCcm_[i].r, redCcm_[i].b); - std::swap(greenCcm_[i].r, greenCcm_[i].b); - std::swap(blueCcm_[i].r, blueCcm_[i].b); - } - } else { - red_ = params.red; - blue_ = params.blue; - redCcm_ = params.redCcm; - blueCcm_ = params.blueCcm; - } - gammaLut_ = params.gammaLut; -} - /** * \fn void Debayer::dmaSyncBegin(DebayerParams ¶ms) * \brief Common CPU/GPU Dma Sync Buffer begin diff --git a/src/libcamera/software_isp/debayer.h b/src/libcamera/software_isp/debayer.h index cd2db9930..652cff4cc 100644 --- a/src/libcamera/software_isp/debayer.h +++ b/src/libcamera/software_isp/debayer.h @@ -78,13 +78,6 @@ public: Size outputSize_; PixelFormat inputPixelFormat_; PixelFormat outputPixelFormat_; - DebayerParams::LookupTable red_; - DebayerParams::LookupTable green_; - DebayerParams::LookupTable blue_; - DebayerParams::CcmLookupTable redCcm_; - DebayerParams::CcmLookupTable greenCcm_; - DebayerParams::CcmLookupTable blueCcm_; - DebayerParams::LookupTable gammaLut_; bool swapRedBlueGains_; Benchmark bench_; @@ -92,7 +85,6 @@ private: virtual Size patternSize(PixelFormat inputFormat) = 0; protected: - void setParams(DebayerParams ¶ms); void dmaSyncBegin(std::vector &dmaSyncers, FrameBuffer *input, FrameBuffer *output); static bool isStandardBayerOrder(BayerFormat::Order order); }; diff --git a/src/libcamera/software_isp/debayer_cpu.cpp b/src/libcamera/software_isp/debayer_cpu.cpp index 00738c56b..af7af0a8d 100644 --- a/src/libcamera/software_isp/debayer_cpu.cpp +++ b/src/libcamera/software_isp/debayer_cpu.cpp @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ /* * Copyright (C) 2023, Linaro Ltd - * Copyright (C) 2023-2025 Red Hat Inc. + * Copyright (C) 2023-2026 Red Hat Inc. * * Authors: * Hans de Goede @@ -68,21 +68,21 @@ DebayerCpu::~DebayerCpu() = default; #define GAMMA(value) \ *dst++ = gammaLut_[std::clamp(value, 0, static_cast(gammaLut_.size()) - 1)] -#define STORE_PIXEL(b_, g_, r_) \ - if constexpr (ccmEnabled) { \ - const DebayerParams::CcmColumn &blue = blueCcm_[b_]; \ - const DebayerParams::CcmColumn &green = greenCcm_[g_]; \ - const DebayerParams::CcmColumn &red = redCcm_[r_]; \ - GAMMA(blue.b + green.b + red.b); \ - GAMMA(blue.g + green.g + red.g); \ - GAMMA(blue.r + green.r + red.r); \ - } else { \ - *dst++ = blue_[b_]; \ - *dst++ = green_[g_]; \ - *dst++ = red_[r_]; \ - } \ - if constexpr (addAlphaByte) \ - *dst++ = 255; \ +#define STORE_PIXEL(b_, g_, r_) \ + if constexpr (ccmEnabled) { \ + const CcmColumn &blue = blueCcm_[b_]; \ + const CcmColumn &green = greenCcm_[g_]; \ + const CcmColumn &red = redCcm_[r_]; \ + GAMMA(blue.b + green.b + red.b); \ + GAMMA(blue.g + green.g + red.g); \ + GAMMA(blue.r + green.r + red.r); \ + } else { \ + *dst++ = blue_[b_]; \ + *dst++ = green_[g_]; \ + *dst++ = red_[r_]; \ + } \ + if constexpr (addAlphaByte) \ + *dst++ = 255; \ x++; /* @@ -525,6 +525,16 @@ int DebayerCpu::configure(const StreamConfiguration &inputCfg, if (ret != 0) return -EINVAL; + ccmEnabled_ = ccmEnabled; + + /* + * Lookup tables must be initialized because the initial value is used for + * the first two frames, i.e. until stats processing starts providing its + * own parameters. Let's enforce recomputing lookup tables by setting the + * stored last used gamma to an out-of-range value. + */ + params_.gamma = 1.0; + window_.x = ((inputCfg.size.width - outputCfg.size.width) / 2) & ~(inputConfig_.patternSize.width - 1); window_.y = ((inputCfg.size.height - outputCfg.size.height) / 2) & @@ -740,6 +750,98 @@ void DebayerCpu::process4(uint32_t frame, const uint8_t *src, uint8_t *dst) } } +void DebayerCpu::updateGammaTable(DebayerParams ¶ms) +{ + const RGB blackLevel = params.blackLevel; + /* Take let's say the green channel black level */ + const unsigned int blackIndex = blackLevel[1] * gammaTable_.size(); + const float gamma = params.gamma; + const float contrastExp = params.contrastExp; + + const float divisor = gammaTable_.size() - blackIndex - 1.0; + for (unsigned int i = blackIndex; i < gammaTable_.size(); i++) { + float normalized = (i - blackIndex) / divisor; + /* Convert 0..2 to 0..infinity; avoid actual inifinity at tan(pi/2) */ + /* Apply simple S-curve */ + if (normalized < 0.5) + normalized = 0.5 * std::pow(normalized / 0.5, contrastExp); + else + normalized = 1.0 - 0.5 * std::pow((1.0 - normalized) / 0.5, contrastExp); + gammaTable_[i] = UINT8_MAX * + std::pow(normalized, gamma); + } + /* + * Due to CCM operations, the table lookup may reach indices below the black + * level. Let's set the table values below black level to the minimum + * non-black value to prevent problems when the minimum value is + * significantly non-zero (for example, when the image should be all grey). + */ + std::fill(gammaTable_.begin(), gammaTable_.begin() + blackIndex, + gammaTable_[blackIndex]); +} + +void DebayerCpu::updateLookupTables(DebayerParams ¶ms) +{ + const bool gammaUpdateNeeded = + params.gamma != params_.gamma || + params.blackLevel != params_.blackLevel || + params.contrastExp != params_.contrastExp; + if (gammaUpdateNeeded) + updateGammaTable(params); + + auto matrixChanged = [](const Matrix &m1, const Matrix &m2) -> bool { + return !std::equal(m1.data().begin(), m1.data().end(), m2.data().begin()); + }; + const unsigned int gammaTableSize = gammaTable_.size(); + const double div = static_cast(kRGBLookupSize) / gammaTableSize; + if (ccmEnabled_) { + if (gammaUpdateNeeded || + matrixChanged(params.combinedMatrix, params_.combinedMatrix)) { + auto &red = swapRedBlueGains_ ? blueCcm_ : redCcm_; + auto &green = greenCcm_; + auto &blue = swapRedBlueGains_ ? redCcm_ : blueCcm_; + const unsigned int redIndex = swapRedBlueGains_ ? 2 : 0; + const unsigned int greenIndex = 1; + const unsigned int blueIndex = swapRedBlueGains_ ? 0 : 2; + for (unsigned int i = 0; i < kRGBLookupSize; i++) { + red[i].r = std::round(i * params.combinedMatrix[redIndex][0]); + red[i].g = std::round(i * params.combinedMatrix[greenIndex][0]); + red[i].b = std::round(i * params.combinedMatrix[blueIndex][0]); + green[i].r = std::round(i * params.combinedMatrix[redIndex][1]); + green[i].g = std::round(i * params.combinedMatrix[greenIndex][1]); + green[i].b = std::round(i * params.combinedMatrix[blueIndex][1]); + blue[i].r = std::round(i * params.combinedMatrix[redIndex][2]); + blue[i].g = std::round(i * params.combinedMatrix[greenIndex][2]); + blue[i].b = std::round(i * params.combinedMatrix[blueIndex][2]); + gammaLut_[i] = gammaTable_[i / div]; + } + } + } else { + if (gammaUpdateNeeded || params.gains != params_.gains) { + auto &gains = params.gains; + auto &red = swapRedBlueGains_ ? blue_ : red_; + auto &green = green_; + auto &blue = swapRedBlueGains_ ? red_ : blue_; + for (unsigned int i = 0; i < kRGBLookupSize; i++) { + /* Apply gamma after gain! */ + const RGB lutGains = (gains * i / div).min(gammaTableSize - 1); + red[i] = gammaTable_[static_cast(lutGains.r())]; + green[i] = gammaTable_[static_cast(lutGains.g())]; + blue[i] = gammaTable_[static_cast(lutGains.b())]; + } + } + } + + LOG(Debayer, Debug) + << "Debayer parameters: blackLevel=" << params.blackLevel + << "; gamma=" << params.gamma + << "; contrastExp=" << params.contrastExp + << "; gains=" << params.gains + << "; matrix=" << params.combinedMatrix; + + params_ = params; +} + void DebayerCpu::process(uint32_t frame, FrameBuffer *input, FrameBuffer *output, DebayerParams params) { bench_.startFrame(); @@ -748,7 +850,7 @@ void DebayerCpu::process(uint32_t frame, FrameBuffer *input, FrameBuffer *output dmaSyncBegin(dmaSyncers, input, output); - setParams(params); + updateLookupTables(params); /* Copy metadata from the input buffer */ FrameMetadata &metadata = output->_d()->metadata(); diff --git a/src/libcamera/software_isp/debayer_cpu.h b/src/libcamera/software_isp/debayer_cpu.h index 67df2b93a..b5cbb5bd2 100644 --- a/src/libcamera/software_isp/debayer_cpu.h +++ b/src/libcamera/software_isp/debayer_cpu.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ /* * Copyright (C) 2023, Linaro Ltd - * Copyright (C) 2023-2025 Red Hat Inc. + * Copyright (C) 2023-2026 Red Hat Inc. * * Authors: * Hans de Goede @@ -18,6 +18,8 @@ #include #include "libcamera/internal/bayer_format.h" +#include "libcamera/internal/global_configuration.h" +#include "libcamera/internal/software_isp/debayer_params.h" #include "libcamera/internal/software_isp/swstats_cpu.h" #include "debayer.h" @@ -108,10 +110,32 @@ private: void memcpyNextLine(const uint8_t *linePointers[]); void process2(uint32_t frame, const uint8_t *src, uint8_t *dst); void process4(uint32_t frame, const uint8_t *src, uint8_t *dst); + void updateGammaTable(DebayerParams ¶ms); + void updateLookupTables(DebayerParams ¶ms); /* Max. supported Bayer pattern height is 4, debayering this requires 5 lines */ static constexpr unsigned int kMaxLineBuffers = 5; + static constexpr unsigned int kRGBLookupSize = 256; + static constexpr unsigned int kGammaLookupSize = 1024; + struct CcmColumn { + int16_t r; + int16_t g; + int16_t b; + }; + using LookupTable = std::array; + using CcmLookupTable = std::array; + LookupTable red_; + LookupTable green_; + LookupTable blue_; + CcmLookupTable redCcm_; + CcmLookupTable greenCcm_; + CcmLookupTable blueCcm_; + std::array gammaTable_; + LookupTable gammaLut_; + bool ccmEnabled_; + DebayerParams params_; + debayerFn debayer0_; debayerFn debayer1_; debayerFn debayer2_; diff --git a/src/libcamera/software_isp/debayer_egl.cpp b/src/libcamera/software_isp/debayer_egl.cpp index 9693d7252..af04d60ca 100644 --- a/src/libcamera/software_isp/debayer_egl.cpp +++ b/src/libcamera/software_isp/debayer_egl.cpp @@ -475,18 +475,18 @@ void DebayerEGL::setShaderVariableValues(DebayerParams ¶ms) << " textureUniformProjMatrix_ " << textureUniformProjMatrix_; GLfloat ccm[9] = { - params.ccm[0][0], - params.ccm[0][1], - params.ccm[0][2], - params.ccm[1][0], - params.ccm[1][1], - params.ccm[1][2], - params.ccm[2][0], - params.ccm[2][1], - params.ccm[2][2], + params.combinedMatrix[0][0], + params.combinedMatrix[0][1], + params.combinedMatrix[0][2], + params.combinedMatrix[1][0], + params.combinedMatrix[1][1], + params.combinedMatrix[1][2], + params.combinedMatrix[2][0], + params.combinedMatrix[2][1], + params.combinedMatrix[2][2], }; glUniformMatrix3fv(ccmUniformDataIn_, 1, GL_FALSE, ccm); - LOG(Debayer, Debug) << " ccmUniformDataIn_ " << ccmUniformDataIn_ << " data " << params.ccm; + LOG(Debayer, Debug) << " ccmUniformDataIn_ " << ccmUniformDataIn_ << " data " << params.combinedMatrix; /* * 0 = Red, 1 = Green, 2 = Blue @@ -544,8 +544,6 @@ void DebayerEGL::process(uint32_t frame, FrameBuffer *input, FrameBuffer *output dmaSyncBegin(dmaSyncers, input, nullptr); - setParams(params); - /* Copy metadata from the input buffer */ FrameMetadata &metadata = output->_d()->metadata(); metadata.status = input->metadata().status; diff --git a/src/libcamera/software_isp/software_isp.cpp b/src/libcamera/software_isp/software_isp.cpp index 7ad3511db..a83986b78 100644 --- a/src/libcamera/software_isp/software_isp.cpp +++ b/src/libcamera/software_isp/software_isp.cpp @@ -84,23 +84,6 @@ SoftwareIsp::SoftwareIsp(PipelineHandler *pipe, const CameraSensor *sensor, DmaBufAllocator::DmaBufAllocatorFlag::SystemHeap | DmaBufAllocator::DmaBufAllocatorFlag::UDmaBuf) { - /* - * debayerParams_ must be initialized because the initial value is used for - * the first two frames, i.e. until stats processing starts providing its - * own parameters. - * - * \todo This should be handled in the same place as the related - * operations, in the IPA module. - */ - std::array gammaTable; - for (unsigned int i = 0; i < 256; i++) - gammaTable[i] = UINT8_MAX * std::pow(i / 256.0, 0.5); - for (unsigned int i = 0; i < DebayerParams::kRGBLookupSize; i++) { - debayerParams_.red[i] = gammaTable[i]; - debayerParams_.green[i] = gammaTable[i]; - debayerParams_.blue[i] = gammaTable[i]; - } - if (!dmaHeap_.isValid()) { LOG(SoftwareIsp, Error) << "Failed to create DmaBufAllocator object"; return; @@ -121,8 +104,6 @@ SoftwareIsp::SoftwareIsp(PipelineHandler *pipe, const CameraSensor *sensor, } stats->statsReady.connect(this, &SoftwareIsp::statsReady); - bool gpuIspEnabled; - #if HAVE_DEBAYER_EGL std::optional softISPMode = configuration.envOption("LIBCAMERA_SOFTISP_MODE", { "software_isp", "mode" }); if (softISPMode) { @@ -133,15 +114,12 @@ SoftwareIsp::SoftwareIsp(PipelineHandler *pipe, const CameraSensor *sensor, } } - if (!softISPMode || softISPMode == "gpu") { + if (!softISPMode || softISPMode == "gpu") debayer_ = std::make_unique(std::move(stats), configuration); - gpuIspEnabled = true; - } + #endif - if (!debayer_) { + if (!debayer_) debayer_ = std::make_unique(std::move(stats), configuration); - gpuIspEnabled = false; - } debayer_->inputBufferReady.connect(this, &SoftwareIsp::inputReady); debayer_->outputBufferReady.connect(this, &SoftwareIsp::outputReady); @@ -173,7 +151,6 @@ SoftwareIsp::SoftwareIsp(PipelineHandler *pipe, const CameraSensor *sensor, sharedParams_.fd(), sensorInfo, sensor->controls(), - gpuIspEnabled, ipaControls, &ccmEnabled_); if (ret) { From patchwork Thu Jan 22 16:19:35 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Milan Zamazal X-Patchwork-Id: 25941 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 4CCA7BDCBF for ; Thu, 22 Jan 2026 16:20:49 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id DD49A61FE7; Thu, 22 Jan 2026 17:20:48 +0100 (CET) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=redhat.com header.i=@redhat.com header.b="H9Om2u+B"; 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 E360F61FD1 for ; Thu, 22 Jan 2026 17:20:47 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1769098846; 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=r0wQ6PJ2+koJySdZLiip9pAuwO/KpuEJkukxjpF88Ts=; b=H9Om2u+BIYeZwz/j8B2My/LymU8eeaWoMUtTy0fLTfuz6uXFBSRf3wHdlLqccgqu5w6QsY 6iMQMgXPnqEQ0SBt3MFAQ68H3J1MnyPUang2+DIBaY5KG7p8W7VXsDryGN1AFmKaGiFfUq 5WuteIFEdmR2lOn4pG3V198BTXibTz4= 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-692-Rb9mYsOpOh2lt808rg7dwQ-1; Thu, 22 Jan 2026 11:20:44 -0500 X-MC-Unique: Rb9mYsOpOh2lt808rg7dwQ-1 X-Mimecast-MFC-AGG-ID: Rb9mYsOpOh2lt808rg7dwQ_1769098843 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 73EA71944DFF; Thu, 22 Jan 2026 16:20:43 +0000 (UTC) Received: from mzamazal-thinkpadp1gen7.tpbc.com (unknown [10.45.224.97]) by mx-prod-int-08.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id 94D941800577; Thu, 22 Jan 2026 16:20:41 +0000 (UTC) From: Milan Zamazal To: libcamera-devel@lists.libcamera.org Cc: Milan Zamazal , Kieran Bingham , =?utf-8?q?Barnab=C3=A1s_P=C5=91cze?= Subject: [PATCH v4 15/15] libcamera: ipa: simple: Disable Ccm algorithm by default again Date: Thu, 22 Jan 2026 17:19:35 +0100 Message-ID: <20260122161935.208562-16-mzamazal@redhat.com> In-Reply-To: <20260122161935.208562-1-mzamazal@redhat.com> References: <20260122161935.208562-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: vcVKgYh85ahzxS3qQNCMprLeM9_m1brfZC5fDRcj6LQ_1769098843 X-Mimecast-Originator: redhat.com X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" The default CCM in uncalibrated.yaml is just an identity transformation and has been enabled by default only to always provide a correction matrix to GPU ISP. It slows down CPU ISP when CCM is not used. Now, when a default correction matrix is always provided to GPU ISP, we can disable the Ccm algorithm in uncalibrated.yaml again. The check for ccmEnabled in GPU ISP is no longer needed and it must be removed in order not to fail when Ccm algorithm is not enabled. ccmEnabled flag is still needed in CPU ISP where the processing differs based on whether CCM is present or not. Reviewed-by: Barnabás Pőcze Signed-off-by: Milan Zamazal --- src/ipa/simple/data/uncalibrated.yaml | 12 ++++++------ src/libcamera/software_isp/debayer_egl.cpp | 5 +---- 2 files changed, 7 insertions(+), 10 deletions(-) diff --git a/src/ipa/simple/data/uncalibrated.yaml b/src/ipa/simple/data/uncalibrated.yaml index c6feda36d..fc90ca526 100644 --- a/src/ipa/simple/data/uncalibrated.yaml +++ b/src/ipa/simple/data/uncalibrated.yaml @@ -8,12 +8,12 @@ algorithms: # Color correction matrices can be defined here. The CCM algorithm # has a significant performance impact, and should only be enabled # if tuned. - - Ccm: - ccms: - - ct: 6500 - ccm: [ 1, 0, 0, - 0, 1, 0, - 0, 0, 1] + # - Ccm: + # ccms: + # - ct: 6500 + # ccm: [ 1, 0, 0, + # 0, 1, 0, + # 0, 0, 1] - Adjust: - Agc: ... diff --git a/src/libcamera/software_isp/debayer_egl.cpp b/src/libcamera/software_isp/debayer_egl.cpp index af04d60ca..3ed9b93df 100644 --- a/src/libcamera/software_isp/debayer_egl.cpp +++ b/src/libcamera/software_isp/debayer_egl.cpp @@ -288,7 +288,7 @@ unsigned int DebayerEGL::frameSize() int DebayerEGL::configure(const StreamConfiguration &inputCfg, const std::vector> &outputCfgs, - bool ccmEnabled) + [[maybe_unused]] bool ccmEnabled) { if (getInputConfig(inputCfg.pixelFormat, inputConfig_) != 0) return -EINVAL; @@ -296,9 +296,6 @@ int DebayerEGL::configure(const StreamConfiguration &inputCfg, if (stats_->configure(inputCfg) != 0) return -EINVAL; - if (!ccmEnabled) - return -EINVAL; - const Size &stats_pattern_size = stats_->patternSize(); if (inputConfig_.patternSize.width != stats_pattern_size.width || inputConfig_.patternSize.height != stats_pattern_size.height) {