@raven-js/cortex
Version:
Zero-dependency machine learning, AI, and data processing library for modern JavaScript
105 lines (97 loc) • 3.01 kB
JavaScript
/**
* @author Anonyfox <max@anonyfox.com>
* @license MIT
* @see {@link https://github.com/Anonyfox/ravenjs}
* @see {@link https://ravenjs.dev}
* @see {@link https://anonyfox.com}
*/
/**
* @file JPEG dequantization and IDCT (reference-quality, deterministic).
*
* Implements a separable 2D IDCT with exact cosine basis and JPEG normalization:
* f[x,y] = 1/4 * sum_u sum_v alpha(u) alpha(v) F[u,v]
* cos((2x+1)u*pi/16) cos((2y+1)v*pi/16)
* where alpha(0)=1/sqrt(2), alpha(k>0)=1.
*
* The output is biased by +128 and clamped to [0,255].
*
* This implementation favors clarity and determinism. It can be swapped with a
* fixed-point Loeffler/AAN variant later without changing the API.
*/
const PI = Math.PI;
const COS = new Float64Array(8 * 8);
for (let x = 0; x < 8; x++) {
for (let u = 0; u < 8; u++) {
COS[x * 8 + u] = Math.cos(((2 * x + 1) * u * PI) / 16);
}
}
const ALPHA = new Float64Array(8);
for (let u = 0; u < 8; u++) ALPHA[u] = u === 0 ? 1 / Math.SQRT2 : 1;
/**
* Multiply coefficients by quantization table into Float64 domain.
* @param {Int16Array} coeffs length 64
* @param {Int32Array} quant length 64
* @param {Float64Array} out length 64
*/
export function dequantizeToFloat(coeffs, quant, out) {
for (let i = 0; i < 64; i++) out[i] = coeffs[i] * quant[i];
}
/**
* Perform 2D IDCT on F (length 64 Float64), write Uint8 samples to out.
* Adds +128 bias and clamps to [0,255].
* @param {Float64Array} F length 64 (frequency domain, dequantized)
* @param {Uint8Array} out length 64
*/
export function idctFloat64(F, out) {
// DC-only fast path
let acZero = true;
for (let i = 1; i < 64; i++)
if (F[i] !== 0) {
acZero = false;
break;
}
if (acZero) {
// f[x,y] = 1/4 * alpha(0)^2 * F[0]
const val = 0.25 * ALPHA[0] * ALPHA[0] * F[0] + 128;
const s = clampTo8(Math.round(val));
out.fill(s);
return;
}
// Compute 2D IDCT via separable 1D transforms
const tmp = new Float64Array(64);
// Row-wise (y fixed, sum over v)
for (let y = 0; y < 8; y++) {
for (let x = 0; x < 8; x++) {
let sum = 0;
for (let v = 0; v < 8; v++) {
const cv = COS[y * 8 + v] * ALPHA[v];
// sum over u inside to increase cache? We'll just do nested sums explicitly
for (let u = 0; u < 8; u++) {
sum += ALPHA[u] * COS[x * 8 + u] * cv * F[v * 8 + u];
}
}
tmp[y * 8 + x] = 0.25 * sum;
}
}
// Bias and clamp
for (let i = 0; i < 64; i++) {
out[i] = clampTo8(Math.round(tmp[i] + 128));
}
}
/**
* Combined convenience: coeffs(Int16), quant(Int32) -> out(Uint8 length 64)
* @param {Int16Array} coeffs
* @param {Int32Array} quant
* @param {Uint8Array} out
*/
export function dequantizeAndIDCTBlock(coeffs, quant, out) {
const F = new Float64Array(64);
dequantizeToFloat(coeffs, quant, F);
idctFloat64(F, out);
}
/** @param {number} x */
function clampTo8(x) {
if (x < 0) return 0;
if (x > 255) return 255;
return x | 0;
}