Spaces:
Sleeping
Sleeping
Commit ·
f8dd93d
1
Parent(s): ae18450
Revamp explanation generation in YOLO model using FLAN-T5, and add Florence-2 Fusion model
Browse files- ai_router.py +3 -0
- models/blip_yolo_model.py +27 -12
- models/florence_fusion_model.py +55 -0
- static/app.js +6 -5
- templates/index.html +1 -0
ai_router.py
CHANGED
|
@@ -4,6 +4,7 @@ from models.gemini_vision import gemini_vision_answer
|
|
| 4 |
from models.groq_vision import groq_vision_answer
|
| 5 |
from models.blip_yolo_model import blip_yolo_answer
|
| 6 |
from models.hf_boss_api import hf_boss_answer
|
|
|
|
| 7 |
|
| 8 |
try:
|
| 9 |
from deep_translator import GoogleTranslator
|
|
@@ -30,6 +31,8 @@ def route_model(model_choice, image, question, lang="en"):
|
|
| 30 |
cap, ans, exp = groq_vision_answer(image, question, lang)
|
| 31 |
elif model_choice == "hf_boss":
|
| 32 |
cap, ans, exp = hf_boss_answer(image, question, lang)
|
|
|
|
|
|
|
| 33 |
else:
|
| 34 |
cap, ans, exp = "Unknown", "Invalid", "Invalid"
|
| 35 |
|
|
|
|
| 4 |
from models.groq_vision import groq_vision_answer
|
| 5 |
from models.blip_yolo_model import blip_yolo_answer
|
| 6 |
from models.hf_boss_api import hf_boss_answer
|
| 7 |
+
from models.florence_fusion_model import florence_answer
|
| 8 |
|
| 9 |
try:
|
| 10 |
from deep_translator import GoogleTranslator
|
|
|
|
| 31 |
cap, ans, exp = groq_vision_answer(image, question, lang)
|
| 32 |
elif model_choice == "hf_boss":
|
| 33 |
cap, ans, exp = hf_boss_answer(image, question, lang)
|
| 34 |
+
elif model_choice == "florence":
|
| 35 |
+
cap, ans, exp = florence_answer(image, question, lang)
|
| 36 |
else:
|
| 37 |
cap, ans, exp = "Unknown", "Invalid", "Invalid"
|
| 38 |
|
models/blip_yolo_model.py
CHANGED
|
@@ -1,15 +1,24 @@
|
|
| 1 |
import torch
|
|
|
|
| 2 |
from models.blip_model import processor_cap, model_cap, processor_vqa, model_vqa
|
| 3 |
from config import DEVICE
|
| 4 |
|
| 5 |
try:
|
| 6 |
from ultralytics import YOLO
|
| 7 |
-
# Setting verbose=False to minimize logs
|
| 8 |
yolo_model = YOLO("yolov8n.pt")
|
| 9 |
except Exception as e:
|
| 10 |
print("YOLO initialization failed:", e)
|
| 11 |
yolo_model = None
|
| 12 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 13 |
def blip_yolo_answer(image, question, lang="en"):
|
| 14 |
objects_detected = []
|
| 15 |
if yolo_model is not None:
|
|
@@ -21,11 +30,9 @@ def blip_yolo_answer(image, question, lang="en"):
|
|
| 21 |
except:
|
| 22 |
pass
|
| 23 |
|
| 24 |
-
|
| 25 |
-
|
| 26 |
-
|
| 27 |
-
counts = {obj: objects_detected.count(obj) for obj in unique_objs}
|
| 28 |
-
det_str = ", ".join([f"{count} {obj}(s)" for obj, count in counts.items()])
|
| 29 |
|
| 30 |
if image.mode != "RGB":
|
| 31 |
image = image.convert("RGB")
|
|
@@ -35,16 +42,24 @@ def blip_yolo_answer(image, question, lang="en"):
|
|
| 35 |
cap_output = model_cap.generate(**cap_inputs, max_new_tokens=30)
|
| 36 |
caption = processor_cap.decode(cap_output[0], skip_special_tokens=True).strip()
|
| 37 |
|
| 38 |
-
|
| 39 |
-
caption = f"YOLO objects: {det_str}. General scene: {caption}"
|
| 40 |
-
|
| 41 |
-
# Feed YOLO logic directly into BLIP reasoning via question parameter
|
| 42 |
-
context_question = f"Given objects {det_str}, {question}" if objects_detected else question
|
| 43 |
vqa_inputs = processor_vqa(images=image, text=context_question, return_tensors="pt").to(DEVICE)
|
| 44 |
with torch.no_grad():
|
| 45 |
vqa_output = model_vqa.generate(**vqa_inputs, max_new_tokens=20)
|
| 46 |
answer = processor_vqa.decode(vqa_output[0], skip_special_tokens=True).strip()
|
| 47 |
|
| 48 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 49 |
|
| 50 |
return caption, answer, explanation
|
|
|
|
| 1 |
import torch
|
| 2 |
+
from transformers import T5Tokenizer, T5ForConditionalGeneration
|
| 3 |
from models.blip_model import processor_cap, model_cap, processor_vqa, model_vqa
|
| 4 |
from config import DEVICE
|
| 5 |
|
| 6 |
try:
|
| 7 |
from ultralytics import YOLO
|
|
|
|
| 8 |
yolo_model = YOLO("yolov8n.pt")
|
| 9 |
except Exception as e:
|
| 10 |
print("YOLO initialization failed:", e)
|
| 11 |
yolo_model = None
|
| 12 |
|
| 13 |
+
# Initialize FLAN-T5 specifically to synthesize intelligent explanations locally
|
| 14 |
+
try:
|
| 15 |
+
tokenizer_t5 = T5Tokenizer.from_pretrained("google/flan-t5-base")
|
| 16 |
+
model_t5 = T5ForConditionalGeneration.from_pretrained("google/flan-t5-base").to(DEVICE)
|
| 17 |
+
model_t5.eval()
|
| 18 |
+
except Exception as e:
|
| 19 |
+
tokenizer_t5 = None
|
| 20 |
+
model_t5 = None
|
| 21 |
+
|
| 22 |
def blip_yolo_answer(image, question, lang="en"):
|
| 23 |
objects_detected = []
|
| 24 |
if yolo_model is not None:
|
|
|
|
| 30 |
except:
|
| 31 |
pass
|
| 32 |
|
| 33 |
+
unique_objs = list(set(objects_detected))
|
| 34 |
+
counts = {obj: objects_detected.count(obj) for obj in unique_objs}
|
| 35 |
+
det_str = ", ".join([f"{count} {obj}(s)" for obj, count in counts.items()]) if objects_detected else "no distinct YOLO objects"
|
|
|
|
|
|
|
| 36 |
|
| 37 |
if image.mode != "RGB":
|
| 38 |
image = image.convert("RGB")
|
|
|
|
| 42 |
cap_output = model_cap.generate(**cap_inputs, max_new_tokens=30)
|
| 43 |
caption = processor_cap.decode(cap_output[0], skip_special_tokens=True).strip()
|
| 44 |
|
| 45 |
+
context_question = f"The image features {caption} and {det_str}. {question}"
|
|
|
|
|
|
|
|
|
|
|
|
|
| 46 |
vqa_inputs = processor_vqa(images=image, text=context_question, return_tensors="pt").to(DEVICE)
|
| 47 |
with torch.no_grad():
|
| 48 |
vqa_output = model_vqa.generate(**vqa_inputs, max_new_tokens=20)
|
| 49 |
answer = processor_vqa.decode(vqa_output[0], skip_special_tokens=True).strip()
|
| 50 |
|
| 51 |
+
if model_t5 is not None:
|
| 52 |
+
prompt = f"Explain logically why the answer to the question '{question}' is '{answer}', knowing the scene is {caption} and precisely contains {det_str}. Be concise and logical."
|
| 53 |
+
input_ids = tokenizer_t5(prompt, return_tensors="pt").input_ids.to(DEVICE)
|
| 54 |
+
with torch.no_grad():
|
| 55 |
+
out = model_t5.generate(input_ids, max_new_tokens=50)
|
| 56 |
+
explanation = tokenizer_t5.decode(out[0], skip_special_tokens=True)
|
| 57 |
+
# Capitalize first letter strictly
|
| 58 |
+
explanation = explanation[0].upper() + explanation[1:] if len(explanation) > 0 else explanation
|
| 59 |
+
else:
|
| 60 |
+
explanation = f"The visual structure shows {caption} alongside {det_str}, directly leading to the conclusion that the answer is {answer}."
|
| 61 |
+
|
| 62 |
+
if objects_detected:
|
| 63 |
+
caption = f"YOLO found: {det_str}. Scene: {caption}"
|
| 64 |
|
| 65 |
return caption, answer, explanation
|
models/florence_fusion_model.py
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import torch
|
| 2 |
+
from transformers import AutoProcessor, AutoModelForCausalLM
|
| 3 |
+
from config import DEVICE
|
| 4 |
+
import gc
|
| 5 |
+
|
| 6 |
+
processor_florence = None
|
| 7 |
+
model_florence = None
|
| 8 |
+
|
| 9 |
+
def load_florence():
|
| 10 |
+
global processor_florence, model_florence
|
| 11 |
+
if model_florence is not None:
|
| 12 |
+
return
|
| 13 |
+
try:
|
| 14 |
+
processor_florence = AutoProcessor.from_pretrained("microsoft/Florence-2-base-ft", trust_remote_code=True)
|
| 15 |
+
model_florence = AutoModelForCausalLM.from_pretrained("microsoft/Florence-2-base-ft", trust_remote_code=True).to(DEVICE)
|
| 16 |
+
model_florence.eval()
|
| 17 |
+
except Exception as e:
|
| 18 |
+
print("Florence-2 failed to load:", e)
|
| 19 |
+
|
| 20 |
+
def florence_answer(image, question, lang="en"):
|
| 21 |
+
load_florence()
|
| 22 |
+
if model_florence is None:
|
| 23 |
+
return "Server Error", "Florence-2 Not Loaded", "The model ran out of memory or failed to load on the Hugging Face space."
|
| 24 |
+
|
| 25 |
+
if image.mode != "RGB":
|
| 26 |
+
image = image.convert("RGB")
|
| 27 |
+
|
| 28 |
+
def run_task(task_prompt, text_input=None):
|
| 29 |
+
prompt = task_prompt if text_input is None else task_prompt + text_input
|
| 30 |
+
inputs = processor_florence(text=prompt, images=image, return_tensors="pt").to(DEVICE)
|
| 31 |
+
with torch.no_grad():
|
| 32 |
+
generated_ids = model_florence.generate(
|
| 33 |
+
input_ids=inputs["input_ids"],
|
| 34 |
+
pixel_values=inputs["pixel_values"],
|
| 35 |
+
max_new_tokens=100,
|
| 36 |
+
num_beams=3
|
| 37 |
+
)
|
| 38 |
+
generated_text = processor_florence.batch_decode(generated_ids, skip_special_tokens=False)[0]
|
| 39 |
+
parsed = processor_florence.post_process_generation(generated_text, task=task_prompt, image_size=(image.width, image.height))
|
| 40 |
+
return parsed[task_prompt]
|
| 41 |
+
|
| 42 |
+
try:
|
| 43 |
+
# Step 1: Detailed Captioning
|
| 44 |
+
caption = run_task("<MORE_DETAILED_CAPTION>")
|
| 45 |
+
|
| 46 |
+
# Step 2: VQA Answering
|
| 47 |
+
answer = run_task("<VQA>", question)
|
| 48 |
+
|
| 49 |
+
# Step 3: Explanation Logic
|
| 50 |
+
# Florence-2 dense captions act as a perfect contextual reasoning base
|
| 51 |
+
explanation = f"Florence-2 performed a massive deep visual scan, identifying: '{caption}'. Because of these precise extracted contextual elements, the model deduced the answer is {answer}."
|
| 52 |
+
|
| 53 |
+
return caption, answer, explanation
|
| 54 |
+
except Exception as e:
|
| 55 |
+
return "Florence API Error", "Execution Crashed", repr(e)
|
static/app.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
| 2 |
let appState = {
|
| 3 |
username: '',
|
| 4 |
logs: [],
|
| 5 |
-
stats: { total: 0, local: 0, yolo: 0, gemini: 0, groq: 0, hf_boss: 0 },
|
| 6 |
geminiEnabled: true
|
| 7 |
};
|
| 8 |
|
|
@@ -358,6 +358,7 @@ async function fetchLogs() {
|
|
| 358 |
appState.stats.gemini = appState.logs.filter(l => ['gemini', 'external'].includes(l.model.toLowerCase())).length;
|
| 359 |
appState.stats.groq = appState.logs.filter(l => l.model.toLowerCase().includes('groq')).length;
|
| 360 |
appState.stats.hf_boss = appState.logs.filter(l => l.model.toLowerCase().includes('hf_boss') || l.model.toLowerCase().includes('boss')).length;
|
|
|
|
| 361 |
|
| 362 |
updateDashboardView();
|
| 363 |
} catch(err) {
|
|
@@ -411,12 +412,12 @@ function initChart() {
|
|
| 411 |
|
| 412 |
function getChartData() {
|
| 413 |
return {
|
| 414 |
-
labels: ['Local BLIP', 'Local YOLO', 'Gemini AI', 'Groq Vision', 'HF
|
| 415 |
datasets: [{
|
| 416 |
label: 'Queries',
|
| 417 |
-
data: [appState.stats.local, appState.stats.yolo, appState.stats.gemini, appState.stats.groq, appState.stats.hf_boss],
|
| 418 |
-
backgroundColor: ['rgba(16, 185, 129, 0.6)', 'rgba(52, 211, 153, 0.6)', 'rgba(99, 102, 241, 0.6)', 'rgba(245, 158, 11, 0.6)', 'rgba(139, 92, 246, 0.6)'],
|
| 419 |
-
borderColor: ['rgba(16, 185, 129, 1)', 'rgba(52, 211, 153, 1)', 'rgba(99, 102, 241, 1)', 'rgba(245, 158, 11, 1)', 'rgba(139, 92, 246, 1)'],
|
| 420 |
borderWidth: 1, borderRadius: 6
|
| 421 |
}]
|
| 422 |
};
|
|
|
|
| 2 |
let appState = {
|
| 3 |
username: '',
|
| 4 |
logs: [],
|
| 5 |
+
stats: { total: 0, local: 0, yolo: 0, gemini: 0, groq: 0, hf_boss: 0, florence: 0 },
|
| 6 |
geminiEnabled: true
|
| 7 |
};
|
| 8 |
|
|
|
|
| 358 |
appState.stats.gemini = appState.logs.filter(l => ['gemini', 'external'].includes(l.model.toLowerCase())).length;
|
| 359 |
appState.stats.groq = appState.logs.filter(l => l.model.toLowerCase().includes('groq')).length;
|
| 360 |
appState.stats.hf_boss = appState.logs.filter(l => l.model.toLowerCase().includes('hf_boss') || l.model.toLowerCase().includes('boss')).length;
|
| 361 |
+
appState.stats.florence = appState.logs.filter(l => l.model.toLowerCase().includes('florence') || l.model.toLowerCase().includes('fusion')).length;
|
| 362 |
|
| 363 |
updateDashboardView();
|
| 364 |
} catch(err) {
|
|
|
|
| 412 |
|
| 413 |
function getChartData() {
|
| 414 |
return {
|
| 415 |
+
labels: ['Local BLIP', 'Local YOLO', 'Gemini AI', 'Groq Vision', 'HF API', 'Florence Fusion'],
|
| 416 |
datasets: [{
|
| 417 |
label: 'Queries',
|
| 418 |
+
data: [appState.stats.local, appState.stats.yolo, appState.stats.gemini, appState.stats.groq, appState.stats.hf_boss, appState.stats.florence],
|
| 419 |
+
backgroundColor: ['rgba(16, 185, 129, 0.6)', 'rgba(52, 211, 153, 0.6)', 'rgba(99, 102, 241, 0.6)', 'rgba(245, 158, 11, 0.6)', 'rgba(139, 92, 246, 0.6)', 'rgba(236, 72, 153, 0.6)'],
|
| 420 |
+
borderColor: ['rgba(16, 185, 129, 1)', 'rgba(52, 211, 153, 1)', 'rgba(99, 102, 241, 1)', 'rgba(245, 158, 11, 1)', 'rgba(139, 92, 246, 1)', 'rgba(236, 72, 153, 1)'],
|
| 421 |
borderWidth: 1, borderRadius: 6
|
| 422 |
}]
|
| 423 |
};
|
templates/index.html
CHANGED
|
@@ -100,6 +100,7 @@
|
|
| 100 |
<option value="gemini" id="gemini-option">Gemini 3.1 Flash Vision (High Quota)</option>
|
| 101 |
<option value="groq" id="groq-option">Groq Native Vision: Llama 4 Scout</option>
|
| 102 |
<option value="hf_boss" id="hf-option">Hugging Face API: Llama 11B Vision (Unlimited Boss)</option>
|
|
|
|
| 103 |
</select>
|
| 104 |
</div>
|
| 105 |
<div class="control-group" style="flex: 1;">
|
|
|
|
| 100 |
<option value="gemini" id="gemini-option">Gemini 3.1 Flash Vision (High Quota)</option>
|
| 101 |
<option value="groq" id="groq-option">Groq Native Vision: Llama 4 Scout</option>
|
| 102 |
<option value="hf_boss" id="hf-option">Hugging Face API: Llama 11B Vision (Unlimited Boss)</option>
|
| 103 |
+
<option value="florence" id="florence-option">Florence-2 Fusion (Local Ultimate)</option>
|
| 104 |
</select>
|
| 105 |
</div>
|
| 106 |
<div class="control-group" style="flex: 1;">
|