Files
camwatch/app/analyzer.py
T
percoandClaude Sonnet 4.6 65b74ce46d Passe de Frigate polling à GetAiState Reolink + LPR ONNX local
- Remplace la source Frigate par l'API GetAiState de la caméra Reolink
- Capture RTSP en temps réel (~20s) quand véhicule détecté, garde la meilleure frame
- LPR avec YOLOv9 (détection plaque) + PaddleOCR v4 (lecture texte) via ONNX
- Modèles partagés avec Frigate (volume local ./models/)
- Cooldown 60s entre événements pour éviter les doublons

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-02 14:49:49 +02:00

72 lines
2.1 KiB
Python

import cv2
import numpy as np
import colorsys
def extract_dominant_color_from_frame(frame: np.ndarray, bbox: dict | None = None) -> tuple[str, str]:
"""Returns (hex_color, color_name) from a BGR numpy frame."""
if frame is None:
return "#808080", "Inconnu"
return _dominant_color(frame, bbox)
def extract_dominant_color(image_path: str, bbox: dict | None = None) -> tuple[str, str]:
"""Returns (hex_color, color_name) from image path, optionally cropped to bbox."""
img = cv2.imread(image_path)
if img is None:
return "#808080", "Inconnu"
return _dominant_color(img, bbox)
def _dominant_color(img: np.ndarray, bbox: dict | None = None) -> tuple[str, str]:
if bbox:
h, w = img.shape[:2]
x1 = max(0, int(bbox.get("x", 0) * w))
y1 = max(0, int(bbox.get("y", 0) * h))
x2 = min(w, int((bbox.get("x", 0) + bbox.get("width", 1)) * w))
y2 = min(h, int((bbox.get("y", 0) + bbox.get("height", 1)) * h))
if x2 > x1 and y2 > y1:
img = img[y1:y2, x1:x2]
small = cv2.resize(img, (60, 60), interpolation=cv2.INTER_AREA)
pixels = small.reshape(-1, 3).astype(np.float32)
k = min(4, len(pixels))
_, labels, centers = cv2.kmeans(
pixels, k, None,
(cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 20, 1.0),
10, cv2.KMEANS_RANDOM_CENTERS
)
counts = np.bincount(labels.flatten())
dominant = centers[np.argmax(counts)]
b, g, r = int(dominant[0]), int(dominant[1]), int(dominant[2])
hex_color = f"#{r:02x}{g:02x}{b:02x}"
return hex_color, _name_color(r, g, b)
def _name_color(r: int, g: int, b: int) -> str:
h, s, v = colorsys.rgb_to_hsv(r / 255, g / 255, b / 255)
if v < 0.18:
return "Noir"
if v > 0.82 and s < 0.18:
return "Blanc"
if s < 0.18:
return "Gris"
hue = h * 360
if hue < 15 or hue >= 345:
return "Rouge"
if hue < 45:
return "Orange"
if hue < 75:
return "Jaune"
if hue < 150:
return "Vert"
if hue < 195:
return "Cyan"
if hue < 255:
return "Bleu"
if hue < 290:
return "Violet"
return "Rose"