File size: 2,731 Bytes
56dd776
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
import cv2
import numpy as np


def get_color(idx):
    """Generate a distinct, bright BGR color based on integer ID using golden ratio HSV palette."""
    idx = int(idx)
    golden_ratio_conjugate = 0.618033988749895
    h = (idx * golden_ratio_conjugate) % 1.0
    s = 0.85
    v = 0.95
    
    # Convert HSV (0..1) to BGR (0..255)
    hsv = np.uint8([[[h * 180, s * 255, v * 255]]])
    bgr = cv2.cvtColor(hsv, cv2.COLOR_HSV2BGR)[0][0]
    return (int(bgr[0]), int(bgr[1]), int(bgr[2]))


def draw_bounding_box(frame, track_id, bbox, confidence=None, text_prefix="ID"):
    """
    Draw a clean, modern bounding box with a filled header badge for the ID label.
    bbox: [x1, y1, x2, y2]
    """
    x1, y1, x2, y2 = [int(v) for v in bbox]
    h, w = frame.shape[:2]
    
    x1 = max(0, min(x1, w - 1))
    y1 = max(0, min(y1, h - 1))
    x2 = max(0, min(x2, w - 1))
    y2 = max(0, min(y2, h - 1))
    
    color = get_color(track_id)
    line_thickness = max(2, int(round(min(w, h) / 400.)))
    
    # Draw main rectangle
    cv2.rectangle(frame, (x1, y1), (x2, y2), color, line_thickness)
    
    # Label text
    if confidence is not None:
        label = f"{text_prefix} #{track_id} ({confidence:.2f})"
    else:
        label = f"{text_prefix} #{track_id}"
        
    font_scale = max(0.4, min(w, h) / 1000.)
    font_thickness = max(1, int(font_scale * 2))
    (text_width, text_height), baseline = cv2.getTextSize(label, cv2.FONT_HERSHEY_SIMPLEX, font_scale, font_thickness)
    
    # Draw background box for text badge
    badge_y1 = max(0, y1 - text_height - 8)
    badge_y2 = y1
    badge_x2 = min(w, x1 + text_width + 10)
    
    cv2.rectangle(frame, (x1, badge_y1), (badge_x2, badge_y2), color, -1)
    
    # Text inside badge (white or dark depending on color brightness)
    text_color = (255, 255, 255)
    cv2.putText(
        frame,
        label,
        (x1 + 5, badge_y2 - 5),
        cv2.FONT_HERSHEY_SIMPLEX,
        font_scale,
        text_color,
        font_thickness,
        lineType=cv2.LINE_AA
    )


def create_side_by_side_grid(frames):
    """Stack a list of frames horizontally or in a grid for multi-camera view."""
    if not frames:
        return None
    if len(frames) == 1:
        return frames[0]
        
    # Resize frames to match height of first frame
    target_h = frames[0].shape[0]
    resized_frames = []
    
    for f in frames:
        h, w = f.shape[:2]
        if h != target_h:
            target_w = int(w * (target_h / float(h)))
            f_res = cv2.resize(f, (target_w, target_h))
        else:
            f_res = f
        resized_frames.append(f_res)
        
    # Combine horizontally
    combined = np.hstack(resized_frames)
    return combined