diff options
Diffstat (limited to 'tools/cjxl.cc')
-rw-r--r-- | tools/cjxl.cc | 851 |
1 files changed, 0 insertions, 851 deletions
diff --git a/tools/cjxl.cc b/tools/cjxl.cc deleted file mode 100644 index 4377027..0000000 --- a/tools/cjxl.cc +++ /dev/null @@ -1,851 +0,0 @@ -// Copyright (c) the JPEG XL Project Authors. All rights reserved. -// -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -#include "tools/cjxl.h" - -#include <math.h> -#include <stdint.h> -#include <stdio.h> - -#include <algorithm> -#include <string> -#include <utility> -#include <vector> - -#include "lib/extras/codec.h" -#if JPEGXL_ENABLE_JPEG -#include "lib/extras/codec_jpg.h" -#endif - -#include "lib/extras/time.h" -#include "lib/jxl/aux_out.h" -#include "lib/jxl/base/cache_aligned.h" -#include "lib/jxl/base/compiler_specific.h" -#include "lib/jxl/base/data_parallel.h" -#include "lib/jxl/base/padded_bytes.h" -#include "lib/jxl/base/profiler.h" -#include "lib/jxl/base/status.h" -#include "lib/jxl/base/thread_pool_internal.h" -#include "lib/jxl/codec_in_out.h" -#include "lib/jxl/common.h" -#include "lib/jxl/enc_cache.h" -#include "lib/jxl/enc_file.h" -#include "lib/jxl/enc_params.h" -#include "lib/jxl/frame_header.h" -#include "lib/jxl/image.h" -#include "lib/jxl/image_bundle.h" -#include "lib/jxl/modular/encoding/encoding.h" -#include "tools/args.h" -#include "tools/box/box.h" -#include "tools/cpu/cpu.h" -#include "tools/speed_stats.h" - -namespace jpegxl { -namespace tools { -namespace { - -static inline bool ParseSpeedTier(const char* arg, jxl::SpeedTier* out) { - return jxl::ParseSpeedTier(arg, out); -} -static inline bool ParseColorTransform(const char* arg, - jxl::ColorTransform* out) { - size_t value = 0; - bool ret = ParseUnsigned(arg, &value); - if (ret && value > 2) ret = false; - if (ret) *out = jxl::ColorTransform(value); - return ret; -} -static inline bool ParseIntensityTarget(const char* arg, float* out) { - return ParseFloat(arg, out) && *out > 0; -} -static inline bool ParsePhotonNoiseParameter(const char* arg, float* out) { - return strncmp(arg, "ISO", 3) == 0 && ParseFloat(arg + 3, out) && *out > 0; -} - -// Proposes a distance to try for a given bpp target. This could depend -// on the entropy in the image, too, but let's start with something. -static double ApproximateDistanceForBPP(double bpp) { - return 1.704 * pow(bpp, -0.804); -} - -jxl::Status LoadSaliencyMap(const std::string& filename_heatmap, - jxl::ThreadPool* pool, jxl::ImageF* out_map) { - jxl::CodecInOut io_heatmap; - if (!SetFromFile(filename_heatmap, jxl::ColorHints(), &io_heatmap, pool)) { - return JXL_FAILURE("Could not load heatmap."); - } - *out_map = std::move(io_heatmap.Main().color()->Plane(0)); - return true; -} - -// Search algorithm for modular mode instead of Butteraugli distance. -void SetModularQualityForBitrate(jxl::ThreadPoolInternal* pool, - const size_t pixels, const double target_size, - CompressArgs* args) { - JXL_ASSERT(args->params.modular_mode); - - CompressArgs s = *args; // Args for search. - float quality = -100 + target_size * 8.0 / pixels * 50; - if (quality > 100.f) quality = 100.f; - s.params.target_size = 0; - s.params.target_bitrate = 0; - double best_loss = 1e99; - float best_quality = quality; - float best_below = -10000.f; - float best_below_size = 0; - float best_above = 200.f; - float best_above_size = pixels * 15.f; - - jxl::CodecInOut io; - double decode_mps = 0; - - if (!LoadAll(*args, pool, &io, &decode_mps)) { - s.params.quality_pair = std::make_pair(quality, quality); - printf("couldn't load image\n"); - return; - } - - for (int i = 0; i < 10; ++i) { - s.params.quality_pair = std::make_pair(quality, quality); - jxl::PaddedBytes candidate; - bool ok = - CompressJxl(io, decode_mps, pool, s, &candidate, /*print_stats=*/false); - if (!ok) { - printf( - "Compression error occurred during the search for best size." - " Trying with quality %.1f\n", - quality); - break; - } - printf("Quality %.2f yields %6zu bytes, %.3f bpp.\n", quality, - candidate.size(), candidate.size() * 8.0 / pixels); - const double ratio = static_cast<double>(candidate.size()) / target_size; - const double loss = std::abs(1.0 - ratio); - if (best_loss > loss) { - best_quality = quality; - best_loss = loss; - if (loss < 0.01f) break; - } - if (quality == 100.f && ratio < 1.f) break; // can't spend more bits - if (ratio > 1.f && quality < best_above) { - best_above = quality; - best_above_size = candidate.size(); - } - if (ratio < 1.f && quality > best_below) { - best_below = quality; - best_below_size = candidate.size(); - } - float t = - (target_size - best_below_size) / (best_above_size - best_below_size); - if (best_above > 100.f && ratio < 1.f) { - quality = (quality + 105) / 2; - } else if (best_above - best_below > 1000 && ratio > 1.f) { - quality -= 1000; - } else { - quality = best_above * t + best_below * (1.f - t); - } - if (quality >= 100.f) quality = 100.f; - } - args->params.quality_pair = std::make_pair(best_quality, best_quality); - args->params.target_bitrate = 0; - args->params.target_size = 0; -} - -void SetParametersForSizeOrBitrate(jxl::ThreadPoolInternal* pool, - const size_t pixels, CompressArgs* args) { - CompressArgs s = *args; // Args for search. - - // If fixed size, convert to bitrate. - if (s.params.target_size > 0) { - s.params.target_bitrate = s.params.target_size * 8.0 / pixels; - s.params.target_size = 0; - } - const double target_size = s.params.target_bitrate * (1 / 8.) * pixels; - - if (args->params.modular_mode) { - SetModularQualityForBitrate(pool, pixels, target_size, args); - return; - } - - double dist = ApproximateDistanceForBPP(s.params.target_bitrate); - s.params.target_bitrate = 0; - double best_dist = 1.0; - double best_loss = 1e99; - - jxl::CodecInOut io; - double decode_mps = 0; - if (!LoadAll(*args, pool, &io, &decode_mps)) { - s.params.butteraugli_distance = static_cast<float>(dist); - printf("couldn't load image\n"); - return; - } - - for (int i = 0; i < 7; ++i) { - s.params.butteraugli_distance = static_cast<float>(dist); - jxl::PaddedBytes candidate; - bool ok = - CompressJxl(io, decode_mps, pool, s, &candidate, /*print_stats=*/false); - if (!ok) { - printf( - "Compression error occurred during the search for best size. " - "Trying with butteraugli distance %.15g\n", - best_dist); - break; - } - printf("Butteraugli distance %.3f yields %6zu bytes, %.3f bpp.\n", dist, - candidate.size(), candidate.size() * 8.0 / pixels); - const double ratio = static_cast<double>(candidate.size()) / target_size; - const double loss = std::max(ratio, 1.0 / std::max(ratio, 1e-30)); - if (best_loss > loss) { - best_dist = dist; - best_loss = loss; - } - dist *= ratio; - if (dist < 0.01) { - dist = 0.01; - } - if (dist >= 16.0) { - dist = 16.0; - } - } - args->params.butteraugli_distance = static_cast<float>(best_dist); - args->params.target_bitrate = 0; - args->params.target_size = 0; -} - -const char* ModeFromArgs(const CompressArgs& args) { - if (args.jpeg_transcode) return "JPEG"; - if (args.params.modular_mode) return "Modular"; - return "VarDCT"; -} - -std::string QualityFromArgs(const CompressArgs& args) { - char buf[100]; - if (args.jpeg_transcode) { - snprintf(buf, sizeof(buf), "lossless transcode"); - } else if (args.params.modular_mode) { - if (args.params.quality_pair.first == 100 && - args.params.quality_pair.second == 100) { - snprintf(buf, sizeof(buf), "lossless"); - } else if (args.params.quality_pair.first != - args.params.quality_pair.second) { - snprintf(buf, sizeof(buf), "Q%.2f,%.2f", args.params.quality_pair.first, - args.params.quality_pair.second); - } else { - snprintf(buf, sizeof(buf), "Q%.2f", args.params.quality_pair.first); - } - } else { - snprintf(buf, sizeof(buf), "d%.3f", args.params.butteraugli_distance); - } - return buf; -} - -void PrintMode(jxl::ThreadPoolInternal* pool, const jxl::CodecInOut& io, - const double decode_mps, const CompressArgs& args) { - const char* mode = ModeFromArgs(args); - const char* speed = SpeedTierName(args.params.speed_tier); - const std::string quality = QualityFromArgs(args); - fprintf(stderr, - "Read %zux%zu image, %.1f MP/s\n" - "Encoding [%s%s, %s, %s", - io.xsize(), io.ysize(), decode_mps, - (args.use_container ? "Container | " : ""), mode, quality.c_str(), - speed); - if (args.use_container) { - if (args.jpeg_transcode) fprintf(stderr, " | JPEG reconstruction data"); - if (!io.blobs.exif.empty()) - fprintf(stderr, " | %zu-byte Exif", io.blobs.exif.size()); - if (!io.blobs.xmp.empty()) - fprintf(stderr, " | %zu-byte XMP", io.blobs.xmp.size()); - if (!io.blobs.jumbf.empty()) - fprintf(stderr, " | %zu-byte JUMBF", io.blobs.jumbf.size()); - } - fprintf(stderr, "], %zu threads.\n", pool->NumWorkerThreads()); -} - -} // namespace - -void CompressArgs::AddCommandLineOptions(CommandLineParser* cmdline) { - // Positional arguments. - cmdline->AddPositionalOption("INPUT", /* required = */ true, - "the input can be PNG" -#if JPEGXL_ENABLE_APNG - ", APNG" -#endif -#if JPEGXL_ENABLE_GIF - ", GIF" -#endif -#if JPEGXL_ENABLE_JPEG - ", JPEG" -#endif -#if JPEGXL_ENABLE_EXR - ", EXR" -#endif - ", PPM, PFM, or PGX", - &file_in); - cmdline->AddPositionalOption( - "OUTPUT", /* required = */ true, - "the compressed JXL output file (can be omitted for benchmarking)", - &file_out); - - // Flags. - // TODO(lode): also add options to add exif/xmp/other metadata in the - // container. - // TODO(lode): decide on good name for this flag: box, container, bmff, ... - cmdline->AddOptionFlag( - '\0', "container", - "Always encode using container format (default: only if needed)", - &use_container, &SetBooleanTrue, 1); - - cmdline->AddOptionFlag('\0', "strip", - "Do not encode using container format (strips " - "Exif/XMP/JPEG bitstream reconstruction data)", - &no_container, &SetBooleanTrue, 2); - - // Target distance/size/bpp - opt_distance_id = cmdline->AddOptionValue( - 'd', "distance", "maxError", - ("Max. butteraugli distance, lower = higher quality. Range: 0 .. 25.\n" - " 0.0 = mathematically lossless. Default for already-lossy input " - "(JPEG/GIF).\n" - " 1.0 = visually lossless. Default for other input.\n" - " Recommended range: 0.5 .. 3.0."), - ¶ms.butteraugli_distance, &ParseFloat); - opt_target_size_id = cmdline->AddOptionValue( - '\0', "target_size", "N", - ("Aim at file size of N bytes.\n" - " Compresses to 1 % of the target size in ideal conditions.\n" - " Runs the same algorithm as --target_bpp"), - ¶ms.target_size, &ParseUnsigned, 1); - opt_target_bpp_id = cmdline->AddOptionValue( - '\0', "target_bpp", "BPP", - ("Aim at file size that has N bits per pixel.\n" - " Compresses to 1 % of the target BPP in ideal conditions."), - ¶ms.target_bitrate, &ParseFloat, 1); - - // High-level options - opt_quality_id = cmdline->AddOptionValue( - 'q', "quality", "QUALITY", - "Quality setting (is remapped to --distance). Range: -inf .. 100.\n" - " 100 = mathematically lossless. Default for already-lossy input " - "(JPEG/GIF).\n Positive quality values roughly match libjpeg quality.", - &quality, &ParseFloat); - - cmdline->AddOptionValue( - 'e', "effort", "EFFORT", - "Encoder effort setting. Range: 1 .. 9.\n" - " Default: 7. Higher number is more effort (slower).", - ¶ms.speed_tier, &ParseSpeedTier); - - cmdline->AddOptionValue( - 's', "speed", "ANIMAL", - "Deprecated synonym for --effort. Valid values are:\n" - " lightning (1), thunder, falcon, cheetah, hare, wombat, squirrel, " - "kitten, tortoise (9)\n" - " Default: squirrel. Values are in order from faster to slower.\n", - ¶ms.speed_tier, &ParseSpeedTier, 2); - - cmdline->AddOptionValue('\0', "faster_decoding", "AMOUNT", - "Favour higher decoding speed. 0 = default, higher " - "values give higher speed at the expense of quality", - ¶ms.decoding_speed_tier, &ParseUnsigned, 2); - - cmdline->AddOptionFlag('p', "progressive", - "Enable progressive/responsive decoding.", - &progressive, &SetBooleanTrue); - - cmdline->AddOptionFlag('\0', "premultiply", - "Force premultiplied (associated) alpha.", - &force_premultiplied, &SetBooleanTrue, 1); - cmdline->AddOptionValue('\0', "keep_invisible", "0|1", - "force disable/enable preserving color of invisible " - "pixels (default: 1 if lossless, 0 if lossy).", - ¶ms.keep_invisible, &ParseOverride, 1); - - cmdline->AddOptionFlag('\0', "centerfirst", - "Put center groups first in the compressed file.", - ¶ms.centerfirst, &SetBooleanTrue, 1); - - cmdline->AddOptionValue('\0', "center_x", "0..XSIZE", - "Put center groups first in the compressed file.", - ¶ms.center_x, &ParseUnsigned, 1); - cmdline->AddOptionValue('\0', "center_y", "0..YSIZE", - "Put center groups first in the compressed file.", - ¶ms.center_y, &ParseUnsigned, 1); - - // Flags. - cmdline->AddOptionFlag('\0', "progressive_ac", - "Use the progressive mode for AC.", - ¶ms.progressive_mode, &SetBooleanTrue, 1); - cmdline->AddOptionFlag('\0', "qprogressive_ac", - "Use the progressive mode for AC.", - ¶ms.qprogressive_mode, &SetBooleanTrue, 1); - cmdline->AddOptionValue('\0', "progressive_dc", "num_dc_frames", - "Use progressive mode for DC.", - ¶ms.progressive_dc, &ParseSigned, 1); - cmdline->AddOptionFlag('m', "modular", - "Use the modular mode (lossy / lossless).", - ¶ms.modular_mode, &SetBooleanTrue, 1); - cmdline->AddOptionFlag('\0', "use_new_heuristics", - "use new and not yet ready encoder heuristics", - ¶ms.use_new_heuristics, &SetBooleanTrue, 2); - - // JPEG modes: parallel Brunsli, pixels to JPEG, or JPEG to Brunsli - cmdline->AddOptionFlag('j', "jpeg_transcode", - "Do lossy transcode of input JPEG file (decode to " - "pixels instead of doing lossless transcode).", - &jpeg_transcode, &SetBooleanFalse, 1); - - opt_num_threads_id = cmdline->AddOptionValue( - '\0', "num_threads", "N", "number of worker threads (zero = none).", - &num_threads, &ParseUnsigned, 1); - cmdline->AddOptionValue('\0', "num_reps", "N", "how many times to compress.", - &num_reps, &ParseUnsigned, 1); - - cmdline->AddOptionValue('\0', "noise", "0|1", - "force disable/enable noise generation.", - ¶ms.noise, &ParseOverride, 1); - cmdline->AddOptionValue( - '\0', "photon_noise", "ISO3200", - "Set the noise to approximately what it would be at a given nominal " - "exposure on a 35mm camera. For formats other than 35mm, or when the " - "whole sensor was not used, you can multiply the ISO value by the " - "equivalence ratio squared, for example by 2.25 for an APS-C camera.", - ¶ms.photon_noise_iso, &ParsePhotonNoiseParameter, 1); - cmdline->AddOptionValue('\0', "dots", "0|1", - "force disable/enable dots generation.", ¶ms.dots, - &ParseOverride, 1); - cmdline->AddOptionValue('\0', "patches", "0|1", - "force disable/enable patches generation.", - ¶ms.patches, &ParseOverride, 1); - cmdline->AddOptionValue( - '\0', "resampling", "0|1|2|4|8", - "Subsample all color channels by this factor, or use 0 to choose the " - "resampling factor based on distance.", - ¶ms.resampling, &ParseUnsigned, 0); - cmdline->AddOptionValue( - '\0', "ec_resampling", "1|2|4|8", - "Subsample all extra channels by this factor. If this value is smaller " - "than the resampling of color channels, it will be increased to match.", - ¶ms.ec_resampling, &ParseUnsigned, 2); - cmdline->AddOptionFlag('\0', "already_downsampled", - "Do not downsample the given input before encoding, " - "but still signal that the decoder should upsample.", - ¶ms.already_downsampled, &SetBooleanTrue, 2); - - cmdline->AddOptionValue( - '\0', "epf", "-1..3", - "Edge preserving filter level (-1 = choose based on quality, default)", - ¶ms.epf, &ParseSigned, 1); - - cmdline->AddOptionValue('\0', "gaborish", "0|1", - "force disable/enable gaborish.", ¶ms.gaborish, - &ParseOverride, 1); - - opt_intensity_target_id = cmdline->AddOptionValue( - '\0', "intensity_target", "N", - ("Intensity target of monitor in nits, higher\n" - " results in higher quality image. Must be strictly positive.\n" - " Default is 255 for standard images, 4000 for input images known to\n" - " to have PQ or HLG transfer function."), - &intensity_target, &ParseIntensityTarget, 1); - - cmdline->AddOptionValue('\0', "saliency_num_progressive_steps", "N", nullptr, - ¶ms.saliency_num_progressive_steps, - &ParseUnsigned, 2); - cmdline->AddOptionValue('\0', "saliency_map_filename", "STRING", nullptr, - &saliency_map_filename, &ParseString, 2); - cmdline->AddOptionValue('\0', "saliency_threshold", "0..1", nullptr, - ¶ms.saliency_threshold, &ParseFloat, 2); - - cmdline->AddOptionValue( - 'x', "dec-hints", "key=value", - "color_space indicates the ColorEncoding, see Description();\n" - "icc_pathname refers to a binary file containing an ICC profile.", - &color_hints, &ParseAndAppendKeyValue, 1); - - cmdline->AddOptionValue( - '\0', "override_bitdepth", "0=use from image, 1-32=override", - "If nonzero, store the given bit depth in the JPEG XL file metadata" - " (1-32), instead of using the bit depth from the original input" - " image.", - &override_bitdepth, &ParseUnsigned, 2); - - opt_color_id = cmdline->AddOptionValue( - 'c', "colortransform", "0..2", "0=XYB, 1=None, 2=YCbCr", - ¶ms.color_transform, &ParseColorTransform, 2); - - // modular mode options - cmdline->AddOptionValue( - 'Q', "mquality", "luma_q[,chroma_q]", - "[modular encoding] lossy 'quality' (100=lossless, lower is more lossy)", - ¶ms.quality_pair, &ParseFloatPair, 1); - - cmdline->AddOptionValue( - 'I', "iterations", "F", - "[modular encoding] fraction of pixels used to learn MA trees " - "(default=0.5, try 0 for no MA and fast decode)", - ¶ms.options.nb_repeats, &ParseFloat, 2); - - cmdline->AddOptionValue( - 'C', "colorspace", "K", - ("[modular encoding] color transform: 0=RGB, 1=YCoCg, " - "2-37=RCT (default: try several, depending on speed)"), - ¶ms.colorspace, &ParseSigned, 1); - - opt_m_group_size_id = cmdline->AddOptionValue( - 'g', "group-size", "K", - ("[modular encoding] set group size to 128 << K " - "(default: 1 or 2)"), - ¶ms.modular_group_size_shift, &ParseUnsigned, 1); - - cmdline->AddOptionValue( - 'P', "predictor", "K", - "[modular encoding] predictor(s) to use: 0=zero, " - "1=left, 2=top, 3=avg0, 4=select, 5=gradient, 6=weighted, " - "7=topright, 8=topleft, 9=leftleft, 10=avg1, 11=avg2, 12=avg3, " - "13=toptop predictive average " - "14=mix 5 and 6, 15=mix everything. Default 14, at slowest speed " - "default 15", - ¶ms.options.predictor, &ParsePredictor, 1); - - cmdline->AddOptionValue( - 'E', "extra-properties", "K", - "[modular encoding] number of extra MA tree properties to use", - ¶ms.options.max_properties, &ParseSigned, 2); - - cmdline->AddOptionValue('\0', "palette", "K", - "[modular encoding] use a palette if image has at " - "most K colors (default: 1024)", - ¶ms.palette_colors, &ParseSigned, 1); - - cmdline->AddOptionFlag( - '\0', "lossy-palette", - "[modular encoding] quantize to a palette that has fewer entries than " - "would be necessary for perfect preservation; for the time being, it is " - "recommended to set --palette=0 with this option to use the default " - "palette only", - ¶ms.lossy_palette, &SetBooleanTrue, 1); - - cmdline->AddOptionValue( - 'X', "pre-compact", "PERCENT", - ("[modular encoding] compact channels (globally) if ratio " - "used/range is below this (default: 80%)"), - ¶ms.channel_colors_pre_transform_percent, &ParseFloat, 2); - - cmdline->AddOptionValue( - 'Y', "post-compact", "PERCENT", - ("[modular encoding] compact channels (per-group) if ratio " - "used/range is below this (default: 80%)"), - ¶ms.channel_colors_percent, &ParseFloat, 2); - - cmdline->AddOptionValue('R', "responsive", "K", - "[modular encoding] do Squeeze transform, 0=false, " - "1=true (default: true if lossy, false if lossless)", - ¶ms.responsive, &ParseSigned, 1); - - cmdline->AddOptionFlag('V', "version", "Print version number and exit", - &version, &SetBooleanTrue, 1); - cmdline->AddOptionFlag('\0', "quiet", "Be more silent", &quiet, - &SetBooleanTrue, 1); - cmdline->AddOptionValue('\0', "print_profile", "0|1", - "Print timing information before exiting", - &print_profile, &ParseOverride, 1); - - cmdline->AddOptionFlag( - 'v', "verbose", - "Verbose output; can be repeated, also applies to help (!).", - ¶ms.verbose, &SetBooleanTrue); -} - -jxl::Status CompressArgs::ValidateArgs(const CommandLineParser& cmdline) { - params.file_in = file_in; - params.file_out = file_out; - - if (file_in == nullptr) { - fprintf(stderr, "Missing INPUT filename.\n"); - return false; - } - - bool got_distance = cmdline.GetOption(opt_distance_id)->matched(); - bool got_target_size = cmdline.GetOption(opt_target_size_id)->matched(); - bool got_target_bpp = cmdline.GetOption(opt_target_bpp_id)->matched(); - bool got_quality = cmdline.GetOption(opt_quality_id)->matched(); - bool got_intensity_target = - cmdline.GetOption(opt_intensity_target_id)->matched(); - - if (got_quality) { - default_settings = false; - if (quality < 100) jpeg_transcode = false; - // Quality settings roughly match libjpeg qualities. - if (quality < 7 || quality == 100 || params.modular_mode) { - if (jpeg_transcode == false) params.modular_mode = true; - // Internal modular quality to roughly match VarDCT size. - if (quality < 7) { - params.quality_pair.first = params.quality_pair.second = - std::min(35 + (quality - 7) * 3.0f, 100.0f); - } else { - params.quality_pair.first = params.quality_pair.second = - std::min(35 + (quality - 7) * 65.f / 93.f, 100.0f); - } - } else { - if (quality >= 30) { - params.butteraugli_distance = 0.1 + (100 - quality) * 0.09; - } else { - params.butteraugli_distance = - 6.4 + pow(2.5, (30 - quality) / 5.0f) / 6.25f; - } - } - } - if (params.resampling > 1 && !params.already_downsampled) - jpeg_transcode = false; - - if (progressive) { - params.qprogressive_mode = true; - params.responsive = 1; - default_settings = false; - } - if (got_target_size || got_target_bpp || got_intensity_target) { - default_settings = false; - } - - if (params.progressive_dc < -1 || params.progressive_dc > 2) { - fprintf(stderr, "Invalid/out of range progressive_dc (%d), try -1 to 2.\n", - params.progressive_dc); - return false; - } - - if (got_distance) { - constexpr float butteraugli_min_dist = 0.1f; - constexpr float butteraugli_max_dist = 25.0f; - if (!(0 <= params.butteraugli_distance && - params.butteraugli_distance <= butteraugli_max_dist)) { - fprintf(stderr, "Invalid/out of range distance, try 0 to %g.\n", - butteraugli_max_dist); - return false; - } - if (params.butteraugli_distance > 0) jpeg_transcode = false; - if (params.butteraugli_distance == 0) { - // Use modular for lossless. - if (jpeg_transcode == false) params.modular_mode = true; - } else if (params.butteraugli_distance < butteraugli_min_dist) { - params.butteraugli_distance = butteraugli_min_dist; - } - default_settings = false; - } - - if (got_target_bpp || got_target_size) { - jpeg_transcode = false; - } - - if (got_target_bpp + got_target_size + got_distance + got_quality > 1) { - fprintf(stderr, - "You can specify only one of '--distance', '-q', " - "'--target_bpp' and '--target_size'. They are all different ways" - " to specify the image quality. When in doubt, use --distance." - " It gives the most visually consistent results.\n"); - return false; - } - - if (!saliency_map_filename.empty()) { - if (!params.progressive_mode) { - saliency_map_filename.clear(); - fprintf(stderr, - "Warning: Specifying --saliency_map_filename only makes sense " - "for --progressive_ac mode.\n"); - } - } - - if (!params.file_in) { - fprintf(stderr, "Missing input filename.\n"); - return false; - } - - if (!cmdline.GetOption(opt_color_id)->matched()) { - // default to RGB for lossless modular - if (params.modular_mode) { - if (params.quality_pair.first != 100 || - params.quality_pair.second != 100) { - params.color_transform = jxl::ColorTransform::kXYB; - } else { - params.color_transform = jxl::ColorTransform::kNone; - } - } - } - - if (override_bitdepth > 32) { - fprintf(stderr, "override_bitdepth must be <= 32\n"); - return false; - } - - if (params.epf > 3) { - fprintf(stderr, "--epf must be in the 0..3 range\n"); - return false; - } - - // User didn't override num_threads, so we have to compute a default, which - // might fail, so only do so when necessary. Don't just check num_threads != 0 - // because the user may have set it to that. - if (!cmdline.GetOption(opt_num_threads_id)->matched()) { - cpu::ProcessorTopology topology; - if (!cpu::DetectProcessorTopology(&topology)) { - // We have seen sporadic failures caused by setaffinity_np. - fprintf(stderr, - "Failed to choose default num_threads; you can avoid this " - "error by specifying a --num_threads N argument.\n"); - return false; - } - num_threads = topology.packages * topology.cores_per_package; - } - - return true; -} - -jxl::Status CompressArgs::ValidateArgsAfterLoad( - const CommandLineParser& cmdline, const jxl::CodecInOut& io) { - if (!ValidateArgs(cmdline)) return false; - bool got_m_group_size = cmdline.GetOption(opt_m_group_size_id)->matched(); - if (params.modular_mode && !got_m_group_size) { - // Default modular group size: set to 512 if 256 would be silly - const size_t kThinImageThr = 256 + 64; - const size_t kSmallImageThr = 256 + 128; - if (io.xsize() < kThinImageThr || io.ysize() < kThinImageThr || - (io.xsize() < kSmallImageThr && io.ysize() < kSmallImageThr)) { - params.modular_group_size_shift = 2; - } - } - if (!io.blobs.exif.empty() || !io.blobs.xmp.empty() || - !io.blobs.jumbf.empty() || !io.blobs.iptc.empty() || jpeg_transcode) { - use_container = true; - } - if (no_container) use_container = false; - if (jpeg_transcode && params.modular_mode) { - fprintf(stderr, - "Error: cannot do lossless JPEG transcode in modular mode.\n"); - return false; - } - if (jpeg_transcode) { - if (params.progressive_mode || params.qprogressive_mode || - params.progressive_dc > 0) { - fprintf(stderr, - "Error: progressive lossless JPEG transcode is not yet " - "implemented.\n"); - return false; - } - } - return true; -} - -jxl::Status LoadAll(CompressArgs& args, jxl::ThreadPoolInternal* pool, - jxl::CodecInOut* io, double* decode_mps) { - const double t0 = jxl::Now(); - - io->target_nits = args.intensity_target; - io->dec_target = (args.jpeg_transcode ? jxl::DecodeTarget::kQuantizedCoeffs - : jxl::DecodeTarget::kPixels); - jxl::Codec input_codec; - if (!SetFromFile(args.params.file_in, args.color_hints, io, nullptr, - &input_codec)) { - fprintf(stderr, "Failed to read image %s.\n", args.params.file_in); - return false; - } - if (input_codec != jxl::Codec::kJPG) args.jpeg_transcode = false; - if (args.jpeg_transcode) args.params.butteraugli_distance = 0; - - if (input_codec == jxl::Codec::kGIF && args.default_settings) { - args.params.modular_mode = true; - args.params.quality_pair.first = args.params.quality_pair.second = 100; - } - if (args.override_bitdepth != 0) { - if (args.override_bitdepth == 32) { - io->metadata.m.SetFloat32Samples(); - } else { - io->metadata.m.SetUintSamples(args.override_bitdepth); - } - } - if (args.force_premultiplied) { - io->PremultiplyAlpha(); - } - - jxl::ImageF saliency_map; - if (!args.saliency_map_filename.empty()) { - if (!LoadSaliencyMap(args.saliency_map_filename, pool, &saliency_map)) { - fprintf(stderr, "Failed to read saliency map %s.\n", - args.saliency_map_filename.c_str()); - return false; - } - args.params.saliency_map = &saliency_map; - } - - const double t1 = jxl::Now(); - const size_t pixels = io->xsize() * io->ysize(); - *decode_mps = pixels * io->frames.size() * 1E-6 / (t1 - t0); - - return true; -} - -jxl::Status CompressJxl(jxl::CodecInOut& io, double decode_mps, - jxl::ThreadPoolInternal* pool, CompressArgs& args, - jxl::PaddedBytes* compressed, bool print_stats) { - JXL_CHECK(pool); - - const size_t pixels = io.xsize() * io.ysize(); - - if (args.params.target_size > 0 || args.params.target_bitrate > 0) { - // Slow iterative search for parameters that reach target bpp / size. - SetParametersForSizeOrBitrate(pool, pixels, &args); - } - - if (print_stats) PrintMode(pool, io, decode_mps, args); - - // Final/actual compression run (possibly repeated for benchmarking). - jxl::AuxOut aux_out; - if (args.inspector_image3f) { - aux_out.SetInspectorImage3F(args.inspector_image3f); - } - SpeedStats stats; - jxl::PassesEncoderState passes_encoder_state; - if (args.params.use_new_heuristics) { - passes_encoder_state.heuristics = - jxl::make_unique<jxl::FastEncoderHeuristics>(); - } - for (size_t i = 0; i < args.num_reps; ++i) { - const double t0 = jxl::Now(); - jxl::Status ok = false; - if (io.Main().IsJPEG()) { - // TODO(lode): automate this in the encoder. The encoder must in the - // beginning choose to either do all in xyb, or all in non-xyb, write - // that in the xyb_encoded header flag, and persistently keep that state - // to check if every frame uses an allowed color transform. - args.params.color_transform = io.Main().color_transform; - } - ok = EncodeFile(args.params, &io, &passes_encoder_state, compressed, - &aux_out, pool); - if (!ok) { - fprintf(stderr, "Failed to compress to %s.\n", ModeFromArgs(args)); - return false; - } - const double t1 = jxl::Now(); - stats.NotifyElapsed(t1 - t0); - stats.SetImageSize(io.xsize(), io.ysize()); - } - - if (print_stats) { - const double bpp = - static_cast<double>(compressed->size() * jxl::kBitsPerByte) / pixels; - fprintf(stderr, "Compressed to %zu bytes (%.3f bpp%s).\n", - compressed->size(), bpp / io.frames.size(), - io.frames.size() == 1 ? "" : "/frame"); - JXL_CHECK(stats.Print(args.num_threads)); - if (args.params.verbose) { - aux_out.Print(1); - } - } - - return true; -} - -} // namespace tools -} // namespace jpegxl |