Coverage Report

Created: 2025-06-16 07:00

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