CarPurchaseAdvisor / src /vision_analyzer.py
ochsncon's picture
Update src/vision_analyzer.py
bcbe62f verified
Raw
History Blame Contribute Delete
4.06 kB
"""Computer Vision analyzer using transfer-learning ResNet-18 for vehicle brand classification.
The module loads a Hugging Face fine-tuned image classification model from
models/car-image-classifier/ and provides vehicle brand predictions.
No damage detection and no technical condition estimation is implemented.
"""
from __future__ import annotations
import json
from functools import lru_cache
from pathlib import Path
from typing import Any
import numpy as np
from PIL import Image
from src.config import MODEL_DIR
def ensure_pil_image(image: Image.Image | np.ndarray | None) -> Image.Image:
"""Convert input to PIL Image."""
if image is None:
raise ValueError("No image provided.")
if isinstance(image, np.ndarray):
return Image.fromarray(image.astype("uint8"))
if not isinstance(image, Image.Image):
raise TypeError("Input is not a valid image.")
return image
@lru_cache(maxsize=1)
def _load_transfer_model() -> dict[str, Any] | None:
"""Load the transfer-learning model from models/car-image-classifier/.
Returns a dict with 'pipeline' and 'metadata' on success, None if model not found.
"""
model_dir = MODEL_DIR / "car-image-classifier"
if not model_dir.exists():
return None
try:
from transformers import pipeline
# Load the image classification pipeline
clf_pipeline = pipeline("image-classification", model=str(model_dir), device=-1)
# Load metadata if it exists
metadata_path = model_dir / "vision_metadata.json"
metadata = {}
if metadata_path.exists():
metadata = json.loads(metadata_path.read_text(encoding="utf-8"))
return {"pipeline": clf_pipeline, "metadata": metadata}
except Exception:
return None
def analyze_car_image(image: Image.Image | np.ndarray | None) -> dict[str, Any]:
"""Analyze uploaded image and return vehicle brand prediction.
Uses a transfer-learning ResNet-18 model to classify vehicle brands.
"""
if image is None:
return {
"predicted_class": "Unknown",
"confidence": 0.0,
"method": "no_image",
"notes": ["No image was provided."],
}
if isinstance(image, np.ndarray):
image = Image.fromarray(image.astype("uint8"))
if not isinstance(image, Image.Image):
return {
"predicted_class": "Unknown",
"confidence": 0.0,
"method": "invalid_input",
"notes": ["Input is not a valid image format."],
}
model_bundle = _load_transfer_model()
if model_bundle is not None:
try:
pipeline = model_bundle["pipeline"]
metadata = model_bundle.get("metadata", {})
# Run inference
pil_image = ensure_pil_image(image)
results = pipeline(pil_image, top_k=1)
if results:
top_result = results[0]
predicted = top_result["label"]
confidence = float(top_result["score"])
return {
"predicted_class": predicted,
"confidence": round(confidence, 3),
"method": "local_transfer_model",
"notes": [
"Vehicle brand classification using transfer learning (ResNet-18).",
"The classifier can only predict one of the trained classes.",
f"Model accuracy on test set: {metadata.get('accuracy', 'n/a')}.",
"No damage detection or technical condition assessment.",
],
}
except Exception:
pass
# Fallback if model not found or inference fails
return {
"predicted_class": "Unknown",
"confidence": 0.0,
"method": "fallback",
"notes": [
"No trained transfer-learning model found.",
"Please train the model using: python -m src.train_vision_model",
],
}