[{"id":36831,"web_url":"https://patchwork.libcamera.org/comment/36831/","msgid":"<ad1aa837-85eb-42a4-8d15-f93d2235b836@ideasonboard.com>","date":"2025-11-14T18:08:06","subject":"Re: [PATCH v4 02/21] test: libipa: Add tests for Quantized types","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. 11. 14. 1:54 keltezéssel, Kieran Bingham írta:\n> Provide use case tests for the Quantized types to ensure construction\n> and usages are consistent and work as expected.\n> \n> Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>\n> \n> ---\n> v3:\n> - Rename quantized_type to QuantizedType\n> \n>   test/ipa/libipa/meson.build   |   1 +\n>   test/ipa/libipa/quantized.cpp | 130 ++++++++++++++++++++++++++++++++++\n>   2 files changed, 131 insertions(+)\n>   create mode 100644 test/ipa/libipa/quantized.cpp\n> \n> diff --git a/test/ipa/libipa/meson.build b/test/ipa/libipa/meson.build\n> index 2070bed70222..c3e255871f4f 100644\n> --- a/test/ipa/libipa/meson.build\n> +++ b/test/ipa/libipa/meson.build\n> @@ -5,6 +5,7 @@ libipa_test = [\n>       {'name': 'histogram', 'sources': ['histogram.cpp']},\n>       {'name': 'interpolator', 'sources': ['interpolator.cpp']},\n>       {'name': 'pwl', 'sources': ['pwl.cpp'] },\n> +    {'name': 'quantized', 'sources': ['quantized.cpp']},\n>   ]\n>   \n>   foreach test : libipa_test\n> diff --git a/test/ipa/libipa/quantized.cpp b/test/ipa/libipa/quantized.cpp\n> new file mode 100644\n> index 000000000000..3bbd6dd48c14\n> --- /dev/null\n> +++ b/test/ipa/libipa/quantized.cpp\n> @@ -0,0 +1,130 @@\n> +/* SPDX-License-Identifier: GPL-2.0-or-later */\n> +/*\n> + * Copyright (C) 2025, Ideas on Board\n> + *\n> + * Dual Type and Quantizer tests\n> + */\n> +\n> +#include \"../src/ipa/libipa/quantized.h\"\n> +\n> +#include <algorithm>\n> +#include <cmath>\n> +#include <iostream>\n> +#include <map>\n> +#include <stdint.h>\n> +\n> +#include \"test.h\"\n> +\n> +using namespace std;\n> +using namespace libcamera;\n> +using namespace ipa;\n> +\n> +struct BrightnessHueTraits {\n> +\tusing QuantizedType = int8_t;\n> +\tstatic constexpr QuantizedType fromFloat(float v)\n\nstd::lround() is only constexpr since C++23; and `Quantized<>` itself does not\ncall the conversion functions in constexpr contexts, so I would drop it.\n\n\n> +\t{\n> +\t\tint quantized = std::lround(v * 128.0f);\n> +\t\treturn static_cast<int8_t>(std::clamp<int>(quantized, -128, 127));\n> +\t}\n> +\tstatic float toFloat(QuantizedType v)\n> +\t{\n> +\t\treturn static_cast<float>(v) / 128.0f;\n> +\t}\n> +};\n> +\n> +using BrightnessHueQuantizer = Quantized<BrightnessHueTraits>;\n> +\n> +struct ContrastSaturationTraits {\n> +\tusing QuantizedType = uint8_t;\n> +\tstatic constexpr QuantizedType fromFloat(float v)\n> +\t{\n> +\t\tint quantized = std::lround(v * 128.0f);\n> +\t\treturn static_cast<uint8_t>(std::clamp<int>(quantized, 0, 255));\n> +\t}\n> +\tstatic float toFloat(QuantizedType v)\n> +\t{\n> +\t\treturn static_cast<float>(v) / 128.0f;\n> +\t}\n> +};\n> +\n> +using ContrastSaturationQuantizer = Quantized<ContrastSaturationTraits>;\n> +\n> +using BrightnessQ = BrightnessHueQuantizer;\n> +using HueQ = BrightnessHueQuantizer;\n> +using ContrastQ = ContrastSaturationQuantizer;\n> +using SaturationQ = ContrastSaturationQuantizer;\n> +\n> +class QuantizedTest : public Test\n> +{\n> +protected:\n> +\tint run()\n> +\t{\n> +\t\t/* Test construction from float */\n> +\t\t{\n> +\t\t\tBrightnessQ b(0.5f);\n> +\t\t\tif (b.quantized() != 64 || std::abs(b.value() - 0.5f) > 0.01f)\n> +\t\t\t\treturn TestFail;\n> +\t\t}\n> +\n> +\t\t/* Test construction from T */\n> +\t\t{\n> +\t\t\tContrastQ c(uint8_t(128));\n> +\t\t\tif (c.quantized() != 128 || std::abs(c.value() - 1.0f) > 0.01f)\n> +\t\t\t\treturn TestFail;\n> +\t\t}\n> +\n> +\t\t/*\n> +\t\t * Test construction from non-float/non-T type (Expected Fail)\n> +\t\t * These should be a compile-time error if uncommented:\n> +\t\t */\n> +\t\t// BrightnessQ bad1(15); // overloaded ‘Quantized(int)’ is ambiguous\n> +\t\t// BrightnessQ bad2(0xff); // overloaded ‘Quantized(int)’ is ambiguous\n> +\t\t// ContrastQ bad3(0x33); // overloaded ‘Quantized(int)’ is ambiguous\n> +\t\t// ContrastQ bad4(55U); // overloaded ‘Quantized(unsigned int)’ is ambiguous\n\nI think you should be able to do something e.g.\n\n   static_assert(!std::is_constructible_v<BrightnessQ, unsigned int>);\n   static_assert(!std::is_constructible_v<BrightnessQ, double>);\n   // etc.\n\nif you want to test this.\n\n\n> +\n> +\t\t/* Test equality */\n> +\t\t{\n> +\t\t\tBrightnessQ b1(0.5f), b2((int8_t)64);\n> +\t\t\tif (!(b1 == b2))\n> +\t\t\t\treturn TestFail;\n> +\t\t}\n> +\n> +\t\t/* Test inequality */\n> +\t\t{\n> +\t\t\tBrightnessQ b1(0.5f), b2(-0.5f);\n> +\t\t\tif (b1 == b2)\n\nI would do\n\n   if (!(b1 != b2))\n\n\n> +\t\t\t\treturn TestFail;\n> +\t\t}\n> +\n> +\t\t/* Test copying */\n> +\t\t{\n> +\t\t\tBrightnessQ b1(0.25f);\n> +\t\t\tBrightnessQ b2 = b1;\n> +\t\t\tif (!(b1 == b2))\n> +\t\t\t\treturn TestFail;\n> +\t\t}\n> +\n> +\t\t/* Test moving */\n> +\t\t{\n> +\t\t\tBrightnessQ b1(0.25f);\n> +\t\t\tBrightnessQ b2 = std::move(b1); // Allow move semantics\n> +\t\t\tif (b2.value() != 0.25f)\n> +\t\t\t\treturn TestFail;\n> +\t\t}\n> +\n> +\t\t/* Test assignment */\n> +\t\t{\n> +\t\t\tContrastQ c1(1.5f);\n> +\t\t\tContrastQ c2(0.0f);\n> +\t\t\tc2 = c1;\n> +\t\t\tif (!(c1 == c2))\n> +\t\t\t\treturn TestFail;\n> +\t\t}\n> +\n> +\t\tstd::cout << \"Quantised tests passed successfully.\" << std::endl;\n> +\n> +\t\treturn TestPass;\n> +\t}\n> +};\n> +\n> +TEST_REGISTER(QuantizedTest)","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 E13C7C32DB\n\tfor <parsemail@patchwork.libcamera.org>;\n\tFri, 14 Nov 2025 18:08:11 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 3858360A80;\n\tFri, 14 Nov 2025 19:08:10 +0100 (CET)","from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[213.167.242.64])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 931C8606E6\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tFri, 14 Nov 2025 19:08:07 +0100 (CET)","from [192.168.33.35] (185.221.143.100.nat.pool.zt.hu\n\t[185.221.143.100])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id 92F45BD2;\n\tFri, 14 Nov 2025 19:06:06 +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=\"uAUSGbAL\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1763143566;\n\tbh=lHUliZAu6B/epMDNWlZHDWxNe5Ek900Yz+IIqG2Vf3U=;\n\th=Date:Subject:To:References:From:In-Reply-To:From;\n\tb=uAUSGbALczYdDfE59JEv0+bPKBY15SV8D7QSh3yPCm1uC3DryDUd1F6b61T0yxS1o\n\t+2W3rc/rgBTcXoAcwcYEmFkxixsxDoDsc+mFieBCgtqRIJkmruditDY7zkt/14oTeu\n\tgOKXnkx+d1szxwrVpZMvfjN+IqJOgUnKp0FkAxgU=","Message-ID":"<ad1aa837-85eb-42a4-8d15-f93d2235b836@ideasonboard.com>","Date":"Fri, 14 Nov 2025 19:08:06 +0100","MIME-Version":"1.0","User-Agent":"Mozilla Thunderbird","Subject":"Re: [PATCH v4 02/21] test: libipa: Add tests for Quantized types","To":"Kieran Bingham <kieran.bingham@ideasonboard.com>,\n\tlibcamera devel <libcamera-devel@lists.libcamera.org>","References":"<20251114005428.90024-1-kieran.bingham@ideasonboard.com>\n\t<20251114005428.90024-3-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":"<20251114005428.90024-3-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":36842,"web_url":"https://patchwork.libcamera.org/comment/36842/","msgid":"<176314659184.567526.15603111306350607310@ping.linuxembedded.co.uk>","date":"2025-11-14T18:56:31","subject":"Re: [PATCH v4 02/21] test: libipa: Add tests for Quantized types","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-14 18:08:06)\n> Hi\n> \n> 2025. 11. 14. 1:54 keltezéssel, Kieran Bingham írta:\n> > Provide use case tests for the Quantized types to ensure construction\n> > and usages are consistent and work as expected.\n> > \n> > Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>\n> > \n> > ---\n> > v3:\n> > - Rename quantized_type to QuantizedType\n> > \n> >   test/ipa/libipa/meson.build   |   1 +\n> >   test/ipa/libipa/quantized.cpp | 130 ++++++++++++++++++++++++++++++++++\n> >   2 files changed, 131 insertions(+)\n> >   create mode 100644 test/ipa/libipa/quantized.cpp\n> > \n> > diff --git a/test/ipa/libipa/meson.build b/test/ipa/libipa/meson.build\n> > index 2070bed70222..c3e255871f4f 100644\n> > --- a/test/ipa/libipa/meson.build\n> > +++ b/test/ipa/libipa/meson.build\n> > @@ -5,6 +5,7 @@ libipa_test = [\n> >       {'name': 'histogram', 'sources': ['histogram.cpp']},\n> >       {'name': 'interpolator', 'sources': ['interpolator.cpp']},\n> >       {'name': 'pwl', 'sources': ['pwl.cpp'] },\n> > +    {'name': 'quantized', 'sources': ['quantized.cpp']},\n> >   ]\n> >   \n> >   foreach test : libipa_test\n> > diff --git a/test/ipa/libipa/quantized.cpp b/test/ipa/libipa/quantized.cpp\n> > new file mode 100644\n> > index 000000000000..3bbd6dd48c14\n> > --- /dev/null\n> > +++ b/test/ipa/libipa/quantized.cpp\n> > @@ -0,0 +1,130 @@\n> > +/* SPDX-License-Identifier: GPL-2.0-or-later */\n> > +/*\n> > + * Copyright (C) 2025, Ideas on Board\n> > + *\n> > + * Dual Type and Quantizer tests\n> > + */\n> > +\n> > +#include \"../src/ipa/libipa/quantized.h\"\n> > +\n> > +#include <algorithm>\n> > +#include <cmath>\n> > +#include <iostream>\n> > +#include <map>\n> > +#include <stdint.h>\n> > +\n> > +#include \"test.h\"\n> > +\n> > +using namespace std;\n> > +using namespace libcamera;\n> > +using namespace ipa;\n> > +\n> > +struct BrightnessHueTraits {\n> > +     using QuantizedType = int8_t;\n> > +     static constexpr QuantizedType fromFloat(float v)\n> \n> std::lround() is only constexpr since C++23; and `Quantized<>` itself does not\n> call the conversion functions in constexpr contexts, so I would drop it.\n> \n\nAck.\n\n\n> \n> > +     {\n> > +             int quantized = std::lround(v * 128.0f);\n> > +             return static_cast<int8_t>(std::clamp<int>(quantized, -128, 127));\n> > +     }\n> > +     static float toFloat(QuantizedType v)\n> > +     {\n> > +             return static_cast<float>(v) / 128.0f;\n> > +     }\n> > +};\n> > +\n> > +using BrightnessHueQuantizer = Quantized<BrightnessHueTraits>;\n> > +\n> > +struct ContrastSaturationTraits {\n> > +     using QuantizedType = uint8_t;\n> > +     static constexpr QuantizedType fromFloat(float v)\n> > +     {\n> > +             int quantized = std::lround(v * 128.0f);\n> > +             return static_cast<uint8_t>(std::clamp<int>(quantized, 0, 255));\n> > +     }\n> > +     static float toFloat(QuantizedType v)\n> > +     {\n> > +             return static_cast<float>(v) / 128.0f;\n> > +     }\n> > +};\n> > +\n> > +using ContrastSaturationQuantizer = Quantized<ContrastSaturationTraits>;\n> > +\n> > +using BrightnessQ = BrightnessHueQuantizer;\n> > +using HueQ = BrightnessHueQuantizer;\n> > +using ContrastQ = ContrastSaturationQuantizer;\n> > +using SaturationQ = ContrastSaturationQuantizer;\n> > +\n> > +class QuantizedTest : public Test\n> > +{\n> > +protected:\n> > +     int run()\n> > +     {\n> > +             /* Test construction from float */\n> > +             {\n> > +                     BrightnessQ b(0.5f);\n> > +                     if (b.quantized() != 64 || std::abs(b.value() - 0.5f) > 0.01f)\n> > +                             return TestFail;\n> > +             }\n> > +\n> > +             /* Test construction from T */\n> > +             {\n> > +                     ContrastQ c(uint8_t(128));\n> > +                     if (c.quantized() != 128 || std::abs(c.value() - 1.0f) > 0.01f)\n> > +                             return TestFail;\n> > +             }\n> > +\n> > +             /*\n> > +              * Test construction from non-float/non-T type (Expected Fail)\n> > +              * These should be a compile-time error if uncommented:\n> > +              */\n> > +             // BrightnessQ bad1(15); // overloaded ‘Quantized(int)’ is ambiguous\n> > +             // BrightnessQ bad2(0xff); // overloaded ‘Quantized(int)’ is ambiguous\n> > +             // ContrastQ bad3(0x33); // overloaded ‘Quantized(int)’ is ambiguous\n> > +             // ContrastQ bad4(55U); // overloaded ‘Quantized(unsigned int)’ is ambiguous\n> \n> I think you should be able to do something e.g.\n> \n>    static_assert(!std::is_constructible_v<BrightnessQ, unsigned int>);\n>    static_assert(!std::is_constructible_v<BrightnessQ, double>);\n>    // etc.\n> \n> if you want to test this.\n\nInteresting, I didn't know I could do something like that - I'll give it\na go.\n\n\n\n> \n> \n> > +\n> > +             /* Test equality */\n> > +             {\n> > +                     BrightnessQ b1(0.5f), b2((int8_t)64);\n> > +                     if (!(b1 == b2))\n> > +                             return TestFail;\n> > +             }\n> > +\n> > +             /* Test inequality */\n> > +             {\n> > +                     BrightnessQ b1(0.5f), b2(-0.5f);\n> > +                     if (b1 == b2)\n> \n> I would do\n> \n>    if (!(b1 != b2))\n> \n\nack\n\n> \n> > +                             return TestFail;\n> > +             }\n> > +\n> > +             /* Test copying */\n> > +             {\n> > +                     BrightnessQ b1(0.25f);\n> > +                     BrightnessQ b2 = b1;\n> > +                     if (!(b1 == b2))\n> > +                             return TestFail;\n> > +             }\n> > +\n> > +             /* Test moving */\n> > +             {\n> > +                     BrightnessQ b1(0.25f);\n> > +                     BrightnessQ b2 = std::move(b1); // Allow move semantics\n> > +                     if (b2.value() != 0.25f)\n> > +                             return TestFail;\n> > +             }\n> > +\n> > +             /* Test assignment */\n> > +             {\n> > +                     ContrastQ c1(1.5f);\n> > +                     ContrastQ c2(0.0f);\n> > +                     c2 = c1;\n> > +                     if (!(c1 == c2))\n> > +                             return TestFail;\n> > +             }\n> > +\n> > +             std::cout << \"Quantised tests passed successfully.\" << std::endl;\n> > +\n> > +             return TestPass;\n> > +     }\n> > +};\n> > +\n> > +TEST_REGISTER(QuantizedTest)\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 8940CC3263\n\tfor <parsemail@patchwork.libcamera.org>;\n\tFri, 14 Nov 2025 18:56:37 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id E0556609D8;\n\tFri, 14 Nov 2025 19:56: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 EF2BF606E6\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tFri, 14 Nov 2025 19:56:34 +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 C7592169;\n\tFri, 14 Nov 2025 19:54:33 +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=\"nTrhYqJu\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1763146473;\n\tbh=4LUuxJBb7Q/hsodyX9tChc7wXVMNGrArjRMSYxFuzXs=;\n\th=In-Reply-To:References:Subject:From:To:Date:From;\n\tb=nTrhYqJu4HmPt9/7ITjFiV+cPHKimpGuyupEw5lo4ZRaJd3AAWourXby+CMMFs3Ft\n\t+/bB7IDbVt8h8jZY8TGcbjoUZp4WUS1KI899tIH7zDOOqSmzbfN4PsDEFtu625wix/\n\t09T4xm1urv6vq4YHerlWOrBe5Y5M0gErhn7zzHtk=","Content-Type":"text/plain; charset=\"utf-8\"","MIME-Version":"1.0","Content-Transfer-Encoding":"quoted-printable","In-Reply-To":"<ad1aa837-85eb-42a4-8d15-f93d2235b836@ideasonboard.com>","References":"<20251114005428.90024-1-kieran.bingham@ideasonboard.com>\n\t<20251114005428.90024-3-kieran.bingham@ideasonboard.com>\n\t<ad1aa837-85eb-42a4-8d15-f93d2235b836@ideasonboard.com>","Subject":"Re: [PATCH v4 02/21] test: libipa: Add tests for Quantized types","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":"Fri, 14 Nov 2025 18:56:31 +0000","Message-ID":"<176314659184.567526.15603111306350607310@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":36868,"web_url":"https://patchwork.libcamera.org/comment/36868/","msgid":"<176346943923.880260.4666256785214585453@isaac-ThinkPad-T16-Gen-2>","date":"2025-11-18T12:37:19","subject":"Re: [PATCH v4 02/21] test: libipa: Add tests for Quantized types","submitter":{"id":215,"url":"https://patchwork.libcamera.org/api/people/215/","name":"Isaac Scott","email":"isaac.scott@ideasonboard.com"},"content":"Hi Kieran,\n\nThank you for the patch!\n\nReviewed-by: Isaac Scott <isaac.scott@ideasonboard.com>\n\nQuoting Kieran Bingham (2025-11-14 00:54:06)\n> Provide use case tests for the Quantized types to ensure construction\n> and usages are consistent and work as expected.\n> \n> Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>\n> \n> ---\n> v3:\n> - Rename quantized_type to QuantizedType\n> \n>  test/ipa/libipa/meson.build   |   1 +\n>  test/ipa/libipa/quantized.cpp | 130 ++++++++++++++++++++++++++++++++++\n>  2 files changed, 131 insertions(+)\n>  create mode 100644 test/ipa/libipa/quantized.cpp\n> \n> diff --git a/test/ipa/libipa/meson.build b/test/ipa/libipa/meson.build\n> index 2070bed70222..c3e255871f4f 100644\n> --- a/test/ipa/libipa/meson.build\n> +++ b/test/ipa/libipa/meson.build\n> @@ -5,6 +5,7 @@ libipa_test = [\n>      {'name': 'histogram', 'sources': ['histogram.cpp']},\n>      {'name': 'interpolator', 'sources': ['interpolator.cpp']},\n>      {'name': 'pwl', 'sources': ['pwl.cpp'] },\n> +    {'name': 'quantized', 'sources': ['quantized.cpp']},\n>  ]\n>  \n>  foreach test : libipa_test\n> diff --git a/test/ipa/libipa/quantized.cpp b/test/ipa/libipa/quantized.cpp\n> new file mode 100644\n> index 000000000000..3bbd6dd48c14\n> --- /dev/null\n> +++ b/test/ipa/libipa/quantized.cpp\n> @@ -0,0 +1,130 @@\n> +/* SPDX-License-Identifier: GPL-2.0-or-later */\n> +/*\n> + * Copyright (C) 2025, Ideas on Board\n> + *\n> + * Dual Type and Quantizer tests\n> + */\n> +\n> +#include \"../src/ipa/libipa/quantized.h\"\n> +\n> +#include <algorithm>\n> +#include <cmath>\n> +#include <iostream>\n> +#include <map>\n> +#include <stdint.h>\n> +\n> +#include \"test.h\"\n> +\n> +using namespace std;\n> +using namespace libcamera;\n> +using namespace ipa;\n> +\n> +struct BrightnessHueTraits {\n> +       using QuantizedType = int8_t;\n> +       static constexpr QuantizedType fromFloat(float v)\n> +       {\n> +               int quantized = std::lround(v * 128.0f);\n> +               return static_cast<int8_t>(std::clamp<int>(quantized, -128, 127));\n> +       }\n> +       static float toFloat(QuantizedType v)\n> +       {\n> +               return static_cast<float>(v) / 128.0f;\n> +       }\n> +};\n> +\n> +using BrightnessHueQuantizer = Quantized<BrightnessHueTraits>;\n> +\n> +struct ContrastSaturationTraits {\n> +       using QuantizedType = uint8_t;\n> +       static constexpr QuantizedType fromFloat(float v)\n> +       {\n> +               int quantized = std::lround(v * 128.0f);\n> +               return static_cast<uint8_t>(std::clamp<int>(quantized, 0, 255));\n> +       }\n> +       static float toFloat(QuantizedType v)\n> +       {\n> +               return static_cast<float>(v) / 128.0f;\n> +       }\n> +};\n> +\n> +using ContrastSaturationQuantizer = Quantized<ContrastSaturationTraits>;\n> +\n> +using BrightnessQ = BrightnessHueQuantizer;\n> +using HueQ = BrightnessHueQuantizer;\n> +using ContrastQ = ContrastSaturationQuantizer;\n> +using SaturationQ = ContrastSaturationQuantizer;\n> +\n> +class QuantizedTest : public Test\n> +{\n> +protected:\n> +       int run()\n> +       {\n> +               /* Test construction from float */\n> +               {\n> +                       BrightnessQ b(0.5f);\n> +                       if (b.quantized() != 64 || std::abs(b.value() - 0.5f) > 0.01f)\n> +                               return TestFail;\n> +               }\n> +\n> +               /* Test construction from T */\n> +               {\n> +                       ContrastQ c(uint8_t(128));\n> +                       if (c.quantized() != 128 || std::abs(c.value() - 1.0f) > 0.01f)\n> +                               return TestFail;\n> +               }\n> +\n> +               /*\n> +                * Test construction from non-float/non-T type (Expected Fail)\n> +                * These should be a compile-time error if uncommented:\n> +                */\n> +               // BrightnessQ bad1(15); // overloaded ‘Quantized(int)’ is ambiguous\n> +               // BrightnessQ bad2(0xff); // overloaded ‘Quantized(int)’ is ambiguous\n> +               // ContrastQ bad3(0x33); // overloaded ‘Quantized(int)’ is ambiguous\n> +               // ContrastQ bad4(55U); // overloaded ‘Quantized(unsigned int)’ is ambiguous\n> +\n> +               /* Test equality */\n> +               {\n> +                       BrightnessQ b1(0.5f), b2((int8_t)64);\n> +                       if (!(b1 == b2))\n> +                               return TestFail;\n> +               }\n> +\n> +               /* Test inequality */\n> +               {\n> +                       BrightnessQ b1(0.5f), b2(-0.5f);\n> +                       if (b1 == b2)\n> +                               return TestFail;\n> +               }\n> +\n> +               /* Test copying */\n> +               {\n> +                       BrightnessQ b1(0.25f);\n> +                       BrightnessQ b2 = b1;\n> +                       if (!(b1 == b2))\n> +                               return TestFail;\n> +               }\n> +\n> +               /* Test moving */\n> +               {\n> +                       BrightnessQ b1(0.25f);\n> +                       BrightnessQ b2 = std::move(b1); // Allow move semantics\n> +                       if (b2.value() != 0.25f)\n> +                               return TestFail;\n> +               }\n> +\n> +               /* Test assignment */\n> +               {\n> +                       ContrastQ c1(1.5f);\n> +                       ContrastQ c2(0.0f);\n> +                       c2 = c1;\n> +                       if (!(c1 == c2))\n> +                               return TestFail;\n> +               }\n> +\n> +               std::cout << \"Quantised tests passed successfully.\" << std::endl;\n> +\n> +               return TestPass;\n> +       }\n> +};\n> +\n> +TEST_REGISTER(QuantizedTest)\n> -- \n> 2.51.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 D2C3DBD80A\n\tfor <parsemail@patchwork.libcamera.org>;\n\tTue, 18 Nov 2025 12:37:23 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 7D06060A80;\n\tTue, 18 Nov 2025 13:37:23 +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 C5D2A606D5\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tTue, 18 Nov 2025 13:37:21 +0100 (CET)","from thinkpad.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 0B888D52;\n\tTue, 18 Nov 2025 13:35: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=\"Cjyjlwrk\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1763469318;\n\tbh=0J9RSLxLlbFCapCQY6HpoWi1FgL53vixWnBKwJSpmDA=;\n\th=In-Reply-To:References:Subject:From:Cc:To:Date:From;\n\tb=CjyjlwrkDd0Sjo+y5Mq9wGBO25Ik7wPCHTI7MUDKarvjX6KCxQ8PxafBt/erwjYwh\n\tgWCi2SaFZsmK+erIWURHcCSoJ9EOuRPCKYnBzWLFbPtVGHgvv2/Iu0Bo91qwTTj6cp\n\tX5HsRRDzoq+lCuCbeqQIYBss/WSVZm2ScLE7wQgg=","Content-Type":"text/plain; charset=\"utf-8\"","MIME-Version":"1.0","Content-Transfer-Encoding":"quoted-printable","In-Reply-To":"<20251114005428.90024-3-kieran.bingham@ideasonboard.com>","References":"<20251114005428.90024-1-kieran.bingham@ideasonboard.com>\n\t<20251114005428.90024-3-kieran.bingham@ideasonboard.com>","Subject":"Re: [PATCH v4 02/21] test: libipa: Add tests for Quantized types","From":"Isaac Scott <isaac.scott@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":"Tue, 18 Nov 2025 12:37:19 +0000","Message-ID":"<176346943923.880260.4666256785214585453@isaac-ThinkPad-T16-Gen-2>","User-Agent":"alot/0.10","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":36894,"web_url":"https://patchwork.libcamera.org/comment/36894/","msgid":"<20251119042335.GM10711@pendragon.ideasonboard.com>","date":"2025-11-19T04:23:35","subject":"Re: [PATCH v4 02/21] test: libipa: Add tests for Quantized types","submitter":{"id":2,"url":"https://patchwork.libcamera.org/api/people/2/","name":"Laurent Pinchart","email":"laurent.pinchart@ideasonboard.com"},"content":"On Fri, Nov 14, 2025 at 12:54:06AM +0000, Kieran Bingham wrote:\n> Provide use case tests for the Quantized types to ensure construction\n> and usages are consistent and work as expected.\n> \n> Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>\n> \n> ---\n> v3:\n> - Rename quantized_type to QuantizedType\n> \n>  test/ipa/libipa/meson.build   |   1 +\n>  test/ipa/libipa/quantized.cpp | 130 ++++++++++++++++++++++++++++++++++\n>  2 files changed, 131 insertions(+)\n>  create mode 100644 test/ipa/libipa/quantized.cpp\n> \n> diff --git a/test/ipa/libipa/meson.build b/test/ipa/libipa/meson.build\n> index 2070bed70222..c3e255871f4f 100644\n> --- a/test/ipa/libipa/meson.build\n> +++ b/test/ipa/libipa/meson.build\n> @@ -5,6 +5,7 @@ libipa_test = [\n>      {'name': 'histogram', 'sources': ['histogram.cpp']},\n>      {'name': 'interpolator', 'sources': ['interpolator.cpp']},\n>      {'name': 'pwl', 'sources': ['pwl.cpp'] },\n> +    {'name': 'quantized', 'sources': ['quantized.cpp']},\n>  ]\n>  \n>  foreach test : libipa_test\n> diff --git a/test/ipa/libipa/quantized.cpp b/test/ipa/libipa/quantized.cpp\n> new file mode 100644\n> index 000000000000..3bbd6dd48c14\n> --- /dev/null\n> +++ b/test/ipa/libipa/quantized.cpp\n> @@ -0,0 +1,130 @@\n> +/* SPDX-License-Identifier: GPL-2.0-or-later */\n> +/*\n> + * Copyright (C) 2025, Ideas on Board\n> + *\n> + * Dual Type and Quantizer tests\n> + */\n> +\n> +#include \"../src/ipa/libipa/quantized.h\"\n> +\n> +#include <algorithm>\n> +#include <cmath>\n> +#include <iostream>\n> +#include <map>\n> +#include <stdint.h>\n> +\n> +#include \"test.h\"\n> +\n> +using namespace std;\n> +using namespace libcamera;\n> +using namespace ipa;\n> +\n> +struct BrightnessHueTraits {\n> +\tusing QuantizedType = int8_t;\n> +\tstatic constexpr QuantizedType fromFloat(float v)\n> +\t{\n> +\t\tint quantized = std::lround(v * 128.0f);\n> +\t\treturn static_cast<int8_t>(std::clamp<int>(quantized, -128, 127));\n> +\t}\n> +\tstatic float toFloat(QuantizedType v)\n> +\t{\n> +\t\treturn static_cast<float>(v) / 128.0f;\n> +\t}\n> +};\n> +\n> +using BrightnessHueQuantizer = Quantized<BrightnessHueTraits>;\n> +\n> +struct ContrastSaturationTraits {\n> +\tusing QuantizedType = uint8_t;\n> +\tstatic constexpr QuantizedType fromFloat(float v)\n> +\t{\n> +\t\tint quantized = std::lround(v * 128.0f);\n> +\t\treturn static_cast<uint8_t>(std::clamp<int>(quantized, 0, 255));\n> +\t}\n> +\tstatic float toFloat(QuantizedType v)\n> +\t{\n> +\t\treturn static_cast<float>(v) / 128.0f;\n> +\t}\n> +};\n> +\n> +using ContrastSaturationQuantizer = Quantized<ContrastSaturationTraits>;\n> +\n> +using BrightnessQ = BrightnessHueQuantizer;\n> +using HueQ = BrightnessHueQuantizer;\n> +using ContrastQ = ContrastSaturationQuantizer;\n> +using SaturationQ = ContrastSaturationQuantizer;\n> +\n> +class QuantizedTest : public Test\n> +{\n> +protected:\n> +\tint run()\n> +\t{\n> +\t\t/* Test construction from float */\n> +\t\t{\n> +\t\t\tBrightnessQ b(0.5f);\n> +\t\t\tif (b.quantized() != 64 || std::abs(b.value() - 0.5f) > 0.01f)\n> +\t\t\t\treturn TestFail;\n> +\t\t}\n> +\n> +\t\t/* Test construction from T */\n> +\t\t{\n> +\t\t\tContrastQ c(uint8_t(128));\n> +\t\t\tif (c.quantized() != 128 || std::abs(c.value() - 1.0f) > 0.01f)\n> +\t\t\t\treturn TestFail;\n> +\t\t}\n> +\n> +\t\t/*\n> +\t\t * Test construction from non-float/non-T type (Expected Fail)\n> +\t\t * These should be a compile-time error if uncommented:\n> +\t\t */\n> +\t\t// BrightnessQ bad1(15); // overloaded ‘Quantized(int)’ is ambiguous\n> +\t\t// BrightnessQ bad2(0xff); // overloaded ‘Quantized(int)’ is ambiguous\n> +\t\t// ContrastQ bad3(0x33); // overloaded ‘Quantized(int)’ is ambiguous\n> +\t\t// ContrastQ bad4(55U); // overloaded ‘Quantized(unsigned int)’ is ambiguous\n\nC-style comments.\n\n> +\n> +\t\t/* Test equality */\n> +\t\t{\n> +\t\t\tBrightnessQ b1(0.5f), b2((int8_t)64);\n> +\t\t\tif (!(b1 == b2))\n> +\t\t\t\treturn TestFail;\n> +\t\t}\n> +\n> +\t\t/* Test inequality */\n> +\t\t{\n> +\t\t\tBrightnessQ b1(0.5f), b2(-0.5f);\n> +\t\t\tif (b1 == b2)\n> +\t\t\t\treturn TestFail;\n> +\t\t}\n> +\n> +\t\t/* Test copying */\n> +\t\t{\n> +\t\t\tBrightnessQ b1(0.25f);\n> +\t\t\tBrightnessQ b2 = b1;\n> +\t\t\tif (!(b1 == b2))\n> +\t\t\t\treturn TestFail;\n> +\t\t}\n> +\n> +\t\t/* Test moving */\n> +\t\t{\n> +\t\t\tBrightnessQ b1(0.25f);\n> +\t\t\tBrightnessQ b2 = std::move(b1); // Allow move semantics\n\nC-style comments.\n\n> +\t\t\tif (b2.value() != 0.25f)\n> +\t\t\t\treturn TestFail;\n> +\t\t}\n\nThere are no move constructors or assignment operators in the class, I'd\ndrop this.\n\n> +\n> +\t\t/* Test assignment */\n> +\t\t{\n> +\t\t\tContrastQ c1(1.5f);\n> +\t\t\tContrastQ c2(0.0f);\n> +\t\t\tc2 = c1;\n> +\t\t\tif (!(c1 == c2))\n> +\t\t\t\treturn TestFail;\n> +\t\t}\n> +\n> +\t\tstd::cout << \"Quantised tests passed successfully.\" << std::endl;\n> +\n> +\t\treturn TestPass;\n> +\t}\n> +};\n> +\n> +TEST_REGISTER(QuantizedTest)","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 BA00FBD80A\n\tfor <parsemail@patchwork.libcamera.org>;\n\tWed, 19 Nov 2025 04:24:16 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id E7DC0609D8;\n\tWed, 19 Nov 2025 05:24:15 +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 CD9F360856\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tWed, 19 Nov 2025 05:24:13 +0100 (CET)","from pendragon.ideasonboard.com (unknown [205.220.129.225])\n\tby perceval.ideasonboard.com (Postfix) with UTF8SMTPSA id 64AB714B0; \n\tWed, 19 Nov 2025 05:22:04 +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=\"HRH4lgpO\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1763526129;\n\tbh=CMHUa8pSgZlxbdQe6TUyRSJPgtTV5BLbUB950J+xkg4=;\n\th=Date:From:To:Cc:Subject:References:In-Reply-To:From;\n\tb=HRH4lgpOwCl0QqaoDOUca44EFY4fiFLS/2Se/nBOaAY3OVsZeb90RbW+JpcDg6k93\n\tTtdeyYj2BpPBpJZkCXDAbB9ZiZk4lxxbFocMn7AlcYwyHLXtOzQRAwKhm69MYvbf2m\n\ty5iNHZWiDQtzDzgbzNh6/6+4fk7p2T70yzK2nbzg=","Date":"Wed, 19 Nov 2025 13:23:35 +0900","From":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>","To":"Kieran Bingham <kieran.bingham@ideasonboard.com>","Cc":"libcamera devel <libcamera-devel@lists.libcamera.org>","Subject":"Re: [PATCH v4 02/21] test: libipa: Add tests for Quantized types","Message-ID":"<20251119042335.GM10711@pendragon.ideasonboard.com>","References":"<20251114005428.90024-1-kieran.bingham@ideasonboard.com>\n\t<20251114005428.90024-3-kieran.bingham@ideasonboard.com>","MIME-Version":"1.0","Content-Type":"text/plain; charset=utf-8","Content-Disposition":"inline","Content-Transfer-Encoding":"8bit","In-Reply-To":"<20251114005428.90024-3-kieran.bingham@ideasonboard.com>","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":36915,"web_url":"https://patchwork.libcamera.org/comment/36915/","msgid":"<176354514650.567526.16917202169868336140@ping.linuxembedded.co.uk>","date":"2025-11-19T09:39:06","subject":"Re: [PATCH v4 02/21] test: libipa: Add tests for Quantized types","submitter":{"id":4,"url":"https://patchwork.libcamera.org/api/people/4/","name":"Kieran Bingham","email":"kieran.bingham@ideasonboard.com"},"content":"Quoting Laurent Pinchart (2025-11-19 04:23:35)\n> On Fri, Nov 14, 2025 at 12:54:06AM +0000, Kieran Bingham wrote:\n> > Provide use case tests for the Quantized types to ensure construction\n> > and usages are consistent and work as expected.\n> > \n> > Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>\n> > \n> > ---\n> > v3:\n> > - Rename quantized_type to QuantizedType\n> > \n> >  test/ipa/libipa/meson.build   |   1 +\n> >  test/ipa/libipa/quantized.cpp | 130 ++++++++++++++++++++++++++++++++++\n> >  2 files changed, 131 insertions(+)\n> >  create mode 100644 test/ipa/libipa/quantized.cpp\n> > \n> > diff --git a/test/ipa/libipa/meson.build b/test/ipa/libipa/meson.build\n> > index 2070bed70222..c3e255871f4f 100644\n> > --- a/test/ipa/libipa/meson.build\n> > +++ b/test/ipa/libipa/meson.build\n> > @@ -5,6 +5,7 @@ libipa_test = [\n> >      {'name': 'histogram', 'sources': ['histogram.cpp']},\n> >      {'name': 'interpolator', 'sources': ['interpolator.cpp']},\n> >      {'name': 'pwl', 'sources': ['pwl.cpp'] },\n> > +    {'name': 'quantized', 'sources': ['quantized.cpp']},\n> >  ]\n> >  \n> >  foreach test : libipa_test\n> > diff --git a/test/ipa/libipa/quantized.cpp b/test/ipa/libipa/quantized.cpp\n> > new file mode 100644\n> > index 000000000000..3bbd6dd48c14\n> > --- /dev/null\n> > +++ b/test/ipa/libipa/quantized.cpp\n> > @@ -0,0 +1,130 @@\n> > +/* SPDX-License-Identifier: GPL-2.0-or-later */\n> > +/*\n> > + * Copyright (C) 2025, Ideas on Board\n> > + *\n> > + * Dual Type and Quantizer tests\n> > + */\n> > +\n> > +#include \"../src/ipa/libipa/quantized.h\"\n> > +\n> > +#include <algorithm>\n> > +#include <cmath>\n> > +#include <iostream>\n> > +#include <map>\n> > +#include <stdint.h>\n> > +\n> > +#include \"test.h\"\n> > +\n> > +using namespace std;\n> > +using namespace libcamera;\n> > +using namespace ipa;\n> > +\n> > +struct BrightnessHueTraits {\n> > +     using QuantizedType = int8_t;\n> > +     static constexpr QuantizedType fromFloat(float v)\n> > +     {\n> > +             int quantized = std::lround(v * 128.0f);\n> > +             return static_cast<int8_t>(std::clamp<int>(quantized, -128, 127));\n> > +     }\n> > +     static float toFloat(QuantizedType v)\n> > +     {\n> > +             return static_cast<float>(v) / 128.0f;\n> > +     }\n> > +};\n> > +\n> > +using BrightnessHueQuantizer = Quantized<BrightnessHueTraits>;\n> > +\n> > +struct ContrastSaturationTraits {\n> > +     using QuantizedType = uint8_t;\n> > +     static constexpr QuantizedType fromFloat(float v)\n> > +     {\n> > +             int quantized = std::lround(v * 128.0f);\n> > +             return static_cast<uint8_t>(std::clamp<int>(quantized, 0, 255));\n> > +     }\n> > +     static float toFloat(QuantizedType v)\n> > +     {\n> > +             return static_cast<float>(v) / 128.0f;\n> > +     }\n> > +};\n> > +\n> > +using ContrastSaturationQuantizer = Quantized<ContrastSaturationTraits>;\n> > +\n> > +using BrightnessQ = BrightnessHueQuantizer;\n> > +using HueQ = BrightnessHueQuantizer;\n> > +using ContrastQ = ContrastSaturationQuantizer;\n> > +using SaturationQ = ContrastSaturationQuantizer;\n> > +\n> > +class QuantizedTest : public Test\n> > +{\n> > +protected:\n> > +     int run()\n> > +     {\n> > +             /* Test construction from float */\n> > +             {\n> > +                     BrightnessQ b(0.5f);\n> > +                     if (b.quantized() != 64 || std::abs(b.value() - 0.5f) > 0.01f)\n> > +                             return TestFail;\n> > +             }\n> > +\n> > +             /* Test construction from T */\n> > +             {\n> > +                     ContrastQ c(uint8_t(128));\n> > +                     if (c.quantized() != 128 || std::abs(c.value() - 1.0f) > 0.01f)\n> > +                             return TestFail;\n> > +             }\n> > +\n> > +             /*\n> > +              * Test construction from non-float/non-T type (Expected Fail)\n> > +              * These should be a compile-time error if uncommented:\n> > +              */\n> > +             // BrightnessQ bad1(15); // overloaded ‘Quantized(int)’ is ambiguous\n> > +             // BrightnessQ bad2(0xff); // overloaded ‘Quantized(int)’ is ambiguous\n> > +             // ContrastQ bad3(0x33); // overloaded ‘Quantized(int)’ is ambiguous\n> > +             // ContrastQ bad4(55U); // overloaded ‘Quantized(unsigned int)’ is ambiguous\n> \n> C-style comments.\n\nThis was intentional to allow easy 'uncommenting for test'.\nOf course that doesn't apply to the text after which is the actual\ncomment.\n\nBut anyway, thanks to Barnabas' proposal these are removed so there's no\nissue here anymore.\n\n\n> \n> > +\n> > +             /* Test equality */\n> > +             {\n> > +                     BrightnessQ b1(0.5f), b2((int8_t)64);\n> > +                     if (!(b1 == b2))\n> > +                             return TestFail;\n> > +             }\n> > +\n> > +             /* Test inequality */\n> > +             {\n> > +                     BrightnessQ b1(0.5f), b2(-0.5f);\n> > +                     if (b1 == b2)\n> > +                             return TestFail;\n> > +             }\n> > +\n> > +             /* Test copying */\n> > +             {\n> > +                     BrightnessQ b1(0.25f);\n> > +                     BrightnessQ b2 = b1;\n> > +                     if (!(b1 == b2))\n> > +                             return TestFail;\n> > +             }\n> > +\n> > +             /* Test moving */\n> > +             {\n> > +                     BrightnessQ b1(0.25f);\n> > +                     BrightnessQ b2 = std::move(b1); // Allow move semantics\n> \n> C-style comments.\n> \n> > +                     if (b2.value() != 0.25f)\n> > +                             return TestFail;\n> > +             }\n> \n> There are no move constructors or assignment operators in the class, I'd\n> drop this.\n\nOk.\n\n> > +\n> > +             /* Test assignment */\n> > +             {\n> > +                     ContrastQ c1(1.5f);\n> > +                     ContrastQ c2(0.0f);\n> > +                     c2 = c1;\n> > +                     if (!(c1 == c2))\n> > +                             return TestFail;\n> > +             }\n> > +\n> > +             std::cout << \"Quantised tests passed successfully.\" << std::endl;\n> > +\n> > +             return TestPass;\n> > +     }\n> > +};\n> > +\n> > +TEST_REGISTER(QuantizedTest)\n> \n> -- \n> Regards,\n> \n> Laurent Pinchart","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 9BDB5BD80A\n\tfor <parsemail@patchwork.libcamera.org>;\n\tWed, 19 Nov 2025 09:39:12 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id C48B560A8B;\n\tWed, 19 Nov 2025 10:39:11 +0100 (CET)","from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[213.167.242.64])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 8279460805\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tWed, 19 Nov 2025 10:39:09 +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 D9813EB7;\n\tWed, 19 Nov 2025 10:37:04 +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=\"ewtygaxb\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1763545024;\n\tbh=I7bPVe0HUEEVv/WknP96neHjvaQbv+Cy2QPuZMA/XXM=;\n\th=In-Reply-To:References:Subject:From:Cc:To:Date:From;\n\tb=ewtygaxbpz/3D2glDPOHBxpkSIk1SsUtgfkCjtwAxoH1tMOgi8cXJVYeph8ZUz+2B\n\t/qx+Drkav/agI5hU7MzkEDhnA3mRvtuPs87Zet++NcXeu++KtRud99N1xmvS5pJKBl\n\tAD4BX1ehfXxtH8z16FYMyCWRvSFGfNGqHarde4NI=","Content-Type":"text/plain; charset=\"utf-8\"","MIME-Version":"1.0","Content-Transfer-Encoding":"quoted-printable","In-Reply-To":"<20251119042335.GM10711@pendragon.ideasonboard.com>","References":"<20251114005428.90024-1-kieran.bingham@ideasonboard.com>\n\t<20251114005428.90024-3-kieran.bingham@ideasonboard.com>\n\t<20251119042335.GM10711@pendragon.ideasonboard.com>","Subject":"Re: [PATCH v4 02/21] test: libipa: Add tests for Quantized types","From":"Kieran Bingham <kieran.bingham@ideasonboard.com>","Cc":"libcamera devel <libcamera-devel@lists.libcamera.org>","To":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>","Date":"Wed, 19 Nov 2025 09:39:06 +0000","Message-ID":"<176354514650.567526.16917202169868336140@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>"}}]