| """ |
| Streamlit app for Image Captioning using BLIP-2 (or fallback BLIP). |
| Usage: |
| - Set environment variable HUGGINGFACE_HUB_TOKEN if you need to access gated models. |
| - On a machine without GPU memory, choose the smaller fallback model in the UI. |
| Notes: |
| - BLIP-2 large variants require GPU and potentially accelerate/device_map configuration. |
| - If you hit OOM errors, switch to "Salesforce/blip-image-captioning-base". |
| """ |
|
|
| import io |
| import os |
| import streamlit as st |
| from PIL import Image |
| import torch |
|
|
| |
| st.set_page_config(page_title="BLIP-2 Image Captioner", layout="centered") |
|
|
| |
| st.title("🖼️ Image Captioner — BLIP-2 / BLIP") |
| st.markdown( |
| "Upload an image and get an automatic caption generated by a BLIP/BLIP-2 model. " |
| "⚠️ Large BLIP-2 models require GPU memory — use the smaller fallback if needed." |
| ) |
|
|
| col1, col2 = st.columns([3, 1]) |
| with col2: |
| st.write("") |
| model_choice = st.selectbox( |
| "Model", |
| options=[ |
| "Salesforce/blip2-opt-2.7b (BLIP-2, large, needs GPU)", |
| "Salesforce/blip2-vicuna-7b-instruct (example; large)", |
| "Salesforce/blip-image-captioning-base (BLIP fallback - small)" |
| ], |
| index=2 |
| ) |
|
|
| with col1: |
| uploaded_file = st.file_uploader("Upload an image", type=["png", "jpg", "jpeg"]) |
| st.markdown("Or try the example image below:") |
| if st.button("Use example image"): |
| |
| example_path = "example.jpg" |
| |
| img = Image.new("RGB", (512, 384), color=(200, 200, 220)) |
| st.session_state["_example_image"] = img |
| uploaded_file = None |
| uploaded_image = img |
| else: |
| uploaded_image = None |
|
|
| prompt = st.text_input("Optional instruction/prompt (e.g. 'Describe briefly')", value="") |
|
|
| st.markdown("---") |
|
|
| |
| @st.cache_resource(ttl=3600) |
| def load_caption_pipeline(model_id: str): |
| """Load the image-to-text pipeline from transformers. |
| Note: large BLIP-2 variants require GPU and may need accelerate/device_map settings. |
| """ |
| try: |
| from transformers import pipeline |
| except Exception as e: |
| raise RuntimeError("transformers is required. Install via requirements.txt") from e |
|
|
| |
| if torch.cuda.is_available(): |
| device = 0 |
| else: |
| device = -1 |
|
|
| |
| |
| pipe = pipeline("image-to-text", model=model_id, device=device) |
| return pipe |
|
|
| |
| def generate_caption(image: Image.Image, model_choice_str: str, prompt_text: str): |
| |
| if "blip-image-captioning-base" in model_choice_str: |
| model_id = "Salesforce/blip-image-captioning-base" |
| elif "blip2-opt-2.7b" in model_choice_str: |
| model_id = "Salesforce/blip2-opt-2.7b" |
| else: |
| |
| model_id = "Salesforce/blip-image-captioning-base" |
|
|
| st.info(f"Loading model: `{model_id}` — this may take a moment (and may need GPU).") |
| try: |
| pipe = load_caption_pipeline(model_id) |
| except Exception as e: |
| st.error(f"Failed to load model: {e}") |
| st.stop() |
|
|
| with st.spinner("Generating caption..."): |
| |
| input_for_pipe = image |
| if prompt_text: |
| |
| out = pipe(input_for_pipe, prompt=prompt_text, max_new_tokens=64) |
| else: |
| out = pipe(input_for_pipe, max_new_tokens=64) |
|
|
| |
| if isinstance(out, list) and len(out) > 0: |
| |
| caption = out[0].get("generated_text") or out[0].get("caption") or str(out[0]) |
| else: |
| caption = str(out) |
|
|
| return caption |
|
|
| |
| if uploaded_file is not None: |
| try: |
| image = Image.open(uploaded_file).convert("RGB") |
| except Exception as e: |
| st.error(f"Could not open image: {e}") |
| st.stop() |
| st.image(image, caption="Uploaded image", use_column_width=True) |
| if st.button("Generate caption"): |
| caption = generate_caption(image, model_choice, prompt) |
| st.success("Caption generated!") |
| st.markdown("**Caption**") |
| st.write(caption) |
| |
| st.download_button("Download caption (.txt)", data=caption, file_name="caption.txt", mime="text/plain") |
| st.button("Copy to clipboard") |
| else: |
| if "_example_image" in st.session_state: |
| img = st.session_state["_example_image"] |
| st.image(img, caption="Example image", use_column_width=True) |
| if st.button("Generate caption from example"): |
| caption = generate_caption(img, model_choice, prompt) |
| st.success("Caption generated!") |
| st.write(caption) |
| st.download_button("Download caption (.txt)", data=caption, file_name="caption.txt", mime="text/plain") |
| else: |
| st.info("Upload an image above or click 'Use example image'.") |
|
|
| |
| st.markdown("---") |
| st.markdown( |
| "Tips:\n" |
| "- If you get an out-of-memory error with BLIP-2 large variants, switch to the smaller `Salesforce/blip-image-captioning-base` model.\n" |
| "- To access private or gated models, set `HUGGINGFACE_HUB_TOKEN` as an environment variable before launching this app (or log in to `huggingface-cli`).\n" |
| ) |
|
|