| """Visualization utilities for face detection results.""" |
|
|
| import numpy as np |
| import cv2 |
| from typing import List, Optional, Tuple |
|
|
|
|
| def draw_detections(image: np.ndarray, boxes: np.ndarray, |
| scores: Optional[np.ndarray] = None, |
| track_ids: Optional[np.ndarray] = None, |
| landmarks: Optional[np.ndarray] = None, |
| color: Tuple[int, int, int] = (0, 255, 0), |
| thickness: int = 2) -> np.ndarray: |
| """Draw face detections on image.""" |
| vis = image.copy() |
|
|
| for i in range(len(boxes)): |
| x1, y1, x2, y2 = boxes[i].astype(int) |
| cv2.rectangle(vis, (x1, y1), (x2, y2), color, thickness) |
|
|
| label_parts = [] |
| if track_ids is not None: |
| label_parts.append(f"ID:{track_ids[i]}") |
| if scores is not None: |
| label_parts.append(f"{scores[i]:.2f}") |
| label = ' '.join(label_parts) |
|
|
| if label: |
| (tw, th), _ = cv2.getTextSize(label, cv2.FONT_HERSHEY_SIMPLEX, 0.5, 1) |
| cv2.rectangle(vis, (x1, y1 - th - 4), (x1 + tw, y1), color, -1) |
| cv2.putText(vis, label, (x1, y1 - 2), |
| cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 0, 0), 1) |
|
|
| if landmarks is not None and i < len(landmarks): |
| lmk = landmarks[i] |
| colors_lmk = [(0, 0, 255), (0, 255, 255), (0, 255, 0), |
| (255, 255, 0), (255, 0, 0)] |
| for j in range(5): |
| x, y = int(lmk[j*2]), int(lmk[j*2+1]) |
| if x > 0 and y > 0: |
| cv2.circle(vis, (x, y), 3, colors_lmk[j], -1) |
|
|
| return vis |
|
|
|
|
| def create_comparison_grid(images: List[np.ndarray], titles: List[str], |
| cols: int = 3, cell_size: Tuple[int, int] = (400, 400) |
| ) -> np.ndarray: |
| """Create a grid of images with titles for comparison.""" |
| rows = (len(images) + cols - 1) // cols |
| grid = np.zeros((rows * cell_size[1], cols * cell_size[0], 3), dtype=np.uint8) |
|
|
| for idx, (img, title) in enumerate(zip(images, titles)): |
| r, c = idx // cols, idx % cols |
| resized = cv2.resize(img, cell_size) |
| y1, y2 = r * cell_size[1], (r + 1) * cell_size[1] |
| x1, x2 = c * cell_size[0], (c + 1) * cell_size[0] |
| grid[y1:y2, x1:x2] = resized |
| cv2.putText(grid, title, (x1 + 5, y1 + 20), |
| cv2.FONT_HERSHEY_SIMPLEX, 0.6, (255, 255, 255), 2) |
|
|
| return grid |
|
|