[v7,01/15] ipa: libipa: Provide a Quantized data type support
diff mbox series

Message ID 20260213-kbingham-quantizers-v7-1-1626b9aaabf1@ideasonboard.com
State Accepted
Headers show
Series
  • libipa: Introduce a Quantized type
Related show

Commit Message

Kieran Bingham Feb. 13, 2026, 4:57 p.m. UTC
Frequently when handling data in IPA components we must convert and
store user interface values which may be floating point values, and
perform a specific operation or conversion to quantize this to a
hardware value.

This value may be to a fixed point type, or more custom code mappings,
but in either case it is important to contain both the required hardware
value, with its effective quantized value.

Provide a new storage type 'Quantized' which can be defined based on a
set of type specific Traits to perform the conversions between floats
and the underlying hardware type.

Reviewed-by: Barnabás Pőcze <barnabas.pocze@ideasonboard.com>
Reviewed-by: Isaac Scott <isaac.scott@ideasonboard.com>
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

v5:
- introduce operator<<(std::ostream &out, const Quantized<Traits> &q)
- Remove unused iomanip and stdint.h from includes
---
 src/ipa/libipa/meson.build   |   2 +
 src/ipa/libipa/quantized.cpp | 135 +++++++++++++++++++++++++++++++++++++++++++
 src/ipa/libipa/quantized.h   |  75 ++++++++++++++++++++++++
 3 files changed, 212 insertions(+)

Comments

Paul Elder Feb. 19, 2026, 7:30 a.m. UTC | #1
Quoting Kieran Bingham (2026-02-14 01:57:40)
> Frequently when handling data in IPA components we must convert and
> store user interface values which may be floating point values, and
> perform a specific operation or conversion to quantize this to a
> hardware value.
> 
> This value may be to a fixed point type, or more custom code mappings,
> but in either case it is important to contain both the required hardware
> value, with its effective quantized value.
> 
> Provide a new storage type 'Quantized' which can be defined based on a
> set of type specific Traits to perform the conversions between floats
> and the underlying hardware type.
> 
> Reviewed-by: Barnabás Pőcze <barnabas.pocze@ideasonboard.com>
> Reviewed-by: Isaac Scott <isaac.scott@ideasonboard.com>
> Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>

Reviewed-by: Paul Elder <paul.elder@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
> 
> v5:
> - introduce operator<<(std::ostream &out, const Quantized<Traits> &q)
> - Remove unused iomanip and stdint.h from includes
> ---
>  src/ipa/libipa/meson.build   |   2 +
>  src/ipa/libipa/quantized.cpp | 135 +++++++++++++++++++++++++++++++++++++++++++
>  src/ipa/libipa/quantized.h   |  75 ++++++++++++++++++++++++
>  3 files changed, 212 insertions(+)
> 
> diff --git a/src/ipa/libipa/meson.build b/src/ipa/libipa/meson.build
> index 7202df869c2f..963c5ee73063 100644
> --- a/src/ipa/libipa/meson.build
> +++ b/src/ipa/libipa/meson.build
> @@ -17,6 +17,7 @@ libipa_headers = files([
>      'lux.h',
>      'module.h',
>      'pwl.h',
> +    'quantized.h',
>      'v4l2_params.h',
>  ])
>  
> @@ -37,6 +38,7 @@ libipa_sources = files([
>      'lux.cpp',
>      'module.cpp',
>      'pwl.cpp',
> +    'quantized.cpp',
>      'v4l2_params.cpp',
>  ])
>  
> diff --git a/src/ipa/libipa/quantized.cpp b/src/ipa/libipa/quantized.cpp
> new file mode 100644
> index 000000000000..06143a97ab3e
> --- /dev/null
> +++ b/src/ipa/libipa/quantized.cpp
> @@ -0,0 +1,135 @@
> +/* 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::operator==(const Quantized &other) const noexcept
> + * \brief Compare two Quantized objects for equality
> + * \param[in] other The other Quantized object to compare against
> + * \return True if both objects have the same quantized integer value
> + */
> +
> +/**
> + * \fn Quantized::operator!=(const Quantized &other) const noexcept
> + * \brief Compare two Quantized objects for inequality
> + * \param[in] other The other Quantized object to compare against
> + * \return True if the quantized integer values differ
> + */
> +
> +/**
> + * \fn std::ostream &Quantized::operator<<(std::ostream &out, const Quantized<Traits> &q)
> + * \brief Insert a text representation of a Quantized into an output stream
> + * \param[in] out The output stream
> + * \param[in] q The Quantized
> + * \return The output stream \a out
> + */
> +
> +} /* namespace ipa */
> +
> +} /* namespace libcamera */
> diff --git a/src/ipa/libipa/quantized.h b/src/ipa/libipa/quantized.h
> new file mode 100644
> index 000000000000..0b2f7148c821
> --- /dev/null
> +++ b/src/ipa/libipa/quantized.h
> @@ -0,0 +1,75 @@
> +/* 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 <sstream>
> +#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_; }
> +
> +       bool operator==(const Quantized &other) const noexcept
> +       {
> +               return quantized_ == other.quantized_;
> +       }
> +
> +       bool operator!=(const Quantized &other) const noexcept
> +       {
> +               return !(*this == other);
> +       }
> +
> +       friend std::ostream &operator<<(std::ostream &out,
> +                                       const Quantized<Traits> &q)
> +       {
> +               out << "[" << utils::hex(q.quantized())
> +                   << ":" << q.value() << "]";
> +
> +               return out;
> +       }
> +
> +private:
> +       QuantizedType quantized_;
> +       float value_;
> +};
> +
> +} /* namespace ipa */
> +
> +} /* namespace libcamera */
> 
> -- 
> 2.52.0
>
Laurent Pinchart Feb. 19, 2026, 9:27 a.m. UTC | #2
Hi Kieran,

Thank you for the patch.

It seems you forgot to address my comments on v6.

On Fri, Feb 13, 2026 at 04:57:40PM +0000, Kieran Bingham wrote:
> Frequently when handling data in IPA components we must convert and
> store user interface values which may be floating point values, and
> perform a specific operation or conversion to quantize this to a
> hardware value.
> 
> This value may be to a fixed point type, or more custom code mappings,
> but in either case it is important to contain both the required hardware
> value, with its effective quantized value.
> 
> Provide a new storage type 'Quantized' which can be defined based on a
> set of type specific Traits to perform the conversions between floats
> and the underlying hardware type.
> 
> Reviewed-by: Barnabás Pőcze <barnabas.pocze@ideasonboard.com>
> Reviewed-by: Isaac Scott <isaac.scott@ideasonboard.com>
> 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
> 
> v5:
> - introduce operator<<(std::ostream &out, const Quantized<Traits> &q)
> - Remove unused iomanip and stdint.h from includes
> ---
>  src/ipa/libipa/meson.build   |   2 +
>  src/ipa/libipa/quantized.cpp | 135 +++++++++++++++++++++++++++++++++++++++++++
>  src/ipa/libipa/quantized.h   |  75 ++++++++++++++++++++++++
>  3 files changed, 212 insertions(+)
> 
> diff --git a/src/ipa/libipa/meson.build b/src/ipa/libipa/meson.build
> index 7202df869c2f..963c5ee73063 100644
> --- a/src/ipa/libipa/meson.build
> +++ b/src/ipa/libipa/meson.build
> @@ -17,6 +17,7 @@ libipa_headers = files([
>      'lux.h',
>      'module.h',
>      'pwl.h',
> +    'quantized.h',
>      'v4l2_params.h',
>  ])
>  
> @@ -37,6 +38,7 @@ libipa_sources = files([
>      'lux.cpp',
>      'module.cpp',
>      'pwl.cpp',
> +    'quantized.cpp',
>      'v4l2_params.cpp',
>  ])
>  
> diff --git a/src/ipa/libipa/quantized.cpp b/src/ipa/libipa/quantized.cpp
> new file mode 100644
> index 000000000000..06143a97ab3e
> --- /dev/null
> +++ b/src/ipa/libipa/quantized.cpp
> @@ -0,0 +1,135 @@
> +/* 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::operator==(const Quantized &other) const noexcept
> + * \brief Compare two Quantized objects for equality
> + * \param[in] other The other Quantized object to compare against
> + * \return True if both objects have the same quantized integer value
> + */
> +
> +/**
> + * \fn Quantized::operator!=(const Quantized &other) const noexcept
> + * \brief Compare two Quantized objects for inequality
> + * \param[in] other The other Quantized object to compare against
> + * \return True if the quantized integer values differ
> + */
> +
> +/**
> + * \fn std::ostream &Quantized::operator<<(std::ostream &out, const Quantized<Traits> &q)
> + * \brief Insert a text representation of a Quantized into an output stream
> + * \param[in] out The output stream
> + * \param[in] q The Quantized
> + * \return The output stream \a out
> + */
> +
> +} /* namespace ipa */
> +
> +} /* namespace libcamera */
> diff --git a/src/ipa/libipa/quantized.h b/src/ipa/libipa/quantized.h
> new file mode 100644
> index 000000000000..0b2f7148c821
> --- /dev/null
> +++ b/src/ipa/libipa/quantized.h
> @@ -0,0 +1,75 @@
> +/* 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 <sstream>
> +#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_; }
> +
> +	bool operator==(const Quantized &other) const noexcept
> +	{
> +		return quantized_ == other.quantized_;
> +	}
> +
> +	bool operator!=(const Quantized &other) const noexcept
> +	{
> +		return !(*this == other);
> +	}
> +
> +	friend std::ostream &operator<<(std::ostream &out,
> +					const Quantized<Traits> &q)
> +	{
> +		out << "[" << utils::hex(q.quantized())
> +		    << ":" << q.value() << "]";
> +
> +		return out;
> +	}
> +
> +private:
> +	QuantizedType quantized_;
> +	float value_;
> +};
> +
> +} /* namespace ipa */
> +
> +} /* namespace libcamera */

Patch
diff mbox series

diff --git a/src/ipa/libipa/meson.build b/src/ipa/libipa/meson.build
index 7202df869c2f..963c5ee73063 100644
--- a/src/ipa/libipa/meson.build
+++ b/src/ipa/libipa/meson.build
@@ -17,6 +17,7 @@  libipa_headers = files([
     'lux.h',
     'module.h',
     'pwl.h',
+    'quantized.h',
     'v4l2_params.h',
 ])
 
@@ -37,6 +38,7 @@  libipa_sources = files([
     'lux.cpp',
     'module.cpp',
     'pwl.cpp',
+    'quantized.cpp',
     'v4l2_params.cpp',
 ])
 
diff --git a/src/ipa/libipa/quantized.cpp b/src/ipa/libipa/quantized.cpp
new file mode 100644
index 000000000000..06143a97ab3e
--- /dev/null
+++ b/src/ipa/libipa/quantized.cpp
@@ -0,0 +1,135 @@ 
+/* 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::operator==(const Quantized &other) const noexcept
+ * \brief Compare two Quantized objects for equality
+ * \param[in] other The other Quantized object to compare against
+ * \return True if both objects have the same quantized integer value
+ */
+
+/**
+ * \fn Quantized::operator!=(const Quantized &other) const noexcept
+ * \brief Compare two Quantized objects for inequality
+ * \param[in] other The other Quantized object to compare against
+ * \return True if the quantized integer values differ
+ */
+
+/**
+ * \fn std::ostream &Quantized::operator<<(std::ostream &out, const Quantized<Traits> &q)
+ * \brief Insert a text representation of a Quantized into an output stream
+ * \param[in] out The output stream
+ * \param[in] q The Quantized
+ * \return The output stream \a out
+ */
+
+} /* namespace ipa */
+
+} /* namespace libcamera */
diff --git a/src/ipa/libipa/quantized.h b/src/ipa/libipa/quantized.h
new file mode 100644
index 000000000000..0b2f7148c821
--- /dev/null
+++ b/src/ipa/libipa/quantized.h
@@ -0,0 +1,75 @@ 
+/* 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 <sstream>
+#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_; }
+
+	bool operator==(const Quantized &other) const noexcept
+	{
+		return quantized_ == other.quantized_;
+	}
+
+	bool operator!=(const Quantized &other) const noexcept
+	{
+		return !(*this == other);
+	}
+
+	friend std::ostream &operator<<(std::ostream &out,
+					const Quantized<Traits> &q)
+	{
+		out << "[" << utils::hex(q.quantized())
+		    << ":" << q.value() << "]";
+
+		return out;
+	}
+
+private:
+	QuantizedType quantized_;
+	float value_;
+};
+
+} /* namespace ipa */
+
+} /* namespace libcamera */