residualForEllipse computed C = H^T·E·H, but H maps image → output, so
the correct output-space conic is C = H^{-T}·E·H^{-1}. The forward form
represents a geometrically meaningless conic and produced nonsensical
numbers in the per-datum table (a 210mm circle was reported as 2046mm).
The deskewed image itself was correct — only the diagnostic was wrong,
because these residuals are display-only and don't feed back into the
solver. Now invert H once and compute the conic in output space.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Datum editor (step 3):
- Add world-axis role to rectangles (isAxisReference) and lines
(axisRole: "x"|"y"). Exclusive via a new store action that clears
any other axis flag on write. The solver's pickPrimary now honors an
explicit user flag ahead of the type-priority fallback; line-primary
correspondences target world +x or +y depending on the flag.
- Panel UI: checkbox on rect cards, three-way button row on line cards,
and an axis badge in each card header.
- Ellipse datum switches to 8 user-placed points on the circle contour.
New src/lib/ellipse-fit.ts does an algebraic LSQ conic fit (data-
normalised, 5x5 Gaussian solve, f=-1 constraint) and returns the
geometric center + perpendicular conjugate semi-axes, which we cache
on the datum for the solver and renderer. Dragging any handle
refits; an extra center handle translates all 8 points together.
datum-cache migrates legacy 3-handle storage by synthesising 8
samples from the old parametric form.
- ResultViewer auto-scale now floors to an int to match the integer-
only scale input (step=1).
Measurement tool (step 4) — CorrectedImageViewer.vue:
- Three measurement tools: line (length), ellipse (semi-axes + area),
angle (0-180 degrees between two rays).
- Persistent, multi-measurement state. Each has id, colorIndex, and
type-specific geometry; colors cycle via the existing getDatumColor
palette with a monotonic counter so deletion doesn't recolor.
- Selection model with hit-testing on handles, geometry, and labels.
Selected draws on top in white; others render dashed with 0.8/0.5
alpha so the active measurement pops.
- Dragging geometry or label moves the whole measurement; dragging a
handle reshapes just that handle. 3px mouse threshold distinguishes
click from drag.
- Side panel lists measurements with color chip, type, value, and a
delete button; clicking selects on canvas. Delete/Backspace deletes
the selected measurement. Escape cancels in-progress placement.
- Live placement preview + inline hint strip describes what the next
click does. Pinch-zoom and single-finger pan still work.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Replace the two-pass closed-form deskew (getPerspectiveTransform +
per-axis scale corrections) with an alternating-minimisation loop around
cv.findHomography (internal Levenberg–Marquardt). Each outer iteration
recomputes per-datum point correspondences from the current H and the
datum's shape constraint, then findHomography refines H. Confidence
drives per-correspondence replication; primary gets a 3× gauge boost.
- Add EllipseDatum type (center + two conjugate semi-axis endpoints +
known diameter) with 3-handle Konva rendering and coin presets.
- Generalise primary selection to any datum type. Priority rect >
ellipse > line; within type, confidence then image size. Warm-start
anchors: rect = 4 axis-aligned corners; ellipse = 4 conjugate-axis
samples on a world circle; line = 2 endpoints + 2 synthetic
perpendicular points (isotropic image-scale assumption).
- Direction-agnostic shape residuals: Procrustes-fit ideal (w × h) rect
to projected corners; midpoint-preserving line rescale; radial-snap
ellipse samples to a circle at projectPoint(H, center).
- Drop the "at least one rectangle" requirement. Any datum combination
works; diagnostics widgets auto-pick a scale reference across types.
- Diagnostics: replace X/Y axis-correction cards with RMS residual +
iteration count; per-datum table shows a residual breakdown column
(edge %, perp Δ°, iso/skew/dia).
- Detect period-2 oscillation in the outer loop and warn to console.
- Relative convergence threshold so the affine and perspective entries
of H are weighted comparably.
- Guard diagnostic diameter via geometric-mean-radius for non-circular
conics; guard collinear-axes ellipses; fix Mat leak in
solveHomography on the exception path.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>