Repository: roboflow/supervision

Issue:
Add annotator RoundBoundingBoxAnnotator which has rectangle with round edges
### Search before asking

- [X] I have searched the Supervision [issues](https://github.com/roboflow/supervision/issues) and found no similar feature requests.


### Description

I have created a new annotator class named `RoundBoundingBoxAnnotator` which draws bounding box with rounded edges. 

`RoundBoundingBoxAnnotator` has the same attributes with `BoundingBoxAnnotator` including one extra attribute `roundness` which accepts float values `0 < roundness <= 1.0`. Roundness attribute controls the percentage of edge roundness based each time on the lower length of width or height, for each object independently. 

You can see some examples with `roundness = 0.5 `and `roundess = 1.0` in the following screenshots.

` round_box_annotator = sv.RoundBoundingBoxAnnotator(thickness=2, roundess=0.5)`
![roundness_05](https://github.com/roboflow/supervision/assets/65918758/e01e81d0-404f-44a5-bd6a-59a927faa051)

` round_box_annotator = sv.RoundBoundingBoxAnnotator(thickness=2, roundess=1.0)`
![roundness_1](https://github.com/roboflow/supervision/assets/65918758/3844cf19-eaaf-4574-8e2b-a7f8ffdd0cb2)


I have created two different cases of `RoundBoundingBoxAnnotator`. The first one has the same `color` and `thickness` for edges and lines of the box. The second one can accept different values for `color` and `thickness`. You can view a demo video for each case below.

I think this annotator look cool and could be included in Supervision API. I can't decide which one looks better or if extra feature for different color and thickness of edges and lines is too much or not. Let me know your thoughts.



https://github.com/roboflow/supervision/assets/65918758/c5c21ec5-79b1-483b-9b66-36b098b11ee2




https://github.com/roboflow/supervision/assets/65918758/76150e2f-e743-41f7-bf3b-810bfbe4e0d9





### Use case

_No response_

### Additional

_No response_

### Are you willing to submit a PR?

- [X] Yes I'd like to help by submitting a PR!


Discussion:
Hello once again @xaristeidou 👋🏻 ! I like this idea very much. If you have the code ready, open PR I would love to see.

Resolving patch:
diff --git a/supervision/__init__.py b/supervision/__init__.py
--- a/supervision/__init__.py
+++ b/supervision/__init__.py
@@ -20,6 +20,7 @@
     MaskAnnotator,
     PixelateAnnotator,
     PolygonAnnotator,
+    RoundBoxAnnotator,
     TraceAnnotator,
     TriangleAnnotator,
 )
diff --git a/supervision/annotators/core.py b/supervision/annotators/core.py
--- a/supervision/annotators/core.py
+++ b/supervision/annotators/core.py
@@ -1368,3 +1368,131 @@ def annotate(
             cv2.fillPoly(scene, [vertices], color.as_bgr())
 
         return scene
+
+
+class RoundBoxAnnotator(BaseAnnotator):
+    """
+    A class for drawing bounding boxes with round edges on an image
+    using provided detections.
+    """
+
+    def __init__(
+        self,
+        color: Union[Color, ColorPalette] = ColorPalette.default(),
+        thickness: int = 2,
+        color_lookup: ColorLookup = ColorLookup.CLASS,
+        roundness: float = 0.6,
+    ):
+        """
+        Args:
+            color (Union[Color, ColorPalette]): The color or color palette to use for
+                annotating detections.
+            thickness (int): Thickness of the bounding box lines.
+            color_lookup (str): Strategy for mapping colors to annotations.
+                Options are `INDEX`, `CLASS`, `TRACK`.
+            roundness (float): Percent of roundness for edges of bounding box.
+                Value must be float 0 < roundness <= 1.0
+                By default roundness percent is calculated based on smaller side
+                length (width or height).
+        """
+        self.color: Union[Color, ColorPalette] = color
+        self.thickness: int = thickness
+        self.color_lookup: ColorLookup = color_lookup
+        if not 0 < roundness <= 1.0:
+            raise ValueError("roundness attribute must be float between (0, 1.0]")
+        self.roundness: float = roundness
+
+    def annotate(
+        self,
+        scene: np.ndarray,
+        detections: Detections,
+        custom_color_lookup: Optional[np.ndarray] = None,
+    ) -> np.ndarray:
+        """
+        Annotates the given scene with bounding boxes with rounded edges
+        based on the provided detections.
+
+        Args:
+            scene (np.ndarray): The image where rounded bounding boxes will be drawn.
+            detections (Detections): Object detections to annotate.
+            custom_color_lookup (Optional[np.ndarray]): Custom color lookup array.
+                Allows to override the default color mapping strategy.
+
+        Returns:
+            The annotated image.
+
+        Example:
+            ```python
+            >>> import supervision as sv
+
+            >>> image = ...
+            >>> detections = sv.Detections(...)
+
+            >>> round_box_annotator = sv.RoundBoxAnnotator()
+            >>> annotated_frame = round_box_annotator.annotate(
+            ...     scene=image.copy(),
+            ...     detections=detections
+            ... )
+            ```
+
+        ![round-box-annotator-example](https://media.roboflow.com/
+        supervision-annotator-examples/round-box-annotator-example.png)
+        """
+
+        for detection_idx in range(len(detections)):
+            x1, y1, x2, y2 = detections.xyxy[detection_idx].astype(int)
+            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,
+            )
+
+            radius = (
+                int((x2 - x1) // 2 * self.roundness)
+                if abs(x1 - x2) < abs(y1 - y2)
+                else int((y2 - y1) // 2 * self.roundness)
+            )
+
+            circle_coordinates = [
+                ((x1 + radius), (y1 + radius)),
+                ((x2 - radius), (y1 + radius)),
+                ((x2 - radius), (y2 - radius)),
+                ((x1 + radius), (y2 - radius)),
+            ]
+
+            line_coordinates = [
+                ((x1 + radius, y1), (x2 - radius, y1)),
+                ((x2, y1 + radius), (x2, y2 - radius)),
+                ((x1 + radius, y2), (x2 - radius, y2)),
+                ((x1, y1 + radius), (x1, y2 - radius)),
+            ]
+
+            start_angles = (180, 270, 0, 90)
+            end_angles = (270, 360, 90, 180)
+
+            for center_coordinates, line, start_angle, end_angle in zip(
+                circle_coordinates, line_coordinates, start_angles, end_angles
+            ):
+                cv2.ellipse(
+                    img=scene,
+                    center=center_coordinates,
+                    axes=(radius, radius),
+                    angle=0,
+                    startAngle=start_angle,
+                    endAngle=end_angle,
+                    color=color.as_bgr(),
+                    thickness=self.thickness,
+                )
+
+                cv2.line(
+                    img=scene,
+                    pt1=line[0],
+                    pt2=line[1],
+                    color=color.as_bgr(),
+                    thickness=self.thickness,
+                )
+
+        return scene


Test patch: