Implement step 5 (white-border padding: 10 px on all sides), wire all preprocessing steps into the final preprocess(input, ImageSource) -> GrayImage entry point, and curate fixtures for the three image-source paths (PhysicalScan / DigitalOrigin / Jbig2). Changes: - Add add_border_padding() function: creates (width+20) x (height+20) image with 10px white border on all sides - Add preprocess() pipeline orchestrator: applies deskew, contrast normalization, binarization, denoising, and padding in correct order - Skip contrast, binarization, and denoising for JBIG2 images - Generate test fixtures for skewed_2deg, uneven_lighting, clean_digital, and jbig2_scan scenarios - Add integration tests for all critical test scenarios - Add A4-page benchmarks targeting < 500ms for physical/digital, < 200ms for JBIG2 Refs: - Plan section: Phase 5.3 step 5 (line 1878) + critical tests (lines 1882-1885) - Bead: pdftract-27n3 - Note: notes/pdftract-27n3.md Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
107 lines
3.2 KiB
Python
107 lines
3.2 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
Generate preprocessing test fixtures.
|
|
|
|
This script creates synthetic test images for the preprocessing pipeline:
|
|
- skewed_2deg: 2-degree skewed text lines (tests deskew)
|
|
- uneven_lighting: gradient background with text (tests Sauvola binarization)
|
|
- clean_digital: crisp digital text (tests Otsu binarization)
|
|
- jbig2_scan: binary text (tests JBIG2 skip logic)
|
|
"""
|
|
|
|
import math
|
|
from PIL import Image, ImageDraw, ImageFont
|
|
|
|
|
|
def create_skewed_2deg():
|
|
"""Create a 2-degree skewed image for deskew testing."""
|
|
width, height = 400, 300
|
|
|
|
# Create an image with horizontal text lines
|
|
img = Image.new('L', (width, height), color=255)
|
|
draw = ImageDraw.Draw(img)
|
|
|
|
# Draw horizontal text lines
|
|
for y in range(50, 250, 20):
|
|
draw.text((50, y), "Lorem ipsum dolor sit amet", fill=0)
|
|
|
|
# Rotate by 2 degrees
|
|
img_skewed = img.rotate(2, resample=Image.BICUBIC, expand=False, fillcolor=255)
|
|
|
|
img_skewed.save('tests/fixtures/preprocess/skewed_2deg/source.png')
|
|
print("Created skewed_2deg/source.png")
|
|
|
|
|
|
def create_uneven_lighting():
|
|
"""Create an image with uneven lighting for Sauvola testing."""
|
|
width, height = 400, 300
|
|
|
|
# Create a gradient background (uneven lighting)
|
|
img = Image.new('L', (width, height))
|
|
pixels = img.load()
|
|
|
|
for x in range(width):
|
|
for y in range(height):
|
|
# Gradient from darker (left) to lighter (right)
|
|
val = int(150 + (x / width) * 80)
|
|
pixels[x, y] = val
|
|
|
|
draw = ImageDraw.Draw(img)
|
|
|
|
# Draw text on the uneven background
|
|
for y in range(50, 250, 25):
|
|
draw.text((50, y), "Sample text for testing", fill=0)
|
|
|
|
img.save('tests/fixtures/preprocess/uneven_lighting/source.png')
|
|
print("Created uneven_lighting/source.png")
|
|
|
|
|
|
def create_clean_digital():
|
|
"""Create a clean digital-origin image for Otsu testing."""
|
|
width, height = 400, 300
|
|
|
|
# Create a clean white background
|
|
img = Image.new('L', (width, height), color=255)
|
|
draw = ImageDraw.Draw(img)
|
|
|
|
# Draw crisp text (as if from a digital PDF)
|
|
for y in range(50, 250, 25):
|
|
draw.text((50, y), "Digital document text", fill=0)
|
|
|
|
img.save('tests/fixtures/preprocess/clean_digital/source.png')
|
|
print("Created clean_digital/source.png")
|
|
|
|
|
|
def create_jbig2_scan():
|
|
"""Create a binary image (simulating JBIG2)."""
|
|
width, height = 400, 300
|
|
|
|
# Create a pure binary image
|
|
img = Image.new('L', (width, height), color=255)
|
|
draw = ImageDraw.Draw(img)
|
|
|
|
# Draw binary text
|
|
for y in range(50, 250, 25):
|
|
draw.text((50, y), "Binary JBIG2 text", fill=0)
|
|
|
|
# Ensure it's truly binary (only 0 and 255)
|
|
pixels = img.load()
|
|
for x in range(width):
|
|
for y in range(height):
|
|
val = pixels[x, y]
|
|
if val < 128:
|
|
pixels[x, y] = 0
|
|
else:
|
|
pixels[x, y] = 255
|
|
|
|
img.save('tests/fixtures/preprocess/jbig2_scan/source.png')
|
|
print("Created jbig2_scan/source.png")
|
|
|
|
|
|
if __name__ == '__main__':
|
|
print("Generating preprocessing test fixtures...")
|
|
create_skewed_2deg()
|
|
create_uneven_lighting()
|
|
create_clean_digital()
|
|
create_jbig2_scan()
|
|
print("Done!")
|