Spaces:
Running
Running
| /** | |
| * 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; | |
| } | |