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:
+6
-7
@@ -11,15 +11,14 @@ import cv2 # noqa: E402
|
||||
import numpy as np # noqa: E402
|
||||
|
||||
from myscan import calib # noqa: E402
|
||||
from myscan.pagegraph import PageGraph # noqa: E402
|
||||
from myscan.stitch import ROT_GAIN, Stitcher # noqa: E402
|
||||
from myscan.stitching import ROT_GAIN, Corrector, LaserOdometry, PageGraph, Stitcher # noqa: E402
|
||||
|
||||
|
||||
def main():
|
||||
ap = argparse.ArgumentParser()
|
||||
ap.add_argument("dir")
|
||||
ap.add_argument("-o", "--out", help="output image (default <dir>/page.png)")
|
||||
ap.add_argument("--live", action="store_true", help="use the live frame-by-frame stitcher instead of the global one")
|
||||
ap.add_argument("--live", action="store_true", help="use the incremental frame-by-frame stitcher")
|
||||
ap.add_argument("--no-refine", action="store_true", help="with --live: lasers only, no image registration")
|
||||
ap.add_argument("--rot-gain", type=float, default=ROT_GAIN)
|
||||
ap.add_argument("--no-flat", action="store_true", help="skip the EEPROM flat-field correction")
|
||||
@@ -35,14 +34,14 @@ def main():
|
||||
rng = range(a.start, a.end if a.end is not None else len(rows))
|
||||
|
||||
cal = calib.load()
|
||||
flat = None if a.no_flat else calib.FlatField(cal)
|
||||
undistort = None if a.no_undistort else calib.Undistort(cal)
|
||||
corrector = Corrector(None if a.no_flat else calib.FlatField(cal),
|
||||
None if a.no_undistort else calib.Undistort(cal))
|
||||
st = None
|
||||
for i in rng:
|
||||
im = cv2.imread(os.path.join(a.dir, f"f{frames[i]:05d}.png"), cv2.IMREAD_UNCHANGED)
|
||||
if st is None:
|
||||
st = (Stitcher(im.shape, refine=not a.no_refine, rot_gain=a.rot_gain, flat=flat) if a.live
|
||||
else PageGraph(im.shape, flat=flat, undistort=undistort, rot_gain=a.rot_gain))
|
||||
st = (Stitcher(im.shape, corrector, LaserOdometry(a.rot_gain, max_step_rot=None), refine=not a.no_refine)
|
||||
if a.live else PageGraph(im.shape, corrector, LaserOdometry(a.rot_gain)))
|
||||
st.add(im, v[i])
|
||||
out = a.out or os.path.join(a.dir, "page.png")
|
||||
cv2.imwrite(out, st.image(white_balance=not a.no_wb))
|
||||
|
||||
Reference in New Issue
Block a user