AVIS / core /evidence /__init__.py
X2-0's picture
HF Clean Deploy
1c0c94d
Raw
History Blame Contribute Delete
1.85 kB
"""Evidence integrity + annotated-image composition. The original image is never
modified — we hash it and draw on a copy.
"""
from __future__ import annotations
import hashlib
from core.schemas import EvidenceGraph, Route, Violation
_ROUTE_COLOR = {
Route.auto_confirmed: (220, 30, 30),
Route.vlm_confirmed: (240, 140, 0),
Route.human_review: (210, 180, 0),
Route.abstain: (130, 130, 130),
}
def hash_image(image_path: str) -> str:
h = hashlib.sha256()
with open(image_path, "rb") as fh:
for chunk in iter(lambda: fh.read(8192), b""):
h.update(chunk)
return h.hexdigest()
def annotate(
image_path: str,
out_path: str,
violations: list[Violation],
graph: EvidenceGraph,
) -> None:
from PIL import Image, ImageDraw
img = Image.open(image_path).convert("RGB")
draw = ImageDraw.Draw(img)
# faint context: all detected vehicles
for v in graph.vehicles:
b = v.bbox
draw.rectangle([b.x1, b.y1, b.x2, b.y2], outline=(70, 130, 220), width=2)
# violation subjects, coloured by disposition
for vio in violations:
color = _ROUTE_COLOR.get(vio.route, (220, 30, 30))
label = f"{vio.type} {vio.scores.fused:.2f} [{vio.route.value}]"
for sid in vio.subjects:
nb = graph.node_bbox(sid)
if nb is None:
continue
draw.rectangle([nb.x1, nb.y1, nb.x2, nb.y2], outline=color, width=3)
first = graph.node_bbox(vio.subjects[0]) if vio.subjects else None
if first is not None:
draw.text((first.x1, max(0, first.y1 - 12)), label, fill=color)
if vio.plate is not None and vio.plate.text:
draw.text(
(first.x1, first.y2 + 2), f"plate {vio.plate.text}", fill=color
)
img.save(out_path)