1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
//! Butteraugli, a perceptual full-reference metric.
//!
//! This binds Google libjxl's Butteraugli comparator via FFI rather than
//! reimplementing it in Rust, keeping results faithful to upstream. The C++
//! sources are vendored under `third_party/` and compiled by `build.rs`,
//! sharing the libjxl subset that the [`ssimulacra2`](crate::ssimulacra2)
//! metric also uses.
use crate;
use crate;
use crateImage;
/// Minimum width and height the reference implementation accepts.
const MIN_DIMENSION: u32 = 8;
/// A pixel format that [`butteraugli`] can score.
///
/// Butteraugli expects sRGB-encoded input (it gamma-expands internally), so
/// this trait is implemented only for the sRGB-family formats (grayscale
/// counts: it is treated as sRGB-encoded luma). It is the seam that keeps a
/// non-sRGB image from reaching the metric: were a linear-light format added to
/// the crate, omitting its `ButteraugliInput` impl would make passing it to
/// [`butteraugli`] a compile error, with no change to the function signature.
/// Tuning parameters for [`butteraugli`].
///
/// The defaults match libjxl's own (`intensity_target = 80`,
/// `hf_asymmetry = 1.0`) together with Butteraugli's canonical `pnorm = 3.0`,
/// so [`ButteraugliOptions::default`] reproduces a standard Butteraugli score.
/// Computes the Butteraugli distance between `reference` and `distorted`.
///
/// The distance is `0.0` for identical images and grows with perceived
/// difference (**lower is better**); a value near `1.0` corresponds roughly to
/// a just-noticeable difference. Both images share the format `F`, which the
/// [`ButteraugliInput`] bound additionally constrains to an sRGB-family format.
/// Each must be at least 8x8. See [`ButteraugliOptions`] for tuning.
///
/// # Errors
///
/// - [`Error::DimensionMismatch`] if the images differ in size.
/// - [`Error::ImageTooSmall`] if either dimension is below 8 pixels.
/// - [`Error::ButteraugliFailed`] if the native implementation reports failure.
///
/// # Examples
///
/// ```no_run
/// use iqa::{ButteraugliOptions, Image, butteraugli};
///
/// let reference = Image::srgb8(8, 8, vec![128; 192])?;
/// let distorted = Image::srgb8(8, 8, vec![130; 192])?;
/// let distance = butteraugli(&reference, &distorted, ButteraugliOptions::default())?;
/// assert!(distance >= 0.0);
/// # Ok::<(), iqa::Error>(())
/// ```
///
/// Comparing two different pixel formats does not type-check:
///
/// ```compile_fail
/// use iqa::{ButteraugliOptions, Image, butteraugli};
///
/// let rgb = Image::srgb8(8, 8, vec![0; 192])?;
/// let gray = Image::gray8(8, 8, vec![0; 64])?;
/// let _ = butteraugli(&rgb, &gray, ButteraugliOptions::default()); // mismatched formats
/// # Ok::<(), iqa::Error>(())
/// ```