From patchwork Wed Oct 29 17:24:26 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Kieran Bingham X-Patchwork-Id: 24890 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 3655DC3259 for ; Wed, 29 Oct 2025 17:24:54 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id B3D5860891; Wed, 29 Oct 2025 18:24:48 +0100 (CET) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="vVHh20r9"; 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 A2E8460856 for ; Wed, 29 Oct 2025 18:24:46 +0100 (CET) Received: from Monstersaurus.hippo-penny.ts.net (cpc89244-aztw30-2-0-cust6594.18-1.cable.virginm.net [86.31.185.195]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 1E22C1E27; Wed, 29 Oct 2025 18:22:57 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1761758577; bh=KwSC2Iobl3oCuLAxMJLb1pnDcgYuRMDkXhCkNVLonOs=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=vVHh20r9BTDKDj/tZsQeBTAe3QOYUCUjO1YBnorKfhWmY5ELp1T6Y1eHL166HtD24 DmjLkN2xc897RfyVuo98YBwNog/aEHbYT0ZfdakttS3pmswJnOQmSomyACMm7fU9t+ wuMVC4e+XaTx6zmVa5WsraaJ3ejlD+Cn7b+EPUI8= From: Kieran Bingham To: libcamera devel Cc: Kieran Bingham Subject: [PATCH v2 01/13] libipa: Provide a Quantized data type support Date: Wed, 29 Oct 2025 17:24:26 +0000 Message-ID: <20251029172439.1513907-2-kieran.bingham@ideasonboard.com> X-Mailer: git-send-email 2.50.1 In-Reply-To: <20251029172439.1513907-1-kieran.bingham@ideasonboard.com> References: <20251029172439.1513907-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. Signed-off-by: Kieran Bingham --- src/ipa/libipa/meson.build | 2 + src/ipa/libipa/quantized.cpp | 134 +++++++++++++++++++++++++++++++++++ src/ipa/libipa/quantized.h | 79 +++++++++++++++++++++ 3 files changed, 215 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 660be94054fa..804289778f72 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', ]) libipa_sources = files([ @@ -36,6 +37,7 @@ libipa_sources = files([ 'lux.cpp', 'module.cpp', 'pwl.cpp', + 'quantized.cpp', ]) libipa_includes = include_directories('..') diff --git a/src/ipa/libipa/quantized.cpp b/src/ipa/libipa/quantized.cpp new file mode 100644 index 000000000000..9772674159bd --- /dev/null +++ b/src/ipa/libipa/quantized.cpp @@ -0,0 +1,134 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2025, Ideas On Board. + * + * 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::quantized_type + * \brief The integer type used for the quantized representation + * + * This alias corresponds to \c TraitsType::quantized_type, 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(quantized_type 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=(quantized_type 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 + */ + +} /* namespace ipa */ + +} /* namespace libcamera */ diff --git a/src/ipa/libipa/quantized.h b/src/ipa/libipa/quantized.h new file mode 100644 index 000000000000..26cb8d619e15 --- /dev/null +++ b/src/ipa/libipa/quantized.h @@ -0,0 +1,79 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2025, Ideas On Board. + * + * Helper class to manage conversions between floating point types and quantized + * storage and representation of those values. + */ + +#pragma once + +#include +#include +#include +#include + +#include + +namespace libcamera { + +namespace ipa { + +template +struct Quantized { + using TraitsType = Traits; + using quantized_type = typename Traits::quantized_type; + static_assert(std::is_arithmetic_v, + "Quantized: quantized_type must be arithmetic"); + + /* Constructors */ + Quantized() + : Quantized(0.0f) {} + Quantized(float x) { *this = x; } + Quantized(quantized_type x) { *this = x; } + + Quantized &operator=(float x) + { + quantized_ = Traits::fromFloat(x); + value_ = Traits::toFloat(quantized_); + return *this; + } + + Quantized &operator=(quantized_type x) + { + value_ = Traits::toFloat(x); + quantized_ = x; + return *this; + } + + float value() const noexcept { return value_; } + quantized_type quantized() const noexcept { return quantized_; } + + std::string toString() const + { + std::ostringstream oss; + + oss << "Q:" << utils::hex(quantized_) + << " V:" << value_; + + return oss.str(); + } + + bool operator==(const Quantized &other) const noexcept + { + return quantized_ == other.quantized_; + } + + bool operator!=(const Quantized &other) const noexcept + { + return !(*this == other); + } + +private: + quantized_type quantized_{}; + float value_{}; +}; + +} /* namespace ipa */ + +} /* namespace libcamera */ From patchwork Wed Oct 29 17:24:27 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Kieran Bingham X-Patchwork-Id: 24891 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 DFFA3C32CE for ; Wed, 29 Oct 2025 17:24:55 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 14C5A608CA; Wed, 29 Oct 2025 18:24:50 +0100 (CET) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="ECzXV0dj"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id CDF2B60859 for ; Wed, 29 Oct 2025 18:24:46 +0100 (CET) Received: from Monstersaurus.hippo-penny.ts.net (cpc89244-aztw30-2-0-cust6594.18-1.cable.virginm.net [86.31.185.195]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 5D0534D2E; Wed, 29 Oct 2025 18:22:57 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1761758577; bh=DDFYJSkZeI/wdp0GA0soR5tvyOK4h/67JILT/V8ysXQ=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=ECzXV0djccnUaI8eMz95IVLWy3M/fU/GqwE2/1zI/cDigBZjbo1h0m+RQ3lx++r2o i9lSnYD2ir5JsckzWWEAdqZ/qr2q4M2lGOHOyqUL68/IH1/Ro3i8vZlwgER6kftQBV WoxSzHPT/v4s6PgEz4+8Xctc5XNEfwVKlsdIr6hs= From: Kieran Bingham To: libcamera devel Cc: Kieran Bingham Subject: [PATCH v2 02/13] test: libipa: Add tests for Quantized types Date: Wed, 29 Oct 2025 17:24:27 +0000 Message-ID: <20251029172439.1513907-3-kieran.bingham@ideasonboard.com> X-Mailer: git-send-email 2.50.1 In-Reply-To: <20251029172439.1513907-1-kieran.bingham@ideasonboard.com> References: <20251029172439.1513907-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. Signed-off-by: Kieran Bingham --- test/ipa/libipa/meson.build | 1 + test/ipa/libipa/quantized.cpp | 130 ++++++++++++++++++++++++++++++++++ 2 files changed, 131 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..1f4984ea93b4 --- /dev/null +++ b/test/ipa/libipa/quantized.cpp @@ -0,0 +1,130 @@ +/* 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 quantized_type = int8_t; + static constexpr quantized_type fromFloat(float v) + { + int quantized = std::lround(v * 128.0f); + return static_cast(std::clamp(quantized, -128, 127)); + } + static float toFloat(quantized_type v) + { + return static_cast(v) / 128.0f; + } +}; + +using BrightnessHueQuantizer = Quantized; + +struct ContrastSaturationTraits { + using quantized_type = uint8_t; + static constexpr quantized_type fromFloat(float v) + { + int quantized = std::lround(v * 128.0f); + return static_cast(std::clamp(quantized, 0, 255)); + } + static float toFloat(quantized_type 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; + } + + /* + * Test construction from non-float/non-T type (Expected Fail) + * These should be a compile-time error if uncommented: + */ + // BrightnessQ bad1(15); // overloaded ‘Quantized(int)’ is ambiguous + // BrightnessQ bad2(0xff); // overloaded ‘Quantized(int)’ is ambiguous + // ContrastQ bad3(0x33); // overloaded ‘Quantized(int)’ is ambiguous + // ContrastQ bad4(55U); // overloaded ‘Quantized(unsigned int)’ is ambiguous + + /* 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 moving */ + { + BrightnessQ b1(0.25f); + BrightnessQ b2 = std::move(b1); // Allow move semantics + if (b2.value() != 0.25f) + 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 Oct 29 17:24:28 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Kieran Bingham X-Patchwork-Id: 24892 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 D788BC3259 for ; Wed, 29 Oct 2025 17:24:56 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 846306086F; Wed, 29 Oct 2025 18:24:51 +0100 (CET) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="mZLSs1kD"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id F1EB060453 for ; Wed, 29 Oct 2025 18:24:46 +0100 (CET) Received: from Monstersaurus.hippo-penny.ts.net (cpc89244-aztw30-2-0-cust6594.18-1.cable.virginm.net [86.31.185.195]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 99CC24E45; Wed, 29 Oct 2025 18:22:57 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1761758577; bh=3Y/kOlzuCJ7hrtzVfrAbQU2EaCvkj+nsrIr8dJIdtJU=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=mZLSs1kD5mUTGKQH7ZcQblkjLWfoTRiVnAr1kgdMrXo4dovWwW01o7X8VB0unbPwH 9V1qlgo3OwvGiwycGQXhSYxsXV+74gOzgCEj45i3pDhz+3UzKPFQPN/WLXKplDMDYu Vgn+NxWFxRI9ztyFos0K5UVATYGqLhpiSTMo+BJg= From: Kieran Bingham To: libcamera devel Cc: Kieran Bingham Subject: [PATCH v2 03/13] ipa: libipa: Provide fixed point quantized traits Date: Wed, 29 Oct 2025 17:24:28 +0000 Message-ID: <20251029172439.1513907-4-kieran.bingham@ideasonboard.com> X-Mailer: git-send-email 2.50.1 In-Reply-To: <20251029172439.1513907-1-kieran.bingham@ideasonboard.com> References: <20251029172439.1513907-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. Signed-off-by: Kieran Bingham --- src/ipa/libipa/fixedpoint.cpp | 114 ++++++++++++++++++++++++++++++++++ src/ipa/libipa/fixedpoint.h | 43 +++++++++++++ 2 files changed, 157 insertions(+) diff --git a/src/ipa/libipa/fixedpoint.cpp b/src/ipa/libipa/fixedpoint.cpp index 6b698fc5d680..2568c7921348 100644 --- a/src/ipa/libipa/fixedpoint.cpp +++ b/src/ipa/libipa/fixedpoint.cpp @@ -37,6 +37,120 @@ 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::quantized_type + * \brief The integral storage type used for the fixed-point representation + */ + +/** + * \var FixedPointQTraits::Bits + * \brief Total number of bits used in the fixed-point format (I + F) + */ + +/** + * \var FixedPointQTraits::BitMask + * \brief Bit mask selecting all valid bits in 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(quantized_type 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 Q1_7 + * \brief 1.7 signed fixed-point quantizer + * + * A Quantized type using 1 bit for the integer part and 7 bits for the + * fractional part, stored in a signed 8-bit integer (\c int8_t). Represents + * values approximately in the range [-1.0, 0.992] with a resolution of 1/128. + */ + +/** + * \typedef UQ1_7 + * \brief 1.7 unsigned fixed-point quantizer + * + * A Quantized type using 1 bit for the integer part and 7 bits for the + * fractional part, stored in an unsigned 8-bit integer (\c uint8_t). Represents + * values in the range [0.0, 1.992] with a resolution of 1/128. + */ + +/** + * \typedef Q12_4 + * \brief 12.4 signed fixed-point quantizer + * + * A Quantized type using 12 bits for the integer part and 4 bits for the + * fractional part, stored in a signed 16-bit integer (\c int16_t). Represents + * values in the range approximately [-2048.0, 2047.9375] with a resolution of + * 1/16. + */ + +/** + * \typedef UQ12_4 + * \brief 12.4 unsigned fixed-point quantizer + * + * A Quantized type using 12 bits for the integer part and 4 bits for the + * fractional part, stored in an unsigned 16-bit integer (\c uint16_t). + * Represents values in the range [0.0, 4095.9375] with a resolution of 1/16. + */ + } /* namespace ipa */ } /* namespace libcamera */ diff --git a/src/ipa/libipa/fixedpoint.h b/src/ipa/libipa/fixedpoint.h index 709cf50f0fcd..9a7674e528d9 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 { @@ -60,6 +62,47 @@ constexpr R fixedToFloatingPoint(T number) return static_cast(t) / static_cast(1 << F); } +template +struct FixedPointQTraits { + static_assert(std::is_integral_v, "FixedPointQTraits: T must be integral"); + using quantized_type = T; + + static constexpr unsigned int Bits = I + F; + + static constexpr T BitMask = (Bits < sizeof(T) * 8) + ? (T{1} << Bits) - 1 + : T{-1}; + + static constexpr T qmin = std::is_signed_v + ? -(T{1} << (Bits - 1)) + : T{0}; + + static constexpr T qmax = std::is_signed_v + ? ((T{1} << (Bits - 1)) - 1) + : ((T{1} << Bits) - 1); + + static constexpr float min = fixedToFloatingPoint(qmin); + static constexpr float max = fixedToFloatingPoint(qmax); + + /* Conversion functions required by Quantized */ + static quantized_type fromFloat(float v) + { + v = std::clamp(v, min, max); + return floatingToFixedPoint(v); + } + + static float toFloat(quantized_type q) + { + return fixedToFloatingPoint(q); + } +}; + +using Q1_7 = Quantized>; +using UQ1_7 = Quantized>; + +using Q12_4 = Quantized>; +using UQ12_4 = Quantized>; + } /* namespace ipa */ } /* namespace libcamera */ From patchwork Wed Oct 29 17:24:29 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Kieran Bingham X-Patchwork-Id: 24893 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 E36C2C3259 for ; Wed, 29 Oct 2025 17:25:02 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 90ACD6086F; Wed, 29 Oct 2025 18:25:02 +0100 (CET) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="hNT3VDpg"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 377436085D for ; Wed, 29 Oct 2025 18:24:47 +0100 (CET) Received: from Monstersaurus.hippo-penny.ts.net (cpc89244-aztw30-2-0-cust6594.18-1.cable.virginm.net [86.31.185.195]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id D648C4E52; Wed, 29 Oct 2025 18:22:57 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1761758578; bh=bFvf0xeM/xbCVogeYdUdPgQhaaUfcDmQSrgvHnM3eEc=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=hNT3VDpgt2Kr0nH9Jzk/S0MhWH0nW4KRynWFGXBAQnWz5elXb7POppkze4UAlBp8C xpK6dGWMpW49thtQBBulXpAC5o4z4J7y1HnBXyl3RVt63gVbp78JETMsVQUAh5h47a Y6YNnunspWR+HVg7ANKlhTmrbK0y/bKiTgAZxpsY= From: Kieran Bingham To: libcamera devel Cc: Kieran Bingham Subject: [PATCH v2 04/13] test: libipa: Provide FixedPoint Quantized tests Date: Wed, 29 Oct 2025 17:24:29 +0000 Message-ID: <20251029172439.1513907-5-kieran.bingham@ideasonboard.com> X-Mailer: git-send-email 2.50.1 In-Reply-To: <20251029172439.1513907-1-kieran.bingham@ideasonboard.com> References: <20251029172439.1513907-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" Validate the new fixed-point Quantized types with tests covering Q1.7, UQ1.7, Q12.4 and UQ12.4 types. Signed-off-by: Kieran Bingham --- test/ipa/libipa/fixedpoint.cpp | 121 +++++++++++++++++++++++++++++++-- 1 file changed, 117 insertions(+), 4 deletions(-) diff --git a/test/ipa/libipa/fixedpoint.cpp b/test/ipa/libipa/fixedpoint.cpp index 99eb662ddf4e..c88b53a31f28 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,125 @@ protected: return TestPass; } - int run() + template + int quantizedCheck(float input, typename Q::quantized_type expected, float value) { - /* fixed point conversion test */ - if (testFixedPoint() != TestPass) + Q q(input); + using T = typename Q::quantized_type; + + cerr << " Checking " << input << " == " << q.toString() << std::endl; + + T quantized = q.quantized(); + if (quantized != expected) { + cerr << " ** 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) { + cerr << " ** V Expected " << input + << " to quantize to " << value + << ", got " << q.value() + << " - (" << q.toString() << ")" + << std::endl; + return 1; + } + + return 0; + } + + template + int introduce(std::string type) + { + using T = typename Q::quantized_type; + + std::cerr << std::endl; + + cerr << 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; + + if (Q::TraitsType::min > Q::TraitsType::max) { + cerr << " *** " << type + << " Min (" << Q::TraitsType::min + << ") must be less than max (" + << Q::TraitsType::max << ")" << std::endl; + return 1; + } + + return 0; + } + + int testFixedPointQuantizers() + { + unsigned int fails = 0; + + /* clang-format off */ + + /* Q1_7( -1 .. 0.992188) Min: Q:0x80 F:-1 -- Max: Q:0x7F V:0.992188 Step:0.0078125 */ + fails += 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 */ + + fails += 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 */ + + /* Test Q12.4 */ + introduce("Q12.4"); + + /* Q12_4(-2048 .. 2047.94) Min: Q:0x8000 F:-2048 -- Max: Q:0x7FFF V:2047.94 Step:0.0625 */ + introduce("Q12_4"); + fails += quantizedCheck(0.0f, 0b000000000000'0000, 0.0f); + fails += quantizedCheck(7.5f, 0b000000000111'1000, 7.5f); + + 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 */ + cerr << 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::cerr << std::endl; + + if (fails > 0) { + cerr << "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 Oct 29 17:24:30 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Kieran Bingham X-Patchwork-Id: 24894 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 CEBBEC32CE for ; Wed, 29 Oct 2025 17:25:03 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 258EB608CF; Wed, 29 Oct 2025 18:25:03 +0100 (CET) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="wC/hCxxK"; 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 8C39E60856 for ; Wed, 29 Oct 2025 18:24:47 +0100 (CET) Received: from Monstersaurus.hippo-penny.ts.net (cpc89244-aztw30-2-0-cust6594.18-1.cable.virginm.net [86.31.185.195]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 1F5F84E53; Wed, 29 Oct 2025 18:22:58 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1761758578; bh=0DJtx4vem6f5b0jigxFARPnYoLjWn0g5Fx9x6O15FKA=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=wC/hCxxKippFeOO1d5FixAX9FgFuJ5EV4z+PuVcRL95a63t2Laa1BCYLK50PNSXq1 Lrbw4XWtpiRPtBWTWPxko1S9qBRaKYAvwgKeFl6IdfEmqiiGTBC2ZaGP/IuwTMFcR2 OVoLDNj2O+jvvGxGFO4j3MrWs7FMTVf2WHzV0uL0= From: Kieran Bingham To: libcamera devel Cc: Kieran Bingham Subject: [PATCH v2 05/13] ipa: libipa: fixedpoint: Fix unsigned usage Date: Wed, 29 Oct 2025 17:24:30 +0000 Message-ID: <20251029172439.1513907-6-kieran.bingham@ideasonboard.com> X-Mailer: git-send-email 2.50.1 In-Reply-To: <20251029172439.1513907-1-kieran.bingham@ideasonboard.com> References: <20251029172439.1513907-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 calcuation for unsigned types. Convert the storage of the test cases to signed types to correctly represent their intended purpose, to prevent test failures. 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 9a7674e528d9..557f7f5b3382 100644 --- a/src/ipa/libipa/fixedpoint.h +++ b/src/ipa/libipa/fixedpoint.h @@ -51,6 +51,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 c88b53a31f28..6f6ef4ce258e 100644 --- a/test/ipa/libipa/fixedpoint.cpp +++ b/test/ipa/libipa/fixedpoint.cpp @@ -70,7 +70,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 }, @@ -83,14 +83,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 Oct 29 17:24:31 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Kieran Bingham X-Patchwork-Id: 24895 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 BC86BC32D4 for ; Wed, 29 Oct 2025 17:25:04 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 0B9A460875; Wed, 29 Oct 2025 18:25:04 +0100 (CET) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="vadAB4Wx"; 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 D12D360869 for ; Wed, 29 Oct 2025 18:24:47 +0100 (CET) Received: from Monstersaurus.hippo-penny.ts.net (cpc89244-aztw30-2-0-cust6594.18-1.cable.virginm.net [86.31.185.195]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 78CFD11DD; Wed, 29 Oct 2025 18:22:58 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1761758578; bh=SZQYT53r5Ox7Ud7HzN///PZjUGCxZfuCQ4pvDrFIiCY=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=vadAB4WxqivO+MqbfKTro5xMTFx+o4m8+aA02iopJyhfHAAKlTSUrOIC/hjqTcjYW mUO1jUlSLaFfMdrh2xMd93cFIQ/JL8QXiQ5MmXNndGq/9mYI8N7mGf4yAYx9k0x0Zq PbfYNN+EmeYNWSdqRJiy2sLIwBylCzsfzptX7x9A= From: Kieran Bingham To: libcamera devel Cc: Kieran Bingham Subject: [PATCH v2 06/13] test: libipa: Add Q4.7 type and tests to match existing use case tests Date: Wed, 29 Oct 2025 17:24:31 +0000 Message-ID: <20251029172439.1513907-7-kieran.bingham@ideasonboard.com> X-Mailer: git-send-email 2.50.1 In-Reply-To: <20251029172439.1513907-1-kieran.bingham@ideasonboard.com> References: <20251029172439.1513907-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 existing fixed point test cases utilise a signed Q4.7 format. Add matching tests covering the same values and extend to validate the full type. Signed-off-by: Kieran Bingham --- test/ipa/libipa/fixedpoint.cpp | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/test/ipa/libipa/fixedpoint.cpp b/test/ipa/libipa/fixedpoint.cpp index 6f6ef4ce258e..4bffd40f5a63 100644 --- a/test/ipa/libipa/fixedpoint.cpp +++ b/test/ipa/libipa/fixedpoint.cpp @@ -20,6 +20,9 @@ using namespace std; using namespace libcamera; using namespace ipa; +/* Q4_7(-8 .. 7.99219) Min: Q:0x0400 V:-8 -- Max: Q:0x03ff V:7.99219 Step:0.0078125 */ +using Q4_7 = Quantized>; + class FixedPointUtilsTest : public Test { protected: @@ -171,6 +174,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: Q:0x0400 F:-8 -- Max: Q:0x03FF V:7.99219 Step:0.0078125 */ + introduce("Q4_7"); /* No Sign Extension */ + 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 */ + /* Test Q12.4 */ introduce("Q12.4"); From patchwork Wed Oct 29 17:24:32 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Kieran Bingham X-Patchwork-Id: 24896 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 5CED9C32DB for ; Wed, 29 Oct 2025 17:25:05 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id C71C760896; Wed, 29 Oct 2025 18:25:04 +0100 (CET) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="C+1xsz0v"; 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 16F956085D for ; Wed, 29 Oct 2025 18:24:48 +0100 (CET) Received: from Monstersaurus.hippo-penny.ts.net (cpc89244-aztw30-2-0-cust6594.18-1.cable.virginm.net [86.31.185.195]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id B38034E56; Wed, 29 Oct 2025 18:22:58 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1761758578; bh=qtciVlkHQP17kVgBOI2psXZNZNVKNA4SMUPtzt+80GI=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=C+1xsz0vGUxziHmKgxb0jCDgtg/iWA6Dlyi8G6ec8scNGYdTGkZ2fAQcCKSlOk4N6 1Z5gS3svaVw8OccYPrs/Gi6InqaM7s8w4I7IqURlNrLPHzjNqSf4elLd3YdWqdcfi+ 69zaYGQE5FJIMEfMpQCK1EFRj6r5MaOKXr2qyaDA= From: Kieran Bingham To: libcamera devel Cc: Kieran Bingham Subject: [PATCH v2 07/13] ipa: libipa: fixedpoint: Provide a ScaledFixedPoint type Date: Wed, 29 Oct 2025 17:24:32 +0000 Message-ID: <20251029172439.1513907-8-kieran.bingham@ideasonboard.com> X-Mailer: git-send-email 2.50.1 In-Reply-To: <20251029172439.1513907-1-kieran.bingham@ideasonboard.com> References: <20251029172439.1513907-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 FixedPoint capabilities with a ScaledFixedPointQTraits which takes an existing Quantized type and scales the conversion by the given Scale template parameter. This can be useful to represent fixed point values which represent a linear range with a different scale to the underlying float or integral type. Signed-off-by: Kieran Bingham --- src/ipa/libipa/fixedpoint.cpp | 59 +++++++++++++++++++++++++++++++++++ src/ipa/libipa/fixedpoint.h | 24 ++++++++++++++ 2 files changed, 83 insertions(+) diff --git a/src/ipa/libipa/fixedpoint.cpp b/src/ipa/libipa/fixedpoint.cpp index 2568c7921348..e6e1408ccff6 100644 --- a/src/ipa/libipa/fixedpoint.cpp +++ b/src/ipa/libipa/fixedpoint.cpp @@ -151,6 +151,65 @@ namespace ipa { * Represents values in the range [0.0, 4095.9375] with a resolution of 1/16. */ +/** + * \struct ScaledFixedPointQTraits + * \brief Wrap a FixedPointQTraits with a linear scaling factor + * + * This trait extends an existing fixed-point quantisation policy + * by applying an additional multiplicative scale between the + * floating-point and quantised domains. + * + * \tparam Q The base fixed-point traits type + * \tparam Scale The scale factor applied to the floating-point domain + */ + +/** + * \typedef ScaledFixedPointQTraits::quantized_type + * \copydoc FixedPointQTraits::quantized_type + */ + +/** + * \var ScaledFixedPointQTraits::scale + * \brief The constant scaling factor applied to the floating-point domain. + * + * Floating-point inputs are divided by this factor before quantisation, + * and multiplied by it after dequantisation. + */ + +/** + * \var ScaledFixedPointQTraits::qmin + * \copydoc FixedPointQTraits::qmin + */ + +/** + * \var ScaledFixedPointQTraits::qmax + * \copydoc FixedPointQTraits::qmax + */ + +/** + * \var ScaledFixedPointQTraits::min + * \copydoc FixedPointQTraits::min + */ + +/** + * \var ScaledFixedPointQTraits::max + * \copydoc FixedPointQTraits::max + */ + +/** + * \fn ScaledFixedPointQTraits::fromFloat(float v) + * \copydoc FixedPointQTraits::fromFloat(float v) + * + * The input value \a v is divided by the scaling factor before conversion. + */ + +/** + * \fn ScaledFixedPointQTraits::toFloat(quantized_type q) + * \copydoc FixedPointQTraits::toFloat(quantized_type q) + * + * The output value is multiplied by the scaling factor after conversion. + */ + } /* namespace ipa */ } /* namespace libcamera */ diff --git a/src/ipa/libipa/fixedpoint.h b/src/ipa/libipa/fixedpoint.h index 557f7f5b3382..7a9103ed3a77 100644 --- a/src/ipa/libipa/fixedpoint.h +++ b/src/ipa/libipa/fixedpoint.h @@ -106,6 +106,30 @@ using UQ1_7 = Quantized>; using Q12_4 = Quantized>; using UQ12_4 = Quantized>; +template +struct ScaledFixedPointQTraits { + using quantized_type = typename Q::quantized_type; + + static constexpr float scale = static_cast(Scale); + + /* Re-expose base limits, adjusted by the scaling factor */ + static constexpr quantized_type qmin = Q::qmin; + static constexpr quantized_type qmax = Q::qmax; + static constexpr float min = Q::min * scale; + static constexpr float max = Q::max * scale; + + static quantized_type fromFloat(float v) + { + v = std::clamp(v, min, max); + return Q::fromFloat(v / scale); + } + + static float toFloat(quantized_type q) + { + return Q::toFloat(q) * scale; + } +}; + } /* namespace ipa */ } /* namespace libcamera */ From patchwork Wed Oct 29 17:24:33 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Kieran Bingham X-Patchwork-Id: 24897 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 2142BC3331 for ; Wed, 29 Oct 2025 17:25:06 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id A318C6085D; Wed, 29 Oct 2025 18:25:05 +0100 (CET) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="chNTWVtY"; 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 4BB5860879 for ; Wed, 29 Oct 2025 18:24:48 +0100 (CET) Received: from Monstersaurus.hippo-penny.ts.net (cpc89244-aztw30-2-0-cust6594.18-1.cable.virginm.net [86.31.185.195]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id EF5911E27; Wed, 29 Oct 2025 18:22:58 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1761758579; bh=PTwzVqeQFmvDw4CuaQJJQ+zLm+zmOChgslJvh1fXYKg=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=chNTWVtYVCtrydS7diU96gRQJL6+gxPH1hXDxRoxRzZ6v+9///49sUhY5qpTOBkHP aUSMhfjVPFyoa/fSEuOCVdCoe7IYOxF7j+4q5Kuvh7Hx1Hxd5wDZWp5SmFxUAXiulZ QExdUS6LEm/Ts6cDn2QC3DN9Abv+qpYM1AhY0/aU= From: Kieran Bingham To: libcamera devel Cc: Kieran Bingham Subject: [PATCH v2 08/13] test: libipa: Provide ScaledFixedPoint tests Date: Wed, 29 Oct 2025 17:24:33 +0000 Message-ID: <20251029172439.1513907-9-kieran.bingham@ideasonboard.com> X-Mailer: git-send-email 2.50.1 In-Reply-To: <20251029172439.1513907-1-kieran.bingham@ideasonboard.com> References: <20251029172439.1513907-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" Use the new ScaledFixedPointQTraits to create a HueQ representing the equivalent scale used by the RKISP1 Hue control to validate the scaling capabilities of the new quantized type. Signed-off-by: Kieran Bingham --- test/ipa/libipa/fixedpoint.cpp | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/test/ipa/libipa/fixedpoint.cpp b/test/ipa/libipa/fixedpoint.cpp index 4bffd40f5a63..f8be1e4bf70f 100644 --- a/test/ipa/libipa/fixedpoint.cpp +++ b/test/ipa/libipa/fixedpoint.cpp @@ -221,6 +221,32 @@ protected: return TestPass; } + int testScaledFixedPointQuantizers() + { + unsigned int fails = 0; + cerr << std::endl + << "Scaled Fixed-Point Quantizer tests:" << std::endl; + + using HueQ = Quantized>; + + fails += introduce("HueQ (Scaled Q1_7 * 90) "); + + /* clang-format off */ + + fails += quantizedCheck(-90.0f, -128, -90.0f); /* Min */ + fails += quantizedCheck(-45.0f, -64, -45.0f); /* Mid negative */ + fails += quantizedCheck( 0.0f, 0, 0.0f); /* Zero */ + fails += quantizedCheck( 45.0f, 64, 45.0f); /* Mid positive */ + fails += quantizedCheck( 90.0f, 127, 89.2969f); /* Max */ + + fails += quantizedCheck(-99.0f, -128, -90.0f); /* Clamp low */ + fails += quantizedCheck( 99.0f, 127, 89.2969f); /* Clamp high */ + + /* clang-format on */ + + return fails ? TestFail : TestPass; + } + int run() { unsigned int fails = 0; @@ -232,6 +258,9 @@ protected: if (testFixedPointQuantizers() != TestPass) fails++; + if (testScaledFixedPointQuantizers() != TestPass) + fails++; + return fails ? TestFail : TestPass; } }; From patchwork Wed Oct 29 17:24:34 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Kieran Bingham X-Patchwork-Id: 24898 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 ED4E8C3332 for ; Wed, 29 Oct 2025 17:25:06 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 70EC7608BC; Wed, 29 Oct 2025 18:25:06 +0100 (CET) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="HFKOrXas"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 902596088F for ; Wed, 29 Oct 2025 18:24:48 +0100 (CET) Received: from Monstersaurus.hippo-penny.ts.net (cpc89244-aztw30-2-0-cust6594.18-1.cable.virginm.net [86.31.185.195]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 370874E58; Wed, 29 Oct 2025 18:22:59 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1761758579; bh=yyXZ2uF43fZaTf57oTb/Nr63Ep6X8BuetKHEskLMWyQ=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=HFKOrXasdxWNFS0JxbZOZcIRXZafWdxO6qOHgdAh8iJ5InT9Q2JvIFyecGPv1FRml /ezKQu0crjcw+oROH6vaG16I1k2QdtLpnnSu/eOM0H5qSZC5wnMJOY5GTVn17FCB/d PpChXZm0EKIW9Vt8+uKNmwal6dneg4Ae/lFZP5r0= From: Kieran Bingham To: libcamera devel Cc: Kieran Bingham Subject: [PATCH v2 09/13] ipa: rkisp1: cproc: Convert to use Quantized types Date: Wed, 29 Oct 2025 17:24:34 +0000 Message-ID: <20251029172439.1513907-10-kieran.bingham@ideasonboard.com> X-Mailer: git-send-email 2.50.1 In-Reply-To: <20251029172439.1513907-1-kieran.bingham@ideasonboard.com> References: <20251029172439.1513907-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 Contrast and Saturuation helper functions to a Quantizer type to support maintaining the data. Signed-off-by: Kieran Bingham --- src/ipa/rkisp1/algorithms/cproc.cpp | 36 +++++++++++------------------ src/ipa/rkisp1/ipa_context.h | 20 +++++++++++----- 2 files changed, 28 insertions(+), 28 deletions(-) diff --git a/src/ipa/rkisp1/algorithms/cproc.cpp b/src/ipa/rkisp1/algorithms/cproc.cpp index d1fff6990d37..4637a824af03 100644 --- a/src/ipa/rkisp1/algorithms/cproc.cpp +++ b/src/ipa/rkisp1/algorithms/cproc.cpp @@ -14,6 +14,8 @@ #include +#include "libipa/quantized.h" + /** * \file cproc.h */ @@ -39,16 +41,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 +66,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,35 +89,35 @@ 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; } - LOG(RkISP1CProc, Debug) << "Set brightness to " << value; + LOG(RkISP1CProc, Debug) << "Set brightness to " << value.value(); } 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; } - LOG(RkISP1CProc, Debug) << "Set contrast to " << value; + LOG(RkISP1CProc, Debug) << "Set contrast to " << value.value(); } 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; } - LOG(RkISP1CProc, Debug) << "Set saturation to " << value; + LOG(RkISP1CProc, Debug) << "Set saturation to " << value.value(); } frameContext.cproc.brightness = cproc.brightness; @@ -148,9 +140,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 f85a130d9c23..0a5fb2769d31 100644 --- a/src/ipa/rkisp1/ipa_context.h +++ b/src/ipa/rkisp1/ipa_context.h @@ -8,6 +8,7 @@ #pragma once +#include #include #include @@ -27,11 +28,17 @@ #include #include #include "libipa/agc_mean_luminance.h" +#include "libipa/fixedpoint.h" namespace libcamera { namespace ipa::rkisp1 { +/* Fixed point types used by CPROC */ +using BrightnessQ = Q1_7; +using ContrastQ = UQ1_7; +using SaturationQ = UQ1_7; + struct IPAHwSettings { unsigned int numAeCells; unsigned int numHistogramBins; @@ -115,9 +122,9 @@ struct IPAActiveState { } ccm; struct { - int8_t brightness; - uint8_t contrast; - uint8_t saturation; + BrightnessQ brightness; + ContrastQ contrast; + SaturationQ saturation; } cproc; struct { @@ -169,9 +176,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 Oct 29 17:24:35 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Kieran Bingham X-Patchwork-Id: 24899 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 B59D7C3333 for ; Wed, 29 Oct 2025 17:25:07 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 56F8F60891; Wed, 29 Oct 2025 18:25:07 +0100 (CET) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="l3LOAf8M"; 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 D0871608BB for ; Wed, 29 Oct 2025 18:24:48 +0100 (CET) Received: from Monstersaurus.hippo-penny.ts.net (cpc89244-aztw30-2-0-cust6594.18-1.cable.virginm.net [86.31.185.195]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 747A24EE1; Wed, 29 Oct 2025 18:22:59 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1761758579; bh=ZDKADU8J1J7zQW8MsAHlsQUSL5ojgZvZw1wTXkf6pMI=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=l3LOAf8Mp3lschymqL0119CdS0NF8zc6agjwh/+URAQ1OnZaTbOv10V6Qqz3QTDw9 zGjIjKTPXu4pbmtwFRs0NfyNBQRlt+EYy634I/gKVeXBfagXZkp11cMhq6jvvbCL03 c9/1YC6/Qfz3m/DeWKIffRK7JYUoIINWpjUkvNqE= From: Kieran Bingham To: libcamera devel Cc: Kieran Bingham Subject: [PATCH v2 10/13] ipa: rkisp1: cproc: Report metadata Date: Wed, 29 Oct 2025 17:24:35 +0000 Message-ID: <20251029172439.1513907-11-kieran.bingham@ideasonboard.com> X-Mailer: git-send-email 2.50.1 In-Reply-To: <20251029172439.1513907-1-kieran.bingham@ideasonboard.com> References: <20251029172439.1513907-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. Signed-off-by: Kieran Bingham --- src/ipa/rkisp1/algorithms/cproc.cpp | 12 ++++++++++++ src/ipa/rkisp1/algorithms/cproc.h | 4 ++++ 2 files changed, 16 insertions(+) diff --git a/src/ipa/rkisp1/algorithms/cproc.cpp b/src/ipa/rkisp1/algorithms/cproc.cpp index 4637a824af03..7475e6c1b609 100644 --- a/src/ipa/rkisp1/algorithms/cproc.cpp +++ b/src/ipa/rkisp1/algorithms/cproc.cpp @@ -145,6 +145,18 @@ 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 Oct 29 17:24:36 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Kieran Bingham X-Patchwork-Id: 24901 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 20DBDC3334 for ; Wed, 29 Oct 2025 17:25:09 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id B1B63608F7; Wed, 29 Oct 2025 18:25:08 +0100 (CET) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="EEzdVN2v"; 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 1A68960859 for ; Wed, 29 Oct 2025 18:24:49 +0100 (CET) Received: from Monstersaurus.hippo-penny.ts.net (cpc89244-aztw30-2-0-cust6594.18-1.cable.virginm.net [86.31.185.195]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id B3C8B4D2E; Wed, 29 Oct 2025 18:22:59 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1761758579; bh=wfsf72qv2NCaX6PuHQmPl5h9CQMIf+I4Pg95L5F4S3M=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=EEzdVN2vliU150496y9WSdykjfApU+AR7iZMoGUJIGB3kwsipOqldPxytsqp82V+V lSt/BAmBxGdY9gULO0emcLqqxJkslrYRmln/9mrLWizybJgmwFogpaE6HNx4Ohsv3P PbDmsvr82xUDVokNTFAMp67aCEuDZH7YrgussLR4= From: Kieran Bingham To: libcamera devel Cc: "van Veen, Stephan" , Kieran Bingham Subject: [PATCH v2 11/13] libcamera: controls: Define a new core Hue control Date: Wed, 29 Oct 2025 17:24:36 +0000 Message-ID: <20251029172439.1513907-12-kieran.bingham@ideasonboard.com> X-Mailer: git-send-email 2.50.1 In-Reply-To: <20251029172439.1513907-1-kieran.bingham@ideasonboard.com> References: <20251029172439.1513907-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. Signed-off-by: van Veen, Stephan [Kieran: Rework to define as a rotation in degrees] Signed-off-by: Kieran Bingham --- src/libcamera/control_ids_core.yaml | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/libcamera/control_ids_core.yaml b/src/libcamera/control_ids_core.yaml index f781865859ac..4993da14b3f6 100644 --- a/src/libcamera/control_ids_core.yaml +++ b/src/libcamera/control_ids_core.yaml @@ -1346,4 +1346,17 @@ controls: reduces the WdrExposureValue until the amount of pixels that are close to saturation is lower than this value. + - Hue: + type: float + direction: inout + description: | + Adjusts the image hue (colour rotation) in degrees. + + The value specifies a rotation around the colour wheel: + positive values rotate hues counter-clockwise (for example a +60 degree + rotation turns Red hues to Magenta hues), and negative values rotate + clockwise (a -60 degree rotation turns Red hues to Yellow hues). + + A value of 0 leaves the hue unchanged. + ... From patchwork Wed Oct 29 17:24:37 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Kieran Bingham X-Patchwork-Id: 24900 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 5B6FCC3259 for ; Wed, 29 Oct 2025 17:25:08 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 025DB608F3; Wed, 29 Oct 2025 18:25:08 +0100 (CET) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="LPUlsNDC"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 5B2B8608BF for ; Wed, 29 Oct 2025 18:24:49 +0100 (CET) Received: from Monstersaurus.hippo-penny.ts.net (cpc89244-aztw30-2-0-cust6594.18-1.cable.virginm.net [86.31.185.195]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 01AF64FB7; Wed, 29 Oct 2025 18:22:59 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1761758580; bh=l966tlqAt5TcMyFfQ13zq22CqD1lNDZDBsQfPsUjJa4=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=LPUlsNDCX30I99OUQPVzEt/CslsIwmPIdkE+ikbc7loNo9Z5rXwyY3KvBdPSbaO0e yZSvU5L5BOH84JeBWmuO98uuEyRLmOooBW5aKcrFP7gsYwXFNyq+9B4h6w7snPeugP CTaZ+N/zjFZXVHAhQ+nx1riEZFxK01eVBK/l6YP0= From: Kieran Bingham To: libcamera devel Cc: Kieran Bingham Subject: [PATCH v2 12/13] ipa: rkisp1: cproc: Provide a Hue control Date: Wed, 29 Oct 2025 17:24:37 +0000 Message-ID: <20251029172439.1513907-13-kieran.bingham@ideasonboard.com> X-Mailer: git-send-email 2.50.1 In-Reply-To: <20251029172439.1513907-1-kieran.bingham@ideasonboard.com> References: <20251029172439.1513907-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). 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. Implement the new control converting to the hardware scale accordingly and report the applied control in the completed request metadata. Signed-off-by: Kieran Bingham --- src/ipa/rkisp1/algorithms/cproc.cpp | 18 ++++++++++++++++++ src/ipa/rkisp1/ipa_context.h | 3 +++ 2 files changed, 21 insertions(+) diff --git a/src/ipa/rkisp1/algorithms/cproc.cpp b/src/ipa/rkisp1/algorithms/cproc.cpp index 7475e6c1b609..5389882c1685 100644 --- a/src/ipa/rkisp1/algorithms/cproc.cpp +++ b/src/ipa/rkisp1/algorithms/cproc.cpp @@ -39,6 +39,7 @@ namespace { constexpr float kDefaultBrightness = 0.0f; constexpr float kDefaultContrast = 1.0f; +constexpr float kDefaultHue = 0.0f; constexpr float kDefaultSaturation = 1.0f; } /* namespace */ @@ -53,6 +54,8 @@ int ColorProcessing::init(IPAContext &context, cmap[&controls::Brightness] = ControlInfo(-1.0f, 0.993f, kDefaultBrightness); cmap[&controls::Contrast] = ControlInfo(0.0f, 1.993f, kDefaultContrast); + cmap[&controls::Hue] = ControlInfo(HueQ::TraitsType::min, + HueQ::TraitsType::max, kDefaultHue); cmap[&controls::Saturation] = ControlInfo(0.0f, 1.993f, kDefaultSaturation); return 0; @@ -68,6 +71,7 @@ int ColorProcessing::configure(IPAContext &context, cproc.brightness = BrightnessQ(kDefaultBrightness); cproc.contrast = ContrastQ(kDefaultContrast); + cproc.hue = HueQ(kDefaultHue); cproc.saturation = SaturationQ(kDefaultSaturation); return 0; @@ -109,6 +113,17 @@ void ColorProcessing::queueRequest(IPAContext &context, LOG(RkISP1CProc, Debug) << "Set contrast to " << value.value(); } + const auto &hue = controls.get(controls::Hue); + if (hue) { + HueQ value = *hue; + if (cproc.hue != value) { + cproc.hue = value; + update = true; + } + + LOG(RkISP1CProc, Debug) << "Set hue to " << value.value(); + } + const auto saturation = controls.get(controls::Saturation); if (saturation) { SaturationQ value = *saturation; @@ -122,6 +137,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; } @@ -142,6 +158,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 +171,7 @@ void ColorProcessing::process([[maybe_unused]] IPAContext &context, [[maybe_unus { metadata.set(controls::Brightness, frameContext.cproc.brightness.value()); metadata.set(controls::Contrast, frameContext.cproc.contrast.value()); + metadata.set(controls::Hue, frameContext.cproc.hue.value()); 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 0a5fb2769d31..a4badbfc0c36 100644 --- a/src/ipa/rkisp1/ipa_context.h +++ b/src/ipa/rkisp1/ipa_context.h @@ -37,6 +37,7 @@ namespace ipa::rkisp1 { /* Fixed point types used by CPROC */ using BrightnessQ = Q1_7; using ContrastQ = UQ1_7; +using HueQ = Quantized>; using SaturationQ = UQ1_7; struct IPAHwSettings { @@ -124,6 +125,7 @@ struct IPAActiveState { struct { BrightnessQ brightness; ContrastQ contrast; + HueQ hue; SaturationQ saturation; } cproc; @@ -178,6 +180,7 @@ struct IPAFrameContext : public FrameContext { struct { BrightnessQ brightness; ContrastQ contrast; + HueQ hue; SaturationQ saturation; bool update; From patchwork Wed Oct 29 17:24:38 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Kieran Bingham X-Patchwork-Id: 24902 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 BA81BC3335 for ; Wed, 29 Oct 2025 17:25:09 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 63F9B608CA; Wed, 29 Oct 2025 18:25:09 +0100 (CET) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="DQ+7QCV6"; 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 94E6F608C8 for ; Wed, 29 Oct 2025 18:24:49 +0100 (CET) Received: from Monstersaurus.hippo-penny.ts.net (cpc89244-aztw30-2-0-cust6594.18-1.cable.virginm.net [86.31.185.195]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 3C1004E45; Wed, 29 Oct 2025 18:23:00 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1761758580; bh=zUiPKTtmmpRi8Hr6zoav5cWPwz3HCSmysAqavYZInCY=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=DQ+7QCV6j6WoViQO6JF9aKTjP8eEtM/dYfIKXapOYuOqs9jnxOaj/1fQELCIhKGkb LJc4t5dLWqNnPG9hCDuewW7T02bVjJnRu6IeBfk1bn5p/yJ9S3zl5tJlF+xFPETRdz em1YLZKCIDoefQq1e4z3MrvSvCCv0ez8zssCiFSQ= From: Kieran Bingham To: libcamera devel Cc: Kieran Bingham Subject: [PATCH v2 13/13] DNI: test: libipa: fixedpoint: Validate extra types Date: Wed, 29 Oct 2025 17:24:38 +0000 Message-ID: <20251029172439.1513907-14-kieran.bingham@ideasonboard.com> X-Mailer: git-send-email 2.50.1 In-Reply-To: <20251029172439.1513907-1-kieran.bingham@ideasonboard.com> References: <20251029172439.1513907-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" Check that the Cproc manual types can be directly served by the new fixed point types. DNI: This patch is just here to validate that my conversions are using the right types and work as the original code did. I don't expect this one to be merged. Signed-off-by: Kieran Bingham --- test/ipa/libipa/fixedpoint.cpp | 68 ++++++++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) diff --git a/test/ipa/libipa/fixedpoint.cpp b/test/ipa/libipa/fixedpoint.cpp index f8be1e4bf70f..ffab9638fa29 100644 --- a/test/ipa/libipa/fixedpoint.cpp +++ b/test/ipa/libipa/fixedpoint.cpp @@ -247,6 +247,71 @@ protected: return fails ? TestFail : TestPass; } + struct BrightnessTraits { + using quantized_type = int8_t; + static constexpr quantized_type fromFloat(float v) + { + int quantized = std::lround(v * 128.0f); + return static_cast(std::clamp(quantized, -128, 127)); + } + static float toFloat(quantized_type v) + { + return static_cast(v) / 128.0f; + } + }; + + using BrightnessQOriginal = Quantized; + + struct ContrastSaturationTraits { + using quantized_type = uint8_t; + static constexpr quantized_type fromFloat(float v) + { + int quantized = std::lround(v * 128.0f); + return static_cast(std::clamp(quantized, 0, 255)); + } + static float toFloat(quantized_type v) + { + return static_cast(v) / 128.0f; + } + }; + + using ContrastQOriginal = Quantized; + + /* FixedPoint equivalents to validate */ + using BrightnessQ = Quantized>; + using ContrastQ = Quantized>; + + template + int testTwoIdenticalTypes(float v) + { + T1 a(v); + T2 b(v); + + std::cout << " Checking " << a.toString() + << " == " << b.toString() + << std::endl; + + /* 0/false success, 1/true fail */ + return (a.quantized() != b.quantized()); + } + + int testCprocFixedPointConversion() + { + unsigned int fails = 0; + + std::cout << std::endl + << "CPROC Fixed-Point Quantizer conversion tests:" + << std::endl; + + fails += testTwoIdenticalTypes(BrightnessQ::TraitsType::min); + fails += testTwoIdenticalTypes(BrightnessQ::TraitsType::max); + + fails += testTwoIdenticalTypes(ContrastQ::TraitsType::min); + fails += testTwoIdenticalTypes(ContrastQ::TraitsType::max); + + return fails ? TestFail : TestPass; + } + int run() { unsigned int fails = 0; @@ -261,6 +326,9 @@ protected: if (testScaledFixedPointQuantizers() != TestPass) fails++; + if (testCprocFixedPointConversion() != TestPass) + fails++; + return fails ? TestFail : TestPass; } };