libjxl

FORK: libjxl patches used on blog
git clone https://git.neptards.moe/blog/libjxl.git
Log | Files | Refs | Submodules | README | LICENSE

splines_test.cc (14670B)


      1 // Copyright (c) the JPEG XL Project Authors. All rights reserved.
      2 //
      3 // Use of this source code is governed by a BSD-style
      4 // license that can be found in the LICENSE file.
      5 
      6 #include "lib/jxl/splines.h"
      7 
      8 #include <jxl/cms.h>
      9 
     10 #include <cstddef>
     11 #include <cstdint>
     12 #include <cstdio>
     13 #include <ostream>
     14 #include <utility>
     15 #include <vector>
     16 
     17 #include "lib/extras/codec.h"
     18 #include "lib/jxl/base/common.h"
     19 #include "lib/jxl/base/compiler_specific.h"
     20 #include "lib/jxl/base/printf_macros.h"
     21 #include "lib/jxl/base/span.h"
     22 #include "lib/jxl/base/status.h"
     23 #include "lib/jxl/chroma_from_luma.h"
     24 #include "lib/jxl/enc_aux_out.h"
     25 #include "lib/jxl/enc_bit_writer.h"
     26 #include "lib/jxl/enc_splines.h"
     27 #include "lib/jxl/image.h"
     28 #include "lib/jxl/image_ops.h"
     29 #include "lib/jxl/image_test_utils.h"
     30 #include "lib/jxl/test_utils.h"
     31 #include "lib/jxl/testing.h"
     32 
     33 namespace jxl {
     34 
     35 std::ostream& operator<<(std::ostream& os, const Spline::Point& p) {
     36   return os << "(" << p.x << ", " << p.y << ")";
     37 }
     38 
     39 std::ostream& operator<<(std::ostream& os, const Spline& spline) {
     40   return os << "(spline with " << spline.control_points.size()
     41             << " control points)";
     42 }
     43 
     44 namespace {
     45 
     46 using test::ReadTestData;
     47 using ::testing::AllOf;
     48 using ::testing::Field;
     49 using ::testing::FloatNear;
     50 using ::testing::Pointwise;
     51 
     52 constexpr int kQuantizationAdjustment = 0;
     53 const ColorCorrelationMap* const cmap = new ColorCorrelationMap;
     54 const float kYToX = cmap->YtoXRatio(0);
     55 const float kYToB = cmap->YtoBRatio(0);
     56 
     57 constexpr float kTolerance = 0.003125;
     58 
     59 std::vector<Spline> DequantizeSplines(const Splines& splines) {
     60   const auto& quantized_splines = splines.QuantizedSplines();
     61   const auto& starting_points = splines.StartingPoints();
     62   JXL_CHECK(quantized_splines.size() == starting_points.size());
     63 
     64   std::vector<Spline> dequantized;
     65   uint64_t total = 0;
     66   for (size_t i = 0; i < quantized_splines.size(); ++i) {
     67     dequantized.emplace_back();
     68     JXL_CHECK(quantized_splines[i].Dequantize(
     69         starting_points[i], kQuantizationAdjustment, kYToX, kYToB, 2u << 30u,
     70         &total, dequantized.back()));
     71   }
     72   return dequantized;
     73 }
     74 
     75 MATCHER(ControlPointIs, "") {
     76   const Spline::Point& actual = std::get<0>(arg);
     77   const Spline::Point& expected = std::get<1>(arg);
     78   return testing::ExplainMatchResult(
     79       AllOf(Field(&Spline::Point::x, FloatNear(expected.x, kTolerance)),
     80             Field(&Spline::Point::y, FloatNear(expected.y, kTolerance))),
     81       actual, result_listener);
     82 }
     83 
     84 MATCHER(ControlPointsMatch, "") {
     85   const Spline& actual = std::get<0>(arg);
     86   const Spline& expected = std::get<1>(arg);
     87   return testing::ExplainMatchResult(
     88       Field(&Spline::control_points,
     89             Pointwise(ControlPointIs(), expected.control_points)),
     90       actual, result_listener);
     91 }
     92 
     93 MATCHER(SplinesMatch, "") {
     94   const Spline& actual = std::get<0>(arg);
     95   const Spline& expected = std::get<1>(arg);
     96   if (!testing::ExplainMatchResult(ControlPointsMatch(), arg,
     97                                    result_listener)) {
     98     return false;
     99   }
    100   for (int i = 0; i < 3; ++i) {
    101     size_t color_dct_size =
    102         sizeof(expected.color_dct[i]) / sizeof(expected.color_dct[i][0]);
    103     for (size_t j = 0; j < color_dct_size; j++) {
    104       testing::StringMatchResultListener color_dct_listener;
    105       if (!testing::ExplainMatchResult(
    106               FloatNear(expected.color_dct[i][j], kTolerance),
    107               actual.color_dct[i][j], &color_dct_listener)) {
    108         *result_listener << ", where color_dct[" << i << "][" << j
    109                          << "] don't match, " << color_dct_listener.str();
    110         return false;
    111       }
    112     }
    113   }
    114   size_t sigma_dct_size =
    115       sizeof(expected.sigma_dct) / sizeof(expected.sigma_dct[0]);
    116   for (size_t i = 0; i < sigma_dct_size; i++) {
    117     testing::StringMatchResultListener sigma_listener;
    118     if (!testing::ExplainMatchResult(
    119             FloatNear(expected.sigma_dct[i], kTolerance), actual.sigma_dct[i],
    120             &sigma_listener)) {
    121       *result_listener << ", where sigma_dct[" << i << "] don't match, "
    122                        << sigma_listener.str();
    123       return false;
    124     }
    125   }
    126   return true;
    127 }
    128 
    129 }  // namespace
    130 
    131 TEST(SplinesTest, Serialization) {
    132   std::vector<Spline> spline_data = {
    133       {/*control_points=*/{
    134            {109, 54}, {218, 159}, {80, 3}, {110, 274}, {94, 185}, {17, 277}},
    135        /*color_dct=*/
    136        {{36.3, 39.7, 23.2, 67.5, 4.4,  71.5, 62.3, 32.3, 92.2, 10.1, 10.8,
    137          9.2,  6.1,  10.5, 79.1, 7,    24.6, 90.8, 5.5,  84,   43.8, 49,
    138          33.5, 78.9, 54.5, 77.9, 62.1, 51.4, 36.4, 14.3, 83.7, 35.4},
    139         {9.4,  53.4, 9.5,  74.9, 72.7, 26.7, 7.9,  0.9, 84.9, 23.2, 26.5,
    140          31.1, 91,   11.7, 74.1, 39.3, 23.7, 82.5, 4.8, 2.7,  61.2, 96.4,
    141          13.7, 66.7, 62.9, 82.4, 5.9,  98.7, 21.5, 7.9, 51.7, 63.1},
    142         {48,   39.3, 6.9,  26.3, 33.3, 6.2,  1.7,  98.9, 59.9, 59.6, 95,
    143          61.3, 82.7, 53,   6.1,  30.4, 34.7, 96.9, 93.4, 17,   38.8, 80.8,
    144          63,   18.6, 43.6, 32.3, 61,   20.2, 24.3, 28.3, 69.1, 62.4}},
    145        /*sigma_dct=*/{32.7, 21.5, 44.4, 1.8,  45.8, 90.6, 29.3, 59.2,
    146                       23.7, 85.2, 84.8, 27.2, 42.1, 84.1, 50.6, 17.6,
    147                       93.7, 4.9,  2.6,  69.8, 94.9, 52,   24.3, 18.8,
    148                       12.1, 95.7, 28.5, 81.4, 89.9, 31.4, 74.8, 52}},
    149       {/*control_points=*/{{172, 309},
    150                            {196, 277},
    151                            {42, 238},
    152                            {114, 350},
    153                            {307, 290},
    154                            {316, 269},
    155                            {124, 66},
    156                            {233, 267}},
    157        /*color_dct=*/
    158        {{15,   28.9, 22, 6.6,  41.8, 83,   8.6,  56.8, 68.9, 9.7,  5.4,
    159          19.8, 70.8, 90, 52.5, 65.2, 7.8,  23.5, 26.4, 72.2, 64.7, 87.1,
    160          1.3,  67.5, 46, 68.4, 65.4, 35.5, 29.1, 13,   41.6, 23.9},
    161         {47.7, 79.4, 62.7, 29.1, 96.8, 18.5, 17.6, 15.2, 80.5, 56,  96.2,
    162          59.9, 26.7, 96.1, 92.3, 42.1, 35.8, 54,   23.2, 55,   76,  35.8,
    163          58.4, 88.7, 2.4,  78.1, 95.6, 27.5, 6.6,  78.5, 24.1, 69.8},
    164         {43.8, 96.5, 0.9,  95.1, 49.1, 71.2, 25.1, 33.6, 75.2, 95,  82.1,
    165          19.7, 10.5, 44.9, 50,   93.3, 83.5, 99.5, 64.6, 54,   3.5, 99.7,
    166          45.3, 82.1, 22.4, 37.9, 60,   32.2, 12.6, 4.6,  65.5, 96.4}},
    167        /*sigma_dct=*/{72.5, 2.6,  41.7, 2.2,  39.7, 79.1, 69.6, 19.9,
    168                       92.3, 71.5, 41.9, 62.1, 30,   49.4, 70.3, 45.3,
    169                       62.5, 47.2, 46.7, 41.2, 90.8, 46.8, 91.2, 55,
    170                       8.1,  69.6, 25.4, 84.7, 61.7, 27.6, 3.7,  46.9}},
    171       {/*control_points=*/{{100, 186},
    172                            {257, 97},
    173                            {170, 49},
    174                            {25, 169},
    175                            {309, 104},
    176                            {232, 237},
    177                            {385, 101},
    178                            {122, 168},
    179                            {26, 300},
    180                            {390, 88}},
    181        /*color_dct=*/
    182        {{16.9, 64.8, 4.2,  10.6, 23.5, 17,   79.3, 5.7,  60.4, 16.6, 94.9,
    183          63.7, 87.6, 10.5, 3.8,  61.1, 22.9, 81.9, 80.4, 40.5, 45.9, 25.4,
    184          39.8, 30,   50.2, 90.4, 27.9, 93.7, 65.1, 48.2, 22.3, 43.9},
    185         {24.9, 66,   3.5,  90.2, 97.1, 15.8, 35.6, 0.6,  68,   39.6, 24.4,
    186          85.9, 57.7, 77.6, 47.5, 67.9, 4.3,  5.4,  91.2, 58.5, 0.1,  52.2,
    187          3.5,  47.8, 63.2, 43.5, 85.8, 35.8, 50.2, 35.9, 19.2, 48.2},
    188         {82.8, 44.9, 76.4, 39.5, 94.1, 14.3, 89.8, 10,   10.5, 74.5, 56.3,
    189          65.8, 7.8,  23.3, 52.8, 99.3, 56.8, 46,   76.7, 13.5, 67,   22.4,
    190          29.9, 43.3, 70.3, 26,   74.3, 53.9, 62,   19.1, 49.3, 46.7}},
    191        /*sigma_dct=*/{83.5, 1.7,  25.1, 18.7, 46.5, 75.3, 28,   62.3,
    192                       50.3, 23.3, 85.6, 96,   45.8, 33.1, 33.4, 52.9,
    193                       26.3, 58.5, 19.6, 70,   92.6, 22.5, 57,   21.6,
    194                       76.8, 87.5, 22.9, 66.3, 35.7, 35.6, 56.8, 67.2}},
    195   };
    196 
    197   std::vector<QuantizedSpline> quantized_splines;
    198   std::vector<Spline::Point> starting_points;
    199   for (const Spline& spline : spline_data) {
    200     quantized_splines.emplace_back(spline, kQuantizationAdjustment, kYToX,
    201                                    kYToB);
    202     starting_points.push_back(spline.control_points.front());
    203   }
    204 
    205   Splines splines(kQuantizationAdjustment, std::move(quantized_splines),
    206                   std::move(starting_points));
    207   const std::vector<Spline> quantized_spline_data = DequantizeSplines(splines);
    208   EXPECT_THAT(quantized_spline_data,
    209               Pointwise(ControlPointsMatch(), spline_data));
    210 
    211   BitWriter writer;
    212   EncodeSplines(splines, &writer, kLayerSplines, HistogramParams(), nullptr);
    213   writer.ZeroPadToByte();
    214   const size_t bits_written = writer.BitsWritten();
    215 
    216   printf("Wrote %" PRIuS " bits of splines.\n", bits_written);
    217 
    218   BitReader reader(writer.GetSpan());
    219   Splines decoded_splines;
    220   ASSERT_TRUE(decoded_splines.Decode(&reader, /*num_pixels=*/1000));
    221   ASSERT_TRUE(reader.JumpToByteBoundary());
    222   EXPECT_EQ(reader.TotalBitsConsumed(), bits_written);
    223   ASSERT_TRUE(reader.Close());
    224 
    225   const std::vector<Spline> decoded_spline_data =
    226       DequantizeSplines(decoded_splines);
    227   EXPECT_THAT(decoded_spline_data,
    228               Pointwise(SplinesMatch(), quantized_spline_data));
    229 }
    230 
    231 #ifdef JXL_CRASH_ON_ERROR
    232 TEST(SplinesTest, DISABLED_TooManySplinesTest) {
    233 #else
    234 TEST(SplinesTest, TooManySplinesTest) {
    235 #endif
    236   // This is more than the limit for 1000 pixels.
    237   const size_t kNumSplines = 300;
    238 
    239   std::vector<QuantizedSpline> quantized_splines;
    240   std::vector<Spline::Point> starting_points;
    241   for (size_t i = 0; i < kNumSplines; i++) {
    242     Spline spline = {
    243         /*control_points=*/{{1.f + i, 2}, {10.f + i, 25}, {30.f + i, 300}},
    244         /*color_dct=*/
    245         {{1.f, 0.2f, 0.1f}, {35.7f, 10.3f}, {35.7f, 7.8f}},
    246         /*sigma_dct=*/{10.f, 0.f, 0.f, 2.f}};
    247     quantized_splines.emplace_back(spline, kQuantizationAdjustment, kYToX,
    248                                    kYToB);
    249     starting_points.push_back(spline.control_points.front());
    250   }
    251 
    252   Splines splines(kQuantizationAdjustment, std::move(quantized_splines),
    253                   std::move(starting_points));
    254   BitWriter writer;
    255   EncodeSplines(splines, &writer, kLayerSplines,
    256                 HistogramParams(SpeedTier::kFalcon, 1), nullptr);
    257   writer.ZeroPadToByte();
    258   // Re-read splines.
    259   BitReader reader(writer.GetSpan());
    260   Splines decoded_splines;
    261   EXPECT_FALSE(decoded_splines.Decode(&reader, /*num_pixels=*/1000));
    262   EXPECT_TRUE(reader.Close());
    263 }
    264 
    265 #ifdef JXL_CRASH_ON_ERROR
    266 TEST(SplinesTest, DISABLED_DuplicatePoints) {
    267 #else
    268 TEST(SplinesTest, DuplicatePoints) {
    269 #endif
    270   std::vector<Spline::Point> control_points{
    271       {9, 54}, {118, 159}, {97, 3},  // Repeated.
    272       {97, 3}, {10, 40},   {150, 25}, {120, 300}};
    273   Spline spline{control_points,
    274                 /*color_dct=*/
    275                 {{1.f, 0.2f, 0.1f}, {35.7f, 10.3f}, {35.7f, 7.8f}},
    276                 /*sigma_dct=*/{10.f, 0.f, 0.f, 2.f}};
    277   std::vector<Spline> spline_data{spline};
    278   std::vector<QuantizedSpline> quantized_splines;
    279   std::vector<Spline::Point> starting_points;
    280   for (const Spline& spline : spline_data) {
    281     quantized_splines.emplace_back(spline, kQuantizationAdjustment, kYToX,
    282                                    kYToB);
    283     starting_points.push_back(spline.control_points.front());
    284   }
    285   Splines splines(kQuantizationAdjustment, std::move(quantized_splines),
    286                   std::move(starting_points));
    287 
    288   JXL_ASSIGN_OR_DIE(Image3F image, Image3F::Create(320, 320));
    289   ZeroFillImage(&image);
    290   EXPECT_FALSE(
    291       splines.InitializeDrawCache(image.xsize(), image.ysize(), *cmap));
    292 }
    293 
    294 TEST(SplinesTest, Drawing) {
    295   CodecInOut io_expected;
    296   const std::vector<uint8_t> orig = ReadTestData("jxl/splines.pfm");
    297   ASSERT_TRUE(SetFromBytes(Bytes(orig), &io_expected,
    298                            /*pool=*/nullptr));
    299 
    300   std::vector<Spline::Point> control_points{{9, 54},  {118, 159}, {97, 3},
    301                                             {10, 40}, {150, 25},  {120, 300}};
    302   // Use values that survive quant/decorellation roundtrip.
    303   const Spline spline{
    304       control_points,
    305       /*color_dct=*/
    306       {{0.4989345073699951171875000f, 0.4997999966144561767578125f},
    307        {0.4772970676422119140625000f, 0.f, 0.5250000357627868652343750f},
    308        {-0.0176776945590972900390625f, 0.4900000095367431640625000f,
    309         0.5250000357627868652343750f}},
    310       /*sigma_dct=*/
    311       {0.9427147507667541503906250f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f,
    312        0.6665999889373779296875000f}};
    313   std::vector<Spline> spline_data = {spline};
    314   std::vector<QuantizedSpline> quantized_splines;
    315   std::vector<Spline::Point> starting_points;
    316   for (const Spline& spline : spline_data) {
    317     quantized_splines.emplace_back(spline, kQuantizationAdjustment, kYToX,
    318                                    kYToB);
    319     starting_points.push_back(spline.control_points.front());
    320   }
    321   Splines splines(kQuantizationAdjustment, std::move(quantized_splines),
    322                   std::move(starting_points));
    323 
    324   JXL_ASSIGN_OR_DIE(Image3F image, Image3F::Create(320, 320));
    325   ZeroFillImage(&image);
    326   ASSERT_TRUE(splines.InitializeDrawCache(image.xsize(), image.ysize(), *cmap));
    327   splines.AddTo(&image, Rect(image), Rect(image));
    328 
    329   CodecInOut io_actual;
    330   JXL_ASSIGN_OR_DIE(Image3F image2, Image3F::Create(320, 320));
    331   CopyImageTo(image, &image2);
    332   io_actual.SetFromImage(std::move(image2), ColorEncoding::SRGB());
    333   ASSERT_TRUE(io_actual.frames[0].TransformTo(io_expected.Main().c_current(),
    334                                               *JxlGetDefaultCms()));
    335 
    336   JXL_ASSERT_OK(VerifyRelativeError(
    337       *io_expected.Main().color(), *io_actual.Main().color(), 1e-2f, 1e-1f, _));
    338 }
    339 
    340 TEST(SplinesTest, ClearedEveryFrame) {
    341   CodecInOut io_expected;
    342   const std::vector<uint8_t> bytes_expected =
    343       ReadTestData("jxl/spline_on_first_frame.png");
    344   ASSERT_TRUE(SetFromBytes(Bytes(bytes_expected), &io_expected,
    345                            /*pool=*/nullptr));
    346   CodecInOut io_actual;
    347   const std::vector<uint8_t> bytes_actual =
    348       ReadTestData("jxl/spline_on_first_frame.jxl");
    349   ASSERT_TRUE(test::DecodeFile({}, Bytes(bytes_actual), &io_actual));
    350 
    351   ASSERT_TRUE(io_actual.frames[0].TransformTo(ColorEncoding::SRGB(),
    352                                               *JxlGetDefaultCms()));
    353   for (size_t c = 0; c < 3; ++c) {
    354     for (size_t y = 0; y < io_actual.ysize(); ++y) {
    355       float* const JXL_RESTRICT row = io_actual.Main().color()->PlaneRow(c, y);
    356       for (size_t x = 0; x < io_actual.xsize(); ++x) {
    357         row[x] = Clamp1(row[x], 0.f, 1.f);
    358       }
    359     }
    360   }
    361   JXL_ASSERT_OK(VerifyRelativeError(
    362       *io_expected.Main().color(), *io_actual.Main().color(), 1e-2f, 1e-1f, _));
    363 }
    364 
    365 }  // namespace jxl