Skip to content

Commit b8ccee1

Browse files
committed
Fast cbrt approximation
1 parent 4ccda9d commit b8ccee1

File tree

1 file changed

+56
-3
lines changed

1 file changed

+56
-3
lines changed

dssim-core/src/tolab.rs

Lines changed: 56 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,9 @@ impl ToLAB for RGBLU {
3131

3232
let epsilon: f32 = 216. / 24389.;
3333
let k = 24389. / (27. * 116.); // http://www.brucelindbloom.com/LContinuity.html
34-
let X = if fx > epsilon { fx.cbrt() - 16. / 116. } else { k * fx };
35-
let Y = if fy > epsilon { fy.cbrt() - 16. / 116. } else { k * fy };
36-
let Z = if fz > epsilon { fz.cbrt() - 16. / 116. } else { k * fz };
34+
let X = if fx > epsilon { cbrt_poly(fx) - 16. / 116. } else { k * fx };
35+
let Y = if fy > epsilon { cbrt_poly(fy) - 16. / 116. } else { k * fy };
36+
let Z = if fz > epsilon { cbrt_poly(fz) - 16. / 116. } else { k * fz };
3737

3838
let lab = (
3939
(Y * 1.05f32), // 1.05 instead of 1.16 to boost color importance without pushing colors outside of 1.0 range
@@ -45,6 +45,21 @@ impl ToLAB for RGBLU {
4545
}
4646
}
4747

48+
fn cbrt_poly(x: f32) -> f32 {
49+
// Polynomial approximation
50+
let poly = [0.2, 1.51, -0.5];
51+
let y = (poly[2] * x + poly[1]) * x + poly[0];
52+
53+
// 2x Halley's Method
54+
let y3 = y*y*y;
55+
let y = y * (y3 + 2. * x) / (2. * y3 + x);
56+
let y3 = y*y*y;
57+
let y = y * (y3 + 2. * x) / (2. * y3 + x);
58+
debug_assert!(y < 1.001);
59+
debug_assert!(x < 216. / 24389. || y >= 16. / 116.);
60+
y
61+
}
62+
4863
/// Convert image to L\*a\*b\* planar
4964
///
5065
/// It should return 1 (gray) or 3 (color) planes.
@@ -153,3 +168,41 @@ impl ToLABBitmap for ImgRef<'_, RGBLU> {
153168
})
154169
}
155170
}
171+
172+
#[test]
173+
fn cbrts1() {
174+
let mut totaldiff = 0.;
175+
let mut maxdiff: f64 = 0.;
176+
for i in (0..=10001).rev() {
177+
let x = (i as f64 / 10001.) as f32;
178+
let a = cbrt_poly(x);
179+
let actual = a * a * a;
180+
let expected = x;
181+
let absdiff = (expected as f64 - actual as f64).abs();
182+
assert!(absdiff < 0.0002, "{expected} - {actual} = {} @ {x}", expected - actual);
183+
if i % 400 == 0 {
184+
println!("{:+0.3}", (expected - actual)*255.);
185+
}
186+
totaldiff += absdiff;
187+
maxdiff = maxdiff.max(absdiff);
188+
}
189+
println!("1={totaldiff:0.6}; {maxdiff:0.8}");
190+
assert!(totaldiff < 0.0025, "{totaldiff}");
191+
}
192+
193+
#[test]
194+
fn cbrts2() {
195+
let mut totaldiff = 0.;
196+
let mut maxdiff: f64 = 0.;
197+
for i in (2000..=10001).rev() {
198+
let x = i as f64 / 10001.;
199+
let actual = cbrt_poly(x as f32) as f64;
200+
let expected = x.cbrt();
201+
let absdiff = (expected - actual).abs();
202+
totaldiff += absdiff;
203+
maxdiff = maxdiff.max(absdiff);
204+
assert!(absdiff < 0.0000005, "{expected} - {actual} = {} @ {x}", expected - actual);
205+
}
206+
println!("2={totaldiff:0.6}; {maxdiff:0.8}");
207+
assert!(totaldiff < 0.0025, "{totaldiff}");
208+
}

0 commit comments

Comments
 (0)