diff options
Diffstat (limited to 'media/libjxl/src/lib/jxl/aux_out.h')
-rw-r--r-- | media/libjxl/src/lib/jxl/aux_out.h | 310 |
1 files changed, 310 insertions, 0 deletions
diff --git a/media/libjxl/src/lib/jxl/aux_out.h b/media/libjxl/src/lib/jxl/aux_out.h new file mode 100644 index 0000000000..23d498652f --- /dev/null +++ b/media/libjxl/src/lib/jxl/aux_out.h @@ -0,0 +1,310 @@ +// 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. + +#ifndef LIB_JXL_AUX_OUT_H_ +#define LIB_JXL_AUX_OUT_H_ + +// Optional output information for debugging and analyzing size usage. + +#include <inttypes.h> +#include <stddef.h> +#include <stdint.h> +#include <stdio.h> + +#include <array> +#include <functional> +#include <sstream> +#include <string> +#include <utility> + +#include "lib/jxl/aux_out_fwd.h" +#include "lib/jxl/base/compiler_specific.h" +#include "lib/jxl/base/status.h" +#include "lib/jxl/codec_in_out.h" +#include "lib/jxl/color_management.h" +#include "lib/jxl/common.h" +#include "lib/jxl/dec_xyb.h" +#include "lib/jxl/image.h" +#include "lib/jxl/image_bundle.h" +#include "lib/jxl/image_ops.h" +#include "lib/jxl/jxl_inspection.h" + +namespace jxl { + +// For LayerName and AuxOut::layers[] index. Order does not matter. +enum { + kLayerHeader = 0, + kLayerTOC, + kLayerNoise, + kLayerQuant, + kLayerDequantTables, + kLayerOrder, + kLayerDC, + kLayerControlFields, + kLayerAC, + kLayerACTokens, + kLayerDictionary, + kLayerDots, + kLayerSplines, + kLayerLossless, + kLayerModularGlobal, + kLayerModularDcGroup, + kLayerModularAcGroup, + kLayerModularTree, + kLayerAlpha, + kLayerDepth, + kLayerExtraChannels, + kNumImageLayers +}; + +static inline const char* LayerName(size_t layer) { + switch (layer) { + case kLayerHeader: + return "headers"; + case kLayerTOC: + return "TOC"; + case kLayerNoise: + return "noise"; + case kLayerQuant: + return "quantizer"; + case kLayerDequantTables: + return "quant tables"; + case kLayerOrder: + return "order"; + case kLayerDC: + return "DC"; + case kLayerControlFields: + return "ControlFields"; + case kLayerAC: + return "AC"; + case kLayerACTokens: + return "ACTokens"; + case kLayerDictionary: + return "dictionary"; + case kLayerDots: + return "dots"; + case kLayerSplines: + return "splines"; + case kLayerLossless: + return "lossless"; + case kLayerModularGlobal: + return "modularGlobal"; + case kLayerModularDcGroup: + return "modularDcGroup"; + case kLayerModularAcGroup: + return "modularAcGroup"; + case kLayerModularTree: + return "modularTree"; + case kLayerAlpha: + return "alpha"; + case kLayerDepth: + return "depth"; + case kLayerExtraChannels: + return "extra channels"; + default: + JXL_ABORT("Invalid layer %d\n", static_cast<int>(layer)); + } +} + +// Statistics gathered during compression or decompression. +struct AuxOut { + private: + struct LayerTotals { + void Assimilate(const LayerTotals& victim) { + num_clustered_histograms += victim.num_clustered_histograms; + histogram_bits += victim.histogram_bits; + extra_bits += victim.extra_bits; + total_bits += victim.total_bits; + clustered_entropy += victim.clustered_entropy; + } + void Print(size_t num_inputs) const { + printf("%10" PRId64, static_cast<int64_t>(total_bits)); + if (histogram_bits != 0) { + printf(" [c/i:%6.2f | hst:%8" PRId64 " | ex:%8" PRId64 + " | h+c+e:%12.3f", + num_clustered_histograms * 1.0 / num_inputs, + static_cast<int64_t>(histogram_bits >> 3), + static_cast<int64_t>(extra_bits >> 3), + (histogram_bits + clustered_entropy + extra_bits) / 8.0); + printf("]"); + } + printf("\n"); + } + size_t num_clustered_histograms = 0; + size_t extra_bits = 0; + + // Set via BitsWritten below + size_t histogram_bits = 0; + size_t total_bits = 0; + + double clustered_entropy = 0.0; + }; + + public: + AuxOut() = default; + AuxOut(const AuxOut&) = default; + + void Assimilate(const AuxOut& victim) { + for (size_t i = 0; i < layers.size(); ++i) { + layers[i].Assimilate(victim.layers[i]); + } + num_blocks += victim.num_blocks; + num_small_blocks += victim.num_small_blocks; + num_dct4x8_blocks += victim.num_dct4x8_blocks; + num_afv_blocks += victim.num_afv_blocks; + num_dct8_blocks += victim.num_dct8_blocks; + num_dct8x16_blocks += victim.num_dct8x16_blocks; + num_dct8x32_blocks += victim.num_dct8x32_blocks; + num_dct16_blocks += victim.num_dct16_blocks; + num_dct16x32_blocks += victim.num_dct16x32_blocks; + num_dct32_blocks += victim.num_dct32_blocks; + num_dct32x64_blocks += victim.num_dct32x64_blocks; + num_dct64_blocks += victim.num_dct64_blocks; + num_butteraugli_iters += victim.num_butteraugli_iters; + for (size_t i = 0; i < dc_pred_usage.size(); ++i) { + dc_pred_usage[i] += victim.dc_pred_usage[i]; + dc_pred_usage_xb[i] += victim.dc_pred_usage_xb[i]; + } + } + + void Print(size_t num_inputs) const; + + template <typename T> + void DumpImage(const char* label, const Image3<T>& image) const { + if (!dump_image) return; + if (debug_prefix.empty()) return; + std::ostringstream pathname; + pathname << debug_prefix << label << ".png"; + CodecInOut io; + // Always save to 16-bit png. + io.metadata.m.SetUintSamples(16); + io.metadata.m.color_encoding = ColorEncoding::SRGB(); + io.SetFromImage(ConvertToFloat(image), io.metadata.m.color_encoding); + (void)dump_image(io, pathname.str()); + } + template <typename T> + void DumpImage(const char* label, const Plane<T>& image) { + DumpImage(label, + Image3<T>(CopyImage(image), CopyImage(image), CopyImage(image))); + } + + template <typename T> + void DumpXybImage(const char* label, const Image3<T>& image) const { + if (!dump_image) return; + if (debug_prefix.empty()) return; + std::ostringstream pathname; + pathname << debug_prefix << label << ".png"; + + Image3F linear(image.xsize(), image.ysize()); + OpsinParams opsin_params; + opsin_params.Init(kDefaultIntensityTarget); + OpsinToLinear(image, Rect(linear), nullptr, &linear, opsin_params); + + CodecInOut io; + io.metadata.m.SetUintSamples(16); + io.metadata.m.color_encoding = ColorEncoding::LinearSRGB(); + io.SetFromImage(std::move(linear), io.metadata.m.color_encoding); + + (void)dump_image(io, pathname.str()); + } + + // Normalizes all the channels to range 0-1, creating a false-color image + // which allows seeing the information from non-RGB channels in an RGB debug + // image. + template <typename T> + void DumpImageNormalized(const char* label, const Image3<T>& image) const { + std::array<T, 3> min; + std::array<T, 3> max; + Image3MinMax(image, &min, &max); + Image3B normalized(image.xsize(), image.ysize()); + for (size_t c = 0; c < 3; ++c) { + float mul = min[c] == max[c] ? 0 : (255.0f / (max[c] - min[c])); + for (size_t y = 0; y < image.ysize(); ++y) { + const T* JXL_RESTRICT row_in = image.ConstPlaneRow(c, y); + uint8_t* JXL_RESTRICT row_out = normalized.PlaneRow(c, y); + for (size_t x = 0; x < image.xsize(); ++x) { + row_out[x] = static_cast<uint8_t>((row_in[x] - min[c]) * mul); + } + } + } + DumpImage(label, normalized); + } + + template <typename T> + void DumpPlaneNormalized(const char* label, const Plane<T>& image) const { + T min; + T max; + ImageMinMax(image, &min, &max); + Image3B normalized(image.xsize(), image.ysize()); + for (size_t c = 0; c < 3; ++c) { + float mul = min == max ? 0 : (255.0f / (max - min)); + for (size_t y = 0; y < image.ysize(); ++y) { + const T* JXL_RESTRICT row_in = image.ConstRow(y); + uint8_t* JXL_RESTRICT row_out = normalized.PlaneRow(c, y); + for (size_t x = 0; x < image.xsize(); ++x) { + row_out[x] = static_cast<uint8_t>((row_in[x] - min) * mul); + } + } + } + DumpImage(label, normalized); + } + + void SetInspectorImage3F(const jxl::InspectorImage3F& inspector) { + inspector_image3f_ = inspector; + } + + // Allows hooking intermediate data inspection into various places of the + // processing pipeline. Returns true iff processing should proceed. + bool InspectImage3F(const char* label, const Image3F& image) { + if (inspector_image3f_ != nullptr) { + return inspector_image3f_(label, image); + } + return true; + } + + std::array<LayerTotals, kNumImageLayers> layers; + size_t num_blocks = 0; + + // Number of blocks that use larger DCT (set by ac_strategy). + size_t num_small_blocks = 0; + size_t num_dct4x8_blocks = 0; + size_t num_afv_blocks = 0; + size_t num_dct8_blocks = 0; + size_t num_dct8x16_blocks = 0; + size_t num_dct8x32_blocks = 0; + size_t num_dct16_blocks = 0; + size_t num_dct16x32_blocks = 0; + size_t num_dct32_blocks = 0; + size_t num_dct32x64_blocks = 0; + size_t num_dct64_blocks = 0; + + std::array<uint32_t, 8> dc_pred_usage = {{0}}; + std::array<uint32_t, 8> dc_pred_usage_xb = {{0}}; + + int num_butteraugli_iters = 0; + + // If not empty, additional debugging information (e.g. debug images) is + // saved in files with this prefix. + std::string debug_prefix; + + // By how much the decoded image was downsampled relative to the encoded + // image. + size_t downsampling = 1; + + jxl::InspectorImage3F inspector_image3f_; + + std::function<Status(const CodecInOut&, const std::string&)> dump_image = + nullptr; +}; + +// Used to skip image creation if they won't be written to debug directory. +static inline bool WantDebugOutput(const AuxOut* aux_out) { + // Need valid pointer and filename. + return aux_out != nullptr && !aux_out->debug_prefix.empty(); +} + +} // namespace jxl + +#endif // LIB_JXL_AUX_OUT_H_ |