| import time |
| import hashlib |
|
|
| import spaces |
| import torch |
| from PIL import Image |
| from transformers import BlipProcessor, BlipForConditionalGeneration |
|
|
| MODEL_VARIANTS = { |
| "base": "Salesforce/blip-image-captioning-base", |
| "large": "Salesforce/blip-image-captioning-large", |
| } |
|
|
|
|
| _loaded_models = {} |
| caption_cache = {} |
|
|
|
|
| def _load_model(model_variant): |
| """Load a BLIP variant once (on CPU) and reuse it on later requests.""" |
| if model_variant not in MODEL_VARIANTS: |
| raise ValueError(f"model_variant must be one of {list(MODEL_VARIANTS)}") |
| model_name = MODEL_VARIANTS[model_variant] |
|
|
| start_load = time.time() |
| if model_name not in _loaded_models: |
| processor = BlipProcessor.from_pretrained(model_name) |
| model = BlipForConditionalGeneration.from_pretrained(model_name) |
| model.eval() |
| _loaded_models[model_name] = (processor, model) |
| load_time = time.time() - start_load |
|
|
| processor, model = _loaded_models[model_name] |
| return processor, model, load_time, model_name |
|
|
|
|
| @spaces.GPU |
| def run_inference(model, inputs): |
| device = "cuda" if torch.cuda.is_available() else "cpu" |
| model = model.to(device) |
| inputs = {k: v.to(device) for k, v in inputs.items()} |
| with torch.no_grad(): |
| output = model.generate(**inputs) |
| return output.to("cpu") |
|
|
|
|
| def _cache_key(model_variant, image): |
| |
| |
| digest = hashlib.sha256(image.tobytes()).hexdigest() |
| return f"{model_variant}:{digest}" |
|
|
|
|
| def generate_caption(image, model_variant="base", use_cache=True): |
| start_total = time.time() |
|
|
| |
| start_preprocess = time.time() |
| if not isinstance(image, Image.Image): |
| image = Image.fromarray(image) |
| image = image.convert("RGB") |
| key = _cache_key(model_variant, image) |
| preprocess_time = time.time() - start_preprocess |
|
|
| |
| if use_cache and key in caption_cache: |
| cached = caption_cache[key].copy() |
| cached["cache_hit"] = True |
| cached["total_time"] = round(time.time() - start_total, 3) |
| cached["preprocess_time"] = round(preprocess_time, 3) |
| cached["model_load_time"] = 0.0 |
| cached["inference_time"] = 0.0 |
| cached["postprocess_time"] = 0.0 |
| cached["approx_gpu_time"] = 0.0 |
| return cached |
|
|
| |
| processor, model, load_time, model_name = _load_model(model_variant) |
| inputs = processor(image, return_tensors="pt") |
|
|
| start_inference = time.time() |
| output = run_inference(model, inputs) |
| inference_time = time.time() - start_inference |
|
|
| |
| start_postprocess = time.time() |
| caption = processor.decode(output[0], skip_special_tokens=True) |
| postprocess_time = time.time() - start_postprocess |
|
|
| total_time = time.time() - start_total |
|
|
| result = { |
| "caption": caption, |
| "total_time": round(total_time, 3), |
| "preprocess_time": round(preprocess_time, 3), |
| "model_load_time": round(load_time, 3), |
| "inference_time": round(inference_time, 3), |
| "postprocess_time": round(postprocess_time, 3), |
| |
| |
| |
| |
| "approx_gpu_time": round(inference_time, 3), |
| "cache_hit": False, |
| "model_variant": model_variant, |
| "model": model_name, |
| "use_cache": use_cache, |
| } |
|
|
| if use_cache: |
| caption_cache[key] = result.copy() |
|
|
| return result |
|
|
| _load_model("base") |