an.bench.stage#
Measuring a pan: does the stage move as planes, or as one rigid image?
The trap the epic’s own sentence sets. “The planes moved at different rates” is satisfied by a scene with no parallax whatsoever, because a centre-anchored zoom already produces unequal per-plane displacements. The honest null hypothesis is “the camera moved the whole stage as one rigid image”, and a zoom has to be excluded rather than assumed away.
The quantity. Probe each plane at scene-space x = 0 — the canvas
centre column. There the zoom term vanishes exactly:
Δ_i = s₁ · p_i · D₁ ratio_ij = Δ_i / Δ_j = p_i / p_j (any zoom)
Under a rigid pan every ratio is exactly 1, so the measurement can tell a parallaxing stage from a zooming one. Two additions the epic does not require:
assert
Δy ≈ 0, so a pan is distinguishable from a zoom — but only for a ZOOM-FREE pan: §4 endorses zoom composing through the pivot, and a pan+zoom shot measures a non-zero Δy by design;assert the ORDERING
p_far < p_mid < p_near, because wrong-order parallax is a real bug that a bare inequality passes. It is a **bonus, not a second gate**: the zoom false positive satisfies the ordering too. Only the x = 0 probe excludes a zoom.
Two measurements, deliberately different instruments:
(a) JSON — free, on every PR, from the compiled document through
an.stage.timeline.screen_position(). Composed screen space,
not local channel values: a rigid pan on root leaves every plane’s local
Δx at zero.
(b) Pixels — on a labelled PR, per-plane centroids over exact-colour
masks. The x = 0 cancellation does not reach (b): a centroid sits at
the plane’s own offset, not at x = 0, so the fixture holds zoom constant.
And each mask’s pixel COUNT is asserted unchanged between frames, because a
plane panning partly off-canvas biases its centroid — measured, that read a
depth = 2 plane as 1.975.
Functions
|
Measurement (a): composed screen displacement, probed at |
|
|
|
Measurement (b): per-plane centroid displacement between two frames. |
|
The smallest gap between any two planes' ratios. |
Classes
|
One plane's displacement between the two probed times. |
|
What a pan did, plane by plane. |
Exceptions
The camera rolls, so a per-axis ratio is not a depth ratio. |
- class an.bench.stage.PanMeasurement(tracks, reference)[source]#
Bases:
objectWhat a pan did, plane by plane.
- property is_rigid: bool#
True when every plane moved by the same amount — the null hypothesis, and what a stage with no parallax looks like.
Computed from the DISPLACEMENTS, not from the ratios, because a stage that did not move at all has no ratios (the reference is zero, so every ratio is NaN) and is nonetheless as rigid as a stage can be. Reading it off the ratios made a pure zoom — which cancels to zero displacement at the probe column, exactly as intended — report as parallaxing.
>>> PanMeasurement((PlaneTrack("a", 10.0, 0.0), PlaneTrack("b", 10.0, 0.0)), "b").is_rigid True >>> PanMeasurement((PlaneTrack("a", 0.0, 0.0), PlaneTrack("b", 0.0, 0.0)), "b").is_rigid True >>> PanMeasurement((PlaneTrack("a", 5.0, 0.0), PlaneTrack("b", 10.0, 0.0)), "b").is_rigid False
- class an.bench.stage.PlaneTrack(name, dx, dy, depth=None)[source]#
Bases:
objectOne plane’s displacement between the two probed times.
- exception an.bench.stage.RotatingCamera[source]#
Bases:
RuntimeErrorThe camera rolls, so a per-axis ratio is not a depth ratio.
Under a rotation the composed x-displacement is
−fx·A + fy·Bwith plane-independentA/B, so the axes mix andΔ_i/Δ_jstops beingf_i/f_j. Measured withrotation = 0.6and per-axis factors: one plane reported a NEGATIVE ratio and the resulting gap was larger than the honest one, so the tripwire passed on a number that meant nothing (an#111 review, M2).Refused rather than reported. The measurement’s whole job is to exclude a camera move that mimics depth; silently reporting one is the failure it exists to prevent, pointed the other way.
- an.bench.stage.measure_pan_json(scene, plane_paths, times, *, depths=None)[source]#
Measurement (a): composed screen displacement, probed at
x = 0.plane_pathsare full node paths ("depths/far"); the returned tracks are keyed by the LAST segment, which is the plane’s own name.- Return type:
- an.bench.stage.measure_pan_pixels(frames, colours, *, depths=None)[source]#
Measurement (b): per-plane centroid displacement between two frames.
- Return type:
- an.bench.stage.min_ratio_gap(ratios)[source]#
The smallest gap between any two planes’ ratios.
This is the number the ledger row reports and the tripwire guards. Zero means two planes moved together, which on a stage that declares distinct depths means the parallax flattened.
- Return type:
>>> min_ratio_gap({"far": 0.25, "mid": 1.0, "near": 2.0}) 0.75 >>> min_ratio_gap({"far": 1.0, "mid": 1.0}) 0.0
- an.bench.stage.plane_centroids(frame, colours)[source]#
{plane: (cx, cy, pixel count)}for exact-colour masks.an/bench/masks.pyis deliberately not reused: it has no colour selection. The primitive ismetrics.pack_rgbplus equality — one integer per colour, compared exactly, so a plane’s mask cannot pick up an anti-aliased edge pixel from its neighbour.