| Message ID | 20251114005428.90024-2-kieran.bingham@ideasonboard.com |
|---|---|
| State | New |
| Headers | show |
| Series |
|
| Related | show |
Hi 2025. 11. 14. 1:54 keltezéssel, Kieran Bingham írta: > 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 <kieran.bingham@ideasonboard.com> > > --- > 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 > > src/ipa/libipa/meson.build | 2 + > src/ipa/libipa/quantized.cpp | 134 +++++++++++++++++++++++++++++++++++ > src/ipa/libipa/quantized.h | 78 ++++++++++++++++++++ > 3 files changed, 214 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..ef3ae21e9a3b > --- /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 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 > + */ > + > +} /* namespace ipa */ > + > +} /* namespace libcamera */ > diff --git a/src/ipa/libipa/quantized.h b/src/ipa/libipa/quantized.h > new file mode 100644 > index 000000000000..db1c0d72db86 > --- /dev/null > +++ b/src/ipa/libipa/quantized.h > @@ -0,0 +1,78 @@ > +/* 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 <iomanip> <iomanip> does not seem necessary. > +#include <sstream> > +#include <stdint.h> <stdint.h> does not seem necessary. > +#include <type_traits> > + > +#include <libcamera/base/utils.h> > + > +namespace libcamera { > + > +namespace ipa { > + > +template<typename Traits> > +struct Quantized { > + using TraitsType = Traits; > + using QuantizedType = typename Traits::QuantizedType; > + static_assert(std::is_arithmetic_v<QuantizedType>, > + "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_); I think I have brought this up earlier, but I'm wondering if we might want to store `x` in `value_`. For example, if a control has float type, is it expected that the application should account for the quantization when comparing the desired value and actual value in the request metadata to check for convergence? > + 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::ostringstream oss; > + > + oss << "[" << utils::hex(quantized_) > + << ":" << value_ << "]"; > + > + return oss.str(); > + } I'd really like to have `operator<<()` and then `toString()` implemented using that. Reviewed-by: Barnabás Pőcze <barnabas.pocze@ideasonboard.com> > + > + bool operator==(const Quantized &other) const noexcept > + { > + return quantized_ == other.quantized_; > + } > + > + bool operator!=(const Quantized &other) const noexcept > + { > + return !(*this == other); > + } > + > +private: > + QuantizedType quantized_; > + float value_; > +}; > + > +} /* namespace ipa */ > + > +} /* namespace libcamera */
Quoting Barnabás Pőcze (2025-11-14 18:08:03) > Hi > > 2025. 11. 14. 1:54 keltezéssel, Kieran Bingham írta: > > 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 <kieran.bingham@ideasonboard.com> > > > > --- > > 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 > > > > src/ipa/libipa/meson.build | 2 + > > src/ipa/libipa/quantized.cpp | 134 +++++++++++++++++++++++++++++++++++ > > src/ipa/libipa/quantized.h | 78 ++++++++++++++++++++ > > 3 files changed, 214 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..ef3ae21e9a3b > > --- /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 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 > > + */ > > + > > +} /* namespace ipa */ > > + > > +} /* namespace libcamera */ > > diff --git a/src/ipa/libipa/quantized.h b/src/ipa/libipa/quantized.h > > new file mode 100644 > > index 000000000000..db1c0d72db86 > > --- /dev/null > > +++ b/src/ipa/libipa/quantized.h > > @@ -0,0 +1,78 @@ > > +/* 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 <iomanip> > > <iomanip> does not seem necessary. > > > > +#include <sstream> > > +#include <stdint.h> > > <stdint.h> does not seem necessary. > > > > +#include <type_traits> > > + > > +#include <libcamera/base/utils.h> > > + > > +namespace libcamera { > > + > > +namespace ipa { > > + > > +template<typename Traits> > > +struct Quantized { > > + using TraitsType = Traits; > > + using QuantizedType = typename Traits::QuantizedType; > > + static_assert(std::is_arithmetic_v<QuantizedType>, > > + "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_); > > I think I have brought this up earlier, but I'm wondering if we might want to > store `x` in `value_`. For example, if a control has float type, is it expected > that the application should account for the quantization when comparing the > desired value and actual value in the request metadata to check for convergence? > > > > + 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::ostringstream oss; > > + > > + oss << "[" << utils::hex(quantized_) > > + << ":" << value_ << "]"; > > + > > + return oss.str(); > > + } > > I'd really like to have `operator<<()` and then `toString()` implemented using that. > I'm sure in some earlier iteration I tried to add operator<<() and I struggled, but now things have changed a lot I can indeed try again and let you know if I struggle again. > > Reviewed-by: Barnabás Pőcze <barnabas.pocze@ideasonboard.com> > > > + > > + bool operator==(const Quantized &other) const noexcept > > + { > > + return quantized_ == other.quantized_; > > + } > > + > > + bool operator!=(const Quantized &other) const noexcept > > + { > > + return !(*this == other); > > + } > > + > > +private: > > + QuantizedType quantized_; > > + float value_; > > +}; > > + > > +} /* namespace ipa */ > > + > > +} /* namespace libcamera */ >
On Fri, Nov 14, 2025 at 06:54:14PM +0000, Kieran Bingham wrote: > Quoting Barnabás Pőcze (2025-11-14 18:08:03) > > 2025. 11. 14. 1:54 keltezéssel, Kieran Bingham írta: > > > 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 <kieran.bingham@ideasonboard.com> > > > > > > --- > > > 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 > > > > > > src/ipa/libipa/meson.build | 2 + > > > src/ipa/libipa/quantized.cpp | 134 +++++++++++++++++++++++++++++++++++ > > > src/ipa/libipa/quantized.h | 78 ++++++++++++++++++++ > > > 3 files changed, 214 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..ef3ae21e9a3b > > > --- /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 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 > > > + */ > > > + > > > +} /* namespace ipa */ > > > + > > > +} /* namespace libcamera */ > > > diff --git a/src/ipa/libipa/quantized.h b/src/ipa/libipa/quantized.h > > > new file mode 100644 > > > index 000000000000..db1c0d72db86 > > > --- /dev/null > > > +++ b/src/ipa/libipa/quantized.h > > > @@ -0,0 +1,78 @@ > > > +/* 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 <iomanip> > > > > <iomanip> does not seem necessary. > > > > > +#include <sstream> > > > +#include <stdint.h> > > > > <stdint.h> does not seem necessary. > > > > > +#include <type_traits> > > > + > > > +#include <libcamera/base/utils.h> > > > + > > > +namespace libcamera { > > > + > > > +namespace ipa { > > > + > > > +template<typename Traits> > > > +struct Quantized { > > > + using TraitsType = Traits; > > > + using QuantizedType = typename Traits::QuantizedType; > > > + static_assert(std::is_arithmetic_v<QuantizedType>, > > > + "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_); > > > > I think I have brought this up earlier, but I'm wondering if we might want to > > store `x` in `value_`. For example, if a control has float type, is it expected > > that the application should account for the quantization when comparing the > > desired value and actual value in the request metadata to check for convergence? Metadata should report the exact value applied to the frame, so I think it should be up to the application to deal with the fact they will receive rounded/quantized values. There's however another use case for the float value. Within libcamera, we often need to check if control values set in a request have changed compared to the previous requests. Comparing the float value will fail. Is it envisioned that this would be done by first converting the float value to a Quantized instance, and then comparing the Quantized instances ? If so, I think we should consider making the foat (and possibly QuantizedType) constructors explicit, to avoid unexpected behaviour when comparing Quantized and float. > > > + 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::ostringstream oss; > > > + > > > + oss << "[" << utils::hex(quantized_) > > > + << ":" << value_ << "]"; > > > + > > > + return oss.str(); > > > + } > > > > I'd really like to have `operator<<()` and then `toString()` implemented using that. > > I'm sure in some earlier iteration I tried to add operator<<() and I > struggled, but now things have changed a lot I can indeed try again and > let you know if I struggle again. If you run into problems let me know. I've had a look at operator<<() issues when reviewing your hex() series and researched ADL (https://en.cppreference.com/w/cpp/language/adl.html) issues. > > Reviewed-by: Barnabás Pőcze <barnabas.pocze@ideasonboard.com> > > > > > + > > > + bool operator==(const Quantized &other) const noexcept > > > + { > > > + return quantized_ == other.quantized_; > > > + } > > > + > > > + bool operator!=(const Quantized &other) const noexcept > > > + { > > > + return !(*this == other); > > > + } > > > + > > > +private: > > > + QuantizedType quantized_; > > > + float value_; > > > +}; > > > + > > > +} /* namespace ipa */ > > > + > > > +} /* namespace libcamera */
Hi Kieran, Thank you for the patch! Reviewed-by: Isaac Scott <isaac.scott@ideasonboard.com> Quoting Kieran Bingham (2025-11-14 00:54:05) > 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 <kieran.bingham@ideasonboard.com> > > --- > 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 > > src/ipa/libipa/meson.build | 2 + > src/ipa/libipa/quantized.cpp | 134 +++++++++++++++++++++++++++++++++++ > src/ipa/libipa/quantized.h | 78 ++++++++++++++++++++ > 3 files changed, 214 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..ef3ae21e9a3b > --- /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 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 > + */ > + > +} /* namespace ipa */ > + > +} /* namespace libcamera */ > diff --git a/src/ipa/libipa/quantized.h b/src/ipa/libipa/quantized.h > new file mode 100644 > index 000000000000..db1c0d72db86 > --- /dev/null > +++ b/src/ipa/libipa/quantized.h > @@ -0,0 +1,78 @@ > +/* 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 <iomanip> > +#include <sstream> > +#include <stdint.h> > +#include <type_traits> > + > +#include <libcamera/base/utils.h> > + > +namespace libcamera { > + > +namespace ipa { > + > +template<typename Traits> > +struct Quantized { > + using TraitsType = Traits; > + using QuantizedType = typename Traits::QuantizedType; > + static_assert(std::is_arithmetic_v<QuantizedType>, > + "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::ostringstream oss; > + > + oss << "[" << utils::hex(quantized_) > + << ":" << 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: > + QuantizedType quantized_; > + float value_; > +}; > + > +} /* namespace ipa */ > + > +} /* namespace libcamera */ > -- > 2.51.1 >
Quoting Laurent Pinchart (2025-11-17 12:48:38) > On Fri, Nov 14, 2025 at 06:54:14PM +0000, Kieran Bingham wrote: > > Quoting Barnabás Pőcze (2025-11-14 18:08:03) > > > 2025. 11. 14. 1:54 keltezéssel, Kieran Bingham írta: > > > > 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 <kieran.bingham@ideasonboard.com> > > > > > > > > --- > > > > 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 > > > > > > > > src/ipa/libipa/meson.build | 2 + > > > > src/ipa/libipa/quantized.cpp | 134 +++++++++++++++++++++++++++++++++++ > > > > src/ipa/libipa/quantized.h | 78 ++++++++++++++++++++ > > > > 3 files changed, 214 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..ef3ae21e9a3b > > > > --- /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 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 > > > > + */ > > > > + > > > > +} /* namespace ipa */ > > > > + > > > > +} /* namespace libcamera */ > > > > diff --git a/src/ipa/libipa/quantized.h b/src/ipa/libipa/quantized.h > > > > new file mode 100644 > > > > index 000000000000..db1c0d72db86 > > > > --- /dev/null > > > > +++ b/src/ipa/libipa/quantized.h > > > > @@ -0,0 +1,78 @@ > > > > +/* 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 <iomanip> > > > > > > <iomanip> does not seem necessary. > > > > > > > +#include <sstream> > > > > +#include <stdint.h> > > > > > > <stdint.h> does not seem necessary. > > > > > > > +#include <type_traits> > > > > + > > > > +#include <libcamera/base/utils.h> > > > > + > > > > +namespace libcamera { > > > > + > > > > +namespace ipa { > > > > + > > > > +template<typename Traits> > > > > +struct Quantized { > > > > + using TraitsType = Traits; > > > > + using QuantizedType = typename Traits::QuantizedType; > > > > + static_assert(std::is_arithmetic_v<QuantizedType>, > > > > + "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_); > > > > > > I think I have brought this up earlier, but I'm wondering if we might want to > > > store `x` in `value_`. For example, if a control has float type, is it expected > > > that the application should account for the quantization when comparing the > > > desired value and actual value in the request metadata to check for convergence? > > Metadata should report the exact value applied to the frame, so I think > it should be up to the application to deal with the fact they will > receive rounded/quantized values. > > There's however another use case for the float value. Within libcamera, > we often need to check if control values set in a request have changed > compared to the previous requests. Comparing the float value will fail. > Is it envisioned that this would be done by first converting the float > value to a Quantized instance, and then comparing the Quantized > instances ? If so, I think we should consider making the foat (and > possibly QuantizedType) constructors explicit, to avoid unexpected > behaviour when comparing Quantized and float. I don't yet understand why you need explicit constructors for this, or what specifically your asking to (remove?) Later in the series shows where this exact use case is added: /* Only process if the control is in the request */ const auto &hue = controls.get(controls::Hue); if (hue) { /* Immediately quantize the incoming value */ HueQ value = *hue; /* This compares *quantized* values, and cproc.hue is a * quantized type so this compares the quantized not the * float correctly.*/ if (cproc.hue != value) { cproc.hue = value; update = true; } LOG(RkISP1CProc, Debug) << "Set hue to " << value.value(); } Is there something specific you're concerned about or would like to inhibit ? -- Kieran > > > > > + 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::ostringstream oss; > > > > + > > > > + oss << "[" << utils::hex(quantized_) > > > > + << ":" << value_ << "]"; > > > > + > > > > + return oss.str(); > > > > + } > > > > > > I'd really like to have `operator<<()` and then `toString()` implemented using that. > > > > I'm sure in some earlier iteration I tried to add operator<<() and I > > struggled, but now things have changed a lot I can indeed try again and > > let you know if I struggle again. > > If you run into problems let me know. I've had a look at operator<<() > issues when reviewing your hex() series and researched ADL > (https://en.cppreference.com/w/cpp/language/adl.html) issues. > > > > Reviewed-by: Barnabás Pőcze <barnabas.pocze@ideasonboard.com> > > > > > > > + > > > > + bool operator==(const Quantized &other) const noexcept > > > > + { > > > > + return quantized_ == other.quantized_; > > > > + } > > > > + > > > > + bool operator!=(const Quantized &other) const noexcept > > > > + { > > > > + return !(*this == other); > > > > + } > > > > + > > > > +private: > > > > + QuantizedType quantized_; > > > > + float value_; > > > > +}; > > > > + > > > > +} /* namespace ipa */ > > > > + > > > > +} /* namespace libcamera */ > > -- > Regards, > > Laurent Pinchart
On Tue, Nov 18, 2025 at 11:04:43PM +0000, Kieran Bingham wrote: > Quoting Laurent Pinchart (2025-11-17 12:48:38) > > On Fri, Nov 14, 2025 at 06:54:14PM +0000, Kieran Bingham wrote: > > > Quoting Barnabás Pőcze (2025-11-14 18:08:03) > > > > 2025. 11. 14. 1:54 keltezéssel, Kieran Bingham írta: > > > > > 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 <kieran.bingham@ideasonboard.com> > > > > > > > > > > --- > > > > > 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 > > > > > > > > > > src/ipa/libipa/meson.build | 2 + > > > > > src/ipa/libipa/quantized.cpp | 134 +++++++++++++++++++++++++++++++++++ > > > > > src/ipa/libipa/quantized.h | 78 ++++++++++++++++++++ > > > > > 3 files changed, 214 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..ef3ae21e9a3b > > > > > --- /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 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 > > > > > + */ > > > > > + > > > > > +} /* namespace ipa */ > > > > > + > > > > > +} /* namespace libcamera */ > > > > > diff --git a/src/ipa/libipa/quantized.h b/src/ipa/libipa/quantized.h > > > > > new file mode 100644 > > > > > index 000000000000..db1c0d72db86 > > > > > --- /dev/null > > > > > +++ b/src/ipa/libipa/quantized.h > > > > > @@ -0,0 +1,78 @@ > > > > > +/* 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 <iomanip> > > > > > > > > <iomanip> does not seem necessary. > > > > > > > > > +#include <sstream> > > > > > +#include <stdint.h> > > > > > > > > <stdint.h> does not seem necessary. > > > > > > > > > +#include <type_traits> > > > > > + > > > > > +#include <libcamera/base/utils.h> > > > > > + > > > > > +namespace libcamera { > > > > > + > > > > > +namespace ipa { > > > > > + > > > > > +template<typename Traits> > > > > > +struct Quantized { > > > > > + using TraitsType = Traits; > > > > > + using QuantizedType = typename Traits::QuantizedType; > > > > > + static_assert(std::is_arithmetic_v<QuantizedType>, > > > > > + "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_); > > > > > > > > I think I have brought this up earlier, but I'm wondering if we might want to > > > > store `x` in `value_`. For example, if a control has float type, is it expected > > > > that the application should account for the quantization when comparing the > > > > desired value and actual value in the request metadata to check for convergence? > > > > Metadata should report the exact value applied to the frame, so I think > > it should be up to the application to deal with the fact they will > > receive rounded/quantized values. > > > > There's however another use case for the float value. Within libcamera, > > we often need to check if control values set in a request have changed > > compared to the previous requests. Comparing the float value will fail. > > Is it envisioned that this would be done by first converting the float > > value to a Quantized instance, and then comparing the Quantized > > instances ? If so, I think we should consider making the foat (and > > possibly QuantizedType) constructors explicit, to avoid unexpected > > behaviour when comparing Quantized and float. > > I don't yet understand why you need explicit constructors for this, or > what specifically your asking to (remove?) > > Later in the series shows where this exact use case is added: > > /* Only process if the control is in the request */ > const auto &hue = controls.get(controls::Hue); > if (hue) { > /* Immediately quantize the incoming value */ > HueQ value = *hue; > /* This compares *quantized* values, and cproc.hue is a > * quantized type so this compares the quantized not the > * float correctly.*/ > if (cproc.hue != value) { > cproc.hue = value; > update = true; > } > > LOG(RkISP1CProc, Debug) << "Set hue to " << value.value(); > } > > > Is there something specific you're concerned about or would like to > inhibit ? The above is fine. What I'd like to avoid is const auto &hue = controls.get(controls::Hue); if (hue) { /* * This implicitly creates a HueQ instance from *hue (a float) * and then compares Quantized instances. */ if (cproc.hue != *hue) { ... } ... } Making the constructor explicit will ensure implicit conversion won't occur. Or do you think the above construct should be allowed ? > > > > > + 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::ostringstream oss; > > > > > + > > > > > + oss << "[" << utils::hex(quantized_) > > > > > + << ":" << value_ << "]"; > > > > > + > > > > > + return oss.str(); > > > > > + } > > > > > > > > I'd really like to have `operator<<()` and then `toString()` implemented using that. > > > > > > I'm sure in some earlier iteration I tried to add operator<<() and I > > > struggled, but now things have changed a lot I can indeed try again and > > > let you know if I struggle again. > > > > If you run into problems let me know. I've had a look at operator<<() > > issues when reviewing your hex() series and researched ADL > > (https://en.cppreference.com/w/cpp/language/adl.html) issues. > > > > > > Reviewed-by: Barnabás Pőcze <barnabas.pocze@ideasonboard.com> > > > > > > > > > + > > > > > + bool operator==(const Quantized &other) const noexcept > > > > > + { > > > > > + return quantized_ == other.quantized_; > > > > > + } > > > > > + > > > > > + bool operator!=(const Quantized &other) const noexcept > > > > > + { > > > > > + return !(*this == other); > > > > > + } > > > > > + > > > > > +private: > > > > > + QuantizedType quantized_; > > > > > + float value_; > > > > > +}; > > > > > + > > > > > +} /* namespace ipa */ > > > > > + > > > > > +} /* namespace libcamera */
Quoting Laurent Pinchart (2025-11-19 05:12:36) > On Tue, Nov 18, 2025 at 11:04:43PM +0000, Kieran Bingham wrote: > > Quoting Laurent Pinchart (2025-11-17 12:48:38) > > > On Fri, Nov 14, 2025 at 06:54:14PM +0000, Kieran Bingham wrote: > > > > Quoting Barnabás Pőcze (2025-11-14 18:08:03) > > > > > 2025. 11. 14. 1:54 keltezéssel, Kieran Bingham írta: > > > > > > 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 <kieran.bingham@ideasonboard.com> <snip> > > > > > > + Quantized &operator=(float x) > > > > > > + { > > > > > > + quantized_ = Traits::fromFloat(x); > > > > > > + value_ = Traits::toFloat(quantized_); > > > > > > > > > > I think I have brought this up earlier, but I'm wondering if we might want to > > > > > store `x` in `value_`. For example, if a control has float type, is it expected > > > > > that the application should account for the quantization when comparing the > > > > > desired value and actual value in the request metadata to check for convergence? > > > > > > Metadata should report the exact value applied to the frame, so I think > > > it should be up to the application to deal with the fact they will > > > receive rounded/quantized values. > > > > > > There's however another use case for the float value. Within libcamera, > > > we often need to check if control values set in a request have changed > > > compared to the previous requests. Comparing the float value will fail. > > > Is it envisioned that this would be done by first converting the float > > > value to a Quantized instance, and then comparing the Quantized > > > instances ? If so, I think we should consider making the foat (and > > > possibly QuantizedType) constructors explicit, to avoid unexpected > > > behaviour when comparing Quantized and float. > > > > I don't yet understand why you need explicit constructors for this, or > > what specifically your asking to (remove?) > > > > Later in the series shows where this exact use case is added: > > > > /* Only process if the control is in the request */ > > const auto &hue = controls.get(controls::Hue); > > if (hue) { > > /* Immediately quantize the incoming value */ > > HueQ value = *hue; > > /* This compares *quantized* values, and cproc.hue is a > > * quantized type so this compares the quantized not the > > * float correctly.*/ > > if (cproc.hue != value) { > > cproc.hue = value; > > update = true; > > } > > > > LOG(RkISP1CProc, Debug) << "Set hue to " << value.value(); > > } > > > > > > Is there something specific you're concerned about or would like to > > inhibit ? > > The above is fine. What I'd like to avoid is > > const auto &hue = controls.get(controls::Hue); > if (hue) { > /* > * This implicitly creates a HueQ instance from *hue (a float) > * and then compares Quantized instances. > */ > if (cproc.hue != *hue) { > ... > } > > ... > } > > Making the constructor explicit will ensure implicit conversion won't > occur. > > Or do you think the above construct should be allowed ? In fact - I think the above is equivalent and valid just like the use case I had. *hue will be implicitly converted to a HueQ and quantized, and only the quantized value will be compared. Iff that value is different, it will then update the cproc hardware. So in your case, the implementation is: const auto &hue = controls.get(controls::Hue); if (hue) { /* * This implicitly creates a HueQ instance from *hue (a float) * and then compares Quantized instances. */ if (cproc.hue != *hue) { cproc.hue = *hue; /* Duplicated quantisation */ update = true; /* * Use the new operator<< to output: * "Set hue to [0x80:-1]" */ LOG(RkISP1CProc, Debug) << "Set hue to " << cproc.hue; } } -- Kieran > > > > > > > + 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::ostringstream oss; > > > > > > + > > > > > > + oss << "[" << utils::hex(quantized_) > > > > > > + << ":" << value_ << "]"; > > > > > > + > > > > > > + return oss.str(); > > > > > > + } > > > > > > > > > > I'd really like to have `operator<<()` and then `toString()` implemented using that. > > > > > > > > I'm sure in some earlier iteration I tried to add operator<<() and I > > > > struggled, but now things have changed a lot I can indeed try again and > > > > let you know if I struggle again. > > > > > > If you run into problems let me know. I've had a look at operator<<() > > > issues when reviewing your hex() series and researched ADL > > > (https://en.cppreference.com/w/cpp/language/adl.html) issues. I'll dig into this first, but come back to you if I can't get it solved. -- Thanks Kieran > > -- > Regards, > > Laurent Pinchart
On Wed, Nov 19, 2025 at 09:33:48AM +0000, Kieran Bingham wrote: > Quoting Laurent Pinchart (2025-11-19 05:12:36) > > On Tue, Nov 18, 2025 at 11:04:43PM +0000, Kieran Bingham wrote: > > > Quoting Laurent Pinchart (2025-11-17 12:48:38) > > > > On Fri, Nov 14, 2025 at 06:54:14PM +0000, Kieran Bingham wrote: > > > > > Quoting Barnabás Pőcze (2025-11-14 18:08:03) > > > > > > 2025. 11. 14. 1:54 keltezéssel, Kieran Bingham írta: > > > > > > > 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 <kieran.bingham@ideasonboard.com> > > <snip> > > > > > > > > + Quantized &operator=(float x) > > > > > > > + { > > > > > > > + quantized_ = Traits::fromFloat(x); > > > > > > > + value_ = Traits::toFloat(quantized_); > > > > > > > > > > > > I think I have brought this up earlier, but I'm wondering if we might want to > > > > > > store `x` in `value_`. For example, if a control has float type, is it expected > > > > > > that the application should account for the quantization when comparing the > > > > > > desired value and actual value in the request metadata to check for convergence? > > > > > > > > Metadata should report the exact value applied to the frame, so I think > > > > it should be up to the application to deal with the fact they will > > > > receive rounded/quantized values. > > > > > > > > There's however another use case for the float value. Within libcamera, > > > > we often need to check if control values set in a request have changed > > > > compared to the previous requests. Comparing the float value will fail. > > > > Is it envisioned that this would be done by first converting the float > > > > value to a Quantized instance, and then comparing the Quantized > > > > instances ? If so, I think we should consider making the foat (and > > > > possibly QuantizedType) constructors explicit, to avoid unexpected > > > > behaviour when comparing Quantized and float. > > > > > > I don't yet understand why you need explicit constructors for this, or > > > what specifically your asking to (remove?) > > > > > > Later in the series shows where this exact use case is added: > > > > > > /* Only process if the control is in the request */ > > > const auto &hue = controls.get(controls::Hue); > > > if (hue) { > > > /* Immediately quantize the incoming value */ > > > HueQ value = *hue; > > > /* This compares *quantized* values, and cproc.hue is a > > > * quantized type so this compares the quantized not the > > > * float correctly.*/ > > > if (cproc.hue != value) { > > > cproc.hue = value; > > > update = true; > > > } > > > > > > LOG(RkISP1CProc, Debug) << "Set hue to " << value.value(); > > > } > > > > > > > > > Is there something specific you're concerned about or would like to > > > inhibit ? > > > > The above is fine. What I'd like to avoid is > > > > const auto &hue = controls.get(controls::Hue); > > if (hue) { > > /* > > * This implicitly creates a HueQ instance from *hue (a float) > > * and then compares Quantized instances. > > */ > > if (cproc.hue != *hue) { > > ... > > } > > > > ... > > } > > > > Making the constructor explicit will ensure implicit conversion won't > > occur. > > > > Or do you think the above construct should be allowed ? > > In fact - I think the above is equivalent and valid just like the use > case I had. They are equivalent, but that's not my point. I think the code in my example is confusing, as the comparison between cproc.hue and a float makes it seem that float values are compared. Forcing explicit conversion to a Quantized type avoids that. > *hue will be implicitly converted to a HueQ and quantized, and only the > quantized value will be compared. > > Iff that value is different, it will then update the cproc hardware. > > So in your case, the implementation is: > > const auto &hue = controls.get(controls::Hue); > if (hue) { > /* > * This implicitly creates a HueQ instance from *hue (a float) > * and then compares Quantized instances. > */ > if (cproc.hue != *hue) { > cproc.hue = *hue; /* Duplicated quantisation */ > update = true; > /* > * Use the new operator<< to output: > * "Set hue to [0x80:-1]" > */ > LOG(RkISP1CProc, Debug) << "Set hue to " << cproc.hue; > } > } > > > > > > > > + 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::ostringstream oss; > > > > > > > + > > > > > > > + oss << "[" << utils::hex(quantized_) > > > > > > > + << ":" << value_ << "]"; > > > > > > > + > > > > > > > + return oss.str(); > > > > > > > + } > > > > > > > > > > > > I'd really like to have `operator<<()` and then `toString()` implemented using that. > > > > > > > > > > I'm sure in some earlier iteration I tried to add operator<<() and I > > > > > struggled, but now things have changed a lot I can indeed try again and > > > > > let you know if I struggle again. > > > > > > > > If you run into problems let me know. I've had a look at operator<<() > > > > issues when reviewing your hex() series and researched ADL > > > > (https://en.cppreference.com/w/cpp/language/adl.html) issues. > > I'll dig into this first, but come back to you if I can't get it solved.
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..ef3ae21e9a3b --- /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 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 + */ + +} /* namespace ipa */ + +} /* namespace libcamera */ diff --git a/src/ipa/libipa/quantized.h b/src/ipa/libipa/quantized.h new file mode 100644 index 000000000000..db1c0d72db86 --- /dev/null +++ b/src/ipa/libipa/quantized.h @@ -0,0 +1,78 @@ +/* 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 <iomanip> +#include <sstream> +#include <stdint.h> +#include <type_traits> + +#include <libcamera/base/utils.h> + +namespace libcamera { + +namespace ipa { + +template<typename Traits> +struct Quantized { + using TraitsType = Traits; + using QuantizedType = typename Traits::QuantizedType; + static_assert(std::is_arithmetic_v<QuantizedType>, + "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::ostringstream oss; + + oss << "[" << utils::hex(quantized_) + << ":" << 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: + QuantizedType quantized_; + float value_; +}; + +} /* namespace ipa */ + +} /* namespace libcamera */
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 <kieran.bingham@ideasonboard.com> --- 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 src/ipa/libipa/meson.build | 2 + src/ipa/libipa/quantized.cpp | 134 +++++++++++++++++++++++++++++++++++ src/ipa/libipa/quantized.h | 78 ++++++++++++++++++++ 3 files changed, 214 insertions(+) create mode 100644 src/ipa/libipa/quantized.cpp create mode 100644 src/ipa/libipa/quantized.h