Detect and confirm page orientation during automatic splitting
Bundle the trained heatmap/image orientation model and run it after provisional blank-page and split analysis. Confirm candidates with Gemini using low thinking; resolve mixed A3 sheet ordering with the complete statement and medium thinking. Keep A4 scans independent and export paired A3 scans in booklet reading order. Reanalyse uniformly inverted source sheets, validate ordering permutations, and preserve source provenance, corrected rotations and reading slots in manual review. Document the experiments, rejected approaches, calibration and runtime limits. Validation: focused splitting/orientation regression suites pass; bundled model checksum and wheel assets verified. Full Interro07 offline run: 44.26 seconds, eight candidates, mocked upright confirmations, source hashes and geometry intact.
This commit is contained in:
1 parent
78f3e4b2f0
commit
0a4f4a80d0
21 files changed
+1574
-51
No files matched your search
@@ -0,0 +1,62 @@
|
||||
# Interro07 orientation experiment — 2026-11-04
|
||||
|
||||
Historical experiment. Start with the [orientation decision record](page_orientation.md)
|
||||
for the current recommendation, rejected approaches and links to later validation.
|
||||
|
||||
All scripts, manifests, model hashes, raw scores, test PDFs and galleries are in `tmp/orientation-benchmark/`. Paths below are relative to that experiment directory.
|
||||
|
||||
The best tested screen combines the official PaddlePaddle PP-LCNet_x1_0_doc_ori ONNX model on a full page and an ink-bounding crop, each in both orientations. At exploratory threshold 0.52: 61/64 inverted examples detected, 1/64 upright false positives; 24/24 inverted sparse pages detected. On the randomly mixed set, all 33 inverted pages detected and 1/31 upright false positives. Across 347 unrotated pages, 16 flags (4.61%). The 18 flags at threshold 0.5 were visually inspected and appear upright.
|
||||
|
||||
## Dataset
|
||||
|
||||
Source: saved automatic split outputs in ../auto-page-split-interro07-fast-final-20261103/pdfs (47 copies, 347 retained logical pages). Full sheets at 75 dpi, before margin cropping. 24 pages with largest blank vertical gaps, plus 40 seeded random remaining pages (seed 73107), spanning 36 copies. All 64 selected originals visually checked in contact-0.jpg through contact-3.jpg. Sparse subset includes a single exercise heading, very faint notes, equations, and writing occupying only the top or bottom of a sheet. Selected maximum blank runs range from 60% to 97% of height. Gap detection is a simple dark-ink projection, not a ground-truth whitespace segmentation.
|
||||
|
||||
sample.json records a random 0/180° assignment (33 rotated, 31 unchanged). rotated-pdfs/ contains copies of the selected PDFs with those actual PDF rotations applied. Other pages in those PDFs are unchanged. Mixed sample gallery: index.html. Paired testing additionally evaluates every selected page at both 0° and 180° (128 examples). This avoids drawing conclusions solely from lucky random assignment.
|
||||
|
||||
Original evaluation PDFs were only read. All artifacts are isolated here. Input PDFs for the measured splitter trial are Interro07/Copies Originales; its experimental exports go to batch-with-orientation/.
|
||||
|
||||
## Results
|
||||
|
||||
| Screen | Rotated detected /64 | Upright false alarms /64 | Sparse rotations detected /24 |
|
||||
|---|---:|---:|---:|
|
||||
| Official page preprocessing (short side 256, centre crop 224) | 51 | 4 | 16 |
|
||||
| Whole sheet resized to 224 square | 50 | 1 | 19 |
|
||||
| Up to five detected writing components, small text-line classifier | 30 | 7 | 10 |
|
||||
| Top/bottom ink-margin asymmetry | 49 | 16 | 23 |
|
||||
| Ink crop resized to square | 47 | 0 | 19 |
|
||||
| Whole sheet, both orientations | 55 | 1 | 19 |
|
||||
| Ink crop, both orientations | 55 | 0 | 23 |
|
||||
| Maximum of whole-sheet and ink-crop paired scores, threshold .52 | **61** | **1** | **24** |
|
||||
| Tesseract OSD (180° output) | 9 | 23 | — |
|
||||
|
||||
Tesseract returned no orientation on 29/64 upright and 28/64 inverted examples; 128 calls took 6.07 seconds with four workers. These failures are counted as missed rotations, not upright successes. Its sample runtime extrapolates to about 16.5 seconds for 347 pages, but that is not a measured full-batch time.
|
||||
|
||||
All neural and layout scores above use threshold .5 except layout (>0) and combined (.52). Combined .5 catches the same 61, with the same one sample false alarm, but flags 18/347 original pages (5.19%); .52 reduces that to 16/347. Threshold .55 catches 60/64 with 13/347 full-batch flags.
|
||||
|
||||
Missed by the combined .52 screen: Copie01-p03, Copie03-p07, Copie36-p08. They are predominantly formula-heavy; none belongs to the sparse subset. These may still be flagged through a companion half of the same scanned spread, but no credit for that was included.
|
||||
|
||||
## Algorithm and timing boundaries
|
||||
|
||||
ONNX Runtime 1.31.1 CPU execution provider; Ryzen 7 PRO 7840U; one inference thread per session. Both sessions load in 0.034 seconds. Models are official PaddlePaddle Hugging Face exports; models/*.yml contain their supplied preprocessing configuration, model-hashes.json pins actual downloaded bytes.
|
||||
|
||||
Paired score = (P180(image) + P0(rotated image))/2. Combined score = max(paired full-page score, paired ink-crop score). This is an empirical ranking score, not a calibrated probability. Crop bounds use .5/99.5 percentiles of pixels with min RGB <100, with 12-pixel padding; 15-pixel borders are excluded. The line model receives up to five large horizontally connected ink components resized to its official 160x80 input. This is a cheap extraction baseline, not a full text detector; its poor result does not establish that every line-based approach fails.
|
||||
|
||||
Separate full-corpus kernel timings: whole-page forward screen 1.43 seconds, resized-sheet screen 1.35 seconds, line extraction/classification 5.69 seconds, layout projection 3.91 seconds. Four combined model passes take about 6.52 seconds sequentially for 347 pages, excluding image loading and shared ink extraction/crop creation. Rendering the already split PDFs separately took 52.49 seconds and is deliberately avoided by reusing splitting previews.
|
||||
|
||||
integration.py temporarily wraps the splitter inside the benchmark process to reuse its already rendered RGB overview for each retained output. It records orientation_score in experimental reports and performs normal splitting/export/preview generation. No application source was edited. Its inputs include all 214 original scanned pages. See batch-with-orientation/timing.json for measured total time including process startup, detection, splitting, exports and previews. Scores from source overviews can differ slightly from separately rendered output PDFs due to raster alignment and overlapping crop boundaries; classification accuracy above is measured on the saved split-page test set.
|
||||
|
||||
## Interpretation
|
||||
|
||||
The combined model is the best measured candidate. At this exploratory operating point it satisfies the desired low referral volume on this batch and catches 95.3% of paired rotations, including all sparse examples tested. LLM confirmation should check every flag; no LLM requests were made in this experiment.
|
||||
|
||||
The variants and .52 threshold were explored on this same evaluation. These numbers are descriptive, not independent validation or a guarantee for another evaluation. Full-corpus referral counts use the existing scans as the presumed upright baseline; selected examples and all combined flags were visually reviewed, not every other page. New evaluation/student holdout data should determine the eventual threshold. Referral rate also depends on the true proportion of inverted pages: when testing a deliberately half-inverted set it should exceed 5%.
|
||||
|
||||
## Reproduce
|
||||
|
||||
Use a Python environment with project dependencies and onnxruntime. Run prepare.py, bench.py, variants.py, report.py. bench.py overwrites results.json; variants.py appends its extra scores. `python integration.py with-orientation` runs the full splitter trial; its output directory must not already exist. baseline runs the same splitter without the wrapper. Model URLs: https://huggingface.co/PaddlePaddle/PP-LCNet_x1_0_doc_ori_onnx and https://huggingface.co/PaddlePaddle/PP-LCNet_x0_25_textline_ori_onnx .
|
||||
|
||||
## Measured full splitting run
|
||||
|
||||
Matched runs with four worker processes: baseline 36.55 seconds; combined orientation screen 39.62 seconds; added time 3.07 seconds. One run of each, sequentially, on the same machine, not a statistical timing distribution. An earlier baseline attempt was interrupted and excluded.
|
||||
|
||||
Integrated screen flagged 15/347 retained pages (4.32%) at .52. The separate saved-PDF screen flagged 16/347 (4.61%); small raster/crop differences explain why these are distinct measurements. Export boundaries and all 420 keep/remove decisions match the existing reference report exactly. Verified all 33 PDF rotations and all 47 original source hashes (verification.json). LLM confirmation latency is not included.
|
||||
Reference in new issue
Block a user