Spaces:
Sleeping
Sleeping
update app.py
Browse files
app.py
CHANGED
|
@@ -16,7 +16,7 @@ from sklearn.preprocessing import normalize
|
|
| 16 |
st.set_page_config(
|
| 17 |
page_title="Image Captioning Refinement Fusion System",
|
| 18 |
layout="wide",
|
| 19 |
-
|
| 20 |
)
|
| 21 |
|
| 22 |
JINA_KEY = os.environ.get("JINA_KEY", "")
|
|
@@ -87,20 +87,6 @@ def image_to_data_uri(image: Image.Image) -> str:
|
|
| 87 |
b64 = base64.b64encode(raw).decode()
|
| 88 |
return f"data:image/jpeg;base64,{b64}"
|
| 89 |
|
| 90 |
-
# ============================================================================
|
| 91 |
-
# STEP 1 β FLORENCE-2-LARGE: 5 DISTINCT CAPTION APPROACHES
|
| 92 |
-
#
|
| 93 |
-
# Cap 1: <CAPTION> greedy
|
| 94 |
-
# β single concise sentence, primary subject only
|
| 95 |
-
# Cap 2: <CAPTION> sampling temp=1.0
|
| 96 |
-
# β alt-text accessibility style, concise but different phrasing
|
| 97 |
-
# Cap 3: <DETAILED_CAPTION> temp=0.7
|
| 98 |
-
# β paragraph describing foreground, background, colors
|
| 99 |
-
# Cap 4: <DETAILED_CAPTION> temp=1.1
|
| 100 |
-
# β focuses on mood, atmosphere, implied action
|
| 101 |
-
# Cap 5: <MORE_DETAILED_CAPTION> temp=0.8
|
| 102 |
-
# β exhaustive breakdown of every visible element
|
| 103 |
-
# ============================================================================
|
| 104 |
def generate_captions_florence(image: Image.Image, florence_proc, florence_mod) -> list:
|
| 105 |
|
| 106 |
captions = []
|
|
@@ -246,25 +232,25 @@ def majority_voting(captions, itm, jina, cosine) -> tuple:
|
|
| 246 |
def fuse_captions(cap1: str, cap2: str, qwen_tok, qwen_mod) -> str:
|
| 247 |
|
| 248 |
system_prompt = (
|
| 249 |
-
"You write image captions. "
|
| 250 |
"You will receive two captions of the same image. "
|
| 251 |
-
"
|
| 252 |
-
"Include ALL
|
| 253 |
-
"
|
| 254 |
"what each person looks like and what they are doing, "
|
| 255 |
-
"
|
| 256 |
-
"and the
|
| 257 |
-
"Write
|
| 258 |
-
"
|
| 259 |
-
"
|
| 260 |
"Return ONLY the caption, nothing else."
|
| 261 |
)
|
| 262 |
|
| 263 |
user_prompt = (
|
| 264 |
f"Caption A: {cap1}\n"
|
| 265 |
f"Caption B: {cap2}\n\n"
|
| 266 |
-
"Write a detailed caption
|
| 267 |
-
"people, objects and background
|
| 268 |
)
|
| 269 |
|
| 270 |
try:
|
|
@@ -282,8 +268,11 @@ def fuse_captions(cap1: str, cap2: str, qwen_tok, qwen_mod) -> str:
|
|
| 282 |
with torch.no_grad():
|
| 283 |
generated_ids = qwen_mod.generate(
|
| 284 |
**model_inputs,
|
| 285 |
-
max_new_tokens=
|
| 286 |
-
do_sample=False
|
|
|
|
|
|
|
|
|
|
| 287 |
)
|
| 288 |
|
| 289 |
output_ids = generated_ids[0][len(model_inputs.input_ids[0]):]
|
|
@@ -293,6 +282,16 @@ def fuse_captions(cap1: str, cap2: str, qwen_tok, qwen_mod) -> str:
|
|
| 293 |
if fused.lower().startswith(prefix.lower()):
|
| 294 |
fused = fused[len(prefix):].strip()
|
| 295 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 296 |
return fused if fused else cap1
|
| 297 |
|
| 298 |
except Exception as e:
|
|
@@ -340,10 +339,6 @@ def compute_caption_quality(image, final_caption, blip_proc, blip_itm) -> tuple:
|
|
| 340 |
avg_score = round((itm_score + cosine_score) / 2, 4)
|
| 341 |
return avg_score, round(itm_score, 4), round(cosine_score, 4)
|
| 342 |
|
| 343 |
-
# ============================================================================
|
| 344 |
-
# GAUGE β updated to match reference style
|
| 345 |
-
# Bright saturated zone colors, sharp black needle, clean arc, no dark shades
|
| 346 |
-
# ============================================================================
|
| 347 |
def render_gauge(score, itm, cosine, placeholder):
|
| 348 |
|
| 349 |
if score >= 0.75:
|
|
@@ -421,9 +416,7 @@ def render_gauge(score, itm, cosine, placeholder):
|
|
| 421 |
unsafe_allow_html=True
|
| 422 |
)
|
| 423 |
|
| 424 |
-
#
|
| 425 |
-
# SIDEBAR β pipeline steps + live accuracy section (session_state)
|
| 426 |
-
# ============================================================================
|
| 427 |
with st.sidebar:
|
| 428 |
st.title("Image Captioning Refinement Fusion")
|
| 429 |
st.markdown("---")
|
|
@@ -450,8 +443,6 @@ Caption fusion
|
|
| 450 |
st.markdown("---")
|
| 451 |
st.markdown("**Local:** Florence-2, BLIP ITM, Qwen2.5")
|
| 452 |
st.markdown("**API:** Jina")
|
| 453 |
-
|
| 454 |
-
# ββ accuracy section ββββββββββββββ
|
| 455 |
st.markdown("---")
|
| 456 |
st.markdown("### Caption Quality Metrics")
|
| 457 |
st.markdown("""
|
|
@@ -464,9 +455,7 @@ Measures embedding distance
|
|
| 464 |
between image and caption.
|
| 465 |
""")
|
| 466 |
|
| 467 |
-
#
|
| 468 |
-
# MAIN UI
|
| 469 |
-
# ============================================================================
|
| 470 |
st.title("Image Captioning Refinement Fusion System")
|
| 471 |
st.markdown("Upload an image to generate a refined, grounded caption.")
|
| 472 |
st.markdown("---")
|
|
@@ -499,7 +488,9 @@ if uploaded_file is not None:
|
|
| 499 |
status = st.empty()
|
| 500 |
|
| 501 |
status.info("Step 1/6: Generating captions with Florence-2-Large...")
|
| 502 |
-
captions = generate_captions_florence(
|
|
|
|
|
|
|
| 503 |
progress.progress(16)
|
| 504 |
|
| 505 |
with st.expander("5 Generated Captions", expanded=True):
|
|
@@ -507,7 +498,9 @@ if uploaded_file is not None:
|
|
| 507 |
st.write(f"**{i+1}.** {cap}")
|
| 508 |
|
| 509 |
status.info("Step 2/6: Computing BLIP ITM scores...")
|
| 510 |
-
itm_scores = compute_itm_scores(
|
|
|
|
|
|
|
| 511 |
progress.progress(32)
|
| 512 |
|
| 513 |
status.info("Step 3/6: Computing Jina Reranker scores...")
|
|
@@ -515,7 +508,9 @@ if uploaded_file is not None:
|
|
| 515 |
progress.progress(50)
|
| 516 |
|
| 517 |
status.info("Step 4/6: Computing Cosine Similarity scores...")
|
| 518 |
-
cosine_scores = compute_cosine_scores(
|
|
|
|
|
|
|
| 519 |
progress.progress(66)
|
| 520 |
|
| 521 |
scores_df = pd.DataFrame({
|
|
@@ -560,7 +555,6 @@ if uploaded_file is not None:
|
|
| 560 |
input_image, final, blip_proc, blip_itm
|
| 561 |
)
|
| 562 |
|
| 563 |
-
# Store in session_state so sidebar updates on rerender
|
| 564 |
st.session_state.avg_score = avg_score
|
| 565 |
st.session_state.itm_q = itm_q
|
| 566 |
st.session_state.cosine_q = cosine_q
|
|
|
|
| 16 |
st.set_page_config(
|
| 17 |
page_title="Image Captioning Refinement Fusion System",
|
| 18 |
layout="wide",
|
| 19 |
+
initial_sidebar_bar="expanded"
|
| 20 |
)
|
| 21 |
|
| 22 |
JINA_KEY = os.environ.get("JINA_KEY", "")
|
|
|
|
| 87 |
b64 = base64.b64encode(raw).decode()
|
| 88 |
return f"data:image/jpeg;base64,{b64}"
|
| 89 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 90 |
def generate_captions_florence(image: Image.Image, florence_proc, florence_mod) -> list:
|
| 91 |
|
| 92 |
captions = []
|
|
|
|
| 232 |
def fuse_captions(cap1: str, cap2: str, qwen_tok, qwen_mod) -> str:
|
| 233 |
|
| 234 |
system_prompt = (
|
| 235 |
+
"You write detailed image captions. "
|
| 236 |
"You will receive two captions of the same image. "
|
| 237 |
+
"Combine them into one complete, detailed caption. "
|
| 238 |
+
"Include ALL visible details: "
|
| 239 |
+
"clothing colors and style of each person, "
|
| 240 |
"what each person looks like and what they are doing, "
|
| 241 |
+
"objects and surroundings visible in the scene, "
|
| 242 |
+
"and the background or setting. "
|
| 243 |
+
"Write 3 to 4 complete sentences. "
|
| 244 |
+
"Always finish the last sentence properly β never leave it incomplete. "
|
| 245 |
+
"Use simple, clear, everyday words. "
|
| 246 |
"Return ONLY the caption, nothing else."
|
| 247 |
)
|
| 248 |
|
| 249 |
user_prompt = (
|
| 250 |
f"Caption A: {cap1}\n"
|
| 251 |
f"Caption B: {cap2}\n\n"
|
| 252 |
+
"Write a detailed caption covering all clothing, "
|
| 253 |
+
"people, objects and background:"
|
| 254 |
)
|
| 255 |
|
| 256 |
try:
|
|
|
|
| 268 |
with torch.no_grad():
|
| 269 |
generated_ids = qwen_mod.generate(
|
| 270 |
**model_inputs,
|
| 271 |
+
max_new_tokens = 220,
|
| 272 |
+
do_sample = False,
|
| 273 |
+
repetition_penalty = 1.1,
|
| 274 |
+
eos_token_id = qwen_tok.eos_token_id,
|
| 275 |
+
pad_token_id = qwen_tok.eos_token_id
|
| 276 |
)
|
| 277 |
|
| 278 |
output_ids = generated_ids[0][len(model_inputs.input_ids[0]):]
|
|
|
|
| 282 |
if fused.lower().startswith(prefix.lower()):
|
| 283 |
fused = fused[len(prefix):].strip()
|
| 284 |
|
| 285 |
+
# Safety β trim to last complete sentence if still cut off
|
| 286 |
+
if fused and not fused.endswith((".", "!", "?")):
|
| 287 |
+
last_stop = max(
|
| 288 |
+
fused.rfind("."),
|
| 289 |
+
fused.rfind("!"),
|
| 290 |
+
fused.rfind("?")
|
| 291 |
+
)
|
| 292 |
+
if last_stop > len(fused) // 2:
|
| 293 |
+
fused = fused[:last_stop + 1].strip()
|
| 294 |
+
|
| 295 |
return fused if fused else cap1
|
| 296 |
|
| 297 |
except Exception as e:
|
|
|
|
| 339 |
avg_score = round((itm_score + cosine_score) / 2, 4)
|
| 340 |
return avg_score, round(itm_score, 4), round(cosine_score, 4)
|
| 341 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 342 |
def render_gauge(score, itm, cosine, placeholder):
|
| 343 |
|
| 344 |
if score >= 0.75:
|
|
|
|
| 416 |
unsafe_allow_html=True
|
| 417 |
)
|
| 418 |
|
| 419 |
+
# ββ SIDEBAR ββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
|
|
|
|
|
|
| 420 |
with st.sidebar:
|
| 421 |
st.title("Image Captioning Refinement Fusion")
|
| 422 |
st.markdown("---")
|
|
|
|
| 443 |
st.markdown("---")
|
| 444 |
st.markdown("**Local:** Florence-2, BLIP ITM, Qwen2.5")
|
| 445 |
st.markdown("**API:** Jina")
|
|
|
|
|
|
|
| 446 |
st.markdown("---")
|
| 447 |
st.markdown("### Caption Quality Metrics")
|
| 448 |
st.markdown("""
|
|
|
|
| 455 |
between image and caption.
|
| 456 |
""")
|
| 457 |
|
| 458 |
+
# ββ MAIN UI ββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
|
|
|
|
|
|
| 459 |
st.title("Image Captioning Refinement Fusion System")
|
| 460 |
st.markdown("Upload an image to generate a refined, grounded caption.")
|
| 461 |
st.markdown("---")
|
|
|
|
| 488 |
status = st.empty()
|
| 489 |
|
| 490 |
status.info("Step 1/6: Generating captions with Florence-2-Large...")
|
| 491 |
+
captions = generate_captions_florence(
|
| 492 |
+
input_image, florence_proc, florence_mod
|
| 493 |
+
)
|
| 494 |
progress.progress(16)
|
| 495 |
|
| 496 |
with st.expander("5 Generated Captions", expanded=True):
|
|
|
|
| 498 |
st.write(f"**{i+1}.** {cap}")
|
| 499 |
|
| 500 |
status.info("Step 2/6: Computing BLIP ITM scores...")
|
| 501 |
+
itm_scores = compute_itm_scores(
|
| 502 |
+
input_image, captions, blip_proc, blip_itm
|
| 503 |
+
)
|
| 504 |
progress.progress(32)
|
| 505 |
|
| 506 |
status.info("Step 3/6: Computing Jina Reranker scores...")
|
|
|
|
| 508 |
progress.progress(50)
|
| 509 |
|
| 510 |
status.info("Step 4/6: Computing Cosine Similarity scores...")
|
| 511 |
+
cosine_scores = compute_cosine_scores(
|
| 512 |
+
input_image, captions, blip_proc, blip_itm
|
| 513 |
+
)
|
| 514 |
progress.progress(66)
|
| 515 |
|
| 516 |
scores_df = pd.DataFrame({
|
|
|
|
| 555 |
input_image, final, blip_proc, blip_itm
|
| 556 |
)
|
| 557 |
|
|
|
|
| 558 |
st.session_state.avg_score = avg_score
|
| 559 |
st.session_state.itm_q = itm_q
|
| 560 |
st.session_state.cosine_q = cosine_q
|