File size: 3,812 Bytes
a9d655b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
"""HOWC hierarchical perception - HuggingFace Space (Gradio).

Upload a road image; a detector proposes boxes and each is classified by
hierarchical taxonomic abstraction: the most specific safe level, or an explicit
UNKNOWN, never a confident wrong leaf. Models download lazily on first run.

To deploy: this file plus requirements.txt and README.md, together with the
`hpercept/` package and `taxonomy.yaml` at the Space root (copy them in, or
`pip install` the package). See README.md.
"""
from __future__ import annotations

import numpy as np
import gradio as gr

from hpercept.abstraction import AbstractionConfig
from hpercept.pipeline import get_pipeline
from hpercept.viz import annotate, taxonomy_html

PIPE = get_pipeline()
TAX = PIPE.taxonomy


def analyze(image: np.ndarray, det_conf: float, commit_mass: float, floor: bool):
    if image is None:
        return None, "Upload an image first.", [], taxonomy_html(TAX, None), gr.update(choices=[])
    cfg = AbstractionConfig(commit_mass=float(commit_mass), enforce_floor=bool(floor))
    scene = PIPE.run(image, mode="clip", det_conf=float(det_conf), cfg=cfg)
    annotated = annotate(image, scene)
    rows = [[i, p.classification.label, p.classification.outcome.value,
             f"{p.classification.confidence:.2f}", p.box.coco_name]
            for i, p in enumerate(scene.predictions)]
    c = scene.counts()
    summary = (f"**{len(scene.predictions)} detections** - "
               f"🟢 {c['identified']} identified · 🟠 {c['abstracted']} abstracted · "
               f"🔴 {c['unknown']} unknown · ⬜ {c['rejected']} rejected")
    choices = [f"{i}: {p.classification.label}" for i, p in enumerate(scene.predictions)]
    first = scene.predictions[0] if scene.predictions else None
    analyze.scene = scene
    return annotated, summary, rows, taxonomy_html(TAX, first), gr.update(
        choices=choices, value=(choices[0] if choices else None))


def highlight(sel: str):
    scene = getattr(analyze, "scene", None)
    if not scene or not sel:
        return taxonomy_html(TAX, None)
    i = min(int(sel.split(":")[0]), len(scene.predictions) - 1)
    return taxonomy_html(TAX, scene.predictions[i])


with gr.Blocks(title="HOWC Hierarchical Perception") as demo:
    gr.Markdown(
        "# HOWC : hierarchical perception for novel road objects\n"
        "Objects that fit no flat class are **abstracted up a taxonomy** to the "
        "most specific *safe* level (bounded by a 🛡 floor), or flagged as an "
        "explicit **UNKNOWN OBSTACLE** - never dropped, never a confident wrong "
        "label. Paper: doi:10.5281/zenodo.21593472")
    with gr.Row():
        with gr.Column():
            img = gr.Image(label="Road image", type="numpy")
            det = gr.Slider(0.05, 0.9, value=0.25, step=0.05, label="Detection confidence")
            cm = gr.Slider(0.3, 0.9, value=0.40, step=0.05,
                           label="Commit mass (↑ = abstracts sooner)")
            fl = gr.Checkbox(value=True, label="Safety floor 🛡 (anti-paranoia)")
            btn = gr.Button("Analyze", variant="primary")
        with gr.Column():
            out = gr.Image(label="Annotated", type="numpy")
            summ = gr.Markdown()
            tbl = gr.Dataframe(headers=["#", "label", "outcome", "conf", "yolo"],
                               label="Detections")
            pick = gr.Dropdown(label="Highlight in taxonomy", choices=[])
            tree = gr.HTML(taxonomy_html(TAX, None))
    btn.click(analyze, [img, det, cm, fl], [out, summ, tbl, tree, pick])
    pick.change(highlight, pick, tree)

if __name__ == "__main__":
    import sys
    # `python app.py --share` -> a temporary public URL (~72h), for launch-day
    # demand tests without hosting a Space.
    demo.launch(share="--share" in sys.argv)