libjxl

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

butteraugli.h (8156B)


      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 // Author: Jyrki Alakuijala (jyrki.alakuijala@gmail.com)
      7 
      8 #ifndef LIB_JXL_BUTTERAUGLI_BUTTERAUGLI_H_
      9 #define LIB_JXL_BUTTERAUGLI_BUTTERAUGLI_H_
     10 
     11 #include <stdint.h>
     12 #include <stdlib.h>
     13 #include <string.h>
     14 
     15 #include <atomic>
     16 #include <cmath>
     17 #include <cstddef>
     18 #include <memory>
     19 
     20 #include "lib/jxl/base/compiler_specific.h"
     21 #include "lib/jxl/base/status.h"
     22 #include "lib/jxl/image.h"
     23 
     24 #define BUTTERAUGLI_ENABLE_CHECKS 0
     25 #define BUTTERAUGLI_RESTRICT JXL_RESTRICT
     26 
     27 // This is the main interface to butteraugli image similarity
     28 // analysis function.
     29 
     30 namespace jxl {
     31 
     32 struct ButteraugliParams {
     33   // Multiplier for penalizing new HF artifacts more than blurring away
     34   // features. 1.0=neutral.
     35   float hf_asymmetry = 1.0f;
     36 
     37   // Multiplier for the psychovisual difference in the X channel.
     38   float xmul = 1.0f;
     39 
     40   // Number of nits that correspond to 1.0f input values.
     41   float intensity_target = 80.0f;
     42 };
     43 
     44 // ButteraugliInterface defines the public interface for butteraugli.
     45 //
     46 // It calculates the difference between rgb0 and rgb1.
     47 //
     48 // rgb0 and rgb1 contain the images. rgb0[c][px] and rgb1[c][px] contains
     49 // the red image for c == 0, green for c == 1, blue for c == 2. Location index
     50 // px is calculated as y * xsize + x.
     51 //
     52 // Value of pixels of images rgb0 and rgb1 need to be represented as raw
     53 // intensity. Most image formats store gamma corrected intensity in pixel
     54 // values. This gamma correction has to be removed, by applying the following
     55 // function to values in the 0-1 range:
     56 // butteraugli_val = pow(input_val, gamma);
     57 // A typical value of gamma is 2.2. It is usually stored in the image header.
     58 // Take care not to confuse that value with its inverse. The gamma value should
     59 // be always greater than one.
     60 // Butteraugli does not work as intended if the caller does not perform
     61 // gamma correction.
     62 //
     63 // hf_asymmetry is a multiplier for penalizing new HF artifacts more than
     64 // blurring away features (1.0 -> neutral).
     65 //
     66 // diffmap will contain an image of the size xsize * ysize, containing
     67 // localized differences for values px (indexed with the px the same as rgb0
     68 // and rgb1). diffvalue will give a global score of similarity.
     69 //
     70 // A diffvalue smaller than kButteraugliGood indicates that images can be
     71 // observed as the same image.
     72 // diffvalue larger than kButteraugliBad indicates that a difference between
     73 // the images can be observed.
     74 // A diffvalue between kButteraugliGood and kButteraugliBad indicates that
     75 // a subtle difference can be observed between the images.
     76 //
     77 // Returns true on success.
     78 bool ButteraugliInterface(const Image3F &rgb0, const Image3F &rgb1,
     79                           const ButteraugliParams &params, ImageF &diffmap,
     80                           double &diffvalue);
     81 
     82 // Deprecated (calls the previous function)
     83 bool ButteraugliInterface(const Image3F &rgb0, const Image3F &rgb1,
     84                           float hf_asymmetry, float xmul, ImageF &diffmap,
     85                           double &diffvalue);
     86 
     87 // Same as ButteraugliInterface, but reuses rgb0 and rgb1 for other purposes
     88 // inside the function after they are not needed any more, and it ignores
     89 // params.xmul.
     90 Status ButteraugliInterfaceInPlace(Image3F &&rgb0, Image3F &&rgb1,
     91                                    const ButteraugliParams &params,
     92                                    ImageF &diffmap, double &diffvalue);
     93 
     94 // Converts the butteraugli score into fuzzy class values that are continuous
     95 // at the class boundary. The class boundary location is based on human
     96 // raters, but the slope is arbitrary. Particularly, it does not reflect
     97 // the expectation value of probabilities of the human raters. It is just
     98 // expected that a smoother class boundary will allow for higher-level
     99 // optimization algorithms to work faster.
    100 //
    101 // Returns 2.0 for a perfect match, and 1.0 for 'ok', 0.0 for bad. Because the
    102 // scoring is fuzzy, a butteraugli score of 0.96 would return a class of
    103 // around 1.9.
    104 double ButteraugliFuzzyClass(double score);
    105 
    106 // Input values should be in range 0 (bad) to 2 (good). Use
    107 // kButteraugliNormalization as normalization.
    108 double ButteraugliFuzzyInverse(double seek);
    109 
    110 // Implementation details, don't use anything below or your code will
    111 // break in the future.
    112 
    113 #ifdef _MSC_VER
    114 #define BUTTERAUGLI_INLINE __forceinline
    115 #else
    116 #define BUTTERAUGLI_INLINE inline
    117 #endif
    118 
    119 #ifdef __clang__
    120 // Early versions of Clang did not support __builtin_assume_aligned.
    121 #define BUTTERAUGLI_HAS_ASSUME_ALIGNED __has_builtin(__builtin_assume_aligned)
    122 #elif defined(__GNUC__)
    123 #define BUTTERAUGLI_HAS_ASSUME_ALIGNED 1
    124 #else
    125 #define BUTTERAUGLI_HAS_ASSUME_ALIGNED 0
    126 #endif
    127 
    128 // Returns a void* pointer which the compiler then assumes is N-byte aligned.
    129 // Example: float* JXL_RESTRICT aligned = (float*)JXL_ASSUME_ALIGNED(in, 32);
    130 //
    131 // The assignment semantics are required by GCC/Clang. ICC provides an in-place
    132 // __assume_aligned, whereas MSVC's __assume appears unsuitable.
    133 #if BUTTERAUGLI_HAS_ASSUME_ALIGNED
    134 #define BUTTERAUGLI_ASSUME_ALIGNED(ptr, align) \
    135   __builtin_assume_aligned((ptr), (align))
    136 #else
    137 #define BUTTERAUGLI_ASSUME_ALIGNED(ptr, align) (ptr)
    138 #endif  // BUTTERAUGLI_HAS_ASSUME_ALIGNED
    139 
    140 struct PsychoImage {
    141   ImageF uhf[2];  // XY
    142   ImageF hf[2];   // XY
    143   Image3F mf;     // XYB
    144   Image3F lf;     // XYB
    145 };
    146 
    147 // Blur needs a transposed image.
    148 // Hold it here and only allocate on demand to reduce memory usage.
    149 struct BlurTemp {
    150   Status GetTransposed(const ImageF &in, ImageF **out) {
    151     if (transposed_temp.xsize() == 0) {
    152       JXL_ASSIGN_OR_RETURN(transposed_temp,
    153                            ImageF::Create(in.ysize(), in.xsize()));
    154     }
    155     *out = &transposed_temp;
    156     return true;
    157   }
    158 
    159   ImageF transposed_temp;
    160 };
    161 
    162 class ButteraugliComparator {
    163  public:
    164   // Butteraugli is calibrated at xmul = 1.0. We add a multiplier here so that
    165   // we can test the hypothesis that a higher weighing of the X channel would
    166   // improve results at higher Butteraugli values.
    167   virtual ~ButteraugliComparator() = default;
    168 
    169   static StatusOr<std::unique_ptr<ButteraugliComparator>> Make(
    170       const Image3F &rgb0, const ButteraugliParams &params);
    171 
    172   // Computes the butteraugli map between the original image given in the
    173   // constructor and the distorted image give here.
    174   Status Diffmap(const Image3F &rgb1, ImageF &result) const;
    175 
    176   // Same as above, but OpsinDynamicsImage() was already applied.
    177   Status DiffmapOpsinDynamicsImage(const Image3F &xyb1, ImageF &result) const;
    178 
    179   // Same as above, but the frequency decomposition was already applied.
    180   Status DiffmapPsychoImage(const PsychoImage &pi1, ImageF &diffmap) const;
    181 
    182   Status Mask(ImageF *BUTTERAUGLI_RESTRICT mask) const;
    183 
    184  private:
    185   ButteraugliComparator(size_t xsize, size_t ysize,
    186                         const ButteraugliParams &params);
    187   Image3F *Temp() const;
    188   void ReleaseTemp() const;
    189 
    190   const size_t xsize_;
    191   const size_t ysize_;
    192   ButteraugliParams params_;
    193   PsychoImage pi0_;
    194 
    195   // Shared temporary image storage to reduce the number of allocations;
    196   // obtained via Temp(), must call ReleaseTemp when no longer needed.
    197   mutable Image3F temp_;
    198   mutable std::atomic_flag temp_in_use_ = ATOMIC_FLAG_INIT;
    199 
    200   mutable BlurTemp blur_temp_;
    201   std::unique_ptr<ButteraugliComparator> sub_;
    202 };
    203 
    204 // Deprecated.
    205 Status ButteraugliDiffmap(const Image3F &rgb0, const Image3F &rgb1,
    206                           double hf_asymmetry, double xmul, ImageF &diffmap);
    207 
    208 Status ButteraugliDiffmap(const Image3F &rgb0, const Image3F &rgb1,
    209                           const ButteraugliParams &params, ImageF &diffmap);
    210 
    211 double ButteraugliScoreFromDiffmap(const ImageF &diffmap,
    212                                    const ButteraugliParams *params = nullptr);
    213 
    214 // Generate rgb-representation of the distance between two images.
    215 StatusOr<Image3F> CreateHeatMapImage(const ImageF &distmap,
    216                                      double good_threshold,
    217                                      double bad_threshold);
    218 
    219 }  // namespace jxl
    220 
    221 #endif  // LIB_JXL_BUTTERAUGLI_BUTTERAUGLI_H_