Sempy32 commited on
Commit
35fefbe
·
verified ·
1 Parent(s): 23e7f36

Upload folder using huggingface_hub

Browse files
Files changed (3) hide show
  1. README.md +2 -2
  2. __pycache__/app.cpython-311.pyc +0 -0
  3. app.py +95 -12
README.md CHANGED
@@ -7,7 +7,7 @@ sdk: gradio
7
  sdk_version: 6.6.0
8
  app_file: app.py
9
  pinned: false
10
- short_description: Florence-2 tagging for SAM 3 auto-discovery
11
  ---
12
 
13
- `api_autotag(image, max_tags)` -> JSON {labels: [...], tags: [{name, count}]} via Florence-2 <OD>.
 
7
  sdk_version: 6.6.0
8
  app_file: app.py
9
  pinned: false
10
+ short_description: Florence-2 multi-task tagging for SAM 3 auto-discovery
11
  ---
12
 
13
+ `api_autotag(image, max_tags)` -> JSON `{labels: [...], tags: [{name, count}], tasks: [...]}` via Florence-2 `<OD>`, `<DENSE_REGION_CAPTION>`, and `<MORE_DETAILED_CAPTION>`.
__pycache__/app.cpython-311.pyc ADDED
Binary file (9.46 kB). View file
 
app.py CHANGED
@@ -1,6 +1,14 @@
1
- """Florence-2 object tagger (ZeroGPU): proposes class names from an image."""
 
 
 
 
 
 
 
2
  import os
3
  from collections import Counter
 
4
 
5
  import gradio as gr
6
  import spaces
@@ -14,27 +22,102 @@ processor = AutoProcessor.from_pretrained(MODEL_ID)
14
  model = Florence2ForConditionalGeneration.from_pretrained(MODEL_ID)
15
  model.eval()
16
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
17
 
18
  @spaces.GPU(duration=120)
19
  def api_autotag(image, max_tags, num_beams=3):
20
  if image is None:
21
  return {"error": "no image provided"}
22
  image = image.convert("RGB")
23
- device = "cuda"
24
- model.to(device)
25
- inputs = processor(text="<OD>", images=image, return_tensors="pt").to(device)
26
- with torch.no_grad():
27
- gen = model.generate(**inputs, max_new_tokens=1024, num_beams=int(num_beams))
28
- text = processor.batch_decode(gen, skip_special_tokens=False)[0]
29
- parsed = processor.post_process_generation(text, task="<OD>", image_size=image.size)
30
- labels = parsed.get("<OD>", {}).get("labels", [])
31
- counts = Counter(str(l).strip().lower() for l in labels if str(l).strip())
32
  tags = [{"name": n, "count": c} for n, c in counts.most_common(int(max_tags))]
33
- return {"model": MODEL_ID, "tags": tags, "labels": [t["name"] for t in tags]}
34
 
35
 
36
  with gr.Blocks(title="SAM3 AutoTag") as demo:
37
- gr.Markdown("# Florence-2 AutoTag\nUpload an image; returns detected object class names.")
38
  with gr.Row():
39
  inp = gr.Image(type="pil", label="Image")
40
  out = gr.JSON(label="Tags")
 
1
+ """Florence-2 scene tagger (ZeroGPU): proposes class names from an image.
2
+
3
+ The first implementation used only the Florence ``<OD>`` task, which is too
4
+ sparse for street-level panoptic work because object detection misses broad
5
+ surface/stuff classes. This endpoint now combines object detection, dense
6
+ region captions, and detailed caption text into one open-vocabulary concept
7
+ list for SAM3.
8
+ """
9
  import os
10
  from collections import Counter
11
+ import re
12
 
13
  import gradio as gr
14
  import spaces
 
22
  model = Florence2ForConditionalGeneration.from_pretrained(MODEL_ID)
23
  model.eval()
24
 
25
+ TASKS = ("<OD>", "<DENSE_REGION_CAPTION>", "<MORE_DETAILED_CAPTION>")
26
+ DROP_WORDS = {
27
+ "a", "an", "the", "this", "that", "these", "those", "there", "here",
28
+ "image", "photo", "picture", "view", "scene", "background", "foreground",
29
+ "left", "right", "top", "bottom", "front", "back", "side", "area", "part",
30
+ "visible", "large", "small", "several", "multiple", "many", "some",
31
+ }
32
+ CAPTION_SPLIT = re.compile(
33
+ r"[,.;:]|\\bwith\\b|\\band\\b|\\bnext to\\b|\\bin front of\\b|\\bbehind\\b|\\bon\\b|\\balong\\b|\\bnear\\b",
34
+ flags=re.IGNORECASE,
35
+ )
36
+
37
+
38
+ def _clean_label(value: str) -> str:
39
+ text = str(value).strip().lower()
40
+ text = re.sub(r"[_/\\-]+", " ", text)
41
+ text = re.sub(r"[^a-z0-9\\s]+", " ", text)
42
+ words = [w for w in text.split() if w and w not in DROP_WORDS]
43
+ if not words:
44
+ return ""
45
+ if len(words) > 5:
46
+ words = words[-5:]
47
+ return " ".join(words)
48
+
49
+
50
+ def _labels_from_caption(text: str) -> list[str]:
51
+ labels: list[str] = []
52
+ for chunk in CAPTION_SPLIT.split(str(text)):
53
+ clean = _clean_label(chunk)
54
+ if not clean:
55
+ continue
56
+ words = clean.split()
57
+ # Prefer compact noun-like endings from descriptive chunks while keeping
58
+ # short labels intact. This is intentionally generic, not a fixed class
59
+ # list for one dataset.
60
+ for candidate in (" ".join(words[-3:]), " ".join(words[-2:]), words[-1]):
61
+ candidate = _clean_label(candidate)
62
+ if candidate and len(candidate) > 2:
63
+ labels.append(candidate)
64
+ break
65
+ return labels
66
+
67
+
68
+ def _extract_labels(parsed) -> list[str]:
69
+ labels: list[str] = []
70
+
71
+ def walk(obj, key_hint: str = ""):
72
+ if isinstance(obj, dict):
73
+ for key, value in obj.items():
74
+ k = str(key).lower()
75
+ if k in {"label", "labels", "caption", "captions", "text", "description", "descriptions"}:
76
+ walk(value, k)
77
+ else:
78
+ walk(value, key_hint)
79
+ elif isinstance(obj, (list, tuple)):
80
+ for item in obj:
81
+ walk(item, key_hint)
82
+ elif isinstance(obj, str):
83
+ if key_hint in {"label", "labels"}:
84
+ clean = _clean_label(obj)
85
+ if clean:
86
+ labels.append(clean)
87
+ else:
88
+ labels.extend(_labels_from_caption(obj))
89
+
90
+ walk(parsed)
91
+ return labels
92
+
93
+
94
+ def _run_florence_task(image, task: str, num_beams: int) -> dict:
95
+ device = "cuda"
96
+ model.to(device)
97
+ inputs = processor(text=task, images=image, return_tensors="pt").to(device)
98
+ with torch.no_grad():
99
+ gen = model.generate(**inputs, max_new_tokens=1536, num_beams=int(num_beams))
100
+ text = processor.batch_decode(gen, skip_special_tokens=False)[0]
101
+ parsed = processor.post_process_generation(text, task=task, image_size=image.size)
102
+ return {"task": task, "text": text, "parsed": parsed, "labels": _extract_labels(parsed)}
103
+
104
 
105
  @spaces.GPU(duration=120)
106
  def api_autotag(image, max_tags, num_beams=3):
107
  if image is None:
108
  return {"error": "no image provided"}
109
  image = image.convert("RGB")
110
+ task_results = [_run_florence_task(image, task, int(num_beams)) for task in TASKS]
111
+ labels = []
112
+ for result in task_results:
113
+ labels.extend(result["labels"])
114
+ counts = Counter(label for label in labels if label)
 
 
 
 
115
  tags = [{"name": n, "count": c} for n, c in counts.most_common(int(max_tags))]
116
+ return {"model": MODEL_ID, "tasks": task_results, "tags": tags, "labels": [t["name"] for t in tags]}
117
 
118
 
119
  with gr.Blocks(title="SAM3 AutoTag") as demo:
120
+ gr.Markdown("# Florence-2 AutoTag\nUpload an image; returns multi-task scene class names for SAM3.")
121
  with gr.Row():
122
  inp = gr.Image(type="pil", label="Image")
123
  out = gr.JSON(label="Tags")