DVPApplied Creative AI Engineering

Search this course

Search lesson and reference material.

↑ ↓ to choose · Enter to open · Esc to close

Milestone 6 of 12

Rebuild the CineQC baseline

Measure artifacts before asking a model to learn them.

Baseline calibration
controlled defectraw signalreview threshold
Five controlled temporal defects form distinct signals on a shared calibration timeline.
7 hoursPhase 2 · Visual computingRelease 3 · available
Learning objectives
Entry diagnostic

Explain why a metric fires on a synthetic defect and where it could produce a false positive.

Before training a model, make five deterministic signals visible. The Artifact Lab creates controlled defects, records raw scores, calibrates bounded thresholds, and shows where each heuristic lies.

Required route: diagnostic/model (45 min), five baselines (120 min), controlled defects and calibration (75 min), sandbox build (75 min), failure clinic (35 min), transfer/evidence (70 min). Total: 7 hours, including the selected OpenCV reading.

Entry diagnostic

A brightness-jump metric fires on a deliberate white flash. Is that automatically a defect? Explain the signal it measured, one plausible false positive, one clean control, and the human decision still required.

Self-check

Why must a test pair one changed sequence with an otherwise identical clean control?

Why this matters in CineQC

Heuristics are cheap, inspectable baselines. They expose signal before complexity and provide a fallback when a learned model is unavailable. They are not truth: editing, camera motion, intentional flashes, grain, and compression can resemble defects. CineQC should preserve the raw measurement, calibrated threshold, context, and human-review route.

Visual model — sequence to signal to decision

Five signal rows compare flicker, stutter, color drift, sharpness collapse, and motion incoherence measurements.

Five signal rows compare flicker, stutter, color drift, sharpness collapse, and motion incoherence measurements.
Static explanation. The same relationship becomes learner-controlled when JavaScript is available; the lesson remains complete without it.

Each baseline follows decoded frame → CineQC worker reduction → tiny time series → metric score → review flag. The faithful bounded route here mirrors the current cineqc/workers/analysis.worker.js reductions and cineqc/js/metrics.js aggregations. It uses smaller arrays and grids so you can inspect them; it does not silently substitute different metrics.

Use luma units consistently (0..255 in CineQC). The first signal is second-difference RMS flicker: compute adjacent luma deltas, then the RMS of changes between those deltas. This high-pass measure ignores slow exposure drift better than a largest-jump proxy.

Runtime: Local Python 3.12 · NumPy 2.x · 20 min.

import numpy as np

luma_clean = np.array([100, 101, 102, 103, 104], dtype=np.float32)
luma_pulse = np.array([100, 101, 125, 103, 104], dtype=np.float32)

def second_difference_rms(luma):
    deltas = np.diff(luma)
    return float(np.sqrt(np.mean(np.diff(deltas) ** 2))) if len(deltas) >= 2 else 0.0

print(second_difference_rms(luma_clean), round(second_difference_rms(luma_pulse), 2))

Five deterministic baselines

Instructional video · 30 seconds
Five synthetic temporal defects appear beside their deterministic measurement signals.

Five defect frames and their signal plots shown side by side. If the video cannot play, this image and the transcript preserve the complete teaching claim.

  1. Flicker: second-difference RMS over the mean-luma series; CineQC maps about 8 luma RMS acceleration to its lowest quality score. It can flag flashes and cuts.
  2. Stutter: an 8×8 grid of mean-luma block fingerprints; adjacent fingerprints whose mean absolute block delta is below 0.08 are duplicates. It can flag valid held frames or static shots.
  3. Color drift: Bhattacharyya distance between normalized 4×4×4 RGB histograms eight frames apart. Comparisons crossing a scene boundary are set to zero; boundaries use adjacent-histogram Hellinger distance. It can flag intentional lighting changes.
  4. Sharpness collapse: variance of the four-neighbour Laplacian per frame, then a flag when a value falls below half the clip median. It can flag focus pulls or low-detail frames.
  5. Motion incoherence: downsample luma to 64×64, block-match 8×8 regions within a four-pixel search radius, then average each flow vector’s disagreement with its four-neighbour mean. It can flag cuts, whip pans, occlusion, or ambiguous texture.

The exercises may use 2×2 fingerprints, fewer histogram bins, and smaller flow grids for legibility, but preserve the same reduction and aggregation. Any such simplification must be named beside its result; the CineQC transfer reruns the production grid, bins, window, and search settings.

Runtime: Local Python 3.12 · NumPy 2.x · 25 min.

import numpy as np

def block_fingerprint(frame, grid=2):
    height, width = frame.shape
    return np.array([
        frame[y * height // grid:(y + 1) * height // grid,
              x * width // grid:(x + 1) * width // grid].mean()
        for y in range(grid) for x in range(grid)
    ])

clean_a = np.arange(16, dtype=np.float32).reshape(4, 4)
clean_b = clean_a + 1
duplicate = clean_a.copy()
delta_real = np.abs(block_fingerprint(clean_b) - block_fingerprint(clean_a)).mean()
delta_duplicate = np.abs(block_fingerprint(duplicate) - block_fingerprint(clean_a)).mean()
print(delta_real, delta_duplicate, delta_duplicate < 0.08)

For color drift, compare window-lagged normalized histograms, and mask any comparison whose interval includes a scene-cut boundary. The production window is eight frames; this tiny demonstration uses two so every term is visible.

Runtime: Browser lab · Pyodide with NumPy · 20 min.

import numpy as np

def bhattacharyya(p, q):
    return float(-np.log(max(float(np.sqrt(p * q).sum()), 1e-9)))

histograms = [
    np.array([1.0, 0.0]), np.array([0.9, 0.1]),
    np.array([0.2, 0.8]), np.array([0.1, 0.9]),
]
boundaries = [False, False, True, False]
window = 2
series = []
for index in range(window, len(histograms)):
    crosses_cut = any(boundaries[index - window + 1:index + 1])
    series.append(0.0 if crosses_cut else bhattacharyya(histograms[index], histograms[index - window]))
print(series)

Sharpness uses the actual four-neighbour Laplacian variance, not general edge energy. Compare every frame to the sequence median and flag values below median × 0.5.

Runtime: Browser lab · Pyodide with NumPy · 20 min.

import numpy as np

def laplacian_variance(frame):
    values = frame.astype(np.float32)
    lap = (4 * values[1:-1, 1:-1] - values[1:-1, :-2] - values[1:-1, 2:]
           - values[:-2, 1:-1] - values[2:, 1:-1])
    return float(lap.var())

checker = (np.indices((8, 8)).sum(axis=0) % 2 * 255).astype(np.float32)
blurred = np.full((8, 8), 127.5, dtype=np.float32)
sharp_values = np.array([laplacian_variance(checker)] * 4 + [laplacian_variance(blurred)])
median = float(np.median(sharp_values))
print(sharp_values.tolist(), (sharp_values < median * 0.5).tolist())

Motion uses local block-flow vectors, then measures neighbour disagreement. For the learning version, inspect a prepared vector grid: uniform pan vectors have zero disagreement; one contradictory block raises it. In the CineQC transfer, obtain those vectors with the production 64×64 downsample, 8×8 blocks, and ±4 search.

Runtime: Local Python 3.12 · NumPy 2.x · 20 min.

import numpy as np

def neighbor_disagreement(flow):
    scores = []
    height, width, _ = flow.shape
    for y in range(height):
        for x in range(width):
            neighbors = [flow[ny, nx] for nx, ny in ((x-1,y),(x+1,y),(x,y-1),(x,y+1))
                         if 0 <= nx < width and 0 <= ny < height]
            scores.append(np.linalg.norm(flow[y, x] - np.mean(neighbors, axis=0)))
    return float(np.mean(scores))

smooth_pan = np.ones((3, 3, 2), dtype=np.float32)
chaotic = smooth_pan.copy(); chaotic[1, 1] = (-3, 4)
print(neighbor_disagreement(smooth_pan), round(neighbor_disagreement(chaotic), 3))

Self-check

A static locked-off shot produces a high duplicate ratio. What context prevents the stutter heuristic from becoming a false claim?

Generate one defect at a time

Instructional video · 4 seconds
A synthetic clip repeats one numbered frame to create controlled stutter.

Numbered contact strip exposes the repeated frame without motion. If the video cannot play, this image and the transcript preserve the complete teaching claim.

Start with deterministic arrays or a lawful sanitized clip. Create a clean/defect pair for every metric, changing exactly one factor:

Keep a manifest with generator seed, parameters, affected frame range, and parent fixture identity. If multiple defects change at once, attribution becomes ambiguous.

Runtime: Local Python 3.12 · NumPy 2.x · 20 min.

import numpy as np

rng = np.random.default_rng(7)
base = rng.random((12, 16, 16), dtype=np.float32) * 12 + 110
drift = base.copy()
drift[6:] = np.clip(drift[6:] + 25, 0, 255)
print(base.shape, round(float(base.mean()), 2), round(float(drift[6:].mean()), 2))

Calibrate, do not guess

Create a small calibration table with raw score, defect/control label, and review note. Sweep candidate thresholds on calibration data only. Choose an operating point based on the cost of missed defects versus extra human review, then report results on untouched examples.

Do not tune on the final examples and then call their result a test. Preserve false-positive and false-negative galleries because aggregate accuracy hides why a heuristic failed.

Sandbox lab

Use local Python for the complete Artifact Lab; the current browser runner supports NumPy/Pillow scratch blocks but has no guided M06 package and no OpenCV. Implement faithful bounded equivalents of the five CineQC metrics as pure functions that return raw scores plus intermediate values. Generate five clean/defect pairs—one pair per metric—and an extra cut-masking check for color drift. Save a CSV or JSON score table and a compact HTML or notebook gallery.

Failure clinic

Diagnose these believable mistakes:

  1. adjacent luma jump used for flicker instead of second-difference RMS;
  2. a hard-coded threshold calibrated on the same examples reported as test results;
  3. scene cuts counted as flicker and motion defects without context;
  4. stutter inferred from nominal fps instead of block-fingerprint similarity;
  5. generic edge energy or frame difference substituted for Laplacian collapse or block-flow neighbour disagreement.

Repair with float conversion, separated calibration/test examples, review context, decoded measurements, and per-signal evidence.

CineQC transfer

For each CineQC-adjacent signal, compare the reimplementation with the current system on the same sanitized sequence. Record input contract, raw intermediates, aggregation, threshold, and outcome. A mismatch is an investigation result—not permission to silently modify production. State which version and settings were compared.

Self-check

What evidence distinguishes a useful imperfect heuristic from an untrustworthy one? Include calibration provenance, error cases, and a bounded human-review role.

Evidence gate

The Artifact Lab is complete when it contains:

  1. five named pure baseline functions faithfully matching second-difference RMS, block-fingerprint duplicates, masked window-lagged histogram distance, Laplacian-variance collapse, and block-flow neighbour disagreement;
  2. five deterministic clean/defect pairs plus a scene-cut masking pair, all with a generation manifest;
  3. a calibration table, frozen threshold choices, and results on separate examples;
  4. false-positive and false-negative cases with production context;
  5. a concise comparison with CineQC behavior, versioned and free of private media;
  6. a README that calls every metric a heuristic and states its known limitations.

Bound the artifact to five signals and a small lawful fixture. Do not add a learned model here.

One-page summary

Required resource
docs
OpenCV image filtering tutorial
Use the official blur and filtering concepts to reason about sharpness and controlled defects. · 20 min
Go deeper
docs
OpenCV optical flow tutorial
Read only the flow assumptions and failure cases when extending the motion baseline. · 20 min
Evidence gate

Mini-capstone 2: Artifact Lab, an interactive case study comparing Python reimplementations with CineQC behavior.

Diagnostic route

Check the statements you can demonstrate in the challenge. Selections route your learning; they are not proof of mastery.

What can you demonstrate?

Start the full milestone.

Evidence gate

Submit an inspectable artifact for independent review. Only a verified diagnostic challenge or reviewed evidence can mark this milestone proven. Proof is local self-tracking, not tamper-resistant against edits to this browser storage.

Local mastery progress

Current state: not-started. Advance through learning, practice, and submitted evidence; proof requires a later passed review.

Notes

Your local study notes mount here when JavaScript is available.

Protected AI Tutor

Teaching modes mount here when JavaScript is available. The readable course remains available without them.