Repository: roboflow/supervision
File: supervision/annotators/core.py (Python)

Code before:
            blur_annotator = sv.BlurAnnotator()
            annotated_frame = circle_annotator.annotate(
                scene=image.copy(),
                detections=detections
            )
            ```

        ![blur-annotator-example](https://media.roboflow.com/
        supervision-annotator-examples/blur-annotator-example-purple.png)
        """
        image_height, image_width = scene.shape[:2]
        clipped_xyxy = clip_boxes(
            xyxy=detections.xyxy, resolution_wh=(image_width, image_height)
        ).astype(int)

        for x1, y1, x2, y2 in clipped_xyxy:
            roi = scene[y1:y2, x1:x2]
            roi = cv2.blur(roi, (self.kernel_size, self.kernel_size))
            scene[y1:y2, x1:x2] = roi

        return scene


class TraceAnnotator:
    """
    A class for drawing trace paths on an image based on detection coordinates.

    !!! warning

        This annotator uses the `sv.Detections.tracker_id`. Read
        [here](/latest/trackers/) to learn how to plug
        tracking into your inference pipeline.
    """

    def __init__(
        self,
        color: Union[Color, ColorPalette] = ColorPalette.DEFAULT,
        position: Position = Position.CENTER,
        trace_length: int = 30,
        thickness: int = 2,
        color_lookup: ColorLookup = ColorLookup.CLASS,
    ):
        """
        Args:
            color (Union[Color, ColorPalette]): The color to draw the trace, can be
                a single color or a color palette.
            position (Position): The position of the trace.
                Defaults to `CENTER`.
            trace_length (int): The maximum length of the trace in terms of historical
                points. Defaults to `30`.
            thickness (int): The thickness of the trace lines. Defaults to `2`.

Review comment (bug):
It's nice when libraries tell what exactly is wrong :) 

Still, we can make it more concise. We can remove this:

```
 This discrepancy can occur if the labels and "
f"detections are not aligned or if an incorrect number of labels has "
f"been provided. Please ensure that the labels array has the same "
f"length as the Detections object.
```

Code after:
            ]

            rich_label_annotator = sv.RichLabelAnnotator(font_path="path/to/font.ttf")
            annotated_frame = label_annotator.annotate(
                scene=image.copy(),
                detections=detections,
                labels=labels
            )
            ```

        """
        if isinstance(scene, np.ndarray):
            scene = Image.fromarray(cv2.cvtColor(scene, cv2.COLOR_BGR2RGB))
        draw = ImageDraw.Draw(scene)
        anchors_coordinates = detections.get_anchors_coordinates(
            anchor=self.text_anchor
        ).astype(int)
        if labels is not None and len(labels) != len(detections):
            raise ValueError(
                f"The number of labels provided ({len(labels)}) does not match the "
                f"number of detections ({len(detections)}). Each detection should have "
                f"a corresponding label. This discrepancy can occur if the labels and "
                f"detections are not aligned or if an incorrect number of labels has "
                f"been provided. Please ensure that the labels array has the same "
                f"length as the Detections object."
            )
        for detection_idx, center_coordinates in enumerate(anchors_coordinates):
            color = resolve_color(
                color=self.color,
                detections=detections,
                detection_idx=detection_idx,
                color_lookup=(
                    self.color_lookup
                    if custom_color_lookup is None
                    else custom_color_lookup
                ),
            )
            if labels is not None:
                text = labels[detection_idx]
            elif detections[CLASS_NAME_DATA_FIELD] is not None:
                text = detections[CLASS_NAME_DATA_FIELD][detection_idx]
            elif detections.class_id is not None:
                text = str(detections.class_id[detection_idx])
            else:
                text = str(detection_idx)

            left, top, right, bottom = draw.textbbox((0, 0), text, font=self.font)
            text_width = right - left
            text_height = bottom - top
            text_w_padded = text_width + 2 * self.text_padding
            text_h_padded = text_height + 2 * self.text_padding