benchmark_codec_custom.cc (7482B)
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 "tools/benchmark/benchmark_codec_custom.h" 7 8 #include <jxl/types.h> 9 #include <stdio.h> 10 11 #include <cstdint> 12 #include <string> 13 #include <utility> 14 #include <vector> 15 16 #include "lib/extras/dec/color_hints.h" 17 #include "lib/extras/packed_image_convert.h" 18 #include "lib/jxl/base/span.h" 19 #include "lib/jxl/base/status.h" 20 #include "tools/benchmark/benchmark_args.h" 21 #include "tools/benchmark/benchmark_codec.h" 22 #include "tools/speed_stats.h" 23 24 // Not supported on Windows due to Linux-specific functions. 25 #ifndef _WIN32 26 27 #include <libgen.h> 28 29 #include <fstream> 30 31 #include "lib/extras/codec.h" 32 #include "lib/extras/time.h" 33 #include "lib/jxl/image_bundle.h" 34 #include "tools/benchmark/benchmark_utils.h" 35 #include "tools/file_io.h" 36 #include "tools/thread_pool_internal.h" 37 38 namespace jpegxl { 39 namespace tools { 40 41 struct CustomCodecArgs { 42 std::string extension; 43 std::string colorspace; 44 bool quiet; 45 }; 46 47 static CustomCodecArgs* const custom_args = new CustomCodecArgs; 48 49 Status AddCommandLineOptionsCustomCodec(BenchmarkArgs* args) { 50 args->AddString( 51 &custom_args->extension, "custom_codec_extension", 52 "Converts input and output of codec to this file type (default: png).", 53 "png"); 54 args->AddString( 55 &custom_args->colorspace, "custom_codec_colorspace", 56 "If not empty, converts input and output of codec to this colorspace.", 57 ""); 58 args->AddFlag(&custom_args->quiet, "custom_codec_quiet", 59 "Whether stdin and stdout of custom codec should be shown.", 60 false); 61 return true; 62 } 63 64 namespace { 65 66 // This uses `output_filename` to determine the name of the corresponding 67 // `.time` file. 68 template <typename F> 69 Status ReportCodecRunningTime(F&& function, std::string output_filename, 70 jpegxl::tools::SpeedStats* const speed_stats) { 71 const double start = jxl::Now(); 72 JXL_RETURN_IF_ERROR(function()); 73 const double end = jxl::Now(); 74 const std::string time_filename = 75 GetBaseName(std::move(output_filename)) + ".time"; 76 std::ifstream time_stream(time_filename); 77 double time; 78 if (time_stream >> time) { 79 // Report the time measured by the external codec itself. 80 speed_stats->NotifyElapsed(time); 81 } else { 82 // Fall back to the less accurate time that we measured. 83 speed_stats->NotifyElapsed(end - start); 84 } 85 if (time_stream.is_open()) { 86 remove(time_filename.c_str()); 87 } 88 return true; 89 } 90 91 class CustomCodec : public ImageCodec { 92 public: 93 explicit CustomCodec(const BenchmarkArgs& args) : ImageCodec(args) {} 94 95 Status ParseParam(const std::string& param) override { 96 if (param_index_ == 0) { 97 description_ = ""; 98 } 99 switch (param_index_) { 100 case 0: 101 extension_ = param; 102 description_ += param; 103 break; 104 case 1: 105 compress_command_ = param; 106 description_ += std::string(":"); 107 if (param.find_last_of('/') < param.size()) { 108 description_ += param.substr(param.find_last_of('/') + 1); 109 } else { 110 description_ += param; 111 } 112 break; 113 case 2: 114 decompress_command_ = param; 115 break; 116 default: 117 compress_args_.push_back(param); 118 description_ += std::string(":"); 119 if (param.size() > 2 && param[0] == '-' && param[1] == '-') { 120 description_ += param.substr(2); 121 } else if (param.size() > 2 && param[0] == '-') { 122 description_ += param.substr(1); 123 } else { 124 description_ += param; 125 } 126 break; 127 } 128 ++param_index_; 129 return true; 130 } 131 132 Status Compress(const std::string& filename, const PackedPixelFile& ppf, 133 ThreadPool* pool, std::vector<uint8_t>* compressed, 134 jpegxl::tools::SpeedStats* speed_stats) override { 135 JXL_RETURN_IF_ERROR(param_index_ > 2); 136 137 const std::string basename = GetBaseName(filename); 138 TemporaryFile in_file(basename, custom_args->extension); 139 TemporaryFile encoded_file(basename, extension_); 140 std::string in_filename; 141 std::string encoded_filename; 142 JXL_RETURN_IF_ERROR(in_file.GetFileName(&in_filename)); 143 JXL_RETURN_IF_ERROR(encoded_file.GetFileName(&encoded_filename)); 144 // TODO(szabadka) Support custom_args->colorspace again. 145 std::vector<uint8_t> encoded; 146 JXL_RETURN_IF_ERROR(Encode(ppf, in_filename, &encoded, pool)); 147 JXL_RETURN_IF_ERROR(WriteFile(in_filename, encoded)); 148 std::vector<std::string> arguments = compress_args_; 149 arguments.push_back(in_filename); 150 arguments.push_back(encoded_filename); 151 JXL_RETURN_IF_ERROR(ReportCodecRunningTime( 152 [&, this] { 153 return RunCommand(compress_command_, arguments, custom_args->quiet); 154 }, 155 encoded_filename, speed_stats)); 156 return ReadFile(encoded_filename, compressed); 157 } 158 159 Status Decompress(const std::string& filename, 160 const Span<const uint8_t> compressed, ThreadPool* pool, 161 PackedPixelFile* ppf, 162 jpegxl::tools::SpeedStats* speed_stats) override { 163 CodecInOut io; 164 JXL_RETURN_IF_ERROR( 165 Decompress(filename, compressed, pool, &io, speed_stats)); 166 JxlPixelFormat format{0, JXL_TYPE_UINT16, JXL_NATIVE_ENDIAN, 0}; 167 return jxl::extras::ConvertCodecInOutToPackedPixelFile( 168 io, format, io.Main().c_current(), pool, ppf); 169 }; 170 171 Status Decompress(const std::string& filename, 172 const Span<const uint8_t> compressed, ThreadPool* pool, 173 CodecInOut* io, jpegxl::tools::SpeedStats* speed_stats) { 174 const std::string basename = GetBaseName(filename); 175 TemporaryFile encoded_file(basename, extension_); 176 TemporaryFile out_file(basename, custom_args->extension); 177 std::string encoded_filename; 178 std::string out_filename; 179 JXL_RETURN_IF_ERROR(encoded_file.GetFileName(&encoded_filename)); 180 JXL_RETURN_IF_ERROR(out_file.GetFileName(&out_filename)); 181 182 JXL_RETURN_IF_ERROR(WriteFile(encoded_filename, compressed)); 183 JXL_RETURN_IF_ERROR(ReportCodecRunningTime( 184 [&, this] { 185 return RunCommand( 186 decompress_command_, 187 std::vector<std::string>{encoded_filename, out_filename}, 188 custom_args->quiet); 189 }, 190 out_filename, speed_stats)); 191 jxl::extras::ColorHints hints; 192 if (!custom_args->colorspace.empty()) { 193 hints.Add("color_space", custom_args->colorspace); 194 } 195 std::vector<uint8_t> encoded; 196 JXL_RETURN_IF_ERROR(ReadFile(out_filename, &encoded)); 197 JXL_RETURN_IF_ERROR( 198 jxl::SetFromBytes(jxl::Bytes(encoded), hints, io, pool)); 199 io->metadata.m.SetIntensityTarget(saved_intensity_target_); 200 return true; 201 } 202 203 private: 204 std::string extension_; 205 std::string compress_command_; 206 std::string decompress_command_; 207 std::vector<std::string> compress_args_; 208 int param_index_ = 0; 209 int saved_intensity_target_ = 255; 210 }; 211 212 } // namespace 213 214 ImageCodec* CreateNewCustomCodec(const BenchmarkArgs& args) { 215 return new CustomCodec(args); 216 } 217 218 } // namespace tools 219 } // namespace jpegxl 220 221 #else 222 223 namespace jpegxl { 224 namespace tools { 225 226 ImageCodec* CreateNewCustomCodec(const BenchmarkArgs& args) { return nullptr; } 227 Status AddCommandLineOptionsCustomCodec(BenchmarkArgs* args) { return true; } 228 229 } // namespace tools 230 } // namespace jpegxl 231 232 #endif // _MSC_VER