| import time |
| import hashlib |
|
|
| import spaces |
| import torch |
| from PIL import Image |
| from transformers import BlipProcessor, BlipForConditionalGeneration |
|
|
| MODEL_NAME = "Salesforce/blip-image-captioning-base" |
|
|
| |
| |
| |
| processor = None |
| model = None |
|
|
| caption_cache = {} |
|
|
|
|
| def load_model(): |
| """Load the model on first use (CPU, main process) and reuse it after.""" |
| global processor, model |
| start_load = time.time() |
| if processor is None or model is None: |
| processor = BlipProcessor.from_pretrained(MODEL_NAME) |
| model = BlipForConditionalGeneration.from_pretrained(MODEL_NAME) |
| model.eval() |
| load_time = time.time() - start_load |
| return load_time |
|
|
|
|
| @spaces.GPU |
| def run_inference(model, inputs): |
| """The ONLY function that touches the GPU.""" |
| device = "cuda" if torch.cuda.is_available() else "cpu" |
| model = model.to(device) |
| inputs = {k: v.to(device) for k, v in inputs.items()} |
| print("inference device:", next(model.parameters()).device) |
| with torch.no_grad(): |
| output = model.generate(**inputs) |
| return output.to("cpu") |
|
|
|
|
| def _cache_key(image): |
| return hashlib.sha256(image.tobytes()).hexdigest() |
|
|
|
|
| def generate_caption(image, 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(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 |
|
|
| |
| load_time = load_model() |
| 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": MODEL_NAME, |
| } |
|
|
| if use_cache: |
| caption_cache[key] = result.copy() |
|
|
| return result |