summaryrefslogtreecommitdiff
path: root/tools/cjxl.cc
diff options
context:
space:
mode:
Diffstat (limited to 'tools/cjxl.cc')
-rw-r--r--tools/cjxl.cc851
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."),
- &params.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"),
- &params.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."),
- &params.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).",
- &params.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",
- &params.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",
- &params.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).",
- &params.keep_invisible, &ParseOverride, 1);
-
- cmdline->AddOptionFlag('\0', "centerfirst",
- "Put center groups first in the compressed file.",
- &params.centerfirst, &SetBooleanTrue, 1);
-
- cmdline->AddOptionValue('\0', "center_x", "0..XSIZE",
- "Put center groups first in the compressed file.",
- &params.center_x, &ParseUnsigned, 1);
- cmdline->AddOptionValue('\0', "center_y", "0..YSIZE",
- "Put center groups first in the compressed file.",
- &params.center_y, &ParseUnsigned, 1);
-
- // Flags.
- cmdline->AddOptionFlag('\0', "progressive_ac",
- "Use the progressive mode for AC.",
- &params.progressive_mode, &SetBooleanTrue, 1);
- cmdline->AddOptionFlag('\0', "qprogressive_ac",
- "Use the progressive mode for AC.",
- &params.qprogressive_mode, &SetBooleanTrue, 1);
- cmdline->AddOptionValue('\0', "progressive_dc", "num_dc_frames",
- "Use progressive mode for DC.",
- &params.progressive_dc, &ParseSigned, 1);
- cmdline->AddOptionFlag('m', "modular",
- "Use the modular mode (lossy / lossless).",
- &params.modular_mode, &SetBooleanTrue, 1);
- cmdline->AddOptionFlag('\0', "use_new_heuristics",
- "use new and not yet ready encoder heuristics",
- &params.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.",
- &params.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.",
- &params.photon_noise_iso, &ParsePhotonNoiseParameter, 1);
- cmdline->AddOptionValue('\0', "dots", "0|1",
- "force disable/enable dots generation.", &params.dots,
- &ParseOverride, 1);
- cmdline->AddOptionValue('\0', "patches", "0|1",
- "force disable/enable patches generation.",
- &params.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.",
- &params.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.",
- &params.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.",
- &params.already_downsampled, &SetBooleanTrue, 2);
-
- cmdline->AddOptionValue(
- '\0', "epf", "-1..3",
- "Edge preserving filter level (-1 = choose based on quality, default)",
- &params.epf, &ParseSigned, 1);
-
- cmdline->AddOptionValue('\0', "gaborish", "0|1",
- "force disable/enable gaborish.", &params.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,
- &params.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,
- &params.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",
- &params.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)",
- &params.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)",
- &params.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)"),
- &params.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)"),
- &params.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",
- &params.options.predictor, &ParsePredictor, 1);
-
- cmdline->AddOptionValue(
- 'E', "extra-properties", "K",
- "[modular encoding] number of extra MA tree properties to use",
- &params.options.max_properties, &ParseSigned, 2);
-
- cmdline->AddOptionValue('\0', "palette", "K",
- "[modular encoding] use a palette if image has at "
- "most K colors (default: 1024)",
- &params.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",
- &params.lossy_palette, &SetBooleanTrue, 1);
-
- cmdline->AddOptionValue(
- 'X', "pre-compact", "PERCENT",
- ("[modular encoding] compact channels (globally) if ratio "
- "used/range is below this (default: 80%)"),
- &params.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%)"),
- &params.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)",
- &params.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 (!).",
- &params.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