| import logging |
| import asyncio |
| import aiofiles |
| from PIL import Image |
| from transformers import BlipForConditionalGeneration, BlipProcessor |
| from src.utils.asyncHandler import asyncHandler |
| from src.constants import IMAGE_ANALYSIS_MODEL_NAME |
|
|
| |
| BLIP_CAPTION_PREFIX = "a product image showing" |
|
|
|
|
| @asyncHandler |
| async def load_image(image_path: str) -> bytes: |
| async with aiofiles.open(image_path, mode='rb') as f: |
| return await f.read() |
|
|
|
|
| def _run_blip(image_path: str) -> str: |
| """Synchronous BLIP inference — called via run_in_executor to avoid blocking.""" |
| logging.info(f"_run_blip - loading BLIP model: {IMAGE_ANALYSIS_MODEL_NAME}") |
|
|
| processor = BlipProcessor.from_pretrained(IMAGE_ANALYSIS_MODEL_NAME) |
| model = BlipForConditionalGeneration.from_pretrained( |
| IMAGE_ANALYSIS_MODEL_NAME, |
| low_cpu_mem_usage=True, |
| ) |
| logging.info("_run_blip - model and processor loaded") |
|
|
| image = Image.open(image_path).convert("RGB") |
| logging.info(f"_run_blip - image opened: size={image.size}") |
|
|
| |
| inputs = processor(image, text=BLIP_CAPTION_PREFIX, return_tensors="pt") |
| logging.info("_run_blip - running conditional captioning generation") |
|
|
| generated_ids = model.generate( |
| **inputs, |
| max_new_tokens=200, |
| num_beams=4, |
| early_stopping=True, |
| ) |
|
|
| caption = processor.decode(generated_ids[0], skip_special_tokens=True) |
| logging.info(f"_run_blip - caption generated (len={len(caption)}): {caption}") |
| return caption |
|
|
|
|
| @asyncHandler |
| async def analyse_image(image_path: str) -> str: |
| logging.info(f"analyse_image - starting BLIP analysis for: {image_path}") |
| loop = asyncio.get_event_loop() |
| caption = await loop.run_in_executor(None, _run_blip, image_path) |
| logging.info(f"analyse_image - complete. caption length={len(caption)}") |
| return caption |
|
|