libjxl

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

passes_test.cc (13816B)


      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 <jxl/cms.h>
      7 #include <stddef.h>
      8 
      9 #include <cstdint>
     10 #include <future>
     11 #include <string>
     12 #include <utility>
     13 #include <vector>
     14 
     15 #include "lib/extras/codec.h"
     16 #include "lib/extras/dec/jxl.h"
     17 #include "lib/jxl/base/data_parallel.h"
     18 #include "lib/jxl/base/override.h"
     19 #include "lib/jxl/base/span.h"
     20 #include "lib/jxl/enc_params.h"
     21 #include "lib/jxl/image.h"
     22 #include "lib/jxl/image_bundle.h"
     23 #include "lib/jxl/image_ops.h"
     24 #include "lib/jxl/test_utils.h"
     25 #include "lib/jxl/testing.h"
     26 
     27 namespace jxl {
     28 
     29 using test::ButteraugliDistance;
     30 using test::ReadTestData;
     31 using test::Roundtrip;
     32 using test::ThreadPoolForTests;
     33 
     34 namespace {
     35 
     36 TEST(PassesTest, RoundtripSmallPasses) {
     37   const std::vector<uint8_t> orig =
     38       ReadTestData("external/wesaturate/500px/u76c0g_bliznaca_srgb8.png");
     39   CodecInOut io;
     40   ASSERT_TRUE(SetFromBytes(Bytes(orig), &io));
     41   io.ShrinkTo(io.xsize() / 8, io.ysize() / 8);
     42 
     43   CompressParams cparams;
     44   cparams.butteraugli_distance = 1.0;
     45   cparams.progressive_mode = Override::kOn;
     46   cparams.SetCms(*JxlGetDefaultCms());
     47 
     48   CodecInOut io2;
     49   JXL_EXPECT_OK(Roundtrip(&io, cparams, {}, &io2, _));
     50   EXPECT_THAT(ButteraugliDistance(io.frames, io2.frames, ButteraugliParams(),
     51                                   *JxlGetDefaultCms(),
     52                                   /*distmap=*/nullptr),
     53               IsSlightlyBelow(0.8222));
     54 }
     55 
     56 TEST(PassesTest, RoundtripUnalignedPasses) {
     57   const std::vector<uint8_t> orig =
     58       ReadTestData("external/wesaturate/500px/u76c0g_bliznaca_srgb8.png");
     59   CodecInOut io;
     60   ASSERT_TRUE(SetFromBytes(Bytes(orig), &io));
     61   io.ShrinkTo(io.xsize() / 12, io.ysize() / 7);
     62 
     63   CompressParams cparams;
     64   cparams.butteraugli_distance = 2.0;
     65   cparams.progressive_mode = Override::kOn;
     66   cparams.SetCms(*JxlGetDefaultCms());
     67 
     68   CodecInOut io2;
     69   JXL_EXPECT_OK(Roundtrip(&io, cparams, {}, &io2, _));
     70   EXPECT_THAT(ButteraugliDistance(io.frames, io2.frames, ButteraugliParams(),
     71                                   *JxlGetDefaultCms(),
     72                                   /*distmap=*/nullptr),
     73               IsSlightlyBelow(1.72));
     74 }
     75 
     76 TEST(PassesTest, RoundtripMultiGroupPasses) {
     77   const std::vector<uint8_t> orig = ReadTestData("jxl/flower/flower.png");
     78   CodecInOut io;
     79   {
     80     ThreadPoolForTests pool(4);
     81     ASSERT_TRUE(SetFromBytes(Bytes(orig), &io, &pool));
     82   }
     83   io.ShrinkTo(600, 1024);  // partial X, full Y group
     84 
     85   auto test = [&](float target_distance, float threshold) {
     86     ThreadPoolForTests pool(4);
     87     CompressParams cparams;
     88     cparams.butteraugli_distance = target_distance;
     89     cparams.progressive_mode = Override::kOn;
     90     cparams.SetCms(*JxlGetDefaultCms());
     91     CodecInOut io2;
     92     JXL_EXPECT_OK(Roundtrip(&io, cparams, {}, &io2, _,
     93                             /* compressed_size */ nullptr, &pool));
     94     EXPECT_THAT(ButteraugliDistance(io.frames, io2.frames, ButteraugliParams(),
     95                                     *JxlGetDefaultCms(),
     96                                     /*distmap=*/nullptr, &pool),
     97                 IsSlightlyBelow(target_distance + threshold));
     98   };
     99 
    100   auto run1 = std::async(std::launch::async, test, 1.0f, 0.15f);
    101   auto run2 = std::async(std::launch::async, test, 2.0f, 0.0f);
    102 }
    103 
    104 TEST(PassesTest, RoundtripLargeFastPasses) {
    105   ThreadPoolForTests pool(8);
    106   const std::vector<uint8_t> orig = ReadTestData("jxl/flower/flower.png");
    107   CodecInOut io;
    108   ASSERT_TRUE(SetFromBytes(Bytes(orig), &io, &pool));
    109 
    110   CompressParams cparams;
    111   cparams.speed_tier = SpeedTier::kSquirrel;
    112   cparams.progressive_mode = Override::kOn;
    113   cparams.SetCms(*JxlGetDefaultCms());
    114 
    115   CodecInOut io2;
    116   JXL_EXPECT_OK(Roundtrip(&io, cparams, {}, &io2, _,
    117                           /* compressed_size */ nullptr, &pool));
    118 }
    119 
    120 // Checks for differing size/distance in two consecutive runs of distance 2,
    121 // which involves additional processing including adaptive reconstruction.
    122 // Failing this may be a sign of race conditions or invalid memory accesses.
    123 TEST(PassesTest, RoundtripProgressiveConsistent) {
    124   ThreadPoolForTests pool(8);
    125   const std::vector<uint8_t> orig = ReadTestData("jxl/flower/flower.png");
    126   CodecInOut io;
    127   ASSERT_TRUE(SetFromBytes(Bytes(orig), &io, &pool));
    128 
    129   CompressParams cparams;
    130   cparams.speed_tier = SpeedTier::kSquirrel;
    131   cparams.progressive_mode = Override::kOn;
    132   cparams.butteraugli_distance = 2.0;
    133   cparams.SetCms(*JxlGetDefaultCms());
    134 
    135   // Try each xsize mod kBlockDim to verify right border handling.
    136   for (size_t xsize = 48; xsize > 40; --xsize) {
    137     io.ShrinkTo(xsize, 15);
    138 
    139     CodecInOut io2;
    140     size_t size2;
    141     JXL_EXPECT_OK(Roundtrip(&io, cparams, {}, &io2, _, &size2, &pool));
    142 
    143     CodecInOut io3;
    144     size_t size3;
    145     JXL_EXPECT_OK(Roundtrip(&io, cparams, {}, &io3, _, &size3, &pool));
    146 
    147     // Exact same compressed size.
    148     EXPECT_EQ(size2, size3);
    149 
    150     // Exact same distance.
    151     const float dist2 = ButteraugliDistance(
    152         io.frames, io2.frames, ButteraugliParams(), *JxlGetDefaultCms(),
    153         /*distmap=*/nullptr, &pool);
    154     const float dist3 = ButteraugliDistance(
    155         io.frames, io3.frames, ButteraugliParams(), *JxlGetDefaultCms(),
    156         /*distmap=*/nullptr, &pool);
    157     EXPECT_EQ(dist2, dist3);
    158   }
    159 }
    160 
    161 TEST(PassesTest, AllDownsampleFeasible) {
    162   ThreadPoolForTests pool(8);
    163   const std::vector<uint8_t> orig =
    164       ReadTestData("external/wesaturate/500px/u76c0g_bliznaca_srgb8.png");
    165   CodecInOut io;
    166   ASSERT_TRUE(SetFromBytes(Bytes(orig), &io, &pool));
    167 
    168   std::vector<uint8_t> compressed;
    169 
    170   CompressParams cparams;
    171   cparams.speed_tier = SpeedTier::kSquirrel;
    172   cparams.progressive_mode = Override::kOn;
    173   cparams.butteraugli_distance = 1.0;
    174   ASSERT_TRUE(test::EncodeFile(cparams, &io, &compressed, &pool));
    175 
    176   EXPECT_LE(compressed.size(), 240000u);
    177   float target_butteraugli[9] = {};
    178   target_butteraugli[1] = 2.5f;
    179   target_butteraugli[2] = 16.0f;
    180   target_butteraugli[4] = 20.0f;
    181   target_butteraugli[8] = 80.0f;
    182 
    183   // The default progressive encoding scheme should make all these downsampling
    184   // factors achievable.
    185   // TODO(veluca): re-enable downsampling 16.
    186   std::vector<size_t> downsamplings = {1, 2, 4, 8};  //, 16};
    187 
    188   auto check = [&](const uint32_t task, size_t /* thread */) -> void {
    189     const size_t downsampling = downsamplings[task];
    190     extras::JXLDecompressParams dparams;
    191     dparams.max_downsampling = downsampling;
    192     CodecInOut output;
    193     ASSERT_TRUE(test::DecodeFile(dparams, Bytes(compressed), &output));
    194     EXPECT_EQ(output.xsize(), io.xsize()) << "downsampling = " << downsampling;
    195     EXPECT_EQ(output.ysize(), io.ysize()) << "downsampling = " << downsampling;
    196     EXPECT_LE(ButteraugliDistance(io.frames, output.frames, ButteraugliParams(),
    197                                   *JxlGetDefaultCms(),
    198                                   /*distmap=*/nullptr, nullptr),
    199               target_butteraugli[downsampling])
    200         << "downsampling: " << downsampling;
    201   };
    202   EXPECT_TRUE(RunOnPool(&pool, 0, downsamplings.size(), ThreadPool::NoInit,
    203                         check, "TestDownsampling"));
    204 }
    205 
    206 TEST(PassesTest, AllDownsampleFeasibleQProgressive) {
    207   ThreadPoolForTests pool(8);
    208   const std::vector<uint8_t> orig =
    209       ReadTestData("external/wesaturate/500px/u76c0g_bliznaca_srgb8.png");
    210   CodecInOut io;
    211   ASSERT_TRUE(SetFromBytes(Bytes(orig), &io, &pool));
    212 
    213   std::vector<uint8_t> compressed;
    214 
    215   CompressParams cparams;
    216   cparams.speed_tier = SpeedTier::kSquirrel;
    217   cparams.qprogressive_mode = Override::kOn;
    218   cparams.butteraugli_distance = 1.0;
    219   ASSERT_TRUE(test::EncodeFile(cparams, &io, &compressed, &pool));
    220 
    221   EXPECT_LE(compressed.size(), 220000u);
    222 
    223   float target_butteraugli[9] = {};
    224   target_butteraugli[1] = 3.0f;
    225   target_butteraugli[2] = 6.0f;
    226   target_butteraugli[4] = 10.0f;
    227   target_butteraugli[8] = 80.0f;
    228 
    229   // The default progressive encoding scheme should make all these downsampling
    230   // factors achievable.
    231   std::vector<size_t> downsamplings = {1, 2, 4, 8};
    232 
    233   auto check = [&](const uint32_t task, size_t /* thread */) -> void {
    234     const size_t downsampling = downsamplings[task];
    235     extras::JXLDecompressParams dparams;
    236     dparams.max_downsampling = downsampling;
    237     CodecInOut output;
    238     ASSERT_TRUE(test::DecodeFile(dparams, Bytes(compressed), &output));
    239     EXPECT_EQ(output.xsize(), io.xsize()) << "downsampling = " << downsampling;
    240     EXPECT_EQ(output.ysize(), io.ysize()) << "downsampling = " << downsampling;
    241     EXPECT_LE(ButteraugliDistance(io.frames, output.frames, ButteraugliParams(),
    242                                   *JxlGetDefaultCms(),
    243                                   /*distmap=*/nullptr),
    244               target_butteraugli[downsampling])
    245         << "downsampling: " << downsampling;
    246   };
    247   EXPECT_TRUE(RunOnPool(&pool, 0, downsamplings.size(), ThreadPool::NoInit,
    248                         check, "TestQProgressive"));
    249 }
    250 
    251 TEST(PassesTest, ProgressiveDownsample2DegradesCorrectlyGrayscale) {
    252   ThreadPoolForTests pool(8);
    253   const std::vector<uint8_t> orig = ReadTestData(
    254       "external/wesaturate/500px/cvo9xd_keong_macan_grayscale.png");
    255   CodecInOut io_orig;
    256   ASSERT_TRUE(SetFromBytes(Bytes(orig), &io_orig, &pool));
    257   Rect rect(0, 0, io_orig.xsize(), 128);
    258   // need 2 DC groups for the DC frame to actually be progressive.
    259   JXL_ASSIGN_OR_DIE(Image3F large, Image3F::Create(4242, rect.ysize()));
    260   ZeroFillImage(&large);
    261   CopyImageTo(rect, *io_orig.Main().color(), rect, &large);
    262   CodecInOut io;
    263   io.metadata = io_orig.metadata;
    264   io.SetFromImage(std::move(large), io_orig.Main().c_current());
    265 
    266   std::vector<uint8_t> compressed;
    267 
    268   CompressParams cparams;
    269   cparams.speed_tier = SpeedTier::kSquirrel;
    270   cparams.progressive_dc = 1;
    271   cparams.responsive = JXL_TRUE;
    272   cparams.qprogressive_mode = Override::kOn;
    273   cparams.butteraugli_distance = 1.0;
    274   ASSERT_TRUE(test::EncodeFile(cparams, &io, &compressed, &pool));
    275 
    276   EXPECT_LE(compressed.size(), 10000u);
    277 
    278   extras::JXLDecompressParams dparams;
    279   dparams.max_downsampling = 1;
    280   CodecInOut output;
    281   ASSERT_TRUE(test::DecodeFile(dparams, Bytes(compressed), &output));
    282 
    283   dparams.max_downsampling = 2;
    284   CodecInOut output_d2;
    285   ASSERT_TRUE(test::DecodeFile(dparams, Bytes(compressed), &output_d2));
    286 
    287   // 0 if reading all the passes, ~15 if skipping the 8x pass.
    288   float butteraugli_distance_down2_full = ButteraugliDistance(
    289       output.frames, output_d2.frames, ButteraugliParams(), *JxlGetDefaultCms(),
    290       /*distmap=*/nullptr);
    291 
    292   EXPECT_LE(butteraugli_distance_down2_full, 3.2f);
    293   EXPECT_GE(butteraugli_distance_down2_full, 1.0f);
    294 }
    295 
    296 TEST(PassesTest, ProgressiveDownsample2DegradesCorrectly) {
    297   ThreadPoolForTests pool(8);
    298   const std::vector<uint8_t> orig = ReadTestData("jxl/flower/flower.png");
    299   CodecInOut io_orig;
    300   ASSERT_TRUE(SetFromBytes(Bytes(orig), &io_orig, &pool));
    301   Rect rect(0, 0, io_orig.xsize(), 128);
    302   // need 2 DC groups for the DC frame to actually be progressive.
    303   JXL_ASSIGN_OR_DIE(Image3F large, Image3F::Create(4242, rect.ysize()));
    304   ZeroFillImage(&large);
    305   CopyImageTo(rect, *io_orig.Main().color(), rect, &large);
    306   CodecInOut io;
    307   io.SetFromImage(std::move(large), io_orig.Main().c_current());
    308 
    309   std::vector<uint8_t> compressed;
    310 
    311   CompressParams cparams;
    312   cparams.speed_tier = SpeedTier::kSquirrel;
    313   cparams.progressive_dc = 1;
    314   cparams.responsive = JXL_TRUE;
    315   cparams.qprogressive_mode = Override::kOn;
    316   cparams.butteraugli_distance = 1.0;
    317   ASSERT_TRUE(test::EncodeFile(cparams, &io, &compressed, &pool));
    318 
    319   EXPECT_LE(compressed.size(), 220000u);
    320 
    321   extras::JXLDecompressParams dparams;
    322   dparams.max_downsampling = 1;
    323   CodecInOut output;
    324   ASSERT_TRUE(test::DecodeFile(dparams, Bytes(compressed), &output));
    325 
    326   dparams.max_downsampling = 2;
    327   CodecInOut output_d2;
    328   ASSERT_TRUE(test::DecodeFile(dparams, Bytes(compressed), &output_d2));
    329 
    330   // 0 if reading all the passes, ~15 if skipping the 8x pass.
    331   float butteraugli_distance_down2_full = ButteraugliDistance(
    332       output.frames, output_d2.frames, ButteraugliParams(), *JxlGetDefaultCms(),
    333       /*distmap=*/nullptr);
    334 
    335   EXPECT_LE(butteraugli_distance_down2_full, 3.0f);
    336   EXPECT_GE(butteraugli_distance_down2_full, 1.0f);
    337 }
    338 
    339 TEST(PassesTest, NonProgressiveDCImage) {
    340   ThreadPoolForTests pool(8);
    341   const std::vector<uint8_t> orig = ReadTestData("jxl/flower/flower.png");
    342   CodecInOut io;
    343   ASSERT_TRUE(SetFromBytes(Bytes(orig), &io, &pool));
    344 
    345   std::vector<uint8_t> compressed;
    346 
    347   CompressParams cparams;
    348   cparams.speed_tier = SpeedTier::kSquirrel;
    349   cparams.progressive_mode = Override::kOff;
    350   cparams.butteraugli_distance = 2.0;
    351   ASSERT_TRUE(test::EncodeFile(cparams, &io, &compressed, &pool));
    352 
    353   // Even in non-progressive mode, it should be possible to return a DC-only
    354   // image.
    355   extras::JXLDecompressParams dparams;
    356   dparams.max_downsampling = 100;
    357   CodecInOut output;
    358   ASSERT_TRUE(test::DecodeFile(dparams, Bytes(compressed), &output, &pool));
    359   EXPECT_EQ(output.xsize(), io.xsize());
    360   EXPECT_EQ(output.ysize(), io.ysize());
    361 }
    362 
    363 TEST(PassesTest, RoundtripSmallNoGaborishPasses) {
    364   const std::vector<uint8_t> orig =
    365       ReadTestData("external/wesaturate/500px/u76c0g_bliznaca_srgb8.png");
    366   CodecInOut io;
    367   ASSERT_TRUE(SetFromBytes(Bytes(orig), &io));
    368   io.ShrinkTo(io.xsize() / 8, io.ysize() / 8);
    369 
    370   CompressParams cparams;
    371   cparams.gaborish = Override::kOff;
    372   cparams.butteraugli_distance = 1.0;
    373   cparams.progressive_mode = Override::kOn;
    374   cparams.SetCms(*JxlGetDefaultCms());
    375 
    376   CodecInOut io2;
    377   JXL_EXPECT_OK(Roundtrip(&io, cparams, {}, &io2, _));
    378   EXPECT_THAT(ButteraugliDistance(io.frames, io2.frames, ButteraugliParams(),
    379                                   *JxlGetDefaultCms(),
    380                                   /*distmap=*/nullptr),
    381               IsSlightlyBelow(1.2));
    382 }
    383 
    384 }  // namespace
    385 }  // namespace jxl