/** * Dither-Modulated Quantization Index Modulation (DM-QIM) * * Embeds a single bit into a coefficient using quantization. * The dither value provides key-dependent randomization. */ /** * Embed a single bit into a coefficient using DM-QIM * * @param coeff - Original coefficient value * @param bit - Bit to embed (0 or 1) * @param delta - Quantization step size * @param dither - Key-dependent dither value * @returns Watermarked coefficient */ export function dmqimEmbed(coeff: number, bit: number, delta: number, dither: number): number { // Dither modulation: shift coefficient by dither before quantizing const shifted = coeff - dither; // Quantize to the nearest lattice point for the given bit // Bit 0 → even quantization levels: ..., -2Δ, 0, 2Δ, ... // Bit 1 → odd quantization levels: ..., -Δ, Δ, 3Δ, ... const halfDelta = delta / 2; if (bit === 0) { // Quantize to nearest even multiple of delta const quantized = Math.round(shifted / delta) * delta; return quantized + dither; } else { // Quantize to nearest odd multiple of delta (offset by delta/2) const quantized = Math.round((shifted - halfDelta) / delta) * delta + halfDelta; return quantized + dither; } } /** * Extract a soft decision from a coefficient using DM-QIM * * Returns a signed float: * Positive → likely bit 1 * Negative → likely bit 0 * Magnitude → confidence (closer to ±delta/4 = maximum confidence) * * @param coeff - Possibly watermarked coefficient * @param delta - Quantization step size * @param dither - Key-dependent dither value (must match embed) * @returns Soft decision value in [-delta/4, +delta/4] */ export function dmqimExtractSoft(coeff: number, delta: number, dither: number): number { const shifted = coeff - dither; const halfDelta = delta / 2; // Distance to nearest even lattice point (bit 0) const d0 = Math.abs(shifted - Math.round(shifted / delta) * delta); // Distance to nearest odd lattice point (bit 1) const d1 = Math.abs(shifted - halfDelta - Math.round((shifted - halfDelta) / delta) * delta); // Soft output: positive for bit 1, negative for bit 0 // Magnitude reflects confidence return (d0 - d1) / delta; } /** * Extract a hard decision (0 or 1) from a coefficient */ export function dmqimExtractHard(coeff: number, delta: number, dither: number): number { return dmqimExtractSoft(coeff, delta, dither) > 0 ? 1 : 0; }