Spaces:
Running on Zero
Running on Zero
File size: 8,091 Bytes
fed954e | 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 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 | """
fuse_schema.py — the FusedScene pydantic schema (fusion tier).
Hand-written nested models (the SubjectValue precedent — the SlotSpec codegen in
schema.py handles one nesting level; FusedScene needs three: entities ->
attributes -> ownership). Deliberately NOT a registered VisionTaskSpec: FusedScene
is a deterministically-produced dataset artifact, not a VLM probe — it needs no
GBNF, no system prompt, no scorer. If a VLM is ever trained to EMIT FusedScene,
register a flattened variant then.
Versioned from day one: this module is a second schema authority next to the
registry, so every instance stamps `fused_scene_version`.
Coordinate policy: all geometry emitted in the declared `coord_space`
(NORM_0_1000 by default, via specialists.box_to_space/poly_to_space); the one
pixel-unit field is `image_size`, documented as such. Scorers convert via
coords.to_canonical.
"""
from __future__ import annotations
from typing import Optional
from pydantic import BaseModel, Field
FUSED_SCENE_VERSION = "1.0"
# Caps (data, referenced by fuse.py — never hardcoded at call sites)
MAX_ENTITIES = 12 # kept by saliency rank
MAX_RELATION_ENTITIES = 8 # pairwise relations among the top-K entities
MASK_POLY_MAX_POINTS = 64 # outline_polygon(max_points=...) per entity
# Basin reasons (closed set, tested)
BASIN_REASONS = ("low_margin", "no_grounding_multi_entity", "ambiguous_binding",
"abstract_unbound")
# Ownership methods (closed set, tested)
OWNERSHIP_METHODS = ("single_entity", "mask_containment", "box_containment",
"caption_binding")
class GridPosition(BaseModel):
grid: str # "{upper|middle|lower} {left|center|right}"
offset_from_center: list[float] # (centroid-center)/half-extents, in [-1,1]
class DepthInfo(BaseModel):
nearness: float # continuous [0,1], bigger = nearer
rank: int # 1 = nearest
class SaliencyInfo(BaseModel):
score: float
rank: int # 1 = most salient
class MaskInfo(BaseModel):
polygon: list[float] = Field(default_factory=list) # flat x,y interleaved, task space
quality: Optional[float] = None # SAM predicted-IoU (retained signal)
class CaptionBinding(BaseModel):
source: str # caption column key
subject_name: str
confidence: float
class Ownership(BaseModel):
confidence: float
margin: Optional[float] = None # f1 - f2 (containment methods only)
method: str # one of OWNERSHIP_METHODS
class RegionOnOwner(BaseModel):
vertical: str # upper | middle | lower
horizontal: str # left | center | right
offset: list[float] # (attr center - owner centroid)/owner half-extents
class AttributeRecord(BaseModel):
text: str # canonical (longest) form after dedup
stratum: str
sources: list[str] # caption columns that carried it
consensus: float # len(sources)/n_caption_sources
grounded: bool = False
box: Optional[list[float]] = None # present iff grounded (task space)
grounding_score: Optional[float] = None # GDINO phrase score
ownership: Ownership
region_on_owner: Optional[RegionOnOwner] = None
class Entity(BaseModel):
id: str # person_1, person_2, dog, ... (left-to-right)
label: str
detection_score: float
box: list[float] # task space
centroid: list[float] # task space
area_frac: float
position: GridPosition
depth: Optional[DepthInfo] = None
saliency: SaliencyInfo
is_primary: bool = False
mask: Optional[MaskInfo] = None
caption_bindings: list[CaptionBinding] = Field(default_factory=list)
attributes: list[AttributeRecord] = Field(default_factory=list)
class Relation(BaseModel):
a: str # entity id (smaller entity index)
b: str
predicates: list[str] # spatial_relations predicate vocab
dx: float # (centroid_b - centroid_a)/W, task-space-free
dy: float
distance: float # centroid distance / image diagonal
iou: float
depth_delta: Optional[float] = None # nearness_a - nearness_b (continuous)
confidence: float # min(detection scores)
class BasinCandidate(BaseModel):
entity_id: str
likelihood: float
class BasinItem(BaseModel):
text: str
stratum: str
sources: list[str]
consensus: float
reason: str # one of BASIN_REASONS
grounded: bool = False
box: Optional[list[float]] = None
candidates: list[BasinCandidate] = Field(default_factory=list)
class VotedValue(BaseModel):
value: Optional[str] = None
votes: dict[str, int] = Field(default_factory=dict)
class StyleValue(BaseModel):
value: Optional[str] = None # specialist wins conflicts (it saw the image)
caption_votes: dict[str, int] = Field(default_factory=dict)
specialist: Optional[str] = None
class MoodValue(BaseModel):
value: Optional[str] = None
per_source: dict[str, str] = Field(default_factory=dict)
class SymmetryInfo(BaseModel):
axis: str = "none"
lr: Optional[float] = None # continuous correlations (retained)
tb: Optional[float] = None
class SceneAttribute(BaseModel):
text: str
stratum: str = "scene_level"
sources: list[str] = Field(default_factory=list)
class SceneOCRLine(BaseModel):
text: str
box: Optional[list[float]] = None
conf: Optional[float] = None # EasyOCR confidence (retained)
class SceneOCR(BaseModel):
full_text: str = ""
lines: list[SceneOCRLine] = Field(default_factory=list)
class LabelScore(BaseModel):
label: str
score: float
class SceneBlock(BaseModel):
setting: VotedValue = Field(default_factory=VotedValue)
style: StyleValue = Field(default_factory=StyleValue)
mood: MoodValue = Field(default_factory=MoodValue)
layout: str = "unknown"
symmetry: SymmetryInfo = Field(default_factory=SymmetryInfo)
actions: list[SceneAttribute] = Field(default_factory=list) # unbound caption actions
scene_attributes: list[SceneAttribute] = Field(default_factory=list)
ocr: SceneOCR = Field(default_factory=SceneOCR)
class_top: list[LabelScore] = Field(default_factory=list)
class Counts(BaseModel):
total_entities: int = 0
people: int = 0 # via the person synonym group
by_label: dict[str, int] = Field(default_factory=dict)
class GroundingStats(BaseModel):
phrases_total: int = 0
phrases_grounded: int = 0
assigned: int = 0
basin: int = 0
scene_level: int = 0
ungroundable: int = 0
class Quality(BaseModel):
n_caption_sources: int = 0
detection_score_mean: float = 0.0
mask_quality_mean: Optional[float] = None
ocr_conf_mean: Optional[float] = None
grounding: GroundingStats = Field(default_factory=GroundingStats)
overall_confidence: float = 0.0 # the fusion_confidence scalar
class FusedScene(BaseModel):
fused_scene_version: str = FUSED_SCENE_VERSION
coord_space: str
image_size: list[int] # (W, H) PIXELS — the one pixel field
counts: Counts = Field(default_factory=Counts)
entities: list[Entity] = Field(default_factory=list)
relations: list[Relation] = Field(default_factory=list)
shared_basin: list[BasinItem] = Field(default_factory=list)
scene: SceneBlock = Field(default_factory=SceneBlock)
quality: Quality = Field(default_factory=Quality)
FUSED_SCENE_JSON_SCHEMA = FusedScene.model_json_schema()
|