[{"id":36541,"web_url":"https://patchwork.libcamera.org/comment/36541/","msgid":"<7a1e8573-2cd6-4df0-8626-031f18b2ba92@ideasonboard.com>","date":"2025-10-29T18:45:21","subject":"Re: [PATCH v2 01/13] libipa: Provide a Quantized data type support","submitter":{"id":216,"url":"https://patchwork.libcamera.org/api/people/216/","name":"Barnabás Pőcze","email":"barnabas.pocze@ideasonboard.com"},"content":"Hi\n\n2025. 10. 29. 18:24 keltezéssel, Kieran Bingham írta:\n> Frequently when handling data in IPA components we must convert and\n> store user interface values which may be floating point values, and\n> perform a specific operation or conversion to quantize this to a\n> hardware value.\n> \n> This value may be to a fixed point type, or more custom code mappings,\n> but in either case it is important to contain both the required hardware\n> value, with its effective quantized value.\n> \n> Provide a new storage type 'Quantized' which can be defined based on a\n> set of type specific Traits to perform the conversions between floats\n> and the underlying hardware type.\n> \n> Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>\n> ---\n>   src/ipa/libipa/meson.build   |   2 +\n>   src/ipa/libipa/quantized.cpp | 134 +++++++++++++++++++++++++++++++++++\n>   src/ipa/libipa/quantized.h   |  79 +++++++++++++++++++++\n>   3 files changed, 215 insertions(+)\n>   create mode 100644 src/ipa/libipa/quantized.cpp\n>   create mode 100644 src/ipa/libipa/quantized.h\n> \n> diff --git a/src/ipa/libipa/meson.build b/src/ipa/libipa/meson.build\n> index 660be94054fa..804289778f72 100644\n> --- a/src/ipa/libipa/meson.build\n> +++ b/src/ipa/libipa/meson.build\n> @@ -17,6 +17,7 @@ libipa_headers = files([\n>       'lux.h',\n>       'module.h',\n>       'pwl.h',\n> +    'quantized.h',\n>   ])\n>   \n>   libipa_sources = files([\n> @@ -36,6 +37,7 @@ libipa_sources = files([\n>       'lux.cpp',\n>       'module.cpp',\n>       'pwl.cpp',\n> +    'quantized.cpp',\n>   ])\n>   \n>   libipa_includes = include_directories('..')\n> [...]\n> diff --git a/src/ipa/libipa/quantized.h b/src/ipa/libipa/quantized.h\n> new file mode 100644\n> index 000000000000..26cb8d619e15\n> --- /dev/null\n> +++ b/src/ipa/libipa/quantized.h\n> @@ -0,0 +1,79 @@\n> +/* SPDX-License-Identifier: LGPL-2.1-or-later */\n> +/*\n> + * Copyright (C) 2025, Ideas On Board.\n> + *\n> + * Helper class to manage conversions between floating point types and quantized\n> + * storage and representation of those values.\n> + */\n> +\n> +#pragma once\n> +\n> +#include <iomanip>\n> +#include <sstream>\n> +#include <stdint.h>\n> +#include <type_traits>\n> +\n> +#include <libcamera/base/utils.h>\n> +\n> +namespace libcamera {\n> +\n> +namespace ipa {\n> +\n> +template<typename Traits>\n> +struct Quantized {\n> +\tusing TraitsType = Traits;\n> +\tusing quantized_type = typename Traits::quantized_type;\n\nSmall thing, but I think the member type aliases should both be\nsnake_case or PascalCase, but they shouldn't be mixed.\n\n\n> +\tstatic_assert(std::is_arithmetic_v<quantized_type>,\n> +\t\t      \"Quantized: quantized_type must be arithmetic\");\n> +\n> +\t/* Constructors */\n> +\tQuantized()\n> +\t\t: Quantized(0.0f) {}\n> +\tQuantized(float x) { *this = x; }\n> +\tQuantized(quantized_type x) { *this = x; }\n> +\n> +\tQuantized &operator=(float x)\n> +\t{\n> +\t\tquantized_ = Traits::fromFloat(x);\n> +\t\tvalue_ = Traits::toFloat(quantized_);\n\nI might have missed the discussion, but I'm wondering if it should save `x` as is?\n\n\n> +\t\treturn *this;\n> +\t}\n> +\n> +\tQuantized &operator=(quantized_type x)\n> +\t{\n> +\t\tvalue_ = Traits::toFloat(x);\n> +\t\tquantized_ = x;\n> +\t\treturn *this;\n> +\t}\n> +\n> +\tfloat value() const noexcept { return value_; }\n> +\tquantized_type quantized() const noexcept { return quantized_; }\n> +\n> +\tstd::string toString() const\n> +\t{\n> +\t\tstd::ostringstream oss;\n> +\n> +\t\toss << \"Q:\" << utils::hex(quantized_)\n> +\t\t    << \" V:\" << value_;\n\nIt's again a minor detail, but I'm wondering about a more compact\nrepresentation without whitespace in it. Maybe something like\n\n   value_ << '[' << utils::hex(quantized_) << ']'\n\nor\n\n   '[' << value_ << '|' << utils::hex(quantized_) << ']'\n\nMaybe it's just me, but I like if the string representation is visibly\na \"single unit\" even if there are many things in the same line.\n\n\nRegards,\nBarnabás Pőcze\n\n> +\n> +\t\treturn oss.str();\n> +\t}\n> +\n> +\tbool operator==(const Quantized &other) const noexcept\n> +\t{\n> +\t\treturn quantized_ == other.quantized_;\n> +\t}\n> +\n> +\tbool operator!=(const Quantized &other) const noexcept\n> +\t{\n> +\t\treturn !(*this == other);\n> +\t}\n> +\n> +private:\n> +\tquantized_type quantized_{};\n> +\tfloat value_{};\n> +};\n> +\n> +} /* namespace ipa */\n> +\n> +} /* namespace libcamera */","headers":{"Return-Path":"<libcamera-devel-bounces@lists.libcamera.org>","X-Original-To":"parsemail@patchwork.libcamera.org","Delivered-To":"parsemail@patchwork.libcamera.org","Received":["from lancelot.ideasonboard.com (lancelot.ideasonboard.com\n\t[92.243.16.209])\n\tby patchwork.libcamera.org (Postfix) with ESMTPS id EBD16C3259\n\tfor <parsemail@patchwork.libcamera.org>;\n\tWed, 29 Oct 2025 18:45:28 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id E9D396086F;\n\tWed, 29 Oct 2025 19:45:27 +0100 (CET)","from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[IPv6:2001:4b98:dc2:55:216:3eff:fef7:d647])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 46F4460856\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tWed, 29 Oct 2025 19:45:26 +0100 (CET)","from [192.168.33.28] (185.182.215.162.nat.pool.zt.hu\n\t[185.182.215.162])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id 329BE4E52;\n\tWed, 29 Oct 2025 19:43:36 +0100 (CET)"],"Authentication-Results":"lancelot.ideasonboard.com; dkim=pass (1024-bit key;\n\tunprotected) header.d=ideasonboard.com header.i=@ideasonboard.com\n\theader.b=\"DJ18gMoc\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1761763416;\n\tbh=93Kjc++xGm7qfxFbnirbEVir58sfr/FJszuG9/aqsu4=;\n\th=Date:Subject:To:References:From:In-Reply-To:From;\n\tb=DJ18gMoc6xAKtY6V2nntqoBzBuvCurmAR/llZkoARKBABeYagbOfa9NT+f1jptfb3\n\t8VaLSaF1jXNwlykaQcOHiX4EfScQqdSM71ZShffNB1xQ+EB9H/yihGeITnwMBmYWtv\n\tJKNQgx5eF5NWguRK6TQmGIGKIs2DpHkkQgwtxaMc=","Message-ID":"<7a1e8573-2cd6-4df0-8626-031f18b2ba92@ideasonboard.com>","Date":"Wed, 29 Oct 2025 19:45:21 +0100","MIME-Version":"1.0","User-Agent":"Mozilla Thunderbird","Subject":"Re: [PATCH v2 01/13] libipa: Provide a Quantized data type support","To":"Kieran Bingham <kieran.bingham@ideasonboard.com>,\n\tlibcamera devel <libcamera-devel@lists.libcamera.org>","References":"<20251029172439.1513907-1-kieran.bingham@ideasonboard.com>\n\t<20251029172439.1513907-2-kieran.bingham@ideasonboard.com>","From":"=?utf-8?q?Barnab=C3=A1s_P=C5=91cze?= <barnabas.pocze@ideasonboard.com>","Content-Language":"en-US, hu-HU","In-Reply-To":"<20251029172439.1513907-2-kieran.bingham@ideasonboard.com>","Content-Type":"text/plain; charset=UTF-8; format=flowed","Content-Transfer-Encoding":"8bit","X-BeenThere":"libcamera-devel@lists.libcamera.org","X-Mailman-Version":"2.1.29","Precedence":"list","List-Id":"<libcamera-devel.lists.libcamera.org>","List-Unsubscribe":"<https://lists.libcamera.org/options/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=unsubscribe>","List-Archive":"<https://lists.libcamera.org/pipermail/libcamera-devel/>","List-Post":"<mailto:libcamera-devel@lists.libcamera.org>","List-Help":"<mailto:libcamera-devel-request@lists.libcamera.org?subject=help>","List-Subscribe":"<https://lists.libcamera.org/listinfo/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=subscribe>","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"}},{"id":36542,"web_url":"https://patchwork.libcamera.org/comment/36542/","msgid":"<176177183637.2017750.6120344562634809954@ping.linuxembedded.co.uk>","date":"2025-10-29T21:03:56","subject":"Re: [PATCH v2 01/13] libipa: Provide a Quantized data type support","submitter":{"id":4,"url":"https://patchwork.libcamera.org/api/people/4/","name":"Kieran Bingham","email":"kieran.bingham@ideasonboard.com"},"content":"Quoting Barnabás Pőcze (2025-10-29 18:45:21)\n> Hi\n> \n> 2025. 10. 29. 18:24 keltezéssel, Kieran Bingham írta:\n> > Frequently when handling data in IPA components we must convert and\n> > store user interface values which may be floating point values, and\n> > perform a specific operation or conversion to quantize this to a\n> > hardware value.\n> > \n> > This value may be to a fixed point type, or more custom code mappings,\n> > but in either case it is important to contain both the required hardware\n> > value, with its effective quantized value.\n> > \n> > Provide a new storage type 'Quantized' which can be defined based on a\n> > set of type specific Traits to perform the conversions between floats\n> > and the underlying hardware type.\n> > \n> > Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>\n> > ---\n> >   src/ipa/libipa/meson.build   |   2 +\n> >   src/ipa/libipa/quantized.cpp | 134 +++++++++++++++++++++++++++++++++++\n> >   src/ipa/libipa/quantized.h   |  79 +++++++++++++++++++++\n> >   3 files changed, 215 insertions(+)\n> >   create mode 100644 src/ipa/libipa/quantized.cpp\n> >   create mode 100644 src/ipa/libipa/quantized.h\n> > \n> > diff --git a/src/ipa/libipa/meson.build b/src/ipa/libipa/meson.build\n> > index 660be94054fa..804289778f72 100644\n> > --- a/src/ipa/libipa/meson.build\n> > +++ b/src/ipa/libipa/meson.build\n> > @@ -17,6 +17,7 @@ libipa_headers = files([\n> >       'lux.h',\n> >       'module.h',\n> >       'pwl.h',\n> > +    'quantized.h',\n> >   ])\n> >   \n> >   libipa_sources = files([\n> > @@ -36,6 +37,7 @@ libipa_sources = files([\n> >       'lux.cpp',\n> >       'module.cpp',\n> >       'pwl.cpp',\n> > +    'quantized.cpp',\n> >   ])\n> >   \n> >   libipa_includes = include_directories('..')\n> > [...]\n> > diff --git a/src/ipa/libipa/quantized.h b/src/ipa/libipa/quantized.h\n> > new file mode 100644\n> > index 000000000000..26cb8d619e15\n> > --- /dev/null\n> > +++ b/src/ipa/libipa/quantized.h\n> > @@ -0,0 +1,79 @@\n> > +/* SPDX-License-Identifier: LGPL-2.1-or-later */\n> > +/*\n> > + * Copyright (C) 2025, Ideas On Board.\n> > + *\n> > + * Helper class to manage conversions between floating point types and quantized\n> > + * storage and representation of those values.\n> > + */\n> > +\n> > +#pragma once\n> > +\n> > +#include <iomanip>\n> > +#include <sstream>\n> > +#include <stdint.h>\n> > +#include <type_traits>\n> > +\n> > +#include <libcamera/base/utils.h>\n> > +\n> > +namespace libcamera {\n> > +\n> > +namespace ipa {\n> > +\n> > +template<typename Traits>\n> > +struct Quantized {\n> > +     using TraitsType = Traits;\n\nTraitsType ends up being quite long when used in later patches too, I've\nwondered if it should be shorter - or perhaps just\n\ntemplate<typename TraitsType>\n\tusing Traits = TraitsType;\n\nso that Traits can be used here and by the later patches.\n\n\n> > +     using quantized_type = typename Traits::quantized_type;\n> \n> Small thing, but I think the member type aliases should both be\n> snake_case or PascalCase, but they shouldn't be mixed.\n\nyes - agreed - sorry - I've gone through so many iterations and moving\nbits around from my test snippets and then back into patches, then not\nkept it consistent. I'll try to tidy that up.\n\nI'm pretty sure I had this as qType as well... not sure what works best\nyet.\n\n\n\n> \n> \n> > +     static_assert(std::is_arithmetic_v<quantized_type>,\n> > +                   \"Quantized: quantized_type must be arithmetic\");\n> > +\n> > +     /* Constructors */\n> > +     Quantized()\n> > +             : Quantized(0.0f) {}\n> > +     Quantized(float x) { *this = x; }\n> > +     Quantized(quantized_type x) { *this = x; }\n> > +\n> > +     Quantized &operator=(float x)\n> > +     {\n> > +             quantized_ = Traits::fromFloat(x);\n> > +             value_ = Traits::toFloat(quantized_);\n> \n> I might have missed the discussion, but I'm wondering if it should save `x` as is?\n\nThere's no specific discusion, but in fact this is a fundamental part of\nthe type.\n\nIt stores the 'quantized' value of any given input - so the float value_\nshould be the representative value of quantized_ (once it has been\nquantized) so that includes all rounding and such.\n\nThere might be use cases to /also/ store the original requested value -\nor perhaps make it easy to know what 'difference' there is between a\nrequested value and what actually gets applied, but that can also be\ndone by the caller/setter directly if required.\n\n\n> \n> \n> > +             return *this;\n> > +     }\n> > +\n> > +     Quantized &operator=(quantized_type x)\n> > +     {\n> > +             value_ = Traits::toFloat(x);\n> > +             quantized_ = x;\n> > +             return *this;\n> > +     }\n> > +\n> > +     float value() const noexcept { return value_; }\n> > +     quantized_type quantized() const noexcept { return quantized_; }\n> > +\n> > +     std::string toString() const\n> > +     {\n> > +             std::ostringstream oss;\n> > +\n> > +             oss << \"Q:\" << utils::hex(quantized_)\n> > +                 << \" V:\" << value_;\n> \n> It's again a minor detail, but I'm wondering about a more compact\n> representation without whitespace in it. Maybe something like\n> \n>    value_ << '[' << utils::hex(quantized_) << ']'\n> \n> or\n> \n>    '[' << value_ << '|' << utils::hex(quantized_) << ']'\n> \n> Maybe it's just me, but I like if the string representation is visibly\n> a \"single unit\" even if there are many things in the same line.\n> \n\nAbsolutely - either of those is probably good too.\n\n I guess that's\n\n   -1.0(0x80)\n\nvs\n   [-1|0x80]\n\nor perhaps | makes that quite hard to read but this looks parsable:\n\n   [-1:0x80]\n\n\n> \n> Regards,\n> Barnabás Pőcze\n> \n> > +\n> > +             return oss.str();\n> > +     }\n> > +\n> > +     bool operator==(const Quantized &other) const noexcept\n> > +     {\n> > +             return quantized_ == other.quantized_;\n> > +     }\n> > +\n> > +     bool operator!=(const Quantized &other) const noexcept\n> > +     {\n> > +             return !(*this == other);\n> > +     }\n> > +\n> > +private:\n> > +     quantized_type quantized_{};\n> > +     float value_{};\n> > +};\n> > +\n> > +} /* namespace ipa */\n> > +\n> > +} /* namespace libcamera */\n>","headers":{"Return-Path":"<libcamera-devel-bounces@lists.libcamera.org>","X-Original-To":"parsemail@patchwork.libcamera.org","Delivered-To":"parsemail@patchwork.libcamera.org","Received":["from lancelot.ideasonboard.com (lancelot.ideasonboard.com\n\t[92.243.16.209])\n\tby patchwork.libcamera.org (Postfix) with ESMTPS id BDB3BC3259\n\tfor <parsemail@patchwork.libcamera.org>;\n\tWed, 29 Oct 2025 21:04:02 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 9971D6086F;\n\tWed, 29 Oct 2025 22:04:01 +0100 (CET)","from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[IPv6:2001:4b98:dc2:55:216:3eff:fef7:d647])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 406DB60856\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tWed, 29 Oct 2025 22:04:00 +0100 (CET)","from pendragon.ideasonboard.com\n\t(cpc89244-aztw30-2-0-cust6594.18-1.cable.virginm.net [86.31.185.195])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id 517DE501C;\n\tWed, 29 Oct 2025 22:02:10 +0100 (CET)"],"Authentication-Results":"lancelot.ideasonboard.com; dkim=pass (1024-bit key;\n\tunprotected) header.d=ideasonboard.com header.i=@ideasonboard.com\n\theader.b=\"YDJ77Qfe\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1761771730;\n\tbh=o80IfwTeUR7yfm3TonagC4sKboz/kTPD8/RsHuvwplk=;\n\th=In-Reply-To:References:Subject:From:To:Date:From;\n\tb=YDJ77QfeWiw5r51RVxo+1feDJPDyzHm28QrU1HQc11sAkOqF4NyFTWEMn4jwnKaoN\n\tevA/pbgR81x2vfFzrxjYrQEgaZh3yn4UdrpkZjAaN5LiwIecpZ4AAOkDefT6Q9wLZs\n\tADj0KqT/HyHjVzRQDKDTfQZwnazIZEXWiZtWWprY=","Content-Type":"text/plain; charset=\"utf-8\"","MIME-Version":"1.0","Content-Transfer-Encoding":"quoted-printable","In-Reply-To":"<7a1e8573-2cd6-4df0-8626-031f18b2ba92@ideasonboard.com>","References":"<20251029172439.1513907-1-kieran.bingham@ideasonboard.com>\n\t<20251029172439.1513907-2-kieran.bingham@ideasonboard.com>\n\t<7a1e8573-2cd6-4df0-8626-031f18b2ba92@ideasonboard.com>","Subject":"Re: [PATCH v2 01/13] libipa: Provide a Quantized data type support","From":"Kieran Bingham <kieran.bingham@ideasonboard.com>","To":"=?utf-8?q?Barnab=C3=A1s_P=C5=91cze?= <barnabas.pocze@ideasonboard.com>,\n\tlibcamera devel <libcamera-devel@lists.libcamera.org>","Date":"Wed, 29 Oct 2025 21:03:56 +0000","Message-ID":"<176177183637.2017750.6120344562634809954@ping.linuxembedded.co.uk>","User-Agent":"alot/0.9.1","X-BeenThere":"libcamera-devel@lists.libcamera.org","X-Mailman-Version":"2.1.29","Precedence":"list","List-Id":"<libcamera-devel.lists.libcamera.org>","List-Unsubscribe":"<https://lists.libcamera.org/options/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=unsubscribe>","List-Archive":"<https://lists.libcamera.org/pipermail/libcamera-devel/>","List-Post":"<mailto:libcamera-devel@lists.libcamera.org>","List-Help":"<mailto:libcamera-devel-request@lists.libcamera.org?subject=help>","List-Subscribe":"<https://lists.libcamera.org/listinfo/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=subscribe>","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"}},{"id":36694,"web_url":"https://patchwork.libcamera.org/comment/36694/","msgid":"<176233274098.231750.7195627093283025546@localhost>","date":"2025-11-05T08:52:20","subject":"Re: [PATCH v2 01/13] libipa: Provide a Quantized data type support","submitter":{"id":184,"url":"https://patchwork.libcamera.org/api/people/184/","name":"Stefan Klug","email":"stefan.klug@ideasonboard.com"},"content":"Hi Kieran,\n\nThank you for the patch.\n\nQuoting Kieran Bingham (2025-10-29 18:24:26)\n> Frequently when handling data in IPA components we must convert and\n> store user interface values which may be floating point values, and\n> perform a specific operation or conversion to quantize this to a\n> hardware value.\n> \n> This value may be to a fixed point type, or more custom code mappings,\n> but in either case it is important to contain both the required hardware\n> value, with its effective quantized value.\n> \n> Provide a new storage type 'Quantized' which can be defined based on a\n> set of type specific Traits to perform the conversions between floats\n> and the underlying hardware type.\n> \n> Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>\n> ---\n>  src/ipa/libipa/meson.build   |   2 +\n>  src/ipa/libipa/quantized.cpp | 134 +++++++++++++++++++++++++++++++++++\n>  src/ipa/libipa/quantized.h   |  79 +++++++++++++++++++++\n>  3 files changed, 215 insertions(+)\n>  create mode 100644 src/ipa/libipa/quantized.cpp\n>  create mode 100644 src/ipa/libipa/quantized.h\n> \n> diff --git a/src/ipa/libipa/meson.build b/src/ipa/libipa/meson.build\n> index 660be94054fa..804289778f72 100644\n> --- a/src/ipa/libipa/meson.build\n> +++ b/src/ipa/libipa/meson.build\n> @@ -17,6 +17,7 @@ libipa_headers = files([\n>      'lux.h',\n>      'module.h',\n>      'pwl.h',\n> +    'quantized.h',\n>  ])\n>  \n>  libipa_sources = files([\n> @@ -36,6 +37,7 @@ libipa_sources = files([\n>      'lux.cpp',\n>      'module.cpp',\n>      'pwl.cpp',\n> +    'quantized.cpp',\n>  ])\n>  \n>  libipa_includes = include_directories('..')\n> diff --git a/src/ipa/libipa/quantized.cpp b/src/ipa/libipa/quantized.cpp\n> new file mode 100644\n> index 000000000000..9772674159bd\n> --- /dev/null\n> +++ b/src/ipa/libipa/quantized.cpp\n> @@ -0,0 +1,134 @@\n> +/* SPDX-License-Identifier: LGPL-2.1-or-later */\n> +/*\n> + * Copyright (C) 2025, Ideas On Board.\n> + *\n> + * Helper class to manage conversions between floating point types and quantized\n> + * storage and representation of those values.\n> + */\n> +\n> +#include \"quantized.h\"\n> +\n> +/**\n> + * \\file quantized.h\n> + * \\brief Quantized storage and Quantizer representations\n> + */\n> +\n> +namespace libcamera {\n> +\n> +namespace ipa {\n> +\n> +/**\n> + * \\struct libcamera::ipa::Quantized\n> + * \\brief Wrapper that stores a value in both quantized and floating-point form\n> + * \\tparam Traits The traits class defining the quantization behaviour\n> + *\n> + * The Quantized struct template provides a thin wrapper around a quantized\n> + * representation of a floating-point value. It uses a traits type \\a Traits\n> + * to define the conversion policy between the floating-point domain and the\n> + * quantized integer domain.\n> + *\n> + * Each Quantized instance maintains two synchronized members:\n> + *  - the quantized integer representation, and\n> + *  - the corresponding floating-point value.\n> + *\n> + * The traits type defines:\n> + *  - the integer storage type used for quantization,\n> + *  - the static conversion functions \\c fromFloat() and \\c toFloat(), and\n> + *  - optional metadata such as value ranges.\n> + *\n> + * Quantized provides convenient constructors and assignment operators from\n> + * either representation, as well as comparison and string formatting utilities.\n> + */\n> +\n> +/**\n> + * \\typedef Quantized::TraitsType\n> + * \\brief The traits policy type defining the quantization behaviour\n> + *\n> + * Exposes the associated traits type used by this Quantized instance.\n> + * This allows external code to refer to constants or metadata defined in\n> + * the traits, such as \\c TraitsType::min or \\c TraitsType::max.\n> + */\n> +\n> +/**\n> + * \\typedef Quantized::quantized_type\n> + * \\brief The integer type used for the quantized representation\n> + *\n> + * This alias corresponds to \\c TraitsType::quantized_type, as defined by\n> + * the traits class.\n> + */\n> +\n> +/**\n> + * \\fn Quantized::Quantized(float x)\n> + * \\brief Construct a Quantized value from a floating-point number\n> + * \\param[in] x The floating-point value to be quantized\n> + *\n> + * Converts the floating-point input \\a x to its quantized integer\n> + * representation using the associated traits policy, and initializes\n> + * both the quantized and floating-point members.\n> + */\n> +\n> +/**\n> + * \\fn Quantized::Quantized(quantized_type x)\n> + * \\brief Construct a Quantized value from an existing quantized integer\n> + * \\param[in] x The quantized integer value\n> + *\n> + * Converts the quantized integer \\a x to its corresponding floating-point\n> + * value using the traits policy, and initializes both internal members.\n> + */\n> +\n> +/**\n> + * \\fn Quantized::operator=(float x)\n> + * \\brief Assign a floating-point value to the Quantized object\n> + * \\param[in] x The floating-point value to assign\n> + * \\return A reference to the updated Quantized object\n> + *\n> + * Converts the floating-point value \\a x to its quantized integer\n> + * representation using the traits policy and updates both members.\n> + */\n> +\n> +/**\n> + * \\fn Quantized::operator=(quantized_type x)\n> + * \\brief Assign a quantized integer value to the Quantized object\n> + * \\param[in] x The quantized integer value to assign\n> + * \\return A reference to the updated Quantized object\n> + *\n> + * Converts the quantized integer \\a x to its corresponding floating-point\n> + * value using the traits policy and updates both members.\n> + */\n> +\n> +/**\n> + * \\fn Quantized::value() const noexcept\n> + * \\brief Retrieve the floating-point representation\n> + * \\return The floating-point value corresponding to the quantized value\n> + */\n> +\n> +/**\n> + * \\fn Quantized::quantized() const noexcept\n> + * \\brief Retrieve the quantized integer representation\n> + * \\return The quantized integer value\n> + */\n> +\n> +/**\n> + * \\fn Quantized::toString() const\n> + * \\brief Format the quantized and floating-point values as a string\n> + * \\return A string containing the hexadecimal quantized value and its\n> + *         floating-point equivalent.\n> + */\n> +\n> +/**\n> + * \\fn Quantized::operator==(const Quantized &other) const noexcept\n> + * \\brief Compare two Quantized objects for equality\n> + * \\param[in] other The other Quantized object to compare against\n> + * \\return True if both objects have the same quantized integer value\n> + */\n> +\n> +/**\n> + * \\fn Quantized::operator!=(const Quantized &other) const noexcept\n> + * \\brief Compare two Quantized objects for inequality\n> + * \\param[in] other The other Quantized object to compare against\n> + * \\return True if the quantized integer values differ\n> + */\n> +\n> +} /* namespace ipa */\n> +\n> +} /* namespace libcamera */\n> diff --git a/src/ipa/libipa/quantized.h b/src/ipa/libipa/quantized.h\n> new file mode 100644\n> index 000000000000..26cb8d619e15\n> --- /dev/null\n> +++ b/src/ipa/libipa/quantized.h\n> @@ -0,0 +1,79 @@\n> +/* SPDX-License-Identifier: LGPL-2.1-or-later */\n> +/*\n> + * Copyright (C) 2025, Ideas On Board.\n\nmicro nit: No dot at the end.\n\n> + *\n> + * Helper class to manage conversions between floating point types and quantized\n> + * storage and representation of those values.\n> + */\n> +\n> +#pragma once\n> +\n> +#include <iomanip>\n> +#include <sstream>\n> +#include <stdint.h>\n> +#include <type_traits>\n> +\n> +#include <libcamera/base/utils.h>\n> +\n> +namespace libcamera {\n> +\n> +namespace ipa {\n> +\n> +template<typename Traits>\n> +struct Quantized {\n> +       using TraitsType = Traits;\n> +       using quantized_type = typename Traits::quantized_type;\n> +       static_assert(std::is_arithmetic_v<quantized_type>,\n> +                     \"Quantized: quantized_type must be arithmetic\");\n> +\n> +       /* Constructors */\n\nI think this comment can be dropped.\n\n> +       Quantized()\n> +               : Quantized(0.0f) {}\n> +       Quantized(float x) { *this = x; }\n> +       Quantized(quantized_type x) { *this = x; }\n> +\n> +       Quantized &operator=(float x)\n> +       {\n> +               quantized_ = Traits::fromFloat(x);\n> +               value_ = Traits::toFloat(quantized_);\n> +               return *this;\n> +       }\n> +\n> +       Quantized &operator=(quantized_type x)\n> +       {\n> +               value_ = Traits::toFloat(x);\n> +               quantized_ = x;\n> +               return *this;\n> +       }\n> +\n> +       float value() const noexcept { return value_; }\n> +       quantized_type quantized() const noexcept { return quantized_; }\n> +\n> +       std::string toString() const\n> +       {\n> +               std::ostringstream oss;\n> +\n> +               oss << \"Q:\" << utils::hex(quantized_)\n> +                   << \" V:\" << value_;\n> +\n> +               return oss.str();\n> +       }\n> +\n> +       bool operator==(const Quantized &other) const noexcept\n> +       {\n> +               return quantized_ == other.quantized_;\n> +       }\n> +\n> +       bool operator!=(const Quantized &other) const noexcept\n> +       {\n> +               return !(*this == other);\n> +       }\n> +\n> +private:\n> +       quantized_type quantized_{};\n> +       float value_{};\n\nOut of curiosity: Why are the initializers needed here?\n\nAll only nits, so\nReviewed-by: Stefan Klug <stefan.klug@ideasonboard.com> \n\nBest regards,\nStefan\n\n> +};\n> +\n> +} /* namespace ipa */\n> +\n> +} /* namespace libcamera */\n> -- \n> 2.50.1\n>","headers":{"Return-Path":"<libcamera-devel-bounces@lists.libcamera.org>","X-Original-To":"parsemail@patchwork.libcamera.org","Delivered-To":"parsemail@patchwork.libcamera.org","Received":["from lancelot.ideasonboard.com (lancelot.ideasonboard.com\n\t[92.243.16.209])\n\tby patchwork.libcamera.org (Postfix) with ESMTPS id AF1A3C3241\n\tfor <parsemail@patchwork.libcamera.org>;\n\tWed,  5 Nov 2025 08:52:27 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 5856460A80;\n\tWed,  5 Nov 2025 09:52:26 +0100 (CET)","from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[IPv6:2001:4b98:dc2:55:216:3eff:fef7:d647])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 754EC608CF\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tWed,  5 Nov 2025 09:52:24 +0100 (CET)","from ideasonboard.com (unknown\n\t[IPv6:2a00:6020:448c:6c00:3793:9dc2:3dec:ebc3])\n\tby perceval.ideasonboard.com (Postfix) with UTF8SMTPSA id CDBF6664;\n\tWed,  5 Nov 2025 09:50:29 +0100 (CET)"],"Authentication-Results":"lancelot.ideasonboard.com; dkim=pass (1024-bit key;\n\tunprotected) header.d=ideasonboard.com header.i=@ideasonboard.com\n\theader.b=\"Dx1OIe3m\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1762332629;\n\tbh=AtywIIXcfrQQdOwva7fGrbOkmeWFTAHQ0PwTLUMXD+g=;\n\th=In-Reply-To:References:Subject:From:Cc:To:Date:From;\n\tb=Dx1OIe3mEPrNrTMM7ywOCInHbpeIs3eIASWxVuuw00LVjV/DahL9cKYYFubYaeD6K\n\tt5zTlwR5dwZB8mI73Nusp6tUuHwt2sTMdkS9UMkFusRS+17aHoltsg4Amd2JLDoqFW\n\th3Eeu+S72NxBqkAQlIOYNRlPt6YM2HZ0aldH2rco=","Content-Type":"text/plain; charset=\"utf-8\"","MIME-Version":"1.0","Content-Transfer-Encoding":"quoted-printable","In-Reply-To":"<20251029172439.1513907-2-kieran.bingham@ideasonboard.com>","References":"<20251029172439.1513907-1-kieran.bingham@ideasonboard.com>\n\t<20251029172439.1513907-2-kieran.bingham@ideasonboard.com>","Subject":"Re: [PATCH v2 01/13] libipa: Provide a Quantized data type support","From":"Stefan Klug <stefan.klug@ideasonboard.com>","Cc":"Kieran Bingham <kieran.bingham@ideasonboard.com>","To":"Kieran Bingham <kieran.bingham@ideasonboard.com>,\n\tlibcamera devel <libcamera-devel@lists.libcamera.org>","Date":"Wed, 05 Nov 2025 09:52:20 +0100","Message-ID":"<176233274098.231750.7195627093283025546@localhost>","User-Agent":"alot/0.12.dev8+g2c003385c862.d20250602","X-BeenThere":"libcamera-devel@lists.libcamera.org","X-Mailman-Version":"2.1.29","Precedence":"list","List-Id":"<libcamera-devel.lists.libcamera.org>","List-Unsubscribe":"<https://lists.libcamera.org/options/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=unsubscribe>","List-Archive":"<https://lists.libcamera.org/pipermail/libcamera-devel/>","List-Post":"<mailto:libcamera-devel@lists.libcamera.org>","List-Help":"<mailto:libcamera-devel-request@lists.libcamera.org?subject=help>","List-Subscribe":"<https://lists.libcamera.org/listinfo/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=subscribe>","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"}},{"id":36699,"web_url":"https://patchwork.libcamera.org/comment/36699/","msgid":"<176233425195.3742839.7411487210019412435@ping.linuxembedded.co.uk>","date":"2025-11-05T09:17:31","subject":"Re: [PATCH v2 01/13] libipa: Provide a Quantized data type support","submitter":{"id":4,"url":"https://patchwork.libcamera.org/api/people/4/","name":"Kieran Bingham","email":"kieran.bingham@ideasonboard.com"},"content":"Quoting Stefan Klug (2025-11-05 08:52:20)\n> Hi Kieran,\n> \n> Thank you for the patch.\n> \n> Quoting Kieran Bingham (2025-10-29 18:24:26)\n> > Frequently when handling data in IPA components we must convert and\n> > store user interface values which may be floating point values, and\n> > perform a specific operation or conversion to quantize this to a\n> > hardware value.\n> > \n> > This value may be to a fixed point type, or more custom code mappings,\n> > but in either case it is important to contain both the required hardware\n> > value, with its effective quantized value.\n> > \n> > Provide a new storage type 'Quantized' which can be defined based on a\n> > set of type specific Traits to perform the conversions between floats\n> > and the underlying hardware type.\n> > \n> > Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>\n> > ---\n> >  src/ipa/libipa/meson.build   |   2 +\n> >  src/ipa/libipa/quantized.cpp | 134 +++++++++++++++++++++++++++++++++++\n> >  src/ipa/libipa/quantized.h   |  79 +++++++++++++++++++++\n> >  3 files changed, 215 insertions(+)\n> >  create mode 100644 src/ipa/libipa/quantized.cpp\n> >  create mode 100644 src/ipa/libipa/quantized.h\n> > \n> > diff --git a/src/ipa/libipa/meson.build b/src/ipa/libipa/meson.build\n> > index 660be94054fa..804289778f72 100644\n> > --- a/src/ipa/libipa/meson.build\n> > +++ b/src/ipa/libipa/meson.build\n> > @@ -17,6 +17,7 @@ libipa_headers = files([\n> >      'lux.h',\n> >      'module.h',\n> >      'pwl.h',\n> > +    'quantized.h',\n> >  ])\n> >  \n> >  libipa_sources = files([\n> > @@ -36,6 +37,7 @@ libipa_sources = files([\n> >      'lux.cpp',\n> >      'module.cpp',\n> >      'pwl.cpp',\n> > +    'quantized.cpp',\n> >  ])\n> >  \n> >  libipa_includes = include_directories('..')\n> > diff --git a/src/ipa/libipa/quantized.cpp b/src/ipa/libipa/quantized.cpp\n> > new file mode 100644\n> > index 000000000000..9772674159bd\n> > --- /dev/null\n> > +++ b/src/ipa/libipa/quantized.cpp\n> > @@ -0,0 +1,134 @@\n> > +/* SPDX-License-Identifier: LGPL-2.1-or-later */\n> > +/*\n> > + * Copyright (C) 2025, Ideas On Board.\n> > + *\n> > + * Helper class to manage conversions between floating point types and quantized\n> > + * storage and representation of those values.\n> > + */\n> > +\n> > +#include \"quantized.h\"\n> > +\n> > +/**\n> > + * \\file quantized.h\n> > + * \\brief Quantized storage and Quantizer representations\n> > + */\n> > +\n> > +namespace libcamera {\n> > +\n> > +namespace ipa {\n> > +\n> > +/**\n> > + * \\struct libcamera::ipa::Quantized\n> > + * \\brief Wrapper that stores a value in both quantized and floating-point form\n> > + * \\tparam Traits The traits class defining the quantization behaviour\n> > + *\n> > + * The Quantized struct template provides a thin wrapper around a quantized\n> > + * representation of a floating-point value. It uses a traits type \\a Traits\n> > + * to define the conversion policy between the floating-point domain and the\n> > + * quantized integer domain.\n> > + *\n> > + * Each Quantized instance maintains two synchronized members:\n> > + *  - the quantized integer representation, and\n> > + *  - the corresponding floating-point value.\n> > + *\n> > + * The traits type defines:\n> > + *  - the integer storage type used for quantization,\n> > + *  - the static conversion functions \\c fromFloat() and \\c toFloat(), and\n> > + *  - optional metadata such as value ranges.\n> > + *\n> > + * Quantized provides convenient constructors and assignment operators from\n> > + * either representation, as well as comparison and string formatting utilities.\n> > + */\n> > +\n> > +/**\n> > + * \\typedef Quantized::TraitsType\n> > + * \\brief The traits policy type defining the quantization behaviour\n> > + *\n> > + * Exposes the associated traits type used by this Quantized instance.\n> > + * This allows external code to refer to constants or metadata defined in\n> > + * the traits, such as \\c TraitsType::min or \\c TraitsType::max.\n> > + */\n> > +\n> > +/**\n> > + * \\typedef Quantized::quantized_type\n> > + * \\brief The integer type used for the quantized representation\n> > + *\n> > + * This alias corresponds to \\c TraitsType::quantized_type, as defined by\n> > + * the traits class.\n> > + */\n> > +\n> > +/**\n> > + * \\fn Quantized::Quantized(float x)\n> > + * \\brief Construct a Quantized value from a floating-point number\n> > + * \\param[in] x The floating-point value to be quantized\n> > + *\n> > + * Converts the floating-point input \\a x to its quantized integer\n> > + * representation using the associated traits policy, and initializes\n> > + * both the quantized and floating-point members.\n> > + */\n> > +\n> > +/**\n> > + * \\fn Quantized::Quantized(quantized_type x)\n> > + * \\brief Construct a Quantized value from an existing quantized integer\n> > + * \\param[in] x The quantized integer value\n> > + *\n> > + * Converts the quantized integer \\a x to its corresponding floating-point\n> > + * value using the traits policy, and initializes both internal members.\n> > + */\n> > +\n> > +/**\n> > + * \\fn Quantized::operator=(float x)\n> > + * \\brief Assign a floating-point value to the Quantized object\n> > + * \\param[in] x The floating-point value to assign\n> > + * \\return A reference to the updated Quantized object\n> > + *\n> > + * Converts the floating-point value \\a x to its quantized integer\n> > + * representation using the traits policy and updates both members.\n> > + */\n> > +\n> > +/**\n> > + * \\fn Quantized::operator=(quantized_type x)\n> > + * \\brief Assign a quantized integer value to the Quantized object\n> > + * \\param[in] x The quantized integer value to assign\n> > + * \\return A reference to the updated Quantized object\n> > + *\n> > + * Converts the quantized integer \\a x to its corresponding floating-point\n> > + * value using the traits policy and updates both members.\n> > + */\n> > +\n> > +/**\n> > + * \\fn Quantized::value() const noexcept\n> > + * \\brief Retrieve the floating-point representation\n> > + * \\return The floating-point value corresponding to the quantized value\n> > + */\n> > +\n> > +/**\n> > + * \\fn Quantized::quantized() const noexcept\n> > + * \\brief Retrieve the quantized integer representation\n> > + * \\return The quantized integer value\n> > + */\n> > +\n> > +/**\n> > + * \\fn Quantized::toString() const\n> > + * \\brief Format the quantized and floating-point values as a string\n> > + * \\return A string containing the hexadecimal quantized value and its\n> > + *         floating-point equivalent.\n> > + */\n> > +\n> > +/**\n> > + * \\fn Quantized::operator==(const Quantized &other) const noexcept\n> > + * \\brief Compare two Quantized objects for equality\n> > + * \\param[in] other The other Quantized object to compare against\n> > + * \\return True if both objects have the same quantized integer value\n> > + */\n> > +\n> > +/**\n> > + * \\fn Quantized::operator!=(const Quantized &other) const noexcept\n> > + * \\brief Compare two Quantized objects for inequality\n> > + * \\param[in] other The other Quantized object to compare against\n> > + * \\return True if the quantized integer values differ\n> > + */\n> > +\n> > +} /* namespace ipa */\n> > +\n> > +} /* namespace libcamera */\n> > diff --git a/src/ipa/libipa/quantized.h b/src/ipa/libipa/quantized.h\n> > new file mode 100644\n> > index 000000000000..26cb8d619e15\n> > --- /dev/null\n> > +++ b/src/ipa/libipa/quantized.h\n> > @@ -0,0 +1,79 @@\n> > +/* SPDX-License-Identifier: LGPL-2.1-or-later */\n> > +/*\n> > + * Copyright (C) 2025, Ideas On Board.\n> \n> micro nit: No dot at the end.\n> \n> > + *\n> > + * Helper class to manage conversions between floating point types and quantized\n> > + * storage and representation of those values.\n> > + */\n> > +\n> > +#pragma once\n> > +\n> > +#include <iomanip>\n> > +#include <sstream>\n> > +#include <stdint.h>\n> > +#include <type_traits>\n> > +\n> > +#include <libcamera/base/utils.h>\n> > +\n> > +namespace libcamera {\n> > +\n> > +namespace ipa {\n> > +\n> > +template<typename Traits>\n> > +struct Quantized {\n> > +       using TraitsType = Traits;\n> > +       using quantized_type = typename Traits::quantized_type;\n> > +       static_assert(std::is_arithmetic_v<quantized_type>,\n> > +                     \"Quantized: quantized_type must be arithmetic\");\n> > +\n> > +       /* Constructors */\n> \n> I think this comment can be dropped.\n> \n> > +       Quantized()\n> > +               : Quantized(0.0f) {}\n> > +       Quantized(float x) { *this = x; }\n> > +       Quantized(quantized_type x) { *this = x; }\n> > +\n> > +       Quantized &operator=(float x)\n> > +       {\n> > +               quantized_ = Traits::fromFloat(x);\n> > +               value_ = Traits::toFloat(quantized_);\n> > +               return *this;\n> > +       }\n> > +\n> > +       Quantized &operator=(quantized_type x)\n> > +       {\n> > +               value_ = Traits::toFloat(x);\n> > +               quantized_ = x;\n> > +               return *this;\n> > +       }\n> > +\n> > +       float value() const noexcept { return value_; }\n> > +       quantized_type quantized() const noexcept { return quantized_; }\n> > +\n> > +       std::string toString() const\n> > +       {\n> > +               std::ostringstream oss;\n> > +\n> > +               oss << \"Q:\" << utils::hex(quantized_)\n> > +                   << \" V:\" << value_;\n> > +\n> > +               return oss.str();\n> > +       }\n> > +\n> > +       bool operator==(const Quantized &other) const noexcept\n> > +       {\n> > +               return quantized_ == other.quantized_;\n> > +       }\n> > +\n> > +       bool operator!=(const Quantized &other) const noexcept\n> > +       {\n> > +               return !(*this == other);\n> > +       }\n> > +\n> > +private:\n> > +       quantized_type quantized_{};\n> > +       float value_{};\n> \n> Out of curiosity: Why are the initializers needed here?\n\nI think that's a remnant of before I had a default 0 constructor\n       Quantized()\n             : Quantized(0.0f) {}\n\nwhich will now make sure these are initialised to a Quantized 0 instead\nof just {}. (And technically depending on the quantizer traits, the\nquantized for 0.0f could be non-zero)\n\nSo yes, I think I can remove the {} initializers now.\n\n\n> \n> All only nits, so\n> Reviewed-by: Stefan Klug <stefan.klug@ideasonboard.com> \n\nThanks.\n\nKieran\n\n> \n> Best regards,\n> Stefan\n> \n> > +};\n> > +\n> > +} /* namespace ipa */\n> > +\n> > +} /* namespace libcamera */\n> > -- \n> > 2.50.1\n> >","headers":{"Return-Path":"<libcamera-devel-bounces@lists.libcamera.org>","X-Original-To":"parsemail@patchwork.libcamera.org","Delivered-To":"parsemail@patchwork.libcamera.org","Received":["from lancelot.ideasonboard.com (lancelot.ideasonboard.com\n\t[92.243.16.209])\n\tby patchwork.libcamera.org (Postfix) with ESMTPS id 15223BDE4C\n\tfor <parsemail@patchwork.libcamera.org>;\n\tWed,  5 Nov 2025 09:17:37 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 232F760A80;\n\tWed,  5 Nov 2025 10:17:36 +0100 (CET)","from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[213.167.242.64])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 19F1A608CF\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tWed,  5 Nov 2025 10:17:35 +0100 (CET)","from pendragon.ideasonboard.com\n\t(cpc89244-aztw30-2-0-cust6594.18-1.cable.virginm.net [86.31.185.195])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id CB6557E0;\n\tWed,  5 Nov 2025 10:15:40 +0100 (CET)"],"Authentication-Results":"lancelot.ideasonboard.com; dkim=pass (1024-bit key;\n\tunprotected) header.d=ideasonboard.com header.i=@ideasonboard.com\n\theader.b=\"CbdcboRl\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1762334140;\n\tbh=FybXXQoqdKdAHr3GXpFXYUKMkJHQ2Ti9d6v/B8kKXeM=;\n\th=In-Reply-To:References:Subject:From:Cc:To:Date:From;\n\tb=CbdcboRl6SzuVwhD/fl3Gl4u2tTxLr2D2Moop2EnpXBb2ITcjJW8OGaMe/ecpOx2P\n\tr+57F3hPXHOg7jQhJWRY14SLGYZ6hLaB4Wb7/GJlX6PJR+rRotKAn/rHgUr2gGzBCp\n\tNFg1b7cbQ3/FY0yVmEifubIDWx0kHwZfc0fgdo98=","Content-Type":"text/plain; charset=\"utf-8\"","MIME-Version":"1.0","Content-Transfer-Encoding":"quoted-printable","In-Reply-To":"<176233274098.231750.7195627093283025546@localhost>","References":"<20251029172439.1513907-1-kieran.bingham@ideasonboard.com>\n\t<20251029172439.1513907-2-kieran.bingham@ideasonboard.com>\n\t<176233274098.231750.7195627093283025546@localhost>","Subject":"Re: [PATCH v2 01/13] libipa: Provide a Quantized data type support","From":"Kieran Bingham <kieran.bingham@ideasonboard.com>","Cc":"","To":"Stefan Klug <stefan.klug@ideasonboard.com>,\n\tlibcamera devel <libcamera-devel@lists.libcamera.org>","Date":"Wed, 05 Nov 2025 09:17:31 +0000","Message-ID":"<176233425195.3742839.7411487210019412435@ping.linuxembedded.co.uk>","User-Agent":"alot/0.9.1","X-BeenThere":"libcamera-devel@lists.libcamera.org","X-Mailman-Version":"2.1.29","Precedence":"list","List-Id":"<libcamera-devel.lists.libcamera.org>","List-Unsubscribe":"<https://lists.libcamera.org/options/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=unsubscribe>","List-Archive":"<https://lists.libcamera.org/pipermail/libcamera-devel/>","List-Post":"<mailto:libcamera-devel@lists.libcamera.org>","List-Help":"<mailto:libcamera-devel-request@lists.libcamera.org?subject=help>","List-Subscribe":"<https://lists.libcamera.org/listinfo/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=subscribe>","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"}},{"id":36700,"web_url":"https://patchwork.libcamera.org/comment/36700/","msgid":"<176233484083.260669.17716354166087594954@localhost>","date":"2025-11-05T09:27:20","subject":"Re: [PATCH v2 01/13] libipa: Provide a Quantized data type support","submitter":{"id":184,"url":"https://patchwork.libcamera.org/api/people/184/","name":"Stefan Klug","email":"stefan.klug@ideasonboard.com"},"content":"Hi Kieran,\n\nmaybe I was a bit fast on this one...\n\nQuoting Stefan Klug (2025-11-05 09:52:20)\n> Hi Kieran,\n> \n> Thank you for the patch.\n> \n> Quoting Kieran Bingham (2025-10-29 18:24:26)\n> > Frequently when handling data in IPA components we must convert and\n> > store user interface values which may be floating point values, and\n> > perform a specific operation or conversion to quantize this to a\n> > hardware value.\n> > \n> > This value may be to a fixed point type, or more custom code mappings,\n> > but in either case it is important to contain both the required hardware\n> > value, with its effective quantized value.\n> > \n> > Provide a new storage type 'Quantized' which can be defined based on a\n> > set of type specific Traits to perform the conversions between floats\n> > and the underlying hardware type.\n> > \n> > Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>\n> > ---\n> >  src/ipa/libipa/meson.build   |   2 +\n> >  src/ipa/libipa/quantized.cpp | 134 +++++++++++++++++++++++++++++++++++\n> >  src/ipa/libipa/quantized.h   |  79 +++++++++++++++++++++\n> >  3 files changed, 215 insertions(+)\n> >  create mode 100644 src/ipa/libipa/quantized.cpp\n> >  create mode 100644 src/ipa/libipa/quantized.h\n> > \n> > diff --git a/src/ipa/libipa/meson.build b/src/ipa/libipa/meson.build\n> > index 660be94054fa..804289778f72 100644\n> > --- a/src/ipa/libipa/meson.build\n> > +++ b/src/ipa/libipa/meson.build\n> > @@ -17,6 +17,7 @@ libipa_headers = files([\n> >      'lux.h',\n> >      'module.h',\n> >      'pwl.h',\n> > +    'quantized.h',\n> >  ])\n> >  \n> >  libipa_sources = files([\n> > @@ -36,6 +37,7 @@ libipa_sources = files([\n> >      'lux.cpp',\n> >      'module.cpp',\n> >      'pwl.cpp',\n> > +    'quantized.cpp',\n> >  ])\n> >  \n> >  libipa_includes = include_directories('..')\n> > diff --git a/src/ipa/libipa/quantized.cpp b/src/ipa/libipa/quantized.cpp\n> > new file mode 100644\n> > index 000000000000..9772674159bd\n> > --- /dev/null\n> > +++ b/src/ipa/libipa/quantized.cpp\n> > @@ -0,0 +1,134 @@\n> > +/* SPDX-License-Identifier: LGPL-2.1-or-later */\n> > +/*\n> > + * Copyright (C) 2025, Ideas On Board.\n> > + *\n> > + * Helper class to manage conversions between floating point types and quantized\n> > + * storage and representation of those values.\n> > + */\n> > +\n> > +#include \"quantized.h\"\n> > +\n> > +/**\n> > + * \\file quantized.h\n> > + * \\brief Quantized storage and Quantizer representations\n> > + */\n> > +\n> > +namespace libcamera {\n> > +\n> > +namespace ipa {\n> > +\n> > +/**\n> > + * \\struct libcamera::ipa::Quantized\n> > + * \\brief Wrapper that stores a value in both quantized and floating-point form\n> > + * \\tparam Traits The traits class defining the quantization behaviour\n> > + *\n> > + * The Quantized struct template provides a thin wrapper around a quantized\n> > + * representation of a floating-point value. It uses a traits type \\a Traits\n> > + * to define the conversion policy between the floating-point domain and the\n> > + * quantized integer domain.\n> > + *\n> > + * Each Quantized instance maintains two synchronized members:\n> > + *  - the quantized integer representation, and\n> > + *  - the corresponding floating-point value.\n> > + *\n> > + * The traits type defines:\n> > + *  - the integer storage type used for quantization,\n> > + *  - the static conversion functions \\c fromFloat() and \\c toFloat(), and\n> > + *  - optional metadata such as value ranges.\n> > + *\n> > + * Quantized provides convenient constructors and assignment operators from\n> > + * either representation, as well as comparison and string formatting utilities.\n> > + */\n> > +\n> > +/**\n> > + * \\typedef Quantized::TraitsType\n> > + * \\brief The traits policy type defining the quantization behaviour\n> > + *\n> > + * Exposes the associated traits type used by this Quantized instance.\n> > + * This allows external code to refer to constants or metadata defined in\n> > + * the traits, such as \\c TraitsType::min or \\c TraitsType::max.\n> > + */\n> > +\n> > +/**\n> > + * \\typedef Quantized::quantized_type\n> > + * \\brief The integer type used for the quantized representation\n> > + *\n> > + * This alias corresponds to \\c TraitsType::quantized_type, as defined by\n> > + * the traits class.\n> > + */\n> > +\n> > +/**\n> > + * \\fn Quantized::Quantized(float x)\n> > + * \\brief Construct a Quantized value from a floating-point number\n> > + * \\param[in] x The floating-point value to be quantized\n> > + *\n> > + * Converts the floating-point input \\a x to its quantized integer\n> > + * representation using the associated traits policy, and initializes\n> > + * both the quantized and floating-point members.\n> > + */\n> > +\n> > +/**\n> > + * \\fn Quantized::Quantized(quantized_type x)\n> > + * \\brief Construct a Quantized value from an existing quantized integer\n> > + * \\param[in] x The quantized integer value\n> > + *\n> > + * Converts the quantized integer \\a x to its corresponding floating-point\n> > + * value using the traits policy, and initializes both internal members.\n> > + */\n> > +\n> > +/**\n> > + * \\fn Quantized::operator=(float x)\n> > + * \\brief Assign a floating-point value to the Quantized object\n> > + * \\param[in] x The floating-point value to assign\n> > + * \\return A reference to the updated Quantized object\n> > + *\n> > + * Converts the floating-point value \\a x to its quantized integer\n> > + * representation using the traits policy and updates both members.\n> > + */\n> > +\n> > +/**\n> > + * \\fn Quantized::operator=(quantized_type x)\n> > + * \\brief Assign a quantized integer value to the Quantized object\n> > + * \\param[in] x The quantized integer value to assign\n> > + * \\return A reference to the updated Quantized object\n> > + *\n> > + * Converts the quantized integer \\a x to its corresponding floating-point\n> > + * value using the traits policy and updates both members.\n> > + */\n> > +\n> > +/**\n> > + * \\fn Quantized::value() const noexcept\n> > + * \\brief Retrieve the floating-point representation\n> > + * \\return The floating-point value corresponding to the quantized value\n> > + */\n> > +\n> > +/**\n> > + * \\fn Quantized::quantized() const noexcept\n> > + * \\brief Retrieve the quantized integer representation\n> > + * \\return The quantized integer value\n> > + */\n> > +\n> > +/**\n> > + * \\fn Quantized::toString() const\n> > + * \\brief Format the quantized and floating-point values as a string\n> > + * \\return A string containing the hexadecimal quantized value and its\n> > + *         floating-point equivalent.\n> > + */\n> > +\n> > +/**\n> > + * \\fn Quantized::operator==(const Quantized &other) const noexcept\n> > + * \\brief Compare two Quantized objects for equality\n> > + * \\param[in] other The other Quantized object to compare against\n> > + * \\return True if both objects have the same quantized integer value\n> > + */\n> > +\n> > +/**\n> > + * \\fn Quantized::operator!=(const Quantized &other) const noexcept\n> > + * \\brief Compare two Quantized objects for inequality\n> > + * \\param[in] other The other Quantized object to compare against\n> > + * \\return True if the quantized integer values differ\n> > + */\n> > +\n> > +} /* namespace ipa */\n> > +\n> > +} /* namespace libcamera */\n> > diff --git a/src/ipa/libipa/quantized.h b/src/ipa/libipa/quantized.h\n> > new file mode 100644\n> > index 000000000000..26cb8d619e15\n> > --- /dev/null\n> > +++ b/src/ipa/libipa/quantized.h\n> > @@ -0,0 +1,79 @@\n> > +/* SPDX-License-Identifier: LGPL-2.1-or-later */\n> > +/*\n> > + * Copyright (C) 2025, Ideas On Board.\n> \n> micro nit: No dot at the end.\n> \n> > + *\n> > + * Helper class to manage conversions between floating point types and quantized\n> > + * storage and representation of those values.\n> > + */\n> > +\n> > +#pragma once\n> > +\n> > +#include <iomanip>\n> > +#include <sstream>\n> > +#include <stdint.h>\n> > +#include <type_traits>\n> > +\n> > +#include <libcamera/base/utils.h>\n> > +\n> > +namespace libcamera {\n> > +\n> > +namespace ipa {\n> > +\n> > +template<typename Traits>\n> > +struct Quantized {\n\nFollowing the series further down I realized that I'm missing something\nhere. The Traits type encapsulates the conversion from float to int. But\nfor the sensor gain use case we don't know that conversion when we\ndeclare the gain in the activeState/frameContext as the conversion is\nsolely done at runtime by the camera sensor helper. How is this supposed\nto work?\n\nI know you had that idea of \"a quantized value can only be read\". I\ndon't see this anymore - what did I miss?\n\nBest regards,\nStefan\n\n> > +       using TraitsType = Traits;\n> > +       using quantized_type = typename Traits::quantized_type;\n> > +       static_assert(std::is_arithmetic_v<quantized_type>,\n> > +                     \"Quantized: quantized_type must be arithmetic\");\n> > +\n> > +       /* Constructors */\n> \n> I think this comment can be dropped.\n> \n> > +       Quantized()\n> > +               : Quantized(0.0f) {}\n> > +       Quantized(float x) { *this = x; }\n> > +       Quantized(quantized_type x) { *this = x; }\n> > +\n> > +       Quantized &operator=(float x)\n> > +       {\n> > +               quantized_ = Traits::fromFloat(x);\n> > +               value_ = Traits::toFloat(quantized_);\n> > +               return *this;\n> > +       }\n> > +\n> > +       Quantized &operator=(quantized_type x)\n> > +       {\n> > +               value_ = Traits::toFloat(x);\n> > +               quantized_ = x;\n> > +               return *this;\n> > +       }\n> > +\n> > +       float value() const noexcept { return value_; }\n> > +       quantized_type quantized() const noexcept { return quantized_; }\n> > +\n> > +       std::string toString() const\n> > +       {\n> > +               std::ostringstream oss;\n> > +\n> > +               oss << \"Q:\" << utils::hex(quantized_)\n> > +                   << \" V:\" << value_;\n> > +\n> > +               return oss.str();\n> > +       }\n> > +\n> > +       bool operator==(const Quantized &other) const noexcept\n> > +       {\n> > +               return quantized_ == other.quantized_;\n> > +       }\n> > +\n> > +       bool operator!=(const Quantized &other) const noexcept\n> > +       {\n> > +               return !(*this == other);\n> > +       }\n> > +\n> > +private:\n> > +       quantized_type quantized_{};\n> > +       float value_{};\n> \n> Out of curiosity: Why are the initializers needed here?\n> \n> All only nits, so\n> Reviewed-by: Stefan Klug <stefan.klug@ideasonboard.com> \n> \n> Best regards,\n> Stefan\n> \n> > +};\n> > +\n> > +} /* namespace ipa */\n> > +\n> > +} /* namespace libcamera */\n> > -- \n> > 2.50.1\n> >","headers":{"Return-Path":"<libcamera-devel-bounces@lists.libcamera.org>","X-Original-To":"parsemail@patchwork.libcamera.org","Delivered-To":"parsemail@patchwork.libcamera.org","Received":["from lancelot.ideasonboard.com (lancelot.ideasonboard.com\n\t[92.243.16.209])\n\tby patchwork.libcamera.org (Postfix) with ESMTPS id 007AAC3241\n\tfor <parsemail@patchwork.libcamera.org>;\n\tWed,  5 Nov 2025 09:27:27 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 9D5E8609D8;\n\tWed,  5 Nov 2025 10:27:26 +0100 (CET)","from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[IPv6:2001:4b98:dc2:55:216:3eff:fef7:d647])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 072CB608CF\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tWed,  5 Nov 2025 10:27:24 +0100 (CET)","from ideasonboard.com (unknown\n\t[IPv6:2a00:6020:448c:6c00:3793:9dc2:3dec:ebc3])\n\tby perceval.ideasonboard.com (Postfix) with UTF8SMTPSA id AEFF522A;\n\tWed,  5 Nov 2025 10:25:29 +0100 (CET)"],"Authentication-Results":"lancelot.ideasonboard.com; dkim=pass (1024-bit key;\n\tunprotected) header.d=ideasonboard.com header.i=@ideasonboard.com\n\theader.b=\"lv65ZCT+\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1762334729;\n\tbh=3bLJdENYAfoHnmllWTS6ujehmKTY2RNSfMped1hjOQY=;\n\th=In-Reply-To:References:Subject:From:Cc:To:Date:From;\n\tb=lv65ZCT+yHiCVsiwE8EfZwZRO5F1kn5Z+4d5p1iveB52ain4aBp45i/lwGyOrUKV7\n\t7yllcdp7QfaBa3oA6NWUAAnIREjnATLtH1mc+DeUoYyNKdBgabSLHXGPXyOqrU7JnL\n\t6DGGPyFtSOzgYGxTPPpbQXs478JnF8sLWTCSPFWA=","Content-Type":"text/plain; charset=\"utf-8\"","MIME-Version":"1.0","Content-Transfer-Encoding":"quoted-printable","In-Reply-To":"<176233274098.231750.7195627093283025546@localhost>","References":"<20251029172439.1513907-1-kieran.bingham@ideasonboard.com>\n\t<20251029172439.1513907-2-kieran.bingham@ideasonboard.com>\n\t<176233274098.231750.7195627093283025546@localhost>","Subject":"Re: [PATCH v2 01/13] libipa: Provide a Quantized data type support","From":"Stefan Klug <stefan.klug@ideasonboard.com>","Cc":"Kieran Bingham <kieran.bingham@ideasonboard.com>","To":"Kieran Bingham <kieran.bingham@ideasonboard.com>,\n\tlibcamera devel <libcamera-devel@lists.libcamera.org>","Date":"Wed, 05 Nov 2025 10:27:20 +0100","Message-ID":"<176233484083.260669.17716354166087594954@localhost>","User-Agent":"alot/0.12.dev8+g2c003385c862.d20250602","X-BeenThere":"libcamera-devel@lists.libcamera.org","X-Mailman-Version":"2.1.29","Precedence":"list","List-Id":"<libcamera-devel.lists.libcamera.org>","List-Unsubscribe":"<https://lists.libcamera.org/options/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=unsubscribe>","List-Archive":"<https://lists.libcamera.org/pipermail/libcamera-devel/>","List-Post":"<mailto:libcamera-devel@lists.libcamera.org>","List-Help":"<mailto:libcamera-devel-request@lists.libcamera.org?subject=help>","List-Subscribe":"<https://lists.libcamera.org/listinfo/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=subscribe>","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"}},{"id":36702,"web_url":"https://patchwork.libcamera.org/comment/36702/","msgid":"<9558ec1a-1556-4726-8300-9d42fdcbdf1d@ideasonboard.com>","date":"2025-11-05T09:54:48","subject":"Re: [PATCH v2 01/13] libipa: Provide a Quantized data type support","submitter":{"id":216,"url":"https://patchwork.libcamera.org/api/people/216/","name":"Barnabás Pőcze","email":"barnabas.pocze@ideasonboard.com"},"content":"2025. 11. 05. 10:27 keltezéssel, Stefan Klug írta:\n> Hi Kieran,\n> \n> maybe I was a bit fast on this one...\n> \n> Quoting Stefan Klug (2025-11-05 09:52:20)\n>> Hi Kieran,\n>>\n>> Thank you for the patch.\n>>\n>> Quoting Kieran Bingham (2025-10-29 18:24:26)\n>>> Frequently when handling data in IPA components we must convert and\n>>> store user interface values which may be floating point values, and\n>>> perform a specific operation or conversion to quantize this to a\n>>> hardware value.\n>>>\n>>> This value may be to a fixed point type, or more custom code mappings,\n>>> but in either case it is important to contain both the required hardware\n>>> value, with its effective quantized value.\n>>>\n>>> Provide a new storage type 'Quantized' which can be defined based on a\n>>> set of type specific Traits to perform the conversions between floats\n>>> and the underlying hardware type.\n>>>\n>>> Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>\n>>> ---\n>>>   src/ipa/libipa/meson.build   |   2 +\n>>>   src/ipa/libipa/quantized.cpp | 134 +++++++++++++++++++++++++++++++++++\n>>>   src/ipa/libipa/quantized.h   |  79 +++++++++++++++++++++\n>>>   3 files changed, 215 insertions(+)\n>>>   create mode 100644 src/ipa/libipa/quantized.cpp\n>>>   create mode 100644 src/ipa/libipa/quantized.h\n>>>\n>>> diff --git a/src/ipa/libipa/meson.build b/src/ipa/libipa/meson.build\n>>> index 660be94054fa..804289778f72 100644\n>>> --- a/src/ipa/libipa/meson.build\n>>> +++ b/src/ipa/libipa/meson.build\n>>> @@ -17,6 +17,7 @@ libipa_headers = files([\n>>>       'lux.h',\n>>>       'module.h',\n>>>       'pwl.h',\n>>> +    'quantized.h',\n>>>   ])\n>>>   \n>>>   libipa_sources = files([\n>>> @@ -36,6 +37,7 @@ libipa_sources = files([\n>>>       'lux.cpp',\n>>>       'module.cpp',\n>>>       'pwl.cpp',\n>>> +    'quantized.cpp',\n>>>   ])\n>>>   \n>>>   libipa_includes = include_directories('..')\n>>> diff --git a/src/ipa/libipa/quantized.cpp b/src/ipa/libipa/quantized.cpp\n>>> new file mode 100644\n>>> index 000000000000..9772674159bd\n>>> --- /dev/null\n>>> +++ b/src/ipa/libipa/quantized.cpp\n>>> @@ -0,0 +1,134 @@\n>>> +/* SPDX-License-Identifier: LGPL-2.1-or-later */\n>>> +/*\n>>> + * Copyright (C) 2025, Ideas On Board.\n>>> + *\n>>> + * Helper class to manage conversions between floating point types and quantized\n>>> + * storage and representation of those values.\n>>> + */\n>>> +\n>>> +#include \"quantized.h\"\n>>> +\n>>> +/**\n>>> + * \\file quantized.h\n>>> + * \\brief Quantized storage and Quantizer representations\n>>> + */\n>>> +\n>>> +namespace libcamera {\n>>> +\n>>> +namespace ipa {\n>>> +\n>>> +/**\n>>> + * \\struct libcamera::ipa::Quantized\n>>> + * \\brief Wrapper that stores a value in both quantized and floating-point form\n>>> + * \\tparam Traits The traits class defining the quantization behaviour\n>>> + *\n>>> + * The Quantized struct template provides a thin wrapper around a quantized\n>>> + * representation of a floating-point value. It uses a traits type \\a Traits\n>>> + * to define the conversion policy between the floating-point domain and the\n>>> + * quantized integer domain.\n>>> + *\n>>> + * Each Quantized instance maintains two synchronized members:\n>>> + *  - the quantized integer representation, and\n>>> + *  - the corresponding floating-point value.\n>>> + *\n>>> + * The traits type defines:\n>>> + *  - the integer storage type used for quantization,\n>>> + *  - the static conversion functions \\c fromFloat() and \\c toFloat(), and\n>>> + *  - optional metadata such as value ranges.\n>>> + *\n>>> + * Quantized provides convenient constructors and assignment operators from\n>>> + * either representation, as well as comparison and string formatting utilities.\n>>> + */\n>>> +\n>>> +/**\n>>> + * \\typedef Quantized::TraitsType\n>>> + * \\brief The traits policy type defining the quantization behaviour\n>>> + *\n>>> + * Exposes the associated traits type used by this Quantized instance.\n>>> + * This allows external code to refer to constants or metadata defined in\n>>> + * the traits, such as \\c TraitsType::min or \\c TraitsType::max.\n>>> + */\n>>> +\n>>> +/**\n>>> + * \\typedef Quantized::quantized_type\n>>> + * \\brief The integer type used for the quantized representation\n>>> + *\n>>> + * This alias corresponds to \\c TraitsType::quantized_type, as defined by\n>>> + * the traits class.\n>>> + */\n>>> +\n>>> +/**\n>>> + * \\fn Quantized::Quantized(float x)\n>>> + * \\brief Construct a Quantized value from a floating-point number\n>>> + * \\param[in] x The floating-point value to be quantized\n>>> + *\n>>> + * Converts the floating-point input \\a x to its quantized integer\n>>> + * representation using the associated traits policy, and initializes\n>>> + * both the quantized and floating-point members.\n>>> + */\n>>> +\n>>> +/**\n>>> + * \\fn Quantized::Quantized(quantized_type x)\n>>> + * \\brief Construct a Quantized value from an existing quantized integer\n>>> + * \\param[in] x The quantized integer value\n>>> + *\n>>> + * Converts the quantized integer \\a x to its corresponding floating-point\n>>> + * value using the traits policy, and initializes both internal members.\n>>> + */\n>>> +\n>>> +/**\n>>> + * \\fn Quantized::operator=(float x)\n>>> + * \\brief Assign a floating-point value to the Quantized object\n>>> + * \\param[in] x The floating-point value to assign\n>>> + * \\return A reference to the updated Quantized object\n>>> + *\n>>> + * Converts the floating-point value \\a x to its quantized integer\n>>> + * representation using the traits policy and updates both members.\n>>> + */\n>>> +\n>>> +/**\n>>> + * \\fn Quantized::operator=(quantized_type x)\n>>> + * \\brief Assign a quantized integer value to the Quantized object\n>>> + * \\param[in] x The quantized integer value to assign\n>>> + * \\return A reference to the updated Quantized object\n>>> + *\n>>> + * Converts the quantized integer \\a x to its corresponding floating-point\n>>> + * value using the traits policy and updates both members.\n>>> + */\n>>> +\n>>> +/**\n>>> + * \\fn Quantized::value() const noexcept\n>>> + * \\brief Retrieve the floating-point representation\n>>> + * \\return The floating-point value corresponding to the quantized value\n>>> + */\n>>> +\n>>> +/**\n>>> + * \\fn Quantized::quantized() const noexcept\n>>> + * \\brief Retrieve the quantized integer representation\n>>> + * \\return The quantized integer value\n>>> + */\n>>> +\n>>> +/**\n>>> + * \\fn Quantized::toString() const\n>>> + * \\brief Format the quantized and floating-point values as a string\n>>> + * \\return A string containing the hexadecimal quantized value and its\n>>> + *         floating-point equivalent.\n>>> + */\n>>> +\n>>> +/**\n>>> + * \\fn Quantized::operator==(const Quantized &other) const noexcept\n>>> + * \\brief Compare two Quantized objects for equality\n>>> + * \\param[in] other The other Quantized object to compare against\n>>> + * \\return True if both objects have the same quantized integer value\n>>> + */\n>>> +\n>>> +/**\n>>> + * \\fn Quantized::operator!=(const Quantized &other) const noexcept\n>>> + * \\brief Compare two Quantized objects for inequality\n>>> + * \\param[in] other The other Quantized object to compare against\n>>> + * \\return True if the quantized integer values differ\n>>> + */\n>>> +\n>>> +} /* namespace ipa */\n>>> +\n>>> +} /* namespace libcamera */\n>>> diff --git a/src/ipa/libipa/quantized.h b/src/ipa/libipa/quantized.h\n>>> new file mode 100644\n>>> index 000000000000..26cb8d619e15\n>>> --- /dev/null\n>>> +++ b/src/ipa/libipa/quantized.h\n>>> @@ -0,0 +1,79 @@\n>>> +/* SPDX-License-Identifier: LGPL-2.1-or-later */\n>>> +/*\n>>> + * Copyright (C) 2025, Ideas On Board.\n>>\n>> micro nit: No dot at the end.\n>>\n>>> + *\n>>> + * Helper class to manage conversions between floating point types and quantized\n>>> + * storage and representation of those values.\n>>> + */\n>>> +\n>>> +#pragma once\n>>> +\n>>> +#include <iomanip>\n>>> +#include <sstream>\n>>> +#include <stdint.h>\n>>> +#include <type_traits>\n>>> +\n>>> +#include <libcamera/base/utils.h>\n>>> +\n>>> +namespace libcamera {\n>>> +\n>>> +namespace ipa {\n>>> +\n>>> +template<typename Traits>\n>>> +struct Quantized {\n> \n> Following the series further down I realized that I'm missing something\n> here. The Traits type encapsulates the conversion from float to int. But\n> for the sensor gain use case we don't know that conversion when we\n> declare the gain in the activeState/frameContext as the conversion is\n> solely done at runtime by the camera sensor helper. How is this supposed\n> to work?\n\nOne way that can be made to work is to inherit the `Traits` param\nand add (at least one) constructor to initialize the base class:\n\ntemplate<typename Traits>\nstruct Quantized : Traits {\n\n   template<typename... Args>\n   Quantized(Args&&... args)\n     : Traits(std::forward<Args>(args)...)\n   { *this = 0.0f; }\n};\n\neverything else can stay basically the same. Although I suppose\nsome consideration has to be given to operator= and operator==.\n\nThen one can have e.g.\n\nstruct SensorGainQTraits {\nprivate:\n   const CameraSenshorHelper *csh_;\n\npublic:\n   using quantized_type = uint32_t;\n\n   SensorGainQTraits(const CameraSenshorHelper *csh)\n     : csh_(csh)\n   { }\n\n   quantized_type fromFloat(float x) const { return csh->gainCode(x); }\n   float toFloat(quantized_type x) const { return csh->gain(x); }\n};\n\nQuantized<SensorGainQTraits> q(helper);\n\n> \n> I know you had that idea of \"a quantized value can only be read\". I\n> don't see this anymore - what did I miss?\n> \n> Best regards,\n> Stefan\n> \n>>> +       using TraitsType = Traits;\n>>> +       using quantized_type = typename Traits::quantized_type;\n>>> +       static_assert(std::is_arithmetic_v<quantized_type>,\n>>> +                     \"Quantized: quantized_type must be arithmetic\");\n>>> +\n>>> +       /* Constructors */\n>>\n>> I think this comment can be dropped.\n>>\n>>> +       Quantized()\n>>> +               : Quantized(0.0f) {}\n>>> +       Quantized(float x) { *this = x; }\n>>> +       Quantized(quantized_type x) { *this = x; }\n>>> +\n>>> +       Quantized &operator=(float x)\n>>> +       {\n>>> +               quantized_ = Traits::fromFloat(x);\n>>> +               value_ = Traits::toFloat(quantized_);\n>>> +               return *this;\n>>> +       }\n>>> +\n>>> +       Quantized &operator=(quantized_type x)\n>>> +       {\n>>> +               value_ = Traits::toFloat(x);\n>>> +               quantized_ = x;\n>>> +               return *this;\n>>> +       }\n>>> +\n>>> +       float value() const noexcept { return value_; }\n>>> +       quantized_type quantized() const noexcept { return quantized_; }\n>>> +\n>>> +       std::string toString() const\n>>> +       {\n>>> +               std::ostringstream oss;\n>>> +\n>>> +               oss << \"Q:\" << utils::hex(quantized_)\n>>> +                   << \" V:\" << value_;\n>>> +\n>>> +               return oss.str();\n>>> +       }\n>>> +\n>>> +       bool operator==(const Quantized &other) const noexcept\n>>> +       {\n>>> +               return quantized_ == other.quantized_;\n>>> +       }\n>>> +\n>>> +       bool operator!=(const Quantized &other) const noexcept\n>>> +       {\n>>> +               return !(*this == other);\n>>> +       }\n>>> +\n>>> +private:\n>>> +       quantized_type quantized_{};\n>>> +       float value_{};\n>>\n>> Out of curiosity: Why are the initializers needed here?\n>>\n>> All only nits, so\n>> Reviewed-by: Stefan Klug <stefan.klug@ideasonboard.com>\n>>\n>> Best regards,\n>> Stefan\n>>\n>>> +};\n>>> +\n>>> +} /* namespace ipa */\n>>> +\n>>> +} /* namespace libcamera */\n>>> -- \n>>> 2.50.1\n>>>","headers":{"Return-Path":"<libcamera-devel-bounces@lists.libcamera.org>","X-Original-To":"parsemail@patchwork.libcamera.org","Delivered-To":"parsemail@patchwork.libcamera.org","Received":["from lancelot.ideasonboard.com (lancelot.ideasonboard.com\n\t[92.243.16.209])\n\tby patchwork.libcamera.org (Postfix) with ESMTPS id ECC20C3241\n\tfor <parsemail@patchwork.libcamera.org>;\n\tWed,  5 Nov 2025 09:54:54 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id AF46E60A8B;\n\tWed,  5 Nov 2025 10:54:53 +0100 (CET)","from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[IPv6:2001:4b98:dc2:55:216:3eff:fef7:d647])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 70837608CF\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tWed,  5 Nov 2025 10:54:51 +0100 (CET)","from [192.168.33.40] (185.221.140.239.nat.pool.zt.hu\n\t[185.221.140.239])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id 285686F3;\n\tWed,  5 Nov 2025 10:52:57 +0100 (CET)"],"Authentication-Results":"lancelot.ideasonboard.com; dkim=pass (1024-bit key;\n\tunprotected) header.d=ideasonboard.com header.i=@ideasonboard.com\n\theader.b=\"Cw9BTxi0\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1762336377;\n\tbh=mz4PRlV+CMdq+FhEny5ZLFqi3AyYuX+epHVoO4m7lm8=;\n\th=Date:Subject:To:References:From:In-Reply-To:From;\n\tb=Cw9BTxi0YmZazsN/lc6qgmFxpgrYP1iSRIQkmWGbdcSVCLoUyRa/gbCXrOdIgXqR+\n\tvnGJztD6C+CuGq4DRwITCBGGYlmOxXEx3wxY8pZ+ZJRUhoVRuN80LZ6g2ZBZoL2fJ+\n\tD3OF7MZENLM1cs/k7dve6mSRMTu24T3SsbG+7yYo=","Message-ID":"<9558ec1a-1556-4726-8300-9d42fdcbdf1d@ideasonboard.com>","Date":"Wed, 5 Nov 2025 10:54:48 +0100","MIME-Version":"1.0","User-Agent":"Mozilla Thunderbird","Subject":"Re: [PATCH v2 01/13] libipa: Provide a Quantized data type support","To":"Stefan Klug <stefan.klug@ideasonboard.com>,\n\tKieran Bingham <kieran.bingham@ideasonboard.com>,\n\tlibcamera devel <libcamera-devel@lists.libcamera.org>","References":"<20251029172439.1513907-1-kieran.bingham@ideasonboard.com>\n\t<20251029172439.1513907-2-kieran.bingham@ideasonboard.com>\n\t<176233274098.231750.7195627093283025546@localhost>\n\t<176233484083.260669.17716354166087594954@localhost>","From":"=?utf-8?q?Barnab=C3=A1s_P=C5=91cze?= <barnabas.pocze@ideasonboard.com>","Content-Language":"en-US, hu-HU","In-Reply-To":"<176233484083.260669.17716354166087594954@localhost>","Content-Type":"text/plain; charset=UTF-8; format=flowed","Content-Transfer-Encoding":"8bit","X-BeenThere":"libcamera-devel@lists.libcamera.org","X-Mailman-Version":"2.1.29","Precedence":"list","List-Id":"<libcamera-devel.lists.libcamera.org>","List-Unsubscribe":"<https://lists.libcamera.org/options/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=unsubscribe>","List-Archive":"<https://lists.libcamera.org/pipermail/libcamera-devel/>","List-Post":"<mailto:libcamera-devel@lists.libcamera.org>","List-Help":"<mailto:libcamera-devel-request@lists.libcamera.org?subject=help>","List-Subscribe":"<https://lists.libcamera.org/listinfo/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=subscribe>","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"}},{"id":36703,"web_url":"https://patchwork.libcamera.org/comment/36703/","msgid":"<176233668820.567526.17658316737333151664@ping.linuxembedded.co.uk>","date":"2025-11-05T09:58:08","subject":"Re: [PATCH v2 01/13] libipa: Provide a Quantized data type support","submitter":{"id":4,"url":"https://patchwork.libcamera.org/api/people/4/","name":"Kieran Bingham","email":"kieran.bingham@ideasonboard.com"},"content":"Quoting Stefan Klug (2025-11-05 09:27:20)\n> Hi Kieran,\n> \n> maybe I was a bit fast on this one...\n> \n> Quoting Stefan Klug (2025-11-05 09:52:20)\n> > Hi Kieran,\n> > \n> > Thank you for the patch.\n> > \n> > Quoting Kieran Bingham (2025-10-29 18:24:26)\n> > > Frequently when handling data in IPA components we must convert and\n> > > store user interface values which may be floating point values, and\n> > > perform a specific operation or conversion to quantize this to a\n> > > hardware value.\n> > > \n> > > This value may be to a fixed point type, or more custom code mappings,\n> > > but in either case it is important to contain both the required hardware\n> > > value, with its effective quantized value.\n> > > \n> > > Provide a new storage type 'Quantized' which can be defined based on a\n> > > set of type specific Traits to perform the conversions between floats\n> > > and the underlying hardware type.\n> > > \n> > > Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>\n> > > ---\n> > >  src/ipa/libipa/meson.build   |   2 +\n> > >  src/ipa/libipa/quantized.cpp | 134 +++++++++++++++++++++++++++++++++++\n> > >  src/ipa/libipa/quantized.h   |  79 +++++++++++++++++++++\n> > >  3 files changed, 215 insertions(+)\n> > >  create mode 100644 src/ipa/libipa/quantized.cpp\n> > >  create mode 100644 src/ipa/libipa/quantized.h\n> > > \n> > > diff --git a/src/ipa/libipa/meson.build b/src/ipa/libipa/meson.build\n> > > index 660be94054fa..804289778f72 100644\n> > > --- a/src/ipa/libipa/meson.build\n> > > +++ b/src/ipa/libipa/meson.build\n> > > @@ -17,6 +17,7 @@ libipa_headers = files([\n> > >      'lux.h',\n> > >      'module.h',\n> > >      'pwl.h',\n> > > +    'quantized.h',\n> > >  ])\n> > >  \n> > >  libipa_sources = files([\n> > > @@ -36,6 +37,7 @@ libipa_sources = files([\n> > >      'lux.cpp',\n> > >      'module.cpp',\n> > >      'pwl.cpp',\n> > > +    'quantized.cpp',\n> > >  ])\n> > >  \n> > >  libipa_includes = include_directories('..')\n> > > diff --git a/src/ipa/libipa/quantized.cpp b/src/ipa/libipa/quantized.cpp\n> > > new file mode 100644\n> > > index 000000000000..9772674159bd\n> > > --- /dev/null\n> > > +++ b/src/ipa/libipa/quantized.cpp\n> > > @@ -0,0 +1,134 @@\n> > > +/* SPDX-License-Identifier: LGPL-2.1-or-later */\n> > > +/*\n> > > + * Copyright (C) 2025, Ideas On Board.\n> > > + *\n> > > + * Helper class to manage conversions between floating point types and quantized\n> > > + * storage and representation of those values.\n> > > + */\n> > > +\n> > > +#include \"quantized.h\"\n> > > +\n> > > +/**\n> > > + * \\file quantized.h\n> > > + * \\brief Quantized storage and Quantizer representations\n> > > + */\n> > > +\n> > > +namespace libcamera {\n> > > +\n> > > +namespace ipa {\n> > > +\n> > > +/**\n> > > + * \\struct libcamera::ipa::Quantized\n> > > + * \\brief Wrapper that stores a value in both quantized and floating-point form\n> > > + * \\tparam Traits The traits class defining the quantization behaviour\n> > > + *\n> > > + * The Quantized struct template provides a thin wrapper around a quantized\n> > > + * representation of a floating-point value. It uses a traits type \\a Traits\n> > > + * to define the conversion policy between the floating-point domain and the\n> > > + * quantized integer domain.\n> > > + *\n> > > + * Each Quantized instance maintains two synchronized members:\n> > > + *  - the quantized integer representation, and\n> > > + *  - the corresponding floating-point value.\n> > > + *\n> > > + * The traits type defines:\n> > > + *  - the integer storage type used for quantization,\n> > > + *  - the static conversion functions \\c fromFloat() and \\c toFloat(), and\n> > > + *  - optional metadata such as value ranges.\n> > > + *\n> > > + * Quantized provides convenient constructors and assignment operators from\n> > > + * either representation, as well as comparison and string formatting utilities.\n> > > + */\n> > > +\n> > > +/**\n> > > + * \\typedef Quantized::TraitsType\n> > > + * \\brief The traits policy type defining the quantization behaviour\n> > > + *\n> > > + * Exposes the associated traits type used by this Quantized instance.\n> > > + * This allows external code to refer to constants or metadata defined in\n> > > + * the traits, such as \\c TraitsType::min or \\c TraitsType::max.\n> > > + */\n> > > +\n> > > +/**\n> > > + * \\typedef Quantized::quantized_type\n> > > + * \\brief The integer type used for the quantized representation\n> > > + *\n> > > + * This alias corresponds to \\c TraitsType::quantized_type, as defined by\n> > > + * the traits class.\n> > > + */\n> > > +\n> > > +/**\n> > > + * \\fn Quantized::Quantized(float x)\n> > > + * \\brief Construct a Quantized value from a floating-point number\n> > > + * \\param[in] x The floating-point value to be quantized\n> > > + *\n> > > + * Converts the floating-point input \\a x to its quantized integer\n> > > + * representation using the associated traits policy, and initializes\n> > > + * both the quantized and floating-point members.\n> > > + */\n> > > +\n> > > +/**\n> > > + * \\fn Quantized::Quantized(quantized_type x)\n> > > + * \\brief Construct a Quantized value from an existing quantized integer\n> > > + * \\param[in] x The quantized integer value\n> > > + *\n> > > + * Converts the quantized integer \\a x to its corresponding floating-point\n> > > + * value using the traits policy, and initializes both internal members.\n> > > + */\n> > > +\n> > > +/**\n> > > + * \\fn Quantized::operator=(float x)\n> > > + * \\brief Assign a floating-point value to the Quantized object\n> > > + * \\param[in] x The floating-point value to assign\n> > > + * \\return A reference to the updated Quantized object\n> > > + *\n> > > + * Converts the floating-point value \\a x to its quantized integer\n> > > + * representation using the traits policy and updates both members.\n> > > + */\n> > > +\n> > > +/**\n> > > + * \\fn Quantized::operator=(quantized_type x)\n> > > + * \\brief Assign a quantized integer value to the Quantized object\n> > > + * \\param[in] x The quantized integer value to assign\n> > > + * \\return A reference to the updated Quantized object\n> > > + *\n> > > + * Converts the quantized integer \\a x to its corresponding floating-point\n> > > + * value using the traits policy and updates both members.\n> > > + */\n> > > +\n> > > +/**\n> > > + * \\fn Quantized::value() const noexcept\n> > > + * \\brief Retrieve the floating-point representation\n> > > + * \\return The floating-point value corresponding to the quantized value\n> > > + */\n> > > +\n> > > +/**\n> > > + * \\fn Quantized::quantized() const noexcept\n> > > + * \\brief Retrieve the quantized integer representation\n> > > + * \\return The quantized integer value\n> > > + */\n> > > +\n> > > +/**\n> > > + * \\fn Quantized::toString() const\n> > > + * \\brief Format the quantized and floating-point values as a string\n> > > + * \\return A string containing the hexadecimal quantized value and its\n> > > + *         floating-point equivalent.\n> > > + */\n> > > +\n> > > +/**\n> > > + * \\fn Quantized::operator==(const Quantized &other) const noexcept\n> > > + * \\brief Compare two Quantized objects for equality\n> > > + * \\param[in] other The other Quantized object to compare against\n> > > + * \\return True if both objects have the same quantized integer value\n> > > + */\n> > > +\n> > > +/**\n> > > + * \\fn Quantized::operator!=(const Quantized &other) const noexcept\n> > > + * \\brief Compare two Quantized objects for inequality\n> > > + * \\param[in] other The other Quantized object to compare against\n> > > + * \\return True if the quantized integer values differ\n> > > + */\n> > > +\n> > > +} /* namespace ipa */\n> > > +\n> > > +} /* namespace libcamera */\n> > > diff --git a/src/ipa/libipa/quantized.h b/src/ipa/libipa/quantized.h\n> > > new file mode 100644\n> > > index 000000000000..26cb8d619e15\n> > > --- /dev/null\n> > > +++ b/src/ipa/libipa/quantized.h\n> > > @@ -0,0 +1,79 @@\n> > > +/* SPDX-License-Identifier: LGPL-2.1-or-later */\n> > > +/*\n> > > + * Copyright (C) 2025, Ideas On Board.\n> > \n> > micro nit: No dot at the end.\n> > \n> > > + *\n> > > + * Helper class to manage conversions between floating point types and quantized\n> > > + * storage and representation of those values.\n> > > + */\n> > > +\n> > > +#pragma once\n> > > +\n> > > +#include <iomanip>\n> > > +#include <sstream>\n> > > +#include <stdint.h>\n> > > +#include <type_traits>\n> > > +\n> > > +#include <libcamera/base/utils.h>\n> > > +\n> > > +namespace libcamera {\n> > > +\n> > > +namespace ipa {\n> > > +\n> > > +template<typename Traits>\n> > > +struct Quantized {\n> \n> Following the series further down I realized that I'm missing something\n> here. The Traits type encapsulates the conversion from float to int. But\n> for the sensor gain use case we don't know that conversion when we\n> declare the gain in the activeState/frameContext as the conversion is\n> solely done at runtime by the camera sensor helper. How is this supposed\n> to work?\n\nExcellent, now I know you're in deep :D\n\n> \n> I know you had that idea of \"a quantized value can only be read\". I\n> don't see this anymore - what did I miss?\n\nYes, that's originally what I wanted. Then it was recommended to combine\nthe storage and the access and utilise the Traits structure.\n\nSo far this has worked out really well because most of the types I've\nused so far can store the quantizer traits either globally in the case\nof the Q formats or locally.\n\nBut I do agree we might still want a storage only version sometimes so I\nwould propose we make a ReadOnly trait or such:\n\nint testReadOnlyQuantizers()\n{\n\tstruct ReadOnlyGain\n\t{\n\t\tusing quantized_type = int8_t;\n\t};\n\tQuantized<ReadOnlyGain> q = Q1_7(0.5f);\n\n\tstd::cout << \"ReadOnly Quantized: \" << q.toString() << std::endl;\n\treturn TestPass;\n}\n\nBut I haven't got that compiling yet ... maybe we'll need to add some\nassignment operators or such to allow it?\n\nHowever - I think it might also be interesting to see if we can make the\ngain code helpers just an implementation of the Traits so they\nthemselves just become a native Quantized type, and could be passed\naround directly anyway.\n\n--\nKieran\n\n\n> \n> Best regards,\n> Stefan\n> \n> > > +       using TraitsType = Traits;\n> > > +       using quantized_type = typename Traits::quantized_type;\n> > > +       static_assert(std::is_arithmetic_v<quantized_type>,\n> > > +                     \"Quantized: quantized_type must be arithmetic\");\n> > > +\n> > > +       /* Constructors */\n> > \n> > I think this comment can be dropped.\n> > \n> > > +       Quantized()\n> > > +               : Quantized(0.0f) {}\n> > > +       Quantized(float x) { *this = x; }\n> > > +       Quantized(quantized_type x) { *this = x; }\n> > > +\n> > > +       Quantized &operator=(float x)\n> > > +       {\n> > > +               quantized_ = Traits::fromFloat(x);\n> > > +               value_ = Traits::toFloat(quantized_);\n> > > +               return *this;\n> > > +       }\n> > > +\n> > > +       Quantized &operator=(quantized_type x)\n> > > +       {\n> > > +               value_ = Traits::toFloat(x);\n> > > +               quantized_ = x;\n> > > +               return *this;\n> > > +       }\n> > > +\n> > > +       float value() const noexcept { return value_; }\n> > > +       quantized_type quantized() const noexcept { return quantized_; }\n> > > +\n> > > +       std::string toString() const\n> > > +       {\n> > > +               std::ostringstream oss;\n> > > +\n> > > +               oss << \"Q:\" << utils::hex(quantized_)\n> > > +                   << \" V:\" << value_;\n> > > +\n> > > +               return oss.str();\n> > > +       }\n> > > +\n> > > +       bool operator==(const Quantized &other) const noexcept\n> > > +       {\n> > > +               return quantized_ == other.quantized_;\n> > > +       }\n> > > +\n> > > +       bool operator!=(const Quantized &other) const noexcept\n> > > +       {\n> > > +               return !(*this == other);\n> > > +       }\n> > > +\n> > > +private:\n> > > +       quantized_type quantized_{};\n> > > +       float value_{};\n> > \n> > Out of curiosity: Why are the initializers needed here?\n> > \n> > All only nits, so\n> > Reviewed-by: Stefan Klug <stefan.klug@ideasonboard.com> \n> > \n> > Best regards,\n> > Stefan\n> > \n> > > +};\n> > > +\n> > > +} /* namespace ipa */\n> > > +\n> > > +} /* namespace libcamera */\n> > > -- \n> > > 2.50.1\n> > >","headers":{"Return-Path":"<libcamera-devel-bounces@lists.libcamera.org>","X-Original-To":"parsemail@patchwork.libcamera.org","Delivered-To":"parsemail@patchwork.libcamera.org","Received":["from lancelot.ideasonboard.com (lancelot.ideasonboard.com\n\t[92.243.16.209])\n\tby patchwork.libcamera.org (Postfix) with ESMTPS id F098BBDE4C\n\tfor <parsemail@patchwork.libcamera.org>;\n\tWed,  5 Nov 2025 09:58:14 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id EB27660A7B;\n\tWed,  5 Nov 2025 10:58:13 +0100 (CET)","from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[IPv6:2001:4b98:dc2:55:216:3eff:fef7:d647])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id D9ABF609D8\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tWed,  5 Nov 2025 10:58:11 +0100 (CET)","from pendragon.ideasonboard.com\n\t(cpc89244-aztw30-2-0-cust6594.18-1.cable.virginm.net [86.31.185.195])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id 8C4E06F9;\n\tWed,  5 Nov 2025 10:56:17 +0100 (CET)"],"Authentication-Results":"lancelot.ideasonboard.com; dkim=pass (1024-bit key;\n\tunprotected) header.d=ideasonboard.com header.i=@ideasonboard.com\n\theader.b=\"tpi1Ybvk\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1762336577;\n\tbh=MlH0ztQqslUDpn9NcITmDhti81ysmdNbia19TJbj0Lg=;\n\th=In-Reply-To:References:Subject:From:Cc:To:Date:From;\n\tb=tpi1YbvkReZ+qls1UXrMt8qavz5nIzV2DvWvvY/R3KjuWz7U88sRQpQRL1apkLwtC\n\tBCDMIpkTiTeQ/arGUs5Osb+xENpOPPabqNLf+dowHhtMp9hyYHrtGUZ6OR8htCwGCQ\n\typ09QR5f34Typ2xala/IYrEJFJrXFmiKxVKc5DCY=","Content-Type":"text/plain; charset=\"utf-8\"","MIME-Version":"1.0","Content-Transfer-Encoding":"quoted-printable","In-Reply-To":"<176233484083.260669.17716354166087594954@localhost>","References":"<20251029172439.1513907-1-kieran.bingham@ideasonboard.com>\n\t<20251029172439.1513907-2-kieran.bingham@ideasonboard.com>\n\t<176233274098.231750.7195627093283025546@localhost>\n\t<176233484083.260669.17716354166087594954@localhost>","Subject":"Re: [PATCH v2 01/13] libipa: Provide a Quantized data type support","From":"Kieran Bingham <kieran.bingham@ideasonboard.com>","Cc":"","To":"Stefan Klug <stefan.klug@ideasonboard.com>,\n\tlibcamera devel <libcamera-devel@lists.libcamera.org>","Date":"Wed, 05 Nov 2025 09:58:08 +0000","Message-ID":"<176233668820.567526.17658316737333151664@ping.linuxembedded.co.uk>","User-Agent":"alot/0.9.1","X-BeenThere":"libcamera-devel@lists.libcamera.org","X-Mailman-Version":"2.1.29","Precedence":"list","List-Id":"<libcamera-devel.lists.libcamera.org>","List-Unsubscribe":"<https://lists.libcamera.org/options/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=unsubscribe>","List-Archive":"<https://lists.libcamera.org/pipermail/libcamera-devel/>","List-Post":"<mailto:libcamera-devel@lists.libcamera.org>","List-Help":"<mailto:libcamera-devel-request@lists.libcamera.org?subject=help>","List-Subscribe":"<https://lists.libcamera.org/listinfo/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=subscribe>","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"}},{"id":36786,"web_url":"https://patchwork.libcamera.org/comment/36786/","msgid":"<176296165496.2141792.7751445492347344783@ping.linuxembedded.co.uk>","date":"2025-11-12T15:34:14","subject":"Re: [PATCH v2 01/13] libipa: Provide a Quantized data type support","submitter":{"id":4,"url":"https://patchwork.libcamera.org/api/people/4/","name":"Kieran Bingham","email":"kieran.bingham@ideasonboard.com"},"content":"Quoting Barnabás Pőcze (2025-11-05 09:54:48)\n> 2025. 11. 05. 10:27 keltezéssel, Stefan Klug írta:\n> > Hi Kieran,\n> > \n> > maybe I was a bit fast on this one...\n> > \n> > Quoting Stefan Klug (2025-11-05 09:52:20)\n> >> Hi Kieran,\n> >>\n> >> Thank you for the patch.\n> >>\n> >> Quoting Kieran Bingham (2025-10-29 18:24:26)\n> >>> Frequently when handling data in IPA components we must convert and\n> >>> store user interface values which may be floating point values, and\n> >>> perform a specific operation or conversion to quantize this to a\n> >>> hardware value.\n> >>>\n> >>> This value may be to a fixed point type, or more custom code mappings,\n> >>> but in either case it is important to contain both the required hardware\n> >>> value, with its effective quantized value.\n> >>>\n> >>> Provide a new storage type 'Quantized' which can be defined based on a\n> >>> set of type specific Traits to perform the conversions between floats\n> >>> and the underlying hardware type.\n> >>>\n> >>> Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>\n> >>> ---\n> >>>   src/ipa/libipa/meson.build   |   2 +\n> >>>   src/ipa/libipa/quantized.cpp | 134 +++++++++++++++++++++++++++++++++++\n> >>>   src/ipa/libipa/quantized.h   |  79 +++++++++++++++++++++\n> >>>   3 files changed, 215 insertions(+)\n> >>>   create mode 100644 src/ipa/libipa/quantized.cpp\n> >>>   create mode 100644 src/ipa/libipa/quantized.h\n> >>>\n> >>> diff --git a/src/ipa/libipa/meson.build b/src/ipa/libipa/meson.build\n> >>> index 660be94054fa..804289778f72 100644\n> >>> --- a/src/ipa/libipa/meson.build\n> >>> +++ b/src/ipa/libipa/meson.build\n> >>> @@ -17,6 +17,7 @@ libipa_headers = files([\n> >>>       'lux.h',\n> >>>       'module.h',\n> >>>       'pwl.h',\n> >>> +    'quantized.h',\n> >>>   ])\n> >>>   \n> >>>   libipa_sources = files([\n> >>> @@ -36,6 +37,7 @@ libipa_sources = files([\n> >>>       'lux.cpp',\n> >>>       'module.cpp',\n> >>>       'pwl.cpp',\n> >>> +    'quantized.cpp',\n> >>>   ])\n> >>>   \n> >>>   libipa_includes = include_directories('..')\n> >>> diff --git a/src/ipa/libipa/quantized.cpp b/src/ipa/libipa/quantized.cpp\n> >>> new file mode 100644\n> >>> index 000000000000..9772674159bd\n> >>> --- /dev/null\n> >>> +++ b/src/ipa/libipa/quantized.cpp\n> >>> @@ -0,0 +1,134 @@\n> >>> +/* SPDX-License-Identifier: LGPL-2.1-or-later */\n> >>> +/*\n> >>> + * Copyright (C) 2025, Ideas On Board.\n> >>> + *\n> >>> + * Helper class to manage conversions between floating point types and quantized\n> >>> + * storage and representation of those values.\n> >>> + */\n> >>> +\n> >>> +#include \"quantized.h\"\n> >>> +\n> >>> +/**\n> >>> + * \\file quantized.h\n> >>> + * \\brief Quantized storage and Quantizer representations\n> >>> + */\n> >>> +\n> >>> +namespace libcamera {\n> >>> +\n> >>> +namespace ipa {\n> >>> +\n> >>> +/**\n> >>> + * \\struct libcamera::ipa::Quantized\n> >>> + * \\brief Wrapper that stores a value in both quantized and floating-point form\n> >>> + * \\tparam Traits The traits class defining the quantization behaviour\n> >>> + *\n> >>> + * The Quantized struct template provides a thin wrapper around a quantized\n> >>> + * representation of a floating-point value. It uses a traits type \\a Traits\n> >>> + * to define the conversion policy between the floating-point domain and the\n> >>> + * quantized integer domain.\n> >>> + *\n> >>> + * Each Quantized instance maintains two synchronized members:\n> >>> + *  - the quantized integer representation, and\n> >>> + *  - the corresponding floating-point value.\n> >>> + *\n> >>> + * The traits type defines:\n> >>> + *  - the integer storage type used for quantization,\n> >>> + *  - the static conversion functions \\c fromFloat() and \\c toFloat(), and\n> >>> + *  - optional metadata such as value ranges.\n> >>> + *\n> >>> + * Quantized provides convenient constructors and assignment operators from\n> >>> + * either representation, as well as comparison and string formatting utilities.\n> >>> + */\n> >>> +\n> >>> +/**\n> >>> + * \\typedef Quantized::TraitsType\n> >>> + * \\brief The traits policy type defining the quantization behaviour\n> >>> + *\n> >>> + * Exposes the associated traits type used by this Quantized instance.\n> >>> + * This allows external code to refer to constants or metadata defined in\n> >>> + * the traits, such as \\c TraitsType::min or \\c TraitsType::max.\n> >>> + */\n> >>> +\n> >>> +/**\n> >>> + * \\typedef Quantized::quantized_type\n> >>> + * \\brief The integer type used for the quantized representation\n> >>> + *\n> >>> + * This alias corresponds to \\c TraitsType::quantized_type, as defined by\n> >>> + * the traits class.\n> >>> + */\n> >>> +\n> >>> +/**\n> >>> + * \\fn Quantized::Quantized(float x)\n> >>> + * \\brief Construct a Quantized value from a floating-point number\n> >>> + * \\param[in] x The floating-point value to be quantized\n> >>> + *\n> >>> + * Converts the floating-point input \\a x to its quantized integer\n> >>> + * representation using the associated traits policy, and initializes\n> >>> + * both the quantized and floating-point members.\n> >>> + */\n> >>> +\n> >>> +/**\n> >>> + * \\fn Quantized::Quantized(quantized_type x)\n> >>> + * \\brief Construct a Quantized value from an existing quantized integer\n> >>> + * \\param[in] x The quantized integer value\n> >>> + *\n> >>> + * Converts the quantized integer \\a x to its corresponding floating-point\n> >>> + * value using the traits policy, and initializes both internal members.\n> >>> + */\n> >>> +\n> >>> +/**\n> >>> + * \\fn Quantized::operator=(float x)\n> >>> + * \\brief Assign a floating-point value to the Quantized object\n> >>> + * \\param[in] x The floating-point value to assign\n> >>> + * \\return A reference to the updated Quantized object\n> >>> + *\n> >>> + * Converts the floating-point value \\a x to its quantized integer\n> >>> + * representation using the traits policy and updates both members.\n> >>> + */\n> >>> +\n> >>> +/**\n> >>> + * \\fn Quantized::operator=(quantized_type x)\n> >>> + * \\brief Assign a quantized integer value to the Quantized object\n> >>> + * \\param[in] x The quantized integer value to assign\n> >>> + * \\return A reference to the updated Quantized object\n> >>> + *\n> >>> + * Converts the quantized integer \\a x to its corresponding floating-point\n> >>> + * value using the traits policy and updates both members.\n> >>> + */\n> >>> +\n> >>> +/**\n> >>> + * \\fn Quantized::value() const noexcept\n> >>> + * \\brief Retrieve the floating-point representation\n> >>> + * \\return The floating-point value corresponding to the quantized value\n> >>> + */\n> >>> +\n> >>> +/**\n> >>> + * \\fn Quantized::quantized() const noexcept\n> >>> + * \\brief Retrieve the quantized integer representation\n> >>> + * \\return The quantized integer value\n> >>> + */\n> >>> +\n> >>> +/**\n> >>> + * \\fn Quantized::toString() const\n> >>> + * \\brief Format the quantized and floating-point values as a string\n> >>> + * \\return A string containing the hexadecimal quantized value and its\n> >>> + *         floating-point equivalent.\n> >>> + */\n> >>> +\n> >>> +/**\n> >>> + * \\fn Quantized::operator==(const Quantized &other) const noexcept\n> >>> + * \\brief Compare two Quantized objects for equality\n> >>> + * \\param[in] other The other Quantized object to compare against\n> >>> + * \\return True if both objects have the same quantized integer value\n> >>> + */\n> >>> +\n> >>> +/**\n> >>> + * \\fn Quantized::operator!=(const Quantized &other) const noexcept\n> >>> + * \\brief Compare two Quantized objects for inequality\n> >>> + * \\param[in] other The other Quantized object to compare against\n> >>> + * \\return True if the quantized integer values differ\n> >>> + */\n> >>> +\n> >>> +} /* namespace ipa */\n> >>> +\n> >>> +} /* namespace libcamera */\n> >>> diff --git a/src/ipa/libipa/quantized.h b/src/ipa/libipa/quantized.h\n> >>> new file mode 100644\n> >>> index 000000000000..26cb8d619e15\n> >>> --- /dev/null\n> >>> +++ b/src/ipa/libipa/quantized.h\n> >>> @@ -0,0 +1,79 @@\n> >>> +/* SPDX-License-Identifier: LGPL-2.1-or-later */\n> >>> +/*\n> >>> + * Copyright (C) 2025, Ideas On Board.\n> >>\n> >> micro nit: No dot at the end.\n\nFixed in up both .h and .cpp\n\n> >>\n> >>> + *\n> >>> + * Helper class to manage conversions between floating point types and quantized\n> >>> + * storage and representation of those values.\n> >>> + */\n> >>> +\n> >>> +#pragma once\n> >>> +\n> >>> +#include <iomanip>\n> >>> +#include <sstream>\n> >>> +#include <stdint.h>\n> >>> +#include <type_traits>\n> >>> +\n> >>> +#include <libcamera/base/utils.h>\n> >>> +\n> >>> +namespace libcamera {\n> >>> +\n> >>> +namespace ipa {\n> >>> +\n> >>> +template<typename Traits>\n> >>> +struct Quantized {\n> > \n> > Following the series further down I realized that I'm missing something\n> > here. The Traits type encapsulates the conversion from float to int. But\n> > for the sensor gain use case we don't know that conversion when we\n> > declare the gain in the activeState/frameContext as the conversion is\n> > solely done at runtime by the camera sensor helper. How is this supposed\n> > to work?\n> \n> One way that can be made to work is to inherit the `Traits` param\n> and add (at least one) constructor to initialize the base class:\n> \n> template<typename Traits>\n> struct Quantized : Traits {\n> \n>    template<typename... Args>\n>    Quantized(Args&&... args)\n>      : Traits(std::forward<Args>(args)...)\n>    { *this = 0.0f; }\n> };\n> \n> everything else can stay basically the same. Although I suppose\n> some consideration has to be given to operator= and operator==.\n> \n> Then one can have e.g.\n> \n> struct SensorGainQTraits {\n> private:\n>    const CameraSenshorHelper *csh_;\n> \n> public:\n>    using quantized_type = uint32_t;\n> \n>    SensorGainQTraits(const CameraSenshorHelper *csh)\n>      : csh_(csh)\n>    { }\n> \n>    quantized_type fromFloat(float x) const { return csh->gainCode(x); }\n>    float toFloat(quantized_type x) const { return csh->gain(x); }\n> };\n> \n> Quantized<SensorGainQTraits> q(helper);\n> \n> > \n> > I know you had that idea of \"a quantized value can only be read\". I\n> > don't see this anymore - what did I miss?\n\nI did. But I was asked to change from that model of having a storage\ntype and a processing type to this model which uses traits:\n\n - https://patchwork.libcamera.org/patch/24824/#36477\n\n> > \n> > Best regards,\n> > Stefan\n> > \n> >>> +       using TraitsType = Traits;\n> >>> +       using quantized_type = typename Traits::quantized_type;\n> >>> +       static_assert(std::is_arithmetic_v<quantized_type>,\n> >>> +                     \"Quantized: quantized_type must be arithmetic\");\n> >>> +\n> >>> +       /* Constructors */\n> >>\n> >> I think this comment can be dropped.\n> >>\n> >>> +       Quantized()\n> >>> +               : Quantized(0.0f) {}\n> >>> +       Quantized(float x) { *this = x; }\n> >>> +       Quantized(quantized_type x) { *this = x; }\n> >>> +\n> >>> +       Quantized &operator=(float x)\n> >>> +       {\n> >>> +               quantized_ = Traits::fromFloat(x);\n> >>> +               value_ = Traits::toFloat(quantized_);\n> >>> +               return *this;\n> >>> +       }\n> >>> +\n> >>> +       Quantized &operator=(quantized_type x)\n> >>> +       {\n> >>> +               value_ = Traits::toFloat(x);\n> >>> +               quantized_ = x;\n> >>> +               return *this;\n> >>> +       }\n> >>> +\n> >>> +       float value() const noexcept { return value_; }\n> >>> +       quantized_type quantized() const noexcept { return quantized_; }\n> >>> +\n> >>> +       std::string toString() const\n> >>> +       {\n> >>> +               std::ostringstream oss;\n> >>> +\n> >>> +               oss << \"Q:\" << utils::hex(quantized_)\n> >>> +                   << \" V:\" << value_;\n> >>> +\n> >>> +               return oss.str();\n> >>> +       }\n> >>> +\n> >>> +       bool operator==(const Quantized &other) const noexcept\n> >>> +       {\n> >>> +               return quantized_ == other.quantized_;\n> >>> +       }\n> >>> +\n> >>> +       bool operator!=(const Quantized &other) const noexcept\n> >>> +       {\n> >>> +               return !(*this == other);\n> >>> +       }\n> >>> +\n> >>> +private:\n> >>> +       quantized_type quantized_{};\n> >>> +       float value_{};\n> >>\n> >> Out of curiosity: Why are the initializers needed here?\n> >>\n\n\nThey're not any more - now there's explicit constructors.\nI'll remove them.\n\n\n> >> All only nits, so\n> >> Reviewed-by: Stefan Klug <stefan.klug@ideasonboard.com>\n\nDo you think Read Only handling for supporting this type with the Gain\nModels is a requirement for this series?\n\nWould it help to do that now ? Or should it be on top when we look at\nusing something for gain codes?\n\n--\nKieran\n\n\n> >>\n> >> Best regards,\n> >> Stefan\n> >>\n> >>> +};\n> >>> +\n> >>> +} /* namespace ipa */\n> >>> +\n> >>> +} /* namespace libcamera */\n> >>> -- \n> >>> 2.50.1\n> >>>\n>","headers":{"Return-Path":"<libcamera-devel-bounces@lists.libcamera.org>","X-Original-To":"parsemail@patchwork.libcamera.org","Delivered-To":"parsemail@patchwork.libcamera.org","Received":["from lancelot.ideasonboard.com (lancelot.ideasonboard.com\n\t[92.243.16.209])\n\tby patchwork.libcamera.org (Postfix) with ESMTPS id 29566C3241\n\tfor <parsemail@patchwork.libcamera.org>;\n\tWed, 12 Nov 2025 15:34:20 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 4EE4560A80;\n\tWed, 12 Nov 2025 16:34:19 +0100 (CET)","from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[IPv6:2001:4b98:dc2:55:216:3eff:fef7:d647])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 1D2536069A\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tWed, 12 Nov 2025 16:34:18 +0100 (CET)","from pendragon.ideasonboard.com\n\t(cpc89244-aztw30-2-0-cust6594.18-1.cable.virginm.net [86.31.185.195])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id 7EDCFB0B;\n\tWed, 12 Nov 2025 16:32:18 +0100 (CET)"],"Authentication-Results":"lancelot.ideasonboard.com; dkim=pass (1024-bit key;\n\tunprotected) header.d=ideasonboard.com header.i=@ideasonboard.com\n\theader.b=\"nH6jaWjk\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1762961538;\n\tbh=hSePudYKlN9af5vaO6Az76gYITm9Z+nQ9T77V4aYkis=;\n\th=In-Reply-To:References:Subject:From:To:Date:From;\n\tb=nH6jaWjklwveb93Rz9qVjHHZN8z2VFKMaWcwAC+xgVdHw3VZ4ICbFmgaOD6NmED1u\n\tL/DRZxtV2TmBkhJkeXcMKUFObz/4UzaAsBiBA9Cxd40PCxtCVXRRDQDe4X1CgYjorE\n\tC0kYgg1sCm0IbzBBcean7yhUQNzpttZvQPONFbMM=","Content-Type":"text/plain; charset=\"utf-8\"","MIME-Version":"1.0","Content-Transfer-Encoding":"quoted-printable","In-Reply-To":"<9558ec1a-1556-4726-8300-9d42fdcbdf1d@ideasonboard.com>","References":"<20251029172439.1513907-1-kieran.bingham@ideasonboard.com>\n\t<20251029172439.1513907-2-kieran.bingham@ideasonboard.com>\n\t<176233274098.231750.7195627093283025546@localhost>\n\t<176233484083.260669.17716354166087594954@localhost>\n\t<9558ec1a-1556-4726-8300-9d42fdcbdf1d@ideasonboard.com>","Subject":"Re: [PATCH v2 01/13] libipa: Provide a Quantized data type support","From":"Kieran Bingham <kieran.bingham@ideasonboard.com>","To":"=?utf-8?q?Barnab=C3=A1s_P=C5=91cze?= <barnabas.pocze@ideasonboard.com>,\n\tStefan Klug <stefan.klug@ideasonboard.com>, libcamera devel\n\t<libcamera-devel@lists.libcamera.org>","Date":"Wed, 12 Nov 2025 15:34:14 +0000","Message-ID":"<176296165496.2141792.7751445492347344783@ping.linuxembedded.co.uk>","User-Agent":"alot/0.9.1","X-BeenThere":"libcamera-devel@lists.libcamera.org","X-Mailman-Version":"2.1.29","Precedence":"list","List-Id":"<libcamera-devel.lists.libcamera.org>","List-Unsubscribe":"<https://lists.libcamera.org/options/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=unsubscribe>","List-Archive":"<https://lists.libcamera.org/pipermail/libcamera-devel/>","List-Post":"<mailto:libcamera-devel@lists.libcamera.org>","List-Help":"<mailto:libcamera-devel-request@lists.libcamera.org?subject=help>","List-Subscribe":"<https://lists.libcamera.org/listinfo/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=subscribe>","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"}},{"id":36795,"web_url":"https://patchwork.libcamera.org/comment/36795/","msgid":"<176304118323.52665.7721807271127233399@localhost>","date":"2025-11-13T13:39:43","subject":"Re: [PATCH v2 01/13] libipa: Provide a Quantized data type support","submitter":{"id":184,"url":"https://patchwork.libcamera.org/api/people/184/","name":"Stefan Klug","email":"stefan.klug@ideasonboard.com"},"content":"Hi Kieran,\n\nI was too slow, as you already posted v3. But still I'd like to answer\nyour mail...\n\nQuoting Kieran Bingham (2025-11-12 16:34:14)\n> Quoting Barnabás Pőcze (2025-11-05 09:54:48)\n> > 2025. 11. 05. 10:27 keltezéssel, Stefan Klug írta:\n> > > Hi Kieran,\n> > > \n> > > maybe I was a bit fast on this one...\n> > > \n> > > Quoting Stefan Klug (2025-11-05 09:52:20)\n> > >> Hi Kieran,\n> > >>\n> > >> Thank you for the patch.\n> > >>\n> > >> Quoting Kieran Bingham (2025-10-29 18:24:26)\n> > >>> Frequently when handling data in IPA components we must convert and\n> > >>> store user interface values which may be floating point values, and\n> > >>> perform a specific operation or conversion to quantize this to a\n> > >>> hardware value.\n> > >>>\n> > >>> This value may be to a fixed point type, or more custom code mappings,\n> > >>> but in either case it is important to contain both the required hardware\n> > >>> value, with its effective quantized value.\n> > >>>\n> > >>> Provide a new storage type 'Quantized' which can be defined based on a\n> > >>> set of type specific Traits to perform the conversions between floats\n> > >>> and the underlying hardware type.\n> > >>>\n> > >>> Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>\n> > >>> ---\n> > >>>   src/ipa/libipa/meson.build   |   2 +\n> > >>>   src/ipa/libipa/quantized.cpp | 134 +++++++++++++++++++++++++++++++++++\n> > >>>   src/ipa/libipa/quantized.h   |  79 +++++++++++++++++++++\n> > >>>   3 files changed, 215 insertions(+)\n> > >>>   create mode 100644 src/ipa/libipa/quantized.cpp\n> > >>>   create mode 100644 src/ipa/libipa/quantized.h\n> > >>>\n> > >>> diff --git a/src/ipa/libipa/meson.build b/src/ipa/libipa/meson.build\n> > >>> index 660be94054fa..804289778f72 100644\n> > >>> --- a/src/ipa/libipa/meson.build\n> > >>> +++ b/src/ipa/libipa/meson.build\n> > >>> @@ -17,6 +17,7 @@ libipa_headers = files([\n> > >>>       'lux.h',\n> > >>>       'module.h',\n> > >>>       'pwl.h',\n> > >>> +    'quantized.h',\n> > >>>   ])\n> > >>>   \n> > >>>   libipa_sources = files([\n> > >>> @@ -36,6 +37,7 @@ libipa_sources = files([\n> > >>>       'lux.cpp',\n> > >>>       'module.cpp',\n> > >>>       'pwl.cpp',\n> > >>> +    'quantized.cpp',\n> > >>>   ])\n> > >>>   \n> > >>>   libipa_includes = include_directories('..')\n> > >>> diff --git a/src/ipa/libipa/quantized.cpp b/src/ipa/libipa/quantized.cpp\n> > >>> new file mode 100644\n> > >>> index 000000000000..9772674159bd\n> > >>> --- /dev/null\n> > >>> +++ b/src/ipa/libipa/quantized.cpp\n> > >>> @@ -0,0 +1,134 @@\n> > >>> +/* SPDX-License-Identifier: LGPL-2.1-or-later */\n> > >>> +/*\n> > >>> + * Copyright (C) 2025, Ideas On Board.\n> > >>> + *\n> > >>> + * Helper class to manage conversions between floating point types and quantized\n> > >>> + * storage and representation of those values.\n> > >>> + */\n> > >>> +\n> > >>> +#include \"quantized.h\"\n> > >>> +\n> > >>> +/**\n> > >>> + * \\file quantized.h\n> > >>> + * \\brief Quantized storage and Quantizer representations\n> > >>> + */\n> > >>> +\n> > >>> +namespace libcamera {\n> > >>> +\n> > >>> +namespace ipa {\n> > >>> +\n> > >>> +/**\n> > >>> + * \\struct libcamera::ipa::Quantized\n> > >>> + * \\brief Wrapper that stores a value in both quantized and floating-point form\n> > >>> + * \\tparam Traits The traits class defining the quantization behaviour\n> > >>> + *\n> > >>> + * The Quantized struct template provides a thin wrapper around a quantized\n> > >>> + * representation of a floating-point value. It uses a traits type \\a Traits\n> > >>> + * to define the conversion policy between the floating-point domain and the\n> > >>> + * quantized integer domain.\n> > >>> + *\n> > >>> + * Each Quantized instance maintains two synchronized members:\n> > >>> + *  - the quantized integer representation, and\n> > >>> + *  - the corresponding floating-point value.\n> > >>> + *\n> > >>> + * The traits type defines:\n> > >>> + *  - the integer storage type used for quantization,\n> > >>> + *  - the static conversion functions \\c fromFloat() and \\c toFloat(), and\n> > >>> + *  - optional metadata such as value ranges.\n> > >>> + *\n> > >>> + * Quantized provides convenient constructors and assignment operators from\n> > >>> + * either representation, as well as comparison and string formatting utilities.\n> > >>> + */\n> > >>> +\n> > >>> +/**\n> > >>> + * \\typedef Quantized::TraitsType\n> > >>> + * \\brief The traits policy type defining the quantization behaviour\n> > >>> + *\n> > >>> + * Exposes the associated traits type used by this Quantized instance.\n> > >>> + * This allows external code to refer to constants or metadata defined in\n> > >>> + * the traits, such as \\c TraitsType::min or \\c TraitsType::max.\n> > >>> + */\n> > >>> +\n> > >>> +/**\n> > >>> + * \\typedef Quantized::quantized_type\n> > >>> + * \\brief The integer type used for the quantized representation\n> > >>> + *\n> > >>> + * This alias corresponds to \\c TraitsType::quantized_type, as defined by\n> > >>> + * the traits class.\n> > >>> + */\n> > >>> +\n> > >>> +/**\n> > >>> + * \\fn Quantized::Quantized(float x)\n> > >>> + * \\brief Construct a Quantized value from a floating-point number\n> > >>> + * \\param[in] x The floating-point value to be quantized\n> > >>> + *\n> > >>> + * Converts the floating-point input \\a x to its quantized integer\n> > >>> + * representation using the associated traits policy, and initializes\n> > >>> + * both the quantized and floating-point members.\n> > >>> + */\n> > >>> +\n> > >>> +/**\n> > >>> + * \\fn Quantized::Quantized(quantized_type x)\n> > >>> + * \\brief Construct a Quantized value from an existing quantized integer\n> > >>> + * \\param[in] x The quantized integer value\n> > >>> + *\n> > >>> + * Converts the quantized integer \\a x to its corresponding floating-point\n> > >>> + * value using the traits policy, and initializes both internal members.\n> > >>> + */\n> > >>> +\n> > >>> +/**\n> > >>> + * \\fn Quantized::operator=(float x)\n> > >>> + * \\brief Assign a floating-point value to the Quantized object\n> > >>> + * \\param[in] x The floating-point value to assign\n> > >>> + * \\return A reference to the updated Quantized object\n> > >>> + *\n> > >>> + * Converts the floating-point value \\a x to its quantized integer\n> > >>> + * representation using the traits policy and updates both members.\n> > >>> + */\n> > >>> +\n> > >>> +/**\n> > >>> + * \\fn Quantized::operator=(quantized_type x)\n> > >>> + * \\brief Assign a quantized integer value to the Quantized object\n> > >>> + * \\param[in] x The quantized integer value to assign\n> > >>> + * \\return A reference to the updated Quantized object\n> > >>> + *\n> > >>> + * Converts the quantized integer \\a x to its corresponding floating-point\n> > >>> + * value using the traits policy and updates both members.\n> > >>> + */\n> > >>> +\n> > >>> +/**\n> > >>> + * \\fn Quantized::value() const noexcept\n> > >>> + * \\brief Retrieve the floating-point representation\n> > >>> + * \\return The floating-point value corresponding to the quantized value\n> > >>> + */\n> > >>> +\n> > >>> +/**\n> > >>> + * \\fn Quantized::quantized() const noexcept\n> > >>> + * \\brief Retrieve the quantized integer representation\n> > >>> + * \\return The quantized integer value\n> > >>> + */\n> > >>> +\n> > >>> +/**\n> > >>> + * \\fn Quantized::toString() const\n> > >>> + * \\brief Format the quantized and floating-point values as a string\n> > >>> + * \\return A string containing the hexadecimal quantized value and its\n> > >>> + *         floating-point equivalent.\n> > >>> + */\n> > >>> +\n> > >>> +/**\n> > >>> + * \\fn Quantized::operator==(const Quantized &other) const noexcept\n> > >>> + * \\brief Compare two Quantized objects for equality\n> > >>> + * \\param[in] other The other Quantized object to compare against\n> > >>> + * \\return True if both objects have the same quantized integer value\n> > >>> + */\n> > >>> +\n> > >>> +/**\n> > >>> + * \\fn Quantized::operator!=(const Quantized &other) const noexcept\n> > >>> + * \\brief Compare two Quantized objects for inequality\n> > >>> + * \\param[in] other The other Quantized object to compare against\n> > >>> + * \\return True if the quantized integer values differ\n> > >>> + */\n> > >>> +\n> > >>> +} /* namespace ipa */\n> > >>> +\n> > >>> +} /* namespace libcamera */\n> > >>> diff --git a/src/ipa/libipa/quantized.h b/src/ipa/libipa/quantized.h\n> > >>> new file mode 100644\n> > >>> index 000000000000..26cb8d619e15\n> > >>> --- /dev/null\n> > >>> +++ b/src/ipa/libipa/quantized.h\n> > >>> @@ -0,0 +1,79 @@\n> > >>> +/* SPDX-License-Identifier: LGPL-2.1-or-later */\n> > >>> +/*\n> > >>> + * Copyright (C) 2025, Ideas On Board.\n> > >>\n> > >> micro nit: No dot at the end.\n> \n> Fixed in up both .h and .cpp\n> \n> > >>\n> > >>> + *\n> > >>> + * Helper class to manage conversions between floating point types and quantized\n> > >>> + * storage and representation of those values.\n> > >>> + */\n> > >>> +\n> > >>> +#pragma once\n> > >>> +\n> > >>> +#include <iomanip>\n> > >>> +#include <sstream>\n> > >>> +#include <stdint.h>\n> > >>> +#include <type_traits>\n> > >>> +\n> > >>> +#include <libcamera/base/utils.h>\n> > >>> +\n> > >>> +namespace libcamera {\n> > >>> +\n> > >>> +namespace ipa {\n> > >>> +\n> > >>> +template<typename Traits>\n> > >>> +struct Quantized {\n> > > \n> > > Following the series further down I realized that I'm missing something\n> > > here. The Traits type encapsulates the conversion from float to int. But\n> > > for the sensor gain use case we don't know that conversion when we\n> > > declare the gain in the activeState/frameContext as the conversion is\n> > > solely done at runtime by the camera sensor helper. How is this supposed\n> > > to work?\n> > \n> > One way that can be made to work is to inherit the `Traits` param\n> > and add (at least one) constructor to initialize the base class:\n> > \n> > template<typename Traits>\n> > struct Quantized : Traits {\n> > \n> >    template<typename... Args>\n> >    Quantized(Args&&... args)\n> >      : Traits(std::forward<Args>(args)...)\n> >    { *this = 0.0f; }\n> > };\n> > \n> > everything else can stay basically the same. Although I suppose\n> > some consideration has to be given to operator= and operator==.\n> > \n> > Then one can have e.g.\n> > \n> > struct SensorGainQTraits {\n> > private:\n> >    const CameraSenshorHelper *csh_;\n> > \n> > public:\n> >    using quantized_type = uint32_t;\n> > \n> >    SensorGainQTraits(const CameraSenshorHelper *csh)\n> >      : csh_(csh)\n> >    { }\n> > \n> >    quantized_type fromFloat(float x) const { return csh->gainCode(x); }\n> >    float toFloat(quantized_type x) const { return csh->gain(x); }\n> > };\n> > \n> > Quantized<SensorGainQTraits> q(helper);\n> > \n> > > \n> > > I know you had that idea of \"a quantized value can only be read\". I\n> > > don't see this anymore - what did I miss?\n> \n> I did. But I was asked to change from that model of having a storage\n> type and a processing type to this model which uses traits:\n> \n>  - https://patchwork.libcamera.org/patch/24824/#36477\n> \n> > > \n> > > Best regards,\n> > > Stefan\n> > > \n> > >>> +       using TraitsType = Traits;\n> > >>> +       using quantized_type = typename Traits::quantized_type;\n> > >>> +       static_assert(std::is_arithmetic_v<quantized_type>,\n> > >>> +                     \"Quantized: quantized_type must be arithmetic\");\n> > >>> +\n> > >>> +       /* Constructors */\n> > >>\n> > >> I think this comment can be dropped.\n> > >>\n> > >>> +       Quantized()\n> > >>> +               : Quantized(0.0f) {}\n> > >>> +       Quantized(float x) { *this = x; }\n> > >>> +       Quantized(quantized_type x) { *this = x; }\n> > >>> +\n> > >>> +       Quantized &operator=(float x)\n> > >>> +       {\n> > >>> +               quantized_ = Traits::fromFloat(x);\n> > >>> +               value_ = Traits::toFloat(quantized_);\n> > >>> +               return *this;\n> > >>> +       }\n> > >>> +\n> > >>> +       Quantized &operator=(quantized_type x)\n> > >>> +       {\n> > >>> +               value_ = Traits::toFloat(x);\n> > >>> +               quantized_ = x;\n> > >>> +               return *this;\n> > >>> +       }\n> > >>> +\n> > >>> +       float value() const noexcept { return value_; }\n> > >>> +       quantized_type quantized() const noexcept { return quantized_; }\n> > >>> +\n> > >>> +       std::string toString() const\n> > >>> +       {\n> > >>> +               std::ostringstream oss;\n> > >>> +\n> > >>> +               oss << \"Q:\" << utils::hex(quantized_)\n> > >>> +                   << \" V:\" << value_;\n> > >>> +\n> > >>> +               return oss.str();\n> > >>> +       }\n> > >>> +\n> > >>> +       bool operator==(const Quantized &other) const noexcept\n> > >>> +       {\n> > >>> +               return quantized_ == other.quantized_;\n> > >>> +       }\n> > >>> +\n> > >>> +       bool operator!=(const Quantized &other) const noexcept\n> > >>> +       {\n> > >>> +               return !(*this == other);\n> > >>> +       }\n> > >>> +\n> > >>> +private:\n> > >>> +       quantized_type quantized_{};\n> > >>> +       float value_{};\n> > >>\n> > >> Out of curiosity: Why are the initializers needed here?\n> > >>\n> \n> \n> They're not any more - now there's explicit constructors.\n> I'll remove them.\n> \n> \n> > >> All only nits, so\n> > >> Reviewed-by: Stefan Klug <stefan.klug@ideasonboard.com>\n> \n> Do you think Read Only handling for supporting this type with the Gain\n> Models is a requirement for this series?\n> \n> Would it help to do that now ? Or should it be on top when we look at\n> using something for gain codes?\n\nI'm not so much concerned about readonly or not. My concern is that it\nis not possible to declare a Quantized without passing a Traits which in\ncase of the gain is not known at declaration time. The option to pass in\nthe camera helper at construction time doesn't work well in cases like\nthe FCQueue where you would need to forward the camera helper into the\nconstruction of the FrameContexts. That doesn't feel right. So we would\nallow the default construction of the Traits and fatal out in the\ntoFloat() fromFloat() functions if the CameraSensorHelper is null. That\nthen feels like an abuse of the API as that was the whole purpose of\ncompile time traits. So I don't see how that works properly for the gain\nat the moment - which is my main use case :-/\n\nSo as much as I dislike pushing back, I believe having a option to split\nthe value type and a compatible value+conversion type would be good\nindeed.\n\nBest regards,\nStefan\n\n> \n> --\n> Kieran\n> \n> \n> > >>\n> > >> Best regards,\n> > >> Stefan\n> > >>\n> > >>> +};\n> > >>> +\n> > >>> +} /* namespace ipa */\n> > >>> +\n> > >>> +} /* namespace libcamera */\n> > >>> -- \n> > >>> 2.50.1\n> > >>>\n> >","headers":{"Return-Path":"<libcamera-devel-bounces@lists.libcamera.org>","X-Original-To":"parsemail@patchwork.libcamera.org","Delivered-To":"parsemail@patchwork.libcamera.org","Received":["from lancelot.ideasonboard.com (lancelot.ideasonboard.com\n\t[92.243.16.209])\n\tby patchwork.libcamera.org (Postfix) with ESMTPS id 43D36C3241\n\tfor <parsemail@patchwork.libcamera.org>;\n\tThu, 13 Nov 2025 13:39:48 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 3C08E60A8B;\n\tThu, 13 Nov 2025 14:39:47 +0100 (CET)","from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[213.167.242.64])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 03C4D60805\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tThu, 13 Nov 2025 14:39:45 +0100 (CET)","from ideasonboard.com (unknown\n\t[IPv6:2a00:6020:448c:6c00:652:104a:afdd:5867])\n\tby perceval.ideasonboard.com (Postfix) with UTF8SMTPSA id 9C549446;\n\tThu, 13 Nov 2025 14:37:45 +0100 (CET)"],"Authentication-Results":"lancelot.ideasonboard.com; dkim=pass (1024-bit key;\n\tunprotected) header.d=ideasonboard.com header.i=@ideasonboard.com\n\theader.b=\"WomapDfF\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1763041065;\n\tbh=wdAtA8uqryYJbuLntFqwJOENguCrhO2xITUgLhsASb4=;\n\th=In-Reply-To:References:Subject:From:To:Date:From;\n\tb=WomapDfFL3v8gyr/oQPov5d3r+M07kU+z/mNtwCZKqxHtLLfYYO4phwzvz1lgRHDS\n\tDq0AKNbKblEcx0GI/QDuXK6NF7bHMOYDGf30e/+FS9GBCW0aF9GYYByWXP1+u2NjrA\n\tqkaFAfhTyD3cbnIwKzWXEJeZDIpND7EcTkLPkgFM=","Content-Type":"text/plain; charset=\"utf-8\"","MIME-Version":"1.0","Content-Transfer-Encoding":"quoted-printable","In-Reply-To":"<176296165496.2141792.7751445492347344783@ping.linuxembedded.co.uk>","References":"<20251029172439.1513907-1-kieran.bingham@ideasonboard.com>\n\t<20251029172439.1513907-2-kieran.bingham@ideasonboard.com>\n\t<176233274098.231750.7195627093283025546@localhost>\n\t<176233484083.260669.17716354166087594954@localhost>\n\t<9558ec1a-1556-4726-8300-9d42fdcbdf1d@ideasonboard.com>\n\t<176296165496.2141792.7751445492347344783@ping.linuxembedded.co.uk>","Subject":"Re: [PATCH v2 01/13] libipa: Provide a Quantized data type support","From":"Stefan Klug <stefan.klug@ideasonboard.com>","To":"=?utf-8?q?Barnab=C3=A1s_P=C5=91cze?= <barnabas.pocze@ideasonboard.com>,\n\tKieran Bingham <kieran.bingham@ideasonboard.com>, libcamera devel\n\t<libcamera-devel@lists.libcamera.org>","Date":"Thu, 13 Nov 2025 14:39:43 +0100","Message-ID":"<176304118323.52665.7721807271127233399@localhost>","User-Agent":"alot/0.12.dev8+g2c003385c862.d20250602","X-BeenThere":"libcamera-devel@lists.libcamera.org","X-Mailman-Version":"2.1.29","Precedence":"list","List-Id":"<libcamera-devel.lists.libcamera.org>","List-Unsubscribe":"<https://lists.libcamera.org/options/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=unsubscribe>","List-Archive":"<https://lists.libcamera.org/pipermail/libcamera-devel/>","List-Post":"<mailto:libcamera-devel@lists.libcamera.org>","List-Help":"<mailto:libcamera-devel-request@lists.libcamera.org?subject=help>","List-Subscribe":"<https://lists.libcamera.org/listinfo/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=subscribe>","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"}}]