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

Code before:
            (x1 + border_radius, y2 - border_radius),
            (x2 - border_radius, y2 - border_radius),
        ]

        for coordinates in rectangle_coordinates:
            cv2.rectangle(
                img=scene,
                pt1=coordinates[0],
                pt2=coordinates[1],
                color=color,
                thickness=-1,
            )
        for center in circle_centers:
            cv2.circle(
                img=scene,
                center=center,
                radius=border_radius,
                color=color,
                thickness=-1,
            )
        return scene


class BlurAnnotator(BaseAnnotator):
    """
    A class for blurring regions in an image using provided detections.
    """

    def __init__(self, kernel_size: int = 15):
        """
        Args:
            kernel_size (int): The size of the average pooling kernel used for blurring.
        """
        self.kernel_size: int = kernel_size

    @convert_for_annotation_method
    def annotate(
        self,
        scene: ImageType,
        detections: Detections,
    ) -> ImageType:
        """
        Annotates the given scene by blurring regions based on the provided detections.

        Args:
            scene (ImageType): The image where blurring will be applied.
                `ImageType` is a flexible type, accepting either `numpy.ndarray`
                or `PIL.Image.Image`.
            detections (Detections): Object detections to annotate.

        Returns:

Review comment (suggestion):
I see why you skipped the `@convert_for_annotation_method` annotator - you need PIL images here.

What it does is allows user to use either PIL and np.ndarray images, ensuring that only np.ndarray images reach the annotator.

1. Let's leave a comment here to prevent future confusion. `# Needs PIL images. Should not use standard annotator decorator`. Maybe there's a more concise way or better phrasing - that'd be even better.
2. Since it's the only annotator needing PIL, let's implement the conversions in `annotate`, rather than in a decorator. Before converting to PIL, store the datatype in a variable. Before returning, if a numpy array was originally passed as the argument, convert the result to an array.

Code after:
            font_path (str): Path to the font file (e.g., ".ttf" or ".otf") to use for
                rendering text. If `None`, the default PIL font will be used.
            font_size (int): Font size for the text.
            text_padding (int): Padding around the text within its background box.
            text_position (Position): Position of the text relative to the detection.
                Possible values are defined in the `Position` enum.
            color_lookup (ColorLookup): Strategy for mapping colors to annotations.
                Options are `INDEX`, `CLASS`, `TRACK`.
            border_radius (int): The radius to apply round edges. If the selected
                value is higher than the lower dimension, width or height, is clipped.
        """
        self.color = color
        self.text_color = text_color
        self.text_padding = text_padding
        self.text_anchor = text_position
        self.color_lookup = color_lookup
        self.border_radius = border_radius
        if font_path is not None:
            try:
                self.font = ImageFont.truetype(font_path, font_size)
            except OSError:
                print(f"Font path '{font_path}' not found. Using PIL's default font.")
                self.font = ImageFont.load_default(size=font_size)
        else:
            self.font = ImageFont.load_default(size=font_size)

    def annotate(
        self,
        scene: ImageType,
        detections: Detections,
        labels: List[str] = None,
        custom_color_lookup: Optional[np.ndarray] = None,
    ) -> ImageType:
        """
        Annotates the given scene with labels based on the provided
        detections, with support for Unicode characters.

        Args:
            scene (ImageType): The image where labels will be drawn.
                `ImageType` is a flexible type, accepting either `numpy.ndarray`
                or `PIL.Image.Image`.
            detections (Detections): Object detections to annotate.
            labels (List[str]): Optional. Custom labels for each detection.
            custom_color_lookup (Optional[np.ndarray]): Custom color lookup array.
                Allows to override the default color mapping strategy.

        Returns:
            The annotated image, matching the type of `scene` (`numpy.ndarray`
                or `PIL.Image.Image`)

        Example: