| Message ID | 20260114173918.1744023-3-kieran.bingham@ideasonboard.com |
|---|---|
| State | Superseded |
| Headers | show |
| Series |
|
| Related | show |
2026. 01. 14. 18:39 keltezéssel, Kieran Bingham írta: > Provide use case tests for the Quantized types to ensure construction > and usages are consistent and work as expected. > > Reviewed-by: Isaac Scott <isaac.scott@ideasonboard.com> > Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com> > > --- > v3: > - Rename quantized_type to QuantizedType > > v5: > - use static asserts for constructible failure tests > - Remove constexpr from lround users (only possible in C++23) > - Remove move tests > - Fix up inequality test > > Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com> > --- > test/ipa/libipa/meson.build | 1 + > test/ipa/libipa/quantized.cpp | 124 ++++++++++++++++++++++++++++++++++ > 2 files changed, 125 insertions(+) > create mode 100644 test/ipa/libipa/quantized.cpp > > diff --git a/test/ipa/libipa/meson.build b/test/ipa/libipa/meson.build > index 2070bed70222..c3e255871f4f 100644 > --- a/test/ipa/libipa/meson.build > +++ b/test/ipa/libipa/meson.build > @@ -5,6 +5,7 @@ libipa_test = [ > {'name': 'histogram', 'sources': ['histogram.cpp']}, > {'name': 'interpolator', 'sources': ['interpolator.cpp']}, > {'name': 'pwl', 'sources': ['pwl.cpp'] }, > + {'name': 'quantized', 'sources': ['quantized.cpp']}, > ] > > foreach test : libipa_test > diff --git a/test/ipa/libipa/quantized.cpp b/test/ipa/libipa/quantized.cpp > new file mode 100644 > index 000000000000..6b6a61311125 > --- /dev/null > +++ b/test/ipa/libipa/quantized.cpp > @@ -0,0 +1,124 @@ > +/* SPDX-License-Identifier: GPL-2.0-or-later */ > +/* > + * Copyright (C) 2025, Ideas on Board > + * > + * Dual Type and Quantizer tests > + */ > + > +#include "../src/ipa/libipa/quantized.h" > + > +#include <algorithm> > +#include <cmath> > +#include <iostream> > +#include <map> > +#include <stdint.h> > + > +#include "test.h" > + > +using namespace std; > +using namespace libcamera; > +using namespace ipa; > + > +struct BrightnessHueTraits { > + using QuantizedType = int8_t; > + static QuantizedType fromFloat(float v) > + { > + int quantized = std::lround(v * 128.0f); > + return static_cast<int8_t>(std::clamp<int>(quantized, -128, 127)); I don't think there is a need for the static cast. (Same later.) > + } > + static float toFloat(QuantizedType v) > + { > + return static_cast<float>(v) / 128.0f; > + } > +}; > + > +using BrightnessHueQuantizer = Quantized<BrightnessHueTraits>; > + > +struct ContrastSaturationTraits { > + using QuantizedType = uint8_t; > + static QuantizedType fromFloat(float v) > + { > + int quantized = std::lround(v * 128.0f); > + return static_cast<uint8_t>(std::clamp<int>(quantized, 0, 255)); > + } > + static float toFloat(QuantizedType v) > + { > + return static_cast<float>(v) / 128.0f; > + } > +}; > + > +using ContrastSaturationQuantizer = Quantized<ContrastSaturationTraits>; > + > +using BrightnessQ = BrightnessHueQuantizer; > +using HueQ = BrightnessHueQuantizer; > +using ContrastQ = ContrastSaturationQuantizer; > +using SaturationQ = ContrastSaturationQuantizer; > + > +class QuantizedTest : public Test > +{ > +protected: > + int run() > + { > + /* Test construction from float */ > + { > + BrightnessQ b(0.5f); > + if (b.quantized() != 64 || std::abs(b.value() - 0.5f) > 0.01f) > + return TestFail; > + } > + > + /* Test construction from T */ > + { > + ContrastQ c(uint8_t(128)); > + if (c.quantized() != 128 || std::abs(c.value() - 1.0f) > 0.01f) > + return TestFail; > + } > + > + /* > + * Only construction from the exact storage type or a float > + * is permitted. > + */ > + static_assert(!std::is_constructible_v<BrightnessQ, unsigned int>); > + static_assert(!std::is_constructible_v<BrightnessQ, uint8_t>); > + static_assert(!std::is_constructible_v<BrightnessQ, double>); > + static_assert(!std::is_constructible_v<ContrastQ, int>); > + static_assert(!std::is_constructible_v<ContrastQ, int8_t>); > + static_assert(!std::is_constructible_v<ContrastQ, unsigned int>); > + > + /* Test equality */ > + { > + BrightnessQ b1(0.5f), b2((int8_t)64); int8_t(64) looks better in my opinion. > + if (!(b1 == b2)) > + return TestFail; > + } > + > + /* Test inequality */ > + { > + BrightnessQ b1(0.5f), b2(-0.5f); > + if (!(b1 != b2)) > + return TestFail; > + } > + > + /* Test copying */ > + { > + BrightnessQ b1(0.25f); > + BrightnessQ b2 = b1; > + if (!(b1 == b2)) > + return TestFail; > + } > + > + /* Test assignment */ > + { > + ContrastQ c1(1.5f); > + ContrastQ c2(0.0f); > + c2 = c1; > + if (!(c1 == c2)) > + return TestFail; > + } Another test case that I think would be good to have: construction from two different float values that map to the same quantized value, but that quantized value corresponds to a third float value. Then both the quantized and float values should be tested for equality. > + > + std::cout << "Quantised tests passed successfully." << std::endl; > + > + return TestPass; > + } > +}; > + > +TEST_REGISTER(QuantizedTest)
Quoting Barnabás Pőcze (2026-01-15 15:49:35) > 2026. 01. 14. 18:39 keltezéssel, Kieran Bingham írta: > > Provide use case tests for the Quantized types to ensure construction > > and usages are consistent and work as expected. > > > > Reviewed-by: Isaac Scott <isaac.scott@ideasonboard.com> > > Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com> > > > > --- > > v3: > > - Rename quantized_type to QuantizedType > > > > v5: > > - use static asserts for constructible failure tests > > - Remove constexpr from lround users (only possible in C++23) > > - Remove move tests > > - Fix up inequality test > > > > Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com> > > --- > > test/ipa/libipa/meson.build | 1 + > > test/ipa/libipa/quantized.cpp | 124 ++++++++++++++++++++++++++++++++++ > > 2 files changed, 125 insertions(+) > > create mode 100644 test/ipa/libipa/quantized.cpp > > > > diff --git a/test/ipa/libipa/meson.build b/test/ipa/libipa/meson.build > > index 2070bed70222..c3e255871f4f 100644 > > --- a/test/ipa/libipa/meson.build > > +++ b/test/ipa/libipa/meson.build > > @@ -5,6 +5,7 @@ libipa_test = [ > > {'name': 'histogram', 'sources': ['histogram.cpp']}, > > {'name': 'interpolator', 'sources': ['interpolator.cpp']}, > > {'name': 'pwl', 'sources': ['pwl.cpp'] }, > > + {'name': 'quantized', 'sources': ['quantized.cpp']}, > > ] > > > > foreach test : libipa_test > > diff --git a/test/ipa/libipa/quantized.cpp b/test/ipa/libipa/quantized.cpp > > new file mode 100644 > > index 000000000000..6b6a61311125 > > --- /dev/null > > +++ b/test/ipa/libipa/quantized.cpp > > @@ -0,0 +1,124 @@ > > +/* SPDX-License-Identifier: GPL-2.0-or-later */ > > +/* > > + * Copyright (C) 2025, Ideas on Board > > + * > > + * Dual Type and Quantizer tests > > + */ > > + > > +#include "../src/ipa/libipa/quantized.h" > > + > > +#include <algorithm> > > +#include <cmath> > > +#include <iostream> > > +#include <map> > > +#include <stdint.h> > > + > > +#include "test.h" > > + > > +using namespace std; > > +using namespace libcamera; > > +using namespace ipa; > > + > > +struct BrightnessHueTraits { > > + using QuantizedType = int8_t; > > + static QuantizedType fromFloat(float v) > > + { > > + int quantized = std::lround(v * 128.0f); > > + return static_cast<int8_t>(std::clamp<int>(quantized, -128, 127)); > > I don't think there is a need for the static cast. (Same later.) Removed both. > > + } > > + static float toFloat(QuantizedType v) > > + { > > + return static_cast<float>(v) / 128.0f; > > + } > > +}; > > + > > +using BrightnessHueQuantizer = Quantized<BrightnessHueTraits>; > > + > > +struct ContrastSaturationTraits { > > + using QuantizedType = uint8_t; > > + static QuantizedType fromFloat(float v) > > + { > > + int quantized = std::lround(v * 128.0f); > > + return static_cast<uint8_t>(std::clamp<int>(quantized, 0, 255)); > > + } > > + static float toFloat(QuantizedType v) > > + { > > + return static_cast<float>(v) / 128.0f; > > + } > > +}; > > + > > +using ContrastSaturationQuantizer = Quantized<ContrastSaturationTraits>; > > + > > +using BrightnessQ = BrightnessHueQuantizer; > > +using HueQ = BrightnessHueQuantizer; > > +using ContrastQ = ContrastSaturationQuantizer; > > +using SaturationQ = ContrastSaturationQuantizer; > > + > > +class QuantizedTest : public Test > > +{ > > +protected: > > + int run() > > + { > > + /* Test construction from float */ > > + { > > + BrightnessQ b(0.5f); > > + if (b.quantized() != 64 || std::abs(b.value() - 0.5f) > 0.01f) > > + return TestFail; > > + } > > + > > + /* Test construction from T */ > > + { > > + ContrastQ c(uint8_t(128)); > > + if (c.quantized() != 128 || std::abs(c.value() - 1.0f) > 0.01f) > > + return TestFail; > > + } > > + > > + /* > > + * Only construction from the exact storage type or a float > > + * is permitted. > > + */ > > + static_assert(!std::is_constructible_v<BrightnessQ, unsigned int>); > > + static_assert(!std::is_constructible_v<BrightnessQ, uint8_t>); > > + static_assert(!std::is_constructible_v<BrightnessQ, double>); > > + static_assert(!std::is_constructible_v<ContrastQ, int>); > > + static_assert(!std::is_constructible_v<ContrastQ, int8_t>); > > + static_assert(!std::is_constructible_v<ContrastQ, unsigned int>); > > + > > + /* Test equality */ > > + { > > + BrightnessQ b1(0.5f), b2((int8_t)64); > > int8_t(64) > > looks better in my opinion. Fixed. > > > > + if (!(b1 == b2)) > > + return TestFail; > > + } > > + > > + /* Test inequality */ > > + { > > + BrightnessQ b1(0.5f), b2(-0.5f); > > + if (!(b1 != b2)) > > + return TestFail; > > + } > > + > > + /* Test copying */ > > + { > > + BrightnessQ b1(0.25f); > > + BrightnessQ b2 = b1; > > + if (!(b1 == b2)) > > + return TestFail; > > + } > > + > > + /* Test assignment */ > > + { > > + ContrastQ c1(1.5f); > > + ContrastQ c2(0.0f); > > + c2 = c1; > > + if (!(c1 == c2)) > > + return TestFail; > > + } > > Another test case that I think would be good to have: construction from two > different float values that map to the same quantized value, but that quantized > value corresponds to a third float value. Then both the quantized and float > values should be tested for equality. Is this what you had in mind? /* * Test construction from different floats mapping to same * quantized value */ { /* Two floats that have the same quantized value. */ const float f1 = 1.007f; const float f2 = 1.008f; ContrastQ c1(f1); ContrastQ c2(f2); std::cout << " Checking " << f1 << " and " << f2 << " both map to " << c1 << std::endl; std::cout << " c1: " << c1 << std::endl; std::cout << " c2: " << c2 << std::endl; /* Quantized values must match */ if (!(c1.quantized() == c2.quantized())) return TestFail; /* Float values must now match */ if (!(c1.value() == c2.value())) return TestFail; if (!(c1 == c2)) return TestFail; } test/ipa/libipa/quantized Checking 1.007 and 1.008 both map to [0x81:1.00781] c1: [0x81:1.00781] c2: [0x81:1.00781] Quantised tests passed successfully. (I'll remove the Checking prints for the patch) -- Kieran > > > > > + > > + std::cout << "Quantised tests passed successfully." << std::endl; > > + > > + return TestPass; > > + } > > +}; > > + > > +TEST_REGISTER(QuantizedTest) >
2026. 01. 19. 18:07 keltezéssel, Kieran Bingham írta: > Quoting Barnabás Pőcze (2026-01-15 15:49:35) >> 2026. 01. 14. 18:39 keltezéssel, Kieran Bingham írta: >>> Provide use case tests for the Quantized types to ensure construction >>> and usages are consistent and work as expected. >>> >>> Reviewed-by: Isaac Scott <isaac.scott@ideasonboard.com> >>> Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com> >>> >>> --- >>> v3: >>> - Rename quantized_type to QuantizedType >>> >>> v5: >>> - use static asserts for constructible failure tests >>> - Remove constexpr from lround users (only possible in C++23) >>> - Remove move tests >>> - Fix up inequality test >>> >>> Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com> >>> --- >>> test/ipa/libipa/meson.build | 1 + >>> test/ipa/libipa/quantized.cpp | 124 ++++++++++++++++++++++++++++++++++ >>> 2 files changed, 125 insertions(+) >>> create mode 100644 test/ipa/libipa/quantized.cpp >>> >>> diff --git a/test/ipa/libipa/meson.build b/test/ipa/libipa/meson.build >>> index 2070bed70222..c3e255871f4f 100644 >>> --- a/test/ipa/libipa/meson.build >>> +++ b/test/ipa/libipa/meson.build >>> @@ -5,6 +5,7 @@ libipa_test = [ >>> {'name': 'histogram', 'sources': ['histogram.cpp']}, >>> {'name': 'interpolator', 'sources': ['interpolator.cpp']}, >>> {'name': 'pwl', 'sources': ['pwl.cpp'] }, >>> + {'name': 'quantized', 'sources': ['quantized.cpp']}, >>> ] >>> >>> foreach test : libipa_test >>> diff --git a/test/ipa/libipa/quantized.cpp b/test/ipa/libipa/quantized.cpp >>> new file mode 100644 >>> index 000000000000..6b6a61311125 >>> --- /dev/null >>> +++ b/test/ipa/libipa/quantized.cpp >>> @@ -0,0 +1,124 @@ >>> +/* SPDX-License-Identifier: GPL-2.0-or-later */ >>> +/* >>> + * Copyright (C) 2025, Ideas on Board >>> + * >>> + * Dual Type and Quantizer tests >>> + */ >>> + >>> +#include "../src/ipa/libipa/quantized.h" >>> + >>> +#include <algorithm> >>> +#include <cmath> >>> +#include <iostream> >>> +#include <map> >>> +#include <stdint.h> >>> + >>> +#include "test.h" >>> + >>> +using namespace std; >>> +using namespace libcamera; >>> +using namespace ipa; >>> + >>> +struct BrightnessHueTraits { >>> + using QuantizedType = int8_t; >>> + static QuantizedType fromFloat(float v) >>> + { >>> + int quantized = std::lround(v * 128.0f); >>> + return static_cast<int8_t>(std::clamp<int>(quantized, -128, 127)); >> >> I don't think there is a need for the static cast. (Same later.) > > Removed both. > >>> + } >>> + static float toFloat(QuantizedType v) >>> + { >>> + return static_cast<float>(v) / 128.0f; >>> + } >>> +}; >>> + >>> +using BrightnessHueQuantizer = Quantized<BrightnessHueTraits>; >>> + >>> +struct ContrastSaturationTraits { >>> + using QuantizedType = uint8_t; >>> + static QuantizedType fromFloat(float v) >>> + { >>> + int quantized = std::lround(v * 128.0f); >>> + return static_cast<uint8_t>(std::clamp<int>(quantized, 0, 255)); >>> + } >>> + static float toFloat(QuantizedType v) >>> + { >>> + return static_cast<float>(v) / 128.0f; >>> + } >>> +}; >>> + >>> +using ContrastSaturationQuantizer = Quantized<ContrastSaturationTraits>; >>> + >>> +using BrightnessQ = BrightnessHueQuantizer; >>> +using HueQ = BrightnessHueQuantizer; >>> +using ContrastQ = ContrastSaturationQuantizer; >>> +using SaturationQ = ContrastSaturationQuantizer; >>> + >>> +class QuantizedTest : public Test >>> +{ >>> +protected: >>> + int run() >>> + { >>> + /* Test construction from float */ >>> + { >>> + BrightnessQ b(0.5f); >>> + if (b.quantized() != 64 || std::abs(b.value() - 0.5f) > 0.01f) >>> + return TestFail; >>> + } >>> + >>> + /* Test construction from T */ >>> + { >>> + ContrastQ c(uint8_t(128)); >>> + if (c.quantized() != 128 || std::abs(c.value() - 1.0f) > 0.01f) >>> + return TestFail; >>> + } >>> + >>> + /* >>> + * Only construction from the exact storage type or a float >>> + * is permitted. >>> + */ >>> + static_assert(!std::is_constructible_v<BrightnessQ, unsigned int>); >>> + static_assert(!std::is_constructible_v<BrightnessQ, uint8_t>); >>> + static_assert(!std::is_constructible_v<BrightnessQ, double>); >>> + static_assert(!std::is_constructible_v<ContrastQ, int>); >>> + static_assert(!std::is_constructible_v<ContrastQ, int8_t>); >>> + static_assert(!std::is_constructible_v<ContrastQ, unsigned int>); >>> + >>> + /* Test equality */ >>> + { >>> + BrightnessQ b1(0.5f), b2((int8_t)64); >> >> int8_t(64) >> >> looks better in my opinion. > > Fixed. > >> >> >>> + if (!(b1 == b2)) >>> + return TestFail; >>> + } >>> + >>> + /* Test inequality */ >>> + { >>> + BrightnessQ b1(0.5f), b2(-0.5f); >>> + if (!(b1 != b2)) >>> + return TestFail; >>> + } >>> + >>> + /* Test copying */ >>> + { >>> + BrightnessQ b1(0.25f); >>> + BrightnessQ b2 = b1; >>> + if (!(b1 == b2)) >>> + return TestFail; >>> + } >>> + >>> + /* Test assignment */ >>> + { >>> + ContrastQ c1(1.5f); >>> + ContrastQ c2(0.0f); >>> + c2 = c1; >>> + if (!(c1 == c2)) >>> + return TestFail; >>> + } >> >> Another test case that I think would be good to have: construction from two >> different float values that map to the same quantized value, but that quantized >> value corresponds to a third float value. Then both the quantized and float >> values should be tested for equality. > > Is this what you had in mind? Yes, something like this, thanks. > > /* > * Test construction from different floats mapping to same > * quantized value > */ > { > /* Two floats that have the same quantized value. */ > const float f1 = 1.007f; > const float f2 = 1.008f; > > ContrastQ c1(f1); > ContrastQ c2(f2); > > std::cout << " Checking " << f1 << " and " << f2 > << " both map to " << c1 << std::endl; > std::cout << " c1: " << c1 << std::endl; > std::cout << " c2: " << c2 << std::endl; > > /* Quantized values must match */ > if (!(c1.quantized() == c2.quantized())) > return TestFail; > > /* Float values must now match */ > if (!(c1.value() == c2.value())) > return TestFail; > > if (!(c1 == c2)) > return TestFail; > } > > > test/ipa/libipa/quantized > Checking 1.007 and 1.008 both map to [0x81:1.00781] > c1: [0x81:1.00781] > c2: [0x81:1.00781] > Quantised tests passed successfully. > > (I'll remove the Checking prints for the patch) > > -- > Kieran > > >> >> >> >>> + >>> + std::cout << "Quantised tests passed successfully." << std::endl; >>> + >>> + return TestPass; >>> + } >>> +}; >>> + >>> +TEST_REGISTER(QuantizedTest) >>
diff --git a/test/ipa/libipa/meson.build b/test/ipa/libipa/meson.build index 2070bed70222..c3e255871f4f 100644 --- a/test/ipa/libipa/meson.build +++ b/test/ipa/libipa/meson.build @@ -5,6 +5,7 @@ libipa_test = [ {'name': 'histogram', 'sources': ['histogram.cpp']}, {'name': 'interpolator', 'sources': ['interpolator.cpp']}, {'name': 'pwl', 'sources': ['pwl.cpp'] }, + {'name': 'quantized', 'sources': ['quantized.cpp']}, ] foreach test : libipa_test diff --git a/test/ipa/libipa/quantized.cpp b/test/ipa/libipa/quantized.cpp new file mode 100644 index 000000000000..6b6a61311125 --- /dev/null +++ b/test/ipa/libipa/quantized.cpp @@ -0,0 +1,124 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2025, Ideas on Board + * + * Dual Type and Quantizer tests + */ + +#include "../src/ipa/libipa/quantized.h" + +#include <algorithm> +#include <cmath> +#include <iostream> +#include <map> +#include <stdint.h> + +#include "test.h" + +using namespace std; +using namespace libcamera; +using namespace ipa; + +struct BrightnessHueTraits { + using QuantizedType = int8_t; + static QuantizedType fromFloat(float v) + { + int quantized = std::lround(v * 128.0f); + return static_cast<int8_t>(std::clamp<int>(quantized, -128, 127)); + } + static float toFloat(QuantizedType v) + { + return static_cast<float>(v) / 128.0f; + } +}; + +using BrightnessHueQuantizer = Quantized<BrightnessHueTraits>; + +struct ContrastSaturationTraits { + using QuantizedType = uint8_t; + static QuantizedType fromFloat(float v) + { + int quantized = std::lround(v * 128.0f); + return static_cast<uint8_t>(std::clamp<int>(quantized, 0, 255)); + } + static float toFloat(QuantizedType v) + { + return static_cast<float>(v) / 128.0f; + } +}; + +using ContrastSaturationQuantizer = Quantized<ContrastSaturationTraits>; + +using BrightnessQ = BrightnessHueQuantizer; +using HueQ = BrightnessHueQuantizer; +using ContrastQ = ContrastSaturationQuantizer; +using SaturationQ = ContrastSaturationQuantizer; + +class QuantizedTest : public Test +{ +protected: + int run() + { + /* Test construction from float */ + { + BrightnessQ b(0.5f); + if (b.quantized() != 64 || std::abs(b.value() - 0.5f) > 0.01f) + return TestFail; + } + + /* Test construction from T */ + { + ContrastQ c(uint8_t(128)); + if (c.quantized() != 128 || std::abs(c.value() - 1.0f) > 0.01f) + return TestFail; + } + + /* + * Only construction from the exact storage type or a float + * is permitted. + */ + static_assert(!std::is_constructible_v<BrightnessQ, unsigned int>); + static_assert(!std::is_constructible_v<BrightnessQ, uint8_t>); + static_assert(!std::is_constructible_v<BrightnessQ, double>); + static_assert(!std::is_constructible_v<ContrastQ, int>); + static_assert(!std::is_constructible_v<ContrastQ, int8_t>); + static_assert(!std::is_constructible_v<ContrastQ, unsigned int>); + + /* Test equality */ + { + BrightnessQ b1(0.5f), b2((int8_t)64); + if (!(b1 == b2)) + return TestFail; + } + + /* Test inequality */ + { + BrightnessQ b1(0.5f), b2(-0.5f); + if (!(b1 != b2)) + return TestFail; + } + + /* Test copying */ + { + BrightnessQ b1(0.25f); + BrightnessQ b2 = b1; + if (!(b1 == b2)) + return TestFail; + } + + /* Test assignment */ + { + ContrastQ c1(1.5f); + ContrastQ c2(0.0f); + c2 = c1; + if (!(c1 == c2)) + return TestFail; + } + + std::cout << "Quantised tests passed successfully." << std::endl; + + return TestPass; + } +}; + +TEST_REGISTER(QuantizedTest)