- 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>
72 lines
2.1 KiB
Python
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"
|