From 90a26747b6d6266344fb47c9f20b436b2fc716ef Mon Sep 17 00:00:00 2001 From: James Gallagher Date: Wed, 1 Feb 2023 10:28:49 +0000 Subject: [PATCH 1/3] create stub doc files, refactor docstrings, add placeholders for examples --- docs/draw.md | 9 +++++ docs/geometry.md | 0 docs/index.md | 15 ++++---- docs/notebook.md | 3 ++ docs/tools.md | 9 +++++ mkdocs.yml | 8 +++-- supervision/draw/utils.py | 48 ++++++++++++++++++++------ supervision/notebook/utils.py | 13 +++++-- supervision/tools/detections.py | 57 +++++++++++++++++++------------ supervision/tools/line_counter.py | 39 ++++++++++++++------- 10 files changed, 144 insertions(+), 57 deletions(-) create mode 100644 docs/draw.md create mode 100644 docs/geometry.md create mode 100644 docs/notebook.md create mode 100644 docs/tools.md diff --git a/docs/draw.md b/docs/draw.md new file mode 100644 index 00000000..8ba499b1 --- /dev/null +++ b/docs/draw.md @@ -0,0 +1,9 @@ +Utilities for drawing on images. + +## Draw Line + +:::supervision.draw.utils.draw_line + +## Draw Rectangle + +:::supervision.draw.utils.draw_rectangle \ No newline at end of file diff --git a/docs/geometry.md b/docs/geometry.md new file mode 100644 index 00000000..e69de29b diff --git a/docs/index.md b/docs/index.md index 3f1233af..092d38f5 100644 --- a/docs/index.md +++ b/docs/index.md @@ -9,21 +9,22 @@

-## 👋 hello +## 👋 Welcome -A set of easy-to-use utils that will come in handy in any Computer Vision project. **Supervision** is still in -pre-release stage. 🚧 Keep your eyes open for potential bugs and be aware that at this stage our API is still fluid -and may change. +Supervision is a set of easy-to-use utilities that will come in handy in any computer vision project. -## 💻 install +**Supervision** is still in +pre-release stage 🚧 Keep your eyes open for potential bugs and be aware that at this stage our API is still fluid and may change. -Pip install the supervision package in a +## 💻 How to Install + +You can install `supervision` with pip in a [**3.10>=Python>=3.7**](https://www.python.org/) environment. !!! example "Pip install method (recommended)" ```bash - pip install subervision + pip install supervision ``` !!! example "Git clone method (for development)" diff --git a/docs/notebook.md b/docs/notebook.md new file mode 100644 index 00000000..ce650120 --- /dev/null +++ b/docs/notebook.md @@ -0,0 +1,3 @@ +Utilities to help you build computer vision projects in notebook environments. + +:::supervision.notebook.utils.show_frame_in_notebook \ No newline at end of file diff --git a/docs/tools.md b/docs/tools.md new file mode 100644 index 00000000..9274cb0e --- /dev/null +++ b/docs/tools.md @@ -0,0 +1,9 @@ +Useful utilities for common computer vision tasks. + +## Helper for Processing Model Detections + +:::supervision.tools.detections.Detections + +## Count Objects That Pass a Line + +:::supervision.tools.line_counter.LineCounter \ No newline at end of file diff --git a/mkdocs.yml b/mkdocs.yml index 46c257de..cbbcac7a 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -19,8 +19,12 @@ extra: link: https://twitter.com/roboflow nav: - - Home: index.md - - Video: video.md + - Home 🏠: index.md + - Video 📷: video.md + - Notebook Helpers 📓: notebook.md + - Draw 🎨: draw.md + - Geometry 📐: geometry.md + - Tools 🛠: tools.md theme: name: 'material' diff --git a/supervision/draw/utils.py b/supervision/draw/utils.py index 306e3ef5..ff9e60b8 100644 --- a/supervision/draw/utils.py +++ b/supervision/draw/utils.py @@ -11,12 +11,16 @@ def draw_line( """ Draws a line on a given scene. - :param scene: np.ndarray : The scene on which the line will be drawn - :param start: Point : The starting point of the line - :param end: Point : The end point of the line - :param color: Color : The color of the line - :param thickness: int : The thickness of the line - :return: np.ndarray : The scene with the line drawn on it + Attributes: + + scene (np.ndarray): The scene on which the line will be drawn + start (Point): The starting point of the line + end (Point): The end point of the line + color (Color): The color of the line + thickness (int): The thickness of the line + + Returns: + np.ndarray: The scene with the line drawn on it """ cv2.line( scene, @@ -34,11 +38,19 @@ def draw_rectangle( """ Draws a rectangle on an image. - :param scene: np.ndarray : The image on which to draw the rectangle. - :param rect: Rect : The rectangle to draw. - :param color: Color : The color of the rectangle. - :param thickness: int : The thickness of the rectangle border. - :return: np.ndarray : The image with the rectangle drawn on it. + Attributes: + scene (np.ndarray): The scene on which the rectangle will be drawn + rect (Rect): The rectangle to be drawn + color (Color): The color of the rectangle + thickness (int): The thickness of the rectangle border + + Returns: + np.ndarray: The scene with the rectangle drawn on it + + Example: + ```python + >>> # TODO: Add example + ``` """ cv2.rectangle( scene, @@ -58,6 +70,20 @@ def draw_filled_rectangle(scene: np.ndarray, rect: Rect, color: Color) -> np.nda :param rect: Rect : The rectangle to be drawn. :param color: Color : The color of the rectangle. :return: np.ndarray : The updated scene with the filled rectangle drawn on it. + + Attributes: + scene (np.ndarray): The scene on which the rectangle will be drawn + rect (Rect): The rectangle to be drawn + color (Color): The color of the rectangle + + Returns: + np.ndarray: The scene with the rectangle drawn on it + + Example: + ```python + >>> # TODO: Add example + ``` + """ cv2.rectangle( scene, diff --git a/supervision/notebook/utils.py b/supervision/notebook/utils.py index 2afc3a63..c50c9842 100644 --- a/supervision/notebook/utils.py +++ b/supervision/notebook/utils.py @@ -11,9 +11,16 @@ def show_frame_in_notebook( """ Display a frame in Jupyter Notebook using Matplotlib - :param frame: np.ndarray : The frame to be displayed. - :param size: Tuple[int, int] : The size of the plot. default:(10,10) - :param cmap: str : the colormap to use for single channel images. default:gray + Attributes: + frame (np.ndarray): The frame to be displayed. + size (Tuple[int, int]): The size of the plot. default:(10,10) + cmap (str): the colormap to use for single channel images. default:gray + + Examples: + ```python + >>> from supervision.notebook import show_frame_in_notebook + + ``` """ if frame.ndim == 2: plt.figure(figsize=size) diff --git a/supervision/tools/detections.py b/supervision/tools/detections.py index 2cf54867..72a8edd8 100644 --- a/supervision/tools/detections.py +++ b/supervision/tools/detections.py @@ -17,10 +17,11 @@ class Detections: """ Data class containing information about the detections in a video frame. - :param xyxy: np.ndarray : An array of shape (n, 4) containing the bounding boxes coordinates in format [x1, y1, x2, y2] - :param confidence: np.ndarray : An array of shape (n,) containing the confidence scores of the detections. - :param class_id: np.ndarray : An array of shape (n,) containing the class ids of the detections. - :param tracker_id: Optional[np.ndarray] : An array of shape (n,) containing the tracker ids of the detections. + Attributes: + xyxy (np.ndarray): An array of shape (n, 4) containing the bounding boxes coordinates in format [x1, y1, x2, y2] + confidence (np.ndarray): An array of shape (n,) containing the confidence scores of the detections. + class_id (np.ndarray): An array of shape (n,) containing the class ids of the detections. + tracker_id (Optional[np.ndarray]): An array of shape (n,) containing the tracker ids of the detections. """ self.xyxy: np.ndarray = xyxy self.confidence: np.ndarray = confidence @@ -69,11 +70,17 @@ class Detections: """ Creates a Detections instance from a YOLOv5 output tensor - :param yolov5_output: np.ndarray : The output tensor from YOLOv5 - :return: Detections : A Detections instance representing the detections in the frame + Attributes: + yolov5_output (np.ndarray): The output tensor from YOLOv5 + + Returns: Example: - detections = Detections.from_yolov5(yolov5_output) + ```python + >>> from supervision.tools.detections import Detections + + >>> detections = Detections.from_yolov5(yolov5_output) + ``` """ xyxy = yolov5_output[:, :4] confidence = yolov5_output[:, 4] @@ -82,11 +89,14 @@ class Detections: def filter(self, mask: np.ndarray, inplace: bool = False) -> Optional[np.ndarray]: """ - Filter the detections by applying a mask + Filter the detections by applying a mask. - :param mask: np.ndarray : A mask of shape (n,) containing a boolean value for each detection indicating if it should be included in the filtered detections - :param inplace: bool : If True, the original data will be modified and self will be returned. - :return: Optional[np.ndarray] : A new instance of Detections with the filtered detections, if inplace is set to False. None otherwise. + Attributes: + mask (np.ndarray): A mask of shape (n,) containing a boolean value for each detection indicating if it should be included in the filtered detections + inplace (bool): If True, the original data will be modified and self will be returned. + + Returns: + Optional[np.ndarray]: A new instance of Detections with the filtered detections, if inplace is set to False. None otherwise. """ if inplace: self.xyxy = self.xyxy[mask] @@ -120,12 +130,14 @@ class BoxAnnotator: """ A class for drawing bounding boxes on an image using detections provided. - :param color: Union[Color, ColorPalette] : The color to draw the bounding box, can be a single color or a color palette - :param thickness: int : The thickness of the bounding box lines, default is 2 - :param text_color: Color : The color of the text on the bounding box, default is white - :param text_scale: float : The scale of the text on the bounding box, default is 0.5 - :param text_thickness: int : The thickness of the text on the bounding box, default is 1 - :param text_padding: int : The padding around the text on the bounding box, default is 5 + Attributes: + color (Union[Color, ColorPalette]): The color to draw the bounding box, can be a single color or a color palette + thickness (int): The thickness of the bounding box lines, default is 2 + text_color (Color): The color of the text on the bounding box, default is white + text_scale (float): The scale of the text on the bounding box, default is 0.5 + text_thickness (int): The thickness of the text on the bounding box, default is 1 + text_padding (int): The padding around the text on the bounding box, default is 5 + """ self.color: Union[Color, ColorPalette] = color self.thickness: int = thickness @@ -142,11 +154,14 @@ class BoxAnnotator: ) -> np.ndarray: """ Draws bounding boxes on the frame using the detections provided. + + Attributes: + frame (np.ndarray): The image on which the bounding boxes will be drawn + detections (Detections): The detections for which the bounding boxes will be drawn + labels (Optional[List[str]]): An optional list of labels corresponding to each detection. If labels is provided, the confidence score of the detection will be replaced with the label. - :param frame: np.ndarray : The image on which the bounding boxes will be drawn - :param detections: Detections : The detections for which the bounding boxes will be drawn - :param labels: Optional[List[str]] : An optional list of labels corresponding to each detection. If labels is provided, the confidence score of the detection will be replaced with the label. - :return: np.ndarray : The image with the bounding boxes drawn on it + Returns: + np.ndarray: The image with the bounding boxes drawn on it """ font = cv2.FONT_HERSHEY_SIMPLEX for i, (xyxy, confidence, class_id, tracker_id) in enumerate(detections): diff --git a/supervision/tools/line_counter.py b/supervision/tools/line_counter.py index cfa3ee6d..93a4e0c5 100644 --- a/supervision/tools/line_counter.py +++ b/supervision/tools/line_counter.py @@ -9,12 +9,17 @@ from supervision.tools.detections import Detections class LineCounter: + """ + Count the number of objects that cross a line. + """ def __init__(self, start: Point, end: Point): """ Initialize a LineCounter object. - :param start: Point : The starting point of the line. - :param end: Point : The ending point of the line. + Attributes: + start (Point): The starting point of the line. + end (Point): The ending point of the line. + """ self.vector = Vector(start=start, end=end) self.tracker_state: Dict[str, bool] = {} @@ -25,7 +30,9 @@ class LineCounter: """ Update the in_count and out_count for the detections that cross the line. - :param detections: Detections : The detections for which to update the counts. + Attributes: + detections (Detections): The detections for which to update the counts. + """ for xyxy, confidence, class_id, tracker_id in detections: # handle detections with no tracker_id @@ -77,13 +84,15 @@ class LineCounterAnnotator: """ Initialize the LineCounterAnnotator object with default values. - :param thickness: float : The thickness of the line that will be drawn. - :param color: Color : The color of the line that will be drawn. - :param text_thickness: float : The thickness of the text that will be drawn. - :param text_color: Color : The color of the text that will be drawn. - :param text_scale: float : The scale of the text that will be drawn. - :param text_offset: float : The offset of the text that will be drawn. - :param text_padding: int : The padding of the text that will be drawn. + Attributes: + thickness (float): The thickness of the line that will be drawn. + color (Color): The color of the line that will be drawn. + text_thickness (float): The thickness of the text that will be drawn. + text_color (Color): The color of the text that will be drawn. + text_scale (float): The scale of the text that will be drawn. + text_offset (float): The offset of the text that will be drawn. + text_padding (int): The padding of the text that will be drawn. + """ self.thickness: float = thickness self.color: Color = color @@ -97,9 +106,13 @@ class LineCounterAnnotator: """ Draws the line on the frame using the line_counter provided. - :param frame: np.ndarray : The image on which the line will be drawn - :param line_counter: LineCounter : The line counter that will be used to draw the line - :return: np.ndarray : The image with the line drawn on it + Attributes: + frame (np.ndarray): The image on which the line will be drawn. + line_counter (LineCounter): The line counter that will be used to draw the line. + + Returns: + np.ndarray: The image with the line drawn on it. + """ cv2.line( frame, From 61a2f4e0e6c0e54b0fd35daff31ff8e86f613912 Mon Sep 17 00:00:00 2001 From: James Gallagher Date: Wed, 1 Feb 2023 10:29:00 +0000 Subject: [PATCH 2/3] add requirements.txt --- requirements.txt | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 requirements.txt diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 00000000..4b9058af --- /dev/null +++ b/requirements.txt @@ -0,0 +1,30 @@ +certifi==2022.12.7 +charset-normalizer==3.0.1 +click==8.1.3 +colorama==0.4.6 +ghp-import==2.1.0 +griffe==0.25.4 +idna==3.4 +importlib-metadata==6.0.0 +Jinja2==3.1.2 +Markdown==3.3.7 +MarkupSafe==2.1.2 +mergedeep==1.3.4 +mkdocs==1.4.2 +mkdocs-autorefs==0.4.1 +mkdocs-material==9.0.9 +mkdocs-material-extensions==1.1.1 +mkdocstrings==0.20.0 +mkdocstrings-python==0.8.3 +packaging==23.0 +Pygments==2.14.0 +pymdown-extensions==9.9.2 +python-dateutil==2.8.2 +PyYAML==6.0 +pyyaml_env_tag==0.1 +regex==2022.10.31 +requests==2.28.2 +six==1.16.0 +urllib3==1.26.14 +watchdog==2.2.1 +zipp==3.12.0 From 4fbec12b06ee24abf35c71de9f995f472860651d Mon Sep 17 00:00:00 2001 From: James Gallagher Date: Wed, 1 Feb 2023 14:37:54 +0000 Subject: [PATCH 3/3] run linter --- supervision/draw/utils.py | 2 +- supervision/tools/detections.py | 6 +++--- supervision/tools/line_counter.py | 5 +++-- test/draw/test_color.py | 34 +++++++++++++++---------------- test/geometry/test_dataclasses.py | 15 +++----------- 5 files changed, 26 insertions(+), 36 deletions(-) diff --git a/supervision/draw/utils.py b/supervision/draw/utils.py index ff9e60b8..ff6c050c 100644 --- a/supervision/draw/utils.py +++ b/supervision/draw/utils.py @@ -83,7 +83,7 @@ def draw_filled_rectangle(scene: np.ndarray, rect: Rect, color: Color) -> np.nda ```python >>> # TODO: Add example ``` - + """ cv2.rectangle( scene, diff --git a/supervision/tools/detections.py b/supervision/tools/detections.py index 72a8edd8..4faaf6cf 100644 --- a/supervision/tools/detections.py +++ b/supervision/tools/detections.py @@ -72,13 +72,13 @@ class Detections: Attributes: yolov5_output (np.ndarray): The output tensor from YOLOv5 - + Returns: Example: ```python >>> from supervision.tools.detections import Detections - + >>> detections = Detections.from_yolov5(yolov5_output) ``` """ @@ -154,7 +154,7 @@ class BoxAnnotator: ) -> np.ndarray: """ Draws bounding boxes on the frame using the detections provided. - + Attributes: frame (np.ndarray): The image on which the bounding boxes will be drawn detections (Detections): The detections for which the bounding boxes will be drawn diff --git a/supervision/tools/line_counter.py b/supervision/tools/line_counter.py index 93a4e0c5..f791c9a6 100644 --- a/supervision/tools/line_counter.py +++ b/supervision/tools/line_counter.py @@ -12,6 +12,7 @@ class LineCounter: """ Count the number of objects that cross a line. """ + def __init__(self, start: Point, end: Point): """ Initialize a LineCounter object. @@ -92,7 +93,7 @@ class LineCounterAnnotator: text_scale (float): The scale of the text that will be drawn. text_offset (float): The offset of the text that will be drawn. text_padding (int): The padding of the text that will be drawn. - + """ self.thickness: float = thickness self.color: Color = color @@ -112,7 +113,7 @@ class LineCounterAnnotator: Returns: np.ndarray: The image with the line drawn on it. - + """ cv2.line( frame, diff --git a/test/draw/test_color.py b/test/draw/test_color.py index 5ea48abb..723f7043 100644 --- a/test/draw/test_color.py +++ b/test/draw/test_color.py @@ -7,27 +7,25 @@ from supervision.draw.color import Color @pytest.mark.parametrize( - 'color_hex, expected_result, exception', + "color_hex, expected_result, exception", [ - ('fff', Color.white(), DoesNotRaise()), - ('#fff', Color.white(), DoesNotRaise()), - ('ffffff', Color.white(), DoesNotRaise()), - ('#ffffff', Color.white(), DoesNotRaise()), - ('f00', Color.red(), DoesNotRaise()), - ('0f0', Color.green(), DoesNotRaise()), - ('00f', Color.blue(), DoesNotRaise()), - ('#808000', Color(r=128, g=128, b=0), DoesNotRaise()), - ('', None, pytest.raises(ValueError)), - ('00', None, pytest.raises(ValueError)), - ('0000', None, pytest.raises(ValueError)), - ('0000000', None, pytest.raises(ValueError)), - ('ffg', None, pytest.raises(ValueError)), - ] + ("fff", Color.white(), DoesNotRaise()), + ("#fff", Color.white(), DoesNotRaise()), + ("ffffff", Color.white(), DoesNotRaise()), + ("#ffffff", Color.white(), DoesNotRaise()), + ("f00", Color.red(), DoesNotRaise()), + ("0f0", Color.green(), DoesNotRaise()), + ("00f", Color.blue(), DoesNotRaise()), + ("#808000", Color(r=128, g=128, b=0), DoesNotRaise()), + ("", None, pytest.raises(ValueError)), + ("00", None, pytest.raises(ValueError)), + ("0000", None, pytest.raises(ValueError)), + ("0000000", None, pytest.raises(ValueError)), + ("ffg", None, pytest.raises(ValueError)), + ], ) def test_color_from_hex( - color_hex, - expected_result: Optional[Color], - exception: Exception + color_hex, expected_result: Optional[Color], exception: Exception ) -> None: with exception: result = Color.from_hex(color_hex=color_hex) diff --git a/test/geometry/test_dataclasses.py b/test/geometry/test_dataclasses.py index 5e829877..34821884 100644 --- a/test/geometry/test_dataclasses.py +++ b/test/geometry/test_dataclasses.py @@ -4,37 +4,28 @@ from supervision.geometry.dataclasses import Vector, Point @pytest.mark.parametrize( - 'vector, point, expected_result', + "vector, point, expected_result", [ (Vector(start=Point(x=0, y=0), end=Point(x=5, y=5)), Point(x=-1, y=1), False), (Vector(start=Point(x=0, y=0), end=Point(x=5, y=5)), Point(x=6, y=6), False), (Vector(start=Point(x=0, y=0), end=Point(x=5, y=5)), Point(x=3, y=6), False), - (Vector(start=Point(x=5, y=5), end=Point(x=0, y=0)), Point(x=-1, y=1), True), (Vector(start=Point(x=5, y=5), end=Point(x=0, y=0)), Point(x=6, y=6), False), (Vector(start=Point(x=5, y=5), end=Point(x=0, y=0)), Point(x=3, y=6), True), - (Vector(start=Point(x=0, y=0), end=Point(x=1, y=0)), Point(x=0, y=0), False), (Vector(start=Point(x=0, y=0), end=Point(x=1, y=0)), Point(x=0, y=-1), True), (Vector(start=Point(x=0, y=0), end=Point(x=1, y=0)), Point(x=0, y=1), False), - (Vector(start=Point(x=1, y=0), end=Point(x=0, y=0)), Point(x=0, y=0), False), (Vector(start=Point(x=1, y=0), end=Point(x=0, y=0)), Point(x=0, y=-1), False), (Vector(start=Point(x=1, y=0), end=Point(x=0, y=0)), Point(x=0, y=1), True), - (Vector(start=Point(x=1, y=1), end=Point(x=1, y=3)), Point(x=0, y=0), False), (Vector(start=Point(x=1, y=1), end=Point(x=1, y=3)), Point(x=1, y=4), False), (Vector(start=Point(x=1, y=1), end=Point(x=1, y=3)), Point(x=2, y=4), True), - (Vector(start=Point(x=1, y=3), end=Point(x=1, y=1)), Point(x=0, y=0), True), (Vector(start=Point(x=1, y=3), end=Point(x=1, y=1)), Point(x=1, y=4), False), (Vector(start=Point(x=1, y=3), end=Point(x=1, y=1)), Point(x=2, y=4), False), - ] + ], ) -def test_vector_is_in( - vector: Vector, - point: Point, - expected_result: bool -) -> None: +def test_vector_is_in(vector: Vector, point: Point, expected_result: bool) -> None: result = vector.is_in(point=point) assert result == expected_result