feat: green text overlay renderer

Add render_overlay() that composites semi-transparent green text on
the original document image using the detected font. This is the
visual proof mechanism for font detection accuracy. Also adds numpy
to project dependencies for test pixel analysis.

Co-Authored-By: Claude Opus 4.6 <[email protected]>
This commit is contained in:
Alex-GilbertandClaude Opus 4.6 committed 2026-02-15 18:49:39 -06:00
1 parent 2a3a0f2076
commit 73320365a3
3 files changed
+86

No files matched your search

+1
View File
@@ -11,6 +11,7 @@ dependencies = [
"opencv-python>=4.10", "opencv-python>=4.10",
"Pillow>=11.0", "Pillow>=11.0",
"anthropic>=0.52", "anthropic>=0.52",
"numpy>=1.26",
] ]
[project.optional-dependencies] [project.optional-dependencies]
+41
View File
@@ -0,0 +1,41 @@
from pathlib import Path
import numpy as np
from PIL import Image
from unredact.pipeline.overlay import render_overlay
from unredact.pipeline.ocr import ocr_page
from unredact.pipeline.font_detect import detect_font
from unredact.pipeline.rasterize import rasterize_pdf
def test_overlay_same_size_as_original(sample_pdf: Path):
pages = rasterize_pdf(sample_pdf, first_page=1, last_page=1)
lines = ocr_page(pages[0])
font_match = detect_font(lines, pages[0])
result = render_overlay(pages[0], lines, font_match)
assert result.size == pages[0].size
def test_overlay_has_green_pixels(sample_pdf: Path):
pages = rasterize_pdf(sample_pdf, first_page=1, last_page=1)
lines = ocr_page(pages[0])
font_match = detect_font(lines, pages[0])
result = render_overlay(pages[0], lines, font_match)
# Convert to numpy and check for green-ish pixels
arr = np.array(result)
# Green channel significantly higher than red and blue
green_mask = (arr[:, :, 1] > 100) & (arr[:, :, 0] < 100) & (arr[:, :, 2] < 100)
green_pixel_count = green_mask.sum()
assert green_pixel_count > 100, "Expected green text pixels in the overlay"
def test_overlay_preserves_original(sample_pdf: Path):
"""Overlay should be composited — original should still be visible."""
pages = rasterize_pdf(sample_pdf, first_page=1, last_page=1)
original = pages[0].copy()
lines = ocr_page(pages[0])
font_match = detect_font(lines, pages[0])
result = render_overlay(pages[0], lines, font_match)
# The result shouldn't be identical to the original (overlay adds green)
assert list(result.getdata()) != list(original.getdata())
+44
View File
@@ -0,0 +1,44 @@
from PIL import Image, ImageDraw, ImageFont
from unredact.pipeline.font_detect import FontMatch
from unredact.pipeline.ocr import OcrLine
def render_overlay(
page_image: Image.Image,
lines: list[OcrLine],
font_match: FontMatch,
color: tuple[int, int, int, int] = (0, 200, 0, 160),
) -> Image.Image:
"""Render green text overlay on top of the document image.
Draws each OCR'd line's text at its detected position using
the matched font. The overlay is semi-transparent so the
original document is still visible underneath.
Args:
page_image: The original rasterized page.
lines: OCR'd lines with character positions.
font_match: Detected font to use for rendering.
color: RGBA color for the overlay text.
Returns:
A new RGBA image with the overlay composited on the original.
"""
base = page_image.convert("RGBA")
overlay = Image.new("RGBA", base.size, (0, 0, 0, 0))
draw = ImageDraw.Draw(overlay)
font = font_match.to_pil_font()
for line in lines:
if not line.chars:
continue
text = line.text
# Position: use the first character's x and the line's y
x = line.x
y = line.y
draw.text((x, y), text, font=font, fill=color)
return Image.alpha_composite(base, overlay)