feat: staff-finding pipeline improvements — line following, coord fix, stave QA boxes#53
Open
kyrieb-ekat wants to merge 15 commits into
Open
feat: staff-finding pipeline improvements — line following, coord fix, stave QA boxes#53kyrieb-ekat wants to merge 15 commits into
kyrieb-ekat wants to merge 15 commits into
Conversation
fit_centerline.py
- Add x_page_offset / y_page_offset fields to FitResult (default 0.0)
so crop-local coords can be converted to page-absolute downstream
- Add line-following refinement triggered when initial Huber residual
mean > 1.0 * scale_unit (multi-line box signal):
* _trace_line() slides left-to-right within a 1.5h band, emitting
one median-y per window — naturally follows any curve shape
* Refit uses cubic polynomial (LINE_FOLLOW_POLY_DEGREE=3) when
≥ 8 trace points available, else falls back to quadratic; handles
both C-shaped page arches and S-shaped parchment waves
* Only accepted if refit residual improves over initial fit
* Flagged as line_following_applied:deg3/deg2 in FitResult.flags
- Diagnostic overlay now shows cyan scatter dots for trace points when
line-following was applied; legend distinguishes from fitted line
group_staves.py
- _y_at_fit_center: add fit.y_page_offset so gap analysis uses
page-absolute y-positions (fixes all-gaps-near-zero bug that put
every line in one giant stave)
- _save_grouping_diagnostic drawing loop: add x/y page offsets to
xs/ys before cv2.polylines (fixes lines-at-origin visualization bug)
- After polylines loop, draw a red bounding rectangle + Sn label
around each stave group for quick visual QA
run_page.py
- Populate fit_result.x_page_offset / y_page_offset from actual_box
(ulx, uly) immediately after each fit_centerline call
test_fit_centerline.py
- Fix sys.path (was hardcoded /home/claude)
- Add test_two_line_box_line_following: constructs a ComponentFilterResult
directly with two-line pixel data, verifies line_following_applied flag
and tight residual (< 3 px) after the refit
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…and current sauvola settings.
…nore os metadata files .__
Adds GentAnt1475 folio 17 (left and right crops) with YOLO inference outputs (raw + corrected) as the primary test page for the implicit neural and interpolation experiments documented in experiments/implicit_neural/NOTES.md. Moves the Ordo Virtutum page from image-sets/ root into its own image-sets/ordo_virt_000/ subdirectory, matching the layout convention used for gent/. Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Introduces staff-finding/experiments/ — a family of standalone runner
scripts that plug in after YOLO detection and exercise different
curve-fitting approaches. Each runner produces identical JSOMR JSON and
stave-grouping diagnostic PNGs so results are directly comparable.
shared_utils.py: common helpers (YOLO parsing, ExperimentFitResult
bridge, run_grouping_and_save) shared by every runner.
Runners added:
- dp_tracing/ Dynamic-programming horizontal-emphasis tracer
- gp_centerlines/ Gaussian-process Matern fit on ink-pixel coords
- implicit_neural/ Test-time MLP on pixel brightness (best result
on Gent ms: mode=8, see implicit_neural/NOTES.md)
- periodicity/ Autocorrelation comb (limited on per-box crops)
- heatmap_regression/ Planned; NOTES.md stubs the design
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
…terpolate_staves
group_staves.py additions
- Stage 7: gap-based missing-line synthesis with two triggers:
(A) in-stave gap fill — fires when consecutive detected lines in
the same stave have a y-gap in [cut_threshold, max_threshold],
inserts n_missing lines evenly spaced between them
(B) edge extrapolation — grid-anchored at the bottommost detected
line, fills empty slots; requires >= mode_n//2 lines detected
(safety against over-synthesis on sparse/split staves)
- Adaptive thresholds: cut_threshold (Otsu/median), min_threshold
(noise floor = 0.5 * scale_unit), max_threshold (mean of
inter-stave gaps) — all three shown on the gap distribution chart
- Territory boundaries: interpolated lines are constrained to each
stave's y-range; top/bottom staves cannot extrapolate outside
their own detected extent
- Stage 6c _check_stave_rhythm: classifies each stave as normal /
under_populated / over_populated by comparing detected intra-stave
gap count to mode_n-1; also reports gap_cv (spacing consistency)
- Rhythm gate in Stage 7: staves flagged under/over_populated are
skipped entirely — their detected assignments are preserved as-is
- Diagnostic chart: per-bar colouring by rhythm status (red = under,
purple = over), annotated labels showing observed/expected counts,
shaded noise and missing-line trigger zones
- StaveGroupingResult gains min_threshold_px, interpolation_max_gap_px,
rhythm_anomalies fields; summary print shows stave count + avg
interpolate_staves.py (new)
Extracted from group_staves Stage 7 to keep grouping and synthesis
concerns separate. Contains InterpolatedLine dataclass,
_compute_interpolation_max_gap, _interpolate_between, and the public
interpolate_missing_lines() API. group_staves.py re-exports
InterpolatedLine for backward-compatible imports.
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
eval_page.py evaluates a single page run against ground-truth annotations, reporting per-stave line-count accuracy and flagging rhythm anomalies vs. expected stave structure. eval_batch.py runs eval_page across an image-set directory and aggregates results into a summary CSV, enabling cross-method and cross-manuscript comparison once all five experiment runners are complete. Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
staff-finding/README.md gives an overview of the pipeline stages, experiment variants, and output formats for new contributors. .gitignore updated to cover Python cache dirs and experiment output folders that should not be tracked. Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
…le test fix Stage 1 (component_filter.py — already committed): loosen merge thresholds (y-distance 0.5→1.0h, x-gap 5→10h) and add companion-score-floor retention so high-scoring non-overlapping fragments are kept alongside the merge winner. Diagnostics updated with cyan coloring for companions in panels 3–5. Stage 2 (run_page.py): add centerline_page section to each JSOMR record with page-absolute x_start/x_end/y_values (crop-local + x_page_offset/y_page_offset). Old centerline key preserved for backwards compatibility. Tests: fix stale assertion in test_grouping_with_missing_line_and_interpolation — interpolation was implemented in 572c025 but the test still expected []. E2E data: - 28may_stave-fulldata/: Ordo Virtutum 8-page run (8.6MB, all files) - 29may/: Gent right/left multi-variant run (JSON/CSV/TXT only, no PNGs) - pitch_finding_sample/: Gent right, 86 lines, 17 staves — stafflines JSON, stave grouping PNGs, summary CSV, stave grouping report for pitch-finding collaborator Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Line-following refinement (
fit_centerline.py): when a bounding box contains multiple arching stafflines (page curvature), the initial Huber fit averages across them and produces high residuals. A new sliding-window trace walks left-to-right within a ±1.5h band from the box vertical center, isolating one line before refitting with a cubic polynomial. Handles both C-shaped arches and S-shaped parchment waves. Flagged asline_following_applied:deg3/deg2.Page-coordinate offset fix (
fit_centerline.py,run_page.py,group_staves.py):FitResultcoordinates are crop-local. Addsx_page_offset/y_page_offsetfields (populated from YOLO bounding box ulx/uly inrun_page.py). Fixes two bugs:Stave bounding boxes (
group_staves.py): red rectangles +Snlabels drawn around each stave group in the diagnostic PNG for visual QA.Test fix (
test_fit_centerline.py): fixed hardcoded/home/claudesys.path; addedtest_two_line_box_line_followingverifying the line-following trigger and refit quality.Test plan
python -m pytest staff-finding/scripts/script_tests/test_fit_centerline.py staff-finding/scripts/script_tests/test_group_staves.py -v— 15 tests passrun_page.pyon a real page; confirm grouping diagnostic shows colored centerlines at correct positions, red stave boxes, and a reasonable mode lines/stave (~4–5 for square notation)line_following_appliedflag appears in fit diagnostics for boxes with high initial residuals🤖 Generated with Claude Code