feat: Sprint 1 — IPA hardening, regression framework, ground-truth review
Some checks failed
CI / go-lint (push) Has been skipped
CI / python-lint (push) Has been skipped
CI / nodejs-lint (push) Has been skipped
CI / test-go-school (push) Successful in 28s
CI / test-go-edu-search (push) Successful in 27s
CI / test-python-klausur (push) Failing after 1m55s
CI / test-python-agent-core (push) Successful in 16s
CI / test-nodejs-website (push) Successful in 19s
Some checks failed
CI / go-lint (push) Has been skipped
CI / python-lint (push) Has been skipped
CI / nodejs-lint (push) Has been skipped
CI / test-go-school (push) Successful in 28s
CI / test-go-edu-search (push) Successful in 27s
CI / test-python-klausur (push) Failing after 1m55s
CI / test-python-agent-core (push) Successful in 16s
CI / test-nodejs-website (push) Successful in 19s
Track A (Backend): - Compound word IPA decomposition (schoolbag→school+bag) - Trailing garbled IPA fragment removal after brackets (R21 fix) - Regression runner with DB persistence, history endpoints - Page crop determinism verified with tests Track B (Frontend): - OCR Regression dashboard (/ai/ocr-regression) - Ground Truth Review workflow (/ai/ocr-ground-truth) with split-view, confidence highlighting, inline edit, batch mark, progress tracking Track C (Docs): - OCR-Pipeline.md v5.0 (Steps 5e-5h) - Regression testing guide - mkdocs.yml nav update Track D (Infra): - TrOCR baseline benchmark script - run-regression.sh shell script - Migration 008: regression_runs table Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -415,3 +415,53 @@ class TestDetectAndCropPage:
|
||||
assert 0 <= pct["y"] <= 100
|
||||
assert 0 < pct["width"] <= 100
|
||||
assert 0 < pct["height"] <= 100
|
||||
|
||||
|
||||
class TestCropDeterminism:
|
||||
"""A3: Verify that page crop produces identical results across N runs."""
|
||||
|
||||
@pytest.mark.parametrize("image_factory,desc", [
|
||||
(
|
||||
lambda: _make_image_with_content(800, 600, (100, 700, 80, 520)),
|
||||
"standard content",
|
||||
),
|
||||
(
|
||||
lambda: _make_book_scan(1000, 800),
|
||||
"book scan with spine shadow",
|
||||
),
|
||||
])
|
||||
def test_determinism_10_runs(self, image_factory, desc):
|
||||
"""Same image must produce identical crops in 10 consecutive runs."""
|
||||
img = image_factory()
|
||||
results = []
|
||||
for _ in range(10):
|
||||
cropped, result = detect_and_crop_page(img.copy())
|
||||
results.append({
|
||||
"crop_applied": result["crop_applied"],
|
||||
"cropped_size": result["cropped_size"],
|
||||
"border_fractions": result["border_fractions"],
|
||||
"shape": cropped.shape,
|
||||
})
|
||||
|
||||
first = results[0]
|
||||
for i, r in enumerate(results[1:], 1):
|
||||
assert r["crop_applied"] == first["crop_applied"], (
|
||||
f"Run {i} crop_applied differs from run 0 ({desc})"
|
||||
)
|
||||
assert r["cropped_size"] == first["cropped_size"], (
|
||||
f"Run {i} cropped_size differs from run 0 ({desc})"
|
||||
)
|
||||
assert r["shape"] == first["shape"], (
|
||||
f"Run {i} output shape differs from run 0 ({desc})"
|
||||
)
|
||||
|
||||
def test_determinism_pixel_identical(self):
|
||||
"""Crop output pixels must be identical across runs."""
|
||||
img = _make_image_with_content(800, 600, (100, 700, 80, 520))
|
||||
ref_crop, _ = detect_and_crop_page(img.copy())
|
||||
|
||||
for i in range(5):
|
||||
crop, _ = detect_and_crop_page(img.copy())
|
||||
assert np.array_equal(ref_crop, crop), (
|
||||
f"Run {i} produced different pixel output"
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user