Split stitching into a hardware-independent myscan.stitching package

stitch.py and pagegraph.py mixed laser odometry, image registration,
frame correction, canvas handling, pose-graph maths and the two stitchers.
They are now modules of myscan/stitching, which imports nothing from the
device code:

  geometry    rigid transforms, pose vectors, warp_add
  filters     grayscale, high-pass, feather weights
  correct     Corrector: undistortion + flat field (and weighted variant)
  odometry    LaserOdometry: the laser model and the phase-correlation
              override, shared by both stitchers
  register    ECC, phase correlation, SIFT matching, overlap agreement
  canvas      growable Canvas, layout, parallel render, normalize/compose
  posegraph   robust Gauss-Newton, largest connected component
  loops       keyframes and loop-closure candidates
  pagegraph   PageGraph, now orchestration only
  incremental Stitcher, on the shared Canvas

The stitchers take a Corrector and an odometry object (anything with
predict()) instead of building them, so another device can supply its
own motion prior. live.py drops its own canvas and normalisation for the
shared ones; PageGraph's mosaic check uses the parallel renderer.

Output on scan_text is unchanged (global stitcher identical, incremental
within 1 grey level). stitch.py --live now applies lens correction too
(--no-undistort restores the old output).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
erik
2026-10-09 07:55:22 +00:00
co-authored by Claude Opus 5.5
parent 36e714cd62
commit c8b9f610bf
18 changed files with 907 additions and 765 deletions
+2 -2
View File
@@ -339,8 +339,8 @@ handling**. There is nothing in it to build a Linux driver from.
- Record v0/v1 = laser 1 cumulative (a, b) counts, v3/v4 = laser 2; signed 16-bit, wrapping. ~3 counts per frame pixel.
- Frame content shift: dx = -0.022*a - 0.355*b, dy = 0.324*a + 0.002*b (a, b = mean of both lasers).
- Rotation: dtheta ≈ -4.4e-4 rad * (b1 - b2) (fit against ORB rotation over 10-30 frame windows; corr ~0.88).
- At fast motion (~25 px/frame) the lasers disagree (one over-counts by ~40%), so stitch.py checks each step against
frame-to-frame phase correlation, then ECC-refines every frame against the canvas.
- At fast motion (~25 px/frame) the lasers disagree (one over-counts by ~40%), so the odometry (myscan/stitching/odometry.py)
checks each step against frame-to-frame phase correlation, and the stitchers then ECC-refine it.
- Frames are transposed + mirrored relative to the page; output = flip(transpose(canvas), -1).
- `tools/stitch.py <dir>` -> <dir>/page.png. Full 751-frame session stitches cleanly.