veil-pgd / src /veil_pgd /render /region.py
Klaus Clawd
Initial public release: VEIL-PGD v0.1
c793f45
Raw
History Blame Contribute Delete
2.43 kB
"""Placement: find low-variance regions and resolve a position to a pixel box.
Low-variance regions (sky, walls, uniform backgrounds) hide faint text best and
also give the VLM a clean patch to read. We rank a small grid of candidate
boxes by local variance and a position prior.
"""
from __future__ import annotations
import numpy as np
from PIL import Image
from veil_pgd.types import Position
REFERENCE = 1024 # canonical size specs are defined against
def _position_anchor(position: Position, w: int, h: int, bw: int, bh: int) -> tuple[int, int]:
"""Top-left pixel for a text box of size (bw, bh) at a named position."""
pad = int(0.03 * max(w, h))
xmid = (w - bw) // 2
ymid = (h - bh) // 2
table = {
"center": (xmid, ymid),
"top_middle": (xmid, pad),
"bottom_middle": (xmid, h - bh - pad),
"top_left": (pad, pad),
"top_right": (w - bw - pad, pad),
"bottom_left": (pad, h - bh - pad),
"bottom_right": (w - bw - pad, h - bh - pad),
}
return table.get(position, table["bottom_middle"])
def resolve_box(
image: Image.Image, position: Position, box_w: int, box_h: int
) -> tuple[int, int, int, int]:
"""Return (x0, y0, x1, y1) for the overlay text box, clamped to the image."""
w, h = image.size
x0, y0 = _position_anchor(position, w, h, box_w, box_h)
x0 = max(0, min(x0, w - box_w))
y0 = max(0, min(y0, h - box_h))
return x0, y0, x0 + box_w, y0 + box_h
def region_variance(image: Image.Image, box: tuple[int, int, int, int]) -> float:
"""Mean per-channel variance inside a box (lower = flatter = stealthier)."""
crop = np.asarray(image.convert("RGB").crop(box), dtype=np.float32)
if crop.size == 0:
return float("inf")
return float(crop.reshape(-1, 3).var(axis=0).mean())
def rank_positions(
image: Image.Image, positions: list[Position], box_w: int, box_h: int
) -> list[tuple[Position, float]]:
"""Rank candidate positions by ascending region variance."""
scored = []
for p in positions:
box = resolve_box(image, p, box_w, box_h)
scored.append((p, region_variance(image, box)))
return sorted(scored, key=lambda kv: kv[1])
def scale_px(font_px: int, image: Image.Image) -> int:
"""Scale a reference (1024px) font size to the true image's short side."""
short = min(image.size)
return max(6, round(font_px * short / REFERENCE))