@@ -307,6 +307,9 @@ def annotate(self, scene: np.ndarray, detections: Detections) -> np.ndarray:
307307
308308
309309class LabelAnnotator :
310+ """
311+ A class for annotating labels on an image using provided detections.
312+ """
310313 def __init__ (
311314 self ,
312315 color : Union [Color , ColorPalette ] = ColorPalette .default (),
@@ -317,6 +320,19 @@ def __init__(
317320 text_position : Position = Position .TOP_LEFT ,
318321 color_map : str = "class" ,
319322 ):
323+ """
324+ Args:
325+ color (Union[Color, ColorPalette]): The color or color palette to use for
326+ annotating the text background.
327+ text_color (Color): The color to use for the text.
328+ text_scale (float): Font scale for the text.
329+ text_thickness (int): Thickness of the text characters.
330+ text_padding (int): Padding around the text within its background box.
331+ text_position (Position): Position of the text relative to the detection.
332+ Possible values are defined in the `Position` enum.
333+ color_map (str): Strategy for mapping colors to annotations.
334+ Options are `index`, `class`, or `track`.
335+ """
320336 self .color : Union [Color , ColorPalette ] = color
321337 self .text_color : Color = text_color
322338 self .text_scale : float = text_scale
@@ -367,6 +383,31 @@ def annotate(
367383 detections : Detections ,
368384 labels : List [str ] = None ,
369385 ) -> np .ndarray :
386+ """
387+ Annotates the given scene with labels based on the provided detections.
388+
389+ Args:
390+ scene (np.ndarray): The image where labels will be drawn.
391+ detections (Detections): Object detections to annotate.
392+ labels (List[str]): Optional. Custom labels for each detection.
393+
394+ Returns:
395+ np.ndarray: The annotated image.
396+
397+ Example:
398+ ```python
399+ >>> import supervision as sv
400+
401+ >>> image = ...
402+ >>> detections = sv.Detections(...)
403+
404+ >>> label_annotator = sv.LabelAnnotator()
405+ >>> annotated_frame = label_annotator.annotate(
406+ ... scene=image.copy(),
407+ ... detections=detections
408+ ... )
409+ ```
410+ """
370411 font = cv2 .FONT_HERSHEY_SIMPLEX
371412 for detection_idx in range (len (detections )):
372413 detection_xyxy = detections .xyxy [detection_idx ].astype (int )
@@ -416,213 +457,3 @@ def annotate(
416457 lineType = cv2 .LINE_AA ,
417458 )
418459 return scene
419-
420-
421- class LabelAdvancedAnnotator (BaseAnnotator ):
422- def __init__ (
423- self ,
424- color : Union [Color , ColorPalette ] = ColorPalette .default (),
425- text_color : Color = Color .black (),
426- text_padding : int = 20 ,
427- color_by_track : bool = False ,
428- font : Optional [str ] = None ,
429- font_size : Optional [int ] = 15 ,
430- ):
431- if font and os .path .exists (font ):
432- self .font = ImageFont .truetype (font , font_size )
433- else :
434- self .font = ImageFont .load_default ()
435- self .color : Union [Color , ColorPalette ] = color
436- self .text_color : Color = text_color
437- self .text_padding : int = text_padding
438- self .color_by_track = color_by_track
439-
440- def annotate (
441- self ,
442- scene : np .ndarray ,
443- detections : Detections ,
444- labels : Optional [List [str ]] = None ,
445- ) -> np .ndarray :
446- """
447- Draws text on the frame using the detections provided and label.
448-
449- Args:
450- scene (np.ndarray): The image on which the bounding boxes will be drawn
451- detections (Detections): The detections for which
452- the bounding boxes will be drawn
453- labels (Optional[List[str]]): An optional list of labels corresponding
454- to each detection. If `labels` are not provided,
455- corresponding `class_id` will be used as label.
456- Returns:
457- np.ndarray: The image with the bounding boxes drawn on it
458-
459- Example:
460- ```python
461- >>> import supervision as sv
462-
463- >>> classes = ['person', ...]
464- >>> image = ...
465- >>> detections = sv.Detections(...)
466-
467- >>> pil_label_annotator = sv.LabelAdvancedAnnotator()
468- >>> labels = [
469- ... f"{classes[class_id]} {confidence:0.2f}"
470- ... for _, _, confidence, class_id, _
471- ... in detections
472- ... ]
473- >>> annotated_frame = pil_label_annotator.annotate(
474- ... scene=image.copy(),
475- ... detections=detections,
476- ... labels=labels,
477- ... )
478- ```
479- """
480- pil_image = Image .fromarray (scene )
481- draw = ImageDraw .Draw (pil_image )
482- text_color = "#fff"
483-
484- for i in range (len (detections )):
485- x1 , y1 , x2 , y2 = detections .xyxy [i ].astype (int )
486- if self .color_by_track :
487- tracker_id = (
488- detections .tracker_id [i ]
489- if detections .tracker_id is not None
490- else None
491- )
492- idx = tracker_id if tracker_id is not None else i
493- else :
494- class_id = (
495- detections .class_id [i ] if detections .class_id is not None else None
496- )
497- idx = class_id if class_id is not None else i
498-
499- color = (
500- self .color .by_idx (idx )
501- if isinstance (self .color , ColorPalette )
502- else self .color
503- )
504-
505- text = (
506- f"{ idx } "
507- if (labels is None or len (detections ) != len (labels ))
508- else labels [i ]
509- )
510-
511- text_bbox = draw .textbbox ((x1 , y1 ), text , font = self .font )
512-
513- text_height = text_bbox [3 ] - text_bbox [1 ]
514- text_width = text_bbox [2 ] - text_bbox [0 ]
515-
516- text_x = x1 + self .text_padding / 2
517- text_y = y1 - self .text_padding / 2 - text_height
518-
519- text_background_x1 = x1
520- text_background_y1 = y1 - self .text_padding / 2 - text_height
521-
522- text_background_x2 = x1 + 2 * self .text_padding / 2 + text_width
523- text_background_y2 = y1 # correct
524-
525- draw .rectangle (
526- (
527- text_background_x1 ,
528- text_background_y1 ,
529- text_background_x2 ,
530- text_background_y2 ,
531- ),
532- fill = color .as_bgr (),
533- )
534- draw .text ((text_x , text_y ), text , font = self .font , fill = text_color )
535-
536- scene = np .asarray (pil_image )
537- return scene
538-
539-
540- class TraceAnnotator (BaseAnnotator ):
541- """
542- A class for drawing trajectory of a tracker on an image using detections provided.
543-
544- Attributes:
545- color (Union[Color, ColorPalette]): The color to draw the trajectory,
546- can be a single color or a color palette
547- color_by_track (bool): Whther to use tracker id to pick the color
548- position (Optional[Position]): Choose position of trajectory such as
549- center position, top left corner, etc
550- trace_length (int): Length of the previous points
551- thickness (int): thickness of the line
552- """
553-
554- def __init__ (
555- self ,
556- color : Union [Color , ColorPalette ] = ColorPalette .default (),
557- color_by_track : bool = False ,
558- position : Optional [Position ] = Position .CENTER ,
559- trace_length : int = 30 ,
560- thickness : int = 2 ,
561- ):
562- self .color : Union [Color , ColorPalette ] = color
563- self .color_by_track = color_by_track
564- self .position = position
565- self .tracker_storage = defaultdict (lambda : [])
566- self .trace_length = trace_length
567- self .thickness = thickness
568-
569- def annotate (
570- self , scene : np .ndarray , detections : Detections , ** kwargs
571- ) -> np .ndarray :
572- """
573- Draw the object trajectory based on history of tracked objects
574-
575- Args:
576- scene (np.ndarray): The image on which the trace will be drawn
577- detections (Detections): The detections for trajectory and points
578-
579- Returns:
580- np.ndarray: The image with the masks overlaid
581- Example:
582- ```python
583- >>> import supervision as sv
584-
585- >>> classes = ['person', ...]
586- >>> image = ...
587- >>> detections = sv.Detections(...)
588-
589- >>> trace_annotator = sv.TraceAnnotator()
590- >>> annotated_frame = trace_annotator.annotate(
591- ... scene=image.copy(),
592- ... detections=detections
593- ... )
594- ```
595- """
596- if detections .tracker_id is None :
597- return scene
598-
599- anchor_points = detections .get_anchor_coordinates (anchor = self .position )
600-
601- for i , tracker_id in enumerate (detections .tracker_id ):
602- track = self .tracker_storage [tracker_id ]
603- track .append ((anchor_points [i ][0 ], anchor_points [i ][1 ]))
604- if len (track ) > self .trace_length :
605- track .pop (0 )
606- points = np .hstack (track ).astype (np .int32 ).reshape ((- 1 , 1 , 2 ))
607-
608- if self .color_by_track :
609- idx = tracker_id if tracker_id is not None else i
610- else :
611- class_id = (
612- detections .class_id [i ] if detections .class_id is not None else None
613- )
614- idx = class_id if class_id is not None else i
615- color = (
616- self .color .by_idx (idx )
617- if isinstance (self .color , ColorPalette )
618- else self .color
619- )
620- cv2 .polylines (
621- scene ,
622- [points ],
623- isClosed = False ,
624- color = color .as_bgr (),
625- thickness = self .thickness ,
626- )
627-
628- return scene
0 commit comments