vidformer.supervision

  1"""
  2vidformer.supervision is the [supervision](https://supervision.roboflow.com/) frontend for [vidformer](https://github.com/ixlab/vidformer).
  3"""
  4
  5from math import sqrt
  6
  7import numpy as np
  8import supervision as _sv
  9from supervision import Color, ColorLookup, ColorPalette, Detections
 10from supervision.annotators.utils import resolve_color, resolve_text_background_xyxy
 11from supervision.config import CLASS_NAME_DATA_FIELD
 12from supervision.detection.utils import spread_out_boxes
 13from supervision.geometry.core import Position
 14
 15import vidformer.cv2 as vf_cv2
 16
 17CV2_FONT = vf_cv2.FONT_HERSHEY_SIMPLEX
 18
 19
 20class BoxAnnotator:
 21    def __init__(
 22        self,
 23        color=ColorPalette.DEFAULT,
 24        thickness=2,
 25        color_lookup=ColorLookup.CLASS,
 26    ):
 27        self.color = color
 28        self.thickness = thickness
 29        self.color_lookup = color_lookup
 30
 31    def annotate(
 32        self,
 33        scene: vf_cv2.Frame,
 34        detections: Detections,
 35        custom_color_lookup=None,
 36    ):
 37        for detection_idx in range(len(detections)):
 38            x1, y1, x2, y2 = detections.xyxy[detection_idx].astype(int)
 39            color = resolve_color(
 40                color=self.color,
 41                detections=detections,
 42                detection_idx=detection_idx,
 43                color_lookup=(
 44                    self.color_lookup
 45                    if custom_color_lookup is None
 46                    else custom_color_lookup
 47                ),
 48            )
 49            vf_cv2.rectangle(
 50                img=scene,
 51                pt1=(x1, y1),
 52                pt2=(x2, y2),
 53                color=color.as_rgb(),
 54                thickness=self.thickness,
 55            )
 56        return scene
 57
 58
 59class RoundBoxAnnotator:
 60    def __init__(
 61        self,
 62        color=ColorPalette.DEFAULT,
 63        thickness: int = 2,
 64        color_lookup: ColorLookup = ColorLookup.CLASS,
 65        roundness: float = 0.6,
 66    ):
 67        self.color = color
 68        self.thickness = thickness
 69        self.color_lookup = color_lookup
 70        if not 0 < roundness <= 1.0:
 71            raise ValueError("roundness attribute must be float between (0, 1.0]")
 72        self.roundness = roundness
 73
 74    def annotate(
 75        self,
 76        scene: vf_cv2.Frame,
 77        detections: _sv.Detections,
 78        custom_color_lookup=None,
 79    ):
 80        for detection_idx in range(len(detections)):
 81            x1, y1, x2, y2 = detections.xyxy[detection_idx].astype(int)
 82            color = resolve_color(
 83                color=self.color,
 84                detections=detections,
 85                detection_idx=detection_idx,
 86                color_lookup=(
 87                    self.color_lookup
 88                    if custom_color_lookup is None
 89                    else custom_color_lookup
 90                ),
 91            )
 92            radius = (
 93                int((x2 - x1) // 2 * self.roundness)
 94                if abs(x1 - x2) < abs(y1 - y2)
 95                else int((y2 - y1) // 2 * self.roundness)
 96            )
 97            circle_coordinates = [
 98                ((x1 + radius), (y1 + radius)),
 99                ((x2 - radius), (y1 + radius)),
100                ((x2 - radius), (y2 - radius)),
101                ((x1 + radius), (y2 - radius)),
102            ]
103            line_coordinates = [
104                ((x1 + radius, y1), (x2 - radius, y1)),
105                ((x2, y1 + radius), (x2, y2 - radius)),
106                ((x1 + radius, y2), (x2 - radius, y2)),
107                ((x1, y1 + radius), (x1, y2 - radius)),
108            ]
109            start_angles = (180, 270, 0, 90)
110            end_angles = (270, 360, 90, 180)
111            for center_coordinates, line, start_angle, end_angle in zip(
112                circle_coordinates, line_coordinates, start_angles, end_angles
113            ):
114                vf_cv2.ellipse(
115                    img=scene,
116                    center=center_coordinates,
117                    axes=(radius, radius),
118                    angle=0,
119                    startAngle=start_angle,
120                    endAngle=end_angle,
121                    color=color.as_rgb(),
122                    thickness=self.thickness,
123                )
124                vf_cv2.line(
125                    img=scene,
126                    pt1=line[0],
127                    pt2=line[1],
128                    color=color.as_rgb(),
129                    thickness=self.thickness,
130                )
131        return scene
132
133
134class BoxCornerAnnotator:
135    def __init__(
136        self,
137        color=ColorPalette.DEFAULT,
138        thickness=4,
139        corner_length=15,
140        color_lookup=ColorLookup.CLASS,
141    ):
142        self.color = color
143        self.thickness: int = thickness
144        self.corner_length: int = corner_length
145        self.color_lookup: ColorLookup = color_lookup
146
147    def annotate(
148        self,
149        scene: vf_cv2.Frame,
150        detections: Detections,
151        custom_color_lookup=None,
152    ):
153        for detection_idx in range(len(detections)):
154            x1, y1, x2, y2 = detections.xyxy[detection_idx].astype(int)
155            color = resolve_color(
156                color=self.color,
157                detections=detections,
158                detection_idx=detection_idx,
159                color_lookup=(
160                    self.color_lookup
161                    if custom_color_lookup is None
162                    else custom_color_lookup
163                ),
164            )
165            corners = [(x1, y1), (x2, y1), (x1, y2), (x2, y2)]
166            for x, y in corners:
167                x_end = x + self.corner_length if x == x1 else x - self.corner_length
168                vf_cv2.line(
169                    scene, (x, y), (x_end, y), color.as_rgb(), thickness=self.thickness
170                )
171
172                y_end = y + self.corner_length if y == y1 else y - self.corner_length
173                vf_cv2.line(
174                    scene, (x, y), (x, y_end), color.as_rgb(), thickness=self.thickness
175                )
176        return scene
177
178
179class ColorAnnotator:
180    def __init__(
181        self,
182        color=ColorPalette.DEFAULT,
183        opacity: float = 0.5,
184        color_lookup: ColorLookup = ColorLookup.CLASS,
185    ):
186        self.color = color
187        self.color_lookup: ColorLookup = color_lookup
188        self.opacity = opacity
189
190    def annotate(
191        self,
192        scene: vf_cv2.Frame,
193        detections: Detections,
194        custom_color_lookup=None,
195    ):
196        scene_with_boxes = scene.copy()
197        for detection_idx in range(len(detections)):
198            x1, y1, x2, y2 = detections.xyxy[detection_idx].astype(int)
199            color = resolve_color(
200                color=self.color,
201                detections=detections,
202                detection_idx=detection_idx,
203                color_lookup=(
204                    self.color_lookup
205                    if custom_color_lookup is None
206                    else custom_color_lookup
207                ),
208            )
209            vf_cv2.rectangle(
210                img=scene_with_boxes,
211                pt1=(x1, y1),
212                pt2=(x2, y2),
213                color=color.as_rgb(),
214                thickness=-1,
215            )
216
217        vf_cv2.addWeighted(
218            scene_with_boxes, self.opacity, scene, 1 - self.opacity, gamma=0, dst=scene
219        )
220        return scene
221
222
223class CircleAnnotator:
224    def __init__(
225        self,
226        color=ColorPalette.DEFAULT,
227        thickness: int = 2,
228        color_lookup: ColorLookup = ColorLookup.CLASS,
229    ):
230        self.color = color
231        self.thickness: int = thickness
232        self.color_lookup: ColorLookup = color_lookup
233
234    def annotate(
235        self,
236        scene: vf_cv2.Frame,
237        detections: Detections,
238        custom_color_lookup=None,
239    ):
240        for detection_idx in range(len(detections)):
241            x1, y1, x2, y2 = detections.xyxy[detection_idx].astype(int)
242            center = ((x1 + x2) // 2, (y1 + y2) // 2)
243            distance = sqrt((x1 - center[0]) ** 2 + (y1 - center[1]) ** 2)
244            color = resolve_color(
245                color=self.color,
246                detections=detections,
247                detection_idx=detection_idx,
248                color_lookup=(
249                    self.color_lookup
250                    if custom_color_lookup is None
251                    else custom_color_lookup
252                ),
253            )
254            vf_cv2.circle(
255                img=scene,
256                center=center,
257                radius=int(distance),
258                color=color.as_rgb(),
259                thickness=self.thickness,
260            )
261
262        return scene
263
264
265class DotAnnotator:
266    def __init__(
267        self,
268        color=ColorPalette.DEFAULT,
269        radius: int = 4,
270        position: Position = Position.CENTER,
271        color_lookup: ColorLookup = ColorLookup.CLASS,
272        outline_thickness: int = 0,
273        outline_color=Color.BLACK,
274    ):
275
276        self.color = color
277        self.radius: int = radius
278        self.position: Position = position
279        self.color_lookup: ColorLookup = color_lookup
280        self.outline_thickness = outline_thickness
281        self.outline_color = outline_color
282
283    def annotate(
284        self,
285        scene: vf_cv2.Frame,
286        detections: Detections,
287        custom_color_lookup=None,
288    ):
289        xy = detections.get_anchors_coordinates(anchor=self.position)
290        for detection_idx in range(len(detections)):
291            color = resolve_color(
292                color=self.color,
293                detections=detections,
294                detection_idx=detection_idx,
295                color_lookup=(
296                    self.color_lookup
297                    if custom_color_lookup is None
298                    else custom_color_lookup
299                ),
300            )
301            center = (int(xy[detection_idx, 0]), int(xy[detection_idx, 1]))
302
303            vf_cv2.circle(scene, center, self.radius, color.as_rgb(), -1)
304            if self.outline_thickness:
305                outline_color = resolve_color(
306                    color=self.outline_color,
307                    detections=detections,
308                    detection_idx=detection_idx,
309                    color_lookup=(
310                        self.color_lookup
311                        if custom_color_lookup is None
312                        else custom_color_lookup
313                    ),
314                )
315                vf_cv2.circle(
316                    scene,
317                    center,
318                    self.radius,
319                    outline_color.as_rgb(),
320                    self.outline_thickness,
321                )
322        return scene
323
324
325class LabelAnnotator:
326    def __init__(
327        self,
328        color=ColorPalette.DEFAULT,
329        text_color=Color.WHITE,
330        text_scale: float = 0.5,
331        text_thickness: int = 1,
332        text_padding: int = 10,
333        text_position: Position = Position.TOP_LEFT,
334        color_lookup: ColorLookup = ColorLookup.CLASS,
335        border_radius: int = 0,
336        smart_position: bool = False,
337    ):
338        self.border_radius: int = border_radius
339        self.color = color
340        self.text_color = text_color
341        self.text_scale: float = text_scale
342        self.text_thickness: int = text_thickness
343        self.text_padding: int = text_padding
344        self.text_anchor: Position = text_position
345        self.color_lookup: ColorLookup = color_lookup
346        self.smart_position = smart_position
347
348    def annotate(
349        self,
350        scene,
351        detections: Detections,
352        labels,
353        custom_color_lookup=None,
354    ):
355        self._validate_labels(labels, detections)
356
357        labels = self._get_labels_text(detections, labels)
358        label_properties = self._get_label_properties(detections, labels)
359
360        if self.smart_position:
361            xyxy = label_properties[:, :4]
362            xyxy = spread_out_boxes(xyxy)
363            label_properties[:, :4] = xyxy
364
365        self._draw_labels(
366            scene=scene,
367            labels=labels,
368            label_properties=label_properties,
369            detections=detections,
370            custom_color_lookup=custom_color_lookup,
371        )
372
373        return scene
374
375    def _validate_labels(self, labels, detections: Detections):
376        if labels is not None and len(labels) != len(detections):
377            raise ValueError(
378                f"The number of labels ({len(labels)}) does not match the "
379                f"number of detections ({len(detections)}). Each detection "
380                f"should have exactly 1 label."
381            )
382
383    def _get_label_properties(
384        self,
385        detections: Detections,
386        labels,
387    ):
388        label_properties = []
389        anchors_coordinates = detections.get_anchors_coordinates(
390            anchor=self.text_anchor
391        ).astype(int)
392
393        for label, center_coords in zip(labels, anchors_coordinates):
394            (text_w, text_h) = vf_cv2.getTextSize(
395                text=label,
396                fontFace=CV2_FONT,
397                fontScale=self.text_scale,
398                thickness=self.text_thickness,
399            )[0]
400
401            width_padded = text_w + 2 * self.text_padding
402            height_padded = text_h + 2 * self.text_padding
403
404            text_background_xyxy = resolve_text_background_xyxy(
405                center_coordinates=tuple(center_coords),
406                text_wh=(width_padded, height_padded),
407                position=self.text_anchor,
408            )
409
410            label_properties.append(
411                [
412                    *text_background_xyxy,
413                    text_h,
414                ]
415            )
416
417        return np.array(label_properties).reshape(-1, 5)
418
419    @staticmethod
420    def _get_labels_text(detections: Detections, custom_labels):
421        if custom_labels is not None:
422            return custom_labels
423
424        labels = []
425        for idx in range(len(detections)):
426            if CLASS_NAME_DATA_FIELD in detections.data:
427                labels.append(detections.data[CLASS_NAME_DATA_FIELD][idx])
428            elif detections.class_id is not None:
429                labels.append(str(detections.class_id[idx]))
430            else:
431                labels.append(str(idx))
432        return labels
433
434    def _draw_labels(
435        self,
436        scene,
437        labels,
438        label_properties,
439        detections,
440        custom_color_lookup,
441    ) -> None:
442        assert len(labels) == len(label_properties) == len(detections), (
443            f"Number of label properties ({len(label_properties)}), "
444            f"labels ({len(labels)}) and detections ({len(detections)}) "
445            "do not match."
446        )
447
448        color_lookup = (
449            custom_color_lookup
450            if custom_color_lookup is not None
451            else self.color_lookup
452        )
453
454        for idx, label_property in enumerate(label_properties):
455            background_color = resolve_color(
456                color=self.color,
457                detections=detections,
458                detection_idx=idx,
459                color_lookup=color_lookup,
460            )
461            text_color = resolve_color(
462                color=self.text_color,
463                detections=detections,
464                detection_idx=idx,
465                color_lookup=color_lookup,
466            )
467
468            box_xyxy = label_property[:4]
469            text_height_padded = label_property[4]
470            self.draw_rounded_rectangle(
471                scene=scene,
472                xyxy=box_xyxy,
473                color=background_color.as_rgb(),
474                border_radius=self.border_radius,
475            )
476
477            text_x = box_xyxy[0] + self.text_padding
478            text_y = box_xyxy[1] + self.text_padding + text_height_padded
479            vf_cv2.putText(
480                img=scene,
481                text=labels[idx],
482                org=(text_x, text_y),
483                fontFace=CV2_FONT,
484                fontScale=self.text_scale,
485                color=text_color.as_rgb(),
486                thickness=self.text_thickness,
487                lineType=vf_cv2.LINE_AA,
488            )
489
490    @staticmethod
491    def draw_rounded_rectangle(
492        scene: np.ndarray,
493        xyxy,
494        color,
495        border_radius: int,
496    ) -> np.ndarray:
497        x1, y1, x2, y2 = xyxy
498        width = x2 - x1
499        height = y2 - y1
500
501        border_radius = min(border_radius, min(width, height) // 2)
502
503        if border_radius <= 0:
504            vf_cv2.rectangle(
505                img=scene,
506                pt1=(x1, y1),
507                pt2=(x2, y2),
508                color=color,
509                thickness=-1,
510            )
511        else:
512            rectangle_coordinates = [
513                ((x1 + border_radius, y1), (x2 - border_radius, y2)),
514                ((x1, y1 + border_radius), (x2, y2 - border_radius)),
515            ]
516            circle_centers = [
517                (x1 + border_radius, y1 + border_radius),
518                (x2 - border_radius, y1 + border_radius),
519                (x1 + border_radius, y2 - border_radius),
520                (x2 - border_radius, y2 - border_radius),
521            ]
522
523            for coordinates in rectangle_coordinates:
524                vf_cv2.rectangle(
525                    img=scene,
526                    pt1=coordinates[0],
527                    pt2=coordinates[1],
528                    color=color,
529                    thickness=-1,
530                )
531            for center in circle_centers:
532                vf_cv2.circle(
533                    img=scene,
534                    center=center,
535                    radius=border_radius,
536                    color=color,
537                    thickness=-1,
538                )
539        return scene
CV2_FONT = 0
class BoxAnnotator:
21class BoxAnnotator:
22    def __init__(
23        self,
24        color=ColorPalette.DEFAULT,
25        thickness=2,
26        color_lookup=ColorLookup.CLASS,
27    ):
28        self.color = color
29        self.thickness = thickness
30        self.color_lookup = color_lookup
31
32    def annotate(
33        self,
34        scene: vf_cv2.Frame,
35        detections: Detections,
36        custom_color_lookup=None,
37    ):
38        for detection_idx in range(len(detections)):
39            x1, y1, x2, y2 = detections.xyxy[detection_idx].astype(int)
40            color = resolve_color(
41                color=self.color,
42                detections=detections,
43                detection_idx=detection_idx,
44                color_lookup=(
45                    self.color_lookup
46                    if custom_color_lookup is None
47                    else custom_color_lookup
48                ),
49            )
50            vf_cv2.rectangle(
51                img=scene,
52                pt1=(x1, y1),
53                pt2=(x2, y2),
54                color=color.as_rgb(),
55                thickness=self.thickness,
56            )
57        return scene
BoxAnnotator( color=ColorPalette(colors=[Color(r=163, g=81, b=251), Color(r=255, g=64, b=64), Color(r=255, g=161, b=160), Color(r=255, g=118, b=51), Color(r=255, g=182, b=51), Color(r=209, g=212, b=53), Color(r=76, g=251, b=18), Color(r=148, g=207, b=26), Color(r=64, g=222, b=138), Color(r=27, g=150, b=64), Color(r=0, g=214, b=193), Color(r=46, g=156, b=170), Color(r=0, g=196, b=255), Color(r=54, g=71, b=151), Color(r=102, g=117, b=255), Color(r=0, g=25, b=239), Color(r=134, g=58, b=255), Color(r=83, g=0, b=135), Color(r=205, g=58, b=255), Color(r=255, g=151, b=202), Color(r=255, g=57, b=201)]), thickness=2, color_lookup=<ColorLookup.CLASS: 'class'>)
22    def __init__(
23        self,
24        color=ColorPalette.DEFAULT,
25        thickness=2,
26        color_lookup=ColorLookup.CLASS,
27    ):
28        self.color = color
29        self.thickness = thickness
30        self.color_lookup = color_lookup
color
thickness
color_lookup
def annotate( self, scene: vidformer.cv2.Frame, detections: supervision.detection.core.Detections, custom_color_lookup=None):
32    def annotate(
33        self,
34        scene: vf_cv2.Frame,
35        detections: Detections,
36        custom_color_lookup=None,
37    ):
38        for detection_idx in range(len(detections)):
39            x1, y1, x2, y2 = detections.xyxy[detection_idx].astype(int)
40            color = resolve_color(
41                color=self.color,
42                detections=detections,
43                detection_idx=detection_idx,
44                color_lookup=(
45                    self.color_lookup
46                    if custom_color_lookup is None
47                    else custom_color_lookup
48                ),
49            )
50            vf_cv2.rectangle(
51                img=scene,
52                pt1=(x1, y1),
53                pt2=(x2, y2),
54                color=color.as_rgb(),
55                thickness=self.thickness,
56            )
57        return scene
class RoundBoxAnnotator:
 60class RoundBoxAnnotator:
 61    def __init__(
 62        self,
 63        color=ColorPalette.DEFAULT,
 64        thickness: int = 2,
 65        color_lookup: ColorLookup = ColorLookup.CLASS,
 66        roundness: float = 0.6,
 67    ):
 68        self.color = color
 69        self.thickness = thickness
 70        self.color_lookup = color_lookup
 71        if not 0 < roundness <= 1.0:
 72            raise ValueError("roundness attribute must be float between (0, 1.0]")
 73        self.roundness = roundness
 74
 75    def annotate(
 76        self,
 77        scene: vf_cv2.Frame,
 78        detections: _sv.Detections,
 79        custom_color_lookup=None,
 80    ):
 81        for detection_idx in range(len(detections)):
 82            x1, y1, x2, y2 = detections.xyxy[detection_idx].astype(int)
 83            color = resolve_color(
 84                color=self.color,
 85                detections=detections,
 86                detection_idx=detection_idx,
 87                color_lookup=(
 88                    self.color_lookup
 89                    if custom_color_lookup is None
 90                    else custom_color_lookup
 91                ),
 92            )
 93            radius = (
 94                int((x2 - x1) // 2 * self.roundness)
 95                if abs(x1 - x2) < abs(y1 - y2)
 96                else int((y2 - y1) // 2 * self.roundness)
 97            )
 98            circle_coordinates = [
 99                ((x1 + radius), (y1 + radius)),
100                ((x2 - radius), (y1 + radius)),
101                ((x2 - radius), (y2 - radius)),
102                ((x1 + radius), (y2 - radius)),
103            ]
104            line_coordinates = [
105                ((x1 + radius, y1), (x2 - radius, y1)),
106                ((x2, y1 + radius), (x2, y2 - radius)),
107                ((x1 + radius, y2), (x2 - radius, y2)),
108                ((x1, y1 + radius), (x1, y2 - radius)),
109            ]
110            start_angles = (180, 270, 0, 90)
111            end_angles = (270, 360, 90, 180)
112            for center_coordinates, line, start_angle, end_angle in zip(
113                circle_coordinates, line_coordinates, start_angles, end_angles
114            ):
115                vf_cv2.ellipse(
116                    img=scene,
117                    center=center_coordinates,
118                    axes=(radius, radius),
119                    angle=0,
120                    startAngle=start_angle,
121                    endAngle=end_angle,
122                    color=color.as_rgb(),
123                    thickness=self.thickness,
124                )
125                vf_cv2.line(
126                    img=scene,
127                    pt1=line[0],
128                    pt2=line[1],
129                    color=color.as_rgb(),
130                    thickness=self.thickness,
131                )
132        return scene
RoundBoxAnnotator( color=ColorPalette(colors=[Color(r=163, g=81, b=251), Color(r=255, g=64, b=64), Color(r=255, g=161, b=160), Color(r=255, g=118, b=51), Color(r=255, g=182, b=51), Color(r=209, g=212, b=53), Color(r=76, g=251, b=18), Color(r=148, g=207, b=26), Color(r=64, g=222, b=138), Color(r=27, g=150, b=64), Color(r=0, g=214, b=193), Color(r=46, g=156, b=170), Color(r=0, g=196, b=255), Color(r=54, g=71, b=151), Color(r=102, g=117, b=255), Color(r=0, g=25, b=239), Color(r=134, g=58, b=255), Color(r=83, g=0, b=135), Color(r=205, g=58, b=255), Color(r=255, g=151, b=202), Color(r=255, g=57, b=201)]), thickness: int = 2, color_lookup: supervision.annotators.utils.ColorLookup = <ColorLookup.CLASS: 'class'>, roundness: float = 0.6)
61    def __init__(
62        self,
63        color=ColorPalette.DEFAULT,
64        thickness: int = 2,
65        color_lookup: ColorLookup = ColorLookup.CLASS,
66        roundness: float = 0.6,
67    ):
68        self.color = color
69        self.thickness = thickness
70        self.color_lookup = color_lookup
71        if not 0 < roundness <= 1.0:
72            raise ValueError("roundness attribute must be float between (0, 1.0]")
73        self.roundness = roundness
color
thickness
color_lookup
roundness
def annotate( self, scene: vidformer.cv2.Frame, detections: supervision.detection.core.Detections, custom_color_lookup=None):
 75    def annotate(
 76        self,
 77        scene: vf_cv2.Frame,
 78        detections: _sv.Detections,
 79        custom_color_lookup=None,
 80    ):
 81        for detection_idx in range(len(detections)):
 82            x1, y1, x2, y2 = detections.xyxy[detection_idx].astype(int)
 83            color = resolve_color(
 84                color=self.color,
 85                detections=detections,
 86                detection_idx=detection_idx,
 87                color_lookup=(
 88                    self.color_lookup
 89                    if custom_color_lookup is None
 90                    else custom_color_lookup
 91                ),
 92            )
 93            radius = (
 94                int((x2 - x1) // 2 * self.roundness)
 95                if abs(x1 - x2) < abs(y1 - y2)
 96                else int((y2 - y1) // 2 * self.roundness)
 97            )
 98            circle_coordinates = [
 99                ((x1 + radius), (y1 + radius)),
100                ((x2 - radius), (y1 + radius)),
101                ((x2 - radius), (y2 - radius)),
102                ((x1 + radius), (y2 - radius)),
103            ]
104            line_coordinates = [
105                ((x1 + radius, y1), (x2 - radius, y1)),
106                ((x2, y1 + radius), (x2, y2 - radius)),
107                ((x1 + radius, y2), (x2 - radius, y2)),
108                ((x1, y1 + radius), (x1, y2 - radius)),
109            ]
110            start_angles = (180, 270, 0, 90)
111            end_angles = (270, 360, 90, 180)
112            for center_coordinates, line, start_angle, end_angle in zip(
113                circle_coordinates, line_coordinates, start_angles, end_angles
114            ):
115                vf_cv2.ellipse(
116                    img=scene,
117                    center=center_coordinates,
118                    axes=(radius, radius),
119                    angle=0,
120                    startAngle=start_angle,
121                    endAngle=end_angle,
122                    color=color.as_rgb(),
123                    thickness=self.thickness,
124                )
125                vf_cv2.line(
126                    img=scene,
127                    pt1=line[0],
128                    pt2=line[1],
129                    color=color.as_rgb(),
130                    thickness=self.thickness,
131                )
132        return scene
class BoxCornerAnnotator:
135class BoxCornerAnnotator:
136    def __init__(
137        self,
138        color=ColorPalette.DEFAULT,
139        thickness=4,
140        corner_length=15,
141        color_lookup=ColorLookup.CLASS,
142    ):
143        self.color = color
144        self.thickness: int = thickness
145        self.corner_length: int = corner_length
146        self.color_lookup: ColorLookup = color_lookup
147
148    def annotate(
149        self,
150        scene: vf_cv2.Frame,
151        detections: Detections,
152        custom_color_lookup=None,
153    ):
154        for detection_idx in range(len(detections)):
155            x1, y1, x2, y2 = detections.xyxy[detection_idx].astype(int)
156            color = resolve_color(
157                color=self.color,
158                detections=detections,
159                detection_idx=detection_idx,
160                color_lookup=(
161                    self.color_lookup
162                    if custom_color_lookup is None
163                    else custom_color_lookup
164                ),
165            )
166            corners = [(x1, y1), (x2, y1), (x1, y2), (x2, y2)]
167            for x, y in corners:
168                x_end = x + self.corner_length if x == x1 else x - self.corner_length
169                vf_cv2.line(
170                    scene, (x, y), (x_end, y), color.as_rgb(), thickness=self.thickness
171                )
172
173                y_end = y + self.corner_length if y == y1 else y - self.corner_length
174                vf_cv2.line(
175                    scene, (x, y), (x, y_end), color.as_rgb(), thickness=self.thickness
176                )
177        return scene
BoxCornerAnnotator( color=ColorPalette(colors=[Color(r=163, g=81, b=251), Color(r=255, g=64, b=64), Color(r=255, g=161, b=160), Color(r=255, g=118, b=51), Color(r=255, g=182, b=51), Color(r=209, g=212, b=53), Color(r=76, g=251, b=18), Color(r=148, g=207, b=26), Color(r=64, g=222, b=138), Color(r=27, g=150, b=64), Color(r=0, g=214, b=193), Color(r=46, g=156, b=170), Color(r=0, g=196, b=255), Color(r=54, g=71, b=151), Color(r=102, g=117, b=255), Color(r=0, g=25, b=239), Color(r=134, g=58, b=255), Color(r=83, g=0, b=135), Color(r=205, g=58, b=255), Color(r=255, g=151, b=202), Color(r=255, g=57, b=201)]), thickness=4, corner_length=15, color_lookup=<ColorLookup.CLASS: 'class'>)
136    def __init__(
137        self,
138        color=ColorPalette.DEFAULT,
139        thickness=4,
140        corner_length=15,
141        color_lookup=ColorLookup.CLASS,
142    ):
143        self.color = color
144        self.thickness: int = thickness
145        self.corner_length: int = corner_length
146        self.color_lookup: ColorLookup = color_lookup
color
thickness: int
corner_length: int
color_lookup: supervision.annotators.utils.ColorLookup
def annotate( self, scene: vidformer.cv2.Frame, detections: supervision.detection.core.Detections, custom_color_lookup=None):
148    def annotate(
149        self,
150        scene: vf_cv2.Frame,
151        detections: Detections,
152        custom_color_lookup=None,
153    ):
154        for detection_idx in range(len(detections)):
155            x1, y1, x2, y2 = detections.xyxy[detection_idx].astype(int)
156            color = resolve_color(
157                color=self.color,
158                detections=detections,
159                detection_idx=detection_idx,
160                color_lookup=(
161                    self.color_lookup
162                    if custom_color_lookup is None
163                    else custom_color_lookup
164                ),
165            )
166            corners = [(x1, y1), (x2, y1), (x1, y2), (x2, y2)]
167            for x, y in corners:
168                x_end = x + self.corner_length if x == x1 else x - self.corner_length
169                vf_cv2.line(
170                    scene, (x, y), (x_end, y), color.as_rgb(), thickness=self.thickness
171                )
172
173                y_end = y + self.corner_length if y == y1 else y - self.corner_length
174                vf_cv2.line(
175                    scene, (x, y), (x, y_end), color.as_rgb(), thickness=self.thickness
176                )
177        return scene
class ColorAnnotator:
180class ColorAnnotator:
181    def __init__(
182        self,
183        color=ColorPalette.DEFAULT,
184        opacity: float = 0.5,
185        color_lookup: ColorLookup = ColorLookup.CLASS,
186    ):
187        self.color = color
188        self.color_lookup: ColorLookup = color_lookup
189        self.opacity = opacity
190
191    def annotate(
192        self,
193        scene: vf_cv2.Frame,
194        detections: Detections,
195        custom_color_lookup=None,
196    ):
197        scene_with_boxes = scene.copy()
198        for detection_idx in range(len(detections)):
199            x1, y1, x2, y2 = detections.xyxy[detection_idx].astype(int)
200            color = resolve_color(
201                color=self.color,
202                detections=detections,
203                detection_idx=detection_idx,
204                color_lookup=(
205                    self.color_lookup
206                    if custom_color_lookup is None
207                    else custom_color_lookup
208                ),
209            )
210            vf_cv2.rectangle(
211                img=scene_with_boxes,
212                pt1=(x1, y1),
213                pt2=(x2, y2),
214                color=color.as_rgb(),
215                thickness=-1,
216            )
217
218        vf_cv2.addWeighted(
219            scene_with_boxes, self.opacity, scene, 1 - self.opacity, gamma=0, dst=scene
220        )
221        return scene
ColorAnnotator( color=ColorPalette(colors=[Color(r=163, g=81, b=251), Color(r=255, g=64, b=64), Color(r=255, g=161, b=160), Color(r=255, g=118, b=51), Color(r=255, g=182, b=51), Color(r=209, g=212, b=53), Color(r=76, g=251, b=18), Color(r=148, g=207, b=26), Color(r=64, g=222, b=138), Color(r=27, g=150, b=64), Color(r=0, g=214, b=193), Color(r=46, g=156, b=170), Color(r=0, g=196, b=255), Color(r=54, g=71, b=151), Color(r=102, g=117, b=255), Color(r=0, g=25, b=239), Color(r=134, g=58, b=255), Color(r=83, g=0, b=135), Color(r=205, g=58, b=255), Color(r=255, g=151, b=202), Color(r=255, g=57, b=201)]), opacity: float = 0.5, color_lookup: supervision.annotators.utils.ColorLookup = <ColorLookup.CLASS: 'class'>)
181    def __init__(
182        self,
183        color=ColorPalette.DEFAULT,
184        opacity: float = 0.5,
185        color_lookup: ColorLookup = ColorLookup.CLASS,
186    ):
187        self.color = color
188        self.color_lookup: ColorLookup = color_lookup
189        self.opacity = opacity
color
color_lookup: supervision.annotators.utils.ColorLookup
opacity
def annotate( self, scene: vidformer.cv2.Frame, detections: supervision.detection.core.Detections, custom_color_lookup=None):
191    def annotate(
192        self,
193        scene: vf_cv2.Frame,
194        detections: Detections,
195        custom_color_lookup=None,
196    ):
197        scene_with_boxes = scene.copy()
198        for detection_idx in range(len(detections)):
199            x1, y1, x2, y2 = detections.xyxy[detection_idx].astype(int)
200            color = resolve_color(
201                color=self.color,
202                detections=detections,
203                detection_idx=detection_idx,
204                color_lookup=(
205                    self.color_lookup
206                    if custom_color_lookup is None
207                    else custom_color_lookup
208                ),
209            )
210            vf_cv2.rectangle(
211                img=scene_with_boxes,
212                pt1=(x1, y1),
213                pt2=(x2, y2),
214                color=color.as_rgb(),
215                thickness=-1,
216            )
217
218        vf_cv2.addWeighted(
219            scene_with_boxes, self.opacity, scene, 1 - self.opacity, gamma=0, dst=scene
220        )
221        return scene
class CircleAnnotator:
224class CircleAnnotator:
225    def __init__(
226        self,
227        color=ColorPalette.DEFAULT,
228        thickness: int = 2,
229        color_lookup: ColorLookup = ColorLookup.CLASS,
230    ):
231        self.color = color
232        self.thickness: int = thickness
233        self.color_lookup: ColorLookup = color_lookup
234
235    def annotate(
236        self,
237        scene: vf_cv2.Frame,
238        detections: Detections,
239        custom_color_lookup=None,
240    ):
241        for detection_idx in range(len(detections)):
242            x1, y1, x2, y2 = detections.xyxy[detection_idx].astype(int)
243            center = ((x1 + x2) // 2, (y1 + y2) // 2)
244            distance = sqrt((x1 - center[0]) ** 2 + (y1 - center[1]) ** 2)
245            color = resolve_color(
246                color=self.color,
247                detections=detections,
248                detection_idx=detection_idx,
249                color_lookup=(
250                    self.color_lookup
251                    if custom_color_lookup is None
252                    else custom_color_lookup
253                ),
254            )
255            vf_cv2.circle(
256                img=scene,
257                center=center,
258                radius=int(distance),
259                color=color.as_rgb(),
260                thickness=self.thickness,
261            )
262
263        return scene
CircleAnnotator( color=ColorPalette(colors=[Color(r=163, g=81, b=251), Color(r=255, g=64, b=64), Color(r=255, g=161, b=160), Color(r=255, g=118, b=51), Color(r=255, g=182, b=51), Color(r=209, g=212, b=53), Color(r=76, g=251, b=18), Color(r=148, g=207, b=26), Color(r=64, g=222, b=138), Color(r=27, g=150, b=64), Color(r=0, g=214, b=193), Color(r=46, g=156, b=170), Color(r=0, g=196, b=255), Color(r=54, g=71, b=151), Color(r=102, g=117, b=255), Color(r=0, g=25, b=239), Color(r=134, g=58, b=255), Color(r=83, g=0, b=135), Color(r=205, g=58, b=255), Color(r=255, g=151, b=202), Color(r=255, g=57, b=201)]), thickness: int = 2, color_lookup: supervision.annotators.utils.ColorLookup = <ColorLookup.CLASS: 'class'>)
225    def __init__(
226        self,
227        color=ColorPalette.DEFAULT,
228        thickness: int = 2,
229        color_lookup: ColorLookup = ColorLookup.CLASS,
230    ):
231        self.color = color
232        self.thickness: int = thickness
233        self.color_lookup: ColorLookup = color_lookup
color
thickness: int
color_lookup: supervision.annotators.utils.ColorLookup
def annotate( self, scene: vidformer.cv2.Frame, detections: supervision.detection.core.Detections, custom_color_lookup=None):
235    def annotate(
236        self,
237        scene: vf_cv2.Frame,
238        detections: Detections,
239        custom_color_lookup=None,
240    ):
241        for detection_idx in range(len(detections)):
242            x1, y1, x2, y2 = detections.xyxy[detection_idx].astype(int)
243            center = ((x1 + x2) // 2, (y1 + y2) // 2)
244            distance = sqrt((x1 - center[0]) ** 2 + (y1 - center[1]) ** 2)
245            color = resolve_color(
246                color=self.color,
247                detections=detections,
248                detection_idx=detection_idx,
249                color_lookup=(
250                    self.color_lookup
251                    if custom_color_lookup is None
252                    else custom_color_lookup
253                ),
254            )
255            vf_cv2.circle(
256                img=scene,
257                center=center,
258                radius=int(distance),
259                color=color.as_rgb(),
260                thickness=self.thickness,
261            )
262
263        return scene
class DotAnnotator:
266class DotAnnotator:
267    def __init__(
268        self,
269        color=ColorPalette.DEFAULT,
270        radius: int = 4,
271        position: Position = Position.CENTER,
272        color_lookup: ColorLookup = ColorLookup.CLASS,
273        outline_thickness: int = 0,
274        outline_color=Color.BLACK,
275    ):
276
277        self.color = color
278        self.radius: int = radius
279        self.position: Position = position
280        self.color_lookup: ColorLookup = color_lookup
281        self.outline_thickness = outline_thickness
282        self.outline_color = outline_color
283
284    def annotate(
285        self,
286        scene: vf_cv2.Frame,
287        detections: Detections,
288        custom_color_lookup=None,
289    ):
290        xy = detections.get_anchors_coordinates(anchor=self.position)
291        for detection_idx in range(len(detections)):
292            color = resolve_color(
293                color=self.color,
294                detections=detections,
295                detection_idx=detection_idx,
296                color_lookup=(
297                    self.color_lookup
298                    if custom_color_lookup is None
299                    else custom_color_lookup
300                ),
301            )
302            center = (int(xy[detection_idx, 0]), int(xy[detection_idx, 1]))
303
304            vf_cv2.circle(scene, center, self.radius, color.as_rgb(), -1)
305            if self.outline_thickness:
306                outline_color = resolve_color(
307                    color=self.outline_color,
308                    detections=detections,
309                    detection_idx=detection_idx,
310                    color_lookup=(
311                        self.color_lookup
312                        if custom_color_lookup is None
313                        else custom_color_lookup
314                    ),
315                )
316                vf_cv2.circle(
317                    scene,
318                    center,
319                    self.radius,
320                    outline_color.as_rgb(),
321                    self.outline_thickness,
322                )
323        return scene
DotAnnotator( color=ColorPalette(colors=[Color(r=163, g=81, b=251), Color(r=255, g=64, b=64), Color(r=255, g=161, b=160), Color(r=255, g=118, b=51), Color(r=255, g=182, b=51), Color(r=209, g=212, b=53), Color(r=76, g=251, b=18), Color(r=148, g=207, b=26), Color(r=64, g=222, b=138), Color(r=27, g=150, b=64), Color(r=0, g=214, b=193), Color(r=46, g=156, b=170), Color(r=0, g=196, b=255), Color(r=54, g=71, b=151), Color(r=102, g=117, b=255), Color(r=0, g=25, b=239), Color(r=134, g=58, b=255), Color(r=83, g=0, b=135), Color(r=205, g=58, b=255), Color(r=255, g=151, b=202), Color(r=255, g=57, b=201)]), radius: int = 4, position: supervision.geometry.core.Position = <Position.CENTER: 'CENTER'>, color_lookup: supervision.annotators.utils.ColorLookup = <ColorLookup.CLASS: 'class'>, outline_thickness: int = 0, outline_color=Color(r=0, g=0, b=0))
267    def __init__(
268        self,
269        color=ColorPalette.DEFAULT,
270        radius: int = 4,
271        position: Position = Position.CENTER,
272        color_lookup: ColorLookup = ColorLookup.CLASS,
273        outline_thickness: int = 0,
274        outline_color=Color.BLACK,
275    ):
276
277        self.color = color
278        self.radius: int = radius
279        self.position: Position = position
280        self.color_lookup: ColorLookup = color_lookup
281        self.outline_thickness = outline_thickness
282        self.outline_color = outline_color
color
radius: int
position: supervision.geometry.core.Position
color_lookup: supervision.annotators.utils.ColorLookup
outline_thickness
outline_color
def annotate( self, scene: vidformer.cv2.Frame, detections: supervision.detection.core.Detections, custom_color_lookup=None):
284    def annotate(
285        self,
286        scene: vf_cv2.Frame,
287        detections: Detections,
288        custom_color_lookup=None,
289    ):
290        xy = detections.get_anchors_coordinates(anchor=self.position)
291        for detection_idx in range(len(detections)):
292            color = resolve_color(
293                color=self.color,
294                detections=detections,
295                detection_idx=detection_idx,
296                color_lookup=(
297                    self.color_lookup
298                    if custom_color_lookup is None
299                    else custom_color_lookup
300                ),
301            )
302            center = (int(xy[detection_idx, 0]), int(xy[detection_idx, 1]))
303
304            vf_cv2.circle(scene, center, self.radius, color.as_rgb(), -1)
305            if self.outline_thickness:
306                outline_color = resolve_color(
307                    color=self.outline_color,
308                    detections=detections,
309                    detection_idx=detection_idx,
310                    color_lookup=(
311                        self.color_lookup
312                        if custom_color_lookup is None
313                        else custom_color_lookup
314                    ),
315                )
316                vf_cv2.circle(
317                    scene,
318                    center,
319                    self.radius,
320                    outline_color.as_rgb(),
321                    self.outline_thickness,
322                )
323        return scene
class LabelAnnotator:
326class LabelAnnotator:
327    def __init__(
328        self,
329        color=ColorPalette.DEFAULT,
330        text_color=Color.WHITE,
331        text_scale: float = 0.5,
332        text_thickness: int = 1,
333        text_padding: int = 10,
334        text_position: Position = Position.TOP_LEFT,
335        color_lookup: ColorLookup = ColorLookup.CLASS,
336        border_radius: int = 0,
337        smart_position: bool = False,
338    ):
339        self.border_radius: int = border_radius
340        self.color = color
341        self.text_color = text_color
342        self.text_scale: float = text_scale
343        self.text_thickness: int = text_thickness
344        self.text_padding: int = text_padding
345        self.text_anchor: Position = text_position
346        self.color_lookup: ColorLookup = color_lookup
347        self.smart_position = smart_position
348
349    def annotate(
350        self,
351        scene,
352        detections: Detections,
353        labels,
354        custom_color_lookup=None,
355    ):
356        self._validate_labels(labels, detections)
357
358        labels = self._get_labels_text(detections, labels)
359        label_properties = self._get_label_properties(detections, labels)
360
361        if self.smart_position:
362            xyxy = label_properties[:, :4]
363            xyxy = spread_out_boxes(xyxy)
364            label_properties[:, :4] = xyxy
365
366        self._draw_labels(
367            scene=scene,
368            labels=labels,
369            label_properties=label_properties,
370            detections=detections,
371            custom_color_lookup=custom_color_lookup,
372        )
373
374        return scene
375
376    def _validate_labels(self, labels, detections: Detections):
377        if labels is not None and len(labels) != len(detections):
378            raise ValueError(
379                f"The number of labels ({len(labels)}) does not match the "
380                f"number of detections ({len(detections)}). Each detection "
381                f"should have exactly 1 label."
382            )
383
384    def _get_label_properties(
385        self,
386        detections: Detections,
387        labels,
388    ):
389        label_properties = []
390        anchors_coordinates = detections.get_anchors_coordinates(
391            anchor=self.text_anchor
392        ).astype(int)
393
394        for label, center_coords in zip(labels, anchors_coordinates):
395            (text_w, text_h) = vf_cv2.getTextSize(
396                text=label,
397                fontFace=CV2_FONT,
398                fontScale=self.text_scale,
399                thickness=self.text_thickness,
400            )[0]
401
402            width_padded = text_w + 2 * self.text_padding
403            height_padded = text_h + 2 * self.text_padding
404
405            text_background_xyxy = resolve_text_background_xyxy(
406                center_coordinates=tuple(center_coords),
407                text_wh=(width_padded, height_padded),
408                position=self.text_anchor,
409            )
410
411            label_properties.append(
412                [
413                    *text_background_xyxy,
414                    text_h,
415                ]
416            )
417
418        return np.array(label_properties).reshape(-1, 5)
419
420    @staticmethod
421    def _get_labels_text(detections: Detections, custom_labels):
422        if custom_labels is not None:
423            return custom_labels
424
425        labels = []
426        for idx in range(len(detections)):
427            if CLASS_NAME_DATA_FIELD in detections.data:
428                labels.append(detections.data[CLASS_NAME_DATA_FIELD][idx])
429            elif detections.class_id is not None:
430                labels.append(str(detections.class_id[idx]))
431            else:
432                labels.append(str(idx))
433        return labels
434
435    def _draw_labels(
436        self,
437        scene,
438        labels,
439        label_properties,
440        detections,
441        custom_color_lookup,
442    ) -> None:
443        assert len(labels) == len(label_properties) == len(detections), (
444            f"Number of label properties ({len(label_properties)}), "
445            f"labels ({len(labels)}) and detections ({len(detections)}) "
446            "do not match."
447        )
448
449        color_lookup = (
450            custom_color_lookup
451            if custom_color_lookup is not None
452            else self.color_lookup
453        )
454
455        for idx, label_property in enumerate(label_properties):
456            background_color = resolve_color(
457                color=self.color,
458                detections=detections,
459                detection_idx=idx,
460                color_lookup=color_lookup,
461            )
462            text_color = resolve_color(
463                color=self.text_color,
464                detections=detections,
465                detection_idx=idx,
466                color_lookup=color_lookup,
467            )
468
469            box_xyxy = label_property[:4]
470            text_height_padded = label_property[4]
471            self.draw_rounded_rectangle(
472                scene=scene,
473                xyxy=box_xyxy,
474                color=background_color.as_rgb(),
475                border_radius=self.border_radius,
476            )
477
478            text_x = box_xyxy[0] + self.text_padding
479            text_y = box_xyxy[1] + self.text_padding + text_height_padded
480            vf_cv2.putText(
481                img=scene,
482                text=labels[idx],
483                org=(text_x, text_y),
484                fontFace=CV2_FONT,
485                fontScale=self.text_scale,
486                color=text_color.as_rgb(),
487                thickness=self.text_thickness,
488                lineType=vf_cv2.LINE_AA,
489            )
490
491    @staticmethod
492    def draw_rounded_rectangle(
493        scene: np.ndarray,
494        xyxy,
495        color,
496        border_radius: int,
497    ) -> np.ndarray:
498        x1, y1, x2, y2 = xyxy
499        width = x2 - x1
500        height = y2 - y1
501
502        border_radius = min(border_radius, min(width, height) // 2)
503
504        if border_radius <= 0:
505            vf_cv2.rectangle(
506                img=scene,
507                pt1=(x1, y1),
508                pt2=(x2, y2),
509                color=color,
510                thickness=-1,
511            )
512        else:
513            rectangle_coordinates = [
514                ((x1 + border_radius, y1), (x2 - border_radius, y2)),
515                ((x1, y1 + border_radius), (x2, y2 - border_radius)),
516            ]
517            circle_centers = [
518                (x1 + border_radius, y1 + border_radius),
519                (x2 - border_radius, y1 + border_radius),
520                (x1 + border_radius, y2 - border_radius),
521                (x2 - border_radius, y2 - border_radius),
522            ]
523
524            for coordinates in rectangle_coordinates:
525                vf_cv2.rectangle(
526                    img=scene,
527                    pt1=coordinates[0],
528                    pt2=coordinates[1],
529                    color=color,
530                    thickness=-1,
531                )
532            for center in circle_centers:
533                vf_cv2.circle(
534                    img=scene,
535                    center=center,
536                    radius=border_radius,
537                    color=color,
538                    thickness=-1,
539                )
540        return scene
LabelAnnotator( color=ColorPalette(colors=[Color(r=163, g=81, b=251), Color(r=255, g=64, b=64), Color(r=255, g=161, b=160), Color(r=255, g=118, b=51), Color(r=255, g=182, b=51), Color(r=209, g=212, b=53), Color(r=76, g=251, b=18), Color(r=148, g=207, b=26), Color(r=64, g=222, b=138), Color(r=27, g=150, b=64), Color(r=0, g=214, b=193), Color(r=46, g=156, b=170), Color(r=0, g=196, b=255), Color(r=54, g=71, b=151), Color(r=102, g=117, b=255), Color(r=0, g=25, b=239), Color(r=134, g=58, b=255), Color(r=83, g=0, b=135), Color(r=205, g=58, b=255), Color(r=255, g=151, b=202), Color(r=255, g=57, b=201)]), text_color=Color(r=255, g=255, b=255), text_scale: float = 0.5, text_thickness: int = 1, text_padding: int = 10, text_position: supervision.geometry.core.Position = <Position.TOP_LEFT: 'TOP_LEFT'>, color_lookup: supervision.annotators.utils.ColorLookup = <ColorLookup.CLASS: 'class'>, border_radius: int = 0, smart_position: bool = False)
327    def __init__(
328        self,
329        color=ColorPalette.DEFAULT,
330        text_color=Color.WHITE,
331        text_scale: float = 0.5,
332        text_thickness: int = 1,
333        text_padding: int = 10,
334        text_position: Position = Position.TOP_LEFT,
335        color_lookup: ColorLookup = ColorLookup.CLASS,
336        border_radius: int = 0,
337        smart_position: bool = False,
338    ):
339        self.border_radius: int = border_radius
340        self.color = color
341        self.text_color = text_color
342        self.text_scale: float = text_scale
343        self.text_thickness: int = text_thickness
344        self.text_padding: int = text_padding
345        self.text_anchor: Position = text_position
346        self.color_lookup: ColorLookup = color_lookup
347        self.smart_position = smart_position
border_radius: int
color
text_color
text_scale: float
text_thickness: int
text_padding: int
text_anchor: supervision.geometry.core.Position
color_lookup: supervision.annotators.utils.ColorLookup
smart_position
def annotate( self, scene, detections: supervision.detection.core.Detections, labels, custom_color_lookup=None):
349    def annotate(
350        self,
351        scene,
352        detections: Detections,
353        labels,
354        custom_color_lookup=None,
355    ):
356        self._validate_labels(labels, detections)
357
358        labels = self._get_labels_text(detections, labels)
359        label_properties = self._get_label_properties(detections, labels)
360
361        if self.smart_position:
362            xyxy = label_properties[:, :4]
363            xyxy = spread_out_boxes(xyxy)
364            label_properties[:, :4] = xyxy
365
366        self._draw_labels(
367            scene=scene,
368            labels=labels,
369            label_properties=label_properties,
370            detections=detections,
371            custom_color_lookup=custom_color_lookup,
372        )
373
374        return scene
@staticmethod
def draw_rounded_rectangle(scene: numpy.ndarray, xyxy, color, border_radius: int) -> numpy.ndarray:
491    @staticmethod
492    def draw_rounded_rectangle(
493        scene: np.ndarray,
494        xyxy,
495        color,
496        border_radius: int,
497    ) -> np.ndarray:
498        x1, y1, x2, y2 = xyxy
499        width = x2 - x1
500        height = y2 - y1
501
502        border_radius = min(border_radius, min(width, height) // 2)
503
504        if border_radius <= 0:
505            vf_cv2.rectangle(
506                img=scene,
507                pt1=(x1, y1),
508                pt2=(x2, y2),
509                color=color,
510                thickness=-1,
511            )
512        else:
513            rectangle_coordinates = [
514                ((x1 + border_radius, y1), (x2 - border_radius, y2)),
515                ((x1, y1 + border_radius), (x2, y2 - border_radius)),
516            ]
517            circle_centers = [
518                (x1 + border_radius, y1 + border_radius),
519                (x2 - border_radius, y1 + border_radius),
520                (x1 + border_radius, y2 - border_radius),
521                (x2 - border_radius, y2 - border_radius),
522            ]
523
524            for coordinates in rectangle_coordinates:
525                vf_cv2.rectangle(
526                    img=scene,
527                    pt1=coordinates[0],
528                    pt2=coordinates[1],
529                    color=color,
530                    thickness=-1,
531                )
532            for center in circle_centers:
533                vf_cv2.circle(
534                    img=scene,
535                    center=center,
536                    radius=border_radius,
537                    color=color,
538                    thickness=-1,
539                )
540        return scene