diff --git a/supervision/annotators/core.py b/supervision/annotators/core.py index 1c4a2527..822ef9f9 100644 --- a/supervision/annotators/core.py +++ b/supervision/annotators/core.py @@ -21,7 +21,7 @@ class BoundingBoxAnnotator(BaseAnnotator): def __init__( self, - color: Union[Color, ColorPalette] = ColorPalette.default(), + color: Union[Color, ColorPalette] = ColorPalette.DEFAULT, thickness: int = 2, color_lookup: ColorLookup = ColorLookup.CLASS, ): @@ -103,7 +103,7 @@ class MaskAnnotator(BaseAnnotator): def __init__( self, - color: Union[Color, ColorPalette] = ColorPalette.default(), + color: Union[Color, ColorPalette] = ColorPalette.DEFAULT, opacity: float = 0.5, color_lookup: ColorLookup = ColorLookup.CLASS, ): @@ -186,7 +186,7 @@ class PolygonAnnotator(BaseAnnotator): def __init__( self, - color: Union[Color, ColorPalette] = ColorPalette.default(), + color: Union[Color, ColorPalette] = ColorPalette.DEFAULT, thickness: int = 2, color_lookup: ColorLookup = ColorLookup.CLASS, ): @@ -268,7 +268,7 @@ class ColorAnnotator(BaseAnnotator): def __init__( self, - color: Union[Color, ColorPalette] = ColorPalette.default(), + color: Union[Color, ColorPalette] = ColorPalette.DEFAULT, opacity: float = 0.5, color_lookup: ColorLookup = ColorLookup.CLASS, ): @@ -354,7 +354,7 @@ class HaloAnnotator(BaseAnnotator): def __init__( self, - color: Union[Color, ColorPalette] = ColorPalette.default(), + color: Union[Color, ColorPalette] = ColorPalette.DEFAULT, opacity: float = 0.8, kernel_size: int = 40, color_lookup: ColorLookup = ColorLookup.CLASS, @@ -446,7 +446,7 @@ class EllipseAnnotator(BaseAnnotator): def __init__( self, - color: Union[Color, ColorPalette] = ColorPalette.default(), + color: Union[Color, ColorPalette] = ColorPalette.DEFAULT, thickness: int = 2, start_angle: int = -45, end_angle: int = 235, @@ -536,7 +536,7 @@ class BoxCornerAnnotator(BaseAnnotator): def __init__( self, - color: Union[Color, ColorPalette] = ColorPalette.default(), + color: Union[Color, ColorPalette] = ColorPalette.DEFAULT, thickness: int = 4, corner_length: int = 15, color_lookup: ColorLookup = ColorLookup.CLASS, @@ -622,7 +622,7 @@ class CircleAnnotator(BaseAnnotator): def __init__( self, - color: Union[Color, ColorPalette] = ColorPalette.default(), + color: Union[Color, ColorPalette] = ColorPalette.DEFAULT, thickness: int = 2, color_lookup: ColorLookup = ColorLookup.CLASS, ): @@ -706,7 +706,7 @@ class DotAnnotator(BaseAnnotator): def __init__( self, - color: Union[Color, ColorPalette] = ColorPalette.default(), + color: Union[Color, ColorPalette] = ColorPalette.DEFAULT, radius: int = 4, position: Position = Position.CENTER, color_lookup: ColorLookup = ColorLookup.CLASS, @@ -782,7 +782,7 @@ class LabelAnnotator: def __init__( self, - color: Union[Color, ColorPalette] = ColorPalette.default(), + color: Union[Color, ColorPalette] = ColorPalette.DEFAULT, text_color: Color = Color.BLACK, text_scale: float = 0.5, text_thickness: int = 1, @@ -1027,7 +1027,7 @@ class TraceAnnotator: def __init__( self, - color: Union[Color, ColorPalette] = ColorPalette.default(), + color: Union[Color, ColorPalette] = ColorPalette.DEFAULT, position: Position = Position.CENTER, trace_length: int = 30, thickness: int = 2, @@ -1288,7 +1288,7 @@ class TriangleAnnotator(BaseAnnotator): def __init__( self, - color: Union[Color, ColorPalette] = ColorPalette.default(), + color: Union[Color, ColorPalette] = ColorPalette.DEFAULT, base: int = 10, height: int = 10, position: Position = Position.TOP_CENTER, @@ -1378,7 +1378,7 @@ class RoundBoxAnnotator(BaseAnnotator): def __init__( self, - color: Union[Color, ColorPalette] = ColorPalette.default(), + color: Union[Color, ColorPalette] = ColorPalette.DEFAULT, thickness: int = 2, color_lookup: ColorLookup = ColorLookup.CLASS, roundness: float = 0.6, @@ -1507,7 +1507,7 @@ class PercentageBarAnnotator(BaseAnnotator): self, height: int = 16, width: int = 80, - color: Union[Color, ColorPalette] = ColorPalette.default(), + color: Union[Color, ColorPalette] = ColorPalette.DEFAULT, border_color: Color = Color.black(), position: Position = Position.TOP_CENTER, color_lookup: ColorLookup = ColorLookup.CLASS, diff --git a/supervision/detection/annotate.py b/supervision/detection/annotate.py index 1ab1bdc2..f3c80653 100644 --- a/supervision/detection/annotate.py +++ b/supervision/detection/annotate.py @@ -26,7 +26,7 @@ class BoxAnnotator: def __init__( self, - color: Union[Color, ColorPalette] = ColorPalette.default(), + color: Union[Color, ColorPalette] = ColorPalette.DEFAULT, thickness: int = 2, text_color: Color = Color.BLACK, text_scale: float = 0.5, diff --git a/supervision/draw/color.py b/supervision/draw/color.py index fe55b25c..b592e660 100644 --- a/supervision/draw/color.py +++ b/supervision/draw/color.py @@ -3,28 +3,56 @@ from __future__ import annotations from dataclasses import dataclass from typing import List, Tuple -from supervision.utils.internal import deprecated +import matplotlib.pyplot as plt + +from supervision.utils.internal import classproperty, deprecated DEFAULT_COLOR_PALETTE = [ - "#a351fb", - "#e6194b", - "#3cb44b", - "#ffe119", - "#0082c8", - "#f58231", - "#911eb4", - "#46f0f0", - "#f032e6", - "#d2f53c", - "#fabebe", - "#008080", - "#e6beff", - "#aa6e28", - "#fffac8", - "#800000", - "#aaffc3", + "A351FB", + "FF4040", + "FFA1A0", + "FF7633", + "FFB633", + "D1D435", + "4CFB12", + "94CF1A", + "40DE8A", + "1B9640", + "00D6C1", + "2E9CAA", + "00C4FF", + "364797", + "6675FF", + "0019EF", + "863AFF", + "530087", + "CD3AFF", + "FF97CA", + "FF39C9", ] +LEGACY_COLOR_PALETTE = [ + "#A351FB", + "#E6194B", + "#3CB44B", + "#FFE119", + "#0082C8", + "#F58231", + "#911EB4", + "#46F0F0", + "#F032E6", + "#D2F53C", + "#FABEBE", + "#008080", + "#E6BEFF", + "#AA6E28", + "#FFFAC8", + "#800000", + "#AAFFC3", +] + +ROBOFLOW_COLOR_PALETTE = ["C28DFC", "A351FB", "8315F9", "6706CE", "5905B3", "4D049A"] + def _validate_color_hex(color_hex: str): color_hex = color_hex.lstrip("#") @@ -39,10 +67,31 @@ class Color: """ Represents a color in RGB format. + This class provides methods to work with colors, including creating colors from hex + codes, converting colors to hex strings, RGB tuples, and BGR tuples. + Attributes: - r (int): Red channel. - g (int): Green channel. - b (int): Blue channel. + r (int): Red channel value (0-255). + g (int): Green channel value (0-255). + b (int): Blue channel value (0-255). + + Example: + ```python + import supervision as sv + + sv.Color.WHITE + # Color(r=255, g=255, b=255) + ``` + + | Constant | Hex Code | RGB | + |------------|------------|------------------| + | `WHITE` | `#FFFFFF` | `(255, 255, 255)`| + | `BLACK` | `#000000` | `(0, 0, 0)` | + | `RED` | `#FF0000` | `(255, 0, 0)` | + | `GREEN` | `#00FF00` | `(0, 255, 0)` | + | `BLUE` | `#0000FF` | `(0, 0, 255)` | + | `YELLOW` | `#FFFF00` | `(255, 255, 0)` | + | `ROBOFLOW` | `#A351FB` | `(163, 81, 251)` | """ r: int @@ -61,9 +110,11 @@ class Color: Color: Instance representing the color. Example: - ``` - >>> Color.from_hex('#ff00ff') - Color(r=255, g=0, b=255) + ```python + import supervision as sv + + sv.Color.from_hex('#ff00ff') + # Color(r=255, g=0, b=255) ``` """ _validate_color_hex(color_hex) @@ -81,9 +132,11 @@ class Color: str: The hexadecimal color string. Example: - ``` - >>> Color(r=255, g=0, b=255).as_hex() - '#ff00ff' + ```python + import supervision as sv + + sv.Color(r=255, g=255, b=0).as_hex() + # '#ffff00' ``` """ return f"#{self.r:02x}{self.g:02x}{self.b:02x}" @@ -96,9 +149,11 @@ class Color: Tuple[int, int, int]: RGB tuple. Example: - ``` - >>> color.as_rgb() - (255, 0, 255) + ```python + import supervision as sv + + sv.Color(r=255, g=255, b=0).as_rgb() + # (255, 255, 0) ``` """ return self.r, self.g, self.b @@ -111,32 +166,42 @@ class Color: Tuple[int, int, int]: BGR tuple. Example: - ``` - >>> color.as_bgr() - (255, 0, 255) + ```python + import supervision as sv + + sv.Color(r=255, g=255, b=0).as_bgr() + # (0, 255, 255) ``` """ return self.b, self.g, self.r - @property + @classproperty def WHITE(cls): - return cls.from_hex("#ffffff") + return Color.from_hex("#FFFFFF") - @property + @classproperty def BLACK(cls): - return cls.from_hex("#000000") + return Color.from_hex("#000000") - @property + @classproperty def RED(cls): - return cls.from_hex("#ff0000") + return Color.from_hex("#FF0000") - @property + @classproperty def GREEN(cls): - return cls.from_hex("#00ff00") + return Color.from_hex("#00FF00") - @property + @classproperty def BLUE(cls): - return cls.from_hex("#0000ff") + return Color.from_hex("#0000FF") + + @classproperty + def YELLOW(cls): + return Color.from_hex("#FFFF00") + + @classproperty + def ROBOFLOW(cls): + return Color.from_hex("#A351FB") @classmethod @deprecated( @@ -183,7 +248,57 @@ class Color: class ColorPalette: colors: List[Color] + @classproperty + def DEFAULT(cls) -> ColorPalette: + """ + Returns a default color palette. + + Returns: + ColorPalette: A ColorPalette instance with default colors. + + Example: + ```python + import supervision as sv + + sv.ColorPalette.DEFAULT + # ColorPalette(colors=[Color(r=255, g=64, b=64), Color(r=255, g=161, b=160), ...]) + ``` + + ![default-color-palette](https://media.roboflow.com/ + supervision-annotator-examples/default-color-palette.png) + """ # noqa: E501 // docs + return ColorPalette.from_hex(color_hex_list=DEFAULT_COLOR_PALETTE) + + @classproperty + def ROBOFLOW(cls) -> ColorPalette: + """ + Returns a Roboflow color palette. + + Returns: + ColorPalette: A ColorPalette instance with Roboflow colors. + + Example: + ```python + import supervision as sv + + sv.ColorPalette.ROBOFLOW + # ColorPalette(colors=[Color(r=194, g=141, b=252), Color(r=163, g=81, b=251), ...]) + ``` + + ![roboflow-color-palette](https://media.roboflow.com/ + supervision-annotator-examples/roboflow-color-palette.png) + """ # noqa: E501 // docs + return ColorPalette.from_hex(color_hex_list=ROBOFLOW_COLOR_PALETTE) + + @classproperty + def LEGACY(cls) -> ColorPalette: + return ColorPalette.from_hex(color_hex_list=LEGACY_COLOR_PALETTE) + @classmethod + @deprecated( + "`ColorPalette.default()` is deprecated and will be removed in " + "`supervision-0.20.0`. Use `Color.DEFAULT` instead." + ) def default(cls) -> ColorPalette: """ Returns a default color palette. @@ -192,11 +307,13 @@ class ColorPalette: ColorPalette: A ColorPalette instance with default colors. Example: + ```python + import supervision as sv + + sv.ColorPalette.default() + # ColorPalette(colors=[Color(r=255, g=64, b=64), Color(r=255, g=161, b=160), ...]) ``` - >>> ColorPalette.default() - ColorPalette(colors=[Color(r=255, g=0, b=0), Color(r=0, g=255, b=0), ...]) - ``` - """ + """ # noqa: E501 // docs return ColorPalette.from_hex(color_hex_list=DEFAULT_COLOR_PALETTE) @classmethod @@ -211,14 +328,46 @@ class ColorPalette: ColorPalette: A ColorPalette instance. Example: - ``` - >>> ColorPalette.from_hex(['#ff0000', '#00ff00', '#0000ff']) - ColorPalette(colors=[Color(r=255, g=0, b=0), Color(r=0, g=255, b=0), ...]) + ```python + import supervision as sv + + sv.ColorPalette.from_hex(['#ff0000', '#00ff00', '#0000ff']) + # ColorPalette(colors=[Color(r=255, g=0, b=0), Color(r=0, g=255, b=0), ...]) ``` """ colors = [Color.from_hex(color_hex) for color_hex in color_hex_list] return cls(colors) + @classmethod + def from_matplotlib(cls, palette_name: str, color_count: int) -> ColorPalette: + """ + Create a ColorPalette instance from a Matplotlib color palette. + + Args: + palette_name (str): Name of the Matplotlib palette. + color_count (int): Number of colors to sample from the palette. + + Returns: + ColorPalette: A ColorPalette instance. + + Example: + ```python + import supervision as sv + + sv.ColorPalette.from_matplotlib('viridis', 5) + # ColorPalette(colors=[Color(r=68, g=1, b=84), Color(r=59, g=82, b=139), ...]) + ``` + + ![visualized_color_palette](https://media.roboflow.com/ + supervision-annotator-examples/visualized_color_palette.png) + """ # noqa: E501 // docs + mpl_palette = plt.get_cmap(palette_name, color_count) + colors = [ + Color(int(r * 255), int(g * 255), int(b * 255)) + for r, g, b, _ in mpl_palette.colors + ] + return cls(colors) + def by_idx(self, idx: int) -> Color: """ Return the color at a given index in the palette. @@ -230,19 +379,15 @@ class ColorPalette: Color: Color at the given index. Example: - ``` - >>> color_palette.by_idx(1) - Color(r=0, g=255, b=0) + ```python + import supervision as sv + + color_palette = sv.ColorPalette.from_hex(['#ff0000', '#00ff00', '#0000ff']) + color_palette.by_idx(1) + # Color(r=0, g=255, b=0) ``` """ if idx < 0: raise ValueError("idx argument should not be negative") idx = idx % len(self.colors) return self.colors[idx] - - -Color.WHITE = Color.from_hex("#ffffff") -Color.BLACK = Color.from_hex("#000000") -Color.RED = Color.from_hex("#ff0000") -Color.GREEN = Color.from_hex("#00ff00") -Color.BLUE = Color.from_hex("#0000ff") diff --git a/supervision/utils/internal.py b/supervision/utils/internal.py index eb84588e..031f5be1 100644 --- a/supervision/utils/internal.py +++ b/supervision/utils/internal.py @@ -16,3 +16,29 @@ def deprecated(reason: str): return wrapper return decorator + + +class classproperty(property): + """ + A decorator that combines @classmethod and @property. + It allows a method to be accessed as a property of the class, + rather than an instance, similar to a classmethod. + + Usage: + @classproperty + def my_method(cls): + ... + """ + + def __get__(self, owner_self: object, owner_cls: type) -> object: + """ + Override the __get__ method to return the result of the function call. + + Args: + owner_self: The instance through which the attribute was accessed, or None. + owner_cls: The class through which the attribute was accessed. + + Returns: + The result of calling the function stored in 'fget' with 'owner_cls'. + """ + return self.fget(owner_cls)