From patchwork Wed Jan 14 17:39:01 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Kieran Bingham X-Patchwork-Id: 25797 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 6F062BDCBF for ; Wed, 14 Jan 2026 17:39:30 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id B87D961FC8; Wed, 14 Jan 2026 18:39:25 +0100 (CET) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="dNfkwTaq"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 2227C61F9F for ; Wed, 14 Jan 2026 18:39:23 +0100 (CET) Received: from Monstersaurus.infra.iob (cpc89244-aztw30-2-0-cust6594.18-1.cable.virginm.net [86.31.185.195]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id D18E01E2D; Wed, 14 Jan 2026 18:38:55 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1768412336; bh=gyMwlUNl2PhhBYwUFgotn+WprViDbfl407Dj5RVqmUA=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=dNfkwTaqcCmlJTQAjfhngZZJG2HfnfykrzHwxn6WuSLaHwpBnTY5paKi4qSrH05L+ 2Pce73FVKa1rcwOTx4n6NwstvhNWfPOSjFT2/J+az5C9qvtwDWGpxgs77foEcDRHMC /pRKvdlGeiCh6WQ3RtvVRaRda5/tGwf7Ptll5h8A= From: Kieran Bingham To: libcamera devel Cc: Kieran Bingham , Isaac Scott Subject: [PATCH v5 01/16] ipa: libipa: Provide a Quantized data type support Date: Wed, 14 Jan 2026 17:39:01 +0000 Message-ID: <20260114173918.1744023-2-kieran.bingham@ideasonboard.com> X-Mailer: git-send-email 2.52.0 In-Reply-To: <20260114173918.1744023-1-kieran.bingham@ideasonboard.com> References: <20260114173918.1744023-1-kieran.bingham@ideasonboard.com> MIME-Version: 1.0 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" Frequently when handling data in IPA components we must convert and store user interface values which may be floating point values, and perform a specific operation or conversion to quantize this to a hardware value. This value may be to a fixed point type, or more custom code mappings, but in either case it is important to contain both the required hardware value, with its effective quantized value. Provide a new storage type 'Quantized' which can be defined based on a set of type specific Traits to perform the conversions between floats and the underlying hardware type. Reviewed-by: Isaac Scott Signed-off-by: Kieran Bingham --- v3: - adapt string format to [0xff:1.99] style instead of Q:0xff V:1.99 - Clean up comments and copyright - Remove private initialisers - already handled by constructors - Change quantized_type to QuantizedType v5: - introduce operator<<(std::ostream &out, const Quantized &q) - Remove unused iomanip and stdint.h from includes Signed-off-by: Kieran Bingham --- src/ipa/libipa/meson.build | 2 + src/ipa/libipa/quantized.cpp | 142 +++++++++++++++++++++++++++++++++++ src/ipa/libipa/quantized.h | 83 ++++++++++++++++++++ 3 files changed, 227 insertions(+) create mode 100644 src/ipa/libipa/quantized.cpp create mode 100644 src/ipa/libipa/quantized.h diff --git a/src/ipa/libipa/meson.build b/src/ipa/libipa/meson.build index 7202df869c2f..963c5ee73063 100644 --- a/src/ipa/libipa/meson.build +++ b/src/ipa/libipa/meson.build @@ -17,6 +17,7 @@ libipa_headers = files([ 'lux.h', 'module.h', 'pwl.h', + 'quantized.h', 'v4l2_params.h', ]) @@ -37,6 +38,7 @@ libipa_sources = files([ 'lux.cpp', 'module.cpp', 'pwl.cpp', + 'quantized.cpp', 'v4l2_params.cpp', ]) diff --git a/src/ipa/libipa/quantized.cpp b/src/ipa/libipa/quantized.cpp new file mode 100644 index 000000000000..115739e71654 --- /dev/null +++ b/src/ipa/libipa/quantized.cpp @@ -0,0 +1,142 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2025, Ideas On Board Oy + * + * Helper class to manage conversions between floating point types and quantized + * storage and representation of those values. + */ + +#include "quantized.h" + +/** + * \file quantized.h + * \brief Quantized storage and Quantizer representations + */ + +namespace libcamera { + +namespace ipa { + +/** + * \struct libcamera::ipa::Quantized + * \brief Wrapper that stores a value in both quantized and floating-point form + * \tparam Traits The traits class defining the quantization behaviour + * + * The Quantized struct template provides a thin wrapper around a quantized + * representation of a floating-point value. It uses a traits type \a Traits + * to define the conversion policy between the floating-point domain and the + * quantized integer domain. + * + * Each Quantized instance maintains two synchronized members: + * - the quantized integer representation, and + * - the corresponding floating-point value. + * + * The traits type defines: + * - the integer storage type used for quantization, + * - the static conversion functions \c fromFloat() and \c toFloat(), and + * - optional metadata such as value ranges. + * + * Quantized provides convenient constructors and assignment operators from + * either representation, as well as comparison and string formatting utilities. + */ + +/** + * \typedef Quantized::TraitsType + * \brief The traits policy type defining the quantization behaviour + * + * Exposes the associated traits type used by this Quantized instance. + * This allows external code to refer to constants or metadata defined in + * the traits, such as \c TraitsType::min or \c TraitsType::max. + */ + +/** + * \typedef Quantized::QuantizedType + * \brief The integer type used for the quantized representation + * + * This alias corresponds to \c TraitsType::QuantizedType, as defined by + * the traits class. + */ + +/** + * \fn Quantized::Quantized(float x) + * \brief Construct a Quantized value from a floating-point number + * \param[in] x The floating-point value to be quantized + * + * Converts the floating-point input \a x to its quantized integer + * representation using the associated traits policy, and initializes + * both the quantized and floating-point members. + */ + +/** + * \fn Quantized::Quantized(QuantizedType x) + * \brief Construct a Quantized value from an existing quantized integer + * \param[in] x The quantized integer value + * + * Converts the quantized integer \a x to its corresponding floating-point + * value using the traits policy, and initializes both internal members. + */ + +/** + * \fn Quantized::operator=(float x) + * \brief Assign a floating-point value to the Quantized object + * \param[in] x The floating-point value to assign + * \return A reference to the updated Quantized object + * + * Converts the floating-point value \a x to its quantized integer + * representation using the traits policy and updates both members. + */ + +/** + * \fn Quantized::operator=(QuantizedType x) + * \brief Assign a quantized integer value to the Quantized object + * \param[in] x The quantized integer value to assign + * \return A reference to the updated Quantized object + * + * Converts the quantized integer \a x to its corresponding floating-point + * value using the traits policy and updates both members. + */ + +/** + * \fn Quantized::value() const noexcept + * \brief Retrieve the floating-point representation + * \return The floating-point value corresponding to the quantized value + */ + +/** + * \fn Quantized::quantized() const noexcept + * \brief Retrieve the quantized integer representation + * \return The quantized integer value + */ + +/** + * \fn Quantized::toString() const + * \brief Format the quantized and floating-point values as a string + * \return A string containing the hexadecimal quantized value and its + * floating-point equivalent. + */ + +/** + * \fn Quantized::operator==(const Quantized &other) const noexcept + * \brief Compare two Quantized objects for equality + * \param[in] other The other Quantized object to compare against + * \return True if both objects have the same quantized integer value + */ + +/** + * \fn Quantized::operator!=(const Quantized &other) const noexcept + * \brief Compare two Quantized objects for inequality + * \param[in] other The other Quantized object to compare against + * \return True if the quantized integer values differ + */ + +/** + * \fn std::ostream &Quantized::operator<<(std::ostream &out, const Quantized &q) + * \brief Insert a text representation of a Quantized into an output stream + * \param[in] out The output stream + * \param[in] q The Quantized + * \return The output stream \a out + */ + +} /* namespace ipa */ + +} /* namespace libcamera */ diff --git a/src/ipa/libipa/quantized.h b/src/ipa/libipa/quantized.h new file mode 100644 index 000000000000..045ccb755a85 --- /dev/null +++ b/src/ipa/libipa/quantized.h @@ -0,0 +1,83 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2025, Ideas On Board Oy + * + * Helper class to manage conversions between floating point types and quantized + * storage and representation of those values. + */ + +#pragma once + +#include +#include + +#include + +namespace libcamera { + +namespace ipa { + +template +struct Quantized { + using TraitsType = Traits; + using QuantizedType = typename Traits::QuantizedType; + static_assert(std::is_arithmetic_v, + "Quantized: QuantizedType must be arithmetic"); + + Quantized() + : Quantized(0.0f) {} + Quantized(float x) { *this = x; } + Quantized(QuantizedType x) { *this = x; } + + Quantized &operator=(float x) + { + quantized_ = Traits::fromFloat(x); + value_ = Traits::toFloat(quantized_); + return *this; + } + + Quantized &operator=(QuantizedType x) + { + value_ = Traits::toFloat(x); + quantized_ = x; + return *this; + } + + float value() const noexcept { return value_; } + QuantizedType quantized() const noexcept { return quantized_; } + + std::string toString() const + { + std::stringstream ss; + ss << *this; + + return ss.str(); + } + + bool operator==(const Quantized &other) const noexcept + { + return quantized_ == other.quantized_; + } + + bool operator!=(const Quantized &other) const noexcept + { + return !(*this == other); + } + + friend std::ostream &operator<<(std::ostream &out, + const Quantized &q) + { + out << "[" << utils::hex(q.quantized()) + << ":" << q.value() << "]"; + + return out; + } + +private: + QuantizedType quantized_; + float value_; +}; + +} /* namespace ipa */ + +} /* namespace libcamera */ From patchwork Wed Jan 14 17:39:02 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Kieran Bingham X-Patchwork-Id: 25798 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 9075EBDCBF for ; Wed, 14 Jan 2026 17:39:32 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 66C6061FD4; Wed, 14 Jan 2026 18:39:26 +0100 (CET) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="tIpMGGQa"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 3E7B761FB9 for ; Wed, 14 Jan 2026 18:39:23 +0100 (CET) Received: from Monstersaurus.infra.iob (cpc89244-aztw30-2-0-cust6594.18-1.cable.virginm.net [86.31.185.195]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 22EA01E3F; Wed, 14 Jan 2026 18:38:56 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1768412336; bh=8MUUetaI9QEjr8zrILYFvsyoq+IBolRecP+ZiccjtVU=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=tIpMGGQaAYea+8hYWc+JdEKsRkD0wEpvzUmm+k5VJyp2HM0dfkfAPYARWaak2gXNh 2Y7FY1e2X+VviC1j3aNTHqswz4X4RTZkyDUCA/qp+gtj1Vg6scDn+9G/FV9SANyhpt iVXSPq2sK4Ta0cDHkaWYMN4SMpACNIw+8rgENjEw= From: Kieran Bingham To: libcamera devel Cc: Kieran Bingham , Isaac Scott Subject: [PATCH v5 02/16] test: libipa: Add tests for Quantized types Date: Wed, 14 Jan 2026 17:39:02 +0000 Message-ID: <20260114173918.1744023-3-kieran.bingham@ideasonboard.com> X-Mailer: git-send-email 2.52.0 In-Reply-To: <20260114173918.1744023-1-kieran.bingham@ideasonboard.com> References: <20260114173918.1744023-1-kieran.bingham@ideasonboard.com> MIME-Version: 1.0 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" Provide use case tests for the Quantized types to ensure construction and usages are consistent and work as expected. Reviewed-by: Isaac Scott Signed-off-by: Kieran Bingham --- v3: - Rename quantized_type to QuantizedType v5: - use static asserts for constructible failure tests - Remove constexpr from lround users (only possible in C++23) - Remove move tests - Fix up inequality test Signed-off-by: Kieran Bingham --- test/ipa/libipa/meson.build | 1 + test/ipa/libipa/quantized.cpp | 124 ++++++++++++++++++++++++++++++++++ 2 files changed, 125 insertions(+) create mode 100644 test/ipa/libipa/quantized.cpp diff --git a/test/ipa/libipa/meson.build b/test/ipa/libipa/meson.build index 2070bed70222..c3e255871f4f 100644 --- a/test/ipa/libipa/meson.build +++ b/test/ipa/libipa/meson.build @@ -5,6 +5,7 @@ libipa_test = [ {'name': 'histogram', 'sources': ['histogram.cpp']}, {'name': 'interpolator', 'sources': ['interpolator.cpp']}, {'name': 'pwl', 'sources': ['pwl.cpp'] }, + {'name': 'quantized', 'sources': ['quantized.cpp']}, ] foreach test : libipa_test diff --git a/test/ipa/libipa/quantized.cpp b/test/ipa/libipa/quantized.cpp new file mode 100644 index 000000000000..6b6a61311125 --- /dev/null +++ b/test/ipa/libipa/quantized.cpp @@ -0,0 +1,124 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2025, Ideas on Board + * + * Dual Type and Quantizer tests + */ + +#include "../src/ipa/libipa/quantized.h" + +#include +#include +#include +#include +#include + +#include "test.h" + +using namespace std; +using namespace libcamera; +using namespace ipa; + +struct BrightnessHueTraits { + using QuantizedType = int8_t; + static QuantizedType fromFloat(float v) + { + int quantized = std::lround(v * 128.0f); + return static_cast(std::clamp(quantized, -128, 127)); + } + static float toFloat(QuantizedType v) + { + return static_cast(v) / 128.0f; + } +}; + +using BrightnessHueQuantizer = Quantized; + +struct ContrastSaturationTraits { + using QuantizedType = uint8_t; + static QuantizedType fromFloat(float v) + { + int quantized = std::lround(v * 128.0f); + return static_cast(std::clamp(quantized, 0, 255)); + } + static float toFloat(QuantizedType v) + { + return static_cast(v) / 128.0f; + } +}; + +using ContrastSaturationQuantizer = Quantized; + +using BrightnessQ = BrightnessHueQuantizer; +using HueQ = BrightnessHueQuantizer; +using ContrastQ = ContrastSaturationQuantizer; +using SaturationQ = ContrastSaturationQuantizer; + +class QuantizedTest : public Test +{ +protected: + int run() + { + /* Test construction from float */ + { + BrightnessQ b(0.5f); + if (b.quantized() != 64 || std::abs(b.value() - 0.5f) > 0.01f) + return TestFail; + } + + /* Test construction from T */ + { + ContrastQ c(uint8_t(128)); + if (c.quantized() != 128 || std::abs(c.value() - 1.0f) > 0.01f) + return TestFail; + } + + /* + * Only construction from the exact storage type or a float + * is permitted. + */ + static_assert(!std::is_constructible_v); + static_assert(!std::is_constructible_v); + static_assert(!std::is_constructible_v); + static_assert(!std::is_constructible_v); + static_assert(!std::is_constructible_v); + static_assert(!std::is_constructible_v); + + /* Test equality */ + { + BrightnessQ b1(0.5f), b2((int8_t)64); + if (!(b1 == b2)) + return TestFail; + } + + /* Test inequality */ + { + BrightnessQ b1(0.5f), b2(-0.5f); + if (!(b1 != b2)) + return TestFail; + } + + /* Test copying */ + { + BrightnessQ b1(0.25f); + BrightnessQ b2 = b1; + if (!(b1 == b2)) + return TestFail; + } + + /* Test assignment */ + { + ContrastQ c1(1.5f); + ContrastQ c2(0.0f); + c2 = c1; + if (!(c1 == c2)) + return TestFail; + } + + std::cout << "Quantised tests passed successfully." << std::endl; + + return TestPass; + } +}; + +TEST_REGISTER(QuantizedTest) From patchwork Wed Jan 14 17:39:03 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Kieran Bingham X-Patchwork-Id: 25799 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 61106C3274 for ; Wed, 14 Jan 2026 17:39:34 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id B932F61FD8; Wed, 14 Jan 2026 18:39:27 +0100 (CET) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="FHECP9il"; 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 5AFE461FBC for ; Wed, 14 Jan 2026 18:39:23 +0100 (CET) Received: from Monstersaurus.infra.iob (cpc89244-aztw30-2-0-cust6594.18-1.cable.virginm.net [86.31.185.195]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 679961E43; Wed, 14 Jan 2026 18:38:56 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1768412336; bh=Oz5+iydAOiXwieOaY0iM+ms3+pAUlSkp5TaVa1wjFpA=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=FHECP9ilrdtlnf0CX8DcnxiemUjGB+XiXwFcXk9LdZg0SJqBnQh7y+epxfQLKVmho gIwmqsWIjhcu+mmqas/3FUUeoI0Nkj2+ln5sXnEytpS+/nZZyJw7xds2ZC72ZbgnOb TV/W7YHp2SQIhsAjJ54oGOblkloELYJ22zeBbVB4= From: Kieran Bingham To: libcamera devel Cc: Kieran Bingham , Isaac Scott Subject: [PATCH v5 03/16] ipa: libipa: fixedpoint: Fix unsigned usage Date: Wed, 14 Jan 2026 17:39:03 +0000 Message-ID: <20260114173918.1744023-4-kieran.bingham@ideasonboard.com> X-Mailer: git-send-email 2.52.0 In-Reply-To: <20260114173918.1744023-1-kieran.bingham@ideasonboard.com> References: <20260114173918.1744023-1-kieran.bingham@ideasonboard.com> MIME-Version: 1.0 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 fixedToFloatingPoint does not support unsigned Q types, and incorrectly sign-extends all values which have the top most bit set in the quantized values. Fix this by ensuring that only signed types perform sign extension, and simplify the calculation for unsigned types. Convert the storage of the test cases to signed types to correctly represent their intended purpose, to prevent test failures. Reviewed-by: Isaac Scott Signed-off-by: Kieran Bingham --- src/ipa/libipa/fixedpoint.h | 3 +++ test/ipa/libipa/fixedpoint.cpp | 6 +++--- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/ipa/libipa/fixedpoint.h b/src/ipa/libipa/fixedpoint.h index 709cf50f0fcd..b4a7fa5e0ecd 100644 --- a/src/ipa/libipa/fixedpoint.h +++ b/src/ipa/libipa/fixedpoint.h @@ -49,6 +49,9 @@ constexpr R fixedToFloatingPoint(T number) static_assert(sizeof(int) >= sizeof(T)); static_assert(I + F <= sizeof(T) * 8); + if constexpr (std::is_unsigned_v) + return static_cast(number) / static_cast(1 << F); + /* * Recreate the upper bits in case of a negative number by shifting the sign * bit from the fixed point to the first bit of the unsigned and then right shifting diff --git a/test/ipa/libipa/fixedpoint.cpp b/test/ipa/libipa/fixedpoint.cpp index 99eb662ddf4e..4b017e86a74f 100644 --- a/test/ipa/libipa/fixedpoint.cpp +++ b/test/ipa/libipa/fixedpoint.cpp @@ -68,7 +68,7 @@ protected: * The second 7.992 test is to test that unused bits don't * affect the result. */ - std::map testCases = { + std::map testCases = { { 7.992, 0x3ff }, { 0.2, 0x01a }, { -0.2, 0x7e6 }, @@ -81,14 +81,14 @@ protected: int ret; for (const auto &testCase : testCases) { - ret = testSingleFixedPoint<4, 7, uint16_t>(testCase.first, + ret = testSingleFixedPoint<4, 7, int16_t>(testCase.first, testCase.second); if (ret != TestPass) return ret; } /* Special case with a superfluous one in the unused bits */ - ret = testFixedToFloat<4, 7, uint16_t, double>(0xbff, 7.992); + ret = testFixedToFloat<4, 7, int16_t, double>(0xbff, 7.992); if (ret != TestPass) return ret; From patchwork Wed Jan 14 17:39:04 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Kieran Bingham X-Patchwork-Id: 25800 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 C7739C3284 for ; Wed, 14 Jan 2026 17:39:34 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 7CD6361FC5; Wed, 14 Jan 2026 18:39:28 +0100 (CET) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="t7YQaSDZ"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 8C59D61FBF for ; Wed, 14 Jan 2026 18:39:23 +0100 (CET) Received: from Monstersaurus.infra.iob (cpc89244-aztw30-2-0-cust6594.18-1.cable.virginm.net [86.31.185.195]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id A879D1F16; Wed, 14 Jan 2026 18:38:56 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1768412336; bh=6AWHoe1C/ACRuMNYY21QXcRal1P9jzdXPX6+MHKvNec=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=t7YQaSDZpq8H3XJu5PoBtP8qwtT7TR8XeaJlueMo8wsPFVYcfpHeSiDIC6lPHO0V9 NXwllSlF/8W8bbUYsKOJF8p7U2tR0hGPXz1O7HGv0tISX0zrTjOC37CLiPNSp+pwBH Zh/Kfjk7LbdYnkgte8CQNU/4n6dkOSFULZymmoaw= From: Kieran Bingham To: libcamera devel Cc: Kieran Bingham , Isaac Scott Subject: [PATCH v5 04/16] ipa: libipa: Provide fixed point quantized traits Date: Wed, 14 Jan 2026 17:39:04 +0000 Message-ID: <20260114173918.1744023-5-kieran.bingham@ideasonboard.com> X-Mailer: git-send-email 2.52.0 In-Reply-To: <20260114173918.1744023-1-kieran.bingham@ideasonboard.com> References: <20260114173918.1744023-1-kieran.bingham@ideasonboard.com> MIME-Version: 1.0 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" Extend the new Quantized type infrastructure by providing a FixedPointQTraits template. This allows construction of fixed point types with a Quantized storage that allows easy reading of both the underlying quantized type value and a floating point representation of that same value. Reviewed-by: Isaac Scott Signed-off-by: Kieran Bingham --- v4: - Assert that the given type has enough bits for the usage - Use unsigned types for calculating qmin/qmax - Reorder toFloat/fromFloat and min/max for future inlining - Make toFloat and fromFloat constexpr v5: - Make UT, Bits and Bitmask private (and remove doxygen) - Remove constexpr from fromFloat which uses std::round (only constexpr in C++23) - static_assert that minqMin qmax->qMax Bits->bits BitMask->bitMask - Remove typedefs for Q1_7 etc Signed-off-by: Kieran Bingham --- src/ipa/libipa/fixedpoint.cpp | 89 +++++++++++++++++++++++++++++++++++ src/ipa/libipa/fixedpoint.h | 69 +++++++++++++++++++++++++++ 2 files changed, 158 insertions(+) diff --git a/src/ipa/libipa/fixedpoint.cpp b/src/ipa/libipa/fixedpoint.cpp index 6b698fc5d680..43d76f745d8a 100644 --- a/src/ipa/libipa/fixedpoint.cpp +++ b/src/ipa/libipa/fixedpoint.cpp @@ -37,6 +37,95 @@ namespace ipa { * \return The converted value */ +/** + * \struct libcamera::ipa::FixedPointQTraits + * \brief Traits type implementing fixed-point quantisation conversions + * + * The FixedPointQTraits structure defines a policy for mapping floating-point + * values to and from fixed-point integer representations. It is parameterised + * by the number of integer bits \a I, fractional bits \a F, and the integral + * storage type \a T. The traits are used with Quantized to create a + * quantised type that stores both the fixed-point representation and the + * corresponding floating-point value. + * + * The trait exposes compile-time constants describing the bit layout, limits, + * and scaling factors used in the fixed-point representation. + * + * \tparam I Number of integer bits + * \tparam F Number of fractional bits + * \tparam T Integral type used to store the quantised value + */ + +/** + * \typedef FixedPointQTraits::QuantizedType + * \brief The integral storage type used for the fixed-point representation + */ + +/** + * \var FixedPointQTraits::qMin + * \brief Minimum representable quantised integer value + * + * This corresponds to the most negative value for signed formats or zero for + * unsigned formats. + */ + +/** + * \var FixedPointQTraits::qMax + * \brief Maximum representable quantised integer value + */ + +/** + * \var FixedPointQTraits::min + * \brief Minimum representable floating-point value corresponding to qMin + */ + +/** + * \var FixedPointQTraits::max + * \brief Maximum representable floating-point value corresponding to qMax + */ + +/** + * \fn FixedPointQTraits::fromFloat(float v) + * \brief Convert a floating-point value to a fixed-point integer + * \param[in] v The floating-point value to be converted + * \return The quantised fixed-point integer representation + * + * The conversion rounds the floating-point input \a v to the nearest integer + * according to the scaling factor defined by the number of fractional bits F. + */ + +/** + * \fn FixedPointQTraits::toFloat(QuantizedType q) + * \brief Convert a fixed-point integer to a floating-point value + * \param[in] q The fixed-point integer value to be converted + * \return The corresponding floating-point value + * + * The conversion sign-extends the integer value if required and divides by the + * scaling factor defined by the number of fractional bits F. + */ + +/** + * \typedef Q + * \brief Define a signed fixed-point quantised type with automatic storage width + * \tparam I The number of integer bits + * \tparam F The number of fractional bits + * + * This alias defines a signed fixed-point quantised type using the + * \ref FixedPointQTraits trait and a suitable signed integer storage type + * automatically selected based on the total number of bits \a (I + F). + */ + +/** + * \typedef UQ + * \brief Define an unsigned fixed-point quantised type with automatic storage width + * \tparam I The number of integer bits + * \tparam F The number of fractional bits + * + * This alias defines an unsigned fixed-point quantised type using the + * \ref FixedPointQTraits trait and a suitable unsigned integer storage type + * automatically selected based on the total number of bits \a (I + F). + */ + } /* namespace ipa */ } /* namespace libcamera */ diff --git a/src/ipa/libipa/fixedpoint.h b/src/ipa/libipa/fixedpoint.h index b4a7fa5e0ecd..4f6ee081604b 100644 --- a/src/ipa/libipa/fixedpoint.h +++ b/src/ipa/libipa/fixedpoint.h @@ -10,6 +10,8 @@ #include #include +#include "quantized.h" + namespace libcamera { namespace ipa { @@ -63,6 +65,73 @@ constexpr R fixedToFloatingPoint(T number) return static_cast(t) / static_cast(1 << F); } +template +struct FixedPointQTraits { +private: + static_assert(std::is_integral_v, "FixedPointQTraits: T must be integral"); + using UT = std::make_unsigned_t; + + static constexpr unsigned int bits = I + F; + static_assert(bits <= sizeof(T) * 8, "FixedPointQTraits: too many bits for type T"); + + static constexpr T bitMask = (bits < sizeof(T) * 8) + ? static_cast((UT{1} << bits) - 1) + : static_cast(~UT{0}); + +public: + using QuantizedType = T; + + static constexpr T qMin = std::is_signed_v + ? static_cast(-(UT{1} << (bits - 1))) + : static_cast(0); + + static constexpr T qMax = std::is_signed_v + ? static_cast((UT{1} << (bits - 1)) - 1) + : static_cast((UT{1} << bits) - 1); + + static constexpr float toFloat(QuantizedType q) + { + return fixedToFloatingPoint(q); + } + + static constexpr float min = fixedToFloatingPoint(qMin); + static constexpr float max = fixedToFloatingPoint(qMax); + + static_assert(min < max, "FixedPointQTraits: Minimum must be less than maximum"); + + /* Conversion functions required by Quantized */ + static QuantizedType fromFloat(float v) + { + v = std::clamp(v, min, max); + return floatingToFixedPoint(v); + } +}; + +namespace details { + +template +constexpr auto qtype() +{ + static_assert(Bits <= 64); + + if constexpr (Bits <= 8) + return int8_t(); + else if constexpr (Bits <= 16) + return int16_t(); + else if constexpr (Bits <= 32) + return int32_t(); + else if constexpr (Bits <= 64) + return int64_t(); +} + +} /* namespace details */ + +template +using Q = Quantized())>>; + +template +using UQ = Quantized())>>>; + } /* namespace ipa */ } /* namespace libcamera */ From patchwork Wed Jan 14 17:39:05 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Kieran Bingham X-Patchwork-Id: 25801 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 5372DC3285 for ; Wed, 14 Jan 2026 17:39:35 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 294B261FDF; Wed, 14 Jan 2026 18:39:29 +0100 (CET) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="TQbytmck"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id CFECD61F9F for ; Wed, 14 Jan 2026 18:39:23 +0100 (CET) Received: from Monstersaurus.infra.iob (cpc89244-aztw30-2-0-cust6594.18-1.cable.virginm.net [86.31.185.195]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id EE1C41F37; Wed, 14 Jan 2026 18:38:56 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1768412337; bh=DvlLC0e3kblYP9IjPSdpi2R0xDTUshQY0Gi6YYbGg18=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=TQbytmck0IXTtvBg6giLt/YaUd7DbpwyrPgWM8EfMV6IBnYCnR/7xVZx9vjsEB0H2 ZIhNILsNcG6BMQjRv0iWM++jLoIWCmUMbLo2dds8Ar39nhDoGoi0AxB+0zDZsCSere GNJ/MEqAGwnlWj7UCmTMflj++KohXyCGYNMZgHTQ= From: Kieran Bingham To: libcamera devel Cc: Kieran Bingham Subject: [PATCH v5 05/16] test: libipa: Provide FixedPoint Quantized tests Date: Wed, 14 Jan 2026 17:39:05 +0000 Message-ID: <20260114173918.1744023-6-kieran.bingham@ideasonboard.com> X-Mailer: git-send-email 2.52.0 In-Reply-To: <20260114173918.1744023-1-kieran.bingham@ideasonboard.com> References: <20260114173918.1744023-1-kieran.bingham@ideasonboard.com> MIME-Version: 1.0 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" Provide tests to validate the conditions of FixedPoint types used within libcamera explicitly. Signed-off-by: Kieran Bingham --- v3: - Rename quantized_type to QuantizedType v5: - Use string_view for introduction of type - move min/max check to static assert - Squash down all tests into a single implementation and remove extra type proliferation. - Use Q/UQ types directly. - use std::cout consistently Signed-off-by: Kieran Bingham --- test/ipa/libipa/fixedpoint.cpp | 126 +++++++++++++++++++++++++++++++-- 1 file changed, 122 insertions(+), 4 deletions(-) diff --git a/test/ipa/libipa/fixedpoint.cpp b/test/ipa/libipa/fixedpoint.cpp index 4b017e86a74f..9fa7fa688c24 100644 --- a/test/ipa/libipa/fixedpoint.cpp +++ b/test/ipa/libipa/fixedpoint.cpp @@ -5,12 +5,14 @@ * Fixed / Floating point utility tests */ +#include "../src/ipa/libipa/fixedpoint.h" + #include #include #include #include -#include "../src/ipa/libipa/fixedpoint.h" +#include #include "test.h" @@ -95,14 +97,130 @@ protected: return TestPass; } - int run() + template + int quantizedCheck(float input, typename Q::QuantizedType expected, float value) { - /* fixed point conversion test */ - if (testFixedPoint() != TestPass) + Q q(input); + using T = typename Q::QuantizedType; + + std::cout << " Checking " << input << " == " << q.toString() << std::endl; + + T quantized = q.quantized(); + if (quantized != expected) { + std::cout << " ** Q Expected " << input + << " to quantize to " << utils::hex(expected) + << ", got " << utils::hex(quantized) + << " - (" << q.toString() << ")" + << std::endl; + return 1; + } + + if ((std::abs(q.value() - value)) > 0.0001f) { + std::cout << " ** V Expected " << input + << " to quantize to " << value + << ", got " << q.value() + << " - (" << q.toString() << ")" + << std::endl; + return 1; + } + + return 0; + } + + template + void introduce(std::string_view type) + { + using T = typename Q::QuantizedType; + + std::cout << std::endl; + + std::cout << type + << "(" << Q::TraitsType::min << " .. " << Q::TraitsType::max << ") " + << " Min: " << Q(Q::TraitsType::min).toString() + << " -- Max: " << Q(Q::TraitsType::max).toString() + << " Step:" << Q(T(1)).value() + << std::endl; + } + + int testFixedPointQuantizers() + { + unsigned int fails = 0; + + /* clang-format off */ + + /* Q1.7(-1 .. 0.992188) Min: [0x80:-1] -- Max: [0x7f:0.992188] Step:0.0078125*/ + introduce>("Q1.7"); + fails += quantizedCheck>(-1.000f, 0b1'0000000, -1.0f); /* Min */ + fails += quantizedCheck>(-0.992f, 0b1'0000001, -0.992188f); /* Min + 1 step */ + fails += quantizedCheck>(-0.006f, 0b1'1111111, -0.0078125f); /* -1 step */ + fails += quantizedCheck>( 0.000f, 0b0'0000000, 0.0f); /* Zero */ + fails += quantizedCheck>( 0.008f, 0b0'0000001, 0.0078125f); /* +1 step */ + fails += quantizedCheck>( 0.992f, 0b0'1111111, 0.992188f); /* Max */ + + /* UQ1.7(0 .. 1.99219) Min: [0x00:0] -- Max: [0xff:1.99219] Step:0.0078125 */ + introduce>("UQ1.7"); + fails += quantizedCheck>(0.0f, 0b0'0000000, 0.0f); /* Min / Zero */ + fails += quantizedCheck>(1.0f, 0b1'0000000, 1.0f); /* Mid */ + fails += quantizedCheck>(1.992f, 0b1'1111111, 1.99219f); /* Max */ + + /* UQ4.8(0 .. 15.9961) Min: [0x0000:0] -- Max: [0x0fff:15.9961] Step:0.00390625 */ + introduce>("UQ4.8"); + fails += quantizedCheck>( 0.0f, 0b0000'00000000, 0.00f); + fails += quantizedCheck>(16.0f, 0b1111'11111111, 15.9961f); + + /* Q5.4(-16 .. 15.9375) Min: [0x0100:-16] -- Max: [0x00ff:15.9375] Step:0.0625 */ + introduce>("Q5.4"); + fails += quantizedCheck>(-16.00f, 0b10000'0000, -16.00f); + fails += quantizedCheck>( 15.94f, 0b01111'1111, 15.9375f); + + /* UQ5.8(0 .. 31.9961) Min: [0x0000:0] -- Max: [0x1fff:31.9961] Step:0.00390625 */ + introduce>("UQ5.8"); + fails += quantizedCheck>( 0.00f, 0b00000'00000000, 0.00f); + fails += quantizedCheck>(32.00f, 0b11111'11111111, 31.9961f); + + /* Q12.4(-2048 .. 2047.94) Min: [0x8000:-2048] -- Max: [0x7fff:2047.94] Step:0.0625 */ + introduce>("Q12.4"); + fails += quantizedCheck>(0.0f, 0b000000000000'0000, 0.0f); + fails += quantizedCheck>(7.5f, 0b000000000111'1000, 7.5f); + + /* UQ12.4(0 .. 4095.94) Min: [0x0000:0] -- Max: [0xffff:4095.94] Step:0.0625 */ + introduce>("UQ12.4"); + fails += quantizedCheck>(0.0f, 0b000000000000'0000, 0.0f); + fails += quantizedCheck>(7.5f, 0b000000000111'1000, 7.5f); + + /* Validate that exceeding limits clamps to type range */ + std::cout << std::endl << "Range validation:" << std::endl; + fails += quantizedCheck>(-100.0f, 0b1'0000000, -1.0f); + fails += quantizedCheck>(+100.0f, 0b0'1111111, 0.992188f); + fails += quantizedCheck>(-100.0f, 0b0'0000000, 0.0f); + fails += quantizedCheck>(+100.0f, 0b1'1111111, 1.99219f); + + /* clang-format on */ + + std::cout << std::endl; + + if (fails > 0) { + cout << "Fixed point quantizer tests failed: " + << std::dec << fails << " failures." << std::endl; return TestFail; + } return TestPass; } + + int run() + { + unsigned int fails = 0; + + /* fixed point conversion test */ + if (testFixedPoint() != TestPass) + fails++; + + if (testFixedPointQuantizers() != TestPass) + fails++; + + return fails ? TestFail : TestPass; + } }; TEST_REGISTER(FixedPointUtilsTest) From patchwork Wed Jan 14 17:39:06 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Kieran Bingham X-Patchwork-Id: 25802 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 00CD4BDCBF for ; Wed, 14 Jan 2026 17:39:35 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id F361761FCC; Wed, 14 Jan 2026 18:39:30 +0100 (CET) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="DzP2ExmS"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 2AB9361FB9 for ; Wed, 14 Jan 2026 18:39:24 +0100 (CET) Received: from Monstersaurus.infra.iob (cpc89244-aztw30-2-0-cust6594.18-1.cable.virginm.net [86.31.185.195]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 37C411FBC; Wed, 14 Jan 2026 18:38:57 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1768412337; bh=gwN59Dyyn2xEyZ0BFhQmLYiKsf3fQWuQH3iGbipALMA=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=DzP2ExmSHsE/FLlgDdiSPkpzsyw5QV7SNk7ljt4VIPraZQ6v8Ipzwu1XC3s0fsgwV ZutxW4vImwsWOsJbED/V4euXCaYdNlQ/5Y8qJgASqZgzW0xV05JINyjDRY9g65CSGA H1iQh+ax4Z4rIWpb8pAN24+inbH9PLJjAwNxduyc= From: Kieran Bingham To: libcamera devel Cc: Kieran Bingham , Isaac Scott , Laurent Pinchart Subject: [PATCH v5 06/16] ipa: rkisp1: cproc: Convert to use Quantized types Date: Wed, 14 Jan 2026 17:39:06 +0000 Message-ID: <20260114173918.1744023-7-kieran.bingham@ideasonboard.com> X-Mailer: git-send-email 2.52.0 In-Reply-To: <20260114173918.1744023-1-kieran.bingham@ideasonboard.com> References: <20260114173918.1744023-1-kieran.bingham@ideasonboard.com> MIME-Version: 1.0 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 Brightness, Contrast and Saturation helper functions to a Quantizer type to support maintaining the data. While modifying the include blocks of the ipa_context.h, also fix the include style for libipa components. Reviewed-by: Isaac Scott Reviewed-by: Laurent Pinchart Signed-off-by: Kieran Bingham --- v3: - Don't add into ipa_context.h anymore v5: - Use full string of quantised in debug log - Fix libipa include style - Fix commit message to mention brightness and typo in saturation Signed-off-by: Kieran Bingham --- src/ipa/rkisp1/algorithms/cproc.cpp | 28 +++++++++------------------- src/ipa/rkisp1/ipa_context.h | 23 +++++++++++++++-------- 2 files changed, 24 insertions(+), 27 deletions(-) diff --git a/src/ipa/rkisp1/algorithms/cproc.cpp b/src/ipa/rkisp1/algorithms/cproc.cpp index d1fff6990d37..4374c08c2a4f 100644 --- a/src/ipa/rkisp1/algorithms/cproc.cpp +++ b/src/ipa/rkisp1/algorithms/cproc.cpp @@ -39,16 +39,6 @@ constexpr float kDefaultBrightness = 0.0f; constexpr float kDefaultContrast = 1.0f; constexpr float kDefaultSaturation = 1.0f; -int convertBrightness(const float v) -{ - return std::clamp(std::lround(v * 128), -128, 127); -} - -int convertContrastOrSaturation(const float v) -{ - return std::clamp(std::lround(v * 128), 0, 255); -} - } /* namespace */ /** @@ -74,9 +64,9 @@ int ColorProcessing::configure(IPAContext &context, { auto &cproc = context.activeState.cproc; - cproc.brightness = convertBrightness(kDefaultBrightness); - cproc.contrast = convertContrastOrSaturation(kDefaultContrast); - cproc.saturation = convertContrastOrSaturation(kDefaultSaturation); + cproc.brightness = BrightnessQ(kDefaultBrightness); + cproc.contrast = ContrastQ(kDefaultContrast); + cproc.saturation = SaturationQ(kDefaultSaturation); return 0; } @@ -97,7 +87,7 @@ void ColorProcessing::queueRequest(IPAContext &context, const auto &brightness = controls.get(controls::Brightness); if (brightness) { - int value = convertBrightness(*brightness); + BrightnessQ value = *brightness; if (cproc.brightness != value) { cproc.brightness = value; update = true; @@ -108,7 +98,7 @@ void ColorProcessing::queueRequest(IPAContext &context, const auto &contrast = controls.get(controls::Contrast); if (contrast) { - int value = convertContrastOrSaturation(*contrast); + ContrastQ value = *contrast; if (cproc.contrast != value) { cproc.contrast = value; update = true; @@ -119,7 +109,7 @@ void ColorProcessing::queueRequest(IPAContext &context, const auto saturation = controls.get(controls::Saturation); if (saturation) { - int value = convertContrastOrSaturation(*saturation); + SaturationQ value = *saturation; if (cproc.saturation != value) { cproc.saturation = value; update = true; @@ -148,9 +138,9 @@ void ColorProcessing::prepare([[maybe_unused]] IPAContext &context, auto config = params->block(); config.setEnabled(true); - config->brightness = frameContext.cproc.brightness; - config->contrast = frameContext.cproc.contrast; - config->sat = frameContext.cproc.saturation; + config->brightness = frameContext.cproc.brightness.quantized(); + config->contrast = frameContext.cproc.contrast.quantized(); + config->sat = frameContext.cproc.saturation.quantized(); } REGISTER_IPA_ALGORITHM(ColorProcessing, "ColorProcessing") diff --git a/src/ipa/rkisp1/ipa_context.h b/src/ipa/rkisp1/ipa_context.h index b257cee55379..cdb9a17b5adc 100644 --- a/src/ipa/rkisp1/ipa_context.h +++ b/src/ipa/rkisp1/ipa_context.h @@ -24,14 +24,20 @@ #include "libcamera/internal/matrix.h" #include "libcamera/internal/vector.h" -#include -#include #include "libipa/agc_mean_luminance.h" +#include "libipa/camera_sensor_helper.h" +#include "libipa/fc_queue.h" +#include "libipa/fixedpoint.h" namespace libcamera { namespace ipa::rkisp1 { +/* Fixed point types used by CPROC */ +using BrightnessQ = Q<1, 7>; +using ContrastQ = UQ<1, 7>; +using SaturationQ = UQ<1, 7>; + struct IPAHwSettings { unsigned int numAeCells; unsigned int numHistogramBins; @@ -115,9 +121,9 @@ struct IPAActiveState { } ccm; struct { - int8_t brightness; - uint8_t contrast; - uint8_t saturation; + BrightnessQ brightness; + ContrastQ contrast; + SaturationQ saturation; } cproc; struct { @@ -173,9 +179,10 @@ struct IPAFrameContext : public FrameContext { } awb; struct { - int8_t brightness; - uint8_t contrast; - uint8_t saturation; + BrightnessQ brightness; + ContrastQ contrast; + SaturationQ saturation; + bool update; } cproc; From patchwork Wed Jan 14 17:39:07 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Kieran Bingham X-Patchwork-Id: 25803 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 CC18BC32A2 for ; Wed, 14 Jan 2026 17:39:36 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 9419461FBC; Wed, 14 Jan 2026 18:39:31 +0100 (CET) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="Y6nvASCE"; 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 9E52F61FCC for ; Wed, 14 Jan 2026 18:39:24 +0100 (CET) Received: from Monstersaurus.infra.iob (cpc89244-aztw30-2-0-cust6594.18-1.cable.virginm.net [86.31.185.195]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 8746A2830; Wed, 14 Jan 2026 18:38:57 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1768412337; bh=VqyQdtobbcbOg0DatNTBqEkmJcihEw3cYOb/Ib1BH7o=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=Y6nvASCEZCeDM2rqTVJWWpkSzsNiArU8t0Dh2kR0fM1W2vOnqutLILpfKejWlko7b vD51X37olDd53jHUrBBvp8Jd4A1RahhQ2a4fp//6dpPtDgm891jd/NOOcZXqTQQPHX aSnU/lj5jWnebbiRxTnfM8qNwxGmkYu72YAey0Bc= From: Kieran Bingham To: libcamera devel Cc: Kieran Bingham , Isaac Scott , =?utf-8?q?Barnab=C3=A1s_P=C5=91cze?= , Laurent Pinchart Subject: [PATCH v5 07/16] ipa: rkisp1: cproc: Report metadata Date: Wed, 14 Jan 2026 17:39:07 +0000 Message-ID: <20260114173918.1744023-8-kieran.bingham@ideasonboard.com> X-Mailer: git-send-email 2.52.0 In-Reply-To: <20260114173918.1744023-1-kieran.bingham@ideasonboard.com> References: <20260114173918.1744023-1-kieran.bingham@ideasonboard.com> MIME-Version: 1.0 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" Presently the colour processing component exposes controls for brightness, saturation, and contrast to the applications which are handled and processed accordingly on the ISP. The implementation lacks reporting the values that are set back to the application. Utilise the new Quantised types to provide the values that were applied to the hardware and report them in the completed request metadata. Reviewed-by: Isaac Scott Reviewed-by: Barnabás Pőcze Reviewed-by: Laurent Pinchart Signed-off-by: Kieran Bingham --- v5: - Fix line wraps Signed-off-by: Kieran Bingham --- src/ipa/rkisp1/algorithms/cproc.cpp | 14 ++++++++++++++ src/ipa/rkisp1/algorithms/cproc.h | 4 ++++ 2 files changed, 18 insertions(+) diff --git a/src/ipa/rkisp1/algorithms/cproc.cpp b/src/ipa/rkisp1/algorithms/cproc.cpp index 4374c08c2a4f..e9e2b5444bc9 100644 --- a/src/ipa/rkisp1/algorithms/cproc.cpp +++ b/src/ipa/rkisp1/algorithms/cproc.cpp @@ -143,6 +143,20 @@ void ColorProcessing::prepare([[maybe_unused]] IPAContext &context, config->sat = frameContext.cproc.saturation.quantized(); } +/** + * \copydoc libcamera::ipa::Algorithm::process + */ +void ColorProcessing::process([[maybe_unused]] IPAContext &context, + [[maybe_unused]] const uint32_t frame, + IPAFrameContext &frameContext, + [[maybe_unused]] const rkisp1_stat_buffer *stats, + ControlList &metadata) +{ + metadata.set(controls::Brightness, frameContext.cproc.brightness.value()); + metadata.set(controls::Contrast, frameContext.cproc.contrast.value()); + metadata.set(controls::Saturation, frameContext.cproc.saturation.value()); +} + REGISTER_IPA_ALGORITHM(ColorProcessing, "ColorProcessing") } /* namespace ipa::rkisp1::algorithms */ diff --git a/src/ipa/rkisp1/algorithms/cproc.h b/src/ipa/rkisp1/algorithms/cproc.h index fd38fd17e8bb..9b589ebd4ad7 100644 --- a/src/ipa/rkisp1/algorithms/cproc.h +++ b/src/ipa/rkisp1/algorithms/cproc.h @@ -30,6 +30,10 @@ public: void prepare(IPAContext &context, const uint32_t frame, IPAFrameContext &frameContext, RkISP1Params *params) override; + void process(IPAContext &context, const uint32_t frame, + IPAFrameContext &frameContext, + const rkisp1_stat_buffer *stats, + ControlList &metadata) override; }; } /* namespace ipa::rkisp1::algorithms */ From patchwork Wed Jan 14 17:39:08 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Kieran Bingham X-Patchwork-Id: 25804 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 C4387C32AF for ; Wed, 14 Jan 2026 17:39:37 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id F280D61FC4; Wed, 14 Jan 2026 18:39:33 +0100 (CET) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="owKFFVX1"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id D93D661FBC for ; Wed, 14 Jan 2026 18:39:24 +0100 (CET) Received: from Monstersaurus.infra.iob (cpc89244-aztw30-2-0-cust6594.18-1.cable.virginm.net [86.31.185.195]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id E43182832; Wed, 14 Jan 2026 18:38:57 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1768412338; bh=OfHo9f59ilEaoKpfeOq+LrAmt/9ZlkdVEq9PHJLVYrI=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=owKFFVX1mMrTMXad8FohUI29Bgzz9TO3O99EWXcGaghlYfBikPDqb+lI+xbJtFCLr KTSme10olAqGVvE2lhXr66x3ap5sSUr5Vt2/yE3CzJWrdOrAzy66lDxtnyENBaEgj2 /ceDmVYO6f8VDoVKUBWaprwnO5ohAE7XhFUMuB84= From: Kieran Bingham To: libcamera devel Cc: "van Veen, Stephan" , Isaac Scott , Kieran Bingham Subject: [PATCH v5 08/16] libcamera: controls: Define a new core Hue control Date: Wed, 14 Jan 2026 17:39:08 +0000 Message-ID: <20260114173918.1744023-9-kieran.bingham@ideasonboard.com> X-Mailer: git-send-email 2.52.0 In-Reply-To: <20260114173918.1744023-1-kieran.bingham@ideasonboard.com> References: <20260114173918.1744023-1-kieran.bingham@ideasonboard.com> MIME-Version: 1.0 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" From: "van Veen, Stephan" Define a new control to support configuration of Hue adjustments when supported by the available platform. Reviewed-by: Isaac Scott Signed-off-by: van Veen, Stephan [Kieran: Rework to define as a rotation in degrees] Signed-off-by: Kieran Bingham --- v5: - Reworked control definition and direction Signed-off-by: Kieran Bingham --- src/libcamera/control_ids_core.yaml | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/libcamera/control_ids_core.yaml b/src/libcamera/control_ids_core.yaml index 8e99bd84825f..67a44f8089bd 100644 --- a/src/libcamera/control_ids_core.yaml +++ b/src/libcamera/control_ids_core.yaml @@ -1356,4 +1356,19 @@ controls: Enable or disable lens dewarping. This control is only available if lens dewarp parameters are configured in the tuning file. + - Hue: + type: float + direction: inout + description: | + Adjusts the image hue (colour rotation) in degrees, as defined in + the HSL/HSV colour model. + + The value represents a rotation around the hue circle in HSL/HSV space: + positive values rotate hues clockwise (for example a +60° turns + Red hues to Yellow hues), and negative values rotate counter-clockwise + (a -60° turns Red hues to Magenta hues). + + The nominal range is [-180, 180[, where 0° leaves hues unchanged and the + range wraps around continuously, with 180° == -180°. + ... From patchwork Wed Jan 14 17:39:09 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Kieran Bingham X-Patchwork-Id: 25805 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 560C0C32C1 for ; Wed, 14 Jan 2026 17:39:38 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id CCD1A61FFA; Wed, 14 Jan 2026 18:39:34 +0100 (CET) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="NMrORDdC"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 2EF9F61FCE for ; Wed, 14 Jan 2026 18:39:25 +0100 (CET) Received: from Monstersaurus.infra.iob (cpc89244-aztw30-2-0-cust6594.18-1.cable.virginm.net [86.31.185.195]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 42F222833; Wed, 14 Jan 2026 18:38:58 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1768412338; bh=3VSP5skkjuqG/dNBEqFQ5bBMwDLQv2lUyV0JH8hiwcQ=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=NMrORDdCQr7zs6LTvBzLubeiiCsmyJZzl0y/y/qegPHvY1xHuyYFml7SaGXlbekH0 6eEVUHtLx40PukW9nPtVCopTwmWYeuPDH/biWHklxHc5h2KYEqKepNpYAdzHfqlI2l 2uC6MIHKK/Cs5FLWBE+2sewIh/cRj6ScTyahnoBo= From: Kieran Bingham To: libcamera devel Cc: Kieran Bingham , Isaac Scott Subject: [PATCH v5 09/16] ipa: rkisp1: cproc: Provide a Hue control Date: Wed, 14 Jan 2026 17:39:09 +0000 Message-ID: <20260114173918.1744023-10-kieran.bingham@ideasonboard.com> X-Mailer: git-send-email 2.52.0 In-Reply-To: <20260114173918.1744023-1-kieran.bingham@ideasonboard.com> References: <20260114173918.1744023-1-kieran.bingham@ideasonboard.com> MIME-Version: 1.0 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 RKISP1 supports a configurable Hue as part of the colour processing unit (cproc). Implement the new control converting to the hardware scale accordingly and report the applied control in the completed request metadata. This is implemented as a phase shift of the chrominance values between -90 and +87.188 degrees according to the datasheet however the type itself would imply that this is a range between -90 and 89.2969. Moreover, the hardware applies the inverse phase shift to the operation expected and documented by libcamera, so we apply a negative scale when converting the libcamera control to the Q<1,7> type, resulting in a range of -89.2969 to +90.0 degrees control. Reviewed-by: Isaac Scott Signed-off-by: Kieran Bingham --- v5: - Use Q<1, 7> directly and handle scaling in cproc.cpp - Use full string of quantized in debug log - Invert/Negate the hardware hue direction Signed-off-by: Kieran Bingham --- src/ipa/rkisp1/algorithms/cproc.cpp | 28 ++++++++++++++++++++++++++++ src/ipa/rkisp1/ipa_context.h | 3 +++ 2 files changed, 31 insertions(+) diff --git a/src/ipa/rkisp1/algorithms/cproc.cpp b/src/ipa/rkisp1/algorithms/cproc.cpp index e9e2b5444bc9..7484e4780094 100644 --- a/src/ipa/rkisp1/algorithms/cproc.cpp +++ b/src/ipa/rkisp1/algorithms/cproc.cpp @@ -37,8 +37,15 @@ namespace { constexpr float kDefaultBrightness = 0.0f; constexpr float kDefaultContrast = 1.0f; +constexpr float kDefaultHue = 0.0f; constexpr float kDefaultSaturation = 1.0f; +/* + * The Hue scale is negated as the hardware performs the opposite phase shift + * to what is expected and defined from the libcamera Hue control value. + */ +constexpr float kHueScale = -90.0f; + } /* namespace */ /** @@ -53,6 +60,11 @@ int ColorProcessing::init(IPAContext &context, cmap[&controls::Contrast] = ControlInfo(0.0f, 1.993f, kDefaultContrast); cmap[&controls::Saturation] = ControlInfo(0.0f, 1.993f, kDefaultSaturation); + /* Hue adjustment is negated by kHueScale, min/max are swapped */ + cmap[&controls::Hue] = ControlInfo(HueQ::TraitsType::max * kHueScale, + HueQ::TraitsType::min * kHueScale, + kDefaultHue); + return 0; } @@ -66,6 +78,7 @@ int ColorProcessing::configure(IPAContext &context, cproc.brightness = BrightnessQ(kDefaultBrightness); cproc.contrast = ContrastQ(kDefaultContrast); + cproc.hue = HueQ(kDefaultHue); cproc.saturation = SaturationQ(kDefaultSaturation); return 0; @@ -107,6 +120,18 @@ void ColorProcessing::queueRequest(IPAContext &context, LOG(RkISP1CProc, Debug) << "Set contrast to " << value; } + const auto &hue = controls.get(controls::Hue); + if (hue) { + /* Scale the Hue from ]-90, +90] */ + HueQ value = *hue / kHueScale; + if (cproc.hue != value) { + cproc.hue = value; + update = true; + } + + LOG(RkISP1CProc, Debug) << "Set hue to " << value; + } + const auto saturation = controls.get(controls::Saturation); if (saturation) { SaturationQ value = *saturation; @@ -120,6 +145,7 @@ void ColorProcessing::queueRequest(IPAContext &context, frameContext.cproc.brightness = cproc.brightness; frameContext.cproc.contrast = cproc.contrast; + frameContext.cproc.hue = cproc.hue; frameContext.cproc.saturation = cproc.saturation; frameContext.cproc.update = update; } @@ -140,6 +166,7 @@ void ColorProcessing::prepare([[maybe_unused]] IPAContext &context, config.setEnabled(true); config->brightness = frameContext.cproc.brightness.quantized(); config->contrast = frameContext.cproc.contrast.quantized(); + config->hue = frameContext.cproc.hue.quantized(); config->sat = frameContext.cproc.saturation.quantized(); } @@ -154,6 +181,7 @@ void ColorProcessing::process([[maybe_unused]] IPAContext &context, { metadata.set(controls::Brightness, frameContext.cproc.brightness.value()); metadata.set(controls::Contrast, frameContext.cproc.contrast.value()); + metadata.set(controls::Hue, frameContext.cproc.hue.value() * kHueScale); metadata.set(controls::Saturation, frameContext.cproc.saturation.value()); } diff --git a/src/ipa/rkisp1/ipa_context.h b/src/ipa/rkisp1/ipa_context.h index cdb9a17b5adc..80b035044cda 100644 --- a/src/ipa/rkisp1/ipa_context.h +++ b/src/ipa/rkisp1/ipa_context.h @@ -36,6 +36,7 @@ namespace ipa::rkisp1 { /* Fixed point types used by CPROC */ using BrightnessQ = Q<1, 7>; using ContrastQ = UQ<1, 7>; +using HueQ = Q<1, 7>; using SaturationQ = UQ<1, 7>; struct IPAHwSettings { @@ -123,6 +124,7 @@ struct IPAActiveState { struct { BrightnessQ brightness; ContrastQ contrast; + HueQ hue; SaturationQ saturation; } cproc; @@ -181,6 +183,7 @@ struct IPAFrameContext : public FrameContext { struct { BrightnessQ brightness; ContrastQ contrast; + HueQ hue; SaturationQ saturation; bool update; From patchwork Wed Jan 14 17:39:10 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Kieran Bingham X-Patchwork-Id: 25806 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 E1D07C32DE for ; Wed, 14 Jan 2026 17:39:38 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id D92C561FE7; Wed, 14 Jan 2026 18:39:36 +0100 (CET) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="DW1YCNOW"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 7591F61FA3 for ; Wed, 14 Jan 2026 18:39:25 +0100 (CET) Received: from Monstersaurus.infra.iob (cpc89244-aztw30-2-0-cust6594.18-1.cable.virginm.net [86.31.185.195]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 8971D2834; Wed, 14 Jan 2026 18:38:58 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1768412338; bh=fIV9OHZrRoc6aesZ5c1hTb9yYdL4a7NU94b5+uIIL0k=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=DW1YCNOWF7ebYueIXa8BVAv+ks06zb7JDuseNffWD2qFO8rvYNZN70pk3TFxvyO3Q RYcYApZRymzvsgDRGWcU46e0wi+sFEeCdB8iZ8cYC9USRwSM2Rb02t22UlDo1bk9zh xSBpaErikXar7cAl91uIRCwno/jxJVjarWMjG3xg= From: Kieran Bingham To: libcamera devel Cc: Kieran Bingham , Isaac Scott Subject: [PATCH v5 10/16] ipa: rkisp1: ccm: Use Q<4, 7> format directly Date: Wed, 14 Jan 2026 17:39:10 +0000 Message-ID: <20260114173918.1744023-11-kieran.bingham@ideasonboard.com> X-Mailer: git-send-email 2.52.0 In-Reply-To: <20260114173918.1744023-1-kieran.bingham@ideasonboard.com> References: <20260114173918.1744023-1-kieran.bingham@ideasonboard.com> MIME-Version: 1.0 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" Replace the legacy call to floatingToFixedPoint with the new FixedPoint quantizer to explicitly describe the type used by the RKISP1 Colour Correction Matrix. Reviewed-by: Isaac Scott Signed-off-by: Kieran Bingham --- v5: - Use Q<4, 7> Signed-off-by: Kieran Bingham --- src/ipa/rkisp1/algorithms/ccm.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/ipa/rkisp1/algorithms/ccm.cpp b/src/ipa/rkisp1/algorithms/ccm.cpp index de2b6fe775aa..466d7a116eea 100644 --- a/src/ipa/rkisp1/algorithms/ccm.cpp +++ b/src/ipa/rkisp1/algorithms/ccm.cpp @@ -117,8 +117,7 @@ void Ccm::setParameters(struct rkisp1_cif_isp_ctk_config &config, */ for (unsigned int i = 0; i < 3; i++) { for (unsigned int j = 0; j < 3; j++) - config.coeff[i][j] = - floatingToFixedPoint<4, 7, uint16_t, double>(matrix[i][j]); + config.coeff[i][j] = Q<4, 7>(matrix[i][j]).quantized(); } for (unsigned int i = 0; i < 3; i++) From patchwork Wed Jan 14 17:39:11 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Kieran Bingham X-Patchwork-Id: 25807 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 80337C3274 for ; Wed, 14 Jan 2026 17:39:39 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id E618C61FC7; Wed, 14 Jan 2026 18:39:38 +0100 (CET) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="SjL7wX5D"; 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 BA6FF61FC9 for ; Wed, 14 Jan 2026 18:39:25 +0100 (CET) Received: from Monstersaurus.infra.iob (cpc89244-aztw30-2-0-cust6594.18-1.cable.virginm.net [86.31.185.195]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id CCBD155C; Wed, 14 Jan 2026 18:38:58 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1768412338; bh=jUXrNpD5O+E67bCsFbHO1Y9uWRmV5hk6P4zDQcH5uHo=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=SjL7wX5DdvRR/lS73h3cnMWHa0SS/4iV5AwqQuOjEw7VKdTaOeFDnkEGfPQlZjFSk OqfZ6knHzirOGTqhC914KKs08hHldwd9fLLrPnff+a9MPaU/KNa+NkeQDQQumvQGVJ yroVsiJZSiGmaLywX8WCUfLiaq80OdlDwqyeV+hw= From: Kieran Bingham To: libcamera devel Cc: Kieran Bingham Subject: [PATCH v5 11/16] ipa: mali-c55: Reduce AWB calculations to float precision Date: Wed, 14 Jan 2026 17:39:11 +0000 Message-ID: <20260114173918.1744023-12-kieran.bingham@ideasonboard.com> X-Mailer: git-send-email 2.52.0 In-Reply-To: <20260114173918.1744023-1-kieran.bingham@ideasonboard.com> References: <20260114173918.1744023-1-kieran.bingham@ideasonboard.com> MIME-Version: 1.0 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 AWB calculations are determined using double precision, and then will be soon stored in a quantized float. Use float types for the intermediate types after the sums have been converted to an average to remove static cast assignments. Signed-off-by: Kieran Bingham --- v5: - New in v5 Signed-off-by: Kieran Bingham --- src/ipa/mali-c55/algorithms/awb.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/ipa/mali-c55/algorithms/awb.cpp b/src/ipa/mali-c55/algorithms/awb.cpp index 964e810882a9..b179dd7f0c1c 100644 --- a/src/ipa/mali-c55/algorithms/awb.cpp +++ b/src/ipa/mali-c55/algorithms/awb.cpp @@ -159,7 +159,7 @@ void Awb::process(IPAContext &context, const uint32_t frame, * Sometimes the first frame's statistics have no valid pixels, in which * case we'll just assume a grey world until they say otherwise. */ - double rgAvg, bgAvg; + float rgAvg, bgAvg; if (!counted_zones) { rgAvg = 1.0; bgAvg = 1.0; @@ -174,15 +174,15 @@ void Awb::process(IPAContext &context, const uint32_t frame, * figure by the gains that were applied when the statistics for this * frame were generated. */ - double rRatio = rgAvg / frameContext.awb.rGain; - double bRatio = bgAvg / frameContext.awb.bGain; + float rRatio = rgAvg / frameContext.awb.rGain; + float bRatio = bgAvg / frameContext.awb.bGain; /* * And then we can simply invert the ratio to find the gain we should * apply. */ - double rGain = 1 / rRatio; - double bGain = 1 / bRatio; + float rGain = 1 / rRatio; + float bGain = 1 / bRatio; /* * Running at full speed, this algorithm results in oscillations in the @@ -190,7 +190,7 @@ void Awb::process(IPAContext &context, const uint32_t frame, * changes in gain, unless we're in the startup phase in which case we * want to fix the miscolouring as quickly as possible. */ - double speed = frame < kNumStartupFrames ? 1.0 : 0.2; + float speed = frame < kNumStartupFrames ? 1.0 : 0.2; rGain = speed * rGain + context.activeState.awb.rGain * (1.0 - speed); bGain = speed * bGain + context.activeState.awb.bGain * (1.0 - speed); From patchwork Wed Jan 14 17:39:12 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Kieran Bingham X-Patchwork-Id: 25810 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 6CCE3C3284 for ; Wed, 14 Jan 2026 17:39:41 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 34B1261FA3; Wed, 14 Jan 2026 18:39:40 +0100 (CET) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="DBn/xVHA"; 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 1AA5061FD0 for ; Wed, 14 Jan 2026 18:39:26 +0100 (CET) Received: from Monstersaurus.infra.iob (cpc89244-aztw30-2-0-cust6594.18-1.cable.virginm.net [86.31.185.195]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 22195287C; Wed, 14 Jan 2026 18:38:59 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1768412339; bh=KFWe/S6pDckoc9/mrpFYaJ5xwIklopkCX+/y971PI/0=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=DBn/xVHAbjR/umxUV/m29ifKb4vm8pkv3WiZxSb7SbmHbePfLGhgARrdGijmlBOjX GG4J+DJSIfrajQ0LPajLeD1XTy4FQwmbCFGROzV/2JPd2OrUeUpwHT/TuoSPbAEv92 btoFY6W4T2uYmBn4FWr9ONEkFbKhREaXSUGL0rOY= From: Kieran Bingham To: libcamera devel Cc: Kieran Bingham , Isaac Scott , =?utf-8?q?Barnab=C3=A1s_P=C5=91cze?= Subject: [PATCH v5 12/16] ipa: mali-c55: Convert AWB to UQ<4, 8> usage Date: Wed, 14 Jan 2026 17:39:12 +0000 Message-ID: <20260114173918.1744023-13-kieran.bingham@ideasonboard.com> X-Mailer: git-send-email 2.52.0 In-Reply-To: <20260114173918.1744023-1-kieran.bingham@ideasonboard.com> References: <20260114173918.1744023-1-kieran.bingham@ideasonboard.com> MIME-Version: 1.0 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" Utilise the new FixedPoint type to explicitly calculate gains for AWB in Q4.8 format. This ensures that reporting of gains in metadata reflect the true AWB gains applied. Reviewed-by: Isaac Scott Reviewed-by: Barnabás Pőcze Signed-off-by: Kieran Bingham --- v5: - Use UQ<4, 8> directly - Change debug prints to use operator<< - Now based on top of float types instead of doubles Signed-off-by: Kieran Bingham --- src/ipa/mali-c55/algorithms/awb.cpp | 32 ++++++++++++++--------------- src/ipa/mali-c55/ipa_context.h | 10 +++++---- 2 files changed, 22 insertions(+), 20 deletions(-) diff --git a/src/ipa/mali-c55/algorithms/awb.cpp b/src/ipa/mali-c55/algorithms/awb.cpp index b179dd7f0c1c..3b18eb645020 100644 --- a/src/ipa/mali-c55/algorithms/awb.cpp +++ b/src/ipa/mali-c55/algorithms/awb.cpp @@ -37,8 +37,8 @@ int Awb::configure([[maybe_unused]] IPAContext &context, * for the first frame we will make no assumptions and leave the R/B * channels unmodified. */ - context.activeState.awb.rGain = 1.0; - context.activeState.awb.bGain = 1.0; + context.activeState.awb.rGain = 1.0f; + context.activeState.awb.bGain = 1.0f; return 0; } @@ -46,8 +46,8 @@ int Awb::configure([[maybe_unused]] IPAContext &context, void Awb::fillGainsParamBlock(MaliC55Params *params, IPAContext &context, IPAFrameContext &frameContext) { - double rGain = context.activeState.awb.rGain; - double bGain = context.activeState.awb.bGain; + UQ<4, 8> rGain = context.activeState.awb.rGain; + UQ<4, 8> bGain = context.activeState.awb.bGain; /* * The gains here map as follows: @@ -61,10 +61,10 @@ void Awb::fillGainsParamBlock(MaliC55Params *params, IPAContext &context, */ auto block = params->block(); - block->gain00 = floatingToFixedPoint<4, 8, uint16_t, double>(rGain); - block->gain01 = floatingToFixedPoint<4, 8, uint16_t, double>(1.0); - block->gain10 = floatingToFixedPoint<4, 8, uint16_t, double>(1.0); - block->gain11 = floatingToFixedPoint<4, 8, uint16_t, double>(bGain); + block->gain00 = rGain.quantized(); + block->gain01 = UQ<4, 8>(1.0f).quantized(); + block->gain10 = UQ<4, 8>(1.0f).quantized(); + block->gain11 = bGain.quantized(); frameContext.awb.rGain = rGain; frameContext.awb.bGain = bGain; @@ -150,8 +150,8 @@ void Awb::process(IPAContext &context, const uint32_t frame, * The statistics are in Q4.8 format, so we convert to double * here. */ - rgSum += fixedToFloatingPoint<4, 8, double, uint16_t>(awb_ratios[i].avg_rg_gr); - bgSum += fixedToFloatingPoint<4, 8, double, uint16_t>(awb_ratios[i].avg_bg_br); + rgSum += UQ<4, 8>(awb_ratios[i].avg_rg_gr).value(); + bgSum += UQ<4, 8>(awb_ratios[i].avg_bg_br).value(); counted_zones++; } @@ -174,8 +174,8 @@ void Awb::process(IPAContext &context, const uint32_t frame, * figure by the gains that were applied when the statistics for this * frame were generated. */ - float rRatio = rgAvg / frameContext.awb.rGain; - float bRatio = bgAvg / frameContext.awb.bGain; + float rRatio = rgAvg / frameContext.awb.rGain.value(); + float bRatio = bgAvg / frameContext.awb.bGain.value(); /* * And then we can simply invert the ratio to find the gain we should @@ -191,15 +191,15 @@ void Awb::process(IPAContext &context, const uint32_t frame, * want to fix the miscolouring as quickly as possible. */ float speed = frame < kNumStartupFrames ? 1.0 : 0.2; - rGain = speed * rGain + context.activeState.awb.rGain * (1.0 - speed); - bGain = speed * bGain + context.activeState.awb.bGain * (1.0 - speed); + rGain = speed * rGain + context.activeState.awb.rGain.value() * (1.0 - speed); + bGain = speed * bGain + context.activeState.awb.bGain.value() * (1.0 - speed); context.activeState.awb.rGain = rGain; context.activeState.awb.bGain = bGain; metadata.set(controls::ColourGains, { - static_cast(frameContext.awb.rGain), - static_cast(frameContext.awb.bGain), + frameContext.awb.rGain.value(), + frameContext.awb.bGain.value(), }); LOG(MaliC55Awb, Debug) << "For frame number " << frame << ": " diff --git a/src/ipa/mali-c55/ipa_context.h b/src/ipa/mali-c55/ipa_context.h index 13885eb83b5c..08f78e4f74ce 100644 --- a/src/ipa/mali-c55/ipa_context.h +++ b/src/ipa/mali-c55/ipa_context.h @@ -14,6 +14,8 @@ #include +#include "libipa/fixedpoint.h" + namespace libcamera { namespace ipa::mali_c55 { @@ -53,8 +55,8 @@ struct IPAActiveState { } agc; struct { - double rGain; - double bGain; + UQ<4, 8> rGain; + UQ<4, 8> bGain; } awb; }; @@ -66,8 +68,8 @@ struct IPAFrameContext : public FrameContext { } agc; struct { - double rGain; - double bGain; + UQ<4, 8> rGain; + UQ<4, 8> bGain; } awb; }; From patchwork Wed Jan 14 17:39:13 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Kieran Bingham X-Patchwork-Id: 25808 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 32283C32E0 for ; Wed, 14 Jan 2026 17:39:41 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id AD96C61FD3; Wed, 14 Jan 2026 18:39:40 +0100 (CET) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="Nq2Bu8rQ"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 5F91B61FD3 for ; Wed, 14 Jan 2026 18:39:26 +0100 (CET) Received: from Monstersaurus.infra.iob (cpc89244-aztw30-2-0-cust6594.18-1.cable.virginm.net [86.31.185.195]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 7395A287D; Wed, 14 Jan 2026 18:38:59 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1768412339; bh=G4tGhqZOlaCD71sTSW2BmCA+2eytL6hyl6sEqgU4aHo=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=Nq2Bu8rQ+Yz5F71lNDwORrLTFLT5uyEtWU4fodyphw9GwZZZVJ0NzOmHGvhkTFoqE Hg6KVFUBhx+CTfiPvNqjpCKay5kPEUxS5AfqxxCkxP6/7tSUoPN9ZTCxtaJlDaE464 ijDqoXIkWZiU57zPVKSUOE6y1t8qV7RvK8m6XW2A= From: Kieran Bingham To: libcamera devel Cc: Kieran Bingham , Isaac Scott Subject: [PATCH v5 13/16] ipa: mali-c55: agc: Quantise the ISP Digital Gain Date: Wed, 14 Jan 2026 17:39:13 +0000 Message-ID: <20260114173918.1744023-14-kieran.bingham@ideasonboard.com> X-Mailer: git-send-email 2.52.0 In-Reply-To: <20260114173918.1744023-1-kieran.bingham@ideasonboard.com> References: <20260114173918.1744023-1-kieran.bingham@ideasonboard.com> MIME-Version: 1.0 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 Mali-C55 ISP has a digital gain block which allows setting gain in Q5.8 format, a range of 0.0 to (very nearly) 32.0. Convert usage to the new UQ<5, 8> FixedPoint Quantised type which will support the conversion, clamping and quantisation so that the metadata and debug prints can now report the effective gain applied instead of the potentially inaccurate float. As the UQ<5, 8> type already clamps values, remove the explicit clamping. This removes the clamping to a minimum of 1.0 gain, so we rely on calculateNewEv to provide a valid gain. Reviewed-by: Isaac Scott Signed-off-by: Kieran Bingham --- v5: - Use UQ<5, 8> type directly. - Use operator<< to report the UQ<5, 8> value. Signed-off-by: Kieran Bingham --- src/ipa/mali-c55/algorithms/agc.cpp | 18 +++++++++--------- src/ipa/mali-c55/ipa_context.h | 6 +++--- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/ipa/mali-c55/algorithms/agc.cpp b/src/ipa/mali-c55/algorithms/agc.cpp index 014fd12452ac..075717f44ea6 100644 --- a/src/ipa/mali-c55/algorithms/agc.cpp +++ b/src/ipa/mali-c55/algorithms/agc.cpp @@ -38,8 +38,8 @@ static constexpr unsigned int kNumHistogramBins = 256; * format, a range of 0.0 to (very nearly) 32.0. We clamp from 1.0 to the actual * max value which is 8191 * 2^-8. */ -static constexpr double kMinDigitalGain = 1.0; -static constexpr double kMaxDigitalGain = 31.99609375; +static constexpr float kMinDigitalGain = 1.0; +static constexpr float kMaxDigitalGain = UQ<5, 8>::TraitsType::max; uint32_t AgcStatistics::decodeBinValue(uint16_t binVal) { @@ -245,7 +245,7 @@ void Agc::fillGainParamBlock(IPAContext &context, IPAFrameContext &frameContext, MaliC55Params *params) { IPAActiveState &activeState = context.activeState; - double gain; + UQ<5, 8> gain; if (activeState.agc.autoEnabled) gain = activeState.agc.automatic.ispGain; @@ -253,7 +253,7 @@ void Agc::fillGainParamBlock(IPAContext &context, IPAFrameContext &frameContext, gain = activeState.agc.manual.ispGain; auto block = params->block(); - block->gain = floatingToFixedPoint<5, 8, uint16_t, double>(gain); + block->gain = gain.quantized(); frameContext.agc.ispGain = gain; } @@ -358,7 +358,7 @@ void Agc::process(IPAContext &context, */ uint32_t exposure = frameContext.agc.exposure; double analogueGain = frameContext.agc.sensorGain; - double digitalGain = frameContext.agc.ispGain; + double digitalGain = frameContext.agc.ispGain.value(); double totalGain = analogueGain * digitalGain; utils::Duration currentShutter = exposure * configuration.sensor.lineDuration; utils::Duration effectiveExposureValue = currentShutter * totalGain; @@ -370,19 +370,19 @@ void Agc::process(IPAContext &context, activeState.agc.exposureMode, statistics_.yHist, effectiveExposureValue); - dGain = std::clamp(dGain, kMinDigitalGain, kMaxDigitalGain); + UQ<5, 8> dGainQ = static_cast(dGain); LOG(MaliC55Agc, Debug) << "Divided up shutter, analogue gain and digital gain are " - << shutterTime << ", " << aGain << " and " << dGain; + << shutterTime << ", " << aGain << " and " << dGainQ; activeState.agc.automatic.exposure = shutterTime / configuration.sensor.lineDuration; activeState.agc.automatic.sensorGain = aGain; - activeState.agc.automatic.ispGain = dGain; + activeState.agc.automatic.ispGain = dGainQ; metadata.set(controls::ExposureTime, currentShutter.get()); metadata.set(controls::AnalogueGain, frameContext.agc.sensorGain); - metadata.set(controls::DigitalGain, frameContext.agc.ispGain); + metadata.set(controls::DigitalGain, frameContext.agc.ispGain.value()); metadata.set(controls::ColourTemperature, context.activeState.agc.temperatureK); } diff --git a/src/ipa/mali-c55/ipa_context.h b/src/ipa/mali-c55/ipa_context.h index 08f78e4f74ce..ac4b83773803 100644 --- a/src/ipa/mali-c55/ipa_context.h +++ b/src/ipa/mali-c55/ipa_context.h @@ -41,12 +41,12 @@ struct IPAActiveState { struct { uint32_t exposure; double sensorGain; - double ispGain; + UQ<5, 8> ispGain; } automatic; struct { uint32_t exposure; double sensorGain; - double ispGain; + UQ<5, 8> ispGain; } manual; bool autoEnabled; uint32_t constraintMode; @@ -64,7 +64,7 @@ struct IPAFrameContext : public FrameContext { struct { uint32_t exposure; double sensorGain; - double ispGain; + UQ<5, 8> ispGain; } agc; struct { From patchwork Wed Jan 14 17:39:14 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Kieran Bingham X-Patchwork-Id: 25809 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 ADD5CC32E7 for ; Wed, 14 Jan 2026 17:39:41 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 5CC1B61FCA; Wed, 14 Jan 2026 18:39:41 +0100 (CET) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="wfWH/Jt8"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id A5C0D61FC7 for ; Wed, 14 Jan 2026 18:39:26 +0100 (CET) Received: from Monstersaurus.infra.iob (cpc89244-aztw30-2-0-cust6594.18-1.cable.virginm.net [86.31.185.195]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id B97AE1E2D; Wed, 14 Jan 2026 18:38:59 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1768412339; bh=mNtRMd8bKzNeQnB+XSQB5a1XD/+ZWBI8bzkzJbFyAl8=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=wfWH/Jt85hG0J2YFmFzFd9Zv0Cd+IGEWIYUOMITKyd8phI5A6bSqnt0kkVJjHSZn8 R+IkvVCwqDVCSeKv5blK5pXh1z9nEN7kI5SbCPKyrBSSzU080qEdpUYGDMI0JucPUW cSvrpIw5lHQJmogWfHmbY28q23A+33TIC/KT5nSg= From: Kieran Bingham To: libcamera devel Cc: Kieran Bingham , Isaac Scott , Laurent Pinchart Subject: [PATCH v5 14/16] test: libipa: Remove legacy fixed point conversion test Date: Wed, 14 Jan 2026 17:39:14 +0000 Message-ID: <20260114173918.1744023-15-kieran.bingham@ideasonboard.com> X-Mailer: git-send-email 2.52.0 In-Reply-To: <20260114173918.1744023-1-kieran.bingham@ideasonboard.com> References: <20260114173918.1744023-1-kieran.bingham@ideasonboard.com> MIME-Version: 1.0 X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" Now that the fixed point conversions are equally covered by the new Q types, the legacy tests for fixedToFloatingPoint and floatingToFixedPoint are redundant. Remove them, and replace the existing test cases with equivalant tests using the new Q4.7 type directly to maintain identical test coverage. Reviewed-by: Isaac Scott Reviewed-by: Laurent Pinchart Signed-off-by: Kieran Bingham --- v4: - Remove redundant "No Sign Extension" comment v5: - Simplify to new Q<4, 7> - Squash two patches together - test: libipa: Remove legacy fixed point conversion test - test: libipa: Add Q4.7 type and tests to match existing use case tests Signed-off-by: Kieran Bingham --- test/ipa/libipa/fixedpoint.cpp | 93 ++++++---------------------------- 1 file changed, 15 insertions(+), 78 deletions(-) diff --git a/test/ipa/libipa/fixedpoint.cpp b/test/ipa/libipa/fixedpoint.cpp index 9fa7fa688c24..4c47574fd548 100644 --- a/test/ipa/libipa/fixedpoint.cpp +++ b/test/ipa/libipa/fixedpoint.cpp @@ -23,80 +23,6 @@ using namespace ipa; class FixedPointUtilsTest : public Test { protected: - /* R for real, I for integer */ - template - int testFixedToFloat(I input, R expected) - { - R out = fixedToFloatingPoint(input); - R prec = 1.0 / (1 << FracPrec); - if (std::abs(out - expected) > prec) { - cerr << "Reverse conversion expected " << input - << " to convert to " << expected - << ", got " << out << std::endl; - return TestFail; - } - - return TestPass; - } - - template - int testSingleFixedPoint(double input, T expected) - { - T ret = floatingToFixedPoint(input); - if (ret != expected) { - cerr << "Expected " << input << " to convert to " - << expected << ", got " << ret << std::endl; - return TestFail; - } - - /* - * The precision check is fairly arbitrary but is based on what - * the rkisp1 is capable of in the crosstalk module. - */ - double f = fixedToFloatingPoint(ret); - if (std::abs(f - input) > 0.005) { - cerr << "Reverse conversion expected " << ret - << " to convert to " << input - << ", got " << f << std::endl; - return TestFail; - } - - return TestPass; - } - - int testFixedPoint() - { - /* - * The second 7.992 test is to test that unused bits don't - * affect the result. - */ - std::map testCases = { - { 7.992, 0x3ff }, - { 0.2, 0x01a }, - { -0.2, 0x7e6 }, - { -0.8, 0x79a }, - { -0.4, 0x7cd }, - { -1.4, 0x74d }, - { -8, 0x400 }, - { 0, 0 }, - }; - - int ret; - for (const auto &testCase : testCases) { - ret = testSingleFixedPoint<4, 7, int16_t>(testCase.first, - testCase.second); - if (ret != TestPass) - return ret; - } - - /* Special case with a superfluous one in the unused bits */ - ret = testFixedToFloat<4, 7, int16_t, double>(0xbff, 7.992); - if (ret != TestPass) - return ret; - - return TestPass; - } - template int quantizedCheck(float input, typename Q::QuantizedType expected, float value) { @@ -163,6 +89,21 @@ protected: fails += quantizedCheck>(1.0f, 0b1'0000000, 1.0f); /* Mid */ fails += quantizedCheck>(1.992f, 0b1'1111111, 1.99219f); /* Max */ + /* Q4.7(-8 .. 7.99219) Min: [0x0400:-8] -- Max: [0x03ff:7.99219] Step:0.0078125 */ + introduce>("Q4.7"); + fails += quantizedCheck>(-8.0f, 0b1000'0000000, -8.0f); /* Min */ + fails += quantizedCheck>(-0.008f, 0b1111'1111111, -0.0078125); /* -1 step */ + fails += quantizedCheck>( 0.0f, 0b0000'0000000, 0.0f); /* Zero */ + fails += quantizedCheck>( 0.008f, 0b0000'0000001, 0.0078125f); /* +1 step */ + fails += quantizedCheck>( 7.992f, 0b0111'1111111, 7.99219f); /* Max */ + + /* Retain additional tests from original testFixedPoint() */ + fails += quantizedCheck>( 0.2f, 0b0000'0011010, 0.203125f); /* 0x01a */ + fails += quantizedCheck>(-0.2f, 0b1111'1100110, -0.203125f); /* 0x7e6 */ + fails += quantizedCheck>(-0.8f, 0b1111'0011010, -0.796875f); /* 0x79a */ + fails += quantizedCheck>(-0.4f, 0b1111'1001101, -0.398438f); /* 0x7cd */ + fails += quantizedCheck>(-1.4f, 0b1110'1001101, -1.39844f); /* 0x74d */ + /* UQ4.8(0 .. 15.9961) Min: [0x0000:0] -- Max: [0x0fff:15.9961] Step:0.00390625 */ introduce>("UQ4.8"); fails += quantizedCheck>( 0.0f, 0b0000'00000000, 0.00f); @@ -212,10 +153,6 @@ protected: { unsigned int fails = 0; - /* fixed point conversion test */ - if (testFixedPoint() != TestPass) - fails++; - if (testFixedPointQuantizers() != TestPass) fails++; From patchwork Wed Jan 14 17:39:15 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Kieran Bingham X-Patchwork-Id: 25811 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 76B83C32EA for ; Wed, 14 Jan 2026 17:39:42 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 18B5A61FD7; Wed, 14 Jan 2026 18:39:42 +0100 (CET) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="CiwYs72H"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id EFDD461FD9 for ; Wed, 14 Jan 2026 18:39:26 +0100 (CET) Received: from Monstersaurus.infra.iob (cpc89244-aztw30-2-0-cust6594.18-1.cable.virginm.net [86.31.185.195]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 104C82835; Wed, 14 Jan 2026 18:39:00 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1768412340; bh=p59VEdYtGBIOVU/88itgeMbzeNFIHgsYdld9mxiccq4=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=CiwYs72HJN3GEj0dtukideL2hykVVTUUH6mtTomyR6mvaQW68RLi11c+N3K3ysSWb tRz84sDgJlS7dVSLlEcbdc+8vpWFfgxEQuA0c4TCkQ16mxkX/3X5sHJIS59XHS4rdQ X0r0IIZDXrX//11T1VF0MUj5yG76cTqtdZ3DQbFU= From: Kieran Bingham To: libcamera devel Cc: Kieran Bingham , Isaac Scott , Laurent Pinchart Subject: [PATCH v5 15/16] ipa: libipa: fixedpoint: Move float conversion inline Date: Wed, 14 Jan 2026 17:39:15 +0000 Message-ID: <20260114173918.1744023-16-kieran.bingham@ideasonboard.com> X-Mailer: git-send-email 2.52.0 In-Reply-To: <20260114173918.1744023-1-kieran.bingham@ideasonboard.com> References: <20260114173918.1744023-1-kieran.bingham@ideasonboard.com> MIME-Version: 1.0 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" With all users of the floatingToFixedPoint and fixedToFloatingPoint calls converted to use the FixedPoint Quantized types, remove the calls and documentation and move the implementation inline in the FixedPointTraits implementation. Reviewed-by: Isaac Scott Reviewed-by: Laurent Pinchart Signed-off-by: Kieran Bingham --- v5: - Reflow text when moved Signed-off-by: Kieran Bingham --- src/ipa/libipa/fixedpoint.cpp | 22 ---------- src/ipa/libipa/fixedpoint.h | 77 +++++++++++------------------------ 2 files changed, 24 insertions(+), 75 deletions(-) diff --git a/src/ipa/libipa/fixedpoint.cpp b/src/ipa/libipa/fixedpoint.cpp index 43d76f745d8a..6a15f9b068c7 100644 --- a/src/ipa/libipa/fixedpoint.cpp +++ b/src/ipa/libipa/fixedpoint.cpp @@ -15,28 +15,6 @@ namespace libcamera { namespace ipa { -/** - * \fn R floatingToFixedPoint(T number) - * \brief Convert a floating point number to a fixed-point representation - * \tparam I Bit width of the integer part of the fixed-point - * \tparam F Bit width of the fractional part of the fixed-point - * \tparam R Return type of the fixed-point representation - * \tparam T Input type of the floating point representation - * \param number The floating point number to convert to fixed point - * \return The converted value - */ - -/** - * \fn R fixedToFloatingPoint(T number) - * \brief Convert a fixed-point number to a floating point representation - * \tparam I Bit width of the integer part of the fixed-point - * \tparam F Bit width of the fractional part of the fixed-point - * \tparam R Return type of the floating point representation - * \tparam T Input type of the fixed-point representation - * \param number The fixed point number to convert to floating point - * \return The converted value - */ - /** * \struct libcamera::ipa::FixedPointQTraits * \brief Traits type implementing fixed-point quantisation conversions diff --git a/src/ipa/libipa/fixedpoint.h b/src/ipa/libipa/fixedpoint.h index 4f6ee081604b..1d53b0b9fdde 100644 --- a/src/ipa/libipa/fixedpoint.h +++ b/src/ipa/libipa/fixedpoint.h @@ -16,55 +16,6 @@ namespace libcamera { namespace ipa { -#ifndef __DOXYGEN__ -template && - std::is_floating_point_v> * = nullptr> -#else -template -#endif -constexpr R floatingToFixedPoint(T number) -{ - static_assert(sizeof(int) >= sizeof(R)); - static_assert(I + F <= sizeof(R) * 8); - - /* - * The intermediate cast to int is needed on arm platforms to properly - * cast negative values. See - * https://embeddeduse.com/2013/08/25/casting-a-negative-float-to-an-unsigned-int/ - */ - R mask = (1 << (F + I)) - 1; - R frac = static_cast(static_cast(std::round(number * (1 << F)))) & mask; - - return frac; -} - -#ifndef __DOXYGEN__ -template && - std::is_integral_v> * = nullptr> -#else -template -#endif -constexpr R fixedToFloatingPoint(T number) -{ - static_assert(sizeof(int) >= sizeof(T)); - static_assert(I + F <= sizeof(T) * 8); - - if constexpr (std::is_unsigned_v) - return static_cast(number) / static_cast(1 << F); - - /* - * Recreate the upper bits in case of a negative number by shifting the sign - * bit from the fixed point to the first bit of the unsigned and then right shifting - * by the same amount which keeps the sign bit in place. - * This can be optimized by the compiler quite well. - */ - int remaining_bits = sizeof(int) * 8 - (I + F); - int t = static_cast(static_cast(number) << remaining_bits) >> remaining_bits; - return static_cast(t) / static_cast(1 << F); -} - template struct FixedPointQTraits { private: @@ -91,11 +42,25 @@ public: static constexpr float toFloat(QuantizedType q) { - return fixedToFloatingPoint(q); + if constexpr (std::is_unsigned_v) + return static_cast(q) / static_cast(1 << F); + + /* + * Recreate the upper bits in case of a negative number by + * shifting the sign bit from the fixed point to the first bit + * of the unsigned and then right shifting by the same amount + * which keeps the sign bit in place. This can be optimized by + * the compiler quite well. + */ + static_assert(sizeof(int) >= sizeof(T)); + + int remaining_bits = sizeof(int) * 8 - (I + F); + int t = static_cast(static_cast(q) << remaining_bits) >> remaining_bits; + return static_cast(t) / static_cast(1 << F); } - static constexpr float min = fixedToFloatingPoint(qMin); - static constexpr float max = fixedToFloatingPoint(qMax); + static constexpr float min = toFloat(qMin); + static constexpr float max = toFloat(qMax); static_assert(min < max, "FixedPointQTraits: Minimum must be less than maximum"); @@ -103,7 +68,13 @@ public: static QuantizedType fromFloat(float v) { v = std::clamp(v, min, max); - return floatingToFixedPoint(v); + + /* + * The intermediate cast to int is needed on arm platforms to + * properly cast negative values. See + * https://embeddeduse.com/2013/08/25/casting-a-negative-float-to-an-unsigned-int/ + */ + return static_cast(static_cast(std::round(v * (1 << F)))) & bitMask; } }; From patchwork Wed Jan 14 17:39:16 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Kieran Bingham X-Patchwork-Id: 25812 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 B1E51C3285 for ; Wed, 14 Jan 2026 17:39:43 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id A5F6161FEC; Wed, 14 Jan 2026 18:39:42 +0100 (CET) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="emw6MT0M"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 4AD8B61FDA for ; Wed, 14 Jan 2026 18:39:27 +0100 (CET) Received: from Monstersaurus.infra.iob (cpc89244-aztw30-2-0-cust6594.18-1.cable.virginm.net [86.31.185.195]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 58D0828F5; Wed, 14 Jan 2026 18:39:00 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1768412340; bh=xNyBP2yal5rcjb+yjDnH7ikpoplhq9vsZMBSBVS+H5Q=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=emw6MT0MxQ/NZygx2STj3mhWBnQyXJwZtAQ/cFuAqWYS4fPRQcL4O9UUJPmUPa/2y G0pNftmGjHNqYtmL2szhnaiu1a2RPTXdvotAawNbdZSDMPi0Lae7s9cramqCLs7fWa 7eO9fkFU3R+GMJVNHuLIWAjBB6uvh7+LAprdwSVg= From: Kieran Bingham To: libcamera devel Cc: Kieran Bingham , =?utf-8?q?Barnab?= =?utf-8?b?w6FzIFDFkWN6ZQ==?= Subject: [PATCH v5 16/16] ipa: libipa: fixedpoint: Remove unsigned workaround Date: Wed, 14 Jan 2026 17:39:16 +0000 Message-ID: <20260114173918.1744023-17-kieran.bingham@ideasonboard.com> X-Mailer: git-send-email 2.52.0 In-Reply-To: <20260114173918.1744023-1-kieran.bingham@ideasonboard.com> References: <20260114173918.1744023-1-kieran.bingham@ideasonboard.com> MIME-Version: 1.0 X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" Now that we no longer attempt or allow storing signed negative floats as an unsigned integer representation, remove the workaround which required an intermediate cast when converting floats to fixed point values. Reviewed-by: Barnabás Pőcze Signed-off-by: Kieran Bingham --- src/ipa/libipa/fixedpoint.h | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/ipa/libipa/fixedpoint.h b/src/ipa/libipa/fixedpoint.h index 1d53b0b9fdde..b4190988f1ee 100644 --- a/src/ipa/libipa/fixedpoint.h +++ b/src/ipa/libipa/fixedpoint.h @@ -69,12 +69,7 @@ public: { v = std::clamp(v, min, max); - /* - * The intermediate cast to int is needed on arm platforms to - * properly cast negative values. See - * https://embeddeduse.com/2013/08/25/casting-a-negative-float-to-an-unsigned-int/ - */ - return static_cast(static_cast(std::round(v * (1 << F)))) & bitMask; + return static_cast(std::round(v * (1 << F))) & bitMask; } };