Skip to content

chroma_error

The chroma gain and phase error of one capture: a player whose chroma path is slightly mis-adjusted, or whose hue and saturation drift from line to line. Optional — omit the block for an error-free chroma path.

chroma_error:
  gain: 1.05
  phase_deg: 2.0
  line_jitter: { phase_deg: 0.5, gain: 0.01, seed: 5 }
  differential:
    gain: [[0, 1.00], [50, 0.98], [100, 0.95]]
    phase_deg: [[0, 0.0], [50, 1.0], [100, 2.5]]
  crosstalk: { v_to_u: 0.3, alternate_with_switch: true }
  include_burst: false

Unlike channel, which belongs to the pressing, the chroma error belongs to the player that made the capture, so each capture of one pressing may use a different block.

Keys

Key Type Required Range Default Description
gain float No 0.5 to 1.5 1.0 Chroma amplitude multiplier (saturation)
phase_deg float No −180.0 to 180.0 0.0 Chroma phase advance relative to burst (hue), in degrees
line_jitter map No — — Per-line random variation, below
differential map No — — Differential gain and phase, below
crosstalk map No — — U/V crosstalk, below
include_burst bool No true, false false Apply the error to the colour burst as well; see Including the burst

line_jitter

Key Type Required Range Description
phase_deg float No 0.0 to 10.0 Standard deviation of the per-line phase, in degrees
gain float No 0.0 to 0.1 Standard deviation of the per-line gain, about gain
seed integer No any Fixed RNG seed. Omit for a run-specific seed

differential

Chroma gain and phase that depend on the luma level the chroma sits on: the classic composite non-linearity, which leaves residual subcarrier in flat colour for every PAL comb method (Clarke, BBC RD 1988/11).

Key Type Required Range Description
gain list of [IRE, gain] pairs One of the two IRE −50 to 150, strictly ascending; gain 0.5 to 1.5 Gain multiplier against luma level
phase_deg list of [IRE, degrees] pairs One of the two IRE −50 to 150, strictly ascending; phase −30 to 30 Phase advance against luma level
  • The luma level is the Y signal under each chroma sample, in IRE: 100 × (Y − blanking) / (white − blanking). On NTSC with 7.5 IRE setup, black is at 7.5.
  • Breakpoints are joined by straight lines and the end values are held beyond them. An absent curve is the identity (gain 1, phase 0).
  • The gain multiplies gain (and the line's jitter) and the phase adds to phase_deg, sample by sample.
  • Curves that are gain 1 and phase 0 everywhere are the identity, and the output is bit-identical to leaving differential out.

crosstalk

A 2×2 mixing of the decoded colour-difference components, as a player's chroma path that leaks one into the other:

U' = U + a · v_to_u · V
V' = V + a · u_to_v · U
Key Type Required Range Default Description
v_to_u float One of the two −0.5 to 0.5 0.0 Fraction of V added to U
u_to_v float One of the two −0.5 to 0.5 0.0 Fraction of U added to V
alternate_with_switch bool No true, false false Make a the line's PAL V-switch sign; otherwise a is 1

U and V are the components a decoder recovers, with the PAL V-switch removed (NTSC: B−Y and R−Y). With alternate_with_switch: true the crosstalk alternates line by line in the decoded picture — which is the same as a fixed leak in the transmitted signal, before the switch is undone. That needs a V-switch, so it is PAL and PAL-M only. Zero terms are the identity, and the output is bit-identical to leaving crosstalk out.

chroma_error: must set at least one of gain, phase_deg, line_jitter, differential or crosstalk (include_burst alone does not count); line_jitter: must set at least one of its phase_deg or gain, differential: one of its gain or phase_deg, and crosstalk: one of its v_to_u or u_to_v. An empty block is a parse error.

Behaviour

Configuration Result
Block absent No chroma error stage; output is unchanged
gain: 1.0, phase_deg: 0.0, no jitter (or zero deviations) No processing; output is bit-identical to the block being absent
gain Every picture line's chroma amplitude is scaled
phase_deg Every picture line's chroma phase is advanced relative to burst
line_jitter Each picture line adds its own Gaussian deviation to the phase and gain
differential Each sample's gain and phase also follow the luma under it
crosstalk Each picture line's decoded U and V are mixed
include_burst: true Every line's burst is scaled and rotated as well

Picture chroma only

By default the error is applied to chroma inside the active window of active picture lines. The colour burst, VBI lines, lines carrying a project VITS and the blanking between are left bit-identical. A burst-locked decoder therefore sees a real hue and saturation error, as it would from a mis-adjusted player, rather than a burst that has moved with the picture.

Including the burst

include_burst: true models the other real case: a gain or phase error in the whole chroma path, or an FM/MTF loss on the disc, which moves the burst with the picture. The burst of every line is changed as well:

  • A picture line's burst takes that line's own error, jitter included, so picture and burst move together.
  • Bursts on VBI and VITS lines take the block's gain and phase_deg, without jitter.
  • differential and crosstalk apply to the burst as they do to the picture. The burst sits on blanking, so it takes the differential gain and phase at 0 IRE.
  • The picture chroma is bit-identical to include_burst: false; only the bursts change.

A burst-locked decoder therefore sees no hue error against burst, and one that references its chroma gain to the burst amplitude can measure and correct the gain. The window rotated around each burst is the burst gate widened by 8 samples each side, enough to follow the largest channel timing offset. The burst is short against the Hilbert transformer, so a phase rotation of the burst is accurate to about 1° and 3 %; a pure gain error is exact.

How it is applied

On each picture line the stage forms the quadrature of the chroma with a Hilbert transformer and replaces the chroma C with

g · (C · cos θ − Ĉ · sin θ)

which scales the subcarrier by g and advances its phase by θ. With differential, g and θ change sample by sample with the luma. The Hilbert transformer is a 47-tap Kaiser-windowed design, accurate to within 10⁻⁴ from 2 MHz to 2 MHz below Nyquist on every standard (2–6.87 MHz on 625-line, 2–5.16 MHz on 525-line), which covers every chroma band. It is computed from the active window's own samples, so the burst never leaks into it.

Crosstalk is applied first. Each line is demodulated against the subcarrier reference the generator modulated it with — U on the sin axis and V on the cos axis of the 4fsc lattice, with the line's V-switch sign — giving U = C sin φ − Ĉ cos φ and the transmitted V as C cos φ + Ĉ sin φ. The two are mixed, and the line is remodulated before the gain and phase are applied.

On PAL and PAL-M the phase advance is the same on every line, so it alternates in sign once the decoder undoes the V-axis switch — the classic PAL hue error that a delay-line decoder turns into a saturation loss.

The result is not clamped: chroma is centred on zero and saturated colours legitimately exceed the composite limits. The output stage quantises the final signal.

Jitter seeds

The jitter is a capture property, so it is keyed by the jitter seed and the output frame position, drawn line by line in frame order. With seed set the jitter is identical on every run and for any --threads value. Without it, each run draws a fresh seed, as an unseeded noise_seed does.

Each picture line always draws two values, phase then gain, whether or not each deviation is set, so changing one deviation never moves the other's values.

Stage order

  1. Generation
  2. LaserVision channel and channel
  3. Chroma error
  4. Line timing
  5. Noise (capture layer, then shared layer)
  6. Dropouts
  7. Output

The chroma error runs after the channel, so pressing impairments are computed from error-free chroma, and before noise, so the noise is not rotated.

Rules

  • Values outside their ranges, or not finite, are validation errors.
  • A non-numeric value, a non-boolean include_burst, an unknown key or an empty block is a parse error.
  • SECAM is a validation error: it has no quadrature chroma to rotate.
  • crosstalk.alternate_with_switch: true on NTSC is a validation error: NTSC has no V-switch.
  • A differential entry that is not a numeric [IRE, value] pair, or levels that do not ascend, is an error.