libjxl

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

benchmark_codec_jxl.cc (14546B)


      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 #include "tools/benchmark/benchmark_codec_jxl.h"
      6 
      7 #include <jxl/color_encoding.h>
      8 #include <jxl/encode.h>
      9 #include <jxl/stats.h>
     10 #include <jxl/types.h>
     11 
     12 #include <cstdint>
     13 #include <cstdlib>
     14 #include <cstring>
     15 #include <memory>
     16 #include <sstream>
     17 #include <string>
     18 #include <utility>
     19 #include <vector>
     20 
     21 #include "lib/extras/dec/jxl.h"
     22 #include "lib/extras/enc/apng.h"
     23 #include "lib/extras/enc/encode.h"
     24 #include "lib/extras/enc/jxl.h"
     25 #include "lib/extras/packed_image.h"
     26 #include "lib/extras/time.h"
     27 #include "lib/jxl/base/status.h"
     28 #include "lib/jxl/image.h"
     29 #include "tools/benchmark/benchmark_args.h"
     30 #include "tools/benchmark/benchmark_codec.h"
     31 #include "tools/benchmark/benchmark_file_io.h"
     32 #include "tools/benchmark/benchmark_stats.h"
     33 #include "tools/file_io.h"
     34 #include "tools/speed_stats.h"
     35 #include "tools/thread_pool_internal.h"
     36 
     37 namespace jpegxl {
     38 namespace tools {
     39 
     40 using ::jxl::Image3F;
     41 using ::jxl::extras::EncodedImage;
     42 using ::jxl::extras::JXLCompressParams;
     43 using ::jxl::extras::JXLDecompressParams;
     44 using ::jxl::extras::PackedFrame;
     45 using ::jxl::extras::PackedPixelFile;
     46 
     47 struct JxlArgs {
     48   bool qprogressive;  // progressive with shift-quantization.
     49   bool progressive;
     50   int progressive_dc;
     51 
     52   Override noise;
     53   Override dots;
     54   Override patches;
     55 
     56   std::string debug_image_dir;
     57 };
     58 
     59 static JxlArgs* const jxlargs = new JxlArgs;
     60 
     61 Status AddCommandLineOptionsJxlCodec(BenchmarkArgs* args) {
     62   args->AddFlag(&jxlargs->qprogressive, "qprogressive",
     63                 "Enable quantized progressive mode for AC.", false);
     64   args->AddFlag(&jxlargs->progressive, "progressive",
     65                 "Enable progressive mode for AC.", false);
     66   args->AddSigned(&jxlargs->progressive_dc, "progressive_dc",
     67                   "Enable progressive mode for DC.", -1);
     68 
     69   args->AddOverride(&jxlargs->noise, "noise",
     70                     "Enable(1)/disable(0) noise generation.");
     71   args->AddOverride(&jxlargs->dots, "dots",
     72                     "Enable(1)/disable(0) dots generation.");
     73   args->AddOverride(&jxlargs->patches, "patches",
     74                     "Enable(1)/disable(0) patch dictionary.");
     75 
     76   args->AddString(
     77       &jxlargs->debug_image_dir, "debug_image_dir",
     78       "If not empty, saves debug images for each "
     79       "input image and each codec that provides it to this directory.");
     80 
     81   return true;
     82 }
     83 
     84 Status ValidateArgsJxlCodec(BenchmarkArgs* args) { return true; }
     85 
     86 inline bool ParseEffort(const std::string& s, int* out) {
     87   if (s == "lightning") {
     88     *out = 1;
     89     return true;
     90   } else if (s == "thunder") {
     91     *out = 2;
     92     return true;
     93   } else if (s == "falcon") {
     94     *out = 3;
     95     return true;
     96   } else if (s == "cheetah") {
     97     *out = 4;
     98     return true;
     99   } else if (s == "hare") {
    100     *out = 5;
    101     return true;
    102   } else if (s == "fast" || s == "wombat") {
    103     *out = 6;
    104     return true;
    105   } else if (s == "squirrel") {
    106     *out = 7;
    107     return true;
    108   } else if (s == "kitten") {
    109     *out = 8;
    110     return true;
    111   } else if (s == "guetzli" || s == "tortoise") {
    112     *out = 9;
    113     return true;
    114   } else if (s == "glacier") {
    115     *out = 10;
    116     return true;
    117   }
    118   size_t st = static_cast<size_t>(strtoull(s.c_str(), nullptr, 0));
    119   if (st <= 10 && st >= 1) {
    120     *out = st;
    121     return true;
    122   }
    123   return false;
    124 }
    125 
    126 class JxlCodec : public ImageCodec {
    127  public:
    128   explicit JxlCodec(const BenchmarkArgs& args)
    129       : ImageCodec(args), stats_(nullptr, JxlEncoderStatsDestroy) {}
    130 
    131   Status ParseParam(const std::string& param) override {
    132     const std::string kMaxPassesPrefix = "max_passes=";
    133     const std::string kDownsamplingPrefix = "downsampling=";
    134     const std::string kResamplingPrefix = "resampling=";
    135     const std::string kEcResamplingPrefix = "ec_resampling=";
    136     int val;
    137     float fval;
    138     if (param.substr(0, kResamplingPrefix.size()) == kResamplingPrefix) {
    139       std::istringstream parser(param.substr(kResamplingPrefix.size()));
    140       int resampling;
    141       parser >> resampling;
    142       cparams_.AddOption(JXL_ENC_FRAME_SETTING_RESAMPLING, resampling);
    143     } else if (param.substr(0, kEcResamplingPrefix.size()) ==
    144                kEcResamplingPrefix) {
    145       std::istringstream parser(param.substr(kEcResamplingPrefix.size()));
    146       int ec_resampling;
    147       parser >> ec_resampling;
    148       cparams_.AddOption(JXL_ENC_FRAME_SETTING_EXTRA_CHANNEL_RESAMPLING,
    149                          ec_resampling);
    150     } else if (ImageCodec::ParseParam(param)) {
    151       // Nothing to do.
    152     } else if (param == "uint8") {
    153       uint8_ = true;
    154     } else if (param[0] == 'D') {
    155       cparams_.alpha_distance = strtof(param.substr(1).c_str(), nullptr);
    156     } else if (param.substr(0, kMaxPassesPrefix.size()) == kMaxPassesPrefix) {
    157       std::istringstream parser(param.substr(kMaxPassesPrefix.size()));
    158       parser >> dparams_.max_passes;
    159     } else if (param.substr(0, kDownsamplingPrefix.size()) ==
    160                kDownsamplingPrefix) {
    161       std::istringstream parser(param.substr(kDownsamplingPrefix.size()));
    162       parser >> dparams_.max_downsampling;
    163     } else if (ParseEffort(param, &val)) {
    164       cparams_.AddOption(JXL_ENC_FRAME_SETTING_EFFORT, val);
    165     } else if (param[0] == 'X') {
    166       fval = strtof(param.substr(1).c_str(), nullptr);
    167       cparams_.AddFloatOption(
    168           JXL_ENC_FRAME_SETTING_CHANNEL_COLORS_GLOBAL_PERCENT, fval);
    169     } else if (param[0] == 'Y') {
    170       fval = strtof(param.substr(1).c_str(), nullptr);
    171       cparams_.AddFloatOption(
    172           JXL_ENC_FRAME_SETTING_CHANNEL_COLORS_GROUP_PERCENT, fval);
    173     } else if (param[0] == 'p') {
    174       val = strtol(param.substr(1).c_str(), nullptr, 10);
    175       cparams_.AddOption(JXL_ENC_FRAME_SETTING_PALETTE_COLORS, val);
    176     } else if (param == "lp") {
    177       cparams_.AddOption(JXL_ENC_FRAME_SETTING_LOSSY_PALETTE, 1);
    178     } else if (param[0] == 'C') {
    179       val = strtol(param.substr(1).c_str(), nullptr, 10);
    180       cparams_.AddOption(JXL_ENC_FRAME_SETTING_MODULAR_COLOR_SPACE, val);
    181     } else if (param[0] == 'c') {
    182       val = strtol(param.substr(1).c_str(), nullptr, 10);
    183       cparams_.AddOption(JXL_ENC_FRAME_SETTING_COLOR_TRANSFORM, val);
    184       has_ctransform_ = true;
    185     } else if (param[0] == 'I') {
    186       fval = strtof(param.substr(1).c_str(), nullptr);
    187       cparams_.AddFloatOption(
    188           JXL_ENC_FRAME_SETTING_MODULAR_MA_TREE_LEARNING_PERCENT, fval * 100.0);
    189     } else if (param[0] == 'E') {
    190       val = strtol(param.substr(1).c_str(), nullptr, 10);
    191       cparams_.AddOption(JXL_ENC_FRAME_SETTING_MODULAR_NB_PREV_CHANNELS, val);
    192     } else if (param[0] == 'P') {
    193       val = strtol(param.substr(1).c_str(), nullptr, 10);
    194       cparams_.AddOption(JXL_ENC_FRAME_SETTING_MODULAR_PREDICTOR, val);
    195     } else if (param == "slow") {
    196       cparams_.AddFloatOption(
    197           JXL_ENC_FRAME_SETTING_MODULAR_MA_TREE_LEARNING_PERCENT, 50.0);
    198     } else if (param == "R") {
    199       cparams_.AddOption(JXL_ENC_FRAME_SETTING_RESPONSIVE, 1);
    200     } else if (param[0] == 'R') {
    201       val = strtol(param.substr(1).c_str(), nullptr, 10);
    202       cparams_.AddOption(JXL_ENC_FRAME_SETTING_RESPONSIVE, val);
    203     } else if (param == "m") {
    204       cparams_.AddOption(JXL_ENC_FRAME_SETTING_MODULAR, 1);
    205       cparams_.AddOption(JXL_ENC_FRAME_SETTING_COLOR_TRANSFORM, 1);  // kNone
    206       modular_mode_ = true;
    207     } else if (param.substr(0, 3) == "gab") {
    208       val = strtol(param.substr(3).c_str(), nullptr, 10);
    209       if (val != 0 && val != 1) {
    210         return JXL_FAILURE("Invalid gab value");
    211       }
    212       cparams_.AddOption(JXL_ENC_FRAME_SETTING_GABORISH, val);
    213     } else if (param[0] == 'g') {
    214       val = strtol(param.substr(1).c_str(), nullptr, 10);
    215       if (val < 0 || val > 3) {
    216         return JXL_FAILURE("Invalid group size shift value");
    217       }
    218       cparams_.AddOption(JXL_ENC_FRAME_SETTING_MODULAR_GROUP_SIZE, val);
    219     } else if (param == "plt") {
    220       cparams_.AddOption(JXL_ENC_FRAME_SETTING_MODULAR_NB_PREV_CHANNELS, 0);
    221       cparams_.AddFloatOption(
    222           JXL_ENC_FRAME_SETTING_MODULAR_MA_TREE_LEARNING_PERCENT, 0.0f);
    223       cparams_.AddOption(JXL_ENC_FRAME_SETTING_MODULAR_PREDICTOR, 0);
    224       cparams_.AddOption(JXL_ENC_FRAME_SETTING_RESPONSIVE, 0);
    225       cparams_.AddOption(JXL_ENC_FRAME_SETTING_MODULAR_COLOR_SPACE, 0);
    226       cparams_.AddOption(JXL_ENC_FRAME_SETTING_CHANNEL_COLORS_GLOBAL_PERCENT,
    227                          0);
    228       cparams_.AddOption(JXL_ENC_FRAME_SETTING_CHANNEL_COLORS_GROUP_PERCENT, 0);
    229     } else if (param.substr(0, 3) == "epf") {
    230       val = strtol(param.substr(3).c_str(), nullptr, 10);
    231       if (val > 3) {
    232         return JXL_FAILURE("Invalid epf value");
    233       }
    234       cparams_.AddOption(JXL_ENC_FRAME_SETTING_EPF, val);
    235     } else if (param.substr(0, 2) == "fi") {
    236       val = strtol(param.substr(2).c_str(), nullptr, 10);
    237       if (val != 0 && val != 1) {
    238         return JXL_FAILURE("Invalid option value");
    239       }
    240       cparams_.AddOption(JXL_ENC_FRAME_SETTING_USE_FULL_IMAGE_HEURISTICS, val);
    241     } else if (param.substr(0, 3) == "buf") {
    242       val = strtol(param.substr(3).c_str(), nullptr, 10);
    243       if (val > 3) {
    244         return JXL_FAILURE("Invalid buffering value");
    245       }
    246       cparams_.AddOption(JXL_ENC_FRAME_SETTING_BUFFERING, val);
    247     } else if (param.substr(0, 16) == "faster_decoding=") {
    248       val = strtol(param.substr(16).c_str(), nullptr, 10);
    249       cparams_.AddOption(JXL_ENC_FRAME_SETTING_DECODING_SPEED, val);
    250     } else {
    251       return JXL_FAILURE("Unrecognized param");
    252     }
    253     return true;
    254   }
    255 
    256   Status Compress(const std::string& filename, const PackedPixelFile& ppf,
    257                   ThreadPool* pool, std::vector<uint8_t>* compressed,
    258                   jpegxl::tools::SpeedStats* speed_stats) override {
    259     cparams_.runner = pool->runner();
    260     cparams_.runner_opaque = pool->runner_opaque();
    261     cparams_.distance = butteraugli_target_;
    262     cparams_.AddOption(JXL_ENC_FRAME_SETTING_NOISE,
    263                        static_cast<int>(jxlargs->noise));
    264     cparams_.AddOption(JXL_ENC_FRAME_SETTING_DOTS,
    265                        static_cast<int>(jxlargs->dots));
    266     cparams_.AddOption(JXL_ENC_FRAME_SETTING_PATCHES,
    267                        static_cast<int>(jxlargs->patches));
    268     cparams_.AddOption(JXL_ENC_FRAME_SETTING_PROGRESSIVE_AC,
    269                        TO_JXL_BOOL(jxlargs->progressive));
    270     cparams_.AddOption(JXL_ENC_FRAME_SETTING_QPROGRESSIVE_AC,
    271                        TO_JXL_BOOL(jxlargs->qprogressive));
    272     cparams_.AddOption(JXL_ENC_FRAME_SETTING_PROGRESSIVE_DC,
    273                        jxlargs->progressive_dc);
    274     if (butteraugli_target_ > 0.f && modular_mode_ && !has_ctransform_) {
    275       // Reset color transform to default XYB for lossy modular.
    276       cparams_.AddOption(JXL_ENC_FRAME_SETTING_COLOR_TRANSFORM, -1);
    277     }
    278     std::string debug_prefix;
    279     SetDebugImageCallback(filename, &debug_prefix, &cparams_);
    280     if (args_.print_more_stats) {
    281       stats_.reset(JxlEncoderStatsCreate());
    282       cparams_.stats = stats_.get();
    283     }
    284     const double start = jxl::Now();
    285     JXL_RETURN_IF_ERROR(jxl::extras::EncodeImageJXL(
    286         cparams_, ppf, /*jpeg_bytes=*/nullptr, compressed));
    287     const double end = jxl::Now();
    288     speed_stats->NotifyElapsed(end - start);
    289     return true;
    290   }
    291 
    292   Status Decompress(const std::string& filename,
    293                     const Span<const uint8_t> compressed, ThreadPool* pool,
    294                     PackedPixelFile* ppf,
    295                     jpegxl::tools::SpeedStats* speed_stats) override {
    296     dparams_.runner = pool->runner();
    297     dparams_.runner_opaque = pool->runner_opaque();
    298     JxlDataType data_type = uint8_ ? JXL_TYPE_UINT8 : JXL_TYPE_UINT16;
    299     dparams_.accepted_formats = {{3, data_type, JXL_LITTLE_ENDIAN, 0},
    300                                  {4, data_type, JXL_LITTLE_ENDIAN, 0}};
    301     // By default, the decoder will undo exif orientation, giving an image
    302     // with identity exif rotation as result. However, the benchmark does
    303     // not undo exif orientation of the originals, and compares against the
    304     // originals, so we must set the option to keep the original orientation
    305     // instead.
    306     dparams_.keep_orientation = true;
    307     size_t decoded_bytes;
    308     const double start = jxl::Now();
    309     JXL_RETURN_IF_ERROR(jxl::extras::DecodeImageJXL(
    310         compressed.data(), compressed.size(), dparams_, &decoded_bytes, ppf));
    311     const double end = jxl::Now();
    312     speed_stats->NotifyElapsed(end - start);
    313     return true;
    314   }
    315 
    316   void GetMoreStats(BenchmarkStats* stats) override {
    317     stats->jxl_stats.num_inputs += 1;
    318     JxlEncoderStatsMerge(stats->jxl_stats.stats.get(), stats_.get());
    319   }
    320 
    321  protected:
    322   JXLCompressParams cparams_;
    323   bool has_ctransform_ = false;
    324   bool modular_mode_ = false;
    325   JXLDecompressParams dparams_;
    326   bool uint8_ = false;
    327   std::unique_ptr<JxlEncoderStats, decltype(JxlEncoderStatsDestroy)*> stats_;
    328 
    329  private:
    330   void SetDebugImageCallback(const std::string& filename,
    331                              std::string* debug_prefix,
    332                              JXLCompressParams* cparams) {
    333     if (jxlargs->debug_image_dir.empty()) return;
    334     *debug_prefix = JoinPath(jxlargs->debug_image_dir, FileBaseName(filename)) +
    335                     ".jxl:" + params_ + ".dbg/";
    336     JXL_CHECK(MakeDir(*debug_prefix));
    337     cparams->debug_image_opaque = debug_prefix;
    338     cparams->debug_image = [](void* opaque, const char* label, size_t xsize,
    339                               size_t ysize, const JxlColorEncoding* color,
    340                               const uint16_t* pixels) {
    341       auto encoder = jxl::extras::GetAPNGEncoder();
    342       JXL_CHECK(encoder);
    343       PackedPixelFile debug_ppf;
    344       JxlPixelFormat format{3, JXL_TYPE_UINT16, JXL_BIG_ENDIAN, 0};
    345       PackedFrame frame(xsize, ysize, format);
    346       memcpy(frame.color.pixels(), pixels, 6 * xsize * ysize);
    347       debug_ppf.frames.emplace_back(std::move(frame));
    348       debug_ppf.info.xsize = xsize;
    349       debug_ppf.info.ysize = ysize;
    350       debug_ppf.info.num_color_channels = 3;
    351       debug_ppf.info.bits_per_sample = 16;
    352       debug_ppf.color_encoding = *color;
    353       EncodedImage encoded;
    354       JXL_CHECK(encoder->Encode(debug_ppf, &encoded, nullptr));
    355       JXL_CHECK(!encoded.bitstreams.empty());
    356       std::string* debug_prefix = reinterpret_cast<std::string*>(opaque);
    357       std::string fn = *debug_prefix + std::string(label) + ".png";
    358       WriteFile(fn, encoded.bitstreams[0]);
    359     };
    360   }
    361 };
    362 
    363 ImageCodec* CreateNewJxlCodec(const BenchmarkArgs& args) {
    364   return new JxlCodec(args);
    365 }
    366 
    367 }  // namespace tools
    368 }  // namespace jpegxl