feat: initial CamWatch service
Polls Frigate events API, extracts dominant vehicle color (KMeans), reads LPR plate from Frigate sub_label. Responsive dark UI with filter by plate/camera/date and auto-refresh. FastAPI + SQLite. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,60 @@
|
||||
import cv2
|
||||
import numpy as np
|
||||
import colorsys
|
||||
|
||||
|
||||
def extract_dominant_color(image_path: str, bbox: dict | None = None) -> tuple[str, str]:
|
||||
"""Returns (hex_color, color_name) from image, optionally cropped to bbox."""
|
||||
img = cv2.imread(image_path)
|
||||
if img is None:
|
||||
return "#808080", "Inconnu"
|
||||
|
||||
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"
|
||||
Reference in New Issue
Block a user