Initial scaffold: Flask/SQLite backend, React/Vite PWA frontend
Backend: receipt upload -> LLM vision extraction (OpenAI-compatible, provider-agnostic), item review/edit, per-group splitting against Cospend projects/members, highlight+upload via WebDAV, public share link, bill creation via Cospend's OCS API (verified against real source, not just doc summaries). Frontend: capture -> review -> group -> summary flow as an installable PWA. install.sh / run.sh (venv + npm, tmux session) instead of Docker, per the ~/Projects/gain pattern.
This commit is contained in:
@@ -0,0 +1,32 @@
|
||||
"""Draws highlight boxes over selected items on the original receipt image."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import io
|
||||
|
||||
from PIL import Image, ImageDraw
|
||||
|
||||
_BOX_COLOR = (255, 210, 0) # translucent yellow highlighter look
|
||||
_BOX_WIDTH = 4
|
||||
|
||||
|
||||
def highlight_items(image_bytes: bytes, bboxes: list[list[float]]) -> bytes:
|
||||
"""`bboxes` are normalized [x, y, w, h] (0-1). Returns JPEG bytes."""
|
||||
base = Image.open(io.BytesIO(image_bytes)).convert("RGBA")
|
||||
overlay = Image.new("RGBA", base.size, (0, 0, 0, 0))
|
||||
draw = ImageDraw.Draw(overlay)
|
||||
w, h = base.size
|
||||
|
||||
for bbox in bboxes:
|
||||
if not bbox or len(bbox) != 4:
|
||||
continue
|
||||
x, y, bw, bh = bbox
|
||||
left, top = x * w, y * h
|
||||
right, bottom = (x + bw) * w, (y + bh) * h
|
||||
draw.rectangle([left, top, right, bottom], fill=(*_BOX_COLOR, 70))
|
||||
draw.rectangle([left, top, right, bottom], outline=(*_BOX_COLOR, 255), width=_BOX_WIDTH)
|
||||
|
||||
combined = Image.alpha_composite(base, overlay).convert("RGB")
|
||||
out = io.BytesIO()
|
||||
combined.save(out, format="JPEG", quality=90)
|
||||
return out.getvalue()
|
||||
Reference in New Issue
Block a user