From patchwork Tue Jun 16 06:41:35 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Dan Scally X-Patchwork-Id: 26892 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 28576BF415 for ; Tue, 16 Jun 2026 06:41:57 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id E18CA62404; Tue, 16 Jun 2026 08:41:51 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="hx58pO4/"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 00FB7623F5 for ; Tue, 16 Jun 2026 08:41:49 +0200 (CEST) Received: from [127.0.1.1] (chfd-03-b2-v4wan-176392-cust229.vm15.cable.virginm.net [82.19.20.230]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 967D2217B; Tue, 16 Jun 2026 08:41:15 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1781592075; bh=PEZjYKpng5Tu8RX+QavMJLjC77fTL4V4XtaUh1NBXZ4=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=hx58pO4/km+OwfiPRnullhzDfzRCmEGEOHdRvM2HfKw8R8fPoXKsXNYX5+0rUkJZu oRXap3fzr8rghrLtJN9MoOjkZt3m11xZNCYIUe6XT3LeuvDH2cfBD0Ddl8dQTqEP3j PPYkGLyhxU32kbC31GfVk1rqsxA50bmWJJAOsrT4= From: Daniel Scally Date: Tue, 16 Jun 2026 07:41:35 +0100 Subject: [PATCH 01/10] ipa: ipu3: Remove ~Awb() from source and header MIME-Version: 1.0 Message-Id: <20260616-ipu3-libipa-rework-v1-1-d4448b54f1d8@ideasonboard.com> References: <20260616-ipu3-libipa-rework-v1-0-d4448b54f1d8@ideasonboard.com> In-Reply-To: <20260616-ipu3-libipa-rework-v1-0-d4448b54f1d8@ideasonboard.com> To: libcamera-devel@lists.libcamera.org Cc: Daniel Scally X-Mailer: b4 0.14.2 X-Developer-Signature: v=1; a=openpgp-sha256; l=1243; i=dan.scally@ideasonboard.com; h=from:subject:message-id; bh=PEZjYKpng5Tu8RX+QavMJLjC77fTL4V4XtaUh1NBXZ4=; b=owEBbQKS/ZANAwAKAchJV3psRXUyAcsmYgBqMPAoUxpvnueIpdAaVHzoh1slS43QFr7c/IV/5 Z1kMN8TKE+JAjMEAAEKAB0WIQQqyuwyDnZdb+mxmm/ISVd6bEV1MgUCajDwKAAKCRDISVd6bEV1 MvzdD/oC59mfNBvXKsQQZx+dkx2xvMhj39bcBy6i/sHDnZB0YsBVYiLnHZzfrLFa7JWEmlO2E2n FaiUb0yewLpKJrXipNhtS1PgUCY++gueas9Ou4rLELz/miYihWG1ukx8TaJRUi+UngknZ5q6znr xgKh15s7eQQd1A8xa4U6STsGzX7AC7BZbsz7KyHdwnLIH1pHCH8OTHiVMoWoMraXTNCXpQrHvts hgOaz1W+AUB02WS+t8TVei1LEFAb9zBnrwjnt6OqPDpAYgc1HD8GA0001Wouu0v+JVj/5y+gZnk xaaMB5lyDctwKWiNbJcKJcBas3Z9lkEBk2PaPXTQ/K0CfEl41uAxkfU6JwVHqiVwItC0dR+GGZx h2YXUb2FKOhWPIPldn3p3ZyAqZ65BIkOBb/PhZ/0q8S4xnFLiZJeH4Yp1P1TcG4ljvvDnVlGgp8 VSKUvDpfmDzegPiyAc6QIwVkvXFqUsyWfWlvuZ6PEQB5Whusy2D1ws8DU3hekFuLUm3/+NTDYw3 5NfxHX3eAXlc4toPn7Ugjreyvqbhv2wIM06ZQcJaz6FsQ94DPpY9k+EdoUfKylHHRpbcRRqIu2f MhDL5wwtyC+bW+MkEnhOVq5Ux/S55TURurI2VSAXycYA/PxBLATAVDCTKaSmoCq5dQgERHk2Ajz X2B0bl94EV0dk1A== X-Developer-Key: i=dan.scally@ideasonboard.com; a=openpgp; fpr=EEC699ACA1B7CB5D31330C0BBD501C2A3546CCF6 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" Remove the ~Awb() definition from the source and header files and simply allow the compiler to provide the default implementations. Signed-off-by: Daniel Scally Reviewed-by: Barnabás Pőcze --- src/ipa/ipu3/algorithms/awb.cpp | 2 -- src/ipa/ipu3/algorithms/awb.h | 1 - 2 files changed, 3 deletions(-) diff --git a/src/ipa/ipu3/algorithms/awb.cpp b/src/ipa/ipu3/algorithms/awb.cpp index 55de05d9e39f5d2c863b89923964a4c7756f3846..6b4418aa6f3add0a41cae77000c1f5f67897111a 100644 --- a/src/ipa/ipu3/algorithms/awb.cpp +++ b/src/ipa/ipu3/algorithms/awb.cpp @@ -195,8 +195,6 @@ Awb::Awb() zones_.reserve(kAwbStatsSizeX * kAwbStatsSizeY); } -Awb::~Awb() = default; - /** * \copydoc libcamera::ipa::Algorithm::configure */ diff --git a/src/ipa/ipu3/algorithms/awb.h b/src/ipa/ipu3/algorithms/awb.h index dbf69c9073a1627b666fe63158081e347cf3581e..7ec322318dab54ae7c8a647a67a0cf5815a36eb6 100644 --- a/src/ipa/ipu3/algorithms/awb.h +++ b/src/ipa/ipu3/algorithms/awb.h @@ -38,7 +38,6 @@ class Awb : public Algorithm { public: Awb(); - ~Awb(); int configure(IPAContext &context, const IPAConfigInfo &configInfo) override; void prepare(IPAContext &context, const uint32_t frame, From patchwork Tue Jun 16 06:41:36 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Dan Scally X-Patchwork-Id: 26893 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 9A6F6BF415 for ; Tue, 16 Jun 2026 06:41:59 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 93BAC6259D; Tue, 16 Jun 2026 08:41:52 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="pMsQ5sV4"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 2BD89623F6 for ; Tue, 16 Jun 2026 08:41:49 +0200 (CEST) Received: from [127.0.1.1] (chfd-03-b2-v4wan-176392-cust229.vm15.cable.virginm.net [82.19.20.230]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id CF961166C; Tue, 16 Jun 2026 08:41:15 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1781592076; bh=B4LLEhhdkdJIICFVgHPDR1WTHODgJhmo34hBtf3m+z8=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=pMsQ5sV487thOvbtKtfDdfZIG+C5zD/Ybe5bwxSQPTYTGbWqQN4IIcPaUez1/Qp1j kyVfxkBk8pBc0ceQXq/fPAj0hb60NiH6qZzh62FSsl+kGkdcUnkwZnOgRakKspyuSA 9JMkmR4O+hEJ9jdUUG2F+QFRD/3NNEdT85R6Y43c= From: Daniel Scally Date: Tue, 16 Jun 2026 07:41:36 +0100 Subject: [PATCH 02/10] ipu3: awb: Remove CCM settings from Awb algorithm MIME-Version: 1.0 Message-Id: <20260616-ipu3-libipa-rework-v1-2-d4448b54f1d8@ideasonboard.com> References: <20260616-ipu3-libipa-rework-v1-0-d4448b54f1d8@ideasonboard.com> In-Reply-To: <20260616-ipu3-libipa-rework-v1-0-d4448b54f1d8@ideasonboard.com> To: libcamera-devel@lists.libcamera.org Cc: Daniel Scally X-Mailer: b4 0.14.2 X-Developer-Signature: v=1; a=openpgp-sha256; l=1448; i=dan.scally@ideasonboard.com; h=from:subject:message-id; bh=B4LLEhhdkdJIICFVgHPDR1WTHODgJhmo34hBtf3m+z8=; b=owEBbQKS/ZANAwAKAchJV3psRXUyAcsmYgBqMPApnC9i3J5MffrJGwY2B5hlMHtgKpwXjRiyJ f1Z/7c3PXiJAjMEAAEKAB0WIQQqyuwyDnZdb+mxmm/ISVd6bEV1MgUCajDwKQAKCRDISVd6bEV1 Mn7aD/47LR3YJ7F0VHWdTcuCaO//rIQa/3OciazzOt1qoBNuQHBIEHNqwJ8bfpI8cq3zJ+mjNhX 6Qc9P6D27yH495Y/kfnxaagVbNxVv8nq39eXQWq9KGAo0rIqsWfze49PifuxJMbAEAXpvW0JFV6 71pHw4NIWtNdccTY3D9YRyQ2vq0gJEz8rnpT5mbZVzP6ZIa8i7SEXHyH06zBTrW06fZhshMYzAS C9Dj3RrUdb11AO6Btnl3mJYC6pDSZ55eCuJmoHGxe+kxcWkX55iK5EwAxVwXOJUCpgPBlnWnb3X lCYDtv9p35jQxb/wnQwjG7PXg6KbqnQT2wcEkUtmzdyIJQYbO+OzcIBLORTdzjW6Cn6tUOiFM7J EteVS6eZs7aXzh6Oa/qban1+E0YhsMU9jT1Aik6fBvqovp7iuHMB4r6/djXkn1cobfYzjOHOCnt dKZsyQctqk0BsMTehRZJnYLObsWVCziKe00I1+13DTOQrPidFTO0lj+sQPJdI5DsBTFIO39DX2n 9HlOaJU2fs+RL07YHr8lbJGqo70t8c4cH0zMvJ+uKVPPfhffwmnlXEXsY4+gWLICE8ordXaMdQZ wOmJCywCFH1PvIaYajoZ3H4jv9VmvgAh9NYEKoU19TwKMaxtyIE6NTRvC6ZcjNf4A/xvqkPo4Tj 8e+ZojPmEpAQkDQ== X-Developer-Key: i=dan.scally@ideasonboard.com; a=openpgp; fpr=EEC699ACA1B7CB5D31330C0BBD501C2A3546CCF6 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 IPU3 AWB algorithm currently defines a CCM matrix that is just a no-op. Remove the settings entirely; they need really to go in to a dedicated CCM algorithm. Signed-off-by: Daniel Scally Reviewed-by: Barnabás Pőcze --- src/ipa/ipu3/algorithms/awb.cpp | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/src/ipa/ipu3/algorithms/awb.cpp b/src/ipa/ipu3/algorithms/awb.cpp index 6b4418aa6f3add0a41cae77000c1f5f67897111a..613bdbbf51cd127e03906571813260a6857067dc 100644 --- a/src/ipa/ipu3/algorithms/awb.cpp +++ b/src/ipa/ipu3/algorithms/awb.cpp @@ -112,13 +112,6 @@ static const struct ipu3_uapi_bnr_static_config imguCssBnrDefaults = { .opt_center_sqr = { 419904, 133956 }, }; -/* Default color correction matrix defined as an identity matrix */ -static const struct ipu3_uapi_ccm_mat_config imguCssCcmDefault = { - 8191, 0, 0, 0, - 0, 8191, 0, 0, - 0, 0, 8191, 0 -}; - /** * \class Awb * \brief A Grey world white balance correction algorithm @@ -293,12 +286,9 @@ void Awb::prepare(IPAContext &context, LOG(IPU3Awb, Debug) << "Color temperature estimated: " << asyncResults_.temperatureK; - /* The CCM matrix may change when color temperature will be used */ - params->acc_param.ccm = imguCssCcmDefault; params->use.acc_awb = 1; params->use.acc_bnr = 1; - params->use.acc_ccm = 1; } /* Generate an RGB vector with the average values for each zone */ From patchwork Tue Jun 16 06:41:37 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Dan Scally X-Patchwork-Id: 26894 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 10E9AC328C for ; Tue, 16 Jun 2026 06:42:01 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 01D5862403; Tue, 16 Jun 2026 08:41:54 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="IGv1eTXx"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 661A461E76 for ; Tue, 16 Jun 2026 08:41:49 +0200 (CEST) Received: from [127.0.1.1] (chfd-03-b2-v4wan-176392-cust229.vm15.cable.virginm.net [82.19.20.230]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 1DE8F217B; Tue, 16 Jun 2026 08:41:16 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1781592076; bh=qXlIKoGYzTFVKROK9EJEqc4Vdce0o+7fTFYDdlbpedE=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=IGv1eTXxJyPG+wjVw8CdlfeVsqDResPoT7LYygZDavvScst4oLppQrhlw63kICxAE Jj0vRcxqzXwU8YWg+YjwcyiqQpXPDZVE9N0qDBL2UpUXa9GMRdbZ9h3dIyD7LlZq/g c9zizY1+mbzBiAOxi1GA2Wr1xVzryYClSCYqKIfI= From: Daniel Scally Date: Tue, 16 Jun 2026 07:41:37 +0100 Subject: [PATCH 03/10] ipa: ipu3: awb: Port to the new libipa AwbAlgorithm MIME-Version: 1.0 Message-Id: <20260616-ipu3-libipa-rework-v1-3-d4448b54f1d8@ideasonboard.com> References: <20260616-ipu3-libipa-rework-v1-0-d4448b54f1d8@ideasonboard.com> In-Reply-To: <20260616-ipu3-libipa-rework-v1-0-d4448b54f1d8@ideasonboard.com> To: libcamera-devel@lists.libcamera.org Cc: Daniel Scally X-Mailer: b4 0.14.2 X-Developer-Signature: v=1; a=openpgp-sha256; l=17369; i=dan.scally@ideasonboard.com; h=from:subject:message-id; bh=qXlIKoGYzTFVKROK9EJEqc4Vdce0o+7fTFYDdlbpedE=; b=kA0DAAoByElXemxFdTIByyZiAGow8Cmjmb+xJYGNQ08W6fhHWDYB/5cP93GhqKy8pMNaiqLN8 okCMwQAAQoAHRYhBCrK7DIOdl1v6bGab8hJV3psRXUyBQJqMPApAAoJEMhJV3psRXUyq8MP/3w0 rPm6SZ2q5egGjVbwnwZBTc0/h18wgy96T/6J6y4mQksVyWNPSfDyvrPYmuPR0PZxIjrsoOKkVlx 5hWxV/vw0Ucl3todoWjfvY0X6RMzJAOPCVtR9PW17kzxgQ14OWJatVBkiyLNtC830YvzRkbxgtX IzsFpsEv8VR0w2S5s8TJE9pU08flEV86uf51F/6objDS/+RANhhBhMGBtapjN8EsV4dAVzEmGvE o1oLEkqE82NCDmsA3646K6QvAkNhOZh1FcWbNFtf0hI4QMHCUcAu2PYsif86vj3lgUQfMAiJ3KN t/284WyVSuHTvTuX2oGdWrJwoBaAY0eLEE6y9Iqu93FMWEQKQmYSUVgbFVKzA1TuxHxW2URNHXh 0Fi/nh+9xSbLdmOJRWyNk/udxCqTEcUMRq2eg016zKdzUBjhj+3xFZJ7ksbA6ZMy+wN+aUe92ea htMA3P6xKR5BBKZZDtyF21A5XhgjNi9+HjLOGGYdP45OJq3auGBvB+73QDSf8GfvCPza4qmHvq2 XLZpa9jLhmGSsv4ZqUkUfHX0I3DjWua0jL3s4uaLx6Tg2Evn1eTrek9cqADX0Isr5RdMF9A3ofV 2/ePt9oxPnGnx8WoTb9xlZBv4XD15PF7f+ic0kWdQyYEHhmhBo0Nlp6calv/6nP/yYaRl/zw3eE wi8e0 X-Developer-Key: i=dan.scally@ideasonboard.com; a=openpgp; fpr=EEC699ACA1B7CB5D31330C0BBD501C2A3546CCF6 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" Port the IPU3 Awb algorithm to use the new libipa implementation of AwbAlgorithm. In this implementation the awbAlgo_ class member is initialised as UQ<3, 13> following the IPU3 params format documentation. Signed-off-by: Daniel Scally --- src/ipa/ipu3/algorithms/agc.cpp | 12 ++- src/ipa/ipu3/algorithms/awb.cpp | 194 ++++++++++++++++++---------------------- src/ipa/ipu3/algorithms/awb.h | 21 ++--- src/ipa/ipu3/ipa_context.cpp | 35 ++++---- src/ipa/ipu3/ipa_context.h | 15 ++-- 5 files changed, 128 insertions(+), 149 deletions(-) diff --git a/src/ipa/ipu3/algorithms/agc.cpp b/src/ipa/ipu3/algorithms/agc.cpp index d6a7036c6504acb106f5c773b529ad80b8349f85..5120b3bcb177c75ebd61d82c9684779f73305499 100644 --- a/src/ipa/ipu3/algorithms/agc.cpp +++ b/src/ipa/ipu3/algorithms/agc.cpp @@ -208,9 +208,15 @@ void Agc::process(IPAContext &context, [[maybe_unused]] const uint32_t frame, ControlList &metadata) { Histogram hist = parseStatistics(stats, context.configuration.grid.bdsGrid); - rGain_ = context.activeState.awb.gains.red; - gGain_ = context.activeState.awb.gains.blue; - bGain_ = context.activeState.awb.gains.green; + rGain_ = context.activeState.awb.autoEnabled ? + context.activeState.awb.automatic.gains[0] : + context.activeState.awb.manual.gains[0]; + gGain_ = context.activeState.awb.autoEnabled ? + context.activeState.awb.automatic.gains[1] : + context.activeState.awb.manual.gains[1]; + bGain_ = context.activeState.awb.autoEnabled ? + context.activeState.awb.automatic.gains[2] : + context.activeState.awb.manual.gains[2]; /* * The Agc algorithm needs to know the effective exposure value that was diff --git a/src/ipa/ipu3/algorithms/awb.cpp b/src/ipa/ipu3/algorithms/awb.cpp index 613bdbbf51cd127e03906571813260a6857067dc..fa1c70856a3647b188531473e9d9f8b77c747230 100644 --- a/src/ipa/ipu3/algorithms/awb.cpp +++ b/src/ipa/ipu3/algorithms/awb.cpp @@ -26,11 +26,40 @@ namespace ipa::ipu3::algorithms { LOG_DEFINE_CATEGORY(IPU3Awb) /* - * When zones are used for the grey world algorithm, they are only considered if - * their average green value is at least 16/255 (after black level subtraction) - * to exclude zones that are too dark and don't provide relevant colour - * information (on the opposite side of the spectrum, saturated regions are - * excluded by the ImgU statistics engine). + * \todo IPU3 doesn't support the Lux algorithm. + */ +static constexpr unsigned int kDefaultLux = 500; + +/** + * \brief The IPU3 implementation of AwbStats + */ +class Ipu3AwbStats final : public AwbStats +{ +public: + Ipu3AwbStats(){}; + Ipu3AwbStats(const RGB means) + : AwbStats(means) + { + } + + double minColourValue() const override + { + return 0.0; + } +}; + +/** + * \fn Ipu3AwbStats::Ipu3AwbStats(const RGB means) + * \brief Construct an instance of the class with RGB means + * \param[in] means The mean R, G and B values from the statistics + */ + +/* + * Zones are only considered if their average green value is at least + * kMinGreenLevelInZone/255 (after black level subtraction) to exclude zones + * that are too dark and don't provide relevant colour information (on the + * opposite side of the spectrum, saturated regions are excluded by the ImgU + * statistics engine). */ static constexpr uint32_t kMinGreenLevelInZone = 16; @@ -74,26 +103,6 @@ static constexpr uint32_t kMinCellsPerZoneRatio = 255 * 90 / 100; * \brief Sum of the average blue values of each unsaturated cell in the zone */ -/** - * \struct Awb::AwbStatus - * \brief AWB parameters calculated - * - * The AwbStatus structure is intended to store the AWB - * parameters calculated by the algorithm - * - * \var AwbStatus::temperatureK - * \brief Color temperature calculated - * - * \var AwbStatus::redGain - * \brief Gain calculated for the red channel - * - * \var AwbStatus::greenGain - * \brief Gain calculated for the green channel - * - * \var AwbStatus::blueGain - * \brief Gain calculated for the blue channel - */ - /* Default settings for Bayer noise reduction replicated from the Kernel */ static const struct ipu3_uapi_bnr_static_config imguCssBnrDefaults = { .wb_gains = { 16, 16, 16, 16 }, @@ -114,11 +123,7 @@ static const struct ipu3_uapi_bnr_static_config imguCssBnrDefaults = { /** * \class Awb - * \brief A Grey world white balance correction algorithm - * - * The Grey World algorithm assumes that the scene, in average, is neutral grey. - * Reference: Lam, Edmund & Fung, George. (2008). Automatic White Balancing in - * Digital Photography. 10.1201/9781420054538.ch10. + * \brief The IPU3 white balance correction algorithm implementation * * The IPU3 generates statistics from the Bayer Down Scaler output into a grid * defined in the ipu3_uapi_awb_config_s structure. @@ -168,26 +173,26 @@ static const struct ipu3_uapi_bnr_static_config imguCssBnrDefaults = { * cells are ignored. The grid configuration is computed by * IPAIPU3::calculateBdsGrid(). * - * Before calculating the gains, the algorithm aggregates the cell averages for - * each zone in generateAwbStats(). Cells that have a too high ratio of - * saturated pixels are ignored, and only zones that contain enough - * non-saturated cells are then used by the algorithm. - * - * The Grey World algorithm will then estimate the red and blue gains to apply, and - * store the results in the metadata. The green gain is always set to 1. + * Before running the AWB algorithm, we aggregate the cell averages for each + * zone in generateAwbStats(). Cells that have a too high ratio of saturated + * pixels are ignored, and only zones that contain enough non-saturated cells + * are then used by the algorithm. */ Awb::Awb() : Algorithm() { - asyncResults_.blueGain = 1.0; - asyncResults_.greenGain = 1.0; - asyncResults_.redGain = 1.0; - asyncResults_.temperatureK = 4500; - zones_.reserve(kAwbStatsSizeX * kAwbStatsSizeY); } +/** + * \copydoc libcamera::ipa::Algorithm::init + */ +int Awb::init(IPAContext &context, const ValueNode &tuningData) +{ + return awbAlgo_.init(tuningData, context.ctrlMap); +} + /** * \copydoc libcamera::ipa::Algorithm::configure */ @@ -197,12 +202,14 @@ int Awb::configure(IPAContext &context, const ipu3_uapi_grid_config &grid = context.configuration.grid.bdsGrid; stride_ = context.configuration.grid.stride; + awbAlgo_.configure(context.activeState.awb, context.configuration.awb); + cellsPerZoneX_ = std::round(grid.width / static_cast(kAwbStatsSizeX)); cellsPerZoneY_ = std::round(grid.height / static_cast(kAwbStatsSizeY)); /* * Configure the minimum proportion of cells counted within a zone - * for it to be relevant for the grey world algorithm. + * for it to be used. * \todo This proportion could be configured. */ cellsPerZoneThreshold_ = cellsPerZoneX_ * cellsPerZoneY_ * kMaxCellSaturationRatio; @@ -211,6 +218,17 @@ int Awb::configure(IPAContext &context, return 0; } +/** + * \copydoc libcamera::ipa::Algorithm::queueRequest + */ +void Awb::queueRequest(IPAContext &context, const uint32_t frame, + IPAFrameContext &frameContext, + const ControlList &controls) +{ + awbAlgo_.queueRequest(context.activeState.awb, frame, frameContext.awb, + controls); +} + constexpr uint16_t Awb::threshold(float value) { /* AWB thresholds are in the range [0, 8191] */ @@ -237,11 +255,12 @@ constexpr uint16_t Awb::gainValue(double gain) /** * \copydoc libcamera::ipa::Algorithm::prepare */ -void Awb::prepare(IPAContext &context, - [[maybe_unused]] const uint32_t frame, - [[maybe_unused]] IPAFrameContext &frameContext, - ipu3_uapi_params *params) +void Awb::prepare(IPAContext &context, [[maybe_unused]] const uint32_t frame, + IPAFrameContext &frameContext, + [[maybe_unused]] ipu3_uapi_params *params) { + awbAlgo_.prepare(context.activeState.awb, frameContext.awb); + /* * Green saturation thresholds are reduced because we are using the * green channel only in the exposure computation. @@ -279,13 +298,11 @@ void Awb::prepare(IPAContext &context, params->acc_param.bnr.opt_center_sqr.y_sqr_reset = params->acc_param.bnr.opt_center.y_reset * params->acc_param.bnr.opt_center.y_reset; - params->acc_param.bnr.wb_gains.gr = gainValue(context.activeState.awb.gains.green); - params->acc_param.bnr.wb_gains.r = gainValue(context.activeState.awb.gains.red); - params->acc_param.bnr.wb_gains.b = gainValue(context.activeState.awb.gains.blue); - params->acc_param.bnr.wb_gains.gb = gainValue(context.activeState.awb.gains.green); - - LOG(IPU3Awb, Debug) << "Color temperature estimated: " << asyncResults_.temperatureK; + params->acc_param.bnr.wb_gains.gr = gainValue(frameContext.awb.gains.g()); + params->acc_param.bnr.wb_gains.r = gainValue(frameContext.awb.gains.r()); + params->acc_param.bnr.wb_gains.b = gainValue(frameContext.awb.gains.b()); + params->acc_param.bnr.wb_gains.gb = gainValue(frameContext.awb.gains.g()); params->use.acc_awb = 1; params->use.acc_bnr = 1; @@ -366,9 +383,17 @@ void Awb::clearAwbStats() } } -void Awb::awbGreyWorld() +Ipu3AwbStats Awb::calculateRgbMeans(const ipu3_uapi_stats_3a *stats) { - LOG(IPU3Awb, Debug) << "Grey world AWB"; + ASSERT(stats->stats_3a_status.awb_en); + + clearAwbStats(); + generateAwbStats(stats); + generateZones(); + + if (zones_.size() <= 10) + return {}; + /* * Make a separate list of the derivatives for each of red and blue, so * that we can sort them to exclude the extreme gains. We could @@ -399,66 +424,21 @@ void Awb::awbGreyWorld() double redGain = sumRed.g() / (sumRed.r() + 1), blueGain = sumBlue.g() / (sumBlue.b() + 1); - /* Color temperature is not relevant in Grey world but still useful to estimate it :-) */ - asyncResults_.temperatureK = estimateCCT({{ sumRed.r(), sumRed.g(), sumBlue.b() }}); - - /* - * Gain values are unsigned integer value ranging [0, 8) with 13 bit - * fractional part. - */ - redGain = std::clamp(redGain, 0.0, 65535.0 / 8192); - blueGain = std::clamp(blueGain, 0.0, 65535.0 / 8192); - - asyncResults_.redGain = redGain; - /* Hardcode the green gain to 1.0. */ - asyncResults_.greenGain = 1.0; - asyncResults_.blueGain = blueGain; -} - -void Awb::calculateWBGains(const ipu3_uapi_stats_3a *stats) -{ - ASSERT(stats->stats_3a_status.awb_en); - - clearAwbStats(); - generateAwbStats(stats); - generateZones(); - - LOG(IPU3Awb, Debug) << "Valid zones: " << zones_.size(); - - if (zones_.size() > 10) { - awbGreyWorld(); - LOG(IPU3Awb, Debug) << "Gain found for red: " << asyncResults_.redGain - << " and for blue: " << asyncResults_.blueGain; - } + return Ipu3AwbStats({ { 1.0 / redGain, 1.0, 1.0 / blueGain } }); } /** * \copydoc libcamera::ipa::Algorithm::process */ void Awb::process(IPAContext &context, [[maybe_unused]] const uint32_t frame, - [[maybe_unused]] IPAFrameContext &frameContext, - const ipu3_uapi_stats_3a *stats, - [[maybe_unused]] ControlList &metadata) + IPAFrameContext &frameContext, + [[maybe_unused]] const ipu3_uapi_stats_3a *stats, + ControlList &metadata) { - calculateWBGains(stats); + Ipu3AwbStats awbStats = calculateRgbMeans(stats); - /* - * Gains are only recalculated if enough zones were detected. - * The results are cached, so if no results were calculated, we set the - * cached values from asyncResults_ here. - */ - context.activeState.awb.gains.blue = asyncResults_.blueGain; - context.activeState.awb.gains.green = asyncResults_.greenGain; - context.activeState.awb.gains.red = asyncResults_.redGain; - context.activeState.awb.temperatureK = asyncResults_.temperatureK; - - metadata.set(controls::AwbEnable, true); - metadata.set(controls::ColourGains, { - static_cast(context.activeState.awb.gains.red), - static_cast(context.activeState.awb.gains.blue) - }); - metadata.set(controls::ColourTemperature, - context.activeState.awb.temperatureK); + awbAlgo_.process(context.activeState.awb, frameContext.awb, awbStats, + kDefaultLux, metadata); } REGISTER_IPA_ALGORITHM(Awb, "Awb") diff --git a/src/ipa/ipu3/algorithms/awb.h b/src/ipa/ipu3/algorithms/awb.h index 7ec322318dab54ae7c8a647a67a0cf5815a36eb6..3f3996e6c846558acd3902c9fb3cb804cb5b65d4 100644 --- a/src/ipa/ipu3/algorithms/awb.h +++ b/src/ipa/ipu3/algorithms/awb.h @@ -13,6 +13,9 @@ #include +#include "libipa/awb.h" +#include "libipa/fixedpoint.h" + #include "libcamera/internal/vector.h" #include "algorithm.h" @@ -21,6 +24,8 @@ namespace libcamera { namespace ipa::ipu3::algorithms { +class Ipu3AwbStats; + /* Region size for the statistics generation algorithm */ static constexpr uint32_t kAwbStatsSizeX = 16; static constexpr uint32_t kAwbStatsSizeY = 12; @@ -39,7 +44,11 @@ class Awb : public Algorithm public: Awb(); + int init(IPAContext &context, const ValueNode &tuningData) override; int configure(IPAContext &context, const IPAConfigInfo &configInfo) override; + void queueRequest(IPAContext &context, const uint32_t frame, + IPAFrameContext &frameContext, + const ControlList &controls) override; void prepare(IPAContext &context, const uint32_t frame, IPAFrameContext &frameContext, ipu3_uapi_params *params) override; @@ -49,15 +58,7 @@ public: ControlList &metadata) override; private: - struct AwbStatus { - double temperatureK; - double redGain; - double greenGain; - double blueGain; - }; - -private: - void calculateWBGains(const ipu3_uapi_stats_3a *stats); + Ipu3AwbStats calculateRgbMeans(const ipu3_uapi_stats_3a *stats); void generateZones(); void generateAwbStats(const ipu3_uapi_stats_3a *stats); void clearAwbStats(); @@ -67,7 +68,7 @@ private: std::vector> zones_; Accumulator awbStats_[kAwbStatsSizeX * kAwbStatsSizeY]; - AwbStatus asyncResults_; + AwbAlgorithm> awbAlgo_; uint32_t stride_; uint32_t cellsPerZoneX_; diff --git a/src/ipa/ipu3/ipa_context.cpp b/src/ipa/ipu3/ipa_context.cpp index 3b22f7917650d9e400d5368c2f890d6b2dc846a0..b35c925d959027c540257e47944047c238f85571 100644 --- a/src/ipa/ipu3/ipa_context.cpp +++ b/src/ipa/ipu3/ipa_context.cpp @@ -109,6 +109,11 @@ namespace libcamera::ipa::ipu3 { * \brief Maximum analogue gain supported with the configured sensor */ +/** + * \var IPAActiveState::awb + * \brief Active auto-white balance parameters for the IPA + */ + /** * \var IPASessionConfiguration::sensor * \brief Sensor-specific configuration of the IPA @@ -123,6 +128,11 @@ namespace libcamera::ipa::ipu3 { * \brief Sensor output resolution */ +/** + * \var IPASessionConfiguration::awb + * \brief Auto-white balance specific session configuration data + */ + /** * \var IPAActiveState::agc * \brief Context for the Automatic Gain Control algorithm @@ -139,26 +149,6 @@ namespace libcamera::ipa::ipu3 { * The gain should be adapted to the sensor specific gain code before applying. */ -/** - * \var IPAActiveState::awb - * \brief Context for the Automatic White Balance algorithm - * - * \var IPAActiveState::awb.gains - * \brief White balance gains - * - * \var IPAActiveState::awb.gains.red - * \brief White balance gain for R channel - * - * \var IPAActiveState::awb.gains.green - * \brief White balance gain for G channel - * - * \var IPAActiveState::awb.gains.blue - * \brief White balance gain for B channel - * - * \var IPAActiveState::awb.temperatureK - * \brief Estimated color temperature - */ - /** * \var IPAActiveState::toneMapping * \brief Context for ToneMapping and Gamma control @@ -187,4 +177,9 @@ namespace libcamera::ipa::ipu3 { * \brief Analogue gain multiplier */ +/** + * \var IPAFrameContext::awb + * \brief Per-frame auto-white balance parameters for the IPA + */ + } /* namespace libcamera::ipa::ipu3 */ diff --git a/src/ipa/ipu3/ipa_context.h b/src/ipa/ipu3/ipa_context.h index 97fcf06cd4ac9ac6d64c4933fcea80ace0e572df..245cf8b50b270a61df863e314128bede40d30541 100644 --- a/src/ipa/ipu3/ipa_context.h +++ b/src/ipa/ipu3/ipa_context.h @@ -15,6 +15,7 @@ #include #include +#include #include namespace libcamera { @@ -44,6 +45,8 @@ struct IPASessionConfiguration { utils::Duration lineDuration; Size size; } sensor; + + ipa::awb::Session awb; }; struct IPAActiveState { @@ -60,15 +63,7 @@ struct IPAActiveState { uint32_t exposureMode; } agc; - struct { - struct { - double red; - double green; - double blue; - } gains; - - double temperatureK; - } awb; + ipa::awb::ActiveState awb; struct { double gamma; @@ -81,6 +76,8 @@ struct IPAFrameContext : public FrameContext { uint32_t exposure; double gain; } sensor; + + ipa::awb::FrameContext awb; }; struct IPAContext { From patchwork Tue Jun 16 06:41:38 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Dan Scally X-Patchwork-Id: 26895 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 375DCBF415 for ; Tue, 16 Jun 2026 06:42:02 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id B804C62401; Tue, 16 Jun 2026 08:41:54 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="dZc3Sj3w"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [IPv6:2001:4b98:dc2:55:216:3eff:fef7:d647]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 9D9A9623FC for ; Tue, 16 Jun 2026 08:41:49 +0200 (CEST) Received: from [127.0.1.1] (chfd-03-b2-v4wan-176392-cust229.vm15.cable.virginm.net [82.19.20.230]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 60504166C; Tue, 16 Jun 2026 08:41:16 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1781592076; bh=XhrY9g+HIVbvhGzoKDTKfPqcr6KG/n9uBAajpkwSQcM=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=dZc3Sj3w7g+swN/kTVgO9/gH60mQPqkhmBCy6NWlBeXeQe4KsFnDdVnv4AxpmlcXS o69d4qhRDnE/ZUFsuvhKIJPOGrn3EtmcJrsnULHL0/+j0eZy0XWgCK+4bLlaQ23ruw sC0hm+02kZ9GUY0IvWWPI/O5H7AlFbbbW7vw/vNY= From: Daniel Scally Date: Tue, 16 Jun 2026 07:41:38 +0100 Subject: [PATCH 04/10] ipa: ipu3: ccm: Add a Colour Correction Matrix algorithm MIME-Version: 1.0 Message-Id: <20260616-ipu3-libipa-rework-v1-4-d4448b54f1d8@ideasonboard.com> References: <20260616-ipu3-libipa-rework-v1-0-d4448b54f1d8@ideasonboard.com> In-Reply-To: <20260616-ipu3-libipa-rework-v1-0-d4448b54f1d8@ideasonboard.com> To: libcamera-devel@lists.libcamera.org Cc: Daniel Scally X-Mailer: b4 0.14.2 X-Developer-Signature: v=1; a=openpgp-sha256; l=7766; i=dan.scally@ideasonboard.com; h=from:subject:message-id; bh=XhrY9g+HIVbvhGzoKDTKfPqcr6KG/n9uBAajpkwSQcM=; b=owEBbQKS/ZANAwAKAchJV3psRXUyAcsmYgBqMPApPrJ6LJ78559AyiZFIsPAUgT5ulDv19XF0 yD5tTzysbyJAjMEAAEKAB0WIQQqyuwyDnZdb+mxmm/ISVd6bEV1MgUCajDwKQAKCRDISVd6bEV1 Mu3SD/9MiGVHynuAViDiE4IRkMk8meJJ1/bmaFT/fIoOGGf8RLc5N+PAeJfXQoF0ASP0ehYWS3J o0k01Zx+5WaxOb9AswLCxk8xa87ZHbV4vuYC38r3ZHC+HdHUgX/r4jDkmfiY31KVlVk9XFTTg3q mbC5sBPOzy9sdTRJ6vLBxNizqVVVFmPh3BENpgfFO7FdRSZ/p3AtEEg9GGY1ZYIhC41vQxcXXhp o3cQdqyB7j5Xlk0XwNIzuXKLxT1oDi2dG/xPbzou7kkFQC9ju59Uir3e3rjNen7aX1bHpQrvmGT kToaGYxDEt1yBF6pJ4r2vUNSfYKOdIduq52uOg+0XNp87+OVkqkLh0pS9nelGXNDgCHwaBlpdfH XIHLe4u2KgrQfNpH/gcN9YIvPrVidvzPUmPTisV4LicinIccmMNaggFG+pxO0IjEV6ECnjUSMZg /0WhJsTch9RiftMb7GfzrEuge1Xsm5rj/3AkTT98zI3eIxM98K+7k7LMFBIsIpH+DBDhKmIzZuC eGhna8z8RdAEjImDQB+uwSjqx+nNqN5TcJ7D2Nem+19ANgWyUZT2cof9dDPZZP8Df7evtQsSy2/ n2Jv7O5qrg4xJhJRyutuCqYGEuNmL/1Mb2r4SQnhzkB1kXkSEFuXalJWPcngzWtnXyAffb8BxAJ idx3jWYNyauDWvA== X-Developer-Key: i=dan.scally@ideasonboard.com; a=openpgp; fpr=EEC699ACA1B7CB5D31330C0BBD501C2A3546CCF6 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" Add a Colour Correction Matrix algorithm that uses the new libipa implementation. The module isn't well documented in the kernel but the default values from the driver suggest a Q3.13 format. Signed-off-by: Daniel Scally --- src/ipa/ipu3/algorithms/ccm.cpp | 116 ++++++++++++++++++++++++++++++++++++ src/ipa/ipu3/algorithms/ccm.h | 43 +++++++++++++ src/ipa/ipu3/algorithms/meson.build | 1 + src/ipa/ipu3/ipa_context.cpp | 10 ++++ src/ipa/ipu3/ipa_context.h | 3 + 5 files changed, 173 insertions(+) diff --git a/src/ipa/ipu3/algorithms/ccm.cpp b/src/ipa/ipu3/algorithms/ccm.cpp new file mode 100644 index 0000000000000000000000000000000000000000..44b8c107b079c544c5afd1fce3cbe355f6dacd76 --- /dev/null +++ b/src/ipa/ipu3/algorithms/ccm.cpp @@ -0,0 +1,116 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2026, Ideas On Board + * + * IPU3 Colour correction matrix algorithm + */ + +#include "ccm.h" + +/** + * \file ccm.h + */ + +namespace libcamera { + +namespace ipa::ipu3::algorithms { + +/** + * \class Ccm + * \brief The IPU3 color correction matrix algorithm + */ + +LOG_DEFINE_CATEGORY(IPU3Ccm) + +/** + * \copydoc libcamera::ipa::Algorithm::init + */ +int Ccm::init(IPAContext &context, const ValueNode &tuningData) +{ + return ccmAlgo_.init(tuningData, context.ctrlMap); +} + +/** + * \copydoc libcamera::ipa::Algorithm::configure + */ +int Ccm::configure(IPAContext &context, + [[maybe_unused]] const IPAConfigInfo &configInfo) +{ + return ccmAlgo_.configure(context.activeState.ccm, + context.activeState.awb.automatic.temperatureK); +} + +/** + * \copydoc libcamera::ipa::Algorithm::queueRequest + */ +void Ccm::queueRequest(IPAContext &context, [[maybe_unused]] const uint32_t frame, + IPAFrameContext &frameContext, + const ControlList &controls) +{ + /* Nothing to do here, the ccm will be calculated in prepare() */ + if (frameContext.awb.autoEnabled) + return; + + ccmAlgo_.queueRequest(context.activeState.ccm, frameContext.ccm, controls); +} + +void Ccm::setParameters(ipu3_uapi_params *params, IPAFrameContext &context) +{ + const Matrix &matrix = context.ccm.ccm; + const Matrix &offsets = context.ccm.offsets; + + params->use.acc_ccm = 1; + + params->acc_param.ccm.coeff_m11 = Q<3, 13>(matrix[0][0]).quantized(); + params->acc_param.ccm.coeff_m12 = Q<3, 13>(matrix[0][1]).quantized(); + params->acc_param.ccm.coeff_m13 = Q<3, 13>(matrix[0][2]).quantized(); + params->acc_param.ccm.coeff_o_r = offsets[0][0]; + + params->acc_param.ccm.coeff_m21 = Q<3, 13>(matrix[1][0]).quantized(); + params->acc_param.ccm.coeff_m22 = Q<3, 13>(matrix[1][1]).quantized(); + params->acc_param.ccm.coeff_m23 = Q<3, 13>(matrix[1][2]).quantized(); + params->acc_param.ccm.coeff_o_g = offsets[1][0]; + + params->acc_param.ccm.coeff_m31 = Q<3, 13>(matrix[2][0]).quantized(); + params->acc_param.ccm.coeff_m32 = Q<3, 13>(matrix[2][1]).quantized(); + params->acc_param.ccm.coeff_m33 = Q<3, 13>(matrix[2][2]).quantized(); + params->acc_param.ccm.coeff_o_b = offsets[2][0]; + + LOG(IPU3Ccm, Debug) << "Setting matrix " << matrix; + LOG(IPU3Ccm, Debug) << "Setting offsets " << offsets; +} + +/** + * \copydoc libcamera::ipa::Algorithm::prepare + */ +void Ccm::prepare(IPAContext &context, const uint32_t frame, + IPAFrameContext &frameContext, ipu3_uapi_params *params) +{ + if (!frameContext.awb.autoEnabled) { + setParameters(params, frameContext); + return; + } + + ccmAlgo_.prepare(context.activeState.ccm, frameContext.ccm, frame, + frameContext.awb.temperatureK); + + setParameters(params, frameContext); +} + +/** + * \copydoc libcamera::ipa::Algorithm::process + */ +void Ccm::process([[maybe_unused]] IPAContext &context, + [[maybe_unused]] const uint32_t frame, + IPAFrameContext &frameContext, + [[maybe_unused]] const ipu3_uapi_stats_3a *stats, + ControlList &metadata) +{ + ccmAlgo_.process(frameContext.ccm, metadata); +} + +REGISTER_IPA_ALGORITHM(Ccm, "Ccm") + +} /* namespace ipa::ipu3::algorithms */ + +} /* namespace libcamera */ diff --git a/src/ipa/ipu3/algorithms/ccm.h b/src/ipa/ipu3/algorithms/ccm.h new file mode 100644 index 0000000000000000000000000000000000000000..39719c986267aff02e3129aad9dbe4baeec8f8a1 --- /dev/null +++ b/src/ipa/ipu3/algorithms/ccm.h @@ -0,0 +1,43 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2026, Ideas On Board + * + * IPU3 Colour correction matrix algorithm + */ + +#pragma once + +#include + +#include +#include + +#include "algorithm.h" + +namespace libcamera { + +namespace ipa::ipu3::algorithms { + +class Ccm : public Algorithm +{ +public: + int init(IPAContext &context, const ValueNode &tuningData) override; + int configure(IPAContext &context, const IPAConfigInfo &configInfo) override; + void queueRequest(IPAContext &context, const uint32_t frame, + IPAFrameContext &frameContext, + const ControlList &controls) override; + void prepare(IPAContext &context, const uint32_t frame, + IPAFrameContext &frameContext, + ipu3_uapi_params *params) override; + void process(IPAContext &context, const uint32_t frame, + IPAFrameContext &frameContext, + const ipu3_uapi_stats_3a *stats, + ControlList &metadata) override; + +private: + void setParameters(ipu3_uapi_params *params, IPAFrameContext &context); + CcmAlgorithm> ccmAlgo_; +}; + +} /* namespace ipa::ipu3::algorithms */ +} /* namespace libcamera */ diff --git a/src/ipa/ipu3/algorithms/meson.build b/src/ipa/ipu3/algorithms/meson.build index b70a551cacf6cbdedf3ab41771a34ddcdae9fd8b..3dafd2fda9897942cf87d9640665c4fcff383859 100644 --- a/src/ipa/ipu3/algorithms/meson.build +++ b/src/ipa/ipu3/algorithms/meson.build @@ -5,5 +5,6 @@ ipu3_ipa_algorithms = files([ 'agc.cpp', 'awb.cpp', 'blc.cpp', + 'ccm.cpp', 'tone_mapping.cpp', ]) diff --git a/src/ipa/ipu3/ipa_context.cpp b/src/ipa/ipu3/ipa_context.cpp index b35c925d959027c540257e47944047c238f85571..dc43bc0877ed5c2e7287414e12667374f5ee1c80 100644 --- a/src/ipa/ipu3/ipa_context.cpp +++ b/src/ipa/ipu3/ipa_context.cpp @@ -114,6 +114,11 @@ namespace libcamera::ipa::ipu3 { * \brief Active auto-white balance parameters for the IPA */ +/** + * \var IPAActiveState::ccm + * \brief Active colour Correction Matrix parameters for the IPA + */ + /** * \var IPASessionConfiguration::sensor * \brief Sensor-specific configuration of the IPA @@ -182,4 +187,9 @@ namespace libcamera::ipa::ipu3 { * \brief Per-frame auto-white balance parameters for the IPA */ +/** + * \var IPAFrameContext::ccm + * \brief Per-frame colour Correction Matrix parameters for the IPA + */ + } /* namespace libcamera::ipa::ipu3 */ diff --git a/src/ipa/ipu3/ipa_context.h b/src/ipa/ipu3/ipa_context.h index 245cf8b50b270a61df863e314128bede40d30541..be626d30d966b1bdaa322e5154f95f745f799976 100644 --- a/src/ipa/ipu3/ipa_context.h +++ b/src/ipa/ipu3/ipa_context.h @@ -16,6 +16,7 @@ #include #include +#include #include namespace libcamera { @@ -64,6 +65,7 @@ struct IPAActiveState { } agc; ipa::awb::ActiveState awb; + ipa::ccm::ActiveState ccm; struct { double gamma; @@ -78,6 +80,7 @@ struct IPAFrameContext : public FrameContext { } sensor; ipa::awb::FrameContext awb; + ipa::ccm::FrameContext ccm; }; struct IPAContext { From patchwork Tue Jun 16 06:41:39 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Dan Scally X-Patchwork-Id: 26896 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 0A84FC3301 for ; Tue, 16 Jun 2026 06:42:03 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 2D731625A1; Tue, 16 Jun 2026 08:41:56 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="BlM8gDMn"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id D9A6C62401 for ; Tue, 16 Jun 2026 08:41:49 +0200 (CEST) Received: from [127.0.1.1] (chfd-03-b2-v4wan-176392-cust229.vm15.cable.virginm.net [82.19.20.230]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 977A4217B; Tue, 16 Jun 2026 08:41:16 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1781592076; bh=Y0ZLtyoOLcv4vmrV9hPoS/xsNd9dtxzJlb5kWBqmzBo=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=BlM8gDMnjXThDiChj+dICzYm2GJCpFJYeVsKSN/6i4hGGmfOeaW9ahs9PBiGfgTan 6j+0U7fvNBkLDfkPJerwjdr0jVK38SKNTL1xLXle+voj8cN+AraeHPuR4ibypudFTx mbpOswPRutK/OqDArE0TCXT/FGXignkn7jXaP/EM= From: Daniel Scally Date: Tue, 16 Jun 2026 07:41:39 +0100 Subject: [PATCH 05/10] ipa: libipa: Add GammaAlgorithm class MIME-Version: 1.0 Message-Id: <20260616-ipu3-libipa-rework-v1-5-d4448b54f1d8@ideasonboard.com> References: <20260616-ipu3-libipa-rework-v1-0-d4448b54f1d8@ideasonboard.com> In-Reply-To: <20260616-ipu3-libipa-rework-v1-0-d4448b54f1d8@ideasonboard.com> To: libcamera-devel@lists.libcamera.org Cc: Daniel Scally X-Mailer: b4 0.14.2 X-Developer-Signature: v=1; a=openpgp-sha256; l=11753; i=dan.scally@ideasonboard.com; h=from:subject:message-id; bh=Y0ZLtyoOLcv4vmrV9hPoS/xsNd9dtxzJlb5kWBqmzBo=; b=owEBbQKS/ZANAwAKAchJV3psRXUyAcsmYgBqMPApaOrgoJUVlQLIGjxdg/LCTIODPR0RA9lad gfwS/rZxL6JAjMEAAEKAB0WIQQqyuwyDnZdb+mxmm/ISVd6bEV1MgUCajDwKQAKCRDISVd6bEV1 MqiBD/0Su+//Jr0g2xbxxNUQCshEUQG/PK6953VhX7xg/E15uDgvc3RjEAOKWk8cyW4npzeq9up 6Qput34hu0ZWlGAU850r7CEXos/wr8aOiOr51KNXEdRyBsT+NcEQ/R/2koLrv5qS3Bp7B3MZBIz fgyRk2TG07VNlrAiZb10jQwUNkHxwLHLNy4QoUSky551mF7BV1AjMm9CaYnaFrlFakbGE0JfGDE NtaGN370sgfi/x8JaTPsPMeIX/o8MeQjXN7JKSLdyhpKDIIbu++OMjzdJEY2hxz0fBa9lbcP+Io ECVziX0OOOUDrmAdHxJD0rb9vd5sa0YNdT59BR0RqJG7d5gUdYI6mD2ez0fuLkHaMfVSr8c9Y6T n+IyTFaRpxl0762E92AwEr9kK65KIeoME5zuEkK1OJDBnwXaFaig7JkWcEwmREubdafwIcv3iB5 P8as6PUIrEf/nGQp3jKAHWOb3AkoNkaYu3sdC3XPpWNCB7cUF+3G1JGDWmMOIShRxPTsLpb8iFG EC/g0mhip6rFHmqQFGKAFcZo6xUGy5z10VyZSoRXRAdItKTtI46bZDFzFTsWGsAAHbckJElZEmO LWS7v8ec3PpeVTFcRxUde+LJyarWI+iBKLAb8EDe1uJa58oDrr04Ir3S+s/KCZoRpHq/7u7NnWO V+s2DaWRgnbnAdA== X-Developer-Key: i=dan.scally@ideasonboard.com; a=openpgp; fpr=EEC699ACA1B7CB5D31330C0BBD501C2A3546CCF6 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" Add a base GammaAlgorithm class that can be used by IPA specific gamma algorithms to reduce the amount of work that they need to implement. Signed-off-by: Daniel Scally --- src/ipa/libipa/gamma.cpp | 248 +++++++++++++++++++++++++++++++++++++++++++++ src/ipa/libipa/gamma.h | 93 +++++++++++++++++ src/ipa/libipa/meson.build | 2 + 3 files changed, 343 insertions(+) diff --git a/src/ipa/libipa/gamma.cpp b/src/ipa/libipa/gamma.cpp new file mode 100644 index 0000000000000000000000000000000000000000..f84f4cf7049f695f6a4ea21e0bcf851aef0f2c70 --- /dev/null +++ b/src/ipa/libipa/gamma.cpp @@ -0,0 +1,248 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2026 Ideas on Board Oy + * + * libIPA Gamma correction algorithm + */ + +#include "gamma.h" + +#include + +#include + +#include "libcamera/internal/value_node.h" + +/** + * \file gamma.h + * \brief libipa implementation of a gamma curve correction algorithm + */ + +namespace libcamera { + +namespace ipa { + +LOG_DEFINE_CATEGORY(Gamma) + +namespace gamma { + +/** + * \struct ActiveState + * \brief Active gamma correction algorithm state + * + * \var ActiveState::gamma + * \brief The gamma correction value applied as 1.0/gamma + */ + +/** + * \struct FrameContext + * \brief Per-frame gamma correction settings + * + * \var FrameContext::gamma + * \brief The gamma correction value applied for this frame + * + * \var FrameContext::update + * \brief A flag instructing the algorithm to push an update to the hardware + */ + +} /* namespace gamma */ + +/** + * \brief The default gamma correction value + */ +const float kDefaultGamma = 2.2f; + +/** + * \class GammaAlgorithmBase + * \brief Base class for GammaAlgorithm to implement non-templated functions + * + * This base class for GammeaAlgorithm allows us to implement non templated + * functions. IPA specific implementations shall derive from GammaAlgorithm and + * not this class. + */ + +/** + * \fn GammaAlgorithmBase::GammaAlgorithmBase + * \brief Construct an instance of the class + * \param[in] nLutNodes Set the number of function knee-points expected by the + * IPA algorithm + */ + +/** + * \brief Initialise the algorithm with the given tuning data + * \param[out] controls The ControlList into which this algorithm's supported + * controls will be emplaced. + * \param[in] tuningData The tuning data to use with the algorithm + * \param[in] segments A vector of segment spacings to define a custom + * X coordinate system for the curve + * + * Parse \a tuningData and \a segments to initialize the gamma correction curve. + * The tuning data may contain a default gamma value to use; otherwise the value + * of \a kDefaultGamma will be taken as the default. \a segments may provide a + * view into an array of segment spacings, which can be used to vary the X + * co-ordinate of the gamma correction curve. For example, if the piecewise + * linear function of the correction curve is expected to have 16 knee-points, a + * \a segments array like so: + * + * [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1] + * + * would result in evenly spaced knee-points along the X-axis. Hardware may + * expect the knee-points to be spaced more densely towards the start of the + * curve and more sparsely towards the end, in which case an alternative array + * might be: + * + * [1, 1, 1, 1, 2, 2, 2, 2, 4, 4, 4, 8, 8, 8, 8, 8] + * + * As the values in \a segments represent the distance between two knee-points + * relative to the total distance between the first and last point, the length + * of \a segments should be equal to the number of knee-points minus one. As a + * convenience, a hardware-specific algorithm deriving from this class may omit + * \a segments, in which case an evenly-spaced default will be constructed. + * + * IPA modules are expected to call this function as part of their + * implementation of Algorithm::init() + * + * @return 0 on success, a negative error code otherwise + */ +int GammaAlgorithmBase::init(ControlInfoMap::Map &controls, const ValueNode &tuningData, + Span segments) +{ + /* + * If the caller doesn't pass in a segment list we simply construct one + * with equally spaced segments. We need one less segment than we have + * LUT nodes. + */ + unsigned int expectedNSegments = nLutNodes_ - 1; + + if (segments.empty()) + for (unsigned int i = 0; i < expectedNSegments; i++) + segments_.push_back(1); + else + segments_.assign(segments.begin(), segments.end()); + + if (segments_.size() != expectedNSegments) + return -EINVAL; + + segmentsSum_ = std::accumulate(segments_.begin(), segments_.end(), 0.0f); + + defaultGamma_ = tuningData["gamma"].get(kDefaultGamma); + controls[&controls::Gamma] = ControlInfo(0.1f, 10.0f, defaultGamma_); + + return 0; +} + +/** + * \brief Configure the gamma correction algorithm + * \param[out] state The gamma correction algorithm's active state + * + * Reset to the default gamma correction value. + * + * IPA modules are expected to call this function as part of their + * implementation of Algorithm::configure() + */ +void GammaAlgorithmBase::configure(gamma::ActiveState &state) +{ + state.gamma = defaultGamma_; +} + +/** + * \brief Queue a request to the gamma correction algorithm + * \param[in] state The algorithm's active state + * \param[in] frame The current frame number + * \param[in] context The algorithm's frame context + * \param[in] controls The ControlList that was queued with the request + * + * Queue a new request to the gamma correction algorithm and handle any relevant + * controls that were queued. The only control currently handled is: + * + * - controls::Gamma + * + * If a control with that ID is queued the value is stored in \a state and + * \a context. + * + * IPA modules are expected to call this function as part of their + * implementation of Algorithm::queueRequest() + */ +void GammaAlgorithmBase::queueRequest(gamma::ActiveState &state, + const uint32_t frame, + gamma::FrameContext &context, + const ControlList &controls) +{ + if (frame == 0) + context.update = true; + + const auto &gamma = controls.get(controls::Gamma); + if (gamma) { + state.gamma = *gamma; + context.update = true; + LOG(Gamma, Info) << "Set gamma to " << *gamma; + } + + context.gamma = state.gamma; +} + +/** + * \brief Populate metadata with the gamma correction values for a frame + * \param[in] context The frame context + * \param[out] metadata The ControlList of metadata for a frame + * + * Report the gamma value used to calculate the correction curve that was + * applied to a frame. + */ +void GammaAlgorithmBase::process(gamma::FrameContext &context, ControlList &metadata) +{ + metadata.set(controls::Gamma, context.gamma); +} + +/** + * \var GammaAlgorithmBase::nLutNodes_ + * \brief The number of knee-points in the gamma correction curve + */ + +/** + * \var GammaAlgorithmBase::defaultGamma_ + * \brief The default gamma parameter used at stream start + */ + +/** + * \var GammaAlgorithmBase::segments_ + * \brief The vector of segment sizes describing the space between knee-points + */ + +/** + * \var GammaAlgorithmBase::segmentsSum_ + * \brief The sum of \a GammaAlgorithmBase::segments_ + */ + +/** + * \class GammaAlgorithm + * \brief The libipa gamma correction algorithm + * \tparam nLutNodes The number of knee-points in the algorithm's function + * \tparam UQ The fixedpoint representation of the function's values + * + * Gamma correction adjusts for the differences in the way light is perceived + * by a camera and the human eye by applying a function to the input values. + * The GammaAlgorithm class facilitates this by building a piecewise linear + * function from a gamma parameter and supplying it in the hardware-specific + * formats defined by the IPA algorithms. + * + * IPA modules are expected to store an instance of GammaAlgorithm as a class + * member, templated with the format and number of knee-points in the PWL + * expected by their hardware and then call its functions in their overload of + * the Algorithm class's function. + * + * When an application queues a new value for the gamma parameter with a + * Request, the GammaAlgorithm will recalculate and populate the new LUT to be + * sent to the ISP. + */ + +/** + * \fn GammaAlgorithm::prepare() + * \tparam T The type of data expected by the hardware's look-up table + * \param[in] context The frame context + * \param[out] lut The Span into which to place the calculated look-up table + */ + +} /* namespace ipa */ + +} /* namespace libcamera */ diff --git a/src/ipa/libipa/gamma.h b/src/ipa/libipa/gamma.h new file mode 100644 index 0000000000000000000000000000000000000000..2b449d9ec41bc96f576e664afca33acb8267a8e6 --- /dev/null +++ b/src/ipa/libipa/gamma.h @@ -0,0 +1,93 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2026 Ideas on Board Oy + * + * libIPA Gamma correction algorithm + */ + +#pragma once + +#include +#include + +#include +#include + +#include + +#include "libcamera/internal/value_node.h" + +#include "fixedpoint.h" + +namespace libcamera { + +namespace ipa { + +LOG_DECLARE_CATEGORY(Gamma) + +namespace gamma { + +struct ActiveState { + double gamma; +}; + +struct FrameContext { + double gamma; + bool update; +}; + +} /* namespace gamma */ + +class GammaAlgorithmBase +{ +public: + GammaAlgorithmBase(unsigned int nLutNodes) + : nLutNodes_(nLutNodes) + { + } + + int init(ControlInfoMap::Map &controls, const ValueNode &tuningData, + Span segments = {}); + + void configure(gamma::ActiveState &state); + void queueRequest(gamma::ActiveState &state, const uint32_t frame, + gamma::FrameContext &context, const ControlList &controls); + void process(gamma::FrameContext &context, ControlList &metadata); + +protected: + unsigned int nLutNodes_; + float defaultGamma_; + std::vector segments_; + unsigned int segmentsSum_; +}; + +template +class GammaAlgorithm : public GammaAlgorithmBase +{ +public: + GammaAlgorithm() + : GammaAlgorithmBase(nLutNodes) + { + } + + template + void prepare(gamma::FrameContext &context, Span lut) + { + float x = 0; + + for (unsigned int i = 0; i < nLutNodes_; i++) { + float gamma = std::pow(x / segmentsSum_, + 1.0 / context.gamma); + lut[i] = UQ(gamma).quantized(); + + LOG(Gamma, Debug) << "LUT[" << i << "]=" << gamma << "(" << lut[i] << ")"; + + if (i < segments_.size()) + x += segments_[i]; + } + } +}; + +} /* namespace ipa */ + +} /* namespace libcamera */ diff --git a/src/ipa/libipa/meson.build b/src/ipa/libipa/meson.build index da6ea0c5e13000f78b2196c7334610c350f1ad13..565da9be9059f2167d107061d643e58202e655ef 100644 --- a/src/ipa/libipa/meson.build +++ b/src/ipa/libipa/meson.build @@ -12,6 +12,7 @@ libipa_headers = files([ 'exposure_mode_helper.h', 'fc_queue.h', 'fixedpoint.h', + 'gamma.h', 'histogram.h', 'interpolator.h', 'lsc.h', @@ -37,6 +38,7 @@ libipa_sources = files([ 'exposure_mode_helper.cpp', 'fc_queue.cpp', 'fixedpoint.cpp', + 'gamma.cpp', 'histogram.cpp', 'interpolator.cpp', 'lsc.cpp', From patchwork Tue Jun 16 06:41:40 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Dan Scally X-Patchwork-Id: 26897 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 B7531C3302 for ; Tue, 16 Jun 2026 06:42:03 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 04BC9625BB; Tue, 16 Jun 2026 08:41:57 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="ZUfdUrd9"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 1C73A623F5 for ; Tue, 16 Jun 2026 08:41:50 +0200 (CEST) Received: from [127.0.1.1] (chfd-03-b2-v4wan-176392-cust229.vm15.cable.virginm.net [82.19.20.230]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id D21F1166C; Tue, 16 Jun 2026 08:41:16 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1781592076; bh=sAflhLA91ZjuiM83DPI+H8xZ3xvJUxxNg+0tqw96N5M=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=ZUfdUrd9oFWfUSr4MI91v6GdZCDN6ziOlm8x6yTRNDkHfKICw06pDc6TS9C3TxaQX I/hU8wKcybDMLdWIjJmNO0pW+hgkW/C2p7UAMSVoxtefqiAKyXiXcB6Ao6MLue7HHh nldDOAR80qqpSet+myYzqmUCsNuTPeJ3+R8g+QYY= From: Daniel Scally Date: Tue, 16 Jun 2026 07:41:40 +0100 Subject: [PATCH 06/10] ipa: ipu3: ToneMapping: Convert to use GammaAlgorithm MIME-Version: 1.0 Message-Id: <20260616-ipu3-libipa-rework-v1-6-d4448b54f1d8@ideasonboard.com> References: <20260616-ipu3-libipa-rework-v1-0-d4448b54f1d8@ideasonboard.com> In-Reply-To: <20260616-ipu3-libipa-rework-v1-0-d4448b54f1d8@ideasonboard.com> To: libcamera-devel@lists.libcamera.org Cc: Daniel Scally X-Mailer: b4 0.14.2 X-Developer-Signature: v=1; a=openpgp-sha256; l=8324; i=dan.scally@ideasonboard.com; h=from:subject:message-id; bh=sAflhLA91ZjuiM83DPI+H8xZ3xvJUxxNg+0tqw96N5M=; b=owEBbQKS/ZANAwAKAchJV3psRXUyAcsmYgBqMPAqlYyVaISm7jnN4T8x7D5FpCb1feWMutMc6 LyakDfa1wKJAjMEAAEKAB0WIQQqyuwyDnZdb+mxmm/ISVd6bEV1MgUCajDwKgAKCRDISVd6bEV1 MsJTEACzBdHozM3of4+7cgyMiKcNqtMN/VaaVWVEFBNoD5aKHXr+mD8lQUWY9aif1r2d9X/tfQ9 gj1mZ2hgG3Xy7mktf2WsuWnXBBCby+geD5uss/oBhroTb44l86KYRE5PHwd38Dx+N92Byfzmjis JizN5VEoNFL2hrxq//tejm5PTPG3YTkp0E248as4hG5rHCIGVplHfKnkxX9Hp0d6mFORDvG6Drf QvYlOyPgoHokWsq4pEXIKJPGRqs1NMSqnnKkKlIOj0HIBLcJpxzU4gGnrRDSCoLmzStPxk9CpPT M/vyPvvgAJtzi2FvfdzAD6+tSt592mcdHbMYA3c6QTrVc5G96KEjfmEdn3NNbE6j/7jQu14NePB VlYw4AF/+R+y6rNZ8bb5/QV5o//gVG1G+RH6DCcYhkZ/GjAm/zZ6gkQX4IUftGH6ZrlMsEvgfXV UEbaa0fDshO6kQXRlsR/ZumnzgaMfM75H/ZhWaHtdPLT7sqIS0XNOStQWUiflKR6lQTljEMO0zz owCie7q+eWopWaaWGj54LlX3aqiv/mT+t9CliUZxGCwxbXVQ1YCvnH2PPIecD9Q3bybV5OZ06G2 dViyDO84SIU+i9qtdYacsfmqzZaSD/bNinpUHO1gEI5OzraI/s2KnxvHsvUh+JR3IUNo37y3q6o 6lzD2W9Blc+PQvA== X-Developer-Key: i=dan.scally@ideasonboard.com; a=openpgp; fpr=EEC699ACA1B7CB5D31330C0BBD501C2A3546CCF6 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" Convert the IPU3 ToneMapping algorithm to use the new GammaAlgorithm base class. This gives us configurable gamma via the tuning files and at runtime using the Gamma control. Signed-off-by: Daniel Scally --- src/ipa/ipu3/algorithms/tone_mapping.cpp | 73 +++++++++++++++++--------------- src/ipa/ipu3/algorithms/tone_mapping.h | 9 +++- src/ipa/ipu3/ipa_context.cpp | 24 +++++------ src/ipa/ipu3/ipa_context.h | 8 ++-- 4 files changed, 60 insertions(+), 54 deletions(-) diff --git a/src/ipa/ipu3/algorithms/tone_mapping.cpp b/src/ipa/ipu3/algorithms/tone_mapping.cpp index 160338c139448cc9a0bc1fe2400c335a96f68f73..2bc29bb9124dd8bd327ca3064b52c55637f56e7b 100644 --- a/src/ipa/ipu3/algorithms/tone_mapping.cpp +++ b/src/ipa/ipu3/algorithms/tone_mapping.cpp @@ -10,6 +10,8 @@ #include #include +#include + /** * \file tone_mapping.h */ @@ -27,10 +29,17 @@ namespace ipa::ipu3::algorithms { */ ToneMapping::ToneMapping() - : gamma_(1.0) { } +/** + * \copydoc libcamera::ipa::Algorithm::init + */ +int ToneMapping::init(IPAContext &context, const ValueNode &tuningData) +{ + return gammaAlgo_.init(context.ctrlMap, tuningData); +} + /** * \brief Configure the tone mapping given a configInfo * \param[in] context The shared IPA context @@ -41,12 +50,21 @@ ToneMapping::ToneMapping() int ToneMapping::configure(IPAContext &context, [[maybe_unused]] const IPAConfigInfo &configInfo) { - /* Initialise tone mapping gamma value. */ - context.activeState.toneMapping.gamma = 0.0; - + gammaAlgo_.configure(context.activeState.gamma); return 0; } +/** + * \copydoc libcamera::ipa::Algorithm::queueRequest + */ +void ToneMapping::queueRequest(IPAContext &context, const uint32_t frame, + IPAFrameContext &frameContext, + const ControlList &controls) +{ + gammaAlgo_.queueRequest(context.activeState.gamma, frame, + frameContext.gamma, controls); +} + /** * \brief Fill in the parameter structure, and enable gamma control * \param[in] context The shared IPA context @@ -59,14 +77,21 @@ int ToneMapping::configure(IPAContext &context, */ void ToneMapping::prepare([[maybe_unused]] IPAContext &context, [[maybe_unused]] const uint32_t frame, - [[maybe_unused]] IPAFrameContext &frameContext, + IPAFrameContext &frameContext, ipu3_uapi_params *params) { - /* Copy the calculated LUT into the parameters buffer. */ - memcpy(params->acc_param.gamma.gc_lut.lut, - context.activeState.toneMapping.gammaCorrection.lut, - IPU3_UAPI_GAMMA_CORR_LUT_ENTRIES * - sizeof(params->acc_param.gamma.gc_lut.lut[0])); + if (!frameContext.gamma.update) + return; + + /* + * Unfortunately necessary given the IPU3's gamma uAPI struct has the + * __packed attribute. + */ + uint16_t *lutData = reinterpret_cast( + __builtin_assume_aligned(params->acc_param.gamma.gc_lut.lut, 16)); + Span lut {lutData, kNumLutNodes}; + + gammaAlgo_.prepare(frameContext.gamma, lut); /* Enable the custom gamma table. */ params->use.acc_gamma = 1; @@ -84,33 +109,13 @@ void ToneMapping::prepare([[maybe_unused]] IPAContext &context, * The tone mapping look up table is generated as an inverse power curve from * our gamma setting. */ -void ToneMapping::process(IPAContext &context, [[maybe_unused]] const uint32_t frame, - [[maybe_unused]] IPAFrameContext &frameContext, +void ToneMapping::process([[maybe_unused]] IPAContext &context, + [[maybe_unused]] const uint32_t frame, + IPAFrameContext &frameContext, [[maybe_unused]] const ipu3_uapi_stats_3a *stats, [[maybe_unused]] ControlList &metadata) { - /* - * Hardcode gamma to 1.1 as a default for now. - * - * \todo Expose gamma control setting through the libcamera control API - */ - gamma_ = 1.1; - - if (context.activeState.toneMapping.gamma == gamma_) - return; - - struct ipu3_uapi_gamma_corr_lut &lut = - context.activeState.toneMapping.gammaCorrection; - - for (uint32_t i = 0; i < std::size(lut.lut); i++) { - double j = static_cast(i) / (std::size(lut.lut) - 1); - double gamma = std::pow(j, 1.0 / gamma_); - - /* The output value is expressed on 13 bits. */ - lut.lut[i] = gamma * 8191; - } - - context.activeState.toneMapping.gamma = gamma_; + gammaAlgo_.process(frameContext.gamma, metadata); } REGISTER_IPA_ALGORITHM(ToneMapping, "ToneMapping") diff --git a/src/ipa/ipu3/algorithms/tone_mapping.h b/src/ipa/ipu3/algorithms/tone_mapping.h index b2b380108e014b3d5ee7b93bcdd948dea4d2302d..db351a32b4383c4d607a2d6ee6a8fa3994a1d436 100644 --- a/src/ipa/ipu3/algorithms/tone_mapping.h +++ b/src/ipa/ipu3/algorithms/tone_mapping.h @@ -7,6 +7,8 @@ #pragma once +#include + #include "algorithm.h" namespace libcamera { @@ -18,7 +20,11 @@ class ToneMapping : public Algorithm public: ToneMapping(); + int init(IPAContext &context, const ValueNode &tuningData) override; int configure(IPAContext &context, const IPAConfigInfo &configInfo) override; + void queueRequest(IPAContext &context, const uint32_t frame, + IPAFrameContext &frameContext, + const ControlList &controls) override; void prepare(IPAContext &context, const uint32_t frame, IPAFrameContext &frameContext, ipu3_uapi_params *params) override; void process(IPAContext &context, const uint32_t frame, @@ -27,7 +33,8 @@ public: ControlList &metadata) override; private: - double gamma_; + static constexpr unsigned int kNumLutNodes = IPU3_UAPI_GAMMA_CORR_LUT_ENTRIES; + GammaAlgorithm> gammaAlgo_; }; } /* namespace ipa::ipu3::algorithms */ diff --git a/src/ipa/ipu3/ipa_context.cpp b/src/ipa/ipu3/ipa_context.cpp index dc43bc0877ed5c2e7287414e12667374f5ee1c80..7152d070d3ab1bc463fdaad437d5e1c1b87ce25c 100644 --- a/src/ipa/ipu3/ipa_context.cpp +++ b/src/ipa/ipu3/ipa_context.cpp @@ -119,6 +119,11 @@ namespace libcamera::ipa::ipu3 { * \brief Active colour Correction Matrix parameters for the IPA */ +/** + * \var IPAActiveState::gamma + * \brief Active gamma correction parameters for the IPA + */ + /** * \var IPASessionConfiguration::sensor * \brief Sensor-specific configuration of the IPA @@ -154,20 +159,6 @@ namespace libcamera::ipa::ipu3 { * The gain should be adapted to the sensor specific gain code before applying. */ -/** - * \var IPAActiveState::toneMapping - * \brief Context for ToneMapping and Gamma control - * - * \var IPAActiveState::toneMapping.gamma - * \brief Gamma value for the LUT - * - * \var IPAActiveState::toneMapping.gammaCorrection - * \brief Per-pixel tone mapping implemented as a LUT - * - * The LUT structure is defined by the IPU3 kernel interface. See - * struct ipu3_uapi_gamma_corr_lut for further details. - */ - /** * \struct IPAFrameContext * \brief IPU3-specific FrameContext @@ -192,4 +183,9 @@ namespace libcamera::ipa::ipu3 { * \brief Per-frame colour Correction Matrix parameters for the IPA */ +/** + * \var IPAFrameContext::gamma + * \brief Per-frame gamma correction parameters for the IPA + */ + } /* namespace libcamera::ipa::ipu3 */ diff --git a/src/ipa/ipu3/ipa_context.h b/src/ipa/ipu3/ipa_context.h index be626d30d966b1bdaa322e5154f95f745f799976..1eaaac82da0e3ad5bed0749c39d9dad8c585cab0 100644 --- a/src/ipa/ipu3/ipa_context.h +++ b/src/ipa/ipu3/ipa_context.h @@ -18,6 +18,7 @@ #include #include #include +#include namespace libcamera { @@ -66,11 +67,7 @@ struct IPAActiveState { ipa::awb::ActiveState awb; ipa::ccm::ActiveState ccm; - - struct { - double gamma; - struct ipu3_uapi_gamma_corr_lut gammaCorrection; - } toneMapping; + ipa::gamma::ActiveState gamma; }; struct IPAFrameContext : public FrameContext { @@ -81,6 +78,7 @@ struct IPAFrameContext : public FrameContext { ipa::awb::FrameContext awb; ipa::ccm::FrameContext ccm; + ipa::gamma::FrameContext gamma; }; struct IPAContext { From patchwork Tue Jun 16 06:41:41 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Dan Scally X-Patchwork-Id: 26898 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 72CEEC328C for ; Tue, 16 Jun 2026 06:42:04 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id C87F6625AB; Tue, 16 Jun 2026 08:41:58 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="fKYSCP/r"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 560A9623F6 for ; Tue, 16 Jun 2026 08:41:50 +0200 (CEST) Received: from [127.0.1.1] (chfd-03-b2-v4wan-176392-cust229.vm15.cable.virginm.net [82.19.20.230]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 1901D217B; Tue, 16 Jun 2026 08:41:17 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1781592077; bh=/T0uF0Ne1RRm41R7wE/GbUTiIfeSWqME44nIF91/iKw=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=fKYSCP/rTr28kOCyGmQ3yHQVemkpXlEE/0SNhWgNy8DBcdujcek2VULh58H0+NfWV +0PqwuxhuuikOQLpl0tzDTUqj7YmSnZjZZ3bb65Q1LY/3Oxb6rYw2DXHZezeW0ucu8 8J5eLntdBngjHqLxY7gdwYVIu5S4blAxYgixx6DY= From: Daniel Scally Date: Tue, 16 Jun 2026 07:41:41 +0100 Subject: [PATCH 07/10] ipa: ipu3: Add Ccm to uncalibrated.yaml MIME-Version: 1.0 Message-Id: <20260616-ipu3-libipa-rework-v1-7-d4448b54f1d8@ideasonboard.com> References: <20260616-ipu3-libipa-rework-v1-0-d4448b54f1d8@ideasonboard.com> In-Reply-To: <20260616-ipu3-libipa-rework-v1-0-d4448b54f1d8@ideasonboard.com> To: libcamera-devel@lists.libcamera.org Cc: Daniel Scally X-Mailer: b4 0.14.2 X-Developer-Signature: v=1; a=openpgp-sha256; l=680; i=dan.scally@ideasonboard.com; h=from:subject:message-id; bh=/T0uF0Ne1RRm41R7wE/GbUTiIfeSWqME44nIF91/iKw=; b=owEBbQKS/ZANAwAKAchJV3psRXUyAcsmYgBqMPAqV+7hnLHa1A8+pO3EHAQF37RrICGEubgQa wMDl4PRqS+JAjMEAAEKAB0WIQQqyuwyDnZdb+mxmm/ISVd6bEV1MgUCajDwKgAKCRDISVd6bEV1 MngOD/9aOZ0Ux8cinmDz8QcjeWfiaKbiyQfagYe0hurjLh0ieHKS65QSpZ2kv1/LDsUHWVF6KWT bXuJH0eKUlZ1amGItuVQLToSza1vXkXCMdhddRleRMSdrsIUP5DMgORE9uj0gdStXpOhEx6Kxsk 3rr2zyV+d/OxHj/B0QPF91AFe3xaJYVYAkumew5Amf0jAOwOrNgHnSPoe/XvdBiDtlIgvXp4zA8 g1O+6mgpqH2PfRHhHRhBsv3e18Pm4S3e5JJgK6ZOtwXJc+peMHSYXITtxJVS+UavD9BPGmd5ZQq WO0zVPDH6b48ypTKd/IkOLTwL+xXtRMj5JZIYCHKUEO1XL2L1OBxAdppkV6Wf4MWdYBQnyl6l5p /XXNEfnOCAsGtmzMJSZ1ug8GIsRjy/ywI0gH4g7sSSTBXgsL5aqit9IY6GEinKsF9r5mpeDOEW1 UL4sUOMox0mx7TGd7LRxKlJ1iJxD0JBsMkskCUU2UKAh30CsSUv5nL4RnWr88ylnhSKPn++6jN7 RMWOfZ7/gtFzoR4wfzmVL2Q7Y9vIwxhXeASJCOvbn6fBpZT6hPjSPhTFCpBlMppT+uS0L9Vyx2r PBCns9+ymTSHxE7NU8RwCieqDV1hDhxywpR3dzvKO+M05gnJxUZYBEeFsOsY3qNOPnUX1k5wni4 MpEVPBeIUr2Y46A== X-Developer-Key: i=dan.scally@ideasonboard.com; a=openpgp; fpr=EEC699ACA1B7CB5D31330C0BBD501C2A3546CCF6 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" Add the Ccm algorithm to uncalibrated.yaml. Even without calibrated values we should still expose the controls to the application. Signed-off-by: Daniel Scally --- src/ipa/ipu3/data/uncalibrated.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/src/ipa/ipu3/data/uncalibrated.yaml b/src/ipa/ipu3/data/uncalibrated.yaml index 794ab3edbb778bbe14c2dc65e5540413ee7fd536..415ca4578fab57ea731493c978a04e740946197f 100644 --- a/src/ipa/ipu3/data/uncalibrated.yaml +++ b/src/ipa/ipu3/data/uncalibrated.yaml @@ -7,5 +7,6 @@ algorithms: - Agc: - Awb: - BlackLevelCorrection: + - Ccm: - ToneMapping: ... From patchwork Tue Jun 16 06:41:42 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Dan Scally X-Patchwork-Id: 26899 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 E7C1BC3303 for ; Tue, 16 Jun 2026 06:42:04 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 77239625B2; Tue, 16 Jun 2026 08:41:59 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="Gc6e3V5I"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [IPv6:2001:4b98:dc2:55:216:3eff:fef7:d647]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 8D92961E76 for ; Tue, 16 Jun 2026 08:41:50 +0200 (CEST) Received: from [127.0.1.1] (chfd-03-b2-v4wan-176392-cust229.vm15.cable.virginm.net [82.19.20.230]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 4E48D166C; Tue, 16 Jun 2026 08:41:17 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1781592077; bh=ESK//rvtSLlKOvKdItZX6TsAh8BUgChpDfTIaQMnrjQ=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=Gc6e3V5IsVZZyDd0FK+Pg+uuO6a4yDy0I01HV3QIX35ZAXoLekQB/ER2EKY3E5IdB KSbpcQoTWSHMcfK0mqWhXHt+P2mE1lgJWpGX1VA7E8Yr5QKN0Wj/ut+T8dZTar2D9u n5Ccg/GK3QC/gadoX2SA6kTamA2GOMtw6IbV4PJU= From: Daniel Scally Date: Tue, 16 Jun 2026 07:41:42 +0100 Subject: [PATCH 08/10] ipa: rkisp1: goc: Re-work to use GammaAlgorithm class MIME-Version: 1.0 Message-Id: <20260616-ipu3-libipa-rework-v1-8-d4448b54f1d8@ideasonboard.com> References: <20260616-ipu3-libipa-rework-v1-0-d4448b54f1d8@ideasonboard.com> In-Reply-To: <20260616-ipu3-libipa-rework-v1-0-d4448b54f1d8@ideasonboard.com> To: libcamera-devel@lists.libcamera.org Cc: Daniel Scally X-Mailer: b4 0.14.2 X-Developer-Signature: v=1; a=openpgp-sha256; l=5638; i=dan.scally@ideasonboard.com; h=from:subject:message-id; bh=ESK//rvtSLlKOvKdItZX6TsAh8BUgChpDfTIaQMnrjQ=; b=owEBbQKS/ZANAwAKAchJV3psRXUyAcsmYgBqMPAq4Nb9WiPbeVxnUeGsKmpurxIJ4586okxI9 LDbidSvLyWJAjMEAAEKAB0WIQQqyuwyDnZdb+mxmm/ISVd6bEV1MgUCajDwKgAKCRDISVd6bEV1 MvHPD/9nTmMvcZUxDlC5wAgSKSbcSj50YohnY4Ic2QefXn3IdEUjfn6Y1oQLIbLksm+fhWFOfkq wZWbtVY/6DFqaY7GN321xFIZsQORxAJRMUj1I10VbXusdAo68yEgY4HYnP4UIdnpKIe3hZS69yR IIm0WlVaWBx3u7DZB6k+44KHCyo34MtWTI4TzPcQJfepZ8aSH8C7o/VYHCdikWOhRAtIGGI+20B ciIOHOoZ+6L6sjza+0swff7DKQ+yt/sSmZbCPmhXcVGR9BWF80DS1UQ22F6DAAuhby+ODBNCnE+ iXgXSQpltrIo9NhGOEMTyXM3EWTyxCoFG5gsgnOB6GAYxAH22pB0wqbJX7GF07gpI+L2oeEZHkc yNKhTc/qQ/8RPkAAv+rhxIw+OC23Tv2nRGyjvjpqQ9QMmpTzba9zeF3XbyMzuASNgLGkH33t11U QcM7WTBeRgKkMFsckZ1/ZJYSzvHA+NrOTfp+xO47vW4xH1MvT6oKrWxYVIAgk+wDHqtYZ9mvk+y MvnIX/aIap+1SsLZowKGrvFkyKV52Q5po4zchfrkSRsOs6HsKMER/gGF3dkucXOcF6iC+QQ9zKc Nu1Lfuc0LPSdEEnH9KjuG6bSJzbxN13RdsCbgTqA1oTP+Gv9oTGzh+mfyUQ5QwyiyXMZgDd05iO jDb1++CMSES2URg== X-Developer-Key: i=dan.scally@ideasonboard.com; a=openpgp; fpr=EEC699ACA1B7CB5D31330C0BBD501C2A3546CCF6 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" Re-work the RkISP1 Gamma Out Correction algorithm to use the new GammaAlgorithm class from libipa, which allows us to share an implementation with the other IPAs. Signed-off-by: Daniel Scally --- src/ipa/rkisp1/algorithms/goc.cpp | 49 +++++++++++---------------------------- src/ipa/rkisp1/algorithms/goc.h | 5 +++- src/ipa/rkisp1/ipa_context.h | 10 +++----- 3 files changed, 21 insertions(+), 43 deletions(-) diff --git a/src/ipa/rkisp1/algorithms/goc.cpp b/src/ipa/rkisp1/algorithms/goc.cpp index e8f64bf3d5e028c2013d37b7ef9fe90b737fad5f..1afd7fb9538d85f771c5b743b856a4963ec8665c 100644 --- a/src/ipa/rkisp1/algorithms/goc.cpp +++ b/src/ipa/rkisp1/algorithms/goc.cpp @@ -9,7 +9,7 @@ #include #include -#include +#include #include @@ -44,6 +44,7 @@ namespace ipa::rkisp1::algorithms { LOG_DEFINE_CATEGORY(RkISP1Gamma) const float kDefaultGamma = 2.2f; +static constexpr unsigned int kNumLutSegments = RKISP1_CIF_ISP_GAMMA_OUT_MAX_SAMPLES_V10 - 1; /** * \copydoc libcamera::ipa::Algorithm::init @@ -57,10 +58,12 @@ int GammaOutCorrection::init(IPAContext &context, const ValueNode &tuningData) return -EINVAL; } - defaultGamma_ = tuningData["gamma"].get(kDefaultGamma); - context.ctrlMap[&controls::Gamma] = ControlInfo(0.1f, 10.0f, defaultGamma_); + std::array segments = { + 64, 64, 64, 64, 128, 128, 128, 128, + 256, 256, 256, 512, 512, 512, 512, 512 + }; - return 0; + return gammaAlgo_.init(context.ctrlMap, tuningData, segments); } /** @@ -69,7 +72,7 @@ int GammaOutCorrection::init(IPAContext &context, const ValueNode &tuningData) int GammaOutCorrection::configure(IPAContext &context, [[maybe_unused]] const IPACameraSensorInfo &configInfo) { - context.activeState.goc.gamma = defaultGamma_; + gammaAlgo_.configure(context.activeState.gamma); return 0; } @@ -80,17 +83,8 @@ void GammaOutCorrection::queueRequest(IPAContext &context, const uint32_t frame, IPAFrameContext &frameContext, const ControlList &controls) { - if (frame == 0) - frameContext.goc.update = true; - - const auto &gamma = controls.get(controls::Gamma); - if (gamma) { - context.activeState.goc.gamma = *gamma; - frameContext.goc.update = true; - LOG(RkISP1Gamma, Debug) << "Set gamma to " << *gamma; - } - - frameContext.goc.gamma = context.activeState.goc.gamma; + gammaAlgo_.queueRequest(context.activeState.gamma, frame, + frameContext.gamma, controls); } /** @@ -104,29 +98,14 @@ void GammaOutCorrection::prepare(IPAContext &context, ASSERT(context.hw.numGammaOutSamples == RKISP1_CIF_ISP_GAMMA_OUT_MAX_SAMPLES_V10); - if (!frameContext.goc.update) + if (!frameContext.gamma.update) return; - /* - * The logarithmic segments as specified in the reference. - * Plus an additional 0 to make the loop easier - */ - static constexpr std::array segments = { - 64, 64, 64, 64, 128, 128, 128, 128, 256, - 256, 256, 512, 512, 512, 512, 512, 0 - }; - auto config = params->block(); config.setEnabled(true); - __u16 *gamma_y = config->gamma_y; - - unsigned x = 0; - for (const auto [i, size] : utils::enumerate(segments)) { - gamma_y[i] = std::pow(x / 4096.0, 1.0 / frameContext.goc.gamma) * 1023.0; - x += size; - } - + Span lut{ config->gamma_y }; + gammaAlgo_.prepare(frameContext.gamma, lut); config->mode = RKISP1_CIF_ISP_GOC_MODE_LOGARITHMIC; } @@ -139,7 +118,7 @@ void GammaOutCorrection::process([[maybe_unused]] IPAContext &context, [[maybe_unused]] const rkisp1_stat_buffer *stats, ControlList &metadata) { - metadata.set(controls::Gamma, frameContext.goc.gamma); + gammaAlgo_.process(frameContext.gamma, metadata); } REGISTER_IPA_ALGORITHM(GammaOutCorrection, "GammaOutCorrection") diff --git a/src/ipa/rkisp1/algorithms/goc.h b/src/ipa/rkisp1/algorithms/goc.h index bd79fe19cc86b8aefa2603e98e9d7130b44105d9..f7059d206c935fa72195f36091cf953e5f2413e1 100644 --- a/src/ipa/rkisp1/algorithms/goc.h +++ b/src/ipa/rkisp1/algorithms/goc.h @@ -9,6 +9,9 @@ #include "algorithm.h" +#include +#include + namespace libcamera { namespace ipa::rkisp1::algorithms { @@ -35,7 +38,7 @@ public: ControlList &metadata) override; private: - float defaultGamma_; + GammaAlgorithm> gammaAlgo_; }; } /* namespace ipa::rkisp1::algorithms */ diff --git a/src/ipa/rkisp1/ipa_context.h b/src/ipa/rkisp1/ipa_context.h index 005f4102b4f6d465fd370c20060331a4f3b3a943..03be79085052d5161ec57d97fe9600f4bf7e93c4 100644 --- a/src/ipa/rkisp1/ipa_context.h +++ b/src/ipa/rkisp1/ipa_context.h @@ -30,6 +30,7 @@ #include "libipa/ccm.h" #include "libipa/fc_queue.h" #include "libipa/fixedpoint.h" +#include "libipa/gamma.h" #include "libipa/lsc.h" namespace libcamera { @@ -124,9 +125,7 @@ struct IPAActiveState { uint8_t sharpness; } filter; - struct { - double gamma; - } goc; + ipa::gamma::ActiveState gamma; struct { double lux; @@ -190,10 +189,7 @@ struct IPAFrameContext : public FrameContext { bool update; } filter; - struct { - double gamma; - bool update; - } goc; + ipa::gamma::FrameContext gamma; struct { uint32_t exposure; From patchwork Tue Jun 16 06:41:43 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Dan Scally X-Patchwork-Id: 26900 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 60084C3304 for ; Tue, 16 Jun 2026 06:42:05 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 8C00C62405; Tue, 16 Jun 2026 08:42:00 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="oVn8tQpl"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [IPv6:2001:4b98:dc2:55:216:3eff:fef7:d647]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id C55DF62405 for ; Tue, 16 Jun 2026 08:41:50 +0200 (CEST) Received: from [127.0.1.1] (chfd-03-b2-v4wan-176392-cust229.vm15.cable.virginm.net [82.19.20.230]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 87EE6217B; Tue, 16 Jun 2026 08:41:17 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1781592077; bh=WBiLUavGVYS+agA29NNKFx8Asd82s77zRY361/HLD1s=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=oVn8tQpl65IJJ6/kK3Gx73ZlaiqDye3jApNnJO6s2tp3IjtGLys+W0eaRHruzqmIQ GAzYVx8wtmYJ6/QR8qzEx2YnanUX/w9UZoxIsnZvxc2+Ho5J21J03AwXhzCESwSivf gyYuy6uT86D5RcU6NUOp+T71AYqY7xiy1+Xefpco= From: Daniel Scally Date: Tue, 16 Jun 2026 07:41:43 +0100 Subject: [PATCH 09/10] ipa: ipu3: Add IPACameraSensorInfo to IPAContext MIME-Version: 1.0 Message-Id: <20260616-ipu3-libipa-rework-v1-9-d4448b54f1d8@ideasonboard.com> References: <20260616-ipu3-libipa-rework-v1-0-d4448b54f1d8@ideasonboard.com> In-Reply-To: <20260616-ipu3-libipa-rework-v1-0-d4448b54f1d8@ideasonboard.com> To: libcamera-devel@lists.libcamera.org Cc: Daniel Scally X-Mailer: b4 0.14.2 X-Developer-Signature: v=1; a=openpgp-sha256; l=2132; i=dan.scally@ideasonboard.com; h=from:subject:message-id; bh=WBiLUavGVYS+agA29NNKFx8Asd82s77zRY361/HLD1s=; b=owEBbQKS/ZANAwAKAchJV3psRXUyAcsmYgBqMPAqlHKIvBEm9cm+1tluERsnTu9/CyS0CwlCd n8xLnGarGSJAjMEAAEKAB0WIQQqyuwyDnZdb+mxmm/ISVd6bEV1MgUCajDwKgAKCRDISVd6bEV1 MkgxD/4oAM9N87by9UsgTRjiJb3iThu1amiOtTMH+ehwbADzVG50cvVS0f+s6aUKFMRQxWxnuOm bQ3OMUjjyIEfciQqz3idvPkOV70x72bog8zaK4mbph6wsg6HLa/U3hhdMyhnrTwQOMwfKPhkfV3 qnmd+8l+yvMPFskKu4dkyXBTMCQFNrDruqO9uVwRdJWf04E5Q2SWM+iQtbBDiLtzr69J2L6SHdZ oXnjo67QrxpN3U8VRBYT12QrcuvaEtxOj63qz7MBffVraG/RZ/UUlg8UdSfjEiDaNJIEmbB8Qki 1KmcHHT+utJYptRotTlV3qAmJNLEIREb8mEu5qAC9nAjzMa7cBfRaHvMVsltdfQEgU/TS4FEQe4 NXOOf5DSaa8MRpQM/bMyaCH8j66TXJogA1IriLnu2r2VA+xOKv55MudduWw3OTJUmo4O11iTf7S T6semhyvwjI8Kp2dmjIeZt4Bi68Ctv3Tb7RFJP1s4F9X+AcOISy99ZZVGgCq/rTkGN+mmsszj0z xKd2G7w4JqPvqWWxp59xHTLHSAA40ltO0pBKl8pTUPjN3+GaxRPM/PR9db9vkPvZoOMeMrBjWHb YO0HnSS9X0aB/p/R0A1S0MA7yEVlNNS0AhKJaNkfy9XdQlIp5Q5tQ2RD6H81hzmPxB5tHscIMKj 6B+EeEvEpqIVHbw== X-Developer-Key: i=dan.scally@ideasonboard.com; a=openpgp; fpr=EEC699ACA1B7CB5D31330C0BBD501C2A3546CCF6 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" Add an instance of IPACameraSensorInfo to the IPAContext and fill it during IPAIPU3::init(). This will allow us to pass sensor information to algorithms at init time. Signed-off-by: Daniel Scally --- src/ipa/ipu3/ipa_context.cpp | 3 +++ src/ipa/ipu3/ipa_context.h | 3 +++ src/ipa/ipu3/ipu3.cpp | 1 + 3 files changed, 7 insertions(+) diff --git a/src/ipa/ipu3/ipa_context.cpp b/src/ipa/ipu3/ipa_context.cpp index 7152d070d3ab1bc463fdaad437d5e1c1b87ce25c..7fcfd5e0e4ade92521cc2914dd07113235af8e45 100644 --- a/src/ipa/ipu3/ipa_context.cpp +++ b/src/ipa/ipu3/ipa_context.cpp @@ -54,6 +54,9 @@ namespace libcamera::ipa::ipu3 { * * \var IPAContext::ctrlMap * \brief A ControlInfoMap::Map of controls populated by the algorithms + * + * \var IPAContext::sensorInfo + * \brief An IPACameraSensorInfo instance holding information about the sensor */ /** diff --git a/src/ipa/ipu3/ipa_context.h b/src/ipa/ipu3/ipa_context.h index 1eaaac82da0e3ad5bed0749c39d9dad8c585cab0..f157f223cbb3119f108d768b14fca514ac5661ca 100644 --- a/src/ipa/ipu3/ipa_context.h +++ b/src/ipa/ipu3/ipa_context.h @@ -15,6 +15,8 @@ #include #include +#include + #include #include #include @@ -93,6 +95,7 @@ struct IPAContext { FCQueue frameContexts; ControlInfoMap::Map ctrlMap; + IPACameraSensorInfo sensorInfo; }; } /* namespace ipa::ipu3 */ diff --git a/src/ipa/ipu3/ipu3.cpp b/src/ipa/ipu3/ipu3.cpp index 4bdc4b7677fe9703fb968ca91967c68742454514..0827ad207c9d89b8af4887f609fe08881612ab96 100644 --- a/src/ipa/ipu3/ipu3.cpp +++ b/src/ipa/ipu3/ipu3.cpp @@ -313,6 +313,7 @@ int IPAIPU3::init(const IPASettings &settings, context_.configuration = {}; context_.configuration.sensor.lineDuration = sensorInfo.minLineLength * 1.0s / sensorInfo.pixelRate; + context_.sensorInfo = sensorInfo; /* Load the tuning data file. */ File file(settings.configurationFile); From patchwork Tue Jun 16 06:41:44 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Dan Scally X-Patchwork-Id: 26901 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 DD4FCBF415 for ; Tue, 16 Jun 2026 06:42:05 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 424E6625BD; Tue, 16 Jun 2026 08:42:01 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="GHKn97d1"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 2B30C623FC for ; Tue, 16 Jun 2026 08:41:51 +0200 (CEST) Received: from [127.0.1.1] (chfd-03-b2-v4wan-176392-cust229.vm15.cable.virginm.net [82.19.20.230]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id BE097166C; Tue, 16 Jun 2026 08:41:17 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1781592077; bh=IAQrKq63l2GMTBls3JxFXlAC9BHKn37LmkWDTjL3tmM=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=GHKn97d1vlj0XHNDgzBG5mDObNIGJZL1Tag/POEAInijIJPpT0qFyZgyzuLWaYxwe ekUpd23oQmrB4WhDLa3CcRhapWB0Kh7zuNu34CrttyN4t1LtiGpf8bd4GhcDQ5T9S+ DjVo0k/7GYArBz+4l+VHWjd5sAIFfu9jPKdohf/s= From: Daniel Scally Date: Tue, 16 Jun 2026 07:41:44 +0100 Subject: [PATCH 10/10] ipa: ipu3: Add Lens Shading Correction algorithm MIME-Version: 1.0 Message-Id: <20260616-ipu3-libipa-rework-v1-10-d4448b54f1d8@ideasonboard.com> References: <20260616-ipu3-libipa-rework-v1-0-d4448b54f1d8@ideasonboard.com> In-Reply-To: <20260616-ipu3-libipa-rework-v1-0-d4448b54f1d8@ideasonboard.com> To: libcamera-devel@lists.libcamera.org Cc: Daniel Scally X-Mailer: b4 0.14.2 X-Developer-Signature: v=1; a=openpgp-sha256; l=14890; i=dan.scally@ideasonboard.com; h=from:subject:message-id; bh=IAQrKq63l2GMTBls3JxFXlAC9BHKn37LmkWDTjL3tmM=; b=owEBbQKS/ZANAwAKAchJV3psRXUyAcsmYgBqMPAq5Wqd7pFh3ch6GtXWD4sG8dC1pF828CBiI x28NYAMjMuJAjMEAAEKAB0WIQQqyuwyDnZdb+mxmm/ISVd6bEV1MgUCajDwKgAKCRDISVd6bEV1 MsUuEACnaJktsYMw4MzkCJ8+1ndl0S/ZWEVr8gO9XjiWSYvhu6joZ+DaLlX57eqD6WhMVb479xA f7te8XFKuDQSj50AFOTRMMtbIpgTAQRu/+O/MQOXK5GTAuDvz45iJT+G9cGNIHaGB2Q44tTIWFr nCpKSkY7ThK4UF9+1madRdQkCXnUKEUv/fR4QVMdpjafxiB8B45n2Bw8NHgppxPKaumDEAE1hkE 6g0/xOfzV3BBd7I1WKg0PYXFQaAcvJzOyajboAjHrwBFzH/OfgBUFIAxp8NcbzkWZ3xGlc8Z9lK RJjZKZbnZSVWGaCnBzDtxEXGg9jcjgt01tXnADJmkwPL6e5RU8AiDmNVEA1ksCaJFWNc4vLzpkU LZfMF/P2/cfvsblKOji8DaeZOsbcC9puDtzU2S6IqpI3aW0T6sdPhXUvm9lXJO2/njG9o1kxJ3n vd+kLWv/JTwTBY4S0H4Apg/mQkZqeQVuWiEnd+73eaySxI79NeGwuoJhtNYNLa9w5McpL9O6f5G R0fOW0whyw8VhqPnvBXBUM1qgWLi33UcjPHhlwgkws/6H02rEs6j+HRoWwhfiqLTXHGLbi0p1ZL v6Xy5ExR/Ay6PJPUpXs4BO+QnF8bfKjIp3NX1z9qT7RfWdn1QdhPCMWiLFz5b0R3W/witrXM2Aj D1V8NlJjLyb/Sxw== X-Developer-Key: i=dan.scally@ideasonboard.com; a=openpgp; fpr=EEC699ACA1B7CB5D31330C0BBD501C2A3546CCF6 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" Add a lens shading correction algorithm for the IPU3 IPA, using the recently implemented libipa base algorithm class. Signed-off-by: Daniel Scally --- src/ipa/ipu3/algorithms/lsc.cpp | 290 ++++++++++++++++++++++++++++++++++++ src/ipa/ipu3/algorithms/lsc.h | 58 ++++++++ src/ipa/ipu3/algorithms/meson.build | 1 + src/ipa/ipu3/ipa_context.cpp | 10 ++ src/ipa/ipu3/ipa_context.h | 3 + 5 files changed, 362 insertions(+) diff --git a/src/ipa/ipu3/algorithms/lsc.cpp b/src/ipa/ipu3/algorithms/lsc.cpp new file mode 100644 index 0000000000000000000000000000000000000000..70f8e59b9a9238c44f96d4a2a31bd23a8377f662 --- /dev/null +++ b/src/ipa/ipu3/algorithms/lsc.cpp @@ -0,0 +1,290 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2026, Ideas on Board, Oy + * + * IPU3 Lens Shading Correction algorithm + */ + +#include "lsc.h" + +/** + * \file lsc.h + */ + +namespace libcamera { + +namespace ipa::ipu3::algorithms { + +/** + * \class Lsc + * \brief IPU3 Lens Shading Correction algorithm + */ + +LOG_DEFINE_CATEGORY(IPU3Lsc) + +static constexpr unsigned int kMaxNumHCells = 73; +static constexpr unsigned int kMaxNumVCells = 56; +static constexpr int kColourTemperatureQuantization = 10; + +/** + * \copydoc libcamera::ipa::Algorithm::init + */ +int Lsc::init(IPAContext &context, const ValueNode &tuningData) +{ + /* + * The IPU3 lens shading block expects a table of data that isn't of a + * fixed size, but rather is configurable based on 4 parameters: + * + * block_width_log2: The log2 of the horizontal pixel count per cell + * block_height_log2: The log2 of the vertical pixel count per cell + * width: The number of horizontal cells + * height: The number of vertical cells + * + * The constructed grid should be capable of covering the image, but + * ideally won't extend past the edges of the image. Fixing either set + * of parameters for the algorithm as a whole is likely to result in + * suboptimal situations for some sensors, so let's determine them + * programmatically instead. + * + * What we want is the densest possible grid that ideally doesn't extend + * past the edges of the image at all. The maximum grid size is 73x56, + * which gives us the lower bounds on cell size. Unfortunately we can + * only specify sizes in powers of two, which can have the effect of + * making the grid much more coarse. For example for a 2592x1944 input + * image, 2592 / 73 = 35.5...which means we need to set blockWidthLog2 + * to 6 (I.E. 64) and have just 40.5 (or rather 41) cells horizontally. + */ + sensorWidth_ = context.sensorInfo.activeAreaSize.width; + sensorHeight_ = context.sensorInfo.activeAreaSize.height; + + unsigned int cellWidth = (sensorWidth_ + kMaxNumHCells - 1) / kMaxNumHCells; + unsigned int cellHeight = (sensorHeight_ + kMaxNumVCells - 1) / kMaxNumVCells; + + unsigned int minCellWidth = std::bit_ceil(cellWidth); + unsigned int minCellHeight = std::bit_ceil(cellHeight); + + numHCells_ = (sensorWidth_ + minCellWidth - 1) / minCellWidth; + numVCells_ = (sensorHeight_ + minCellHeight - 1) / minCellHeight; + + blockWidthLog2_ = std::bit_width(minCellWidth) - 1; + blockHeightLog2_ = std::bit_width(minCellHeight) - 1; + + LOG(IPU3Lsc, Debug) << "Calculated Grid configuration: " + << numHCells_ << "x" << numVCells_ << " cells of " + << minCellWidth << "x" << minCellHeight << " pixels"; + + /* + * We need to know if we're running the polynomial algorithm or not as + * things will behave slightly differently. + */ + polynomial_ = tuningData["polynomial"].get(false); + + return lscAlgo_.init(tuningData, context.ctrlMap, { + .keys = { "r", "gr", "gb", "b" }, + .numHCells = numHCells_, + .numVCells = numVCells_, + .sensorSize = context.sensorInfo.activeAreaSize + }); +} + +std::vector Lsc::calculatePositions(unsigned int dimension) +{ + std::vector positions(dimension); + for (double i = 0.0; i < dimension; i++) + positions[i] = i / (dimension - 1); + + return positions; +} + +/** + * \copydoc libcamera::ipa::Algorithm::configure + */ +int Lsc::configure(IPAContext &context, const IPAConfigInfo &configInfo) +{ + cropWidth_ = configInfo.sensorInfo.analogCrop.width; + cropHeight_ = configInfo.sensorInfo.analogCrop.height; + std::vector xPos = calculatePositions(numHCells_); + std::vector yPos = calculatePositions(numVCells_); + + return lscAlgo_.configure(context.activeState.lsc, + configInfo.sensorInfo.analogCrop, xPos, yPos); +} + +/** + * \copydoc libcamera::ipa::Algorithm::queueRequest + */ +void Lsc::queueRequest(IPAContext &context, const uint32_t frame, + IPAFrameContext &frameContext, + const ControlList &controls) +{ + /* + * The base algorithm defines the LensShadingCorrectionEnable control + * with a default value of true, but actually the IPU3 driver defaults + * it to off. If this is the first frame, check for the control, but if + * there isn't one, force it on to fulfil the advertised default. + */ + if (frame == 0) { + const auto &lscEnable = controls.get(controls::LensShadingCorrectionEnable); + if (!lscEnable) { + frameContext.lsc.enabled = true; + frameContext.lsc.update = true; + } + } + + lscAlgo_.queueRequest(context.activeState.lsc, frameContext.lsc, controls); +} + +static unsigned int quantize(unsigned int value, unsigned int step) +{ + return std::lround(value / static_cast(step)) * step; +} + +/** + * \copydoc libcamera::ipa::Algorithm::prepare + */ +void Lsc::prepare([[maybe_unused]] IPAContext &context, [[maybe_unused]] const uint32_t frame, + IPAFrameContext &frameContext, ipu3_uapi_params *params) +{ + uint32_t ct = frameContext.awb.temperatureK; + unsigned int quantizedCt = quantize(ct, kColourTemperatureQuantization); + + if (!frameContext.lsc.update) { + if (!frameContext.lsc.enabled) + return; + + /* + * Add a threshold so that oscillations around a quantization + * step don't lead to constant changes. + */ + if (utils::abs_diff(ct, lastAppliedCt_) < kColourTemperatureQuantization / 2) + return; + + if (quantizedCt == lastAppliedQuantizedCt_) + return; + } + + /* + * This flag tells the kernel driver that it should read the LSC params + * passed from userspace instead of using its cached copy. + */ + params->use.acc_shd = 1; + + /* + * Pass the enabled flag. If we're not enabled, we can then just bail + * out. + */ + ipu3_uapi_shd_config_static *config = ¶ms->acc_param.shd.shd; + config->general.shd_enable = frameContext.lsc.enabled; + + if (!frameContext.lsc.enabled) + return; + + config->grid.width = numHCells_; + config->grid.height = numVCells_; + config->grid.block_width_log2 = blockWidthLog2_; + config->grid.block_height_log2 = blockHeightLog2_; + config->grid.grid_height_per_slice = IPU3_UAPI_SHD_MAX_CELLS_PER_SET / numHCells_; + + /* + * The IPU3's documentation describes the x_start and y_start members + * as follows: + * + * "[X/Y] value of top left corner of sensor relative to ROI + * s13, [-4096, 0], default 0, only negative values." + * + * I interpret that as allowing us to configure the cropped rectangle + * relative to the full grid. That's useful if we're running the tabular + * algorithm, which would otherwise apply the full grid inappropriately. + * If we're running the polynomial one though the calculated grid is + * probably more appropriate than a coarse application of the full grid + * so let's tell the hardware not to bother correcting in that case. + */ + if (polynomial_) { + config->grid.x_start = 0; + config->grid.y_start = 0; + } else { + config->grid.x_start = (cropWidth_ - sensorWidth_) / 2; + config->grid.y_start = (cropHeight_ - sensorHeight_) / 2; + } + + /* No idea what this is, but the docs say it should be set as so */ + config->general.init_set_vrt_offst_ul = + config->grid.y_start >> (config->grid.block_height_log2 % + config->grid.grid_height_per_slice); + + /* + * Values in the LUT cease taking effect at 4096, and a value of 0.0 is + * "no correction" rather than black. The gain factor is described by + * the documentation like so: + * + * "Shift calculated anti shading value. Precision u2. 0x0 - gain factor + * [1, 5], means no shift interpolated value. 0x1 - gain factor [1, 9], + * means shift interpolated by 1. 0x2 - gain factor [1, 17], means shift + * interpolated by 2." + * + * The simplest interpretation for those pieces of information is I + * think that the LUT stores 12-bit Q numbers who's represented values + * depend on the gain_factor setting like so: + * + * 0: UQ<2, 10> representing values in range [0, 4) + * 1: UQ<3, 9> representing values in range [0, 8) + * 2: UQ<4, 8> representing values in range [0, 16) + * + * And that a base gain of 1.0 is added to those configured values. As a + * gain of more than 5.0 is fairly unlikely, let's fix gain_factor to 0 + * for now and revisit if needed. + */ + config->general.gain_factor = 0; + + /* + * Disable the black level settings here - we do that through another + * parameters block. + */ + config->black_level.bl_r = 0; + config->black_level.bl_gr = 0; + config->black_level.bl_gb = 0; + config->black_level.bl_b = 0; + + ipu3_uapi_shd_lut *lut = ¶ms->acc_param.shd.shd_lut; + + const auto &set = lscAlgo_.interpolateComponents(quantizedCt); + + unsigned int totalCells = numHCells_ * numVCells_; + unsigned int cellsPerSet = numHCells_ * config->grid.grid_height_per_slice; + unsigned int numSets = (numHCells_ + config->grid.grid_height_per_slice - 1) / + config->grid.grid_height_per_slice; + unsigned int i = 0; + + for (unsigned int s = 0; s < numSets; s++) { + for (unsigned int c = 0; c < cellsPerSet && i < totalCells; c++, i++) { + lut->sets[s].r_and_gr[c].r = set.at("r")[i]; + lut->sets[s].r_and_gr[c].gr = set.at("gr")[i]; + lut->sets[s].gb_and_b[c].gb = set.at("gb")[i]; + lut->sets[s].gb_and_b[c].b = set.at("b")[i]; + } + } + + lastAppliedCt_ = ct; + lastAppliedQuantizedCt_ = quantizedCt; + LOG(IPU3Lsc, Debug) + << "ct is " << ct << ", quantized to " + << quantizedCt; +} + +/** + * \copydoc libcamera::ipa::Algorithm::process + */ +void Lsc::process([[maybe_unused]] IPAContext &context, + [[maybe_unused]] const uint32_t frame, + IPAFrameContext &frameContext, + [[maybe_unused]] const ipu3_uapi_stats_3a *stats, + ControlList &metadata) +{ + lscAlgo_.process(frameContext.lsc, metadata); +} + +REGISTER_IPA_ALGORITHM(Lsc, "Lsc") + +} /* ipa::ipu3::algorithms */ + +} /* namespace libcamera */ diff --git a/src/ipa/ipu3/algorithms/lsc.h b/src/ipa/ipu3/algorithms/lsc.h new file mode 100644 index 0000000000000000000000000000000000000000..f86505029f005c9832c3efa213b86848222ddf62 --- /dev/null +++ b/src/ipa/ipu3/algorithms/lsc.h @@ -0,0 +1,58 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2026, Ideas on Board, Oy + * + * IPU3 Lens Shading Correction algorithm + */ + +#pragma once + +#include "libipa/fixedpoint.h" +#include "libipa/lsc.h" + +#include "algorithm.h" + +namespace libcamera { + +namespace ipa::ipu3::algorithms { + +class Lsc : public Algorithm +{ +public: + int init(IPAContext &context, const ValueNode &tuningData) override; + void queueRequest(IPAContext &context, const uint32_t frame, + IPAFrameContext &frameContext, + const ControlList &controls) override; + int configure(IPAContext &context, const IPAConfigInfo &configInfo) override; + void prepare(IPAContext &context, const uint32_t frame, + IPAFrameContext &frameContext, + ipu3_uapi_params *params) override; + void process(IPAContext &context, const uint32_t frame, + IPAFrameContext &frameContext, + const ipu3_uapi_stats_3a *stats, + ControlList &metadata) override; + +private: + std::vector calculatePositions(unsigned int dimension); + + unsigned int numHCells_; + unsigned int numVCells_; + unsigned int blockWidthLog2_; + unsigned int blockHeightLog2_; + + unsigned int lastAppliedCt_; + unsigned int lastAppliedQuantizedCt_; + + unsigned int sensorWidth_; + unsigned int sensorHeight_; + unsigned int cropWidth_; + unsigned int cropHeight_; + + bool polynomial_; + + LscAlgorithm> lscAlgo_; +}; + +} /* ipa::ipu3::algorithms */ + +} /* namespace libcamera */ diff --git a/src/ipa/ipu3/algorithms/meson.build b/src/ipa/ipu3/algorithms/meson.build index 3dafd2fda9897942cf87d9640665c4fcff383859..70177f50415259e0c2bd207205c385dde07d6624 100644 --- a/src/ipa/ipu3/algorithms/meson.build +++ b/src/ipa/ipu3/algorithms/meson.build @@ -6,5 +6,6 @@ ipu3_ipa_algorithms = files([ 'awb.cpp', 'blc.cpp', 'ccm.cpp', + 'lsc.cpp', 'tone_mapping.cpp', ]) diff --git a/src/ipa/ipu3/ipa_context.cpp b/src/ipa/ipu3/ipa_context.cpp index 7fcfd5e0e4ade92521cc2914dd07113235af8e45..071a7415b61f574d1b525d04d4434ac48fccb23e 100644 --- a/src/ipa/ipu3/ipa_context.cpp +++ b/src/ipa/ipu3/ipa_context.cpp @@ -127,6 +127,11 @@ namespace libcamera::ipa::ipu3 { * \brief Active gamma correction parameters for the IPA */ +/** + * \var IPAActiveState::lsc + * \brief Active lens shading correction parameters for the IPA + */ + /** * \var IPASessionConfiguration::sensor * \brief Sensor-specific configuration of the IPA @@ -191,4 +196,9 @@ namespace libcamera::ipa::ipu3 { * \brief Per-frame gamma correction parameters for the IPA */ +/** + * \var IPAFrameContext::lsc + * \brief Per-frame lens shading correction parameters for the IPA + */ + } /* namespace libcamera::ipa::ipu3 */ diff --git a/src/ipa/ipu3/ipa_context.h b/src/ipa/ipu3/ipa_context.h index f157f223cbb3119f108d768b14fca514ac5661ca..b517639b1e5878138f653918cd2dc71b49d97264 100644 --- a/src/ipa/ipu3/ipa_context.h +++ b/src/ipa/ipu3/ipa_context.h @@ -21,6 +21,7 @@ #include #include #include +#include namespace libcamera { @@ -70,6 +71,7 @@ struct IPAActiveState { ipa::awb::ActiveState awb; ipa::ccm::ActiveState ccm; ipa::gamma::ActiveState gamma; + ipa::lsc::ActiveState lsc; }; struct IPAFrameContext : public FrameContext { @@ -81,6 +83,7 @@ struct IPAFrameContext : public FrameContext { ipa::awb::FrameContext awb; ipa::ccm::FrameContext ccm; ipa::gamma::FrameContext gamma; + ipa::lsc::FrameContext lsc; }; struct IPAContext {