Skip to the content.

How ProjectorCal works

Principles

Formulas

Tolerances

Quantity Good Acceptable
Gray RGB balance (bright steps) 98–102 95–105
Gray color ΔE mean / max < 1.5 / < 2.5 < 3 / < 4
Gamma, 10–90 % target ± 0.05 ± 0.1
Gray step luminance, ≥ 30 % ± 1.5 % ± 3 %
Gray step luminance, 10–25 % ± 3 % ± 6 %
ΔE2000 of the 6 primaries (mean) < 3 < 5

Balance tolerances in the planner follow visibility: 2 % on steps ≥ 60 %, 3 % at 40–50 %, 6 % at 20–30 % (blue ×1.5) — the same percentage is a much smaller ΔE on dark steps.

The full automatic check

The video

One file per mode, built from the patches without re-encoding (tools/make_sequence.py, every segment and splice is verified frame by frame). The schedule lives in projectorcal/sequence.py and is the single source of truth for the video generator and the analyzer.

The analyzer (projectorcal/autorun.py)

  1. Sync. All mark candidates in the readings are tried; the one the schedule fits best wins (a frozen frame or a similar place in the file cannot fool it).
  2. Runs of constant picture. Consecutive readings are grouped by log-luminance + chromaticity. A reading that straddles a transition is recognized as a mix; its mixing fraction gives the exact transition time. Single outliers (menu, glare) are dropped.
  3. Matching runs to schedule segments by dynamic programming: cost for time shift (jitter tolerance grows with the reading length around the transition; player pauses are allowed, going back in time is not), extra/missing runs, implausible luminance or chromaticity. Identical neighboring patches (HDR above the peak) are covered by one run and split by time.
  4. Assignment. A reading counts for a segment only if it lies entirely inside it with margins and also within the scheduled end (a long dark reading must not drag a value toward its neighbor). Robust mean per segment.
  5. Validation. Luminance relative to the mark white; color patches must be closer to their own target chromaticity than to their scheduled neighbors’ (catches a shift invisible in luminance); gray must be monotonic. A tail of failed segments (room light on, beam blocked, video stopped) also flags the patch right before it. Flags: no readings, single reading (only if that reading was fast), implausible, unstable, non-monotonic, before failure. Flagged segments never enter the analysis.

Verified on randomized simulations (Plex stalls, menus in the meter, jitter, a meter 30 % slower than the model): ≈ 6000 patches per test batch, no silent errors.

Slow meters

The standard video is timed for the i1Display Pro / Plus family. For slower meters (ColorMunki Display, i1Display Studio, Spyder, i1Pro and other spectrometers, DTP94, Huey, ColorHug) there is a second variant, *_v3_slow.mp4: every segment — including the marks and control whites — is built for readings twice as long (SDR ≈ 27 min, HDR10 ≈ 13 min). The patch order is the same. The analyzer reads the variant from the schedule and widens its timing tolerances by the same factor (mark length, outlier window, “single reading” flag). Simulation with a meter 1.6–2.4× slower than the i1Display model: ≈ 6800 patches, no silent errors. Which video to play follows from Settings → Meter model (or can be forced there).

Warm-up / drift correction

White is measured several times per run (mark, the 100 % patch, control whites, finish). If it changed by more than 1 %, W(t) = A − B·e^(−t/τ) is fitted (τ by search, A/B by least squares; piecewise-linear if that fits badly) and every reading is scaled by W_ref / W(t). A warm-up of 5 % in the first two minutes is corrected to ± 0.3 %.

The plan and the verdict (projectorcal/fulltest.py)

White balance

Gain and Offset are solved together from the shape of R/G and B/G over 20–100 %: Gain shifts the whole scale equally, Offset acts like k/s (stimulus s), strong in the shadows and still noticeable at white. Whole steps are searched; a solution is accepted only if it lowers the visibility-weighted cost, makes no step worse than the worst one was before, and the deviation is outside the run-to-run spread (≈ 2–3 %) unless the step size is already learned. The first trial of an unlearned Gain is limited to 2 steps.

Gamma points

ΔlnY(s) = g · Σ Δk · tent(s, step·k), g = ln(1 + per-step %), step = 5 % for a 20-point menu or 10 % for a 10-point menu (the layout is part of the projector profile, see below). Least squares over the 10–95 % steps with light regularization and smoothing; steps whose target is below twice the black level are excluded (that would crush near-black into black); a step already within the good tolerance is not touched for ±1. If the plan also changes Offset, the gray scale is first predicted after that Offset change (red/blue share in Y) so both are consistent.

CMS

For each hue the program searches Bright and Saturation steps that minimize the weighted ΔE2000 over all its saturations: weights 1.0 (25 %, 50 %), 0.8 (75 %), 0.5 (100 %). A step’s effect at saturation s is modeled as min(1, (s/0.75)^1.2) of its full effect (measured: ≈ 0.27 at 25 %, ≈ 0.6 at 50 %). Bright ≈ 10 %/step and Saturation ≈ 2 %/step until learned. Color tone is suggested only after its direction is learned. If no combination improves the hue by ≥ 0.2 ΔE, the program says CMS cannot help and points to Color Space.

Learning

Consecutive runs of one mode are compared; the settings diff is grouped (Gain, Offset, gamma points, each CMS color, …). If the groups are separable (one of Gain/Offset + gamma + any CMS colors), each group’s metric change is converted to “per step” (Gain: R/G at 100 %; Offset: k from R/G·s at 20–50 %; gamma: fit of the tent model; CMS: dY/dC of the 100 % patch). The median of all learned values replaces the defaults.

Projectors without gamma points

If the profile says the menu has no luminance gamma points, the plan judges the mean deviation of 20–80 % gray and suggests a darker or brighter gamma preset instead; white balance and CMS are planned as usual.

Projector and meter profiles (projectorcal/profiles.py)

A projector profile only fixes what cannot be learned: the number of gamma Control Points and which stimulus each one controls (20 × 5 %, 10 × 10 % or none). All step sizes start from the values measured on the Vidda C3S and are re-learned from your own runs, so an unverified model costs one or two extra runs, not wrong advice. If the layout is uncertain, Gamma by points → point probe changes one point and shows where its effect peaks.

A meter profile decides which auto-run video to play and shows setup notes. The model is suggested from the instrument name in spotread -? (the whole i1Display family reports one USB name, so the user confirms Pro/Plus vs ColorMunki Display). The display-type list (-y) of the connected meter, including installed .ccss corrections, is read from the same output and offered in Settings.

The manual wizard

Preparation (checklist, meter check, room light) → Black level/contrast (PLUGE, clipping) → Laser level (fix the white) → Gray scale circles (100/80/60/40/20/10 with one “next action”: Gain → Offset → gamma → mids) → Gamma by points (base pass → corrections for all points at once → check pass, with a point probe) → Colors → SDR verification → Visual check → HDR10 (PQ gray, tone-mapping report, reference white 203 nits at 50–70 % of the peak) → Dolby Vision checklist → Summary and report.

A manual reading is rejected if its samples come from two pictures; a reading that does not look like the expected patch asks before recording; a menu change marks dependent steps for recheck.