The message is the motion.

Ghost fonts hide glyphs in the relationship between video frames. This guide explains the complete decoder: from dense optical flow to drift-compensated evidence, cleaned masks, optional OCR, and agent verification.

Same clip · two kinds of evidence Motion recovered
A paused ghost-font video frame that appears to contain only random dots
One frame: statistically uniform dots. No readable glyph boundary.
Binary recovered mask showing I LOVE YOU
Many frame pairs: registered counter-motion accumulates into letters.

Why a still frame contains nothing to read.

A ghost-font clip is a motion-defined random-dot stimulus. Foreground dots inside the letter shapes and background dots can share the same brightness, density, size, and spatial distribution. The only separating feature is their velocity across time.

A · Appearance

Equal spatial statistics

In a single image, glyph and background samples are drawn from the same visual texture. Thresholding, edge detection, template matching, and raw-frame OCR have no stable boundary to follow.

B · Motion

Different temporal vectors

Between frames, background dots follow a dominant translation while dots inside the glyph region move against it. Optical flow turns that temporal difference into a per-pixel vector field.

C · Evidence

Weak per pair, strong in aggregate

A single flow estimate is noisy. The same glyph-shaped counter-motion recurs over many frame pairs, while random estimation error is incoherent and averages down during accumulation.

D · Registration

The letter region also drifts

Even after background subtraction, the entire glyph region can move across the clip. Each score map must be aligned before summation or the recovered letters smear into broad blobs.

Core signal Fₜ(x) = optical flow at pixel x bₜ = medianₓ Fₜ(x) rₜ(x) = Fₜ(x) − bₜ sₜ(x) = max(0, rₜ(x) · (−bₜ / ‖bₜ‖))

The median flow estimates the dominant background because the background covers most of the frame. Positive projection onto the anti-background direction becomes counter-motion evidence. When the background magnitude is below 0.15 pixels, the decoder falls back to residual magnitude instead of dividing by an unstable direction.

From video frames to readable glyphs.

The production decoder is deliberately classical: OpenCV and NumPy perform the recovery, while OCR is a final optional reader rather than the source of the signal.

01

Decode and sample frames

OpenCV reads the video sequentially and converts each selected frame from BGR to grayscale. --stride N keeps every Nth frame; --max-frames caps work on long clips.

Grayscale is sufficient because the encoded feature is motion, not color. At least two usable frames are required to form one flow pair.

02

Compute dense optical flow

The default DIS preset estimates a two-dimensional motion vector at every pixel between consecutive frames. DIS is fast and robust for dense random-dot textures.

Farnebäck is available as a fallback with pyramid scale 0.5, three levels, a 15-pixel window, three iterations, polynomial neighborhood 5, and sigma 1.2.

03

Estimate background motion

The per-frame-pair background vector is the median of every flow vector. Because glyph pixels occupy a minority of the image, the median is resistant to their opposing movement.

Subtracting this vector removes global translation and leaves residual motion: the information needed to distinguish glyph dots from the field.

04

Score counter-motion

Residual vectors are projected onto the direction opposite the background. Negative values are clipped to zero, so only movement opposing the field adds evidence to the candidate glyph mask.

This signed score is more selective than raw motion magnitude: ordinary background error does not score merely because it moves.

05

Track and cancel glyph drift

Each pair score is blurred with a 31×31 Gaussian kernel. The blur suppresses individual dot noise while preserving the slowly moving glyph region.

Phase correlation estimates translation between consecutive smoothed score maps. Updates are accepted only when response exceeds 0.05 and displacement is below 30 pixels. Accepted shifts accumulate into a drift vector.

06

Register and accumulate

The current score map is warped by the negative cumulative drift, aligning it with the first score map. Registered maps are then summed across the clip.

Coherent glyph evidence grows approximately with the number of pairs; uncorrelated noise grows much more slowly. This temporal integration is what makes hidden letter shapes visible.

07

Normalize and clean the mask

The accumulated score is scaled using its 99.5th percentile, clipped to 8-bit range, and blurred with a 5×5 Gaussian. Otsu thresholding builds the strong mask.

A 7×7 elliptical morphological close fills glyph gaps; an open removes isolated noise. Components smaller than one twenty-thousandth of the image are discarded.

08

Recover faint coherent glyphs

A secondary threshold at half the Otsu value looks for lower-energy components. Candidates must be compact, tall enough, large enough, mostly absent from the strong mask, and peak at least 75% of the Otsu level.

Width constraints reject scan lines and pan bands. This conservative pass can restore a faint character—such as the isolated “I” in the proof clip—without flooding the mask with background motion.

09

Run optional OCR and verify

Tesseract receives the inverted clean mask with page segmentation mode 6. A second uppercase-and-digit-whitelisted pass can replace a punctuation-heavy result.

The decoder also checks for the geometry of an isolated uppercase “I”. OCR is never treated as ground truth: the agent inspects both the mask and heatmap and states uncertainty for ambiguous characters.

The thresholds are guards, not magic numbers.

Each constraint protects a specific failure boundary: unstable camera motion, low-energy characters, noisy dot-level flow, or OCR that invents punctuation.

Value Role Why it exists
background magnitude > 0.15 Use directional opposition score A direction derived from near-zero motion is numerically unstable.
31×31 Gaussian Prepare maps for phase correlation Suppresses dot-level motion while retaining the glyph-region envelope.
response > 0.05 Accept phase-correlation shift Rejects low-confidence registration estimates.
shift < 30 px Accept plausible inter-pair drift Large jumps usually represent a bad correlation peak.
99.5th percentile Normalize accumulated score Prevents a few extreme pixels from compressing the useful heatmap range.
7×7 close/open Clean strong binary mask Joins broken strokes and removes small texture remnants at glyph scale.
0.5 × Otsu Faint-component threshold Searches below the global threshold without admitting the full noise floor.
component width ≤ 4 × height Faint-glyph compactness check Rejects wide horizontal bands caused by scanning or camera translation.

Complexity

For N selected frames of width W and height H, the dominant cost is dense optical flow: approximately O(NWH) work and O(WH) live memory for the current frames, flow, score maps, and accumulator. The implementation streams frames; it does not hold the whole video in RAM.

Trust boundary

The algorithm runs locally in the environment executing the skill. No model weights are needed for recovery, and the video is not required to leave that environment. OCR can be disabled entirely with --no-ocr.

Two images answer two different questions.

The clean mask answers “what can be read confidently?” The heatmap answers “where does the motion evidence exist, including weaker characters?”

Clean binary mask revealing HELLO HUMAN
revealed.png

Clean binary mask

High-contrast output for direct inspection and OCR. Morphology and component filtering prioritize readable shapes over weak evidence.

Raw accumulated opposition heatmap revealing HELLO HUMAN
revealed_heatmap.png

Raw opposition heatmap

Normalized accumulated score before binary thresholding. It preserves faint glyph evidence that may sit below the global Otsu cutoff.

Run the same decoder without an agent.

Python 3.8+, OpenCV, and NumPy are required. Tesseract and pytesseract are optional. The images are still generated when OCR is unavailable.

Install and decode
pip install -r requirements.txt
python decode.py docs/assets/ghost-message.mp4
Tuned run
python decode.py docs/assets/ghost-message.mp4 \
  -o docs/assets/ \
  --method farneback \
  --stride 2 \
  --max-frames 200 \
  --no-ocr
Argument Default Behavior
video required Path to MP4, MOV, AVI, WebM, or any format readable by the installed OpenCV build.
-o, --out-dir current directory Destination for revealed.png and revealed_heatmap.png.
--method dis Selects DIS or Farnebäck dense optical flow.
--stride 1 Processes every Nth frame. Use 2 for high-frame-rate clips or faster diagnostics.
--max-frames unlimited Stops after a fixed number of selected frames. A few seconds is usually sufficient.
--no-ocr false Skips Tesseract while preserving both visual outputs.

What weak evidence means—and what to try next.

Uniformly dark heatmap

The clip may not contain coherent counter-motion, may have too few usable frames, or may use a different hiding mechanism. Confirm the video actually moves and process a longer interval.

Letters smear into bands

Drift registration likely failed or the clip contains non-translational camera motion. Try Farnebäck, reduce stride, or inspect whether rotation and scale changes violate the translation-only registration model.

Mask is weak or empty

Retry with --method farneback. For high-frame-rate clips, --stride 2 can increase displacement between sampled frames and improve the flow signal.

One character is missing

Inspect the heatmap before concluding the character is absent. The clean Otsu mask is intentionally conservative; the faint-component recovery may still reject a weak or unusually shaped glyph.

OCR disagrees with the image

Trust visual evidence over OCR. Tesseract sees only the final binary geometry and can confuse broken strokes. The agent workflow requires opening the mask and heatmap, verifying the text, and marking ambiguous characters.

Tesseract is unavailable

This is not a decoding failure. The computer-vision pipeline still writes both images. Read the clean mask directly or install Tesseract later; use --no-ocr to skip the check explicitly.

The screenshots are executions, not mockups.

Claude Code, Claude.ai, and Codex each ran the decoder against motion-hidden text, inspected generated evidence, and reported the recovered message. Select an image to inspect it at full resolution.

Obfuscation changes the sensor, not the information.

Ghost fonts can defeat tools that inspect only individual images, but they do not remove the signal. They move it from spatial appearance into temporal correspondence.

Technical verdict

If a human can recover the letters from motion, a system that measures motion can recover them too.

Dense optical flow and phase correlation are established computer-vision methods. The decoder does not need a language model to infer the hidden phrase, and it does not break encryption because no encryption exists. It reconstructs a deliberately preserved visual signal from the dimension in which that signal was encoded.