verity / vision_classifier.py
leash06's picture
Update vision_classifier.py
479381a verified
Raw History Blame Contribute Delete
3.31 kB
"""
vision_classifier.py
====================
Classification locale d'images ET videos avec CLIP (zero-shot).
Le modele est charge une seule fois au demarrage du module.
Supporte les images (jpg, png...) et les videos (mp4, mov...) en extrayant
la meilleure frame.
"""
import os
import spaces
import cv2
import numpy as np
from transformers import pipeline
from PIL import Image
CATEGORIES = [
"trash and garbage pile",
"deforested area needing reforestation",
"dirty urban street",
"damaged public infrastructure",
"fire or smoke",
"flood or water damage",
"normal safe scene",
]
print("[vision_classifier] Chargement du modele CLIP (premier lancement ~30s)...")
_classifier = pipeline(
"zero-shot-image-classification",
model="openai/clip-vit-base-patch32",
device=-1,
)
print("[vision_classifier] Modele CLIP pret.")
def _est_une_video(file_path: str) -> bool:
video_exts = {".mp4", ".mov", ".avi", ".mkv", ".webm", ".flv", ".wmv"}
ext = os.path.splitext(file_path)[1].lower()
return ext in video_exts
def _extraire_frame_video(video_path: str, nb_frames: int = 5) -> Image.Image:
if not os.path.exists(video_path):
raise ValueError(f"Fichier video introuvable : {video_path}")
cap = cv2.VideoCapture(video_path)
if not cap.isOpened():
raise ValueError(
f"Impossible d'ouvrir la video : {os.path.basename(video_path)}. "
"Le fichier est peut-etre corrompu ou dans un format non supporte."
)
try:
total_frames = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))
fps = cap.get(cv2.CAP_PROP_FPS)
duree = total_frames / fps if fps > 0 else 0
if total_frames == 0:
raise ValueError("Video vide (0 frames)")
if duree < 0.1:
raise ValueError(f"Video trop courte ({duree:.2f}s)")
indices = np.linspace(0, total_frames - 1, nb_frames, dtype=int)
frames = []
for idx in indices:
cap.set(cv2.CAP_PROP_POS_FRAMES, int(idx))
ret, frame = cap.read()
if ret and frame is not None:
if np.mean(frame) > 5:
frame_rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
pil_img = Image.fromarray(frame_rgb)
frames.append(pil_img)
if not frames:
raise ValueError(
"Aucune frame exploitable dans la video. "
"La video est peut-etre toute noire ou corrompue."
)
best = max(frames, key=lambda img: np.var(np.array(img)))
return best
finally:
cap.release()
@spaces.GPU(duration=15)
def classify_image(image_path: str) -> dict:
if _est_une_video(image_path):
image = _extraire_frame_video(image_path)
source_type = "video"
else:
image = Image.open(image_path).convert("RGB")
source_type = "image"
result = _classifier(image, candidate_labels=CATEGORIES)
meilleure = result[0]
alternatives = [
{"classe": r["label"], "confiance": round(r["score"], 4)}
for r in result[1:4]
]
return {
"classe": meilleure["label"],
"confiance": round(meilleure["score"], 4),
"alternatives": alternatives,
"source_type": source_type,
}