From e4ea8b38b3395221b05a51a9affc8f9b44fdc1e9 Mon Sep 17 00:00:00 2001 From: Dan Burzo Date: Sun, 7 Jan 2024 22:39:48 +0200 Subject: [PATCH] Adds PQ EOTF/EOTF^-1 (interal for now), clarify Jzazbz transfer function re: PQ --- src/hdr/transfer.js | 30 ++++++++++++++++++++++++++++++ src/jab/convertJabToXyz65.js | 22 ++++++++++++---------- src/jab/convertXyz65ToJab.js | 20 ++++++++++---------- 3 files changed, 52 insertions(+), 20 deletions(-) create mode 100644 src/hdr/transfer.js diff --git a/src/hdr/transfer.js b/src/hdr/transfer.js new file mode 100644 index 00000000..5b3e39de --- /dev/null +++ b/src/hdr/transfer.js @@ -0,0 +1,30 @@ +/* + https://en.wikipedia.org/wiki/Transfer_functions_in_imaging +*/ + +export const M1 = 0.1593017578125; +export const M2 = 78.84375; +export const C1 = 0.8359375; +export const C2 = 18.8515625; +export const C3 = 18.6875; + +/* + Perceptual Quantizer, as defined in Rec. BT 2100-2 (2018) + + * https://www.itu.int/rec/R-REC-BT.2100-2-201807-I/en + * https://en.wikipedia.org/wiki/Perceptual_quantizer +*/ + +/* PQ EOTF, defined for `v` in [0,1]. */ +export function transferPqDecode(v) { + if (v < 0) return 0; + const c = Math.pow(v, 1 / M2); + return 1e4 * Math.pow(Math.max(0, c - C1) / (C2 - C3 * c), 1 / M1); +} + +/* PQ EOTF^-1, defined for `v` in [0, 1e4]. */ +export function transferPqEncode(v) { + if (v < 0) return 0; + const c = Math.pow(v / 1e4, M1); + return Math.pow((C1 + C2 * c) / (1 + C3 * c), M2); +} diff --git a/src/jab/convertJabToXyz65.js b/src/jab/convertJabToXyz65.js index 84856d19..8c3493cb 100644 --- a/src/jab/convertJabToXyz65.js +++ b/src/jab/convertJabToXyz65.js @@ -1,14 +1,16 @@ -const n = 0.1593017578125; // = 2610 / Math.pow(2, 14); +import { M1 as n, C1, C2, C3 } from '../hdr/transfer.js'; const p = 134.03437499999998; // = 1.7 * 2523 / Math.pow(2, 5); -const c1 = 0.8359375; // = 3424 / Math.pow(2, 12); -const c2 = 18.8515625; // = 2413 / Math.pow(2, 7); -const c3 = 18.6875; // = 2392 / Math.pow(2, 7); const d0 = 1.6295499532821566e-11; -/* `v` may be negative, in which case return 0 instead of NaN */ -const pq_inv = v => { +const npow = (v, exp) => Math.sign(v) * Math.pow(Math.abs(v), exp); + +/* + The encoding function is derived from Perceptual Quantizer. +*/ +const jabPqDecode = v => { + if (v < 0) return 0; let vp = Math.pow(v, 1 / p); - return 10000 * Math.pow((c1 - vp) / (c3 * vp - c2), 1 / n) || 0; + return 10000 * Math.pow((C1 - vp) / (C3 * vp - C2), 1 / n); }; const rel = v => v / 203; @@ -16,9 +18,9 @@ const rel = v => v / 203; const convertJabToXyz65 = ({ j, a, b, alpha }) => { let i = (j + d0) / (0.44 + 0.56 * (j + d0)); - let l = pq_inv(i + 0.13860504 * a + 0.058047316 * b); - let m = pq_inv(i - 0.13860504 * a - 0.058047316 * b); - let s = pq_inv(i - 0.096019242 * a - 0.8118919 * b); + let l = jabPqDecode(i + 0.13860504 * a + 0.058047316 * b); + let m = jabPqDecode(i - 0.13860504 * a - 0.058047316 * b); + let s = jabPqDecode(i - 0.096019242 * a - 0.8118919 * b); let res = { mode: 'xyz65', diff --git a/src/jab/convertXyz65ToJab.js b/src/jab/convertXyz65ToJab.js index bf8aa02d..c90bf48c 100644 --- a/src/jab/convertXyz65ToJab.js +++ b/src/jab/convertXyz65ToJab.js @@ -1,14 +1,14 @@ -const n = 0.1593017578125; // = 2610 / Math.pow(2, 14); +import { M1 as n, C1, C2, C3 } from '../hdr/transfer.js'; const p = 134.03437499999998; // = 1.7 * 2523 / Math.pow(2, 5); -const c1 = 0.8359375; // = 3424 / Math.pow(2, 12); -const c2 = 18.8515625; // = 2413 / Math.pow(2, 7); -const c3 = 18.6875; // = 2392 / Math.pow(2, 7); const d0 = 1.6295499532821566e-11; -/* `v` may be negative, in which case return 0 instead of NaN */ -const pq = v => { +/* + The encoding function is derived from Perceptual Quantizer. +*/ +const jabPqEncode = v => { + if (v < 0) return 0; let vn = Math.pow(v / 10000, n); - return Math.pow((c1 + c2 * vn) / (1 + c3 * vn), p) || 0; + return Math.pow((C1 + C2 * vn) / (1 + C3 * vn), p); }; // Convert to Absolute XYZ @@ -22,9 +22,9 @@ const convertXyz65ToJab = ({ x, y, z, alpha }) => { let xp = 1.15 * x - 0.15 * z; let yp = 0.66 * y + 0.34 * x; - let l = pq(0.41478972 * xp + 0.579999 * yp + 0.014648 * z); - let m = pq(-0.20151 * xp + 1.120649 * yp + 0.0531008 * z); - let s = pq(-0.0166008 * xp + 0.2648 * yp + 0.6684799 * z); + let l = jabPqEncode(0.41478972 * xp + 0.579999 * yp + 0.014648 * z); + let m = jabPqEncode(-0.20151 * xp + 1.120649 * yp + 0.0531008 * z); + let s = jabPqEncode(-0.0166008 * xp + 0.2648 * yp + 0.6684799 * z); let i = (l + m) / 2;