libjxl

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

benchmark_codec_jpeg.cc (12751B)


      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_jpeg.h"
      6 
      7 #include <stddef.h>
      8 #include <stdio.h>
      9 // After stddef/stdio
     10 #include <jxl/types.h>
     11 #include <stdint.h>
     12 #include <string.h>
     13 
     14 #include <cmath>
     15 #include <cstdlib>
     16 #include <memory>
     17 #include <sstream>
     18 #include <string>
     19 #include <vector>
     20 
     21 #include "lib/extras/codec.h"
     22 #include "lib/extras/enc/encode.h"
     23 #include "lib/jxl/base/status.h"
     24 #include "tools/benchmark/benchmark_args.h"
     25 #include "tools/benchmark/benchmark_codec.h"
     26 #include "tools/speed_stats.h"
     27 
     28 #if JPEGXL_ENABLE_JPEGLI
     29 #include "lib/extras/dec/jpegli.h"
     30 #endif
     31 #include "lib/extras/dec/jpg.h"
     32 #if JPEGXL_ENABLE_JPEGLI
     33 #include "lib/extras/enc/jpegli.h"
     34 #endif
     35 #include "lib/extras/enc/jpg.h"
     36 #include "lib/extras/packed_image.h"
     37 #include "lib/extras/time.h"
     38 #include "lib/jxl/base/span.h"
     39 #include "tools/benchmark/benchmark_utils.h"
     40 #include "tools/file_io.h"
     41 #include "tools/thread_pool_internal.h"
     42 
     43 namespace jpegxl {
     44 namespace tools {
     45 
     46 struct JPEGArgs {
     47   std::string base_quant_fn;
     48   float search_q_start;
     49   float search_q_min;
     50   float search_q_max;
     51   float search_d_min;
     52   float search_d_max;
     53   int search_max_iters;
     54   float search_tolerance;
     55   float search_q_precision;
     56   float search_first_iter_slope;
     57 };
     58 
     59 static JPEGArgs* const jpegargs = new JPEGArgs;
     60 
     61 #define SET_ENCODER_ARG(name)                                  \
     62   if (jpegargs->name > 0) {                                    \
     63     encoder->SetOption(#name, std::to_string(jpegargs->name)); \
     64   }
     65 
     66 Status AddCommandLineOptionsJPEGCodec(BenchmarkArgs* args) {
     67   args->AddString(&jpegargs->base_quant_fn, "qtables",
     68                   "Custom base quantization tables.");
     69   args->AddFloat(&jpegargs->search_q_start, "search_q_start",
     70                  "Starting quality for quality-to-target search", 0.0f);
     71   args->AddFloat(&jpegargs->search_q_min, "search_q_min",
     72                  "Minimum quality for quality-to-target search", 0.0f);
     73   args->AddFloat(&jpegargs->search_q_max, "search_q_max",
     74                  "Maximum quality for quality-to-target search", 0.0f);
     75   args->AddFloat(&jpegargs->search_d_min, "search_d_min",
     76                  "Minimum distance for quality-to-target search", 0.0f);
     77   args->AddFloat(&jpegargs->search_d_max, "search_d_max",
     78                  "Maximum distance for quality-to-target search", 0.0f);
     79   args->AddFloat(&jpegargs->search_tolerance, "search_tolerance",
     80                  "Percentage value, if quality-to-target search result "
     81                  "relative error is within this, search stops.",
     82                  0.0f);
     83   args->AddFloat(&jpegargs->search_q_precision, "search_q_precision",
     84                  "If last quality change in quality-to-target search is "
     85                  "within this value, search stops.",
     86                  0.0f);
     87   args->AddFloat(&jpegargs->search_first_iter_slope, "search_first_iter_slope",
     88                  "Slope of first extrapolation step in quality-to-target "
     89                  "search.",
     90                  0.0f);
     91   args->AddSigned(&jpegargs->search_max_iters, "search_max_iters",
     92                   "Maximum search steps in quality-to-target search.", 0);
     93   return true;
     94 }
     95 
     96 class JPEGCodec : public ImageCodec {
     97  public:
     98   explicit JPEGCodec(const BenchmarkArgs& args) : ImageCodec(args) {}
     99 
    100   Status ParseParam(const std::string& param) override {
    101     if (param[0] == 'q' && ImageCodec::ParseParam(param)) {
    102       enc_quality_set_ = true;
    103       return true;
    104     }
    105     if (ImageCodec::ParseParam(param)) {
    106       return true;
    107     }
    108     if (param == "sjpeg" || param.find("cjpeg") != std::string::npos) {
    109       jpeg_encoder_ = param;
    110       return true;
    111     }
    112 #if JPEGXL_ENABLE_JPEGLI
    113     if (param == "enc-jpegli") {
    114       jpeg_encoder_ = "jpegli";
    115       return true;
    116     }
    117 #endif
    118     if (param.compare(0, 3, "yuv") == 0) {
    119       chroma_subsampling_ = param.substr(3);
    120       return true;
    121     }
    122     if (param.compare(0, 4, "psnr") == 0) {
    123       psnr_target_ = std::stof(param.substr(4));
    124       return true;
    125     }
    126     if (param[0] == 'p') {
    127       progressive_id_ = strtol(param.substr(1).c_str(), nullptr, 10);
    128       return true;
    129     }
    130     if (param == "fix") {
    131       fix_codes_ = true;
    132       return true;
    133     }
    134     if (param[0] == 'Q') {
    135       libjpeg_quality_ = strtol(param.substr(1).c_str(), nullptr, 10);
    136       return true;
    137     }
    138     if (param.compare(0, 3, "YUV") == 0) {
    139       if (param.size() != 6) return false;
    140       libjpeg_chroma_subsampling_ = param.substr(3);
    141       return true;
    142     }
    143     if (param == "noaq") {
    144       enable_adaptive_quant_ = false;
    145       return true;
    146     }
    147 #if JPEGXL_ENABLE_JPEGLI
    148     if (param == "xyb") {
    149       xyb_mode_ = true;
    150       return true;
    151     }
    152     if (param == "std") {
    153       use_std_tables_ = true;
    154       return true;
    155     }
    156     if (param == "dec-jpegli") {
    157       jpeg_decoder_ = "jpegli";
    158       return true;
    159     }
    160     if (param.substr(0, 2) == "bd") {
    161       bitdepth_ = strtol(param.substr(2).c_str(), nullptr, 10);
    162       return true;
    163     }
    164     if (param.substr(0, 6) == "cquant") {
    165       num_colors_ = strtol(param.substr(6).c_str(), nullptr, 10);
    166       return true;
    167     }
    168 #endif
    169     return false;
    170   }
    171 
    172   bool IgnoreAlpha() const override { return true; }
    173 
    174   Status Compress(const std::string& filename, const PackedPixelFile& ppf,
    175                   ThreadPool* pool, std::vector<uint8_t>* compressed,
    176                   jpegxl::tools::SpeedStats* speed_stats) override {
    177     if (jpeg_encoder_.find("cjpeg") != std::string::npos) {
    178 // Not supported on Windows due to Linux-specific functions.
    179 // Not supported in Android NDK before API 28.
    180 #if !defined(_WIN32) && !defined(__EMSCRIPTEN__) && \
    181     (!defined(__ANDROID_API__) || __ANDROID_API__ >= 28)
    182       const std::string basename = GetBaseName(filename);
    183       TemporaryFile in_file(basename, "pnm");
    184       TemporaryFile encoded_file(basename, "jpg");
    185       std::string in_filename;
    186       std::string encoded_filename;
    187       JXL_RETURN_IF_ERROR(in_file.GetFileName(&in_filename));
    188       JXL_RETURN_IF_ERROR(encoded_file.GetFileName(&encoded_filename));
    189       std::vector<uint8_t> encoded;
    190       JXL_RETURN_IF_ERROR(Encode(ppf, in_filename, &encoded, pool));
    191       JXL_RETURN_IF_ERROR(WriteFile(in_filename, encoded));
    192       std::string compress_command = jpeg_encoder_;
    193       std::vector<std::string> arguments;
    194       arguments.emplace_back("-outfile");
    195       arguments.push_back(encoded_filename);
    196       arguments.emplace_back("-quality");
    197       arguments.push_back(std::to_string(static_cast<int>(q_target_)));
    198       arguments.emplace_back("-sample");
    199       if (chroma_subsampling_ == "444") {
    200         arguments.emplace_back("1x1");
    201       } else if (chroma_subsampling_ == "420") {
    202         arguments.emplace_back("2x2");
    203       } else if (!chroma_subsampling_.empty()) {
    204         return JXL_FAILURE("Unsupported chroma subsampling");
    205       }
    206       arguments.emplace_back("-optimize");
    207       arguments.push_back(in_filename);
    208       const double start = jxl::Now();
    209       JXL_RETURN_IF_ERROR(RunCommand(compress_command, arguments, false));
    210       const double end = jxl::Now();
    211       speed_stats->NotifyElapsed(end - start);
    212       return ReadFile(encoded_filename, compressed);
    213 #else
    214       return JXL_FAILURE("Not supported on this build");
    215 #endif
    216     }
    217 
    218     double elapsed = 0.0;
    219     if (jpeg_encoder_ == "jpegli") {
    220 #if JPEGXL_ENABLE_JPEGLI
    221       jxl::extras::JpegSettings settings;
    222       settings.xyb = xyb_mode_;
    223       if (!xyb_mode_) {
    224         settings.use_std_quant_tables = use_std_tables_;
    225       }
    226       if (enc_quality_set_) {
    227         settings.quality = q_target_;
    228       } else {
    229         settings.distance = butteraugli_target_;
    230       }
    231       if (progressive_id_ >= 0) {
    232         settings.progressive_level = progressive_id_;
    233       }
    234       if (psnr_target_ > 0) {
    235         settings.psnr_target = psnr_target_;
    236       }
    237       if (jpegargs->search_tolerance > 0) {
    238         settings.search_tolerance = 0.01f * jpegargs->search_tolerance;
    239       }
    240       if (jpegargs->search_d_min > 0) {
    241         settings.min_distance = jpegargs->search_d_min;
    242       }
    243       if (jpegargs->search_d_max > 0) {
    244         settings.max_distance = jpegargs->search_d_max;
    245       }
    246       settings.chroma_subsampling = chroma_subsampling_;
    247       settings.use_adaptive_quantization = enable_adaptive_quant_;
    248       settings.libjpeg_quality = libjpeg_quality_;
    249       settings.libjpeg_chroma_subsampling = libjpeg_chroma_subsampling_;
    250       settings.optimize_coding = !fix_codes_;
    251       const double start = jxl::Now();
    252       JXL_RETURN_IF_ERROR(
    253           jxl::extras::EncodeJpeg(ppf, settings, pool, compressed));
    254       const double end = jxl::Now();
    255       elapsed = end - start;
    256 #endif
    257     } else {
    258       jxl::extras::EncodedImage encoded;
    259       std::unique_ptr<jxl::extras::Encoder> encoder =
    260           jxl::extras::GetJPEGEncoder();
    261       if (!encoder) {
    262         fprintf(stderr, "libjpeg codec is not supported\n");
    263         return false;
    264       }
    265       std::ostringstream os;
    266       os << static_cast<int>(std::round(q_target_));
    267       encoder->SetOption("q", os.str());
    268       encoder->SetOption("jpeg_encoder", jpeg_encoder_);
    269       if (!chroma_subsampling_.empty()) {
    270         encoder->SetOption("chroma_subsampling", chroma_subsampling_);
    271       }
    272       if (progressive_id_ >= 0) {
    273         encoder->SetOption("progressive", std::to_string(progressive_id_));
    274       }
    275       if (libjpeg_quality_ > 0) {
    276         encoder->SetOption("libjpeg_quality", std::to_string(libjpeg_quality_));
    277       }
    278       if (!libjpeg_chroma_subsampling_.empty()) {
    279         encoder->SetOption("libjpeg_chroma_subsampling",
    280                            libjpeg_chroma_subsampling_);
    281       }
    282       if (fix_codes_) {
    283         encoder->SetOption("optimize", "OFF");
    284       }
    285       if (!enable_adaptive_quant_) {
    286         encoder->SetOption("adaptive_q", "OFF");
    287       }
    288       if (psnr_target_ > 0) {
    289         encoder->SetOption("psnr", std::to_string(psnr_target_));
    290       }
    291       if (!jpegargs->base_quant_fn.empty()) {
    292         encoder->SetOption("base_quant_fn", jpegargs->base_quant_fn);
    293       }
    294       SET_ENCODER_ARG(search_q_start);
    295       SET_ENCODER_ARG(search_q_min);
    296       SET_ENCODER_ARG(search_q_max);
    297       SET_ENCODER_ARG(search_q_precision);
    298       SET_ENCODER_ARG(search_tolerance);
    299       SET_ENCODER_ARG(search_first_iter_slope);
    300       SET_ENCODER_ARG(search_max_iters);
    301       const double start = jxl::Now();
    302       JXL_RETURN_IF_ERROR(encoder->Encode(ppf, &encoded, pool));
    303       const double end = jxl::Now();
    304       elapsed = end - start;
    305       *compressed = encoded.bitstreams.back();
    306     }
    307     speed_stats->NotifyElapsed(elapsed);
    308     return true;
    309   }
    310 
    311   Status Decompress(const std::string& filename,
    312                     const Span<const uint8_t> compressed, ThreadPool* pool,
    313                     jxl::extras::PackedPixelFile* ppf,
    314                     jpegxl::tools::SpeedStats* speed_stats) override {
    315     if (jpeg_decoder_ == "jpegli") {
    316 #if JPEGXL_ENABLE_JPEGLI
    317       std::vector<uint8_t> jpeg_bytes(compressed.data(),
    318                                       compressed.data() + compressed.size());
    319       const double start = jxl::Now();
    320       jxl::extras::JpegDecompressParams dparams;
    321       dparams.output_data_type =
    322           bitdepth_ > 8 ? JXL_TYPE_UINT16 : JXL_TYPE_UINT8;
    323       dparams.num_colors = num_colors_;
    324       JXL_RETURN_IF_ERROR(
    325           jxl::extras::DecodeJpeg(jpeg_bytes, dparams, pool, ppf));
    326       const double end = jxl::Now();
    327       speed_stats->NotifyElapsed(end - start);
    328 #endif
    329     } else {
    330       const double start = jxl::Now();
    331       jxl::extras::JPGDecompressParams dparams;
    332       dparams.num_colors = num_colors_;
    333       JXL_RETURN_IF_ERROR(
    334           jxl::extras::DecodeImageJPG(compressed, jxl::extras::ColorHints(),
    335                                       ppf, /*constraints=*/nullptr, &dparams));
    336       const double end = jxl::Now();
    337       speed_stats->NotifyElapsed(end - start);
    338     }
    339     return true;
    340   }
    341 
    342  protected:
    343   // JPEG encoder and its parameters
    344   std::string jpeg_encoder_ = "libjpeg";
    345   std::string chroma_subsampling_;
    346   int progressive_id_ = -1;
    347   bool fix_codes_ = false;
    348   float psnr_target_ = 0.0f;
    349   bool enc_quality_set_ = false;
    350   int libjpeg_quality_ = 0;
    351   std::string libjpeg_chroma_subsampling_;
    352 #if JPEGXL_ENABLE_JPEGLI
    353   bool xyb_mode_ = false;
    354   bool use_std_tables_ = false;
    355 #endif
    356   bool enable_adaptive_quant_ = true;
    357   // JPEG decoder and its parameters
    358   std::string jpeg_decoder_ = "libjpeg";
    359   int num_colors_ = 0;
    360 #if JPEGXL_ENABLE_JPEGLI
    361   size_t bitdepth_ = 8;
    362 #endif
    363 };
    364 
    365 ImageCodec* CreateNewJPEGCodec(const BenchmarkArgs& args) {
    366   return new JPEGCodec(args);
    367 }
    368 
    369 }  // namespace tools
    370 }  // namespace jpegxl