From 71164fd0c14131bdeb3660e50308c97410d89578 Mon Sep 17 00:00:00 2001 From: Juan Cruz Date: Mon, 5 Feb 2024 11:09:04 -0300 Subject: [PATCH 01/63] feat(LineZonaAnnotator): Add support for non-horizontal lines --- supervision/detection/line_counter.py | 268 ++++++++++++++++++++++++-- 1 file changed, 247 insertions(+), 21 deletions(-) diff --git a/supervision/detection/line_counter.py b/supervision/detection/line_counter.py index 8f09f219..ff6ef7a5 100644 --- a/supervision/detection/line_counter.py +++ b/supervision/detection/line_counter.py @@ -1,3 +1,4 @@ +import math from typing import Dict, Iterable, Optional, Tuple import cv2 @@ -173,6 +174,8 @@ class LineZoneAnnotator: text_scale: float = 0.5, text_offset: float = 1.5, text_padding: int = 10, + draw_text_box: bool = True, + draw_centered: bool = True, custom_in_text: Optional[str] = None, custom_out_text: Optional[str] = None, display_in_count: bool = True, @@ -189,6 +192,8 @@ class LineZoneAnnotator: 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. + draw_text_box (bool): Whether to draw a text box under the text or not. + draw_centered (bool): Wheter to draw the count centered in the line or not. display_in_count (bool): Whether to display the in count or not. display_out_count (bool): Whether to display the out count or not. @@ -200,45 +205,270 @@ class LineZoneAnnotator: self.text_scale: float = text_scale self.text_offset: float = text_offset self.text_padding: int = text_padding + self.draw_text_box: bool = draw_text_box + self.draw_centered: bool = draw_centered self.custom_in_text: str = custom_in_text self.custom_out_text: str = custom_out_text self.display_in_count: bool = display_in_count self.display_out_count: bool = display_out_count + def _get_line_angle(self, line_counter: LineZone) -> float: + """ + Calculate the line counter angle using trigonometry. + + Args: + line_counter (LineZone): The line counter object used to annotate. + + Returns: + float: Line counter angle. + """ + start_point = line_counter.vector.start.as_xy_int_tuple() + end_point = line_counter.vector.end.as_xy_int_tuple() + + delta_x = end_point[0] - start_point[0] + delta_y = end_point[1] - start_point[1] + + try: + line_angle = math.degrees(math.atan(delta_y / delta_x)) + line_angle += 180 if delta_x < 0 else 0 + except ZeroDivisionError: + # Add support for vertical lines. + line_angle = 90 + line_angle += 180 if delta_y < 0 else 0 + + return line_angle + + def _calculate_anchor_in_frame( + self, + line_counter: LineZone, + text_width: int, + text_height: int, + is_in_count: bool, + ) -> Point: + """ + Calculate insertion anchor in frame to position the center of the count image. + + Args: + line_counter (LineZone): The line counter object used for counting. + text_width (int): Text width. + text_height (int): Text height. + is_in_count (bool): Whether the count should be placed over or below line. + + Returns: + Point: xy insertion anchor to position count image in frame. + """ + line_angle = self._get_line_angle(line_counter) + + if self.draw_centered: + mid_point = Vector( + start=line_counter.vector.start, end=line_counter.vector.end + ).center.as_xy_int_tuple() + anchor = list(mid_point) + else: + end_point = line_counter.vector.end.as_xy_int_tuple() + anchor = list(end_point) + + move_along_x = int( + math.cos(math.radians(line_angle)) + * (text_width / 2 + self.text_padding) + ) + move_along_y = int( + math.sin(math.radians(line_angle)) + * (text_width / 2 + self.text_padding) + ) + + anchor[0] -= move_along_x + anchor[1] -= move_along_y + + move_perp_x = int( + math.sin(math.radians(line_angle)) * (self.text_offset * text_height) + ) + move_perp_y = int( + math.cos(math.radians(line_angle)) * (self.text_offset * text_height) + ) + + if is_in_count: + anchor[0] += move_perp_x + anchor[1] -= move_perp_y + else: + anchor[0] -= move_perp_x + anchor[1] += move_perp_y + + return Point(x=anchor[0], y=anchor[1]) + + def _calculate_xyxy_in_frame( + self, frame_dims: tuple, img_dim: int, anchor_in_frame: Point + ) -> tuple: + """ + Calculate insertion bbox in frame to position count image. + + Args: + frame_dims (int, int): Width and height of the frame. + img_dim (int): Width/height of squared count image. + anchor_in_frame (Point): xy insertion anchor to position image. + + Returns: + (int, int, int, int): xyxy insertion bbox to position count image. + """ + y1 = max(anchor_in_frame.y - img_dim // 2, 0) + y2 = min( + anchor_in_frame.y + img_dim // 2 + img_dim % 2, + frame_dims[0], + ) + x1 = max(anchor_in_frame.x - img_dim // 2, 0) + x2 = min( + anchor_in_frame.x + img_dim // 2 + img_dim % 2, + frame_dims[1], + ) + + return (x1, y1, x2, y2) + + def _rotate_img(self, img: np.ndarray, line_counter: LineZone) -> np.ndarray: + """ + Rotate count image to align text with the line counter. + + Attributes: + img (np.ndarray): Image to rotate. + line_counter (LineZone): The line counter object. + + Returns: + np.ndarray: Image with the same shape as the input with aligned text. + """ + line_angle = self._get_line_angle(line_counter) + + rotation_center = (img.shape[0] // 2, img.shape[0] // 2) + rotation_angle = -(line_angle) + rotation_scale = 1 + + rotation_matrix = cv2.getRotationMatrix2D( + rotation_center, rotation_angle, rotation_scale + ) + + img_rotated = cv2.warpAffine(img, rotation_matrix, (img.shape[1], img.shape[0])) + + return img_rotated + + def _crop_img(self, img, xyxy_in_frame) -> np.ndarray: + """ + Crop image to fit insertion bbox boundaries. + + Args: + img (np.ndarray): Image to crop. + xyxy_in_frame (list): xyxy insertion bbox used to crop image. + + Returns: + np.ndarray: Cropped image. + """ + img_dim = img.shape[0] + (x1, y1, x2, y2) = xyxy_in_frame + + if y2 - y1 != img_dim: + img = img[(img_dim - y2) :, ...] if y1 == 0 else img[: (y2 - y1), ...] + + if x2 - x1 != img_dim: + img = img[:, (img_dim - x2) :, ...] if x1 == 0 else img[:, : (x2 - x1), ...] + + return img + + def _annotate_img_in_frame( + self, frame: np.ndarray, img: np.ndarray, xyxy_in_frame: tuple + ) -> np.ndarray: + """ + Annotate count image in the original frame. + + Attributes: + frame (np.ndarray): The base image on which to insert the text-box image. + img (np.ndarray): Count image with bgr channels + alpha channel. + xyxy_in_frame (int, int, int, int): xyxy insertion bbox. + + Returns: + np.ndarray: Annotated frame. + """ + (x1, y1, x2, y2) = xyxy_in_frame + + # Paste count image and alpha in empty backgrounds with frame width and height. + img_in_frame = np.zeros_like(frame, dtype=np.uint8) + img_in_frame[y1:y2, x1:x2, ...] = img[:, :, :3] + alpha_in_frame = np.zeros_like(frame[:, :, 0], dtype=np.uint8) + alpha_in_frame[y1:y2, x1:x2] = img[:, :, 3] + + opacity = alpha_in_frame / 255 + for i in range(3): + frame[:, :, i] = frame[:, :, i] * (1 - opacity) + img_in_frame[:, :, i] + + return frame + def _annotate_count( self, frame: np.ndarray, - center_text_anchor: Point, + line_counter: LineZone, text: str, is_in_count: bool, - ) -> None: + ) -> np.ndarray: """This method is drawing the text on the frame. Args: frame (np.ndarray): The image on which the text will be drawn. - center_text_anchor: The center point that the text will be drawn. + line_counter (LineCounter): The line counter + that will be used to draw the line. text (str): The text that will be drawn. is_in_count (bool): Whether to display the in count or out count. + + Returns: + np.ndarray: The image with the count drawn on it. """ - _, text_height = cv2.getTextSize( + text_width, text_height = cv2.getTextSize( text, cv2.FONT_HERSHEY_SIMPLEX, self.text_scale, self.text_thickness )[0] - if is_in_count: - center_text_anchor.y -= int(self.text_offset * text_height) - else: - center_text_anchor.y += int(self.text_offset * text_height) + # Create an auxiliar squared image for the count and its alpha channel + image_dim = int((max(text_width, text_height) + self.text_padding * 2) * 1.5) + image = np.zeros((image_dim, image_dim, 3), dtype=np.uint8) # bgr + image_alpha = np.zeros((image_dim, image_dim, 1), dtype=np.uint8) # gray + text_args = { + "text": text, + "text_anchor": Point(image_dim // 2, image_dim // 2), + "text_scale": self.text_scale, + "text_thickness": self.text_thickness, + "text_padding": self.text_padding, + } draw_text( - scene=frame, - text=text, - text_anchor=center_text_anchor, + scene=image, text_color=self.text_color, - text_scale=self.text_scale, - text_thickness=self.text_thickness, - text_padding=self.text_padding, - background_color=self.color, + background_color=self.color if self.draw_text_box else None, + **text_args, ) + draw_text( + scene=image_alpha, + text_color=Color.WHITE, + background_color=Color.WHITE if self.draw_text_box else None, + **text_args, + ) + image = np.dstack((image, image_alpha)) # Stack bgr and alpha channels + + anchor_in_frame = self._calculate_anchor_in_frame( + line_counter=line_counter, + text_width=text_width, + text_height=text_height, + is_in_count=is_in_count, + ) + + xyxy_in_frame = self._calculate_xyxy_in_frame( + frame_dims=frame.shape[:2], + img_dim=image_dim, + anchor_in_frame=anchor_in_frame, + ) + + image_rotated = self._rotate_img(img=image, line_counter=line_counter) + + image_cropped = self._crop_img(img=image_rotated, xyxy_in_frame=xyxy_in_frame) + + frame = self._annotate_img_in_frame( + frame=frame, img=image_cropped, xyxy_in_frame=xyxy_in_frame + ) + + return frame def annotate(self, frame: np.ndarray, line_counter: LineZone) -> np.ndarray: """ @@ -279,10 +509,6 @@ class LineZoneAnnotator: lineType=cv2.LINE_AA, ) - text_anchor = Vector( - start=line_counter.vector.start, end=line_counter.vector.end - ) - if self.display_in_count: in_text = ( f"{self.custom_in_text}: {line_counter.in_count}" @@ -291,7 +517,7 @@ class LineZoneAnnotator: ) self._annotate_count( frame=frame, - center_text_anchor=text_anchor.center, + line_counter=line_counter, text=in_text, is_in_count=True, ) @@ -304,7 +530,7 @@ class LineZoneAnnotator: ) self._annotate_count( frame=frame, - center_text_anchor=text_anchor.center, + line_counter=line_counter, text=out_text, is_in_count=False, ) From 323e75844f1e5c50148914f012a215fade8e91b6 Mon Sep 17 00:00:00 2001 From: patel-zeel Date: Thu, 5 Sep 2024 12:00:10 +0530 Subject: [PATCH 02/63] Add `oriented_box_iou_batch` function to detection.utils --- supervision/detection/utils.py | 39 ++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/supervision/detection/utils.py b/supervision/detection/utils.py index 1a336ca7..e7d1de5a 100644 --- a/supervision/detection/utils.py +++ b/supervision/detection/utils.py @@ -140,6 +140,45 @@ def mask_iou_batch( return np.vstack(ious) +def oriented_box_iou_batch( + boxes_true: np.ndarray, boxes_detection: np.ndarray +) -> np.ndarray: + """ + Compute Intersection over Union (IoU) of two sets of oriented bounding boxes - + `boxes_true` and `boxes_detection`. Both sets + of boxes are expected to be in `(x1, y1, x2, y2, x3, y3, x4, y4)` format. + + Args: + boxes_true (np.ndarray): 2D `np.ndarray` representing ground-truth boxes. + `shape = (N, 8)` where `N` is number of true objects. + boxes_detection (np.ndarray): 2D `np.ndarray` representing detection boxes. + `shape = (M, 8)` where `M` is number of detected objects. + + Returns: + np.ndarray: Pairwise IoU of boxes from `boxes_true` and `boxes_detection`. + `shape = (N, M)` where `N` is number of true objects and + `M` is number of detected objects. + """ + + boxes_true = boxes_true.reshape(-1, 4, 2) + boxes_detection = boxes_detection.reshape(-1, 4, 2) + + max_height = max(boxes_true[:, :, 0].max(), boxes_detection[:, :, 0].max()) + 1 + # adding 1 because we are 0-indexed + max_width = max(boxes_true[:, :, 1].max(), boxes_detection[:, :, 1].max()) + 1 + + mask_true = np.zeros((boxes_true.shape[0], max_height, max_width)) + for i, box_true in enumerate(boxes_true): + mask_true[i] = polygon_to_mask(box_true, (max_width, max_height)) + + mask_detection = np.zeros((boxes_detection.shape[0], max_height, max_width)) + for i, box_detection in enumerate(boxes_detection): + mask_detection[i] = polygon_to_mask(box_detection, (max_width, max_height)) + + ious = mask_iou_batch(mask_true, mask_detection) + return ious + + def clip_boxes(xyxy: np.ndarray, resolution_wh: Tuple[int, int]) -> np.ndarray: """ Clips bounding boxes coordinates to fit within the frame resolution. From ef52dfff053b176f82142cf2cdf790ad3e9058e2 Mon Sep 17 00:00:00 2001 From: Onuralp SEZER Date: Sun, 15 Sep 2024 04:36:03 +0300 Subject: [PATCH 03/63] =?UTF-8?q?feat:=20=E2=9C=A8=20=20from=5Feasyocr=20d?= =?UTF-8?q?etection=20added?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Onuralp SEZER --- supervision/config.py | 1 + supervision/detection/core.py | 38 ++++++++++++++++++++++++++++++++++- 2 files changed, 38 insertions(+), 1 deletion(-) diff --git a/supervision/config.py b/supervision/config.py index b18d2e20..357ff2d4 100644 --- a/supervision/config.py +++ b/supervision/config.py @@ -1,2 +1,3 @@ CLASS_NAME_DATA_FIELD = "class_name" ORIENTED_BOX_COORDINATES = "xyxyxyxy" +TEXT_DATA_FIELD = "text_data" diff --git a/supervision/detection/core.py b/supervision/detection/core.py index 06995f90..54cae9a7 100644 --- a/supervision/detection/core.py +++ b/supervision/detection/core.py @@ -6,7 +6,11 @@ from typing import Any, Dict, Iterator, List, Optional, Tuple, Union import numpy as np -from supervision.config import CLASS_NAME_DATA_FIELD, ORIENTED_BOX_COORDINATES +from supervision.config import ( + CLASS_NAME_DATA_FIELD, + ORIENTED_BOX_COORDINATES, + TEXT_DATA_FIELD, +) from supervision.detection.lmm import ( LMM, from_florence_2, @@ -843,6 +847,38 @@ class Detections: raise ValueError(f"Unsupported LMM: {lmm}") + @classmethod + def from_easyocr(cls, easyocr_results: list) -> Detections: + """ + Create a Detections object from the + [EasyOCR](https://github.com/JaidedAI/EasyOCR) inference result. + + Args: + easyocr_results (List): The output Results instance from EasyOCR + + Returns: + Detections: A new Detections object. + + Example: + ```python + import supervision as sv + import easyocr + + reader = easyocr.Reader(['en']) + results = reader.readtext() + detections = sv.Detections.from_easyocr(results) + ``` + """ + bbox = np.array([result[0] for result in easyocr_results]) + xyxy = np.hstack((np.min(bbox, axis=1), np.max(bbox, axis=1))) + + return cls( + xyxy=xyxy, + confidence=np.array([result[2] for result in easyocr_results]), + class_id=np.arange(len(xyxy)), + data={TEXT_DATA_FIELD: np.array([result[1] for result in easyocr_results])}, + ) + @classmethod def empty(cls) -> Detections: """ From 6e07b174223cac702865adf30e1f2aeec36f13a2 Mon Sep 17 00:00:00 2001 From: Onuralp SEZER Date: Sun, 15 Sep 2024 05:26:43 +0300 Subject: [PATCH 04/63] =?UTF-8?q?feat:=20=E2=9C=A8=20=20from=5Feasyocr=20p?= =?UTF-8?q?aragraph=20mode=20support=20added?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Onuralp SEZER --- supervision/detection/core.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/supervision/detection/core.py b/supervision/detection/core.py index 54cae9a7..58d4c984 100644 --- a/supervision/detection/core.py +++ b/supervision/detection/core.py @@ -874,7 +874,12 @@ class Detections: return cls( xyxy=xyxy, - confidence=np.array([result[2] for result in easyocr_results]), + confidence=np.array( + [ + result[2] if len(result) > 2 and result[2] else 0 + for result in easyocr_results + ] + ), class_id=np.arange(len(xyxy)), data={TEXT_DATA_FIELD: np.array([result[1] for result in easyocr_results])}, ) From 685929fbd90a2f99cf4cd2f759ff666f75bbf733 Mon Sep 17 00:00:00 2001 From: Onuralp SEZER Date: Wed, 18 Sep 2024 22:29:19 +0300 Subject: [PATCH 05/63] =?UTF-8?q?feat:=20=E2=9C=A8=20initial=20ncnn=20dete?= =?UTF-8?q?ction=20support=20added?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Onuralp SEZER --- supervision/detection/core.py | 57 +++++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) diff --git a/supervision/detection/core.py b/supervision/detection/core.py index 06995f90..beab3e90 100644 --- a/supervision/detection/core.py +++ b/supervision/detection/core.py @@ -843,6 +843,63 @@ class Detections: raise ValueError(f"Unsupported LMM: {lmm}") + @classmethod + def from_ncnn(cls, ncnn_results) -> Detections: + """ + Creates a Detections instance from a + [ncnn](https://github.com/Tencent/ncnn) inference result. + + Args: + ncnn_results (dict): The output Results instance from ncnn. + + Returns: + Detections: A new Detections object. + + Example: + ```python + import cv2 + from ncnn.model_zoo import get_model + import supervision as sv + + image = cv2.imread() + net = get_model( + "yolov8s", + target_size=640 + prob_threshold=0.5, + nms_threshold=0.45, + num_threads=4, + use_gpu=True, + ) + result = net(image) + detections = sv.Detections.from_ncnn(result) + ``` + """ + + xywh, confidences, class_ids = [], [], [] + + if len(ncnn_results) > 0: + for ncnn_result in ncnn_results: + rect = ncnn_result.rect + xywh.append( + [ + rect.x.astype(np.int64), + rect.y.astype(np.int64), + rect.w.astype(np.int64), + rect.h.astype(np.int64), + ] + ) + + confidences.append(ncnn_result.prob) + class_ids.append(ncnn_result.label) + + return cls( + xyxy=xywh_to_xyxy(np.array(xywh)), + confidence=np.array(confidences), + class_id=np.array(class_ids, dtype=int), + ) + + return cls.empty() + @classmethod def empty(cls) -> Detections: """ From 1a870b9b1b87547dbd9c08615ba8c18bde28c111 Mon Sep 17 00:00:00 2001 From: Onuralp SEZER Date: Wed, 18 Sep 2024 22:44:07 +0300 Subject: [PATCH 06/63] =?UTF-8?q?fix:=20=F0=9F=90=9E=20empty=20image=20det?= =?UTF-8?q?ection=20error=20corrected=20refactor:=20=E2=99=BB=EF=B8=8F=20?= =?UTF-8?q?=20update=20text=20data=20field=20to=20CLASS=5FNAME=5FDATA=5FFI?= =?UTF-8?q?ELD=20in=20config=20and=20detection=20modules=20to=20reduce=20c?= =?UTF-8?q?omplexity=20of=20datafield=20names?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Onuralp SEZER --- supervision/config.py | 1 - supervision/detection/core.py | 10 ++++++++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/supervision/config.py b/supervision/config.py index 357ff2d4..b18d2e20 100644 --- a/supervision/config.py +++ b/supervision/config.py @@ -1,3 +1,2 @@ CLASS_NAME_DATA_FIELD = "class_name" ORIENTED_BOX_COORDINATES = "xyxyxyxy" -TEXT_DATA_FIELD = "text_data" diff --git a/supervision/detection/core.py b/supervision/detection/core.py index 58d4c984..ed2ab28c 100644 --- a/supervision/detection/core.py +++ b/supervision/detection/core.py @@ -9,7 +9,6 @@ import numpy as np from supervision.config import ( CLASS_NAME_DATA_FIELD, ORIENTED_BOX_COORDINATES, - TEXT_DATA_FIELD, ) from supervision.detection.lmm import ( LMM, @@ -869,6 +868,9 @@ class Detections: detections = sv.Detections.from_easyocr(results) ``` """ + if len(easyocr_results) == 0: + return cls.empty() + bbox = np.array([result[0] for result in easyocr_results]) xyxy = np.hstack((np.min(bbox, axis=1), np.max(bbox, axis=1))) @@ -881,7 +883,11 @@ class Detections: ] ), class_id=np.arange(len(xyxy)), - data={TEXT_DATA_FIELD: np.array([result[1] for result in easyocr_results])}, + data={ + CLASS_NAME_DATA_FIELD: np.array( + [result[1] for result in easyocr_results] + ) + }, ) @classmethod From 6acfdbf50288b6f3bbac4e12ddd19853e4a358a4 Mon Sep 17 00:00:00 2001 From: LinasKo Date: Thu, 19 Sep 2024 20:15:23 +0300 Subject: [PATCH 07/63] Add obb_iou_batch to __init__ and docs --- docs/detection/utils.md | 6 ++++++ supervision/__init__.py | 1 + 2 files changed, 7 insertions(+) diff --git a/docs/detection/utils.md b/docs/detection/utils.md index d2fd2a0c..25c84475 100644 --- a/docs/detection/utils.md +++ b/docs/detection/utils.md @@ -16,6 +16,12 @@ comments: true :::supervision.detection.utils.mask_iou_batch + + +:::supervision.detection.utils.oriented_box_iou_batch + diff --git a/supervision/__init__.py b/supervision/__init__.py index b3e8160a..e289ae97 100644 --- a/supervision/__init__.py +++ b/supervision/__init__.py @@ -65,6 +65,7 @@ from supervision.detection.utils import ( mask_to_xyxy, move_boxes, move_masks, + oriented_box_iou_batch, pad_boxes, polygon_to_mask, polygon_to_xyxy, From d85ceb0c2324ca396ff98a83903b516e67113df5 Mon Sep 17 00:00:00 2001 From: LinasKo Date: Fri, 20 Sep 2024 11:18:24 +0300 Subject: [PATCH 08/63] Fix incorrect types --- supervision/detection/line_zone.py | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/supervision/detection/line_zone.py b/supervision/detection/line_zone.py index 2b94c404..a2f6201d 100644 --- a/supervision/detection/line_zone.py +++ b/supervision/detection/line_zone.py @@ -1,6 +1,6 @@ import math import warnings -from typing import Dict, Iterable, Optional, Tuple +from typing import Any, Dict, Iterable, Optional, Tuple import cv2 import numpy as np @@ -200,9 +200,9 @@ class LineZone: class LineZoneAnnotator: def __init__( self, - thickness: float = 2, + thickness: int = 2, color: Color = Color.WHITE, - text_thickness: float = 2, + text_thickness: int = 2, text_color: Color = Color.BLACK, text_scale: float = 0.5, text_offset: float = 1.5, @@ -218,30 +218,32 @@ class LineZoneAnnotator: Initialize the LineCounterAnnotator object with default values. Attributes: - thickness (float): The thickness of the line that will be drawn. + thickness (int): 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_thickness (int): 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. draw_text_box (bool): Whether to draw a text box under the text or not. - draw_centered (bool): Wheter to draw the count centered in the line or not. + draw_centered (bool): Whether to draw the count centered in the line or not. + custom_in_text: (Optional[str]): Custom text to display for the in count. + custom_out_text: (Optional[str]): Custom text to display for the out count. display_in_count (bool): Whether to display the in count or not. display_out_count (bool): Whether to display the out count or not. """ - self.thickness: float = thickness + self.thickness: int = thickness self.color: Color = color - self.text_thickness: float = text_thickness + self.text_thickness: int = text_thickness self.text_color: Color = text_color self.text_scale: float = text_scale self.text_offset: float = text_offset self.text_padding: int = text_padding self.draw_text_box: bool = draw_text_box self.draw_centered: bool = draw_centered - self.custom_in_text: str = custom_in_text - self.custom_out_text: str = custom_out_text + self.custom_in_text: Optional[str] = custom_in_text + self.custom_out_text: Optional[str] = custom_out_text self.display_in_count: bool = display_in_count self.display_out_count: bool = display_out_count @@ -459,7 +461,7 @@ class LineZoneAnnotator: image = np.zeros((image_dim, image_dim, 3), dtype=np.uint8) # bgr image_alpha = np.zeros((image_dim, image_dim, 1), dtype=np.uint8) # gray - text_args = { + text_args: Dict[str, Any] = { "text": text, "text_anchor": Point(image_dim // 2, image_dim // 2), "text_scale": self.text_scale, From 3c79677039134173ed3850f1bfef1ef9ff322c0d Mon Sep 17 00:00:00 2001 From: LinasKo Date: Fri, 20 Sep 2024 12:28:52 +0300 Subject: [PATCH 09/63] Flip text if it is upside-down --- supervision/detection/line_zone.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/supervision/detection/line_zone.py b/supervision/detection/line_zone.py index a2f6201d..3e2791a2 100644 --- a/supervision/detection/line_zone.py +++ b/supervision/detection/line_zone.py @@ -255,7 +255,7 @@ class LineZoneAnnotator: line_counter (LineZone): The line counter object used to annotate. Returns: - float: Line counter angle. + float: Line counter angle, in degrees. """ start_point = line_counter.vector.start.as_xy_int_tuple() end_point = line_counter.vector.end.as_xy_int_tuple() @@ -370,9 +370,10 @@ class LineZoneAnnotator: np.ndarray: Image with the same shape as the input with aligned text. """ line_angle = self._get_line_angle(line_counter) + is_line_upside_down = not -90 <= line_angle <= 90 rotation_center = (img.shape[0] // 2, img.shape[0] // 2) - rotation_angle = -(line_angle) + rotation_angle = -(line_angle) + is_line_upside_down * 180 rotation_scale = 1 rotation_matrix = cv2.getRotationMatrix2D( From 6713523e9b4725f5c199b04e02964540cabfd038 Mon Sep 17 00:00:00 2001 From: LinasKo Date: Fri, 20 Sep 2024 13:12:07 +0300 Subject: [PATCH 10/63] Disable alignment to line by default, move arguments around --- supervision/detection/line_zone.py | 38 +++++++++++++++++------------- 1 file changed, 22 insertions(+), 16 deletions(-) diff --git a/supervision/detection/line_zone.py b/supervision/detection/line_zone.py index 3e2791a2..74142af2 100644 --- a/supervision/detection/line_zone.py +++ b/supervision/detection/line_zone.py @@ -207,15 +207,16 @@ class LineZoneAnnotator: text_scale: float = 0.5, text_offset: float = 1.5, text_padding: int = 10, - draw_text_box: bool = True, - draw_centered: bool = True, custom_in_text: Optional[str] = None, custom_out_text: Optional[str] = None, display_in_count: bool = True, display_out_count: bool = True, + display_text_box: bool = True, + text_orient_to_line: bool = False, + text_centered: bool = True, ): """ - Initialize the LineCounterAnnotator object with default values. + A class for drawing the LineZone and its detected object count on an image. Attributes: thickness (int): The thickness of the line that will be drawn. @@ -225,12 +226,14 @@ class LineZoneAnnotator: 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. - draw_text_box (bool): Whether to draw a text box under the text or not. - draw_centered (bool): Whether to draw the count centered in the line or not. + orient_text_to_line (bool): Whether to orient the text to the line or not. custom_in_text: (Optional[str]): Custom text to display for the in count. custom_out_text: (Optional[str]): Custom text to display for the out count. display_in_count (bool): Whether to display the in count or not. display_out_count (bool): Whether to display the out count or not. + display_text_box (bool): Whether to draw a text box under the text or not. + text_orient_to_line (bool): ⭐ Match text orientation to the line. + text_centered (bool): Whether to draw the count centered in the line or not. """ self.thickness: int = thickness @@ -240,12 +243,13 @@ class LineZoneAnnotator: self.text_scale: float = text_scale self.text_offset: float = text_offset self.text_padding: int = text_padding - self.draw_text_box: bool = draw_text_box - self.draw_centered: bool = draw_centered self.custom_in_text: Optional[str] = custom_in_text self.custom_out_text: Optional[str] = custom_out_text self.display_in_count: bool = display_in_count self.display_out_count: bool = display_out_count + self.display_text_box: bool = display_text_box + self.text_orient_to_line: bool = text_orient_to_line + self.text_centered: bool = text_centered def _get_line_angle(self, line_counter: LineZone) -> float: """ @@ -257,19 +261,21 @@ class LineZoneAnnotator: Returns: float: Line counter angle, in degrees. """ + if not self.text_orient_to_line: + return 0 + start_point = line_counter.vector.start.as_xy_int_tuple() end_point = line_counter.vector.end.as_xy_int_tuple() delta_x = end_point[0] - start_point[0] delta_y = end_point[1] - start_point[1] - try: + if delta_x == 0: + line_angle = 90.0 + line_angle += 180 if delta_y < 0 else 0 + else: line_angle = math.degrees(math.atan(delta_y / delta_x)) line_angle += 180 if delta_x < 0 else 0 - except ZeroDivisionError: - # Add support for vertical lines. - line_angle = 90 - line_angle += 180 if delta_y < 0 else 0 return line_angle @@ -294,7 +300,7 @@ class LineZoneAnnotator: """ line_angle = self._get_line_angle(line_counter) - if self.draw_centered: + if self.text_centered: mid_point = Vector( start=line_counter.vector.start, end=line_counter.vector.end ).center.as_xy_int_tuple() @@ -457,7 +463,7 @@ class LineZoneAnnotator: text, cv2.FONT_HERSHEY_SIMPLEX, self.text_scale, self.text_thickness )[0] - # Create an auxiliar squared image for the count and its alpha channel + # Create an auxiliary squared image for the count and its alpha channel image_dim = int((max(text_width, text_height) + self.text_padding * 2) * 1.5) image = np.zeros((image_dim, image_dim, 3), dtype=np.uint8) # bgr image_alpha = np.zeros((image_dim, image_dim, 1), dtype=np.uint8) # gray @@ -472,13 +478,13 @@ class LineZoneAnnotator: draw_text( scene=image, text_color=self.text_color, - background_color=self.color if self.draw_text_box else None, + background_color=self.color if self.display_text_box else None, **text_args, ) draw_text( scene=image_alpha, text_color=Color.WHITE, - background_color=Color.WHITE if self.draw_text_box else None, + background_color=Color.WHITE if self.display_text_box else None, **text_args, ) image = np.dstack((image, image_alpha)) # Stack bgr and alpha channels From 9a565133716726fae6f1fa2b0170b9034e9bc7a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o?= Date: Fri, 20 Sep 2024 11:02:30 -0300 Subject: [PATCH 11/63] adds basic workflow to try supervision annotators on annotators doc page --- docs/detection/annotators.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/detection/annotators.md b/docs/detection/annotators.md index 0e93d6f9..d4b86544 100644 --- a/docs/detection/annotators.md +++ b/docs/detection/annotators.md @@ -5,6 +5,10 @@ status: new # Annotators +Supervision provides a variety of annotators to annotate detections on images and videos. You can try them out below. + +
+ === "Box" ```python From 2759beb293fe6238161b09bd0eb8be2913ad9ce0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o?= Date: Fri, 20 Sep 2024 11:11:01 -0300 Subject: [PATCH 12/63] better copy --- docs/detection/annotators.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/detection/annotators.md b/docs/detection/annotators.md index d4b86544..b18512a9 100644 --- a/docs/detection/annotators.md +++ b/docs/detection/annotators.md @@ -5,7 +5,7 @@ status: new # Annotators -Supervision provides a variety of annotators to annotate detections on images and videos. You can try them out below. +Supervision provides a variety of annotators to annotate detections on images and videos. You can try them out below, with a Workflow that runs [Microsoft's COCO](https://cocodataset.org/#home) dataset through a Instance Segmentation model and annotates the detections using supervision's annotators.
From d2082501238ee5f6feca1713ed3b851a83605e66 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o?= Date: Fri, 20 Sep 2024 11:12:13 -0300 Subject: [PATCH 13/63] reduces border-radius --- docs/detection/annotators.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/detection/annotators.md b/docs/detection/annotators.md index b18512a9..60b8df5d 100644 --- a/docs/detection/annotators.md +++ b/docs/detection/annotators.md @@ -7,7 +7,7 @@ status: new Supervision provides a variety of annotators to annotate detections on images and videos. You can try them out below, with a Workflow that runs [Microsoft's COCO](https://cocodataset.org/#home) dataset through a Instance Segmentation model and annotates the detections using supervision's annotators. -
+
=== "Box" From fef9f10c03617b0b00f0a9d54799d3e72fd65998 Mon Sep 17 00:00:00 2001 From: Onuralp SEZER Date: Mon, 23 Sep 2024 12:48:49 +0300 Subject: [PATCH 14/63] =?UTF-8?q?fix:=20=F0=9F=90=9E=20ncnn=20xywh=20and?= =?UTF-8?q?=20conf=20types=20corrected=20to=20float32?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Onuralp SEZER --- supervision/detection/core.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/supervision/detection/core.py b/supervision/detection/core.py index beab3e90..2d30676f 100644 --- a/supervision/detection/core.py +++ b/supervision/detection/core.py @@ -882,10 +882,10 @@ class Detections: rect = ncnn_result.rect xywh.append( [ - rect.x.astype(np.int64), - rect.y.astype(np.int64), - rect.w.astype(np.int64), - rect.h.astype(np.int64), + rect.x.astype(np.float32), + rect.y.astype(np.float32), + rect.w.astype(np.float32), + rect.h.astype(np.float32), ] ) @@ -893,8 +893,8 @@ class Detections: class_ids.append(ncnn_result.label) return cls( - xyxy=xywh_to_xyxy(np.array(xywh)), - confidence=np.array(confidences), + xyxy=xywh_to_xyxy(np.array(xywh,dtype=np.float32)), + confidence=np.array(confidences,dtype=np.float32), class_id=np.array(class_ids, dtype=int), ) From 856c72eb67776b8a94c479763f7aebff91cb3048 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 23 Sep 2024 09:55:01 +0000 Subject: [PATCH 15/63] =?UTF-8?q?fix(pre=5Fcommit):=20=F0=9F=8E=A8=20auto?= =?UTF-8?q?=20format=20pre-commit=20hooks?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- supervision/detection/core.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/supervision/detection/core.py b/supervision/detection/core.py index 2d30676f..d9629624 100644 --- a/supervision/detection/core.py +++ b/supervision/detection/core.py @@ -893,8 +893,8 @@ class Detections: class_ids.append(ncnn_result.label) return cls( - xyxy=xywh_to_xyxy(np.array(xywh,dtype=np.float32)), - confidence=np.array(confidences,dtype=np.float32), + xyxy=xywh_to_xyxy(np.array(xywh, dtype=np.float32)), + confidence=np.array(confidences, dtype=np.float32), class_id=np.array(class_ids, dtype=int), ) From 504fc59b68def8bb65475cd5acebd3bc45da7b2a Mon Sep 17 00:00:00 2001 From: LinasKo Date: Mon, 23 Sep 2024 13:11:13 +0300 Subject: [PATCH 16/63] ncnn docstring change + sneaky from_transformers tidy-up --- supervision/detection/core.py | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/supervision/detection/core.py b/supervision/detection/core.py index d9629624..ae077c06 100644 --- a/supervision/detection/core.py +++ b/supervision/detection/core.py @@ -514,6 +514,13 @@ class Detections: **process_transformers_detection_result(transformers_results, id2label) ) + else: + raise ValueError( + "The provided Transformers results do not contain any valid fields." + " Expected fields are 'boxes', 'masks', 'segments_info' or" + " 'segmentation'." + ) + @classmethod def from_detectron2(cls, detectron2_results) -> Detections: """ @@ -846,8 +853,9 @@ class Detections: @classmethod def from_ncnn(cls, ncnn_results) -> Detections: """ - Creates a Detections instance from a + Creates a Detections instance from the [ncnn](https://github.com/Tencent/ncnn) inference result. + Supports object detection models. Args: ncnn_results (dict): The output Results instance from ncnn. @@ -862,7 +870,7 @@ class Detections: import supervision as sv image = cv2.imread() - net = get_model( + model = get_model( "yolov8s", target_size=640 prob_threshold=0.5, @@ -870,7 +878,7 @@ class Detections: num_threads=4, use_gpu=True, ) - result = net(image) + result = model(image) detections = sv.Detections.from_ncnn(result) ``` """ From 47a7f929a209ed8628a7442fdfc9a276c193f119 Mon Sep 17 00:00:00 2001 From: LinasKo Date: Mon, 23 Sep 2024 13:45:42 +0300 Subject: [PATCH 17/63] from_easy_ocr: cast results to float, remove class_id --- supervision/detection/core.py | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/supervision/detection/core.py b/supervision/detection/core.py index ed2ab28c..959f1d94 100644 --- a/supervision/detection/core.py +++ b/supervision/detection/core.py @@ -873,20 +873,19 @@ class Detections: bbox = np.array([result[0] for result in easyocr_results]) xyxy = np.hstack((np.min(bbox, axis=1), np.max(bbox, axis=1))) + confidence = np.array( + [ + result[2] if len(result) > 2 and result[2] else 0 + for result in easyocr_results + ] + ) + ocr_text = np.array([result[1] for result in easyocr_results]) return cls( - xyxy=xyxy, - confidence=np.array( - [ - result[2] if len(result) > 2 and result[2] else 0 - for result in easyocr_results - ] - ), - class_id=np.arange(len(xyxy)), + xyxy=xyxy.astype(np.float32), + confidence=confidence.astype(np.float32), data={ - CLASS_NAME_DATA_FIELD: np.array( - [result[1] for result in easyocr_results] - ) + CLASS_NAME_DATA_FIELD: ocr_text, }, ) From 270b5f564fdddcc9f9771416efd8a9590221db29 Mon Sep 17 00:00:00 2001 From: LinasKo Date: Mon, 23 Sep 2024 13:53:29 +0300 Subject: [PATCH 18/63] Update annotator errors, suggesting fixes --- supervision/annotators/utils.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/supervision/annotators/utils.py b/supervision/annotators/utils.py index 100b7874..ae4f8cdc 100644 --- a/supervision/annotators/utils.py +++ b/supervision/annotators/utils.py @@ -51,14 +51,17 @@ def resolve_color_idx( if detections.class_id is None: raise ValueError( "Could not resolve color by class because " - "Detections do not have class_id" + "Detections do not have class_id. If using an annotator, " + "try setting color_lookup to sv.ColorLookup.INDEX or " + "sv.ColorLookup.TRACK." ) return detections.class_id[detection_idx] elif color_lookup == ColorLookup.TRACK: if detections.tracker_id is None: raise ValueError( "Could not resolve color by track because " - "Detections do not have tracker_id" + "Detections do not have tracker_id. Did you call " + "tracker.update_with_detections(...) before annotating?" ) return detections.tracker_id[detection_idx] From d01e76a9c7f1f9003af8b7f9fc4722bcd646499c Mon Sep 17 00:00:00 2001 From: LinasKo Date: Mon, 23 Sep 2024 14:14:08 +0300 Subject: [PATCH 19/63] easy_ocr: docstring explaining usage --- supervision/detection/core.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/supervision/detection/core.py b/supervision/detection/core.py index 052717dc..9ed679dc 100644 --- a/supervision/detection/core.py +++ b/supervision/detection/core.py @@ -857,7 +857,9 @@ class Detections: def from_easyocr(cls, easyocr_results: list) -> Detections: """ Create a Detections object from the - [EasyOCR](https://github.com/JaidedAI/EasyOCR) inference result. + [EasyOCR](https://github.com/JaidedAI/EasyOCR) result. + + Results are placed in the `data` field with the key `"class_name"`. Args: easyocr_results (List): The output Results instance from EasyOCR @@ -873,6 +875,7 @@ class Detections: reader = easyocr.Reader(['en']) results = reader.readtext() detections = sv.Detections.from_easyocr(results) + detected_text = detections["class_name"] ``` """ if len(easyocr_results) == 0: From f221019208293b077254ebcd5d8d7700bec1add5 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 23 Sep 2024 11:15:35 +0000 Subject: [PATCH 20/63] =?UTF-8?q?fix(pre=5Fcommit):=20=F0=9F=8E=A8=20auto?= =?UTF-8?q?=20format=20pre-commit=20hooks?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- supervision/detection/core.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/supervision/detection/core.py b/supervision/detection/core.py index 9ed679dc..343050e9 100644 --- a/supervision/detection/core.py +++ b/supervision/detection/core.py @@ -858,7 +858,7 @@ class Detections: """ Create a Detections object from the [EasyOCR](https://github.com/JaidedAI/EasyOCR) result. - + Results are placed in the `data` field with the key `"class_name"`. Args: From 943a21412ead7eab2dd81302b3ad2142c0d9a396 Mon Sep 17 00:00:00 2001 From: LinasKo Date: Mon, 23 Sep 2024 14:22:39 +0300 Subject: [PATCH 21/63] fix regression: ncnn docs --- supervision/detection/core.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/supervision/detection/core.py b/supervision/detection/core.py index 343050e9..e1a2357e 100644 --- a/supervision/detection/core.py +++ b/supervision/detection/core.py @@ -906,10 +906,15 @@ class Detections: [ncnn](https://github.com/Tencent/ncnn) inference result. Supports object detection models. - Args: + Arguments: ncnn_results (dict): The output Results instance from ncnn. - import cv2 + Returns: + Detections: A new Detections object. + + Example: + ```python + import cv2 from ncnn.model_zoo import get_model import supervision as sv From 0d86159de1a3474bd53d40bf7dfb8e5c2abe5bac Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 23 Sep 2024 17:49:28 +0000 Subject: [PATCH 22/63] =?UTF-8?q?chore(pre=5Fcommit):=20=E2=AC=86=20pre=5F?= =?UTF-8?q?commit=20autoupdate?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/astral-sh/ruff-pre-commit: v0.6.5 → v0.6.7](https://github.com/astral-sh/ruff-pre-commit/compare/v0.6.5...v0.6.7) --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 50e8c1b7..ff62fd44 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -32,7 +32,7 @@ repos: additional_dependencies: ["bandit[toml]"] - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.6.5 + rev: v0.6.7 hooks: - id: ruff args: [--fix, --exit-non-zero-on-fix] From 1b6fd01f7e27ab4e9d45ee5970fe44069b226ca6 Mon Sep 17 00:00:00 2001 From: LinasKo Date: Tue, 24 Sep 2024 12:56:24 +0300 Subject: [PATCH 23/63] Fix arrya shapes in OBB IoU docstring --- supervision/detection/utils.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/supervision/detection/utils.py b/supervision/detection/utils.py index e7d1de5a..2a64a6d0 100644 --- a/supervision/detection/utils.py +++ b/supervision/detection/utils.py @@ -145,14 +145,14 @@ def oriented_box_iou_batch( ) -> np.ndarray: """ Compute Intersection over Union (IoU) of two sets of oriented bounding boxes - - `boxes_true` and `boxes_detection`. Both sets - of boxes are expected to be in `(x1, y1, x2, y2, x3, y3, x4, y4)` format. + `boxes_true` and `boxes_detection`. Both sets of boxes are expected to be in + `((x1, y1), (x2, y2), (x3, y3), (x4, y4))` format. Args: - boxes_true (np.ndarray): 2D `np.ndarray` representing ground-truth boxes. - `shape = (N, 8)` where `N` is number of true objects. - boxes_detection (np.ndarray): 2D `np.ndarray` representing detection boxes. - `shape = (M, 8)` where `M` is number of detected objects. + boxes_true (np.ndarray): a `np.ndarray` representing ground-truth boxes. + `shape = (N, 4, 2)` where `N` is number of true objects. + boxes_detection (np.ndarray): a `np.ndarray` representing detection boxes. + `shape = (M, 4, 2)` where `M` is number of detected objects. Returns: np.ndarray: Pairwise IoU of boxes from `boxes_true` and `boxes_detection`. From 787bbdac963f1861378364f04f1fed10eab8e15a Mon Sep 17 00:00:00 2001 From: Onuralp SEZER Date: Tue, 24 Sep 2024 13:03:46 +0300 Subject: [PATCH 24/63] =?UTF-8?q?ci:=20=F0=9F=91=B7=20pypi=20username=20-?= =?UTF-8?q?=20password=20swap=20with=20token?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Onuralp SEZER --- .github/workflows/publish-test.yml | 12 +++++------- .github/workflows/publish.yml | 10 ++++------ 2 files changed, 9 insertions(+), 13 deletions(-) diff --git a/.github/workflows/publish-test.yml b/.github/workflows/publish-test.yml index f201b7c4..973c30c9 100644 --- a/.github/workflows/publish-test.yml +++ b/.github/workflows/publish-test.yml @@ -30,14 +30,12 @@ jobs: python -m pip install --upgrade build twine python -m build twine check --strict dist/* - - name: 🚀 Publish distribution to PyPI - uses: pypa/gh-action-pypi-publish@release/v1 + - name: 🚀 Publish to PyPi + uses: pypa/gh-action-pypi-publish@release/v1.10 with: - user: ${{ secrets.PYPI_USERNAME }} - password: ${{ secrets.PYPI_PASSWORD }} + password: ${{ secrets.PYPI_API_TOKEN }} - name: 🚀 Publish to Test-PyPi - uses: pypa/gh-action-pypi-publish@release/v1 + uses: pypa/gh-action-pypi-publish@release/v1.10 with: repository-url: https://test.pypi.org/legacy/ - user: ${{ secrets.PYPI_TEST_USERNAME }} - password: ${{ secrets.PYPI_TEST_PASSWORD }} + password: ${{ secrets.TEST_PYPI_API_TOKEN }} diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 4de06532..aee59eba 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -28,13 +28,11 @@ jobs: python -m build twine check --strict dist/* - name: 🚀 Publish to PyPi - uses: pypa/gh-action-pypi-publish@release/v1 + uses: pypa/gh-action-pypi-publish@release/v1.10 with: - user: ${{ secrets.PYPI_USERNAME }} - password: ${{ secrets.PYPI_PASSWORD }} + password: ${{ secrets.PYPI_API_TOKEN }} - name: 🚀 Publish to Test-PyPi - uses: pypa/gh-action-pypi-publish@release/v1 + uses: pypa/gh-action-pypi-publish@release/v1.10 with: repository-url: https://test.pypi.org/legacy/ - user: ${{ secrets.PYPI_TEST_USERNAME }} - password: ${{ secrets.PYPI_TEST_PASSWORD }} + password: ${{ secrets.TEST_PYPI_API_TOKEN }} From a29d9599d7082b133b099aff91e347690a943df2 Mon Sep 17 00:00:00 2001 From: LinasKo Date: Tue, 24 Sep 2024 13:04:59 +0300 Subject: [PATCH 25/63] Fix OBB shape when computing object size --- supervision/metrics/utils/object_size.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/supervision/metrics/utils/object_size.py b/supervision/metrics/utils/object_size.py index 3fdf1627..16287922 100644 --- a/supervision/metrics/utils/object_size.py +++ b/supervision/metrics/utils/object_size.py @@ -101,17 +101,20 @@ def get_obb_size_category(xyxyxyxy: npt.NDArray[np.float32]) -> npt.NDArray[np.i Get the size category of a oriented bounding boxes array. Args: - xyxyxyxy (np.ndarray): The bounding boxes array shaped (N, 8). + xyxyxyxy (np.ndarray): The bounding boxes array shaped (N, 4, 2). Returns: (np.ndarray) The size category of each bounding box, matching the enum values of ObjectSizeCategory. Shaped (N,). """ - if len(xyxyxyxy.shape) != 2 or xyxyxyxy.shape[1] != 8: - raise ValueError("Oriented bounding boxes must be shaped (N, 8)") + if len(xyxyxyxy.shape) != 3 or xyxyxyxy.shape[1] != 4 or xyxyxyxy.shape[2] != 2: + raise ValueError("Oriented bounding boxes must be shaped (N, 4, 2)") # Shoelace formula - x1, y1, x2, y2, x3, y3, x4, y4 = xyxyxyxy.T + x = xyxyxyxy[:, :, 0] + y = xyxyxyxy[:, :, 1] + x1, x2, x3, x4 = x.T + y1, y2, y3, y4 = y.T areas = 0.5 * np.abs( (x1 * y2 + x2 * y3 + x3 * y4 + x4 * y1) - (x2 * y1 + x3 * y2 + x4 * y3 + x1 * y4) From 9bbd81e6ece57d1885d6161e0fa30acd63cc8b46 Mon Sep 17 00:00:00 2001 From: LinasKo Date: Tue, 24 Sep 2024 17:17:49 +0300 Subject: [PATCH 26/63] Link to Workflows in the first paragraph --- docs/detection/annotators.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/detection/annotators.md b/docs/detection/annotators.md index 60b8df5d..09b4d043 100644 --- a/docs/detection/annotators.md +++ b/docs/detection/annotators.md @@ -5,7 +5,7 @@ status: new # Annotators -Supervision provides a variety of annotators to annotate detections on images and videos. You can try them out below, with a Workflow that runs [Microsoft's COCO](https://cocodataset.org/#home) dataset through a Instance Segmentation model and annotates the detections using supervision's annotators. +Supervision provides a variety of annotators to annotate detections on images and videos. You can try them out below, with a [Workflow](https://roboflow.com/workflows) that runs [Microsoft's COCO](https://cocodataset.org/#home) dataset through a Instance Segmentation model and annotates the detections using supervision's annotators.
From d88d8ed58b138795a03bccf02e3a16a31a862f51 Mon Sep 17 00:00:00 2001 From: LinasKo Date: Wed, 25 Sep 2024 21:52:27 +0300 Subject: [PATCH 27/63] Factorization, simplifaction, speedup * Sped up computation from 600 fps to 1700 fps (Mac) * Previously ran at 2000 fps --- supervision/detection/line_zone.py | 231 ++++++++++++++++------------- 1 file changed, 128 insertions(+), 103 deletions(-) diff --git a/supervision/detection/line_zone.py b/supervision/detection/line_zone.py index 74142af2..d174bb44 100644 --- a/supervision/detection/line_zone.py +++ b/supervision/detection/line_zone.py @@ -1,6 +1,6 @@ import math import warnings -from typing import Any, Dict, Iterable, Optional, Tuple +from typing import Any, Dict, Iterable, Tuple import cv2 import numpy as np @@ -207,8 +207,8 @@ class LineZoneAnnotator: text_scale: float = 0.5, text_offset: float = 1.5, text_padding: int = 10, - custom_in_text: Optional[str] = None, - custom_out_text: Optional[str] = None, + custom_in_text: str = "in", + custom_out_text: str = "out", display_in_count: bool = True, display_out_count: bool = True, display_text_box: bool = True, @@ -243,8 +243,8 @@ class LineZoneAnnotator: self.text_scale: float = text_scale self.text_offset: float = text_offset self.text_padding: int = text_padding - self.custom_in_text: Optional[str] = custom_in_text - self.custom_out_text: Optional[str] = custom_out_text + self.custom_in_text: str = custom_in_text + self.custom_out_text: str = custom_out_text self.display_in_count: bool = display_in_count self.display_out_count: bool = display_out_count self.display_text_box: bool = display_text_box @@ -253,10 +253,10 @@ class LineZoneAnnotator: def _get_line_angle(self, line_counter: LineZone) -> float: """ - Calculate the line counter angle using trigonometry. + Calculate the line counter angle (in degrees). Args: - line_counter (LineZone): The line counter object used to annotate. + line_counter (LineZone): The line zone object. Returns: float: Line counter angle, in degrees. @@ -352,68 +352,42 @@ class LineZoneAnnotator: (int, int, int, int): xyxy insertion bbox to position count image. """ y1 = max(anchor_in_frame.y - img_dim // 2, 0) - y2 = min( - anchor_in_frame.y + img_dim // 2 + img_dim % 2, - frame_dims[0], - ) + y2 = min(anchor_in_frame.y + img_dim // 2 + img_dim % 2, frame_dims[0]) x1 = max(anchor_in_frame.x - img_dim // 2, 0) - x2 = min( - anchor_in_frame.x + img_dim // 2 + img_dim % 2, - frame_dims[1], - ) + x2 = min(anchor_in_frame.x + img_dim // 2 + img_dim % 2, frame_dims[1]) - return (x1, y1, x2, y2) + return x1, y1, x2, y2 - def _rotate_img(self, img: np.ndarray, line_counter: LineZone) -> np.ndarray: - """ - Rotate count image to align text with the line counter. - - Attributes: - img (np.ndarray): Image to rotate. - line_counter (LineZone): The line counter object. - - Returns: - np.ndarray: Image with the same shape as the input with aligned text. - """ - line_angle = self._get_line_angle(line_counter) - is_line_upside_down = not -90 <= line_angle <= 90 - - rotation_center = (img.shape[0] // 2, img.shape[0] // 2) - rotation_angle = -(line_angle) + is_line_upside_down * 180 - rotation_scale = 1 - - rotation_matrix = cv2.getRotationMatrix2D( - rotation_center, rotation_angle, rotation_scale - ) - - img_rotated = cv2.warpAffine(img, rotation_matrix, (img.shape[1], img.shape[0])) - - return img_rotated - - def _crop_img(self, img, xyxy_in_frame) -> np.ndarray: + def _crop_img(self, label_image, xyxy_in_frame) -> np.ndarray: """ Crop image to fit insertion bbox boundaries. Args: - img (np.ndarray): Image to crop. + label_image (np.ndarray): Image to crop. xyxy_in_frame (list): xyxy insertion bbox used to crop image. Returns: np.ndarray: Cropped image. """ - img_dim = img.shape[0] - (x1, y1, x2, y2) = xyxy_in_frame + img_dim = label_image.shape[0] + x1, y1, x2, y2 = xyxy_in_frame if y2 - y1 != img_dim: - img = img[(img_dim - y2) :, ...] if y1 == 0 else img[: (y2 - y1), ...] + if y1 == 0: + label_image = label_image[(img_dim - y2) :, ...] + else: + label_image = label_image[: (y2 - y1), ...] if x2 - x1 != img_dim: - img = img[:, (img_dim - x2) :, ...] if x1 == 0 else img[:, : (x2 - x1), ...] + if x1 == 0: + label_image = label_image[:, (img_dim - x2) :, ...] + else: + label_image = label_image[:, : (x2 - x1), ...] - return img + return label_image - def _annotate_img_in_frame( - self, frame: np.ndarray, img: np.ndarray, xyxy_in_frame: tuple + def _place_annotation_on_frame( + self, frame: np.ndarray, annotation_image: np.ndarray, xyxy_in_frame: tuple ) -> np.ndarray: """ Annotate count image in the original frame. @@ -426,21 +400,23 @@ class LineZoneAnnotator: Returns: np.ndarray: Annotated frame. """ - (x1, y1, x2, y2) = xyxy_in_frame + x1, y1, x2, y2 = xyxy_in_frame - # Paste count image and alpha in empty backgrounds with frame width and height. - img_in_frame = np.zeros_like(frame, dtype=np.uint8) - img_in_frame[y1:y2, x1:x2, ...] = img[:, :, :3] + annotation_in_frame = np.zeros_like(frame, dtype=np.uint8) + annotation_in_frame[y1:y2, x1:x2, ...] = annotation_image[:, :, :3] + + # Visually indistinguishable from opacity multiplication alpha_in_frame = np.zeros_like(frame[:, :, 0], dtype=np.uint8) - alpha_in_frame[y1:y2, x1:x2] = img[:, :, 3] + alpha_in_frame[y1:y2, x1:x2] = annotation_image[:, :, 3] + mask = alpha_in_frame == 255 - opacity = alpha_in_frame / 255 + # MUCH faster when not vectorized (Mac) for i in range(3): - frame[:, :, i] = frame[:, :, i] * (1 - opacity) + img_in_frame[:, :, i] + frame[:, :, i][mask] = annotation_in_frame[:, :, i][mask] return frame - def _annotate_count( + def _draw_count_label( self, frame: np.ndarray, line_counter: LineZone, @@ -459,36 +435,24 @@ class LineZoneAnnotator: Returns: np.ndarray: The image with the count drawn on it. """ + + line_angle_degrees = self._get_line_angle(line_counter) + label_image = _make_line_zone_label( + text, + text_scale=self.text_scale, + text_thickness=self.text_thickness, + text_padding=self.text_padding, + text_color=self.text_color, + text_box_show=self.display_text_box, + text_box_color=self.color, + line_angle_degrees=line_angle_degrees, + ) + assert label_image.shape[0] == label_image.shape[1] + text_width, text_height = cv2.getTextSize( text, cv2.FONT_HERSHEY_SIMPLEX, self.text_scale, self.text_thickness )[0] - # Create an auxiliary squared image for the count and its alpha channel - image_dim = int((max(text_width, text_height) + self.text_padding * 2) * 1.5) - image = np.zeros((image_dim, image_dim, 3), dtype=np.uint8) # bgr - image_alpha = np.zeros((image_dim, image_dim, 1), dtype=np.uint8) # gray - - text_args: Dict[str, Any] = { - "text": text, - "text_anchor": Point(image_dim // 2, image_dim // 2), - "text_scale": self.text_scale, - "text_thickness": self.text_thickness, - "text_padding": self.text_padding, - } - draw_text( - scene=image, - text_color=self.text_color, - background_color=self.color if self.display_text_box else None, - **text_args, - ) - draw_text( - scene=image_alpha, - text_color=Color.WHITE, - background_color=Color.WHITE if self.display_text_box else None, - **text_args, - ) - image = np.dstack((image, image_alpha)) # Stack bgr and alpha channels - anchor_in_frame = self._calculate_anchor_in_frame( line_counter=line_counter, text_width=text_width, @@ -498,16 +462,16 @@ class LineZoneAnnotator: xyxy_in_frame = self._calculate_xyxy_in_frame( frame_dims=frame.shape[:2], - img_dim=image_dim, + img_dim=label_image.shape[0], anchor_in_frame=anchor_in_frame, ) - image_rotated = self._rotate_img(img=image, line_counter=line_counter) + image_cropped = self._crop_img( + label_image=label_image, xyxy_in_frame=xyxy_in_frame + ) - image_cropped = self._crop_img(img=image_rotated, xyxy_in_frame=xyxy_in_frame) - - frame = self._annotate_img_in_frame( - frame=frame, img=image_cropped, xyxy_in_frame=xyxy_in_frame + frame = self._place_annotation_on_frame( + frame=frame, annotation_image=image_cropped, xyxy_in_frame=xyxy_in_frame ) return frame @@ -552,12 +516,8 @@ class LineZoneAnnotator: ) if self.display_in_count: - in_text = ( - f"{self.custom_in_text}: {line_counter.in_count}" - if self.custom_in_text is not None - else f"in: {line_counter.in_count}" - ) - self._annotate_count( + in_text = f"{self.custom_in_text}: {line_counter.in_count}" + self._draw_count_label( frame=frame, line_counter=line_counter, text=in_text, @@ -565,15 +525,80 @@ class LineZoneAnnotator: ) if self.display_out_count: - out_text = ( - f"{self.custom_out_text}: {line_counter.out_count}" - if self.custom_out_text is not None - else f"out: {line_counter.out_count}" - ) - self._annotate_count( + out_text = f"{self.custom_out_text}: {line_counter.out_count}" + self._draw_count_label( frame=frame, line_counter=line_counter, text=out_text, is_in_count=False, ) return frame + + +def _make_line_zone_label( + text: str, + *, + text_scale: float, + text_thickness: int, + text_padding: int, + text_color: Color, + text_box_show: bool, + text_box_color: Color, + line_angle_degrees: float, +) -> np.ndarray: + """ + Create the image with the rotated label, showing the in/out counts + of objects crossing the LineZone. + + Args: + text (str): The text to display. + text_scale (float): The scale of the text. + text_thickness (int): The thickness of the text. + text_padding (int): The padding around the text. + text_color (Color): The color of the text. + text_box_show (bool): Whether to display the text box. + text_box_color (Color): The color of the text box. + line_angle_degrees (float): The angle of the line in degrees. + + Returns: + np.ndarray: The label of shape (H, W, 4), in BGRA format. + """ + text_width, text_height = cv2.getTextSize( + text, cv2.FONT_HERSHEY_SIMPLEX, text_scale, text_thickness + )[0] + + annotation_dim = int((max(text_width, text_height) + text_padding * 2) * 1.5) + annotation_shape = (annotation_dim, annotation_dim) + annotation_center = Point(annotation_dim // 2, annotation_dim // 2) + + annotation = np.zeros((*annotation_shape, 3), dtype=np.uint8) + annotation_alpha = np.zeros((*annotation_shape, 1), dtype=np.uint8) + + text_args: Dict[str, Any] = dict( + text=text, + text_anchor=annotation_center, + text_scale=text_scale, + text_thickness=text_thickness, + text_padding=text_padding, + ) + draw_text( + scene=annotation, + text_color=text_color, + background_color=text_box_color if text_box_show else None, + **text_args, + ) + draw_text( + scene=annotation_alpha, + text_color=Color.WHITE, + background_color=Color.WHITE if text_box_show else None, + **text_args, + ) + annotation = np.dstack((annotation, annotation_alpha)) + + rotation_angle = -line_angle_degrees + rotation_matrix = cv2.getRotationMatrix2D( + annotation_center.as_xy_float_tuple(), rotation_angle, scale=1 + ) + annotation = cv2.warpAffine(annotation, rotation_matrix, annotation_shape) + + return annotation From f8b60eab8782603a9c699e1a3c5adf559b2637ce Mon Sep 17 00:00:00 2001 From: LinasKo Date: Wed, 25 Sep 2024 22:09:44 +0300 Subject: [PATCH 28/63] Further simplification & speedup --- supervision/detection/line_zone.py | 334 +++++++++++------------------ 1 file changed, 127 insertions(+), 207 deletions(-) diff --git a/supervision/detection/line_zone.py b/supervision/detection/line_zone.py index d174bb44..7e7dbcb3 100644 --- a/supervision/detection/line_zone.py +++ b/supervision/detection/line_zone.py @@ -10,6 +10,7 @@ from supervision.detection.utils import cross_product from supervision.draw.color import Color from supervision.draw.utils import draw_text from supervision.geometry.core import Point, Position, Vector +from supervision.utils.image import overlay_image from supervision.utils.internal import SupervisionWarnings @@ -285,7 +286,8 @@ class LineZoneAnnotator: text_width: int, text_height: int, is_in_count: bool, - ) -> Point: + label_dimension: int, + ) -> Tuple[int, int]: """ Calculate insertion anchor in frame to position the center of the count image. @@ -294,9 +296,10 @@ class LineZoneAnnotator: text_width (int): Text width. text_height (int): Text height. is_in_count (bool): Whether the count should be placed over or below line. + label_dimension (int): Size of the label image. Assumes the label is rectangular. Returns: - Point: xy insertion anchor to position count image in frame. + Tuple[int, int]: xy, pont in an image where the label will be placed. """ line_angle = self._get_line_angle(line_counter) @@ -335,146 +338,10 @@ class LineZoneAnnotator: anchor[0] -= move_perp_x anchor[1] += move_perp_y - return Point(x=anchor[0], y=anchor[1]) + x1 = max(anchor[0] - label_dimension // 2, 0) + y1 = max(anchor[1] - label_dimension // 2, 0) - def _calculate_xyxy_in_frame( - self, frame_dims: tuple, img_dim: int, anchor_in_frame: Point - ) -> tuple: - """ - Calculate insertion bbox in frame to position count image. - - Args: - frame_dims (int, int): Width and height of the frame. - img_dim (int): Width/height of squared count image. - anchor_in_frame (Point): xy insertion anchor to position image. - - Returns: - (int, int, int, int): xyxy insertion bbox to position count image. - """ - y1 = max(anchor_in_frame.y - img_dim // 2, 0) - y2 = min(anchor_in_frame.y + img_dim // 2 + img_dim % 2, frame_dims[0]) - x1 = max(anchor_in_frame.x - img_dim // 2, 0) - x2 = min(anchor_in_frame.x + img_dim // 2 + img_dim % 2, frame_dims[1]) - - return x1, y1, x2, y2 - - def _crop_img(self, label_image, xyxy_in_frame) -> np.ndarray: - """ - Crop image to fit insertion bbox boundaries. - - Args: - label_image (np.ndarray): Image to crop. - xyxy_in_frame (list): xyxy insertion bbox used to crop image. - - Returns: - np.ndarray: Cropped image. - """ - img_dim = label_image.shape[0] - x1, y1, x2, y2 = xyxy_in_frame - - if y2 - y1 != img_dim: - if y1 == 0: - label_image = label_image[(img_dim - y2) :, ...] - else: - label_image = label_image[: (y2 - y1), ...] - - if x2 - x1 != img_dim: - if x1 == 0: - label_image = label_image[:, (img_dim - x2) :, ...] - else: - label_image = label_image[:, : (x2 - x1), ...] - - return label_image - - def _place_annotation_on_frame( - self, frame: np.ndarray, annotation_image: np.ndarray, xyxy_in_frame: tuple - ) -> np.ndarray: - """ - Annotate count image in the original frame. - - Attributes: - frame (np.ndarray): The base image on which to insert the text-box image. - img (np.ndarray): Count image with bgr channels + alpha channel. - xyxy_in_frame (int, int, int, int): xyxy insertion bbox. - - Returns: - np.ndarray: Annotated frame. - """ - x1, y1, x2, y2 = xyxy_in_frame - - annotation_in_frame = np.zeros_like(frame, dtype=np.uint8) - annotation_in_frame[y1:y2, x1:x2, ...] = annotation_image[:, :, :3] - - # Visually indistinguishable from opacity multiplication - alpha_in_frame = np.zeros_like(frame[:, :, 0], dtype=np.uint8) - alpha_in_frame[y1:y2, x1:x2] = annotation_image[:, :, 3] - mask = alpha_in_frame == 255 - - # MUCH faster when not vectorized (Mac) - for i in range(3): - frame[:, :, i][mask] = annotation_in_frame[:, :, i][mask] - - return frame - - def _draw_count_label( - self, - frame: np.ndarray, - line_counter: LineZone, - text: str, - is_in_count: bool, - ) -> np.ndarray: - """This method is drawing the text on the frame. - - Args: - frame (np.ndarray): The image on which the text will be drawn. - line_counter (LineCounter): The line counter - that will be used to draw the line. - text (str): The text that will be drawn. - is_in_count (bool): Whether to display the in count or out count. - - Returns: - np.ndarray: The image with the count drawn on it. - """ - - line_angle_degrees = self._get_line_angle(line_counter) - label_image = _make_line_zone_label( - text, - text_scale=self.text_scale, - text_thickness=self.text_thickness, - text_padding=self.text_padding, - text_color=self.text_color, - text_box_show=self.display_text_box, - text_box_color=self.color, - line_angle_degrees=line_angle_degrees, - ) - assert label_image.shape[0] == label_image.shape[1] - - text_width, text_height = cv2.getTextSize( - text, cv2.FONT_HERSHEY_SIMPLEX, self.text_scale, self.text_thickness - )[0] - - anchor_in_frame = self._calculate_anchor_in_frame( - line_counter=line_counter, - text_width=text_width, - text_height=text_height, - is_in_count=is_in_count, - ) - - xyxy_in_frame = self._calculate_xyxy_in_frame( - frame_dims=frame.shape[:2], - img_dim=label_image.shape[0], - anchor_in_frame=anchor_in_frame, - ) - - image_cropped = self._crop_img( - label_image=label_image, xyxy_in_frame=xyxy_in_frame - ) - - frame = self._place_annotation_on_frame( - frame=frame, annotation_image=image_cropped, xyxy_in_frame=xyxy_in_frame - ) - - return frame + return x1, y1 def annotate(self, frame: np.ndarray, line_counter: LineZone) -> np.ndarray: """ @@ -533,72 +400,125 @@ class LineZoneAnnotator: is_in_count=False, ) return frame + + + def _draw_count_label( + self, + frame: np.ndarray, + line_counter: LineZone, + text: str, + is_in_count: bool, + ) -> np.ndarray: + """ + This method is drawing the text on the frame. + + Args: + frame (np.ndarray): The image on which the text will be drawn. + line_counter (LineCounter): The line counter + that will be used to draw the line. + text (str): The text that will be drawn. + is_in_count (bool): Whether to display the in count or out count. + + Returns: + np.ndarray: The image with the count drawn on it. + """ + + line_angle_degrees = self._get_line_angle(line_counter) + label_image = self._make_count_label_image( + text, + text_scale=self.text_scale, + text_thickness=self.text_thickness, + text_padding=self.text_padding, + text_color=self.text_color, + text_box_show=self.display_text_box, + text_box_color=self.color, + line_angle_degrees=line_angle_degrees, + ) + assert label_image.shape[0] == label_image.shape[1] + + text_width, text_height = cv2.getTextSize( + text, cv2.FONT_HERSHEY_SIMPLEX, self.text_scale, self.text_thickness + )[0] + + label_origin = self._calculate_anchor_in_frame( + line_counter=line_counter, + text_width=text_width, + text_height=text_height, + is_in_count=is_in_count, + label_dimension=label_image.shape[0], + ) + + frame = overlay_image(frame, label_image, label_origin) + + return frame + + @staticmethod + def _make_count_label_image( + text: str, + *, + text_scale: float, + text_thickness: int, + text_padding: int, + text_color: Color, + text_box_show: bool, + text_box_color: Color, + line_angle_degrees: float, + ) -> np.ndarray: + """ + Create the small text box displaying line zone count. E.g. "out: 7". + + Args: + text (str): The text to display. + text_scale (float): The scale of the text. + text_thickness (int): The thickness of the text. + text_padding (int): The padding around the text. + text_color (Color): The color of the text. + text_box_show (bool): Whether to display the text box. + text_box_color (Color): The color of the text box. + line_angle_degrees (float): The angle of the line in degrees. + + Returns: + np.ndarray: The label of shape (H, W, 4), in BGRA format. + """ + text_width, text_height = cv2.getTextSize( + text, cv2.FONT_HERSHEY_SIMPLEX, text_scale, text_thickness + )[0] + + annotation_dim = int((max(text_width, text_height) + text_padding * 2) * 1.5) + annotation_shape = (annotation_dim, annotation_dim) + annotation_center = Point(annotation_dim // 2, annotation_dim // 2) + + annotation = np.zeros((*annotation_shape, 3), dtype=np.uint8) + annotation_alpha = np.zeros((*annotation_shape, 1), dtype=np.uint8) + + text_args: Dict[str, Any] = dict( + text=text, + text_anchor=annotation_center, + text_scale=text_scale, + text_thickness=text_thickness, + text_padding=text_padding, + ) + draw_text( + scene=annotation, + text_color=text_color, + background_color=text_box_color if text_box_show else None, + **text_args, + ) + draw_text( + scene=annotation_alpha, + text_color=Color.WHITE, + background_color=Color.WHITE if text_box_show else None, + **text_args, + ) + annotation = np.dstack((annotation, annotation_alpha)) + + rotation_angle = -line_angle_degrees + rotation_matrix = cv2.getRotationMatrix2D( + annotation_center.as_xy_float_tuple(), rotation_angle, scale=1 + ) + annotation = cv2.warpAffine(annotation, rotation_matrix, annotation_shape) + + return annotation -def _make_line_zone_label( - text: str, - *, - text_scale: float, - text_thickness: int, - text_padding: int, - text_color: Color, - text_box_show: bool, - text_box_color: Color, - line_angle_degrees: float, -) -> np.ndarray: - """ - Create the image with the rotated label, showing the in/out counts - of objects crossing the LineZone. - Args: - text (str): The text to display. - text_scale (float): The scale of the text. - text_thickness (int): The thickness of the text. - text_padding (int): The padding around the text. - text_color (Color): The color of the text. - text_box_show (bool): Whether to display the text box. - text_box_color (Color): The color of the text box. - line_angle_degrees (float): The angle of the line in degrees. - - Returns: - np.ndarray: The label of shape (H, W, 4), in BGRA format. - """ - text_width, text_height = cv2.getTextSize( - text, cv2.FONT_HERSHEY_SIMPLEX, text_scale, text_thickness - )[0] - - annotation_dim = int((max(text_width, text_height) + text_padding * 2) * 1.5) - annotation_shape = (annotation_dim, annotation_dim) - annotation_center = Point(annotation_dim // 2, annotation_dim // 2) - - annotation = np.zeros((*annotation_shape, 3), dtype=np.uint8) - annotation_alpha = np.zeros((*annotation_shape, 1), dtype=np.uint8) - - text_args: Dict[str, Any] = dict( - text=text, - text_anchor=annotation_center, - text_scale=text_scale, - text_thickness=text_thickness, - text_padding=text_padding, - ) - draw_text( - scene=annotation, - text_color=text_color, - background_color=text_box_color if text_box_show else None, - **text_args, - ) - draw_text( - scene=annotation_alpha, - text_color=Color.WHITE, - background_color=Color.WHITE if text_box_show else None, - **text_args, - ) - annotation = np.dstack((annotation, annotation_alpha)) - - rotation_angle = -line_angle_degrees - rotation_matrix = cv2.getRotationMatrix2D( - annotation_center.as_xy_float_tuple(), rotation_angle, scale=1 - ) - annotation = cv2.warpAffine(annotation, rotation_matrix, annotation_shape) - - return annotation From 60c5292781bd0e4702970fcd828aa7edaa58f7a4 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 25 Sep 2024 19:10:52 +0000 Subject: [PATCH 29/63] =?UTF-8?q?fix(pre=5Fcommit):=20=F0=9F=8E=A8=20auto?= =?UTF-8?q?=20format=20pre-commit=20hooks?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- supervision/detection/line_zone.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/supervision/detection/line_zone.py b/supervision/detection/line_zone.py index 7e7dbcb3..313b0503 100644 --- a/supervision/detection/line_zone.py +++ b/supervision/detection/line_zone.py @@ -400,7 +400,6 @@ class LineZoneAnnotator: is_in_count=False, ) return frame - def _draw_count_label( self, @@ -519,6 +518,3 @@ class LineZoneAnnotator: annotation = cv2.warpAffine(annotation, rotation_matrix, annotation_shape) return annotation - - - From 6fbabeefffe1587cc4ef44eb6d468ef22f3cf072 Mon Sep 17 00:00:00 2001 From: LinasKo Date: Wed, 25 Sep 2024 22:22:30 +0300 Subject: [PATCH 30/63] Make color hashable, use cache for label images --- supervision/detection/line_zone.py | 2 ++ supervision/draw/color.py | 6 ++++++ 2 files changed, 8 insertions(+) diff --git a/supervision/detection/line_zone.py b/supervision/detection/line_zone.py index 313b0503..aaa8eec6 100644 --- a/supervision/detection/line_zone.py +++ b/supervision/detection/line_zone.py @@ -1,3 +1,4 @@ +from functools import lru_cache import math import warnings from typing import Any, Dict, Iterable, Tuple @@ -452,6 +453,7 @@ class LineZoneAnnotator: return frame @staticmethod + @lru_cache(maxsize=32) def _make_count_label_image( text: str, *, diff --git a/supervision/draw/color.py b/supervision/draw/color.py index 8e99ac6c..4422bcb8 100644 --- a/supervision/draw/color.py +++ b/supervision/draw/color.py @@ -254,6 +254,12 @@ class Color: @classproperty def ROBOFLOW(cls) -> Color: return Color.from_hex("#A351FB") + + def __hash__(self): + return hash((self.r, self.g, self.b)) + + def __eq__(self, other): + return isinstance(other, Color) and self.r == other.r and self.g == other.g and self.b == other.b @dataclass From 5f86dbe29bcc3e67b51dbcddd17bcd2fbd44d824 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 25 Sep 2024 19:23:16 +0000 Subject: [PATCH 31/63] =?UTF-8?q?fix(pre=5Fcommit):=20=F0=9F=8E=A8=20auto?= =?UTF-8?q?=20format=20pre-commit=20hooks?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- supervision/detection/line_zone.py | 2 +- supervision/draw/color.py | 11 ++++++++--- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/supervision/detection/line_zone.py b/supervision/detection/line_zone.py index aaa8eec6..870eee07 100644 --- a/supervision/detection/line_zone.py +++ b/supervision/detection/line_zone.py @@ -1,6 +1,6 @@ -from functools import lru_cache import math import warnings +from functools import lru_cache from typing import Any, Dict, Iterable, Tuple import cv2 diff --git a/supervision/draw/color.py b/supervision/draw/color.py index 4422bcb8..b101e0f7 100644 --- a/supervision/draw/color.py +++ b/supervision/draw/color.py @@ -254,12 +254,17 @@ class Color: @classproperty def ROBOFLOW(cls) -> Color: return Color.from_hex("#A351FB") - + def __hash__(self): return hash((self.r, self.g, self.b)) - + def __eq__(self, other): - return isinstance(other, Color) and self.r == other.r and self.g == other.g and self.b == other.b + return ( + isinstance(other, Color) + and self.r == other.r + and self.g == other.g + and self.b == other.b + ) @dataclass From c1444c6d147e98e1957ef6dcbd19cfab5363c523 Mon Sep 17 00:00:00 2001 From: LinasKo Date: Wed, 25 Sep 2024 23:17:55 +0300 Subject: [PATCH 32/63] Futher LineZone code tidy-up --- supervision/detection/line_zone.py | 140 ++++++++++++++++++----------- 1 file changed, 89 insertions(+), 51 deletions(-) diff --git a/supervision/detection/line_zone.py b/supervision/detection/line_zone.py index 870eee07..5ea8bd94 100644 --- a/supervision/detection/line_zone.py +++ b/supervision/detection/line_zone.py @@ -1,7 +1,7 @@ import math import warnings from functools import lru_cache -from typing import Any, Dict, Iterable, Tuple +from typing import Any, Dict, Iterable, Optional, Tuple import cv2 import numpy as np @@ -209,8 +209,8 @@ class LineZoneAnnotator: text_scale: float = 0.5, text_offset: float = 1.5, text_padding: int = 10, - custom_in_text: str = "in", - custom_out_text: str = "out", + custom_in_text: Optional[str] = None, + custom_out_text: Optional[str] = None, display_in_count: bool = True, display_out_count: bool = True, display_text_box: bool = True, @@ -245,29 +245,26 @@ class LineZoneAnnotator: self.text_scale: float = text_scale self.text_offset: float = text_offset self.text_padding: int = text_padding - self.custom_in_text: str = custom_in_text - self.custom_out_text: str = custom_out_text + self.in_text: str = custom_in_text if custom_in_text else "in" + self.out_text: str = custom_out_text if custom_out_text else "out" self.display_in_count: bool = display_in_count self.display_out_count: bool = display_out_count self.display_text_box: bool = display_text_box self.text_orient_to_line: bool = text_orient_to_line self.text_centered: bool = text_centered - def _get_line_angle(self, line_counter: LineZone) -> float: + def _get_line_angle(self, line_zone: LineZone) -> float: """ Calculate the line counter angle (in degrees). Args: - line_counter (LineZone): The line zone object. + line_zone (LineZone): The line zone object. Returns: float: Line counter angle, in degrees. """ - if not self.text_orient_to_line: - return 0 - - start_point = line_counter.vector.start.as_xy_int_tuple() - end_point = line_counter.vector.end.as_xy_int_tuple() + start_point = line_zone.vector.start.as_xy_int_tuple() + end_point = line_zone.vector.end.as_xy_int_tuple() delta_x = end_point[0] - start_point[0] delta_y = end_point[1] - start_point[1] @@ -283,7 +280,7 @@ class LineZoneAnnotator: def _calculate_anchor_in_frame( self, - line_counter: LineZone, + line_zone: LineZone, text_width: int, text_height: int, is_in_count: bool, @@ -293,7 +290,7 @@ class LineZoneAnnotator: Calculate insertion anchor in frame to position the center of the count image. Args: - line_counter (LineZone): The line counter object used for counting. + line_zone (LineZone): The line counter object used for counting. text_width (int): Text width. text_height (int): Text height. is_in_count (bool): Whether the count should be placed over or below line. @@ -302,15 +299,15 @@ class LineZoneAnnotator: Returns: Tuple[int, int]: xy, pont in an image where the label will be placed. """ - line_angle = self._get_line_angle(line_counter) + line_angle = self._get_line_angle(line_zone) if self.text_centered: mid_point = Vector( - start=line_counter.vector.start, end=line_counter.vector.end + start=line_zone.vector.start, end=line_zone.vector.end ).center.as_xy_int_tuple() anchor = list(mid_point) else: - end_point = line_counter.vector.end.as_xy_int_tuple() + end_point = line_zone.vector.end.as_xy_int_tuple() anchor = list(end_point) move_along_x = int( @@ -346,21 +343,24 @@ class LineZoneAnnotator: def annotate(self, frame: np.ndarray, line_counter: LineZone) -> np.ndarray: """ - Draws the line on the frame using the line_counter provided. + Draws the line on the frame using the line zone provided. Attributes: frame (np.ndarray): The image on which the line will be drawn. - line_counter (LineCounter): The line counter + line_counter (LineCounter): The line zone that will be used to draw the line. Returns: np.ndarray: The image with the line drawn on it. """ + line_start = line_counter.vector.start.as_xy_int_tuple() + line_end = line_counter.vector.end.as_xy_int_tuple() + line_center_point = line_counter.vector.center cv2.line( frame, - line_counter.vector.start.as_xy_int_tuple(), - line_counter.vector.end.as_xy_int_tuple(), + line_start, + line_end, self.color.as_bgr(), self.thickness, lineType=cv2.LINE_AA, @@ -368,7 +368,7 @@ class LineZoneAnnotator: ) cv2.circle( frame, - line_counter.vector.start.as_xy_int_tuple(), + line_start, radius=5, color=self.text_color.as_bgr(), thickness=-1, @@ -376,55 +376,93 @@ class LineZoneAnnotator: ) cv2.circle( frame, - line_counter.vector.end.as_xy_int_tuple(), + line_end, radius=5, color=self.text_color.as_bgr(), thickness=-1, lineType=cv2.LINE_AA, ) - if self.display_in_count: - in_text = f"{self.custom_in_text}: {line_counter.in_count}" - self._draw_count_label( - frame=frame, - line_counter=line_counter, - text=in_text, - is_in_count=True, - ) + in_text = f"{self.in_text}: {line_counter.in_count}" + out_text = f"{self.out_text}: {line_counter.out_count}" + line_angle_degrees = self._get_line_angle(line_counter) + + for text, is_shown, is_in_count in [ + (in_text, self.display_in_count, True), + (out_text, self.display_out_count, False), + ]: + if not is_shown: + continue + + if line_angle_degrees == 0 or not self.text_orient_to_line: + self._draw_basic_label( + frame=frame, + line_center=line_center_point, + text=text, + is_in_count=is_in_count, + ) + else: + self._draw_oriented_label( + frame=frame, + line_zone=line_counter, + text=text, + is_in_count=is_in_count, + ) - if self.display_out_count: - out_text = f"{self.custom_out_text}: {line_counter.out_count}" - self._draw_count_label( - frame=frame, - line_counter=line_counter, - text=out_text, - is_in_count=False, - ) return frame - def _draw_count_label( + def _draw_basic_label( self, frame: np.ndarray, - line_counter: LineZone, + line_center: Point, + text: str, + is_in_count: bool, + ) -> np.ndarray: + _, text_height = cv2.getTextSize( + text, cv2.FONT_HERSHEY_SIMPLEX, self.text_scale, self.text_thickness + )[0] + + if is_in_count: + line_center.y -= int(self.text_offset * text_height) + else: + line_center.y += int(self.text_offset * text_height) + + draw_text( + scene=frame, + text=text, + text_anchor=line_center, + text_color=self.text_color, + text_scale=self.text_scale, + text_thickness=self.text_thickness, + text_padding=self.text_padding, + background_color=self.color, + ) + + return frame + + def _draw_oriented_label( + self, + frame: np.ndarray, + line_zone: LineZone, text: str, is_in_count: bool, ) -> np.ndarray: """ - This method is drawing the text on the frame. + Draw the count label on the frame. For example: "out: 7". + The label is oriented to match the line angle. Args: - frame (np.ndarray): The image on which the text will be drawn. - line_counter (LineCounter): The line counter - that will be used to draw the line. + frame (np.ndarray): The entire scene, on which the label will be placed. + line_zone (LineZone): The line zone responsible for counting objects crossing it. text (str): The text that will be drawn. - is_in_count (bool): Whether to display the in count or out count. + is_in_count (bool): Whether to display the in count (above line) or out count (below line). Returns: - np.ndarray: The image with the count drawn on it. + np.ndarray: The scene with the label drawn on it. """ - line_angle_degrees = self._get_line_angle(line_counter) - label_image = self._make_count_label_image( + line_angle_degrees = self._get_line_angle(line_zone) + label_image = self._make_label_image( text, text_scale=self.text_scale, text_thickness=self.text_thickness, @@ -441,7 +479,7 @@ class LineZoneAnnotator: )[0] label_origin = self._calculate_anchor_in_frame( - line_counter=line_counter, + line_zone=line_zone, text_width=text_width, text_height=text_height, is_in_count=is_in_count, @@ -454,7 +492,7 @@ class LineZoneAnnotator: @staticmethod @lru_cache(maxsize=32) - def _make_count_label_image( + def _make_label_image( text: str, *, text_scale: float, From 356a86a46966777f10d46d4fc1de4575ae408829 Mon Sep 17 00:00:00 2001 From: LinasKo Date: Wed, 25 Sep 2024 23:18:40 +0300 Subject: [PATCH 33/63] Ruff formatting --- supervision/detection/line_zone.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/supervision/detection/line_zone.py b/supervision/detection/line_zone.py index 5ea8bd94..616a2673 100644 --- a/supervision/detection/line_zone.py +++ b/supervision/detection/line_zone.py @@ -294,7 +294,8 @@ class LineZoneAnnotator: text_width (int): Text width. text_height (int): Text height. is_in_count (bool): Whether the count should be placed over or below line. - label_dimension (int): Size of the label image. Assumes the label is rectangular. + label_dimension (int): Size of the label image. Assumes the + label is rectangular. Returns: Tuple[int, int]: xy, pont in an image where the label will be placed. @@ -453,9 +454,11 @@ class LineZoneAnnotator: Args: frame (np.ndarray): The entire scene, on which the label will be placed. - line_zone (LineZone): The line zone responsible for counting objects crossing it. + line_zone (LineZone): The line zone responsible for counting + objects crossing it. text (str): The text that will be drawn. - is_in_count (bool): Whether to display the in count (above line) or out count (below line). + is_in_count (bool): Whether to display the in count (above line) + or out count (below line). Returns: np.ndarray: The scene with the label drawn on it. From 7b22cfa7411d74a21390881b925e07416f662374 Mon Sep 17 00:00:00 2001 From: LinasKo Date: Wed, 25 Sep 2024 23:28:28 +0300 Subject: [PATCH 34/63] Bugfix: basic label would always have background box --- supervision/detection/line_zone.py | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/supervision/detection/line_zone.py b/supervision/detection/line_zone.py index 616a2673..d8107cca 100644 --- a/supervision/detection/line_zone.py +++ b/supervision/detection/line_zone.py @@ -419,6 +419,20 @@ class LineZoneAnnotator: text: str, is_in_count: bool, ) -> np.ndarray: + """ + Draw the count label on the frame. For example: "out: 7". + The label contains horizontal text and is not rotated. + + Args: + frame (np.ndarray): The entire scene, on which the label will be placed. + line_center (Point): The center of the line zone. + text (str): The text that will be drawn. + is_in_count (bool): Whether to display the in count (above line) + or out count (below line). + + Returns: + np.ndarray: The scene with the label drawn on it. + """ _, text_height = cv2.getTextSize( text, cv2.FONT_HERSHEY_SIMPLEX, self.text_scale, self.text_thickness )[0] @@ -436,7 +450,7 @@ class LineZoneAnnotator: text_scale=self.text_scale, text_thickness=self.text_thickness, text_padding=self.text_padding, - background_color=self.color, + background_color=self.color if self.display_text_box else None, ) return frame From d6d3213f62d2f31a9fac88294ba385c4a0be85db Mon Sep 17 00:00:00 2001 From: LinasKo Date: Wed, 25 Sep 2024 23:31:53 +0300 Subject: [PATCH 35/63] Move methods around, rename some variables --- supervision/detection/line_zone.py | 152 ++++++++++++++--------------- 1 file changed, 76 insertions(+), 76 deletions(-) diff --git a/supervision/detection/line_zone.py b/supervision/detection/line_zone.py index d8107cca..6a20a2bc 100644 --- a/supervision/detection/line_zone.py +++ b/supervision/detection/line_zone.py @@ -253,6 +253,76 @@ class LineZoneAnnotator: self.text_orient_to_line: bool = text_orient_to_line self.text_centered: bool = text_centered + def annotate(self, frame: np.ndarray, line_counter: LineZone) -> np.ndarray: + """ + Draws the line on the frame using the line zone provided. + + Attributes: + frame (np.ndarray): The image on which the line will be drawn. + line_counter (LineCounter): The line zone + that will be used to draw the line. + + Returns: + np.ndarray: The image with the line drawn on it. + + """ + line_start = line_counter.vector.start.as_xy_int_tuple() + line_end = line_counter.vector.end.as_xy_int_tuple() + line_center_point = line_counter.vector.center + cv2.line( + frame, + line_start, + line_end, + self.color.as_bgr(), + self.thickness, + lineType=cv2.LINE_AA, + shift=0, + ) + cv2.circle( + frame, + line_start, + radius=5, + color=self.text_color.as_bgr(), + thickness=-1, + lineType=cv2.LINE_AA, + ) + cv2.circle( + frame, + line_end, + radius=5, + color=self.text_color.as_bgr(), + thickness=-1, + lineType=cv2.LINE_AA, + ) + + in_text = f"{self.in_text}: {line_counter.in_count}" + out_text = f"{self.out_text}: {line_counter.out_count}" + line_angle_degrees = self._get_line_angle(line_counter) + + for text, is_shown, is_in_count in [ + (in_text, self.display_in_count, True), + (out_text, self.display_out_count, False), + ]: + if not is_shown: + continue + + if line_angle_degrees == 0 or not self.text_orient_to_line: + self._draw_basic_label( + frame=frame, + line_center=line_center_point, + text=text, + is_in_count=is_in_count, + ) + else: + self._draw_oriented_label( + frame=frame, + line_zone=line_counter, + text=text, + is_in_count=is_in_count, + ) + + return frame + def _get_line_angle(self, line_zone: LineZone) -> float: """ Calculate the line counter angle (in degrees). @@ -323,95 +393,25 @@ class LineZoneAnnotator: anchor[0] -= move_along_x anchor[1] -= move_along_y - move_perp_x = int( + move_perpendicular_x = int( math.sin(math.radians(line_angle)) * (self.text_offset * text_height) ) - move_perp_y = int( + move_perpendicular_y = int( math.cos(math.radians(line_angle)) * (self.text_offset * text_height) ) if is_in_count: - anchor[0] += move_perp_x - anchor[1] -= move_perp_y + anchor[0] += move_perpendicular_x + anchor[1] -= move_perpendicular_y else: - anchor[0] -= move_perp_x - anchor[1] += move_perp_y + anchor[0] -= move_perpendicular_x + anchor[1] += move_perpendicular_y x1 = max(anchor[0] - label_dimension // 2, 0) y1 = max(anchor[1] - label_dimension // 2, 0) return x1, y1 - def annotate(self, frame: np.ndarray, line_counter: LineZone) -> np.ndarray: - """ - Draws the line on the frame using the line zone provided. - - Attributes: - frame (np.ndarray): The image on which the line will be drawn. - line_counter (LineCounter): The line zone - that will be used to draw the line. - - Returns: - np.ndarray: The image with the line drawn on it. - - """ - line_start = line_counter.vector.start.as_xy_int_tuple() - line_end = line_counter.vector.end.as_xy_int_tuple() - line_center_point = line_counter.vector.center - cv2.line( - frame, - line_start, - line_end, - self.color.as_bgr(), - self.thickness, - lineType=cv2.LINE_AA, - shift=0, - ) - cv2.circle( - frame, - line_start, - radius=5, - color=self.text_color.as_bgr(), - thickness=-1, - lineType=cv2.LINE_AA, - ) - cv2.circle( - frame, - line_end, - radius=5, - color=self.text_color.as_bgr(), - thickness=-1, - lineType=cv2.LINE_AA, - ) - - in_text = f"{self.in_text}: {line_counter.in_count}" - out_text = f"{self.out_text}: {line_counter.out_count}" - line_angle_degrees = self._get_line_angle(line_counter) - - for text, is_shown, is_in_count in [ - (in_text, self.display_in_count, True), - (out_text, self.display_out_count, False), - ]: - if not is_shown: - continue - - if line_angle_degrees == 0 or not self.text_orient_to_line: - self._draw_basic_label( - frame=frame, - line_center=line_center_point, - text=text, - is_in_count=is_in_count, - ) - else: - self._draw_oriented_label( - frame=frame, - line_zone=line_counter, - text=text, - is_in_count=is_in_count, - ) - - return frame - def _draw_basic_label( self, frame: np.ndarray, From ebd5e0661b6033762bbef49b07b674d0111ec7d3 Mon Sep 17 00:00:00 2001 From: LinasKo Date: Wed, 25 Sep 2024 23:49:19 +0300 Subject: [PATCH 36/63] Make sure text is always displayed upright --- supervision/detection/line_zone.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/supervision/detection/line_zone.py b/supervision/detection/line_zone.py index 6a20a2bc..2959ca20 100644 --- a/supervision/detection/line_zone.py +++ b/supervision/detection/line_zone.py @@ -491,11 +491,15 @@ class LineZoneAnnotator: ) assert label_image.shape[0] == label_image.shape[1] + # Make sure text is displayed upright + if line_zone.vector.start.x > line_zone.vector.end.x: + label_image = cv2.flip(label_image, flipCode=-1) + text_width, text_height = cv2.getTextSize( text, cv2.FONT_HERSHEY_SIMPLEX, self.text_scale, self.text_thickness )[0] - label_origin = self._calculate_anchor_in_frame( + label_anchor = self._calculate_anchor_in_frame( line_zone=line_zone, text_width=text_width, text_height=text_height, @@ -503,7 +507,7 @@ class LineZoneAnnotator: label_dimension=label_image.shape[0], ) - frame = overlay_image(frame, label_image, label_origin) + frame = overlay_image(frame, label_image, label_anchor) return frame From 156dbd5046b9587e7c3d65823d4ee2b2bdfeafd8 Mon Sep 17 00:00:00 2001 From: LinasKo Date: Thu, 26 Sep 2024 00:02:13 +0300 Subject: [PATCH 37/63] New docstring for LineZoneAnnotator --- supervision/detection/line_zone.py | 34 ++++++++++++++++-------------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/supervision/detection/line_zone.py b/supervision/detection/line_zone.py index 2959ca20..a62437ec 100644 --- a/supervision/detection/line_zone.py +++ b/supervision/detection/line_zone.py @@ -218,24 +218,26 @@ class LineZoneAnnotator: text_centered: bool = True, ): """ - A class for drawing the LineZone and its detected object count on an image. + A class for drawing the `LineZone` and its detected object count + on an image. Attributes: - thickness (int): The thickness of the line that will be drawn. - color (Color): The color of the line that will be drawn. - text_thickness (int): 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. - orient_text_to_line (bool): Whether to orient the text to the line or not. - custom_in_text: (Optional[str]): Custom text to display for the in count. - custom_out_text: (Optional[str]): Custom text to display for the out count. - display_in_count (bool): Whether to display the in count or not. - display_out_count (bool): Whether to display the out count or not. - display_text_box (bool): Whether to draw a text box under the text or not. + thickness (int): Line thickness. + color (Color): Line color. + text_thickness (int): Text thickness. + text_color (Color): Text color. + text_scale (float): Text scale. + text_offset (float): How far the text will be from the line. + text_padding (int): The empty space in the text box, surrounding the text. + custom_in_text: (Optional[str]): Write something else instead of "in". + custom_out_text: (Optional[str]): Write something else instead of "out". + display_in_count (bool): Pass `False` to hide the "in" count. + display_out_count (bool): Pass `False` to hide the "out" count. + display_text_box (bool): Pass `False` to hide the text background box. text_orient_to_line (bool): ⭐ Match text orientation to the line. - text_centered (bool): Whether to draw the count centered in the line or not. + Recommended to set to `True`. + text_centered (bool): Pass `False` to disable text centering. Useful + when the label overlaps something important. """ self.thickness: int = thickness @@ -259,7 +261,7 @@ class LineZoneAnnotator: Attributes: frame (np.ndarray): The image on which the line will be drawn. - line_counter (LineCounter): The line zone + line_counter (LineZone): The line zone that will be used to draw the line. Returns: From 4722459bcedc910a5737371376fd4227f1284230 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 26 Sep 2024 00:29:52 +0000 Subject: [PATCH 38/63] :arrow_up: Bump mkdocs-material from 9.5.36 to 9.5.37 Bumps [mkdocs-material](https://github.com/squidfunk/mkdocs-material) from 9.5.36 to 9.5.37. - [Release notes](https://github.com/squidfunk/mkdocs-material/releases) - [Changelog](https://github.com/squidfunk/mkdocs-material/blob/master/CHANGELOG) - [Commits](https://github.com/squidfunk/mkdocs-material/compare/9.5.36...9.5.37) --- updated-dependencies: - dependency-name: mkdocs-material dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- poetry.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/poetry.lock b/poetry.lock index a75e2d78..8061b698 100644 --- a/poetry.lock +++ b/poetry.lock @@ -2220,13 +2220,13 @@ pygments = ">2.12.0" [[package]] name = "mkdocs-material" -version = "9.5.36" +version = "9.5.37" description = "Documentation that simply works" optional = false python-versions = ">=3.8" files = [ - {file = "mkdocs_material-9.5.36-py3-none-any.whl", hash = "sha256:36734c1fd9404bea74236242ba3359b267fc930c7233b9fd086b0898825d0ac9"}, - {file = "mkdocs_material-9.5.36.tar.gz", hash = "sha256:140456f761320f72b399effc073fa3f8aac744c77b0970797c201cae2f6c967f"}, + {file = "mkdocs_material-9.5.37-py3-none-any.whl", hash = "sha256:6e8a986abad77be5edec3dd77cf1ddf2480963fb297a8e971f87a82fd464b070"}, + {file = "mkdocs_material-9.5.37.tar.gz", hash = "sha256:2c31607431ec234db124031255b0a9d4f3e1c3ecc2c47ad97ecfff0460471941"}, ] [package.dependencies] From db0d965e8df6014c2d058f8d4abc6ef2b0c9159d Mon Sep 17 00:00:00 2001 From: LinasKo Date: Thu, 26 Sep 2024 12:12:53 +0300 Subject: [PATCH 39/63] Fix: incorrect label rotation --- supervision/detection/line_zone.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/supervision/detection/line_zone.py b/supervision/detection/line_zone.py index a62437ec..e77d4057 100644 --- a/supervision/detection/line_zone.py +++ b/supervision/detection/line_zone.py @@ -493,10 +493,6 @@ class LineZoneAnnotator: ) assert label_image.shape[0] == label_image.shape[1] - # Make sure text is displayed upright - if line_zone.vector.start.x > line_zone.vector.end.x: - label_image = cv2.flip(label_image, flipCode=-1) - text_width, text_height = cv2.getTextSize( text, cv2.FONT_HERSHEY_SIMPLEX, self.text_scale, self.text_thickness )[0] @@ -574,6 +570,10 @@ class LineZoneAnnotator: ) annotation = np.dstack((annotation, annotation_alpha)) + # Make sure text is displayed upright + if 90 < line_angle_degrees % 360 < 270: + annotation = cv2.flip(annotation, flipCode=-1).astype(np.uint8) + rotation_angle = -line_angle_degrees rotation_matrix = cv2.getRotationMatrix2D( annotation_center.as_xy_float_tuple(), rotation_angle, scale=1 From fedc1a47e028b8a0d3fd4b008bda05f7fd508242 Mon Sep 17 00:00:00 2001 From: Thibault Date: Thu, 26 Sep 2024 11:15:16 +0200 Subject: [PATCH 40/63] fix: InferenceSlicer overlap_ratio_wh argument changed to None by default Changed the default value of the overlap_ratio_wh argument to None, as this parameter will be deprecated in version 0.27.0. This change allows a smoother transition by not forcing users to explicitly set it to None. Additionally, added tests for validating the overlap arguments (overlap_wh and overlap_ratio_wh) to ensure proper error handling and usage. Also started implementing tests for the offset generation method to verify that slices are correctly calculated based on the overlap settings. --- .../detection/tools/inference_slicer.py | 2 +- test/detection/tools/test_inference_slicer.py | 194 ++++++++++++++++++ 2 files changed, 195 insertions(+), 1 deletion(-) create mode 100644 test/detection/tools/test_inference_slicer.py diff --git a/supervision/detection/tools/inference_slicer.py b/supervision/detection/tools/inference_slicer.py index 52f45140..68455015 100644 --- a/supervision/detection/tools/inference_slicer.py +++ b/supervision/detection/tools/inference_slicer.py @@ -94,7 +94,7 @@ class InferenceSlicer: self, callback: Callable[[np.ndarray], Detections], slice_wh: Tuple[int, int] = (320, 320), - overlap_ratio_wh: Optional[Tuple[float, float]] = (0.2, 0.2), + overlap_ratio_wh: Optional[Tuple[float, float]] = None, overlap_wh: Optional[Tuple[int, int]] = None, overlap_filter: Union[OverlapFilter, str] = OverlapFilter.NON_MAX_SUPPRESSION, iou_threshold: float = 0.5, diff --git a/test/detection/tools/test_inference_slicer.py b/test/detection/tools/test_inference_slicer.py new file mode 100644 index 00000000..cccecfc2 --- /dev/null +++ b/test/detection/tools/test_inference_slicer.py @@ -0,0 +1,194 @@ +from contextlib import ExitStack as DoesNotRaise +from typing import Optional, Tuple + +import numpy as np +import pytest + +from supervision.detection.core import Detections +from supervision.detection.overlap_filter import OverlapFilter +from supervision.detection.tools.inference_slicer import InferenceSlicer + + +@pytest.fixture +def mock_callback(): + """Mock callback function for testing.""" + + def callback(image_slice: np.ndarray) -> Detections: + # Here we mock the detection process, returning a mock detection + # Assume detections are just coordinates for simplicity + return Detections(xyxy=np.array([[0, 0, 10, 10]])) + + return callback + + +@pytest.mark.parametrize( + "slice_wh, overlap_ratio_wh, overlap_wh, expected_overlap, exception", + [ + # Valid case: overlap_ratio_wh provided, overlap calculated from the ratio + ((128, 128), (0.2, 0.2), None, None, DoesNotRaise()), + # Valid case: overlap_wh in pixels, no ratio provided + ((128, 128), None, (20, 20), (20, 20), DoesNotRaise()), + # Invalid case: overlap_ratio_wh greater than 1, should raise ValueError + ((128, 128), (1.1, 0.5), None, None, pytest.raises(ValueError)), + # Invalid case: negative overlap_wh, should raise ValueError + ((128, 128), None, (-10, 20), None, pytest.raises(ValueError)), + # Invalid case: + # overlap_ratio_wh and overlap_wh provided, should raise ValueError + ((128, 128), (0.5, 0.5), (20, 20), (20, 20), pytest.raises(ValueError)), + # Valid case: no overlap_ratio_wh, overlap_wh = 50 pixels + ((256, 256), None, (50, 50), (50, 50), DoesNotRaise()), + # Valid case: overlap_ratio_wh provided, overlap calculated from (0.3, 0.3) + ((200, 200), (0.3, 0.3), None, None, DoesNotRaise()), + # Valid case: small overlap_ratio_wh values + ((100, 100), (0.1, 0.1), None, None, DoesNotRaise()), + # Invalid case: negative overlap_ratio_wh value, should raise ValueError + ((128, 128), (-0.1, 0.2), None, None, pytest.raises(ValueError)), + # Invalid case: negative overlap_ratio_wh with overlap_wh provided + ((128, 128), (-0.1, 0.2), (30, 30), None, pytest.raises(ValueError)), + # Invalid case: overlap_wh greater than slice size, should raise ValueError + ((128, 128), None, (150, 150), (150, 150), DoesNotRaise()), + # Valid case: overlap_ratio_wh is 0, no overlap + ((128, 128), (0.0, 0.0), None, None, DoesNotRaise()), + # Invalid case: no overlaps defined, no overlap + ((128, 128), None, None, None, pytest.raises(ValueError)), + ], +) +def test_inference_slicer_overlap( + mock_callback, + slice_wh: Tuple[int, int], + overlap_ratio_wh: Optional[Tuple[float, float]], + overlap_wh: Optional[Tuple[int, int]], + expected_overlap: Optional[Tuple[int, int]], + exception: Exception, +) -> None: + with exception: + slicer = InferenceSlicer( + callback=mock_callback, + slice_wh=slice_wh, + overlap_ratio_wh=overlap_ratio_wh, + overlap_wh=overlap_wh, + overlap_filter=OverlapFilter.NONE, + ) + assert slicer.overlap_wh == expected_overlap + + +@pytest.mark.parametrize( + "resolution_wh, slice_wh, overlap_wh, expected_offsets", + [ + # Case 1: No overlap, exact slices fit within image dimensions + ( + (256, 256), + (128, 128), + (0, 0), + np.array( + [ + [0, 0, 128, 128], + [128, 0, 256, 128], + [0, 128, 128, 256], + [128, 128, 256, 256], + ] + ), + ), + # Case 2: Overlap of 64 pixels in both directions + ( + (256, 256), + (128, 128), + (64, 64), + np.array( + [ + [0, 0, 128, 128], + [64, 0, 192, 128], + [128, 0, 256, 128], + [192, 0, 256, 128], + [0, 64, 128, 192], + [64, 64, 192, 192], + [128, 64, 256, 192], + [192, 64, 256, 192], + [0, 128, 128, 256], + [64, 128, 192, 256], + [128, 128, 256, 256], + [192, 128, 256, 256], + [0, 192, 128, 256], + [64, 192, 192, 256], + [128, 192, 256, 256], + [192, 192, 256, 256], + ] + ), + ), + # Case 3: Image not perfectly divisible by slice size (no overlap) + ( + (300, 300), + (128, 128), + (0, 0), + np.array( + [ + [0, 0, 128, 128], + [128, 0, 256, 128], + [256, 0, 300, 128], + [0, 128, 128, 256], + [128, 128, 256, 256], + [256, 128, 300, 256], + [0, 256, 128, 300], + [128, 256, 256, 300], + [256, 256, 300, 300], + ] + ), + ), + # Case 4: Overlap of 32 pixels, image not perfectly divisible by slice size + ( + (300, 300), + (128, 128), + (32, 32), + np.array( + [ + [0, 0, 128, 128], + [96, 0, 224, 128], + [192, 0, 300, 128], + [288, 0, 300, 128], + [0, 96, 128, 224], + [96, 96, 224, 224], + [192, 96, 300, 224], + [288, 96, 300, 224], + [0, 192, 128, 300], + [96, 192, 224, 300], + [192, 192, 300, 300], + [288, 192, 300, 300], + [0, 288, 128, 300], + [96, 288, 224, 300], + [192, 288, 300, 300], + [288, 288, 300, 300], + ] + ), + ), + # Case 5: Image smaller than slice size (no overlap) + ( + (100, 100), + (128, 128), + (0, 0), + np.array( + [ + [0, 0, 100, 100], + ] + ), + ), + # Case 6: Overlap_wh is greater than the slice size + ((256, 256), (128, 128), (150, 150), np.array([]).reshape(0, 4)), + ], +) +def test_generate_offset( + resolution_wh: Tuple[int, int], + slice_wh: Tuple[int, int], + overlap_wh: Optional[Tuple[int, int]], + expected_offsets: np.ndarray, +) -> None: + offsets = InferenceSlicer._generate_offset( + resolution_wh=resolution_wh, + slice_wh=slice_wh, + overlap_ratio_wh=None, + overlap_wh=overlap_wh, + ) + + # Verify that the generated offsets match the expected offsets + assert np.array_equal( + offsets, expected_offsets + ), f"Expected {expected_offsets}, got {offsets}" From 4a95aaa2c4341e2f7c81d0db6cfb5da6ed56f25a Mon Sep 17 00:00:00 2001 From: LinasKo Date: Thu, 26 Sep 2024 12:27:07 +0300 Subject: [PATCH 41/63] Fix basic_label offset --- supervision/detection/line_zone.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/supervision/detection/line_zone.py b/supervision/detection/line_zone.py index e77d4057..ba2c355b 100644 --- a/supervision/detection/line_zone.py +++ b/supervision/detection/line_zone.py @@ -270,7 +270,6 @@ class LineZoneAnnotator: """ line_start = line_counter.vector.start.as_xy_int_tuple() line_end = line_counter.vector.end.as_xy_int_tuple() - line_center_point = line_counter.vector.center cv2.line( frame, line_start, @@ -311,7 +310,7 @@ class LineZoneAnnotator: if line_angle_degrees == 0 or not self.text_orient_to_line: self._draw_basic_label( frame=frame, - line_center=line_center_point, + line_center=line_counter.vector.center, text=text, is_in_count=is_in_count, ) From c81aec9d1bf895c03ff3d9973921aef9ad40361f Mon Sep 17 00:00:00 2001 From: LinasKo Date: Thu, 26 Sep 2024 12:38:35 +0300 Subject: [PATCH 42/63] Docstrings update --- supervision/detection/line_zone.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/supervision/detection/line_zone.py b/supervision/detection/line_zone.py index ba2c355b..2131c14c 100644 --- a/supervision/detection/line_zone.py +++ b/supervision/detection/line_zone.py @@ -229,8 +229,8 @@ class LineZoneAnnotator: text_scale (float): Text scale. text_offset (float): How far the text will be from the line. text_padding (int): The empty space in the text box, surrounding the text. - custom_in_text: (Optional[str]): Write something else instead of "in". - custom_out_text: (Optional[str]): Write something else instead of "out". + custom_in_text (Optional[str]): Write something else instead of "in". + custom_out_text (Optional[str]): Write something else instead of "out". display_in_count (bool): Pass `False` to hide the "in" count. display_out_count (bool): Pass `False` to hide the "out" count. display_text_box (bool): Pass `False` to hide the text background box. From d50c51fc52dfb321adc7e1fce20a5c00881ab6ea Mon Sep 17 00:00:00 2001 From: LinasKo Date: Thu, 26 Sep 2024 13:18:10 +0300 Subject: [PATCH 43/63] Update supervision docs on PyPi, add Linas as maintainer --- pyproject.toml | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 4e8c7fac..ba990f51 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -3,13 +3,16 @@ name = "supervision" version = "0.24.0rc1" description = "A set of easy-to-use utils that will come in handy in any Computer Vision project" authors = ["Piotr Skalski "] -maintainers = ["Piotr Skalski "] +maintainers = [ + "Piotr Skalski ", + "Linas Kondrackis ", +] readme = "README.md" license = "MIT" packages = [{ include = "supervision" }] homepage = "https://github.com/roboflow/supervision" repository = "https://github.com/roboflow/supervision" -documentation = "https://github.com/roboflow/supervision/blob/main/README.md" +documentation = "https://supervision.roboflow.com/latest/" keywords = [ "machine-learning", "deep-learning", @@ -146,7 +149,7 @@ indent-width = 4 [tool.ruff.lint] # Enable pycodestyle (`E`) and Pyflakes (`F`) codes by default. -select = ["E", "F", "I", "A", "Q", "W","RUF"] +select = ["E", "F", "I", "A", "Q", "W", "RUF"] ignore = [] # Allow autofix for all enabled rules (when `--fix`) is provided. fixable = [ From 06d1640700049e11383ed51a290827fb0c3348bd Mon Sep 17 00:00:00 2001 From: LinasKo Date: Thu, 26 Sep 2024 13:55:06 +0300 Subject: [PATCH 44/63] Fix documentation issues --- CONTRIBUTING.md | 12 ++++++------ docs/deprecated.md | 6 +++--- supervision/annotators/core.py | 3 ++- supervision/detection/core.py | 8 ++++---- supervision/detection/overlap_filter.py | 2 +- supervision/detection/utils.py | 2 +- supervision/keypoint/core.py | 4 ++-- 7 files changed, 19 insertions(+), 18 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 9ce990bf..dd6797c1 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -11,17 +11,17 @@ Please read and adhere to our [Code of Conduct](https://supervision.roboflow.com ## Table of Contents - [Contribution Guidelines](#contribution-guidelines) - - [Contributing Features](#contributing-features-) + - [Contributing Features](#contributing-features) - [How to Contribute Changes](#how-to-contribute-changes) - [Installation for Contributors](#installation-for-contributors) -- [Code Style and Quality](#-code-style-and-quality) +- [Code Style and Quality](#code-style-and-quality) - [Pre-commit tool](#pre-commit-tool) - [Docstrings](#docstrings) - [Type checking](#type-checking) -- [Documentation](#-documentation) -- [Cookbooks](#-cookbooks) -- [Tests](#-tests) -- [License](#-license) +- [Documentation](#documentation) +- [Cookbooks](#cookbooks) +- [Tests](#tests) +- [License](#license) ## Contribution Guidelines diff --git a/docs/deprecated.md b/docs/deprecated.md index c7eb5167..725e17d5 100644 --- a/docs/deprecated.md +++ b/docs/deprecated.md @@ -25,12 +25,12 @@ These features are phased out due to better alternatives or potential issues in ### 0.22.0 -- [`Detections.from_roboflow`](detection/core.md/#supervision.detection.core.Detections.from_roboflow) is removed as of `supervision-0.22.0`. Use [`Detections.from_inference`](detection/core.md/#supervision.detection.core.Detections.from_inference) instead. +- `Detections.from_roboflow` is removed as of `supervision-0.22.0`. Use [`Detections.from_inference`](detection/core.md/#supervision.detection.core.Detections.from_inference) instead. - The method `Color.white()` was removed as of `supervision-0.22.0`. Use the constant `Color.WHITE` instead. - The method `Color.black()` was removed as of `supervision-0.22.0`. Use the constant `Color.BLACK` instead. - The method `Color.red()` was removed as of `supervision-0.22.0`. Use the constant `Color.RED` instead. - The method `Color.green()` was removed as of `supervision-0.22.0`. Use the constant `Color.GREEN` instead. - The method `Color.blue()` was removed as of `supervision-0.22.0`. Use the constant `Color.BLUE` instead. -- The method `ColorPalette.default()` was removed as of `supervision-0.22.0`. Use the constant [`ColorPalette.DEFAULT`](draw/color/#supervision.draw.color.ColorPalette.DEFAULT) instead. +- The method `ColorPalette.default()` was removed as of `supervision-0.22.0`. Use the constant [`ColorPalette.DEFAULT`](/utils/draw/#supervision.draw.color.ColorPalette.DEFAULT) instead. - `BoxAnnotator` was removed as of `supervision-0.22.0`, however `BoundingBoxAnnotator` was immediately renamed to `BoxAnnotator`. Use [`BoxAnnotator`](detection/annotators.md/#supervision.annotators.core.BoxAnnotator) and [`LabelAnnotator`](detection/annotators.md/#supervision.annotators.core.LabelAnnotator) instead of the old `BoxAnnotator`. -- The method [`FPSMonitor.__call__`](utils/video.md/#supervision.utils.video.FPSMonitor.__call__) was removed as of `supervision-0.22.0`. Use the attribute [`FPSMonitor.fps`](utils/video.md/#supervision.utils.video.FPSMonitor.fps) instead. +- The method `FPSMonitor.__call__` was removed as of `supervision-0.22.0`. Use the attribute [`FPSMonitor.fps`](utils/video.md/#supervision.utils.video.FPSMonitor.fps) instead. diff --git a/supervision/annotators/core.py b/supervision/annotators/core.py index 29fe3158..1910ac9f 100644 --- a/supervision/annotators/core.py +++ b/supervision/annotators/core.py @@ -68,7 +68,8 @@ class BoxAnnotator(BaseAnnotator): Args: scene (ImageType): The image where bounding boxes will be drawn. `ImageType` - is a flexible type, accepting either `numpy.ndarray` or `PIL.Image.Image`. + is a flexible type, accepting either `numpy.ndarray` or + `PIL.Image.Image`. detections (Detections): Object detections to annotate. custom_color_lookup (Optional[np.ndarray]): Custom color lookup array. Allows to override the default color mapping strategy. diff --git a/supervision/detection/core.py b/supervision/detection/core.py index e1a2357e..113948fc 100644 --- a/supervision/detection/core.py +++ b/supervision/detection/core.py @@ -525,13 +525,13 @@ class Detections: ) @classmethod - def from_detectron2(cls, detectron2_results) -> Detections: + def from_detectron2(cls, detectron2_results: Any) -> Detections: """ Create a Detections object from the [Detectron2](https://github.com/facebookresearch/detectron2) inference result. Args: - detectron2_results: The output of a + detectron2_results (Any): The output of a Detectron2 model containing instances with prediction data. Returns: @@ -792,7 +792,7 @@ class Detections: @classmethod def from_lmm( - cls, lmm: Union[LMM, str], result: Union[str, dict], **kwargs + cls, lmm: Union[LMM, str], result: Union[str, dict], **kwargs: Any ) -> Detections: """ Creates a Detections object from the given result string based on the specified @@ -801,7 +801,7 @@ class Detections: Args: lmm (Union[LMM, str]): The type of LMM (Large Multimodal Model) to use. result (str): The result string containing the detection data. - **kwargs: Additional keyword arguments required by the specified LMM. + **kwargs (Any): Additional keyword arguments required by the specified LMM. Returns: Detections: A new Detections object. diff --git a/supervision/detection/overlap_filter.py b/supervision/detection/overlap_filter.py index dcfbb642..4c59295f 100644 --- a/supervision/detection/overlap_filter.py +++ b/supervision/detection/overlap_filter.py @@ -66,7 +66,7 @@ def mask_non_max_suppression( Raises: AssertionError: If `iou_threshold` is not within the closed - range from `0` to `1`. + range from `0` to `1`. """ assert 0 <= iou_threshold <= 1, ( "Value of `iou_threshold` must be in the closed range from 0 to 1, " diff --git a/supervision/detection/utils.py b/supervision/detection/utils.py index 2a64a6d0..43fcec5a 100644 --- a/supervision/detection/utils.py +++ b/supervision/detection/utils.py @@ -186,7 +186,7 @@ def clip_boxes(xyxy: np.ndarray, resolution_wh: Tuple[int, int]) -> np.ndarray: Args: xyxy (np.ndarray): A numpy array of shape `(N, 4)` where each row corresponds to a bounding box in - the format `(x_min, y_min, x_max, y_max)`. + the format `(x_min, y_min, x_max, y_max)`. resolution_wh (Tuple[int, int]): A tuple of the form `(width, height)` representing the resolution of the frame. diff --git a/supervision/keypoint/core.py b/supervision/keypoint/core.py index 6e628b24..36d6a596 100644 --- a/supervision/keypoint/core.py +++ b/supervision/keypoint/core.py @@ -459,13 +459,13 @@ class KeyPoints: ) @classmethod - def from_detectron2(cls, detectron2_results) -> KeyPoints: + def from_detectron2(cls, detectron2_results: Any) -> KeyPoints: """ Create a `sv.KeyPoints` object from the [Detectron2](https://github.com/facebookresearch/detectron2) inference result. Args: - detectron2_results: The output of a + detectron2_results (Any): The output of a Detectron2 model containing instances with prediction data. Returns: From f499430aa335aa35288d3af6b2145230b7a5e1f2 Mon Sep 17 00:00:00 2001 From: LinasKo Date: Thu, 26 Sep 2024 14:56:12 +0300 Subject: [PATCH 45/63] Fix mkdocs autorefs depreacation warnings --- poetry.lock | 12 ++++++------ pyproject.toml | 2 ++ 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/poetry.lock b/poetry.lock index 8061b698..ef25bd81 100644 --- a/poetry.lock +++ b/poetry.lock @@ -2291,18 +2291,19 @@ python-legacy = ["mkdocstrings-python-legacy (>=0.2.1)"] [[package]] name = "mkdocstrings-python" -version = "1.10.8" +version = "1.11.1" description = "A Python handler for mkdocstrings." optional = false python-versions = ">=3.8" files = [ - {file = "mkdocstrings_python-1.10.8-py3-none-any.whl", hash = "sha256:bb12e76c8b071686617f824029cb1dfe0e9afe89f27fb3ad9a27f95f054dcd89"}, - {file = "mkdocstrings_python-1.10.8.tar.gz", hash = "sha256:5856a59cbebbb8deb133224a540de1ff60bded25e54d8beacc375bb133d39016"}, + {file = "mkdocstrings_python-1.11.1-py3-none-any.whl", hash = "sha256:a21a1c05acef129a618517bb5aae3e33114f569b11588b1e7af3e9d4061a71af"}, + {file = "mkdocstrings_python-1.11.1.tar.gz", hash = "sha256:8824b115c5359304ab0b5378a91f6202324a849e1da907a3485b59208b797322"}, ] [package.dependencies] griffe = ">=0.49" -mkdocstrings = ">=0.25" +mkdocs-autorefs = ">=1.2" +mkdocstrings = ">=0.26" [[package]] name = "more-itertools" @@ -4294,7 +4295,6 @@ optional = false python-versions = ">=3.8" files = [ {file = "urllib3-2.2.2-py3-none-any.whl", hash = "sha256:a448b2f64d686155468037e1ace9f2d2199776e17f0a46610480d311f73e3472"}, - {file = "urllib3-2.2.2.tar.gz", hash = "sha256:dd505485549a7a552833da5e6063639d0d177c04f23bc3864e41e5dc5f612168"}, ] [package.extras] @@ -4484,4 +4484,4 @@ metrics = ["pandas", "pandas-stubs"] [metadata] lock-version = "2.0" python-versions = "^3.8" -content-hash = "86cf58c784053b04397cd55589f9704969531654dcb0c407435a81ea8305dd52" +content-hash = "da42551ddf31248900f614b81bfeaa1e556fda742e5be86d74fa1d151b99fd57" diff --git a/pyproject.toml b/pyproject.toml index ba990f51..42e1e7fe 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -87,6 +87,8 @@ docutils = [ [tool.poetry.group.docs.dependencies] mkdocs-material = { extras = ["imaging"], version = "^9.5.5" } mkdocstrings = { extras = ["python"], version = ">=0.25.2,<0.27.0" } +# mkdocstrings-python shouldn't be required, but latest mkdocstrings[python] (0.26.1) includes version with warnings +mkdocstrings-python = "^1.10.9" mike = "^2.0.0" # For Documentation Development use Python 3.10 or above # Use Latest mkdocs-jupyter min 0.24.6 for Jupyter Notebook Theme support From 363aa0629e2f0bdf024975e5ab89edb85c9744d1 Mon Sep 17 00:00:00 2001 From: Ethan White Date: Thu, 26 Sep 2024 08:55:31 -0400 Subject: [PATCH 46/63] feat: add __len__ to ColorPalette For downstream use being able to determine how many colors are in a ColorPalette object can be useful. E.g., checking if a user has provided a color palette that matches the number of labels. ColorPalette doesn't currenly have a __len__ attribute meaning that `mypalette.colors` has to be used for calculating lengths. This change adds __len__ to make this more intutitive by allowing `len(mypalette)`. --- supervision/draw/color.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/supervision/draw/color.py b/supervision/draw/color.py index b101e0f7..fe3ba900 100644 --- a/supervision/draw/color.py +++ b/supervision/draw/color.py @@ -397,6 +397,15 @@ class ColorPalette: idx = idx % len(self.colors) return self.colors[idx] + def __len__(self) -> int: + """ + Returns the number of colors in the palette. + + Returns: + int: The number of colors. + """ + return len(self.colors) + def unify_to_bgr(color: Union[Tuple[int, int, int], Color]) -> Tuple[int, int, int]: """ From 58a9588a53caf5d35349deb52c15bdf4f11c994a Mon Sep 17 00:00:00 2001 From: Onuralp SEZER Date: Thu, 26 Sep 2024 18:01:34 +0300 Subject: [PATCH 47/63] =?UTF-8?q?chore:=20=F0=9F=A7=B9=20split=20mkdocstri?= =?UTF-8?q?ng=20and=20mkdocstring-python=20package?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Onuralp SEZER --- poetry.lock | 1226 +++++++++++++++++++++++++----------------------- pyproject.toml | 3 +- 2 files changed, 653 insertions(+), 576 deletions(-) diff --git a/poetry.lock b/poetry.lock index ef25bd81..1fb50f7b 100644 --- a/poetry.lock +++ b/poetry.lock @@ -2,13 +2,13 @@ [[package]] name = "anyio" -version = "4.4.0" +version = "4.5.0" description = "High level compatibility layer for multiple asynchronous event loop implementations" optional = false python-versions = ">=3.8" files = [ - {file = "anyio-4.4.0-py3-none-any.whl", hash = "sha256:c1b2d8f46a8a812513012e1107cb0e68c17159a7a594208005a57dc776e1bdc7"}, - {file = "anyio-4.4.0.tar.gz", hash = "sha256:5aadc6a1bbb7cdb0bede386cac5e2940f5e2ff3aa20277e991cf028e0585ce94"}, + {file = "anyio-4.5.0-py3-none-any.whl", hash = "sha256:fdeb095b7cc5a5563175eedd926ec4ae55413bb4be5770c424af0ba46ccb4a78"}, + {file = "anyio-4.5.0.tar.gz", hash = "sha256:c5a275fe5ca0afd788001f58fca1e69e29ce706d746e317d660e21f70c530ef9"}, ] [package.dependencies] @@ -18,9 +18,9 @@ sniffio = ">=1.1" typing-extensions = {version = ">=4.1", markers = "python_version < \"3.11\""} [package.extras] -doc = ["Sphinx (>=7)", "packaging", "sphinx-autodoc-typehints (>=1.2.0)", "sphinx-rtd-theme"] -test = ["anyio[trio]", "coverage[toml] (>=7)", "exceptiongroup (>=1.2.0)", "hypothesis (>=4.0)", "psutil (>=5.9)", "pytest (>=7.0)", "pytest-mock (>=3.6.1)", "trustme", "uvloop (>=0.17)"] -trio = ["trio (>=0.23)"] +doc = ["Sphinx (>=7.4,<8.0)", "packaging", "sphinx-autodoc-typehints (>=1.2.0)", "sphinx-rtd-theme"] +test = ["anyio[trio]", "coverage[toml] (>=7)", "exceptiongroup (>=1.2.0)", "hypothesis (>=4.0)", "psutil (>=5.9)", "pytest (>=7.0)", "pytest-mock (>=3.6.1)", "trustme", "uvloop (>=0.21.0b1)"] +trio = ["trio (>=0.26.1)"] [[package]] name = "appnope" @@ -336,89 +336,89 @@ test = ["flake8", "isort", "pytest"] [[package]] name = "certifi" -version = "2024.7.4" +version = "2024.8.30" description = "Python package for providing Mozilla's CA Bundle." optional = false python-versions = ">=3.6" files = [ - {file = "certifi-2024.7.4-py3-none-any.whl", hash = "sha256:c198e21b1289c2ab85ee4e67bb4b4ef3ead0892059901a8d5b622f24a1101e90"}, - {file = "certifi-2024.7.4.tar.gz", hash = "sha256:5a1e7645bc0ec61a09e26c36f6106dd4cf40c6db3a1fb6352b0244e7fb057c7b"}, + {file = "certifi-2024.8.30-py3-none-any.whl", hash = "sha256:922820b53db7a7257ffbda3f597266d435245903d80737e34f8a45ff3e3230d8"}, + {file = "certifi-2024.8.30.tar.gz", hash = "sha256:bec941d2aa8195e248a60b31ff9f0558284cf01a52591ceda73ea9afffd69fd9"}, ] [[package]] name = "cffi" -version = "1.17.0" +version = "1.17.1" description = "Foreign Function Interface for Python calling C code." optional = false python-versions = ">=3.8" files = [ - {file = "cffi-1.17.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f9338cc05451f1942d0d8203ec2c346c830f8e86469903d5126c1f0a13a2bcbb"}, - {file = "cffi-1.17.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a0ce71725cacc9ebf839630772b07eeec220cbb5f03be1399e0457a1464f8e1a"}, - {file = "cffi-1.17.0-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c815270206f983309915a6844fe994b2fa47e5d05c4c4cef267c3b30e34dbe42"}, - {file = "cffi-1.17.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d6bdcd415ba87846fd317bee0774e412e8792832e7805938987e4ede1d13046d"}, - {file = "cffi-1.17.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8a98748ed1a1df4ee1d6f927e151ed6c1a09d5ec21684de879c7ea6aa96f58f2"}, - {file = "cffi-1.17.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0a048d4f6630113e54bb4b77e315e1ba32a5a31512c31a273807d0027a7e69ab"}, - {file = "cffi-1.17.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:24aa705a5f5bd3a8bcfa4d123f03413de5d86e497435693b638cbffb7d5d8a1b"}, - {file = "cffi-1.17.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:856bf0924d24e7f93b8aee12a3a1095c34085600aa805693fb7f5d1962393206"}, - {file = "cffi-1.17.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:4304d4416ff032ed50ad6bb87416d802e67139e31c0bde4628f36a47a3164bfa"}, - {file = "cffi-1.17.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:331ad15c39c9fe9186ceaf87203a9ecf5ae0ba2538c9e898e3a6967e8ad3db6f"}, - {file = "cffi-1.17.0-cp310-cp310-win32.whl", hash = "sha256:669b29a9eca6146465cc574659058ed949748f0809a2582d1f1a324eb91054dc"}, - {file = "cffi-1.17.0-cp310-cp310-win_amd64.whl", hash = "sha256:48b389b1fd5144603d61d752afd7167dfd205973a43151ae5045b35793232aa2"}, - {file = "cffi-1.17.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c5d97162c196ce54af6700949ddf9409e9833ef1003b4741c2b39ef46f1d9720"}, - {file = "cffi-1.17.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:5ba5c243f4004c750836f81606a9fcb7841f8874ad8f3bf204ff5e56332b72b9"}, - {file = "cffi-1.17.0-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bb9333f58fc3a2296fb1d54576138d4cf5d496a2cc118422bd77835e6ae0b9cb"}, - {file = "cffi-1.17.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:435a22d00ec7d7ea533db494da8581b05977f9c37338c80bc86314bec2619424"}, - {file = "cffi-1.17.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d1df34588123fcc88c872f5acb6f74ae59e9d182a2707097f9e28275ec26a12d"}, - {file = "cffi-1.17.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:df8bb0010fdd0a743b7542589223a2816bdde4d94bb5ad67884348fa2c1c67e8"}, - {file = "cffi-1.17.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a8b5b9712783415695663bd463990e2f00c6750562e6ad1d28e072a611c5f2a6"}, - {file = "cffi-1.17.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ffef8fd58a36fb5f1196919638f73dd3ae0db1a878982b27a9a5a176ede4ba91"}, - {file = "cffi-1.17.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:4e67d26532bfd8b7f7c05d5a766d6f437b362c1bf203a3a5ce3593a645e870b8"}, - {file = "cffi-1.17.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:45f7cd36186db767d803b1473b3c659d57a23b5fa491ad83c6d40f2af58e4dbb"}, - {file = "cffi-1.17.0-cp311-cp311-win32.whl", hash = "sha256:a9015f5b8af1bb6837a3fcb0cdf3b874fe3385ff6274e8b7925d81ccaec3c5c9"}, - {file = "cffi-1.17.0-cp311-cp311-win_amd64.whl", hash = "sha256:b50aaac7d05c2c26dfd50c3321199f019ba76bb650e346a6ef3616306eed67b0"}, - {file = "cffi-1.17.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:aec510255ce690d240f7cb23d7114f6b351c733a74c279a84def763660a2c3bc"}, - {file = "cffi-1.17.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2770bb0d5e3cc0e31e7318db06efcbcdb7b31bcb1a70086d3177692a02256f59"}, - {file = "cffi-1.17.0-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:db9a30ec064129d605d0f1aedc93e00894b9334ec74ba9c6bdd08147434b33eb"}, - {file = "cffi-1.17.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a47eef975d2b8b721775a0fa286f50eab535b9d56c70a6e62842134cf7841195"}, - {file = "cffi-1.17.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f3e0992f23bbb0be00a921eae5363329253c3b86287db27092461c887b791e5e"}, - {file = "cffi-1.17.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6107e445faf057c118d5050560695e46d272e5301feffda3c41849641222a828"}, - {file = "cffi-1.17.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eb862356ee9391dc5a0b3cbc00f416b48c1b9a52d252d898e5b7696a5f9fe150"}, - {file = "cffi-1.17.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:c1c13185b90bbd3f8b5963cd8ce7ad4ff441924c31e23c975cb150e27c2bf67a"}, - {file = "cffi-1.17.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:17c6d6d3260c7f2d94f657e6872591fe8733872a86ed1345bda872cfc8c74885"}, - {file = "cffi-1.17.0-cp312-cp312-win32.whl", hash = "sha256:c3b8bd3133cd50f6b637bb4322822c94c5ce4bf0d724ed5ae70afce62187c492"}, - {file = "cffi-1.17.0-cp312-cp312-win_amd64.whl", hash = "sha256:dca802c8db0720ce1c49cce1149ff7b06e91ba15fa84b1d59144fef1a1bc7ac2"}, - {file = "cffi-1.17.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:6ce01337d23884b21c03869d2f68c5523d43174d4fc405490eb0091057943118"}, - {file = "cffi-1.17.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:cab2eba3830bf4f6d91e2d6718e0e1c14a2f5ad1af68a89d24ace0c6b17cced7"}, - {file = "cffi-1.17.0-cp313-cp313-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:14b9cbc8f7ac98a739558eb86fabc283d4d564dafed50216e7f7ee62d0d25377"}, - {file = "cffi-1.17.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b00e7bcd71caa0282cbe3c90966f738e2db91e64092a877c3ff7f19a1628fdcb"}, - {file = "cffi-1.17.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:41f4915e09218744d8bae14759f983e466ab69b178de38066f7579892ff2a555"}, - {file = "cffi-1.17.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e4760a68cab57bfaa628938e9c2971137e05ce48e762a9cb53b76c9b569f1204"}, - {file = "cffi-1.17.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:011aff3524d578a9412c8b3cfaa50f2c0bd78e03eb7af7aa5e0df59b158efb2f"}, - {file = "cffi-1.17.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:a003ac9edc22d99ae1286b0875c460351f4e101f8c9d9d2576e78d7e048f64e0"}, - {file = "cffi-1.17.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:ef9528915df81b8f4c7612b19b8628214c65c9b7f74db2e34a646a0a2a0da2d4"}, - {file = "cffi-1.17.0-cp313-cp313-win32.whl", hash = "sha256:70d2aa9fb00cf52034feac4b913181a6e10356019b18ef89bc7c12a283bf5f5a"}, - {file = "cffi-1.17.0-cp313-cp313-win_amd64.whl", hash = "sha256:b7b6ea9e36d32582cda3465f54c4b454f62f23cb083ebc7a94e2ca6ef011c3a7"}, - {file = "cffi-1.17.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:964823b2fc77b55355999ade496c54dde161c621cb1f6eac61dc30ed1b63cd4c"}, - {file = "cffi-1.17.0-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:516a405f174fd3b88829eabfe4bb296ac602d6a0f68e0d64d5ac9456194a5b7e"}, - {file = "cffi-1.17.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dec6b307ce928e8e112a6bb9921a1cb00a0e14979bf28b98e084a4b8a742bd9b"}, - {file = "cffi-1.17.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e4094c7b464cf0a858e75cd14b03509e84789abf7b79f8537e6a72152109c76e"}, - {file = "cffi-1.17.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2404f3de742f47cb62d023f0ba7c5a916c9c653d5b368cc966382ae4e57da401"}, - {file = "cffi-1.17.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3aa9d43b02a0c681f0bfbc12d476d47b2b2b6a3f9287f11ee42989a268a1833c"}, - {file = "cffi-1.17.0-cp38-cp38-win32.whl", hash = "sha256:0bb15e7acf8ab35ca8b24b90af52c8b391690ef5c4aec3d31f38f0d37d2cc499"}, - {file = "cffi-1.17.0-cp38-cp38-win_amd64.whl", hash = "sha256:93a7350f6706b31f457c1457d3a3259ff9071a66f312ae64dc024f049055f72c"}, - {file = "cffi-1.17.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:1a2ddbac59dc3716bc79f27906c010406155031a1c801410f1bafff17ea304d2"}, - {file = "cffi-1.17.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:6327b572f5770293fc062a7ec04160e89741e8552bf1c358d1a23eba68166759"}, - {file = "cffi-1.17.0-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dbc183e7bef690c9abe5ea67b7b60fdbca81aa8da43468287dae7b5c046107d4"}, - {file = "cffi-1.17.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5bdc0f1f610d067c70aa3737ed06e2726fd9d6f7bfee4a351f4c40b6831f4e82"}, - {file = "cffi-1.17.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6d872186c1617d143969defeadac5a904e6e374183e07977eedef9c07c8953bf"}, - {file = "cffi-1.17.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0d46ee4764b88b91f16661a8befc6bfb24806d885e27436fdc292ed7e6f6d058"}, - {file = "cffi-1.17.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6f76a90c345796c01d85e6332e81cab6d70de83b829cf1d9762d0a3da59c7932"}, - {file = "cffi-1.17.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:0e60821d312f99d3e1569202518dddf10ae547e799d75aef3bca3a2d9e8ee693"}, - {file = "cffi-1.17.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:eb09b82377233b902d4c3fbeeb7ad731cdab579c6c6fda1f763cd779139e47c3"}, - {file = "cffi-1.17.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:24658baf6224d8f280e827f0a50c46ad819ec8ba380a42448e24459daf809cf4"}, - {file = "cffi-1.17.0-cp39-cp39-win32.whl", hash = "sha256:0fdacad9e0d9fc23e519efd5ea24a70348305e8d7d85ecbb1a5fa66dc834e7fb"}, - {file = "cffi-1.17.0-cp39-cp39-win_amd64.whl", hash = "sha256:7cbc78dc018596315d4e7841c8c3a7ae31cc4d638c9b627f87d52e8abaaf2d29"}, - {file = "cffi-1.17.0.tar.gz", hash = "sha256:f3157624b7558b914cb039fd1af735e5e8049a87c817cc215109ad1c8779df76"}, + {file = "cffi-1.17.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:df8b1c11f177bc2313ec4b2d46baec87a5f3e71fc8b45dab2ee7cae86d9aba14"}, + {file = "cffi-1.17.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8f2cdc858323644ab277e9bb925ad72ae0e67f69e804f4898c070998d50b1a67"}, + {file = "cffi-1.17.1-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:edae79245293e15384b51f88b00613ba9f7198016a5948b5dddf4917d4d26382"}, + {file = "cffi-1.17.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:45398b671ac6d70e67da8e4224a065cec6a93541bb7aebe1b198a61b58c7b702"}, + {file = "cffi-1.17.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ad9413ccdeda48c5afdae7e4fa2192157e991ff761e7ab8fdd8926f40b160cc3"}, + {file = "cffi-1.17.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5da5719280082ac6bd9aa7becb3938dc9f9cbd57fac7d2871717b1feb0902ab6"}, + {file = "cffi-1.17.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2bb1a08b8008b281856e5971307cc386a8e9c5b625ac297e853d36da6efe9c17"}, + {file = "cffi-1.17.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:045d61c734659cc045141be4bae381a41d89b741f795af1dd018bfb532fd0df8"}, + {file = "cffi-1.17.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:6883e737d7d9e4899a8a695e00ec36bd4e5e4f18fabe0aca0efe0a4b44cdb13e"}, + {file = "cffi-1.17.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:6b8b4a92e1c65048ff98cfe1f735ef8f1ceb72e3d5f0c25fdb12087a23da22be"}, + {file = "cffi-1.17.1-cp310-cp310-win32.whl", hash = "sha256:c9c3d058ebabb74db66e431095118094d06abf53284d9c81f27300d0e0d8bc7c"}, + {file = "cffi-1.17.1-cp310-cp310-win_amd64.whl", hash = "sha256:0f048dcf80db46f0098ccac01132761580d28e28bc0f78ae0d58048063317e15"}, + {file = "cffi-1.17.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a45e3c6913c5b87b3ff120dcdc03f6131fa0065027d0ed7ee6190736a74cd401"}, + {file = "cffi-1.17.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:30c5e0cb5ae493c04c8b42916e52ca38079f1b235c2f8ae5f4527b963c401caf"}, + {file = "cffi-1.17.1-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f75c7ab1f9e4aca5414ed4d8e5c0e303a34f4421f8a0d47a4d019ceff0ab6af4"}, + {file = "cffi-1.17.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a1ed2dd2972641495a3ec98445e09766f077aee98a1c896dcb4ad0d303628e41"}, + {file = "cffi-1.17.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:46bf43160c1a35f7ec506d254e5c890f3c03648a4dbac12d624e4490a7046cd1"}, + {file = "cffi-1.17.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a24ed04c8ffd54b0729c07cee15a81d964e6fee0e3d4d342a27b020d22959dc6"}, + {file = "cffi-1.17.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:610faea79c43e44c71e1ec53a554553fa22321b65fae24889706c0a84d4ad86d"}, + {file = "cffi-1.17.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:a9b15d491f3ad5d692e11f6b71f7857e7835eb677955c00cc0aefcd0669adaf6"}, + {file = "cffi-1.17.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:de2ea4b5833625383e464549fec1bc395c1bdeeb5f25c4a3a82b5a8c756ec22f"}, + {file = "cffi-1.17.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:fc48c783f9c87e60831201f2cce7f3b2e4846bf4d8728eabe54d60700b318a0b"}, + {file = "cffi-1.17.1-cp311-cp311-win32.whl", hash = "sha256:85a950a4ac9c359340d5963966e3e0a94a676bd6245a4b55bc43949eee26a655"}, + {file = "cffi-1.17.1-cp311-cp311-win_amd64.whl", hash = "sha256:caaf0640ef5f5517f49bc275eca1406b0ffa6aa184892812030f04c2abf589a0"}, + {file = "cffi-1.17.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:805b4371bf7197c329fcb3ead37e710d1bca9da5d583f5073b799d5c5bd1eee4"}, + {file = "cffi-1.17.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:733e99bc2df47476e3848417c5a4540522f234dfd4ef3ab7fafdf555b082ec0c"}, + {file = "cffi-1.17.1-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1257bdabf294dceb59f5e70c64a3e2f462c30c7ad68092d01bbbfb1c16b1ba36"}, + {file = "cffi-1.17.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:da95af8214998d77a98cc14e3a3bd00aa191526343078b530ceb0bd710fb48a5"}, + {file = "cffi-1.17.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d63afe322132c194cf832bfec0dc69a99fb9bb6bbd550f161a49e9e855cc78ff"}, + {file = "cffi-1.17.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f79fc4fc25f1c8698ff97788206bb3c2598949bfe0fef03d299eb1b5356ada99"}, + {file = "cffi-1.17.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b62ce867176a75d03a665bad002af8e6d54644fad99a3c70905c543130e39d93"}, + {file = "cffi-1.17.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:386c8bf53c502fff58903061338ce4f4950cbdcb23e2902d86c0f722b786bbe3"}, + {file = "cffi-1.17.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4ceb10419a9adf4460ea14cfd6bc43d08701f0835e979bf821052f1805850fe8"}, + {file = "cffi-1.17.1-cp312-cp312-win32.whl", hash = "sha256:a08d7e755f8ed21095a310a693525137cfe756ce62d066e53f502a83dc550f65"}, + {file = "cffi-1.17.1-cp312-cp312-win_amd64.whl", hash = "sha256:51392eae71afec0d0c8fb1a53b204dbb3bcabcb3c9b807eedf3e1e6ccf2de903"}, + {file = "cffi-1.17.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f3a2b4222ce6b60e2e8b337bb9596923045681d71e5a082783484d845390938e"}, + {file = "cffi-1.17.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0984a4925a435b1da406122d4d7968dd861c1385afe3b45ba82b750f229811e2"}, + {file = "cffi-1.17.1-cp313-cp313-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d01b12eeeb4427d3110de311e1774046ad344f5b1a7403101878976ecd7a10f3"}, + {file = "cffi-1.17.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:706510fe141c86a69c8ddc029c7910003a17353970cff3b904ff0686a5927683"}, + {file = "cffi-1.17.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:de55b766c7aa2e2a3092c51e0483d700341182f08e67c63630d5b6f200bb28e5"}, + {file = "cffi-1.17.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c59d6e989d07460165cc5ad3c61f9fd8f1b4796eacbd81cee78957842b834af4"}, + {file = "cffi-1.17.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd398dbc6773384a17fe0d3e7eeb8d1a21c2200473ee6806bb5e6a8e62bb73dd"}, + {file = "cffi-1.17.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:3edc8d958eb099c634dace3c7e16560ae474aa3803a5df240542b305d14e14ed"}, + {file = "cffi-1.17.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:72e72408cad3d5419375fc87d289076ee319835bdfa2caad331e377589aebba9"}, + {file = "cffi-1.17.1-cp313-cp313-win32.whl", hash = "sha256:e03eab0a8677fa80d646b5ddece1cbeaf556c313dcfac435ba11f107ba117b5d"}, + {file = "cffi-1.17.1-cp313-cp313-win_amd64.whl", hash = "sha256:f6a16c31041f09ead72d69f583767292f750d24913dadacf5756b966aacb3f1a"}, + {file = "cffi-1.17.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:636062ea65bd0195bc012fea9321aca499c0504409f413dc88af450b57ffd03b"}, + {file = "cffi-1.17.1-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c7eac2ef9b63c79431bc4b25f1cd649d7f061a28808cbc6c47b534bd789ef964"}, + {file = "cffi-1.17.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e221cf152cff04059d011ee126477f0d9588303eb57e88923578ace7baad17f9"}, + {file = "cffi-1.17.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:31000ec67d4221a71bd3f67df918b1f88f676f1c3b535a7eb473255fdc0b83fc"}, + {file = "cffi-1.17.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6f17be4345073b0a7b8ea599688f692ac3ef23ce28e5df79c04de519dbc4912c"}, + {file = "cffi-1.17.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0e2b1fac190ae3ebfe37b979cc1ce69c81f4e4fe5746bb401dca63a9062cdaf1"}, + {file = "cffi-1.17.1-cp38-cp38-win32.whl", hash = "sha256:7596d6620d3fa590f677e9ee430df2958d2d6d6de2feeae5b20e82c00b76fbf8"}, + {file = "cffi-1.17.1-cp38-cp38-win_amd64.whl", hash = "sha256:78122be759c3f8a014ce010908ae03364d00a1f81ab5c7f4a7a5120607ea56e1"}, + {file = "cffi-1.17.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b2ab587605f4ba0bf81dc0cb08a41bd1c0a5906bd59243d56bad7668a6fc6c16"}, + {file = "cffi-1.17.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:28b16024becceed8c6dfbc75629e27788d8a3f9030691a1dbf9821a128b22c36"}, + {file = "cffi-1.17.1-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1d599671f396c4723d016dbddb72fe8e0397082b0a77a4fab8028923bec050e8"}, + {file = "cffi-1.17.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ca74b8dbe6e8e8263c0ffd60277de77dcee6c837a3d0881d8c1ead7268c9e576"}, + {file = "cffi-1.17.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f7f5baafcc48261359e14bcd6d9bff6d4b28d9103847c9e136694cb0501aef87"}, + {file = "cffi-1.17.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:98e3969bcff97cae1b2def8ba499ea3d6f31ddfdb7635374834cf89a1a08ecf0"}, + {file = "cffi-1.17.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cdf5ce3acdfd1661132f2a9c19cac174758dc2352bfe37d98aa7512c6b7178b3"}, + {file = "cffi-1.17.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:9755e4345d1ec879e3849e62222a18c7174d65a6a92d5b346b1863912168b595"}, + {file = "cffi-1.17.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:f1e22e8c4419538cb197e4dd60acc919d7696e5ef98ee4da4e01d3f8cfa4cc5a"}, + {file = "cffi-1.17.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:c03e868a0b3bc35839ba98e74211ed2b05d2119be4e8a0f224fba9384f1fe02e"}, + {file = "cffi-1.17.1-cp39-cp39-win32.whl", hash = "sha256:e31ae45bc2e29f6b2abd0de1cc3b9d5205aa847cafaecb8af1476a609a2f6eb7"}, + {file = "cffi-1.17.1-cp39-cp39-win_amd64.whl", hash = "sha256:d016c76bdd850f3c626af19b0542c9677ba156e4ee4fccfdd7848803533ef662"}, + {file = "cffi-1.17.1.tar.gz", hash = "sha256:1c39c6016c32bc48dd54561950ebd6836e1670f2ae46128f67cf49e789c52824"}, ] [package.dependencies] @@ -746,33 +746,33 @@ tests = ["pytest", "pytest-cov", "pytest-xdist"] [[package]] name = "debugpy" -version = "1.8.5" +version = "1.8.6" description = "An implementation of the Debug Adapter Protocol for Python" optional = false python-versions = ">=3.8" files = [ - {file = "debugpy-1.8.5-cp310-cp310-macosx_12_0_x86_64.whl", hash = "sha256:7e4d594367d6407a120b76bdaa03886e9eb652c05ba7f87e37418426ad2079f7"}, - {file = "debugpy-1.8.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4413b7a3ede757dc33a273a17d685ea2b0c09dbd312cc03f5534a0fd4d40750a"}, - {file = "debugpy-1.8.5-cp310-cp310-win32.whl", hash = "sha256:dd3811bd63632bb25eda6bd73bea8e0521794cda02be41fa3160eb26fc29e7ed"}, - {file = "debugpy-1.8.5-cp310-cp310-win_amd64.whl", hash = "sha256:b78c1250441ce893cb5035dd6f5fc12db968cc07f91cc06996b2087f7cefdd8e"}, - {file = "debugpy-1.8.5-cp311-cp311-macosx_12_0_universal2.whl", hash = "sha256:606bccba19f7188b6ea9579c8a4f5a5364ecd0bf5a0659c8a5d0e10dcee3032a"}, - {file = "debugpy-1.8.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:db9fb642938a7a609a6c865c32ecd0d795d56c1aaa7a7a5722d77855d5e77f2b"}, - {file = "debugpy-1.8.5-cp311-cp311-win32.whl", hash = "sha256:4fbb3b39ae1aa3e5ad578f37a48a7a303dad9a3d018d369bc9ec629c1cfa7408"}, - {file = "debugpy-1.8.5-cp311-cp311-win_amd64.whl", hash = "sha256:345d6a0206e81eb68b1493ce2fbffd57c3088e2ce4b46592077a943d2b968ca3"}, - {file = "debugpy-1.8.5-cp312-cp312-macosx_12_0_universal2.whl", hash = "sha256:5b5c770977c8ec6c40c60d6f58cacc7f7fe5a45960363d6974ddb9b62dbee156"}, - {file = "debugpy-1.8.5-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0a65b00b7cdd2ee0c2cf4c7335fef31e15f1b7056c7fdbce9e90193e1a8c8cb"}, - {file = "debugpy-1.8.5-cp312-cp312-win32.whl", hash = "sha256:c9f7c15ea1da18d2fcc2709e9f3d6de98b69a5b0fff1807fb80bc55f906691f7"}, - {file = "debugpy-1.8.5-cp312-cp312-win_amd64.whl", hash = "sha256:28ced650c974aaf179231668a293ecd5c63c0a671ae6d56b8795ecc5d2f48d3c"}, - {file = "debugpy-1.8.5-cp38-cp38-macosx_12_0_x86_64.whl", hash = "sha256:3df6692351172a42af7558daa5019651f898fc67450bf091335aa8a18fbf6f3a"}, - {file = "debugpy-1.8.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1cd04a73eb2769eb0bfe43f5bfde1215c5923d6924b9b90f94d15f207a402226"}, - {file = "debugpy-1.8.5-cp38-cp38-win32.whl", hash = "sha256:8f913ee8e9fcf9d38a751f56e6de12a297ae7832749d35de26d960f14280750a"}, - {file = "debugpy-1.8.5-cp38-cp38-win_amd64.whl", hash = "sha256:a697beca97dad3780b89a7fb525d5e79f33821a8bc0c06faf1f1289e549743cf"}, - {file = "debugpy-1.8.5-cp39-cp39-macosx_12_0_x86_64.whl", hash = "sha256:0a1029a2869d01cb777216af8c53cda0476875ef02a2b6ff8b2f2c9a4b04176c"}, - {file = "debugpy-1.8.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e84c276489e141ed0b93b0af648eef891546143d6a48f610945416453a8ad406"}, - {file = "debugpy-1.8.5-cp39-cp39-win32.whl", hash = "sha256:ad84b7cde7fd96cf6eea34ff6c4a1b7887e0fe2ea46e099e53234856f9d99a34"}, - {file = "debugpy-1.8.5-cp39-cp39-win_amd64.whl", hash = "sha256:7b0fe36ed9d26cb6836b0a51453653f8f2e347ba7348f2bbfe76bfeb670bfb1c"}, - {file = "debugpy-1.8.5-py2.py3-none-any.whl", hash = "sha256:55919dce65b471eff25901acf82d328bbd5b833526b6c1364bd5133754777a44"}, - {file = "debugpy-1.8.5.zip", hash = "sha256:b2112cfeb34b4507399d298fe7023a16656fc553ed5246536060ca7bd0e668d0"}, + {file = "debugpy-1.8.6-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:30f467c5345d9dfdcc0afdb10e018e47f092e383447500f125b4e013236bf14b"}, + {file = "debugpy-1.8.6-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5d73d8c52614432f4215d0fe79a7e595d0dd162b5c15233762565be2f014803b"}, + {file = "debugpy-1.8.6-cp310-cp310-win32.whl", hash = "sha256:e3e182cd98eac20ee23a00653503315085b29ab44ed66269482349d307b08df9"}, + {file = "debugpy-1.8.6-cp310-cp310-win_amd64.whl", hash = "sha256:e3a82da039cfe717b6fb1886cbbe5c4a3f15d7df4765af857f4307585121c2dd"}, + {file = "debugpy-1.8.6-cp311-cp311-macosx_14_0_universal2.whl", hash = "sha256:67479a94cf5fd2c2d88f9615e087fcb4fec169ec780464a3f2ba4a9a2bb79955"}, + {file = "debugpy-1.8.6-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9fb8653f6cbf1dd0a305ac1aa66ec246002145074ea57933978346ea5afdf70b"}, + {file = "debugpy-1.8.6-cp311-cp311-win32.whl", hash = "sha256:cdaf0b9691879da2d13fa39b61c01887c34558d1ff6e5c30e2eb698f5384cd43"}, + {file = "debugpy-1.8.6-cp311-cp311-win_amd64.whl", hash = "sha256:43996632bee7435583952155c06881074b9a742a86cee74e701d87ca532fe833"}, + {file = "debugpy-1.8.6-cp312-cp312-macosx_14_0_universal2.whl", hash = "sha256:db891b141fc6ee4b5fc6d1cc8035ec329cabc64bdd2ae672b4550c87d4ecb128"}, + {file = "debugpy-1.8.6-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:567419081ff67da766c898ccf21e79f1adad0e321381b0dfc7a9c8f7a9347972"}, + {file = "debugpy-1.8.6-cp312-cp312-win32.whl", hash = "sha256:c9834dfd701a1f6bf0f7f0b8b1573970ae99ebbeee68314116e0ccc5c78eea3c"}, + {file = "debugpy-1.8.6-cp312-cp312-win_amd64.whl", hash = "sha256:e4ce0570aa4aca87137890d23b86faeadf184924ad892d20c54237bcaab75d8f"}, + {file = "debugpy-1.8.6-cp38-cp38-macosx_14_0_x86_64.whl", hash = "sha256:df5dc9eb4ca050273b8e374a4cd967c43be1327eeb42bfe2f58b3cdfe7c68dcb"}, + {file = "debugpy-1.8.6-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0a85707c6a84b0c5b3db92a2df685b5230dd8fb8c108298ba4f11dba157a615a"}, + {file = "debugpy-1.8.6-cp38-cp38-win32.whl", hash = "sha256:538c6cdcdcdad310bbefd96d7850be1cd46e703079cc9e67d42a9ca776cdc8a8"}, + {file = "debugpy-1.8.6-cp38-cp38-win_amd64.whl", hash = "sha256:22140bc02c66cda6053b6eb56dfe01bbe22a4447846581ba1dd6df2c9f97982d"}, + {file = "debugpy-1.8.6-cp39-cp39-macosx_14_0_x86_64.whl", hash = "sha256:c1cef65cffbc96e7b392d9178dbfd524ab0750da6c0023c027ddcac968fd1caa"}, + {file = "debugpy-1.8.6-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f1e60bd06bb3cc5c0e957df748d1fab501e01416c43a7bdc756d2a992ea1b881"}, + {file = "debugpy-1.8.6-cp39-cp39-win32.whl", hash = "sha256:f7158252803d0752ed5398d291dee4c553bb12d14547c0e1843ab74ee9c31123"}, + {file = "debugpy-1.8.6-cp39-cp39-win_amd64.whl", hash = "sha256:3358aa619a073b620cd0d51d8a6176590af24abcc3fe2e479929a154bf591b51"}, + {file = "debugpy-1.8.6-py2.py3-none-any.whl", hash = "sha256:b48892df4d810eff21d3ef37274f4c60d32cdcafc462ad5647239036b0f0649f"}, + {file = "debugpy-1.8.6.zip", hash = "sha256:c931a9371a86784cee25dec8d65bc2dc7a21f3f1552e3833d9ef8f919d22280a"}, ] [[package]] @@ -846,13 +846,13 @@ test = ["pytest (>=6)"] [[package]] name = "executing" -version = "2.0.1" +version = "2.1.0" description = "Get the currently executing AST node of a frame, and other information" optional = false -python-versions = ">=3.5" +python-versions = ">=3.8" files = [ - {file = "executing-2.0.1-py2.py3-none-any.whl", hash = "sha256:eac49ca94516ccc753f9fb5ce82603156e590b27525a8bc32cce8ae302eb61bc"}, - {file = "executing-2.0.1.tar.gz", hash = "sha256:35afe2ce3affba8ee97f2d69927fa823b08b472b7b994e36a52a964b93d16147"}, + {file = "executing-2.1.0-py2.py3-none-any.whl", hash = "sha256:8d63781349375b5ebccc3142f4b30350c0cd9c79f921cde38be2be4637e98eaf"}, + {file = "executing-2.1.0.tar.gz", hash = "sha256:8ea27ddd260da8150fa5a708269c4a10e76161e2496ec3e587da9e3c0fe4b9ab"}, ] [package.extras] @@ -874,69 +874,75 @@ devel = ["colorama", "json-spec", "jsonschema", "pylint", "pytest", "pytest-benc [[package]] name = "filelock" -version = "3.15.4" +version = "3.16.1" description = "A platform independent file lock." optional = false python-versions = ">=3.8" files = [ - {file = "filelock-3.15.4-py3-none-any.whl", hash = "sha256:6ca1fffae96225dab4c6eaf1c4f4f28cd2568d3ec2a44e15a08520504de468e7"}, - {file = "filelock-3.15.4.tar.gz", hash = "sha256:2207938cbc1844345cb01a5a95524dae30f0ce089eba5b00378295a17e3e90cb"}, + {file = "filelock-3.16.1-py3-none-any.whl", hash = "sha256:2082e5703d51fbf98ea75855d9d5527e33d8ff23099bec374a134febee6946b0"}, + {file = "filelock-3.16.1.tar.gz", hash = "sha256:c249fbfcd5db47e5e2d6d62198e565475ee65e4831e2561c8e313fa7eb961435"}, ] [package.extras] -docs = ["furo (>=2023.9.10)", "sphinx (>=7.2.6)", "sphinx-autodoc-typehints (>=1.25.2)"] -testing = ["covdefaults (>=2.3)", "coverage (>=7.3.2)", "diff-cover (>=8.0.1)", "pytest (>=7.4.3)", "pytest-asyncio (>=0.21)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)", "pytest-timeout (>=2.2)", "virtualenv (>=20.26.2)"] -typing = ["typing-extensions (>=4.8)"] +docs = ["furo (>=2024.8.6)", "sphinx (>=8.0.2)", "sphinx-autodoc-typehints (>=2.4.1)"] +testing = ["covdefaults (>=2.3)", "coverage (>=7.6.1)", "diff-cover (>=9.2)", "pytest (>=8.3.3)", "pytest-asyncio (>=0.24)", "pytest-cov (>=5)", "pytest-mock (>=3.14)", "pytest-timeout (>=2.3.1)", "virtualenv (>=20.26.4)"] +typing = ["typing-extensions (>=4.12.2)"] [[package]] name = "fonttools" -version = "4.53.1" +version = "4.54.1" description = "Tools to manipulate font files" optional = false python-versions = ">=3.8" files = [ - {file = "fonttools-4.53.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:0679a30b59d74b6242909945429dbddb08496935b82f91ea9bf6ad240ec23397"}, - {file = "fonttools-4.53.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e8bf06b94694251861ba7fdeea15c8ec0967f84c3d4143ae9daf42bbc7717fe3"}, - {file = "fonttools-4.53.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b96cd370a61f4d083c9c0053bf634279b094308d52fdc2dd9a22d8372fdd590d"}, - {file = "fonttools-4.53.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a1c7c5aa18dd3b17995898b4a9b5929d69ef6ae2af5b96d585ff4005033d82f0"}, - {file = "fonttools-4.53.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:e013aae589c1c12505da64a7d8d023e584987e51e62006e1bb30d72f26522c41"}, - {file = "fonttools-4.53.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:9efd176f874cb6402e607e4cc9b4a9cd584d82fc34a4b0c811970b32ba62501f"}, - {file = "fonttools-4.53.1-cp310-cp310-win32.whl", hash = "sha256:c8696544c964500aa9439efb6761947393b70b17ef4e82d73277413f291260a4"}, - {file = "fonttools-4.53.1-cp310-cp310-win_amd64.whl", hash = "sha256:8959a59de5af6d2bec27489e98ef25a397cfa1774b375d5787509c06659b3671"}, - {file = "fonttools-4.53.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:da33440b1413bad53a8674393c5d29ce64d8c1a15ef8a77c642ffd900d07bfe1"}, - {file = "fonttools-4.53.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:5ff7e5e9bad94e3a70c5cd2fa27f20b9bb9385e10cddab567b85ce5d306ea923"}, - {file = "fonttools-4.53.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c6e7170d675d12eac12ad1a981d90f118c06cf680b42a2d74c6c931e54b50719"}, - {file = "fonttools-4.53.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bee32ea8765e859670c4447b0817514ca79054463b6b79784b08a8df3a4d78e3"}, - {file = "fonttools-4.53.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:6e08f572625a1ee682115223eabebc4c6a2035a6917eac6f60350aba297ccadb"}, - {file = "fonttools-4.53.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b21952c092ffd827504de7e66b62aba26fdb5f9d1e435c52477e6486e9d128b2"}, - {file = "fonttools-4.53.1-cp311-cp311-win32.whl", hash = "sha256:9dfdae43b7996af46ff9da520998a32b105c7f098aeea06b2226b30e74fbba88"}, - {file = "fonttools-4.53.1-cp311-cp311-win_amd64.whl", hash = "sha256:d4d0096cb1ac7a77b3b41cd78c9b6bc4a400550e21dc7a92f2b5ab53ed74eb02"}, - {file = "fonttools-4.53.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:d92d3c2a1b39631a6131c2fa25b5406855f97969b068e7e08413325bc0afba58"}, - {file = "fonttools-4.53.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3b3c8ebafbee8d9002bd8f1195d09ed2bd9ff134ddec37ee8f6a6375e6a4f0e8"}, - {file = "fonttools-4.53.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:32f029c095ad66c425b0ee85553d0dc326d45d7059dbc227330fc29b43e8ba60"}, - {file = "fonttools-4.53.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:10f5e6c3510b79ea27bb1ebfcc67048cde9ec67afa87c7dd7efa5c700491ac7f"}, - {file = "fonttools-4.53.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:f677ce218976496a587ab17140da141557beb91d2a5c1a14212c994093f2eae2"}, - {file = "fonttools-4.53.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:9e6ceba2a01b448e36754983d376064730690401da1dd104ddb543519470a15f"}, - {file = "fonttools-4.53.1-cp312-cp312-win32.whl", hash = "sha256:791b31ebbc05197d7aa096bbc7bd76d591f05905d2fd908bf103af4488e60670"}, - {file = "fonttools-4.53.1-cp312-cp312-win_amd64.whl", hash = "sha256:6ed170b5e17da0264b9f6fae86073be3db15fa1bd74061c8331022bca6d09bab"}, - {file = "fonttools-4.53.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:c818c058404eb2bba05e728d38049438afd649e3c409796723dfc17cd3f08749"}, - {file = "fonttools-4.53.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:651390c3b26b0c7d1f4407cad281ee7a5a85a31a110cbac5269de72a51551ba2"}, - {file = "fonttools-4.53.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e54f1bba2f655924c1138bbc7fa91abd61f45c68bd65ab5ed985942712864bbb"}, - {file = "fonttools-4.53.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c9cd19cf4fe0595ebdd1d4915882b9440c3a6d30b008f3cc7587c1da7b95be5f"}, - {file = "fonttools-4.53.1-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:2af40ae9cdcb204fc1d8f26b190aa16534fcd4f0df756268df674a270eab575d"}, - {file = "fonttools-4.53.1-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:35250099b0cfb32d799fb5d6c651220a642fe2e3c7d2560490e6f1d3f9ae9169"}, - {file = "fonttools-4.53.1-cp38-cp38-win32.whl", hash = "sha256:f08df60fbd8d289152079a65da4e66a447efc1d5d5a4d3f299cdd39e3b2e4a7d"}, - {file = "fonttools-4.53.1-cp38-cp38-win_amd64.whl", hash = "sha256:7b6b35e52ddc8fb0db562133894e6ef5b4e54e1283dff606fda3eed938c36fc8"}, - {file = "fonttools-4.53.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:75a157d8d26c06e64ace9df037ee93a4938a4606a38cb7ffaf6635e60e253b7a"}, - {file = "fonttools-4.53.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:4824c198f714ab5559c5be10fd1adf876712aa7989882a4ec887bf1ef3e00e31"}, - {file = "fonttools-4.53.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:becc5d7cb89c7b7afa8321b6bb3dbee0eec2b57855c90b3e9bf5fb816671fa7c"}, - {file = "fonttools-4.53.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:84ec3fb43befb54be490147b4a922b5314e16372a643004f182babee9f9c3407"}, - {file = "fonttools-4.53.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:73379d3ffdeecb376640cd8ed03e9d2d0e568c9d1a4e9b16504a834ebadc2dfb"}, - {file = "fonttools-4.53.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:02569e9a810f9d11f4ae82c391ebc6fb5730d95a0657d24d754ed7763fb2d122"}, - {file = "fonttools-4.53.1-cp39-cp39-win32.whl", hash = "sha256:aae7bd54187e8bf7fd69f8ab87b2885253d3575163ad4d669a262fe97f0136cb"}, - {file = "fonttools-4.53.1-cp39-cp39-win_amd64.whl", hash = "sha256:e5b708073ea3d684235648786f5f6153a48dc8762cdfe5563c57e80787c29fbb"}, - {file = "fonttools-4.53.1-py3-none-any.whl", hash = "sha256:f1f8758a2ad110bd6432203a344269f445a2907dc24ef6bccfd0ac4e14e0d71d"}, - {file = "fonttools-4.53.1.tar.gz", hash = "sha256:e128778a8e9bc11159ce5447f76766cefbd876f44bd79aff030287254e4752c4"}, + {file = "fonttools-4.54.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7ed7ee041ff7b34cc62f07545e55e1468808691dddfd315d51dd82a6b37ddef2"}, + {file = "fonttools-4.54.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:41bb0b250c8132b2fcac148e2e9198e62ff06f3cc472065dff839327945c5882"}, + {file = "fonttools-4.54.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7965af9b67dd546e52afcf2e38641b5be956d68c425bef2158e95af11d229f10"}, + {file = "fonttools-4.54.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:278913a168f90d53378c20c23b80f4e599dca62fbffae4cc620c8eed476b723e"}, + {file = "fonttools-4.54.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:0e88e3018ac809b9662615072dcd6b84dca4c2d991c6d66e1970a112503bba7e"}, + {file = "fonttools-4.54.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:4aa4817f0031206e637d1e685251ac61be64d1adef111060df84fdcbc6ab6c44"}, + {file = "fonttools-4.54.1-cp310-cp310-win32.whl", hash = "sha256:7e3b7d44e18c085fd8c16dcc6f1ad6c61b71ff463636fcb13df7b1b818bd0c02"}, + {file = "fonttools-4.54.1-cp310-cp310-win_amd64.whl", hash = "sha256:dd9cc95b8d6e27d01e1e1f1fae8559ef3c02c76317da650a19047f249acd519d"}, + {file = "fonttools-4.54.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:5419771b64248484299fa77689d4f3aeed643ea6630b2ea750eeab219588ba20"}, + {file = "fonttools-4.54.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:301540e89cf4ce89d462eb23a89464fef50915255ece765d10eee8b2bf9d75b2"}, + {file = "fonttools-4.54.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:76ae5091547e74e7efecc3cbf8e75200bc92daaeb88e5433c5e3e95ea8ce5aa7"}, + {file = "fonttools-4.54.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:82834962b3d7c5ca98cb56001c33cf20eb110ecf442725dc5fdf36d16ed1ab07"}, + {file = "fonttools-4.54.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d26732ae002cc3d2ecab04897bb02ae3f11f06dd7575d1df46acd2f7c012a8d8"}, + {file = "fonttools-4.54.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:58974b4987b2a71ee08ade1e7f47f410c367cdfc5a94fabd599c88165f56213a"}, + {file = "fonttools-4.54.1-cp311-cp311-win32.whl", hash = "sha256:ab774fa225238986218a463f3fe151e04d8c25d7de09df7f0f5fce27b1243dbc"}, + {file = "fonttools-4.54.1-cp311-cp311-win_amd64.whl", hash = "sha256:07e005dc454eee1cc60105d6a29593459a06321c21897f769a281ff2d08939f6"}, + {file = "fonttools-4.54.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:54471032f7cb5fca694b5f1a0aaeba4af6e10ae989df408e0216f7fd6cdc405d"}, + {file = "fonttools-4.54.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8fa92cb248e573daab8d032919623cc309c005086d743afb014c836636166f08"}, + {file = "fonttools-4.54.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0a911591200114969befa7f2cb74ac148bce5a91df5645443371aba6d222e263"}, + {file = "fonttools-4.54.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:93d458c8a6a354dc8b48fc78d66d2a8a90b941f7fec30e94c7ad9982b1fa6bab"}, + {file = "fonttools-4.54.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:5eb2474a7c5be8a5331146758debb2669bf5635c021aee00fd7c353558fc659d"}, + {file = "fonttools-4.54.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:c9c563351ddc230725c4bdf7d9e1e92cbe6ae8553942bd1fb2b2ff0884e8b714"}, + {file = "fonttools-4.54.1-cp312-cp312-win32.whl", hash = "sha256:fdb062893fd6d47b527d39346e0c5578b7957dcea6d6a3b6794569370013d9ac"}, + {file = "fonttools-4.54.1-cp312-cp312-win_amd64.whl", hash = "sha256:e4564cf40cebcb53f3dc825e85910bf54835e8a8b6880d59e5159f0f325e637e"}, + {file = "fonttools-4.54.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:6e37561751b017cf5c40fce0d90fd9e8274716de327ec4ffb0df957160be3bff"}, + {file = "fonttools-4.54.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:357cacb988a18aace66e5e55fe1247f2ee706e01debc4b1a20d77400354cddeb"}, + {file = "fonttools-4.54.1-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f8e953cc0bddc2beaf3a3c3b5dd9ab7554677da72dfaf46951e193c9653e515a"}, + {file = "fonttools-4.54.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:58d29b9a294573d8319f16f2f79e42428ba9b6480442fa1836e4eb89c4d9d61c"}, + {file = "fonttools-4.54.1-cp313-cp313-win32.whl", hash = "sha256:9ef1b167e22709b46bf8168368b7b5d3efeaaa746c6d39661c1b4405b6352e58"}, + {file = "fonttools-4.54.1-cp313-cp313-win_amd64.whl", hash = "sha256:262705b1663f18c04250bd1242b0515d3bbae177bee7752be67c979b7d47f43d"}, + {file = "fonttools-4.54.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:ed2f80ca07025551636c555dec2b755dd005e2ea8fbeb99fc5cdff319b70b23b"}, + {file = "fonttools-4.54.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:9dc080e5a1c3b2656caff2ac2633d009b3a9ff7b5e93d0452f40cd76d3da3b3c"}, + {file = "fonttools-4.54.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1d152d1be65652fc65e695e5619e0aa0982295a95a9b29b52b85775243c06556"}, + {file = "fonttools-4.54.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8583e563df41fdecef31b793b4dd3af8a9caa03397be648945ad32717a92885b"}, + {file = "fonttools-4.54.1-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:0d1d353ef198c422515a3e974a1e8d5b304cd54a4c2eebcae708e37cd9eeffb1"}, + {file = "fonttools-4.54.1-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:fda582236fee135d4daeca056c8c88ec5f6f6d88a004a79b84a02547c8f57386"}, + {file = "fonttools-4.54.1-cp38-cp38-win32.whl", hash = "sha256:e7d82b9e56716ed32574ee106cabca80992e6bbdcf25a88d97d21f73a0aae664"}, + {file = "fonttools-4.54.1-cp38-cp38-win_amd64.whl", hash = "sha256:ada215fd079e23e060157aab12eba0d66704316547f334eee9ff26f8c0d7b8ab"}, + {file = "fonttools-4.54.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:f5b8a096e649768c2f4233f947cf9737f8dbf8728b90e2771e2497c6e3d21d13"}, + {file = "fonttools-4.54.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:4e10d2e0a12e18f4e2dd031e1bf7c3d7017be5c8dbe524d07706179f355c5dac"}, + {file = "fonttools-4.54.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:31c32d7d4b0958600eac75eaf524b7b7cb68d3a8c196635252b7a2c30d80e986"}, + {file = "fonttools-4.54.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c39287f5c8f4a0c5a55daf9eaf9ccd223ea59eed3f6d467133cc727d7b943a55"}, + {file = "fonttools-4.54.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:a7a310c6e0471602fe3bf8efaf193d396ea561486aeaa7adc1f132e02d30c4b9"}, + {file = "fonttools-4.54.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:d3b659d1029946f4ff9b6183984578041b520ce0f8fb7078bb37ec7445806b33"}, + {file = "fonttools-4.54.1-cp39-cp39-win32.whl", hash = "sha256:e96bc94c8cda58f577277d4a71f51c8e2129b8b36fd05adece6320dd3d57de8a"}, + {file = "fonttools-4.54.1-cp39-cp39-win_amd64.whl", hash = "sha256:e8a4b261c1ef91e7188a30571be6ad98d1c6d9fa2427244c545e2fa0a2494dd7"}, + {file = "fonttools-4.54.1-py3-none-any.whl", hash = "sha256:37cddd62d83dc4f72f7c3f3c2bcf2697e89a30efb152079896544a93907733bd"}, + {file = "fonttools-4.54.1.tar.gz", hash = "sha256:957f669d4922f92c171ba01bef7f29410668db09f6c02111e22b2bce446f3285"}, ] [package.extras] @@ -1015,13 +1021,13 @@ test = ["coverage[toml]", "ddt (>=1.1.1,!=1.4.3)", "mock", "mypy", "pre-commit", [[package]] name = "griffe" -version = "1.1.0" +version = "1.3.1" description = "Signatures for entire Python programs. Extract the structure, the frame, the skeleton of your project, to generate API documentation or find breaking changes in your API." optional = false python-versions = ">=3.8" files = [ - {file = "griffe-1.1.0-py3-none-any.whl", hash = "sha256:38ccc5721571c95ae427123074cf0dc0d36bce7c9701ab2ada9fe0566ff50c10"}, - {file = "griffe-1.1.0.tar.gz", hash = "sha256:c6328cbdec0d449549c1cc332f59227cd5603f903479d73e4425d828b782ffc3"}, + {file = "griffe-1.3.1-py3-none-any.whl", hash = "sha256:940aeb630bc3054b4369567f150b6365be6f11eef46b0ed8623aea96e6d17b19"}, + {file = "griffe-1.3.1.tar.gz", hash = "sha256:3f86a716b631a4c0f96a43cb75d05d3c85975003c20540426c0eba3b0581c56a"}, ] [package.dependencies] @@ -1062,13 +1068,13 @@ trio = ["trio (>=0.22.0,<0.26.0)"] [[package]] name = "httpx" -version = "0.27.0" +version = "0.27.2" description = "The next generation HTTP client." optional = false python-versions = ">=3.8" files = [ - {file = "httpx-0.27.0-py3-none-any.whl", hash = "sha256:71d5465162c13681bff01ad59b2cc68dd838ea1f10e51574bac27103f00c91a5"}, - {file = "httpx-0.27.0.tar.gz", hash = "sha256:a0cb88a46f32dc874e04ee956e4c2764aba2aa228f650b06788ba6bda2962ab5"}, + {file = "httpx-0.27.2-py3-none-any.whl", hash = "sha256:7bb2708e112d8fdd7829cd4243970f0c223274051cb35ee80c03301ee29a3df0"}, + {file = "httpx-0.27.2.tar.gz", hash = "sha256:f7c2be1d2f3c3c3160d441802406b206c2b76f5947b11115e6df10c6c65e66c2"}, ] [package.dependencies] @@ -1083,16 +1089,17 @@ brotli = ["brotli", "brotlicffi"] cli = ["click (==8.*)", "pygments (==2.*)", "rich (>=10,<14)"] http2 = ["h2 (>=3,<5)"] socks = ["socksio (==1.*)"] +zstd = ["zstandard (>=0.18.0)"] [[package]] name = "identify" -version = "2.6.0" +version = "2.6.1" description = "File identification library for Python" optional = false python-versions = ">=3.8" files = [ - {file = "identify-2.6.0-py2.py3-none-any.whl", hash = "sha256:e79ae4406387a9d300332b5fd366d8994f1525e8414984e1a59e058b2eda2dd0"}, - {file = "identify-2.6.0.tar.gz", hash = "sha256:cb171c685bdc31bcc4c1734698736a7d5b6c8bf2e0c15117f4d469c8640ae5cf"}, + {file = "identify-2.6.1-py2.py3-none-any.whl", hash = "sha256:53863bcac7caf8d2ed85bd20312ea5dcfc22226800f6d6881f232d861db5a8f0"}, + {file = "identify-2.6.1.tar.gz", hash = "sha256:91478c5fb7c3aac5ff7bf9b4344f803843dc586832d5f110d672b19aa1984c98"}, ] [package.extras] @@ -1100,51 +1107,62 @@ license = ["ukkonen"] [[package]] name = "idna" -version = "3.7" +version = "3.10" description = "Internationalized Domain Names in Applications (IDNA)" optional = false -python-versions = ">=3.5" +python-versions = ">=3.6" files = [ - {file = "idna-3.7-py3-none-any.whl", hash = "sha256:82fee1fc78add43492d3a1898bfa6d8a904cc97d8427f683ed8e798d07761aa0"}, - {file = "idna-3.7.tar.gz", hash = "sha256:028ff3aadf0609c1fd278d8ea3089299412a7a8b9bd005dd08b9f8285bcb5cfc"}, + {file = "idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3"}, + {file = "idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9"}, ] +[package.extras] +all = ["flake8 (>=7.1.1)", "mypy (>=1.11.2)", "pytest (>=8.3.2)", "ruff (>=0.6.2)"] + [[package]] name = "importlib-metadata" -version = "8.2.0" +version = "8.5.0" description = "Read metadata from Python packages" optional = false python-versions = ">=3.8" files = [ - {file = "importlib_metadata-8.2.0-py3-none-any.whl", hash = "sha256:11901fa0c2f97919b288679932bb64febaeacf289d18ac84dd68cb2e74213369"}, - {file = "importlib_metadata-8.2.0.tar.gz", hash = "sha256:72e8d4399996132204f9a16dcc751af254a48f8d1b20b9ff0f98d4a8f901e73d"}, + {file = "importlib_metadata-8.5.0-py3-none-any.whl", hash = "sha256:45e54197d28b7a7f1559e60b95e7c567032b602131fbd588f1497f47880aa68b"}, + {file = "importlib_metadata-8.5.0.tar.gz", hash = "sha256:71522656f0abace1d072b9e5481a48f07c138e00f079c38c8f883823f9c26bd7"}, ] [package.dependencies] -zipp = ">=0.5" +zipp = ">=3.20" [package.extras] +check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1)"] +cover = ["pytest-cov"] doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] +enabler = ["pytest-enabler (>=2.2)"] perf = ["ipython"] -test = ["flufl.flake8", "importlib-resources (>=1.3)", "jaraco.test (>=5.4)", "packaging", "pyfakefs", "pytest (>=6,!=8.1.*)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy", "pytest-perf (>=0.9.2)", "pytest-ruff (>=0.2.1)"] +test = ["flufl.flake8", "importlib-resources (>=1.3)", "jaraco.test (>=5.4)", "packaging", "pyfakefs", "pytest (>=6,!=8.1.*)", "pytest-perf (>=0.9.2)"] +type = ["pytest-mypy"] [[package]] name = "importlib-resources" -version = "6.4.3" +version = "6.4.5" description = "Read resources from Python packages" optional = false python-versions = ">=3.8" files = [ - {file = "importlib_resources-6.4.3-py3-none-any.whl", hash = "sha256:2d6dfe3b9e055f72495c2085890837fc8c758984e209115c8792bddcb762cd93"}, - {file = "importlib_resources-6.4.3.tar.gz", hash = "sha256:4a202b9b9d38563b46da59221d77bb73862ab5d79d461307bcb826d725448b98"}, + {file = "importlib_resources-6.4.5-py3-none-any.whl", hash = "sha256:ac29d5f956f01d5e4bb63102a5a19957f1b9175e45649977264a1416783bb717"}, + {file = "importlib_resources-6.4.5.tar.gz", hash = "sha256:980862a1d16c9e147a59603677fa2aa5fd82b87f223b6cb870695bcfce830065"}, ] [package.dependencies] zipp = {version = ">=3.1.0", markers = "python_version < \"3.10\""} [package.extras] +check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1)"] +cover = ["pytest-cov"] doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] -test = ["jaraco.test (>=5.4)", "pytest (>=6,!=8.1.*)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy", "pytest-ruff (>=0.2.1)", "zipp (>=3.17)"] +enabler = ["pytest-enabler (>=2.2)"] +test = ["jaraco.test (>=5.4)", "pytest (>=6,!=8.1.*)", "zipp (>=3.17)"] +type = ["pytest-mypy"] [[package]] name = "iniconfig" @@ -1284,21 +1302,21 @@ testing = ["pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-ena [[package]] name = "jaraco-context" -version = "5.3.0" +version = "6.0.1" description = "Useful decorators and context managers" optional = false python-versions = ">=3.8" files = [ - {file = "jaraco.context-5.3.0-py3-none-any.whl", hash = "sha256:3e16388f7da43d384a1a7cd3452e72e14732ac9fe459678773a3608a812bf266"}, - {file = "jaraco.context-5.3.0.tar.gz", hash = "sha256:c2f67165ce1f9be20f32f650f25d8edfc1646a8aeee48ae06fb35f90763576d2"}, + {file = "jaraco.context-6.0.1-py3-none-any.whl", hash = "sha256:f797fc481b490edb305122c9181830a3a5b76d84ef6d1aef2fb9b47ab956f9e4"}, + {file = "jaraco_context-6.0.1.tar.gz", hash = "sha256:9bae4ea555cf0b14938dc0aee7c9f32ed303aa20a3b73e7dc80111628792d1b3"}, ] [package.dependencies] "backports.tarfile" = {version = "*", markers = "python_version < \"3.12\""} [package.extras] -docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] -testing = ["portend", "pytest (>=6,!=8.1.1)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy", "pytest-ruff (>=0.2.1)"] +doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] +test = ["portend", "pytest (>=6,!=8.1.*)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy", "pytest-ruff (>=0.2.1)"] [[package]] name = "jaraco-functools" @@ -1439,13 +1457,13 @@ referencing = ">=0.31.0" [[package]] name = "jupyter-client" -version = "8.6.2" +version = "8.6.3" description = "Jupyter protocol implementation and client libraries" optional = false python-versions = ">=3.8" files = [ - {file = "jupyter_client-8.6.2-py3-none-any.whl", hash = "sha256:50cbc5c66fd1b8f65ecb66bc490ab73217993632809b6e505687de18e9dea39f"}, - {file = "jupyter_client-8.6.2.tar.gz", hash = "sha256:2bda14d55ee5ba58552a8c53ae43d215ad9868853489213f37da060ced54d8df"}, + {file = "jupyter_client-8.6.3-py3-none-any.whl", hash = "sha256:e8a19cc986cc45905ac3362915f410f3af85424b4c0905e94fa5f2cb08e8f23f"}, + {file = "jupyter_client-8.6.3.tar.gz", hash = "sha256:35b3a0947c4a6e9d589eb97d7d4cd5e90f910ee73101611f01283732bd6d9419"}, ] [package.dependencies] @@ -1690,13 +1708,13 @@ test-ui = ["calysto-bash"] [[package]] name = "keyring" -version = "25.3.0" +version = "25.4.1" description = "Store and access your passwords safely." optional = false python-versions = ">=3.8" files = [ - {file = "keyring-25.3.0-py3-none-any.whl", hash = "sha256:8d963da00ccdf06e356acd9bf3b743208878751032d8599c6cc89eb51310ffae"}, - {file = "keyring-25.3.0.tar.gz", hash = "sha256:8d85a1ea5d6db8515b59e1c5d1d1678b03cf7fc8b8dcfb1651e8c4a524eb42ef"}, + {file = "keyring-25.4.1-py3-none-any.whl", hash = "sha256:5426f817cf7f6f007ba5ec722b1bcad95a75b27d780343772ad76b17cb47b0bf"}, + {file = "keyring-25.4.1.tar.gz", hash = "sha256:b07ebc55f3e8ed86ac81dd31ef14e81ace9dd9c3d4b5d77a6e9a2016d0d71a1b"}, ] [package.dependencies] @@ -1710,121 +1728,135 @@ pywin32-ctypes = {version = ">=0.2.0", markers = "sys_platform == \"win32\""} SecretStorage = {version = ">=3.2", markers = "sys_platform == \"linux\""} [package.extras] +check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1)"] completion = ["shtab (>=1.1.0)"] +cover = ["pytest-cov"] doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] -test = ["pyfakefs", "pytest (>=6,!=8.1.*)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy", "pytest-ruff (>=0.2.1)"] +enabler = ["pytest-enabler (>=2.2)"] +test = ["pyfakefs", "pytest (>=6,!=8.1.*)"] +type = ["pygobject-stubs", "pytest-mypy", "shtab", "types-pywin32"] [[package]] name = "kiwisolver" -version = "1.4.5" +version = "1.4.7" description = "A fast implementation of the Cassowary constraint solver" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "kiwisolver-1.4.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:05703cf211d585109fcd72207a31bb170a0f22144d68298dc5e61b3c946518af"}, - {file = "kiwisolver-1.4.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:146d14bebb7f1dc4d5fbf74f8a6cb15ac42baadee8912eb84ac0b3b2a3dc6ac3"}, - {file = "kiwisolver-1.4.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6ef7afcd2d281494c0a9101d5c571970708ad911d028137cd558f02b851c08b4"}, - {file = "kiwisolver-1.4.5-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:9eaa8b117dc8337728e834b9c6e2611f10c79e38f65157c4c38e9400286f5cb1"}, - {file = "kiwisolver-1.4.5-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:ec20916e7b4cbfb1f12380e46486ec4bcbaa91a9c448b97023fde0d5bbf9e4ff"}, - {file = "kiwisolver-1.4.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:39b42c68602539407884cf70d6a480a469b93b81b7701378ba5e2328660c847a"}, - {file = "kiwisolver-1.4.5-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:aa12042de0171fad672b6c59df69106d20d5596e4f87b5e8f76df757a7c399aa"}, - {file = "kiwisolver-1.4.5-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2a40773c71d7ccdd3798f6489aaac9eee213d566850a9533f8d26332d626b82c"}, - {file = "kiwisolver-1.4.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:19df6e621f6d8b4b9c4d45f40a66839294ff2bb235e64d2178f7522d9170ac5b"}, - {file = "kiwisolver-1.4.5-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:83d78376d0d4fd884e2c114d0621624b73d2aba4e2788182d286309ebdeed770"}, - {file = "kiwisolver-1.4.5-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:e391b1f0a8a5a10ab3b9bb6afcfd74f2175f24f8975fb87ecae700d1503cdee0"}, - {file = "kiwisolver-1.4.5-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:852542f9481f4a62dbb5dd99e8ab7aedfeb8fb6342349a181d4036877410f525"}, - {file = "kiwisolver-1.4.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:59edc41b24031bc25108e210c0def6f6c2191210492a972d585a06ff246bb79b"}, - {file = "kiwisolver-1.4.5-cp310-cp310-win32.whl", hash = "sha256:a6aa6315319a052b4ee378aa171959c898a6183f15c1e541821c5c59beaa0238"}, - {file = "kiwisolver-1.4.5-cp310-cp310-win_amd64.whl", hash = "sha256:d0ef46024e6a3d79c01ff13801cb19d0cad7fd859b15037aec74315540acc276"}, - {file = "kiwisolver-1.4.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:11863aa14a51fd6ec28688d76f1735f8f69ab1fabf388851a595d0721af042f5"}, - {file = "kiwisolver-1.4.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:8ab3919a9997ab7ef2fbbed0cc99bb28d3c13e6d4b1ad36e97e482558a91be90"}, - {file = "kiwisolver-1.4.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:fcc700eadbbccbf6bc1bcb9dbe0786b4b1cb91ca0dcda336eef5c2beed37b797"}, - {file = "kiwisolver-1.4.5-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dfdd7c0b105af050eb3d64997809dc21da247cf44e63dc73ff0fd20b96be55a9"}, - {file = "kiwisolver-1.4.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:76c6a5964640638cdeaa0c359382e5703e9293030fe730018ca06bc2010c4437"}, - {file = "kiwisolver-1.4.5-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bbea0db94288e29afcc4c28afbf3a7ccaf2d7e027489c449cf7e8f83c6346eb9"}, - {file = "kiwisolver-1.4.5-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ceec1a6bc6cab1d6ff5d06592a91a692f90ec7505d6463a88a52cc0eb58545da"}, - {file = "kiwisolver-1.4.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:040c1aebeda72197ef477a906782b5ab0d387642e93bda547336b8957c61022e"}, - {file = "kiwisolver-1.4.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:f91de7223d4c7b793867797bacd1ee53bfe7359bd70d27b7b58a04efbb9436c8"}, - {file = "kiwisolver-1.4.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:faae4860798c31530dd184046a900e652c95513796ef51a12bc086710c2eec4d"}, - {file = "kiwisolver-1.4.5-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:b0157420efcb803e71d1b28e2c287518b8808b7cf1ab8af36718fd0a2c453eb0"}, - {file = "kiwisolver-1.4.5-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:06f54715b7737c2fecdbf140d1afb11a33d59508a47bf11bb38ecf21dc9ab79f"}, - {file = "kiwisolver-1.4.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:fdb7adb641a0d13bdcd4ef48e062363d8a9ad4a182ac7647ec88f695e719ae9f"}, - {file = "kiwisolver-1.4.5-cp311-cp311-win32.whl", hash = "sha256:bb86433b1cfe686da83ce32a9d3a8dd308e85c76b60896d58f082136f10bffac"}, - {file = "kiwisolver-1.4.5-cp311-cp311-win_amd64.whl", hash = "sha256:6c08e1312a9cf1074d17b17728d3dfce2a5125b2d791527f33ffbe805200a355"}, - {file = "kiwisolver-1.4.5-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:32d5cf40c4f7c7b3ca500f8985eb3fb3a7dfc023215e876f207956b5ea26632a"}, - {file = "kiwisolver-1.4.5-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:f846c260f483d1fd217fe5ed7c173fb109efa6b1fc8381c8b7552c5781756192"}, - {file = "kiwisolver-1.4.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:5ff5cf3571589b6d13bfbfd6bcd7a3f659e42f96b5fd1c4830c4cf21d4f5ef45"}, - {file = "kiwisolver-1.4.5-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7269d9e5f1084a653d575c7ec012ff57f0c042258bf5db0954bf551c158466e7"}, - {file = "kiwisolver-1.4.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:da802a19d6e15dffe4b0c24b38b3af68e6c1a68e6e1d8f30148c83864f3881db"}, - {file = "kiwisolver-1.4.5-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3aba7311af82e335dd1e36ffff68aaca609ca6290c2cb6d821a39aa075d8e3ff"}, - {file = "kiwisolver-1.4.5-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:763773d53f07244148ccac5b084da5adb90bfaee39c197554f01b286cf869228"}, - {file = "kiwisolver-1.4.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2270953c0d8cdab5d422bee7d2007f043473f9d2999631c86a223c9db56cbd16"}, - {file = "kiwisolver-1.4.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:d099e745a512f7e3bbe7249ca835f4d357c586d78d79ae8f1dcd4d8adeb9bda9"}, - {file = "kiwisolver-1.4.5-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:74db36e14a7d1ce0986fa104f7d5637aea5c82ca6326ed0ec5694280942d1162"}, - {file = "kiwisolver-1.4.5-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:7e5bab140c309cb3a6ce373a9e71eb7e4873c70c2dda01df6820474f9889d6d4"}, - {file = "kiwisolver-1.4.5-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:0f114aa76dc1b8f636d077979c0ac22e7cd8f3493abbab152f20eb8d3cda71f3"}, - {file = "kiwisolver-1.4.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:88a2df29d4724b9237fc0c6eaf2a1adae0cdc0b3e9f4d8e7dc54b16812d2d81a"}, - {file = "kiwisolver-1.4.5-cp312-cp312-win32.whl", hash = "sha256:72d40b33e834371fd330fb1472ca19d9b8327acb79a5821d4008391db8e29f20"}, - {file = "kiwisolver-1.4.5-cp312-cp312-win_amd64.whl", hash = "sha256:2c5674c4e74d939b9d91dda0fae10597ac7521768fec9e399c70a1f27e2ea2d9"}, - {file = "kiwisolver-1.4.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:3a2b053a0ab7a3960c98725cfb0bf5b48ba82f64ec95fe06f1d06c99b552e130"}, - {file = "kiwisolver-1.4.5-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3cd32d6c13807e5c66a7cbb79f90b553642f296ae4518a60d8d76243b0ad2898"}, - {file = "kiwisolver-1.4.5-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:59ec7b7c7e1a61061850d53aaf8e93db63dce0c936db1fda2658b70e4a1be709"}, - {file = "kiwisolver-1.4.5-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:da4cfb373035def307905d05041c1d06d8936452fe89d464743ae7fb8371078b"}, - {file = "kiwisolver-1.4.5-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2400873bccc260b6ae184b2b8a4fec0e4082d30648eadb7c3d9a13405d861e89"}, - {file = "kiwisolver-1.4.5-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:1b04139c4236a0f3aff534479b58f6f849a8b351e1314826c2d230849ed48985"}, - {file = "kiwisolver-1.4.5-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:4e66e81a5779b65ac21764c295087de82235597a2293d18d943f8e9e32746265"}, - {file = "kiwisolver-1.4.5-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:7931d8f1f67c4be9ba1dd9c451fb0eeca1a25b89e4d3f89e828fe12a519b782a"}, - {file = "kiwisolver-1.4.5-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:b3f7e75f3015df442238cca659f8baa5f42ce2a8582727981cbfa15fee0ee205"}, - {file = "kiwisolver-1.4.5-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:bbf1d63eef84b2e8c89011b7f2235b1e0bf7dacc11cac9431fc6468e99ac77fb"}, - {file = "kiwisolver-1.4.5-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:4c380469bd3f970ef677bf2bcba2b6b0b4d5c75e7a020fb863ef75084efad66f"}, - {file = "kiwisolver-1.4.5-cp37-cp37m-win32.whl", hash = "sha256:9408acf3270c4b6baad483865191e3e582b638b1654a007c62e3efe96f09a9a3"}, - {file = "kiwisolver-1.4.5-cp37-cp37m-win_amd64.whl", hash = "sha256:5b94529f9b2591b7af5f3e0e730a4e0a41ea174af35a4fd067775f9bdfeee01a"}, - {file = "kiwisolver-1.4.5-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:11c7de8f692fc99816e8ac50d1d1aef4f75126eefc33ac79aac02c099fd3db71"}, - {file = "kiwisolver-1.4.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:53abb58632235cd154176ced1ae8f0d29a6657aa1aa9decf50b899b755bc2b93"}, - {file = "kiwisolver-1.4.5-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:88b9f257ca61b838b6f8094a62418421f87ac2a1069f7e896c36a7d86b5d4c29"}, - {file = "kiwisolver-1.4.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3195782b26fc03aa9c6913d5bad5aeb864bdc372924c093b0f1cebad603dd712"}, - {file = "kiwisolver-1.4.5-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fc579bf0f502e54926519451b920e875f433aceb4624a3646b3252b5caa9e0b6"}, - {file = "kiwisolver-1.4.5-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5a580c91d686376f0f7c295357595c5a026e6cbc3d77b7c36e290201e7c11ecb"}, - {file = "kiwisolver-1.4.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:cfe6ab8da05c01ba6fbea630377b5da2cd9bcbc6338510116b01c1bc939a2c18"}, - {file = "kiwisolver-1.4.5-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:d2e5a98f0ec99beb3c10e13b387f8db39106d53993f498b295f0c914328b1333"}, - {file = "kiwisolver-1.4.5-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:a51a263952b1429e429ff236d2f5a21c5125437861baeed77f5e1cc2d2c7c6da"}, - {file = "kiwisolver-1.4.5-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:3edd2fa14e68c9be82c5b16689e8d63d89fe927e56debd6e1dbce7a26a17f81b"}, - {file = "kiwisolver-1.4.5-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:74d1b44c6cfc897df648cc9fdaa09bc3e7679926e6f96df05775d4fb3946571c"}, - {file = "kiwisolver-1.4.5-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:76d9289ed3f7501012e05abb8358bbb129149dbd173f1f57a1bf1c22d19ab7cc"}, - {file = "kiwisolver-1.4.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:92dea1ffe3714fa8eb6a314d2b3c773208d865a0e0d35e713ec54eea08a66250"}, - {file = "kiwisolver-1.4.5-cp38-cp38-win32.whl", hash = "sha256:5c90ae8c8d32e472be041e76f9d2f2dbff4d0b0be8bd4041770eddb18cf49a4e"}, - {file = "kiwisolver-1.4.5-cp38-cp38-win_amd64.whl", hash = "sha256:c7940c1dc63eb37a67721b10d703247552416f719c4188c54e04334321351ced"}, - {file = "kiwisolver-1.4.5-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:9407b6a5f0d675e8a827ad8742e1d6b49d9c1a1da5d952a67d50ef5f4170b18d"}, - {file = "kiwisolver-1.4.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:15568384086b6df3c65353820a4473575dbad192e35010f622c6ce3eebd57af9"}, - {file = "kiwisolver-1.4.5-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:0dc9db8e79f0036e8173c466d21ef18e1befc02de8bf8aa8dc0813a6dc8a7046"}, - {file = "kiwisolver-1.4.5-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:cdc8a402aaee9a798b50d8b827d7ecf75edc5fb35ea0f91f213ff927c15f4ff0"}, - {file = "kiwisolver-1.4.5-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:6c3bd3cde54cafb87d74d8db50b909705c62b17c2099b8f2e25b461882e544ff"}, - {file = "kiwisolver-1.4.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:955e8513d07a283056b1396e9a57ceddbd272d9252c14f154d450d227606eb54"}, - {file = "kiwisolver-1.4.5-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:346f5343b9e3f00b8db8ba359350eb124b98c99efd0b408728ac6ebf38173958"}, - {file = "kiwisolver-1.4.5-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b9098e0049e88c6a24ff64545cdfc50807818ba6c1b739cae221bbbcbc58aad3"}, - {file = "kiwisolver-1.4.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:00bd361b903dc4bbf4eb165f24d1acbee754fce22ded24c3d56eec268658a5cf"}, - {file = "kiwisolver-1.4.5-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:7b8b454bac16428b22560d0a1cf0a09875339cab69df61d7805bf48919415901"}, - {file = "kiwisolver-1.4.5-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:f1d072c2eb0ad60d4c183f3fb44ac6f73fb7a8f16a2694a91f988275cbf352f9"}, - {file = "kiwisolver-1.4.5-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:31a82d498054cac9f6d0b53d02bb85811185bcb477d4b60144f915f3b3126342"}, - {file = "kiwisolver-1.4.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:6512cb89e334e4700febbffaaa52761b65b4f5a3cf33f960213d5656cea36a77"}, - {file = "kiwisolver-1.4.5-cp39-cp39-win32.whl", hash = "sha256:9db8ea4c388fdb0f780fe91346fd438657ea602d58348753d9fb265ce1bca67f"}, - {file = "kiwisolver-1.4.5-cp39-cp39-win_amd64.whl", hash = "sha256:59415f46a37f7f2efeec758353dd2eae1b07640d8ca0f0c42548ec4125492635"}, - {file = "kiwisolver-1.4.5-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:5c7b3b3a728dc6faf3fc372ef24f21d1e3cee2ac3e9596691d746e5a536de920"}, - {file = "kiwisolver-1.4.5-pp37-pypy37_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:620ced262a86244e2be10a676b646f29c34537d0d9cc8eb26c08f53d98013390"}, - {file = "kiwisolver-1.4.5-pp37-pypy37_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:378a214a1e3bbf5ac4a8708304318b4f890da88c9e6a07699c4ae7174c09a68d"}, - {file = "kiwisolver-1.4.5-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aaf7be1207676ac608a50cd08f102f6742dbfc70e8d60c4db1c6897f62f71523"}, - {file = "kiwisolver-1.4.5-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:ba55dce0a9b8ff59495ddd050a0225d58bd0983d09f87cfe2b6aec4f2c1234e4"}, - {file = "kiwisolver-1.4.5-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:fd32ea360bcbb92d28933fc05ed09bffcb1704ba3fc7942e81db0fd4f81a7892"}, - {file = "kiwisolver-1.4.5-pp38-pypy38_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:5e7139af55d1688f8b960ee9ad5adafc4ac17c1c473fe07133ac092310d76544"}, - {file = "kiwisolver-1.4.5-pp38-pypy38_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:dced8146011d2bc2e883f9bd68618b8247387f4bbec46d7392b3c3b032640126"}, - {file = "kiwisolver-1.4.5-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c9bf3325c47b11b2e51bca0824ea217c7cd84491d8ac4eefd1e409705ef092bd"}, - {file = "kiwisolver-1.4.5-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:5794cf59533bc3f1b1c821f7206a3617999db9fbefc345360aafe2e067514929"}, - {file = "kiwisolver-1.4.5-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:e368f200bbc2e4f905b8e71eb38b3c04333bddaa6a2464a6355487b02bb7fb09"}, - {file = "kiwisolver-1.4.5-pp39-pypy39_pp73-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e5d706eba36b4c4d5bc6c6377bb6568098765e990cfc21ee16d13963fab7b3e7"}, - {file = "kiwisolver-1.4.5-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:85267bd1aa8880a9c88a8cb71e18d3d64d2751a790e6ca6c27b8ccc724bcd5ad"}, - {file = "kiwisolver-1.4.5-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:210ef2c3a1f03272649aff1ef992df2e724748918c4bc2d5a90352849eb40bea"}, - {file = "kiwisolver-1.4.5-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:11d011a7574eb3b82bcc9c1a1d35c1d7075677fdd15de527d91b46bd35e935ee"}, - {file = "kiwisolver-1.4.5.tar.gz", hash = "sha256:e57e563a57fb22a142da34f38acc2fc1a5c864bc29ca1517a88abc963e60d6ec"}, + {file = "kiwisolver-1.4.7-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:8a9c83f75223d5e48b0bc9cb1bf2776cf01563e00ade8775ffe13b0b6e1af3a6"}, + {file = "kiwisolver-1.4.7-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:58370b1ffbd35407444d57057b57da5d6549d2d854fa30249771775c63b5fe17"}, + {file = "kiwisolver-1.4.7-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:aa0abdf853e09aff551db11fce173e2177d00786c688203f52c87ad7fcd91ef9"}, + {file = "kiwisolver-1.4.7-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:8d53103597a252fb3ab8b5845af04c7a26d5e7ea8122303dd7a021176a87e8b9"}, + {file = "kiwisolver-1.4.7-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:88f17c5ffa8e9462fb79f62746428dd57b46eb931698e42e990ad63103f35e6c"}, + {file = "kiwisolver-1.4.7-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:88a9ca9c710d598fd75ee5de59d5bda2684d9db36a9f50b6125eaea3969c2599"}, + {file = "kiwisolver-1.4.7-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f4d742cb7af1c28303a51b7a27aaee540e71bb8e24f68c736f6f2ffc82f2bf05"}, + {file = "kiwisolver-1.4.7-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e28c7fea2196bf4c2f8d46a0415c77a1c480cc0724722f23d7410ffe9842c407"}, + {file = "kiwisolver-1.4.7-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:e968b84db54f9d42046cf154e02911e39c0435c9801681e3fc9ce8a3c4130278"}, + {file = "kiwisolver-1.4.7-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:0c18ec74c0472de033e1bebb2911c3c310eef5649133dd0bedf2a169a1b269e5"}, + {file = "kiwisolver-1.4.7-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:8f0ea6da6d393d8b2e187e6a5e3fb81f5862010a40c3945e2c6d12ae45cfb2ad"}, + {file = "kiwisolver-1.4.7-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:f106407dda69ae456dd1227966bf445b157ccc80ba0dff3802bb63f30b74e895"}, + {file = "kiwisolver-1.4.7-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:84ec80df401cfee1457063732d90022f93951944b5b58975d34ab56bb150dfb3"}, + {file = "kiwisolver-1.4.7-cp310-cp310-win32.whl", hash = "sha256:71bb308552200fb2c195e35ef05de12f0c878c07fc91c270eb3d6e41698c3bcc"}, + {file = "kiwisolver-1.4.7-cp310-cp310-win_amd64.whl", hash = "sha256:44756f9fd339de0fb6ee4f8c1696cfd19b2422e0d70b4cefc1cc7f1f64045a8c"}, + {file = "kiwisolver-1.4.7-cp310-cp310-win_arm64.whl", hash = "sha256:78a42513018c41c2ffd262eb676442315cbfe3c44eed82385c2ed043bc63210a"}, + {file = "kiwisolver-1.4.7-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:d2b0e12a42fb4e72d509fc994713d099cbb15ebf1103545e8a45f14da2dfca54"}, + {file = "kiwisolver-1.4.7-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:2a8781ac3edc42ea4b90bc23e7d37b665d89423818e26eb6df90698aa2287c95"}, + {file = "kiwisolver-1.4.7-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:46707a10836894b559e04b0fd143e343945c97fd170d69a2d26d640b4e297935"}, + {file = "kiwisolver-1.4.7-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ef97b8df011141c9b0f6caf23b29379f87dd13183c978a30a3c546d2c47314cb"}, + {file = "kiwisolver-1.4.7-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3ab58c12a2cd0fc769089e6d38466c46d7f76aced0a1f54c77652446733d2d02"}, + {file = "kiwisolver-1.4.7-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:803b8e1459341c1bb56d1c5c010406d5edec8a0713a0945851290a7930679b51"}, + {file = "kiwisolver-1.4.7-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f9a9e8a507420fe35992ee9ecb302dab68550dedc0da9e2880dd88071c5fb052"}, + {file = "kiwisolver-1.4.7-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:18077b53dc3bb490e330669a99920c5e6a496889ae8c63b58fbc57c3d7f33a18"}, + {file = "kiwisolver-1.4.7-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:6af936f79086a89b3680a280c47ea90b4df7047b5bdf3aa5c524bbedddb9e545"}, + {file = "kiwisolver-1.4.7-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:3abc5b19d24af4b77d1598a585b8a719beb8569a71568b66f4ebe1fb0449460b"}, + {file = "kiwisolver-1.4.7-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:933d4de052939d90afbe6e9d5273ae05fb836cc86c15b686edd4b3560cc0ee36"}, + {file = "kiwisolver-1.4.7-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:65e720d2ab2b53f1f72fb5da5fb477455905ce2c88aaa671ff0a447c2c80e8e3"}, + {file = "kiwisolver-1.4.7-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:3bf1ed55088f214ba6427484c59553123fdd9b218a42bbc8c6496d6754b1e523"}, + {file = "kiwisolver-1.4.7-cp311-cp311-win32.whl", hash = "sha256:4c00336b9dd5ad96d0a558fd18a8b6f711b7449acce4c157e7343ba92dd0cf3d"}, + {file = "kiwisolver-1.4.7-cp311-cp311-win_amd64.whl", hash = "sha256:929e294c1ac1e9f615c62a4e4313ca1823ba37326c164ec720a803287c4c499b"}, + {file = "kiwisolver-1.4.7-cp311-cp311-win_arm64.whl", hash = "sha256:e33e8fbd440c917106b237ef1a2f1449dfbb9b6f6e1ce17c94cd6a1e0d438376"}, + {file = "kiwisolver-1.4.7-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:5360cc32706dab3931f738d3079652d20982511f7c0ac5711483e6eab08efff2"}, + {file = "kiwisolver-1.4.7-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:942216596dc64ddb25adb215c3c783215b23626f8d84e8eff8d6d45c3f29f75a"}, + {file = "kiwisolver-1.4.7-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:48b571ecd8bae15702e4f22d3ff6a0f13e54d3d00cd25216d5e7f658242065ee"}, + {file = "kiwisolver-1.4.7-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ad42ba922c67c5f219097b28fae965e10045ddf145d2928bfac2eb2e17673640"}, + {file = "kiwisolver-1.4.7-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:612a10bdae23404a72941a0fc8fa2660c6ea1217c4ce0dbcab8a8f6543ea9e7f"}, + {file = "kiwisolver-1.4.7-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9e838bba3a3bac0fe06d849d29772eb1afb9745a59710762e4ba3f4cb8424483"}, + {file = "kiwisolver-1.4.7-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:22f499f6157236c19f4bbbd472fa55b063db77a16cd74d49afe28992dff8c258"}, + {file = "kiwisolver-1.4.7-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:693902d433cf585133699972b6d7c42a8b9f8f826ebcaf0132ff55200afc599e"}, + {file = "kiwisolver-1.4.7-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:4e77f2126c3e0b0d055f44513ed349038ac180371ed9b52fe96a32aa071a5107"}, + {file = "kiwisolver-1.4.7-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:657a05857bda581c3656bfc3b20e353c232e9193eb167766ad2dc58b56504948"}, + {file = "kiwisolver-1.4.7-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:4bfa75a048c056a411f9705856abfc872558e33c055d80af6a380e3658766038"}, + {file = "kiwisolver-1.4.7-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:34ea1de54beef1c104422d210c47c7d2a4999bdecf42c7b5718fbe59a4cac383"}, + {file = "kiwisolver-1.4.7-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:90da3b5f694b85231cf93586dad5e90e2d71b9428f9aad96952c99055582f520"}, + {file = "kiwisolver-1.4.7-cp312-cp312-win32.whl", hash = "sha256:18e0cca3e008e17fe9b164b55735a325140a5a35faad8de92dd80265cd5eb80b"}, + {file = "kiwisolver-1.4.7-cp312-cp312-win_amd64.whl", hash = "sha256:58cb20602b18f86f83a5c87d3ee1c766a79c0d452f8def86d925e6c60fbf7bfb"}, + {file = "kiwisolver-1.4.7-cp312-cp312-win_arm64.whl", hash = "sha256:f5a8b53bdc0b3961f8b6125e198617c40aeed638b387913bf1ce78afb1b0be2a"}, + {file = "kiwisolver-1.4.7-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:2e6039dcbe79a8e0f044f1c39db1986a1b8071051efba3ee4d74f5b365f5226e"}, + {file = "kiwisolver-1.4.7-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:a1ecf0ac1c518487d9d23b1cd7139a6a65bc460cd101ab01f1be82ecf09794b6"}, + {file = "kiwisolver-1.4.7-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:7ab9ccab2b5bd5702ab0803676a580fffa2aa178c2badc5557a84cc943fcf750"}, + {file = "kiwisolver-1.4.7-cp313-cp313-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f816dd2277f8d63d79f9c8473a79fe54047bc0467754962840782c575522224d"}, + {file = "kiwisolver-1.4.7-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cf8bcc23ceb5a1b624572a1623b9f79d2c3b337c8c455405ef231933a10da379"}, + {file = "kiwisolver-1.4.7-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dea0bf229319828467d7fca8c7c189780aa9ff679c94539eed7532ebe33ed37c"}, + {file = "kiwisolver-1.4.7-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7c06a4c7cf15ec739ce0e5971b26c93638730090add60e183530d70848ebdd34"}, + {file = "kiwisolver-1.4.7-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:913983ad2deb14e66d83c28b632fd35ba2b825031f2fa4ca29675e665dfecbe1"}, + {file = "kiwisolver-1.4.7-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:5337ec7809bcd0f424c6b705ecf97941c46279cf5ed92311782c7c9c2026f07f"}, + {file = "kiwisolver-1.4.7-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:4c26ed10c4f6fa6ddb329a5120ba3b6db349ca192ae211e882970bfc9d91420b"}, + {file = "kiwisolver-1.4.7-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:c619b101e6de2222c1fcb0531e1b17bbffbe54294bfba43ea0d411d428618c27"}, + {file = "kiwisolver-1.4.7-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:073a36c8273647592ea332e816e75ef8da5c303236ec0167196793eb1e34657a"}, + {file = "kiwisolver-1.4.7-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:3ce6b2b0231bda412463e152fc18335ba32faf4e8c23a754ad50ffa70e4091ee"}, + {file = "kiwisolver-1.4.7-cp313-cp313-win32.whl", hash = "sha256:f4c9aee212bc89d4e13f58be11a56cc8036cabad119259d12ace14b34476fd07"}, + {file = "kiwisolver-1.4.7-cp313-cp313-win_amd64.whl", hash = "sha256:8a3ec5aa8e38fc4c8af308917ce12c536f1c88452ce554027e55b22cbbfbff76"}, + {file = "kiwisolver-1.4.7-cp313-cp313-win_arm64.whl", hash = "sha256:76c8094ac20ec259471ac53e774623eb62e6e1f56cd8690c67ce6ce4fcb05650"}, + {file = "kiwisolver-1.4.7-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:5d5abf8f8ec1f4e22882273c423e16cae834c36856cac348cfbfa68e01c40f3a"}, + {file = "kiwisolver-1.4.7-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:aeb3531b196ef6f11776c21674dba836aeea9d5bd1cf630f869e3d90b16cfade"}, + {file = "kiwisolver-1.4.7-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:b7d755065e4e866a8086c9bdada157133ff466476a2ad7861828e17b6026e22c"}, + {file = "kiwisolver-1.4.7-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:08471d4d86cbaec61f86b217dd938a83d85e03785f51121e791a6e6689a3be95"}, + {file = "kiwisolver-1.4.7-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7bbfcb7165ce3d54a3dfbe731e470f65739c4c1f85bb1018ee912bae139e263b"}, + {file = "kiwisolver-1.4.7-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5d34eb8494bea691a1a450141ebb5385e4b69d38bb8403b5146ad279f4b30fa3"}, + {file = "kiwisolver-1.4.7-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:9242795d174daa40105c1d86aba618e8eab7bf96ba8c3ee614da8302a9f95503"}, + {file = "kiwisolver-1.4.7-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:a0f64a48bb81af7450e641e3fe0b0394d7381e342805479178b3d335d60ca7cf"}, + {file = "kiwisolver-1.4.7-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:8e045731a5416357638d1700927529e2b8ab304811671f665b225f8bf8d8f933"}, + {file = "kiwisolver-1.4.7-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:4322872d5772cae7369f8351da1edf255a604ea7087fe295411397d0cfd9655e"}, + {file = "kiwisolver-1.4.7-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:e1631290ee9271dffe3062d2634c3ecac02c83890ada077d225e081aca8aab89"}, + {file = "kiwisolver-1.4.7-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:edcfc407e4eb17e037bca59be0e85a2031a2ac87e4fed26d3e9df88b4165f92d"}, + {file = "kiwisolver-1.4.7-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:4d05d81ecb47d11e7f8932bd8b61b720bf0b41199358f3f5e36d38e28f0532c5"}, + {file = "kiwisolver-1.4.7-cp38-cp38-win32.whl", hash = "sha256:b38ac83d5f04b15e515fd86f312479d950d05ce2368d5413d46c088dda7de90a"}, + {file = "kiwisolver-1.4.7-cp38-cp38-win_amd64.whl", hash = "sha256:d83db7cde68459fc803052a55ace60bea2bae361fc3b7a6d5da07e11954e4b09"}, + {file = "kiwisolver-1.4.7-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:3f9362ecfca44c863569d3d3c033dbe8ba452ff8eed6f6b5806382741a1334bd"}, + {file = "kiwisolver-1.4.7-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:e8df2eb9b2bac43ef8b082e06f750350fbbaf2887534a5be97f6cf07b19d9583"}, + {file = "kiwisolver-1.4.7-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f32d6edbc638cde7652bd690c3e728b25332acbadd7cad670cc4a02558d9c417"}, + {file = "kiwisolver-1.4.7-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:e2e6c39bd7b9372b0be21456caab138e8e69cc0fc1190a9dfa92bd45a1e6e904"}, + {file = "kiwisolver-1.4.7-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:dda56c24d869b1193fcc763f1284b9126550eaf84b88bbc7256e15028f19188a"}, + {file = "kiwisolver-1.4.7-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:79849239c39b5e1fd906556c474d9b0439ea6792b637511f3fe3a41158d89ca8"}, + {file = "kiwisolver-1.4.7-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5e3bc157fed2a4c02ec468de4ecd12a6e22818d4f09cde2c31ee3226ffbefab2"}, + {file = "kiwisolver-1.4.7-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3da53da805b71e41053dc670f9a820d1157aae77b6b944e08024d17bcd51ef88"}, + {file = "kiwisolver-1.4.7-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:8705f17dfeb43139a692298cb6637ee2e59c0194538153e83e9ee0c75c2eddde"}, + {file = "kiwisolver-1.4.7-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:82a5c2f4b87c26bb1a0ef3d16b5c4753434633b83d365cc0ddf2770c93829e3c"}, + {file = "kiwisolver-1.4.7-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:ce8be0466f4c0d585cdb6c1e2ed07232221df101a4c6f28821d2aa754ca2d9e2"}, + {file = "kiwisolver-1.4.7-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:409afdfe1e2e90e6ee7fc896f3df9a7fec8e793e58bfa0d052c8a82f99c37abb"}, + {file = "kiwisolver-1.4.7-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:5b9c3f4ee0b9a439d2415012bd1b1cc2df59e4d6a9939f4d669241d30b414327"}, + {file = "kiwisolver-1.4.7-cp39-cp39-win32.whl", hash = "sha256:a79ae34384df2b615eefca647a2873842ac3b596418032bef9a7283675962644"}, + {file = "kiwisolver-1.4.7-cp39-cp39-win_amd64.whl", hash = "sha256:cf0438b42121a66a3a667de17e779330fc0f20b0d97d59d2f2121e182b0505e4"}, + {file = "kiwisolver-1.4.7-cp39-cp39-win_arm64.whl", hash = "sha256:764202cc7e70f767dab49e8df52c7455e8de0df5d858fa801a11aa0d882ccf3f"}, + {file = "kiwisolver-1.4.7-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:94252291e3fe68001b1dd747b4c0b3be12582839b95ad4d1b641924d68fd4643"}, + {file = "kiwisolver-1.4.7-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:5b7dfa3b546da08a9f622bb6becdb14b3e24aaa30adba66749d38f3cc7ea9706"}, + {file = "kiwisolver-1.4.7-pp310-pypy310_pp73-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bd3de6481f4ed8b734da5df134cd5a6a64fe32124fe83dde1e5b5f29fe30b1e6"}, + {file = "kiwisolver-1.4.7-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a91b5f9f1205845d488c928e8570dcb62b893372f63b8b6e98b863ebd2368ff2"}, + {file = "kiwisolver-1.4.7-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:40fa14dbd66b8b8f470d5fc79c089a66185619d31645f9b0773b88b19f7223c4"}, + {file = "kiwisolver-1.4.7-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:eb542fe7933aa09d8d8f9d9097ef37532a7df6497819d16efe4359890a2f417a"}, + {file = "kiwisolver-1.4.7-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:bfa1acfa0c54932d5607e19a2c24646fb4c1ae2694437789129cf099789a3b00"}, + {file = "kiwisolver-1.4.7-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:eee3ea935c3d227d49b4eb85660ff631556841f6e567f0f7bda972df6c2c9935"}, + {file = "kiwisolver-1.4.7-pp38-pypy38_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:f3160309af4396e0ed04db259c3ccbfdc3621b5559b5453075e5de555e1f3a1b"}, + {file = "kiwisolver-1.4.7-pp38-pypy38_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:a17f6a29cf8935e587cc8a4dbfc8368c55edc645283db0ce9801016f83526c2d"}, + {file = "kiwisolver-1.4.7-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:10849fb2c1ecbfae45a693c070e0320a91b35dd4bcf58172c023b994283a124d"}, + {file = "kiwisolver-1.4.7-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:ac542bf38a8a4be2dc6b15248d36315ccc65f0743f7b1a76688ffb6b5129a5c2"}, + {file = "kiwisolver-1.4.7-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:8b01aac285f91ca889c800042c35ad3b239e704b150cfd3382adfc9dcc780e39"}, + {file = "kiwisolver-1.4.7-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:48be928f59a1f5c8207154f935334d374e79f2b5d212826307d072595ad76a2e"}, + {file = "kiwisolver-1.4.7-pp39-pypy39_pp73-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f37cfe618a117e50d8c240555331160d73d0411422b59b5ee217843d7b693608"}, + {file = "kiwisolver-1.4.7-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:599b5c873c63a1f6ed7eead644a8a380cfbdf5db91dcb6f85707aaab213b1674"}, + {file = "kiwisolver-1.4.7-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:801fa7802e5cfabe3ab0c81a34c323a319b097dfb5004be950482d882f3d7225"}, + {file = "kiwisolver-1.4.7-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:0c6c43471bc764fad4bc99c5c2d6d16a676b1abf844ca7c8702bdae92df01ee0"}, + {file = "kiwisolver-1.4.7.tar.gz", hash = "sha256:9893ff81bd7107f7b685d3017cc6583daadb4fc26e4a888350df530e41980a60"}, ] [[package]] @@ -2022,13 +2054,13 @@ traitlets = "*" [[package]] name = "mdit-py-plugins" -version = "0.4.1" +version = "0.4.2" description = "Collection of plugins for markdown-it-py" optional = false python-versions = ">=3.8" files = [ - {file = "mdit_py_plugins-0.4.1-py3-none-any.whl", hash = "sha256:1020dfe4e6bfc2c79fb49ae4e3f5b297f5ccd20f010187acc52af2921e27dc6a"}, - {file = "mdit_py_plugins-0.4.1.tar.gz", hash = "sha256:834b8ac23d1cd60cec703646ffd22ae97b7955a6d596eb1d304be1e251ae499c"}, + {file = "mdit_py_plugins-0.4.2-py3-none-any.whl", hash = "sha256:0c673c3f889399a33b95e88d2f0d111b4447bdfea7f237dab2d488f459835636"}, + {file = "mdit_py_plugins-0.4.2.tar.gz", hash = "sha256:5f2cd1fdb606ddf152d37ec30e46101a60512bc0e5fa1a7002c36647b09e26b5"}, ] [package.dependencies] @@ -2099,13 +2131,13 @@ files = [ [[package]] name = "mkdocs" -version = "1.6.0" +version = "1.6.1" description = "Project documentation with Markdown." optional = false python-versions = ">=3.8" files = [ - {file = "mkdocs-1.6.0-py3-none-any.whl", hash = "sha256:1eb5cb7676b7d89323e62b56235010216319217d4af5ddc543a91beb8d125ea7"}, - {file = "mkdocs-1.6.0.tar.gz", hash = "sha256:a73f735824ef83a4f3bcb7a231dcab23f5a838f88b7efc54a0eef5fbdbc3c512"}, + {file = "mkdocs-1.6.1-py3-none-any.whl", hash = "sha256:db91759624d1647f3f34aa0c3f327dd2601beae39a366d6e064c03468d35c20e"}, + {file = "mkdocs-1.6.1.tar.gz", hash = "sha256:7b432f01d928c084353ab39c57282f29f92136665bdd6abf7c1ec8d822ef86f2"}, ] [package.dependencies] @@ -2220,13 +2252,13 @@ pygments = ">2.12.0" [[package]] name = "mkdocs-material" -version = "9.5.37" +version = "9.5.38" description = "Documentation that simply works" optional = false python-versions = ">=3.8" files = [ - {file = "mkdocs_material-9.5.37-py3-none-any.whl", hash = "sha256:6e8a986abad77be5edec3dd77cf1ddf2480963fb297a8e971f87a82fd464b070"}, - {file = "mkdocs_material-9.5.37.tar.gz", hash = "sha256:2c31607431ec234db124031255b0a9d4f3e1c3ecc2c47ad97ecfff0460471941"}, + {file = "mkdocs_material-9.5.38-py3-none-any.whl", hash = "sha256:d4779051d52ba9f1e7e344b34de95449c7c366c212b388e4a2db9a3db043c228"}, + {file = "mkdocs_material-9.5.38.tar.gz", hash = "sha256:1843c5171ad6b489550aeaf7358e5b7128cc03ddcf0fb4d91d19aa1e691a63b8"}, ] [package.dependencies] @@ -2279,7 +2311,6 @@ Markdown = ">=3.6" MarkupSafe = ">=1.1" mkdocs = ">=1.4" mkdocs-autorefs = ">=1.2" -mkdocstrings-python = {version = ">=0.5.2", optional = true, markers = "extra == \"python\""} platformdirs = ">=2.2" pymdown-extensions = ">=6.3" typing-extensions = {version = ">=4.1", markers = "python_version < \"3.10\""} @@ -2307,13 +2338,13 @@ mkdocstrings = ">=0.26" [[package]] name = "more-itertools" -version = "10.4.0" +version = "10.5.0" description = "More routines for operating on iterables, beyond itertools" optional = false python-versions = ">=3.8" files = [ - {file = "more-itertools-10.4.0.tar.gz", hash = "sha256:fe0e63c4ab068eac62410ab05cccca2dc71ec44ba8ef29916a0090df061cf923"}, - {file = "more_itertools-10.4.0-py3-none-any.whl", hash = "sha256:0f7d9f83a0a8dcfa8a2694a770590d98a67ea943e3d9f5298309a484758c4e27"}, + {file = "more-itertools-10.5.0.tar.gz", hash = "sha256:5482bfef7849c25dc3c6dd53a6173ae4795da2a41a80faea6700d9f5846c5da6"}, + {file = "more_itertools-10.5.0-py3-none-any.whl", hash = "sha256:037b0d3203ce90cca8ab1defbbdac29d5f993fc20131f3664dc8d6acfa872aef"}, ] [[package]] @@ -2647,8 +2678,8 @@ numpy = [ {version = ">=1.17.3", markers = "(platform_system != \"Darwin\" and platform_system != \"Linux\") and python_version >= \"3.8\" and python_version < \"3.9\" or platform_system != \"Darwin\" and python_version >= \"3.8\" and python_version < \"3.9\" and platform_machine != \"aarch64\" or platform_machine != \"arm64\" and python_version >= \"3.8\" and python_version < \"3.9\" and platform_system != \"Linux\" or (platform_machine != \"arm64\" and platform_machine != \"aarch64\") and python_version >= \"3.8\" and python_version < \"3.9\""}, {version = ">=1.21.4", markers = "python_version >= \"3.10\" and platform_system == \"Darwin\" and python_version < \"3.11\""}, {version = ">=1.21.2", markers = "platform_system != \"Darwin\" and python_version >= \"3.10\" and python_version < \"3.11\""}, - {version = ">=1.26.0", markers = "python_version >= \"3.12\""}, {version = ">=1.23.5", markers = "python_version >= \"3.11\" and python_version < \"3.12\""}, + {version = ">=1.26.0", markers = "python_version >= \"3.12\""}, ] [[package]] @@ -2675,14 +2706,19 @@ files = [ [[package]] name = "paginate" -version = "0.5.6" +version = "0.5.7" description = "Divides large result sets into pages for easier browsing" optional = false python-versions = "*" files = [ - {file = "paginate-0.5.6.tar.gz", hash = "sha256:5e6007b6a9398177a7e1648d04fdd9f8c9766a1a945bceac82f1929e8c78af2d"}, + {file = "paginate-0.5.7-py2.py3-none-any.whl", hash = "sha256:b885e2af73abcf01d9559fd5216b57ef722f8c42affbb63942377668e35c7591"}, + {file = "paginate-0.5.7.tar.gz", hash = "sha256:22bd083ab41e1a8b4f3690544afb2c60c25e5c9a63a30fa2f483f6c60c8e5945"}, ] +[package.extras] +dev = ["pytest", "tox"] +lint = ["black"] + [[package]] name = "pandas" version = "2.0.3" @@ -2764,6 +2800,24 @@ files = [ [package.dependencies] types-pytz = ">=2022.1.1" +[[package]] +name = "pandas-stubs" +version = "2.0.3.230814" +description = "Type annotations for pandas" +optional = true +python-versions = ">=3.8" +files = [ + {file = "pandas_stubs-2.0.3.230814-py3-none-any.whl", hash = "sha256:4b3dfc027d49779176b7daa031a3405f7b839bcb6e312f4b9f29fea5feec5b4f"}, + {file = "pandas_stubs-2.0.3.230814.tar.gz", hash = "sha256:1d5cc09e36e3d9f9a1ed9dceae4e03eeb26d1b898dd769996925f784365c8769"}, +] + +[package.dependencies] +numpy = [ + {version = "<=1.24.3", markers = "python_full_version <= \"3.8.0\""}, + {version = ">=1.25.0", markers = "python_version >= \"3.9\""}, +] +types-pytz = ">=2022.1.1" + [[package]] name = "pandocfilters" version = "1.5.1" @@ -2950,19 +3004,19 @@ files = [ [[package]] name = "platformdirs" -version = "4.2.2" +version = "4.3.6" description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`." optional = false python-versions = ">=3.8" files = [ - {file = "platformdirs-4.2.2-py3-none-any.whl", hash = "sha256:2d7a1657e36a80ea911db832a8a6ece5ee53d8de21edd5cc5879af6530b1bfee"}, - {file = "platformdirs-4.2.2.tar.gz", hash = "sha256:38b7b51f512eed9e84a22788b4bce1de17c0adb134d6becb09836e37d8654cd3"}, + {file = "platformdirs-4.3.6-py3-none-any.whl", hash = "sha256:73e575e1408ab8103900836b97580d5307456908a03e92031bab39e4554cc3fb"}, + {file = "platformdirs-4.3.6.tar.gz", hash = "sha256:357fb2acbc885b0419afd3ce3ed34564c13c9b95c89360cd9563f73aa5e2b907"}, ] [package.extras] -docs = ["furo (>=2023.9.10)", "proselint (>=0.13)", "sphinx (>=7.2.6)", "sphinx-autodoc-typehints (>=1.25.2)"] -test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.4.3)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)"] -type = ["mypy (>=1.8)"] +docs = ["furo (>=2024.8.6)", "proselint (>=0.14)", "sphinx (>=8.0.2)", "sphinx-autodoc-typehints (>=2.4)"] +test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=8.3.2)", "pytest-cov (>=5)", "pytest-mock (>=3.14)"] +type = ["mypy (>=1.11.2)"] [[package]] name = "pluggy" @@ -2999,13 +3053,13 @@ virtualenv = ">=20.10.0" [[package]] name = "prometheus-client" -version = "0.20.0" +version = "0.21.0" description = "Python client for the Prometheus monitoring system." optional = false python-versions = ">=3.8" files = [ - {file = "prometheus_client-0.20.0-py3-none-any.whl", hash = "sha256:cde524a85bce83ca359cc837f28b8c0db5cac7aa653a588fd7e84ba061c329e7"}, - {file = "prometheus_client-0.20.0.tar.gz", hash = "sha256:287629d00b147a32dcb2be0b9df905da599b2d82f80377083ec8463309a4bb89"}, + {file = "prometheus_client-0.21.0-py3-none-any.whl", hash = "sha256:4fa6b4dd0ac16d58bb587c04b1caae65b8c5043e85f778f42f5f632f6af2e166"}, + {file = "prometheus_client-0.21.0.tar.gz", hash = "sha256:96c83c606b71ff2b0a433c98889d275f51ffec6c5e267de37c7a2b5c9aa9233e"}, ] [package.extras] @@ -3013,13 +3067,13 @@ twisted = ["twisted"] [[package]] name = "prompt-toolkit" -version = "3.0.47" +version = "3.0.48" description = "Library for building powerful interactive command lines in Python" optional = false python-versions = ">=3.7.0" files = [ - {file = "prompt_toolkit-3.0.47-py3-none-any.whl", hash = "sha256:0d7bfa67001d5e39d02c224b663abc33687405033a8c422d0d675a5a13361d10"}, - {file = "prompt_toolkit-3.0.47.tar.gz", hash = "sha256:1e1b29cb58080b1e69f207c893a1a7bf16d127a5c30c9d17a25a5d77792e5360"}, + {file = "prompt_toolkit-3.0.48-py3-none-any.whl", hash = "sha256:f49a827f90062e411f1ce1f854f2aedb3c23353244f8108b89283587397ac10e"}, + {file = "prompt_toolkit-3.0.48.tar.gz", hash = "sha256:d6623ab0477a80df74e646bdbc93621143f5caf104206aa29294d53de1a03d90"}, ] [package.dependencies] @@ -3106,13 +3160,13 @@ windows-terminal = ["colorama (>=0.4.6)"] [[package]] name = "pymdown-extensions" -version = "10.9" +version = "10.10.2" description = "Extension pack for Python Markdown." optional = false python-versions = ">=3.8" files = [ - {file = "pymdown_extensions-10.9-py3-none-any.whl", hash = "sha256:d323f7e90d83c86113ee78f3fe62fc9dee5f56b54d912660703ea1816fed5626"}, - {file = "pymdown_extensions-10.9.tar.gz", hash = "sha256:6ff740bcd99ec4172a938970d42b96128bdc9d4b9bcad72494f29921dc69b753"}, + {file = "pymdown_extensions-10.10.2-py3-none-any.whl", hash = "sha256:513a9e9432b197cf0539356c8f1fc376e0d10b70ad150cadeb649a5628aacd45"}, + {file = "pymdown_extensions-10.10.2.tar.gz", hash = "sha256:65d82324ef2497931bc858c8320540c6264ab0d9a292707edb61f4fe0cd56633"}, ] [package.dependencies] @@ -3124,13 +3178,13 @@ extra = ["pygments (>=2.12)"] [[package]] name = "pyparsing" -version = "3.1.2" +version = "3.1.4" description = "pyparsing module - Classes and methods to define and execute parsing grammars" optional = false python-versions = ">=3.6.8" files = [ - {file = "pyparsing-3.1.2-py3-none-any.whl", hash = "sha256:f9db75911801ed778fe61bb643079ff86601aca99fcae6345aa67292038fb742"}, - {file = "pyparsing-3.1.2.tar.gz", hash = "sha256:a1bac0ce561155ecc3ed78ca94d3c9378656ad4c94c1270de543f621420f94ad"}, + {file = "pyparsing-3.1.4-py3-none-any.whl", hash = "sha256:a6a7ee4235a3f944aa1fa2249307708f893fe5717dc603503c6c7969c070fb7c"}, + {file = "pyparsing-3.1.4.tar.gz", hash = "sha256:f86ec8d1a83f11977c9a6ea7598e8c27fc5cddfa5b07ea2241edbbde1d7bc032"}, ] [package.extras] @@ -3138,13 +3192,13 @@ diagrams = ["jinja2", "railroad-diagrams"] [[package]] name = "pyproject-api" -version = "1.7.1" +version = "1.8.0" description = "API to interact with the python pyproject.toml based projects" optional = false python-versions = ">=3.8" files = [ - {file = "pyproject_api-1.7.1-py3-none-any.whl", hash = "sha256:2dc1654062c2b27733d8fd4cdda672b22fe8741ef1dde8e3a998a9547b071eeb"}, - {file = "pyproject_api-1.7.1.tar.gz", hash = "sha256:7ebc6cd10710f89f4cf2a2731710a98abce37ebff19427116ff2174c9236a827"}, + {file = "pyproject_api-1.8.0-py3-none-any.whl", hash = "sha256:3d7d347a047afe796fd5d1885b1e391ba29be7169bd2f102fcd378f04273d228"}, + {file = "pyproject_api-1.8.0.tar.gz", hash = "sha256:77b8049f2feb5d33eefcc21b57f1e279636277a8ac8ad6b5871037b243778496"}, ] [package.dependencies] @@ -3152,8 +3206,8 @@ packaging = ">=24.1" tomli = {version = ">=2.0.1", markers = "python_version < \"3.11\""} [package.extras] -docs = ["furo (>=2024.5.6)", "sphinx-autodoc-typehints (>=2.2.1)"] -testing = ["covdefaults (>=2.3)", "pytest (>=8.2.2)", "pytest-cov (>=5)", "pytest-mock (>=3.14)", "setuptools (>=70.1)"] +docs = ["furo (>=2024.8.6)", "sphinx-autodoc-typehints (>=2.4.1)"] +testing = ["covdefaults (>=2.3)", "pytest (>=8.3.3)", "pytest-cov (>=5)", "pytest-mock (>=3.14)", "setuptools (>=75.1)"] [[package]] name = "pyproject-hooks" @@ -3215,13 +3269,13 @@ files = [ [[package]] name = "pytz" -version = "2024.1" +version = "2024.2" description = "World timezone definitions, modern and historical" optional = false python-versions = "*" files = [ - {file = "pytz-2024.1-py2.py3-none-any.whl", hash = "sha256:328171f4e3623139da4983451950b28e95ac706e13f3f2630a879749e7a8b319"}, - {file = "pytz-2024.1.tar.gz", hash = "sha256:2a29735ea9c18baf14b448846bde5a48030ed267578472d8955cd0e7443a9812"}, + {file = "pytz-2024.2-py2.py3-none-any.whl", hash = "sha256:31c7c1817eb7fae7ca4b8c7ee50c72f93aa2dd863de768e1ef4245d426aa0725"}, + {file = "pytz-2024.2.tar.gz", hash = "sha256:2aa355083c50a0f93fa581709deac0c9ad65cca8a9e9beac660adcbd493c798a"}, ] [[package]] @@ -3351,120 +3405,120 @@ pyyaml = "*" [[package]] name = "pyzmq" -version = "26.1.0" +version = "26.2.0" description = "Python bindings for 0MQ" optional = false python-versions = ">=3.7" files = [ - {file = "pyzmq-26.1.0-cp310-cp310-macosx_10_15_universal2.whl", hash = "sha256:263cf1e36862310bf5becfbc488e18d5d698941858860c5a8c079d1511b3b18e"}, - {file = "pyzmq-26.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d5c8b17f6e8f29138678834cf8518049e740385eb2dbf736e8f07fc6587ec682"}, - {file = "pyzmq-26.1.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:75a95c2358fcfdef3374cb8baf57f1064d73246d55e41683aaffb6cfe6862917"}, - {file = "pyzmq-26.1.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f99de52b8fbdb2a8f5301ae5fc0f9e6b3ba30d1d5fc0421956967edcc6914242"}, - {file = "pyzmq-26.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7bcbfbab4e1895d58ab7da1b5ce9a327764f0366911ba5b95406c9104bceacb0"}, - {file = "pyzmq-26.1.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:77ce6a332c7e362cb59b63f5edf730e83590d0ab4e59c2aa5bd79419a42e3449"}, - {file = "pyzmq-26.1.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:ba0a31d00e8616149a5ab440d058ec2da621e05d744914774c4dde6837e1f545"}, - {file = "pyzmq-26.1.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:8b88641384e84a258b740801cd4dbc45c75f148ee674bec3149999adda4a8598"}, - {file = "pyzmq-26.1.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:2fa76ebcebe555cce90f16246edc3ad83ab65bb7b3d4ce408cf6bc67740c4f88"}, - {file = "pyzmq-26.1.0-cp310-cp310-win32.whl", hash = "sha256:fbf558551cf415586e91160d69ca6416f3fce0b86175b64e4293644a7416b81b"}, - {file = "pyzmq-26.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:a7b8aab50e5a288c9724d260feae25eda69582be84e97c012c80e1a5e7e03fb2"}, - {file = "pyzmq-26.1.0-cp310-cp310-win_arm64.whl", hash = "sha256:08f74904cb066e1178c1ec706dfdb5c6c680cd7a8ed9efebeac923d84c1f13b1"}, - {file = "pyzmq-26.1.0-cp311-cp311-macosx_10_15_universal2.whl", hash = "sha256:46d6800b45015f96b9d92ece229d92f2aef137d82906577d55fadeb9cf5fcb71"}, - {file = "pyzmq-26.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5bc2431167adc50ba42ea3e5e5f5cd70d93e18ab7b2f95e724dd8e1bd2c38120"}, - {file = "pyzmq-26.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b3bb34bebaa1b78e562931a1687ff663d298013f78f972a534f36c523311a84d"}, - {file = "pyzmq-26.1.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bd3f6329340cef1c7ba9611bd038f2d523cea79f09f9c8f6b0553caba59ec562"}, - {file = "pyzmq-26.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:471880c4c14e5a056a96cd224f5e71211997d40b4bf5e9fdded55dafab1f98f2"}, - {file = "pyzmq-26.1.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:ce6f2b66799971cbae5d6547acefa7231458289e0ad481d0be0740535da38d8b"}, - {file = "pyzmq-26.1.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:0a1f6ea5b1d6cdbb8cfa0536f0d470f12b4b41ad83625012e575f0e3ecfe97f0"}, - {file = "pyzmq-26.1.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:b45e6445ac95ecb7d728604bae6538f40ccf4449b132b5428c09918523abc96d"}, - {file = "pyzmq-26.1.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:94c4262626424683feea0f3c34951d39d49d354722db2745c42aa6bb50ecd93b"}, - {file = "pyzmq-26.1.0-cp311-cp311-win32.whl", hash = "sha256:a0f0ab9df66eb34d58205913f4540e2ad17a175b05d81b0b7197bc57d000e829"}, - {file = "pyzmq-26.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:8efb782f5a6c450589dbab4cb0f66f3a9026286333fe8f3a084399149af52f29"}, - {file = "pyzmq-26.1.0-cp311-cp311-win_arm64.whl", hash = "sha256:f133d05aaf623519f45e16ab77526e1e70d4e1308e084c2fb4cedb1a0c764bbb"}, - {file = "pyzmq-26.1.0-cp312-cp312-macosx_10_15_universal2.whl", hash = "sha256:3d3146b1c3dcc8a1539e7cc094700b2be1e605a76f7c8f0979b6d3bde5ad4072"}, - {file = "pyzmq-26.1.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:d9270fbf038bf34ffca4855bcda6e082e2c7f906b9eb8d9a8ce82691166060f7"}, - {file = "pyzmq-26.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:995301f6740a421afc863a713fe62c0aaf564708d4aa057dfdf0f0f56525294b"}, - {file = "pyzmq-26.1.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e7eca8b89e56fb8c6c26dd3e09bd41b24789022acf1cf13358e96f1cafd8cae3"}, - {file = "pyzmq-26.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:90d4feb2e83dfe9ace6374a847e98ee9d1246ebadcc0cb765482e272c34e5820"}, - {file = "pyzmq-26.1.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:d4fafc2eb5d83f4647331267808c7e0c5722c25a729a614dc2b90479cafa78bd"}, - {file = "pyzmq-26.1.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:58c33dc0e185dd97a9ac0288b3188d1be12b756eda67490e6ed6a75cf9491d79"}, - {file = "pyzmq-26.1.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:68a0a1d83d33d8367ddddb3e6bb4afbb0f92bd1dac2c72cd5e5ddc86bdafd3eb"}, - {file = "pyzmq-26.1.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:2ae7c57e22ad881af78075e0cea10a4c778e67234adc65c404391b417a4dda83"}, - {file = "pyzmq-26.1.0-cp312-cp312-win32.whl", hash = "sha256:347e84fc88cc4cb646597f6d3a7ea0998f887ee8dc31c08587e9c3fd7b5ccef3"}, - {file = "pyzmq-26.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:9f136a6e964830230912f75b5a116a21fe8e34128dcfd82285aa0ef07cb2c7bd"}, - {file = "pyzmq-26.1.0-cp312-cp312-win_arm64.whl", hash = "sha256:a4b7a989c8f5a72ab1b2bbfa58105578753ae77b71ba33e7383a31ff75a504c4"}, - {file = "pyzmq-26.1.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:d416f2088ac8f12daacffbc2e8918ef4d6be8568e9d7155c83b7cebed49d2322"}, - {file = "pyzmq-26.1.0-cp313-cp313-macosx_10_15_universal2.whl", hash = "sha256:ecb6c88d7946166d783a635efc89f9a1ff11c33d680a20df9657b6902a1d133b"}, - {file = "pyzmq-26.1.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:471312a7375571857a089342beccc1a63584315188560c7c0da7e0a23afd8a5c"}, - {file = "pyzmq-26.1.0-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0e6cea102ffa16b737d11932c426f1dc14b5938cf7bc12e17269559c458ac334"}, - {file = "pyzmq-26.1.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ec7248673ffc7104b54e4957cee38b2f3075a13442348c8d651777bf41aa45ee"}, - {file = "pyzmq-26.1.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:0614aed6f87d550b5cecb03d795f4ddbb1544b78d02a4bd5eecf644ec98a39f6"}, - {file = "pyzmq-26.1.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:e8746ce968be22a8a1801bf4a23e565f9687088580c3ed07af5846580dd97f76"}, - {file = "pyzmq-26.1.0-cp313-cp313-musllinux_1_1_i686.whl", hash = "sha256:7688653574392d2eaeef75ddcd0b2de5b232d8730af29af56c5adf1df9ef8d6f"}, - {file = "pyzmq-26.1.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:8d4dac7d97f15c653a5fedcafa82626bd6cee1450ccdaf84ffed7ea14f2b07a4"}, - {file = "pyzmq-26.1.0-cp313-cp313-win32.whl", hash = "sha256:ccb42ca0a4a46232d716779421bbebbcad23c08d37c980f02cc3a6bd115ad277"}, - {file = "pyzmq-26.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:e1e5d0a25aea8b691a00d6b54b28ac514c8cc0d8646d05f7ca6cb64b97358250"}, - {file = "pyzmq-26.1.0-cp313-cp313-win_arm64.whl", hash = "sha256:fc82269d24860cfa859b676d18850cbb8e312dcd7eada09e7d5b007e2f3d9eb1"}, - {file = "pyzmq-26.1.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:416ac51cabd54f587995c2b05421324700b22e98d3d0aa2cfaec985524d16f1d"}, - {file = "pyzmq-26.1.0-cp313-cp313t-macosx_10_15_universal2.whl", hash = "sha256:ff832cce719edd11266ca32bc74a626b814fff236824aa1aeaad399b69fe6eae"}, - {file = "pyzmq-26.1.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:393daac1bcf81b2a23e696b7b638eedc965e9e3d2112961a072b6cd8179ad2eb"}, - {file = "pyzmq-26.1.0-cp313-cp313t-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9869fa984c8670c8ab899a719eb7b516860a29bc26300a84d24d8c1b71eae3ec"}, - {file = "pyzmq-26.1.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3b3b8e36fd4c32c0825b4461372949ecd1585d326802b1321f8b6dc1d7e9318c"}, - {file = "pyzmq-26.1.0-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:3ee647d84b83509b7271457bb428cc347037f437ead4b0b6e43b5eba35fec0aa"}, - {file = "pyzmq-26.1.0-cp313-cp313t-musllinux_1_1_aarch64.whl", hash = "sha256:45cb1a70eb00405ce3893041099655265fabcd9c4e1e50c330026e82257892c1"}, - {file = "pyzmq-26.1.0-cp313-cp313t-musllinux_1_1_i686.whl", hash = "sha256:5cca7b4adb86d7470e0fc96037771981d740f0b4cb99776d5cb59cd0e6684a73"}, - {file = "pyzmq-26.1.0-cp313-cp313t-musllinux_1_1_x86_64.whl", hash = "sha256:91d1a20bdaf3b25f3173ff44e54b1cfbc05f94c9e8133314eb2962a89e05d6e3"}, - {file = "pyzmq-26.1.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c0665d85535192098420428c779361b8823d3d7ec4848c6af3abb93bc5c915bf"}, - {file = "pyzmq-26.1.0-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:96d7c1d35ee4a495df56c50c83df7af1c9688cce2e9e0edffdbf50889c167595"}, - {file = "pyzmq-26.1.0-cp37-cp37m-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:b281b5ff5fcc9dcbfe941ac5c7fcd4b6c065adad12d850f95c9d6f23c2652384"}, - {file = "pyzmq-26.1.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5384c527a9a004445c5074f1e20db83086c8ff1682a626676229aafd9cf9f7d1"}, - {file = "pyzmq-26.1.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:754c99a9840839375ee251b38ac5964c0f369306eddb56804a073b6efdc0cd88"}, - {file = "pyzmq-26.1.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:9bdfcb74b469b592972ed881bad57d22e2c0acc89f5e8c146782d0d90fb9f4bf"}, - {file = "pyzmq-26.1.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:bd13f0231f4788db619347b971ca5f319c5b7ebee151afc7c14632068c6261d3"}, - {file = "pyzmq-26.1.0-cp37-cp37m-win32.whl", hash = "sha256:c5668dac86a869349828db5fc928ee3f58d450dce2c85607067d581f745e4fb1"}, - {file = "pyzmq-26.1.0-cp37-cp37m-win_amd64.whl", hash = "sha256:ad875277844cfaeca7fe299ddf8c8d8bfe271c3dc1caf14d454faa5cdbf2fa7a"}, - {file = "pyzmq-26.1.0-cp38-cp38-macosx_10_15_universal2.whl", hash = "sha256:65c6e03cc0222eaf6aad57ff4ecc0a070451e23232bb48db4322cc45602cede0"}, - {file = "pyzmq-26.1.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:038ae4ffb63e3991f386e7fda85a9baab7d6617fe85b74a8f9cab190d73adb2b"}, - {file = "pyzmq-26.1.0-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:bdeb2c61611293f64ac1073f4bf6723b67d291905308a7de9bb2ca87464e3273"}, - {file = "pyzmq-26.1.0-cp38-cp38-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:61dfa5ee9d7df297c859ac82b1226d8fefaf9c5113dc25c2c00ecad6feeeb04f"}, - {file = "pyzmq-26.1.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f3292d384537b9918010769b82ab3e79fca8b23d74f56fc69a679106a3e2c2cf"}, - {file = "pyzmq-26.1.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:f9499c70c19ff0fbe1007043acb5ad15c1dec7d8e84ab429bca8c87138e8f85c"}, - {file = "pyzmq-26.1.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:d3dd5523ed258ad58fed7e364c92a9360d1af8a9371e0822bd0146bdf017ef4c"}, - {file = "pyzmq-26.1.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:baba2fd199b098c5544ef2536b2499d2e2155392973ad32687024bd8572a7d1c"}, - {file = "pyzmq-26.1.0-cp38-cp38-win32.whl", hash = "sha256:ddbb2b386128d8eca92bd9ca74e80f73fe263bcca7aa419f5b4cbc1661e19741"}, - {file = "pyzmq-26.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:79e45a4096ec8388cdeb04a9fa5e9371583bcb826964d55b8b66cbffe7b33c86"}, - {file = "pyzmq-26.1.0-cp39-cp39-macosx_10_15_universal2.whl", hash = "sha256:add52c78a12196bc0fda2de087ba6c876ea677cbda2e3eba63546b26e8bf177b"}, - {file = "pyzmq-26.1.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:98c03bd7f3339ff47de7ea9ac94a2b34580a8d4df69b50128bb6669e1191a895"}, - {file = "pyzmq-26.1.0-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:dcc37d9d708784726fafc9c5e1232de655a009dbf97946f117aefa38d5985a0f"}, - {file = "pyzmq-26.1.0-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:5a6ed52f0b9bf8dcc64cc82cce0607a3dfed1dbb7e8c6f282adfccc7be9781de"}, - {file = "pyzmq-26.1.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:451e16ae8bea3d95649317b463c9f95cd9022641ec884e3d63fc67841ae86dfe"}, - {file = "pyzmq-26.1.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:906e532c814e1d579138177a00ae835cd6becbf104d45ed9093a3aaf658f6a6a"}, - {file = "pyzmq-26.1.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:05bacc4f94af468cc82808ae3293390278d5f3375bb20fef21e2034bb9a505b6"}, - {file = "pyzmq-26.1.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:57bb2acba798dc3740e913ffadd56b1fcef96f111e66f09e2a8db3050f1f12c8"}, - {file = "pyzmq-26.1.0-cp39-cp39-win32.whl", hash = "sha256:f774841bb0e8588505002962c02da420bcfb4c5056e87a139c6e45e745c0e2e2"}, - {file = "pyzmq-26.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:359c533bedc62c56415a1f5fcfd8279bc93453afdb0803307375ecf81c962402"}, - {file = "pyzmq-26.1.0-cp39-cp39-win_arm64.whl", hash = "sha256:7907419d150b19962138ecec81a17d4892ea440c184949dc29b358bc730caf69"}, - {file = "pyzmq-26.1.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:b24079a14c9596846bf7516fe75d1e2188d4a528364494859106a33d8b48be38"}, - {file = "pyzmq-26.1.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:59d0acd2976e1064f1b398a00e2c3e77ed0a157529779e23087d4c2fb8aaa416"}, - {file = "pyzmq-26.1.0-pp310-pypy310_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:911c43a4117915203c4cc8755e0f888e16c4676a82f61caee2f21b0c00e5b894"}, - {file = "pyzmq-26.1.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b10163e586cc609f5f85c9b233195554d77b1e9a0801388907441aaeb22841c5"}, - {file = "pyzmq-26.1.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:28a8b2abb76042f5fd7bd720f7fea48c0fd3e82e9de0a1bf2c0de3812ce44a42"}, - {file = "pyzmq-26.1.0-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:bef24d3e4ae2c985034439f449e3f9e06bf579974ce0e53d8a507a1577d5b2ab"}, - {file = "pyzmq-26.1.0-pp37-pypy37_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:2cd0f4d314f4a2518e8970b6f299ae18cff7c44d4a1fc06fc713f791c3a9e3ea"}, - {file = "pyzmq-26.1.0-pp37-pypy37_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:fa25a620eed2a419acc2cf10135b995f8f0ce78ad00534d729aa761e4adcef8a"}, - {file = "pyzmq-26.1.0-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ef3b048822dca6d231d8a8ba21069844ae38f5d83889b9b690bf17d2acc7d099"}, - {file = "pyzmq-26.1.0-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:9a6847c92d9851b59b9f33f968c68e9e441f9a0f8fc972c5580c5cd7cbc6ee24"}, - {file = "pyzmq-26.1.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:c9b9305004d7e4e6a824f4f19b6d8f32b3578aad6f19fc1122aaf320cbe3dc83"}, - {file = "pyzmq-26.1.0-pp38-pypy38_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:63c1d3a65acb2f9c92dce03c4e1758cc552f1ae5c78d79a44e3bb88d2fa71f3a"}, - {file = "pyzmq-26.1.0-pp38-pypy38_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:d36b8fffe8b248a1b961c86fbdfa0129dfce878731d169ede7fa2631447331be"}, - {file = "pyzmq-26.1.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:67976d12ebfd61a3bc7d77b71a9589b4d61d0422282596cf58c62c3866916544"}, - {file = "pyzmq-26.1.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:998444debc8816b5d8d15f966e42751032d0f4c55300c48cc337f2b3e4f17d03"}, - {file = "pyzmq-26.1.0-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:e5c88b2f13bcf55fee78ea83567b9fe079ba1a4bef8b35c376043440040f7edb"}, - {file = "pyzmq-26.1.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8d906d43e1592be4b25a587b7d96527cb67277542a5611e8ea9e996182fae410"}, - {file = "pyzmq-26.1.0-pp39-pypy39_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:80b0c9942430d731c786545da6be96d824a41a51742e3e374fedd9018ea43106"}, - {file = "pyzmq-26.1.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:314d11564c00b77f6224d12eb3ddebe926c301e86b648a1835c5b28176c83eab"}, - {file = "pyzmq-26.1.0-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:093a1a3cae2496233f14b57f4b485da01b4ff764582c854c0f42c6dd2be37f3d"}, - {file = "pyzmq-26.1.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:3c397b1b450f749a7e974d74c06d69bd22dd362142f370ef2bd32a684d6b480c"}, - {file = "pyzmq-26.1.0.tar.gz", hash = "sha256:6c5aeea71f018ebd3b9115c7cb13863dd850e98ca6b9258509de1246461a7e7f"}, + {file = "pyzmq-26.2.0-cp310-cp310-macosx_10_15_universal2.whl", hash = "sha256:ddf33d97d2f52d89f6e6e7ae66ee35a4d9ca6f36eda89c24591b0c40205a3629"}, + {file = "pyzmq-26.2.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:dacd995031a01d16eec825bf30802fceb2c3791ef24bcce48fa98ce40918c27b"}, + {file = "pyzmq-26.2.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:89289a5ee32ef6c439086184529ae060c741334b8970a6855ec0b6ad3ff28764"}, + {file = "pyzmq-26.2.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5506f06d7dc6ecf1efacb4a013b1f05071bb24b76350832c96449f4a2d95091c"}, + {file = "pyzmq-26.2.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8ea039387c10202ce304af74def5021e9adc6297067f3441d348d2b633e8166a"}, + {file = "pyzmq-26.2.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:a2224fa4a4c2ee872886ed00a571f5e967c85e078e8e8c2530a2fb01b3309b88"}, + {file = "pyzmq-26.2.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:28ad5233e9c3b52d76196c696e362508959741e1a005fb8fa03b51aea156088f"}, + {file = "pyzmq-26.2.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:1c17211bc037c7d88e85ed8b7d8f7e52db6dc8eca5590d162717c654550f7282"}, + {file = "pyzmq-26.2.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b8f86dd868d41bea9a5f873ee13bf5551c94cf6bc51baebc6f85075971fe6eea"}, + {file = "pyzmq-26.2.0-cp310-cp310-win32.whl", hash = "sha256:46a446c212e58456b23af260f3d9fb785054f3e3653dbf7279d8f2b5546b21c2"}, + {file = "pyzmq-26.2.0-cp310-cp310-win_amd64.whl", hash = "sha256:49d34ab71db5a9c292a7644ce74190b1dd5a3475612eefb1f8be1d6961441971"}, + {file = "pyzmq-26.2.0-cp310-cp310-win_arm64.whl", hash = "sha256:bfa832bfa540e5b5c27dcf5de5d82ebc431b82c453a43d141afb1e5d2de025fa"}, + {file = "pyzmq-26.2.0-cp311-cp311-macosx_10_15_universal2.whl", hash = "sha256:8f7e66c7113c684c2b3f1c83cdd3376103ee0ce4c49ff80a648643e57fb22218"}, + {file = "pyzmq-26.2.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3a495b30fc91db2db25120df5847d9833af237546fd59170701acd816ccc01c4"}, + {file = "pyzmq-26.2.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:77eb0968da535cba0470a5165468b2cac7772cfb569977cff92e240f57e31bef"}, + {file = "pyzmq-26.2.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6ace4f71f1900a548f48407fc9be59c6ba9d9aaf658c2eea6cf2779e72f9f317"}, + {file = "pyzmq-26.2.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:92a78853d7280bffb93df0a4a6a2498cba10ee793cc8076ef797ef2f74d107cf"}, + {file = "pyzmq-26.2.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:689c5d781014956a4a6de61d74ba97b23547e431e9e7d64f27d4922ba96e9d6e"}, + {file = "pyzmq-26.2.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:0aca98bc423eb7d153214b2df397c6421ba6373d3397b26c057af3c904452e37"}, + {file = "pyzmq-26.2.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:1f3496d76b89d9429a656293744ceca4d2ac2a10ae59b84c1da9b5165f429ad3"}, + {file = "pyzmq-26.2.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:5c2b3bfd4b9689919db068ac6c9911f3fcb231c39f7dd30e3138be94896d18e6"}, + {file = "pyzmq-26.2.0-cp311-cp311-win32.whl", hash = "sha256:eac5174677da084abf378739dbf4ad245661635f1600edd1221f150b165343f4"}, + {file = "pyzmq-26.2.0-cp311-cp311-win_amd64.whl", hash = "sha256:5a509df7d0a83a4b178d0f937ef14286659225ef4e8812e05580776c70e155d5"}, + {file = "pyzmq-26.2.0-cp311-cp311-win_arm64.whl", hash = "sha256:c0e6091b157d48cbe37bd67233318dbb53e1e6327d6fc3bb284afd585d141003"}, + {file = "pyzmq-26.2.0-cp312-cp312-macosx_10_15_universal2.whl", hash = "sha256:ded0fc7d90fe93ae0b18059930086c51e640cdd3baebdc783a695c77f123dcd9"}, + {file = "pyzmq-26.2.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:17bf5a931c7f6618023cdacc7081f3f266aecb68ca692adac015c383a134ca52"}, + {file = "pyzmq-26.2.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:55cf66647e49d4621a7e20c8d13511ef1fe1efbbccf670811864452487007e08"}, + {file = "pyzmq-26.2.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4661c88db4a9e0f958c8abc2b97472e23061f0bc737f6f6179d7a27024e1faa5"}, + {file = "pyzmq-26.2.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ea7f69de383cb47522c9c208aec6dd17697db7875a4674c4af3f8cfdac0bdeae"}, + {file = "pyzmq-26.2.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:7f98f6dfa8b8ccaf39163ce872bddacca38f6a67289116c8937a02e30bbe9711"}, + {file = "pyzmq-26.2.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:e3e0210287329272539eea617830a6a28161fbbd8a3271bf4150ae3e58c5d0e6"}, + {file = "pyzmq-26.2.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:6b274e0762c33c7471f1a7471d1a2085b1a35eba5cdc48d2ae319f28b6fc4de3"}, + {file = "pyzmq-26.2.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:29c6a4635eef69d68a00321e12a7d2559fe2dfccfa8efae3ffb8e91cd0b36a8b"}, + {file = "pyzmq-26.2.0-cp312-cp312-win32.whl", hash = "sha256:989d842dc06dc59feea09e58c74ca3e1678c812a4a8a2a419046d711031f69c7"}, + {file = "pyzmq-26.2.0-cp312-cp312-win_amd64.whl", hash = "sha256:2a50625acdc7801bc6f74698c5c583a491c61d73c6b7ea4dee3901bb99adb27a"}, + {file = "pyzmq-26.2.0-cp312-cp312-win_arm64.whl", hash = "sha256:4d29ab8592b6ad12ebbf92ac2ed2bedcfd1cec192d8e559e2e099f648570e19b"}, + {file = "pyzmq-26.2.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:9dd8cd1aeb00775f527ec60022004d030ddc51d783d056e3e23e74e623e33726"}, + {file = "pyzmq-26.2.0-cp313-cp313-macosx_10_15_universal2.whl", hash = "sha256:28c812d9757fe8acecc910c9ac9dafd2ce968c00f9e619db09e9f8f54c3a68a3"}, + {file = "pyzmq-26.2.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4d80b1dd99c1942f74ed608ddb38b181b87476c6a966a88a950c7dee118fdf50"}, + {file = "pyzmq-26.2.0-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8c997098cc65e3208eca09303630e84d42718620e83b733d0fd69543a9cab9cb"}, + {file = "pyzmq-26.2.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7ad1bc8d1b7a18497dda9600b12dc193c577beb391beae5cd2349184db40f187"}, + {file = "pyzmq-26.2.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:bea2acdd8ea4275e1278350ced63da0b166421928276c7c8e3f9729d7402a57b"}, + {file = "pyzmq-26.2.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:23f4aad749d13698f3f7b64aad34f5fc02d6f20f05999eebc96b89b01262fb18"}, + {file = "pyzmq-26.2.0-cp313-cp313-musllinux_1_1_i686.whl", hash = "sha256:a4f96f0d88accc3dbe4a9025f785ba830f968e21e3e2c6321ccdfc9aef755115"}, + {file = "pyzmq-26.2.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:ced65e5a985398827cc9276b93ef6dfabe0273c23de8c7931339d7e141c2818e"}, + {file = "pyzmq-26.2.0-cp313-cp313-win32.whl", hash = "sha256:31507f7b47cc1ead1f6e86927f8ebb196a0bab043f6345ce070f412a59bf87b5"}, + {file = "pyzmq-26.2.0-cp313-cp313-win_amd64.whl", hash = "sha256:70fc7fcf0410d16ebdda9b26cbd8bf8d803d220a7f3522e060a69a9c87bf7bad"}, + {file = "pyzmq-26.2.0-cp313-cp313-win_arm64.whl", hash = "sha256:c3789bd5768ab5618ebf09cef6ec2b35fed88709b104351748a63045f0ff9797"}, + {file = "pyzmq-26.2.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:034da5fc55d9f8da09015d368f519478a52675e558c989bfcb5cf6d4e16a7d2a"}, + {file = "pyzmq-26.2.0-cp313-cp313t-macosx_10_15_universal2.whl", hash = "sha256:c92d73464b886931308ccc45b2744e5968cbaade0b1d6aeb40d8ab537765f5bc"}, + {file = "pyzmq-26.2.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:794a4562dcb374f7dbbfb3f51d28fb40123b5a2abadee7b4091f93054909add5"}, + {file = "pyzmq-26.2.0-cp313-cp313t-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:aee22939bb6075e7afededabad1a56a905da0b3c4e3e0c45e75810ebe3a52672"}, + {file = "pyzmq-26.2.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2ae90ff9dad33a1cfe947d2c40cb9cb5e600d759ac4f0fd22616ce6540f72797"}, + {file = "pyzmq-26.2.0-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:43a47408ac52647dfabbc66a25b05b6a61700b5165807e3fbd40063fcaf46386"}, + {file = "pyzmq-26.2.0-cp313-cp313t-musllinux_1_1_aarch64.whl", hash = "sha256:25bf2374a2a8433633c65ccb9553350d5e17e60c8eb4de4d92cc6bd60f01d306"}, + {file = "pyzmq-26.2.0-cp313-cp313t-musllinux_1_1_i686.whl", hash = "sha256:007137c9ac9ad5ea21e6ad97d3489af654381324d5d3ba614c323f60dab8fae6"}, + {file = "pyzmq-26.2.0-cp313-cp313t-musllinux_1_1_x86_64.whl", hash = "sha256:470d4a4f6d48fb34e92d768b4e8a5cc3780db0d69107abf1cd7ff734b9766eb0"}, + {file = "pyzmq-26.2.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:3b55a4229ce5da9497dd0452b914556ae58e96a4381bb6f59f1305dfd7e53fc8"}, + {file = "pyzmq-26.2.0-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:9cb3a6460cdea8fe8194a76de8895707e61ded10ad0be97188cc8463ffa7e3a8"}, + {file = "pyzmq-26.2.0-cp37-cp37m-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:8ab5cad923cc95c87bffee098a27856c859bd5d0af31bd346035aa816b081fe1"}, + {file = "pyzmq-26.2.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9ed69074a610fad1c2fda66180e7b2edd4d31c53f2d1872bc2d1211563904cd9"}, + {file = "pyzmq-26.2.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:cccba051221b916a4f5e538997c45d7d136a5646442b1231b916d0164067ea27"}, + {file = "pyzmq-26.2.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:0eaa83fc4c1e271c24eaf8fb083cbccef8fde77ec8cd45f3c35a9a123e6da097"}, + {file = "pyzmq-26.2.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:9edda2df81daa129b25a39b86cb57dfdfe16f7ec15b42b19bfac503360d27a93"}, + {file = "pyzmq-26.2.0-cp37-cp37m-win32.whl", hash = "sha256:ea0eb6af8a17fa272f7b98d7bebfab7836a0d62738e16ba380f440fceca2d951"}, + {file = "pyzmq-26.2.0-cp37-cp37m-win_amd64.whl", hash = "sha256:4ff9dc6bc1664bb9eec25cd17506ef6672d506115095411e237d571e92a58231"}, + {file = "pyzmq-26.2.0-cp38-cp38-macosx_10_15_universal2.whl", hash = "sha256:2eb7735ee73ca1b0d71e0e67c3739c689067f055c764f73aac4cc8ecf958ee3f"}, + {file = "pyzmq-26.2.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1a534f43bc738181aa7cbbaf48e3eca62c76453a40a746ab95d4b27b1111a7d2"}, + {file = "pyzmq-26.2.0-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:aedd5dd8692635813368e558a05266b995d3d020b23e49581ddd5bbe197a8ab6"}, + {file = "pyzmq-26.2.0-cp38-cp38-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:8be4700cd8bb02cc454f630dcdf7cfa99de96788b80c51b60fe2fe1dac480289"}, + {file = "pyzmq-26.2.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1fcc03fa4997c447dce58264e93b5aa2d57714fbe0f06c07b7785ae131512732"}, + {file = "pyzmq-26.2.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:402b190912935d3db15b03e8f7485812db350d271b284ded2b80d2e5704be780"}, + {file = "pyzmq-26.2.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:8685fa9c25ff00f550c1fec650430c4b71e4e48e8d852f7ddcf2e48308038640"}, + {file = "pyzmq-26.2.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:76589c020680778f06b7e0b193f4b6dd66d470234a16e1df90329f5e14a171cd"}, + {file = "pyzmq-26.2.0-cp38-cp38-win32.whl", hash = "sha256:8423c1877d72c041f2c263b1ec6e34360448decfb323fa8b94e85883043ef988"}, + {file = "pyzmq-26.2.0-cp38-cp38-win_amd64.whl", hash = "sha256:76589f2cd6b77b5bdea4fca5992dc1c23389d68b18ccc26a53680ba2dc80ff2f"}, + {file = "pyzmq-26.2.0-cp39-cp39-macosx_10_15_universal2.whl", hash = "sha256:b1d464cb8d72bfc1a3adc53305a63a8e0cac6bc8c5a07e8ca190ab8d3faa43c2"}, + {file = "pyzmq-26.2.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:4da04c48873a6abdd71811c5e163bd656ee1b957971db7f35140a2d573f6949c"}, + {file = "pyzmq-26.2.0-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:d049df610ac811dcffdc147153b414147428567fbbc8be43bb8885f04db39d98"}, + {file = "pyzmq-26.2.0-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:05590cdbc6b902101d0e65d6a4780af14dc22914cc6ab995d99b85af45362cc9"}, + {file = "pyzmq-26.2.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c811cfcd6a9bf680236c40c6f617187515269ab2912f3d7e8c0174898e2519db"}, + {file = "pyzmq-26.2.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:6835dd60355593de10350394242b5757fbbd88b25287314316f266e24c61d073"}, + {file = "pyzmq-26.2.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:bc6bee759a6bddea5db78d7dcd609397449cb2d2d6587f48f3ca613b19410cfc"}, + {file = "pyzmq-26.2.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:c530e1eecd036ecc83c3407f77bb86feb79916d4a33d11394b8234f3bd35b940"}, + {file = "pyzmq-26.2.0-cp39-cp39-win32.whl", hash = "sha256:367b4f689786fca726ef7a6c5ba606958b145b9340a5e4808132cc65759abd44"}, + {file = "pyzmq-26.2.0-cp39-cp39-win_amd64.whl", hash = "sha256:e6fa2e3e683f34aea77de8112f6483803c96a44fd726d7358b9888ae5bb394ec"}, + {file = "pyzmq-26.2.0-cp39-cp39-win_arm64.whl", hash = "sha256:7445be39143a8aa4faec43b076e06944b8f9d0701b669df4af200531b21e40bb"}, + {file = "pyzmq-26.2.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:706e794564bec25819d21a41c31d4df2d48e1cc4b061e8d345d7fb4dd3e94072"}, + {file = "pyzmq-26.2.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8b435f2753621cd36e7c1762156815e21c985c72b19135dac43a7f4f31d28dd1"}, + {file = "pyzmq-26.2.0-pp310-pypy310_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:160c7e0a5eb178011e72892f99f918c04a131f36056d10d9c1afb223fc952c2d"}, + {file = "pyzmq-26.2.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2c4a71d5d6e7b28a47a394c0471b7e77a0661e2d651e7ae91e0cab0a587859ca"}, + {file = "pyzmq-26.2.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:90412f2db8c02a3864cbfc67db0e3dcdbda336acf1c469526d3e869394fe001c"}, + {file = "pyzmq-26.2.0-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:2ea4ad4e6a12e454de05f2949d4beddb52460f3de7c8b9d5c46fbb7d7222e02c"}, + {file = "pyzmq-26.2.0-pp37-pypy37_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:fc4f7a173a5609631bb0c42c23d12c49df3966f89f496a51d3eb0ec81f4519d6"}, + {file = "pyzmq-26.2.0-pp37-pypy37_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:878206a45202247781472a2d99df12a176fef806ca175799e1c6ad263510d57c"}, + {file = "pyzmq-26.2.0-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:17c412bad2eb9468e876f556eb4ee910e62d721d2c7a53c7fa31e643d35352e6"}, + {file = "pyzmq-26.2.0-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:0d987a3ae5a71c6226b203cfd298720e0086c7fe7c74f35fa8edddfbd6597eed"}, + {file = "pyzmq-26.2.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:39887ac397ff35b7b775db7201095fc6310a35fdbae85bac4523f7eb3b840e20"}, + {file = "pyzmq-26.2.0-pp38-pypy38_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:fdb5b3e311d4d4b0eb8b3e8b4d1b0a512713ad7e6a68791d0923d1aec433d919"}, + {file = "pyzmq-26.2.0-pp38-pypy38_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:226af7dcb51fdb0109f0016449b357e182ea0ceb6b47dfb5999d569e5db161d5"}, + {file = "pyzmq-26.2.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0bed0e799e6120b9c32756203fb9dfe8ca2fb8467fed830c34c877e25638c3fc"}, + {file = "pyzmq-26.2.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:29c7947c594e105cb9e6c466bace8532dc1ca02d498684128b339799f5248277"}, + {file = "pyzmq-26.2.0-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:cdeabcff45d1c219636ee2e54d852262e5c2e085d6cb476d938aee8d921356b3"}, + {file = "pyzmq-26.2.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:35cffef589bcdc587d06f9149f8d5e9e8859920a071df5a2671de2213bef592a"}, + {file = "pyzmq-26.2.0-pp39-pypy39_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:18c8dc3b7468d8b4bdf60ce9d7141897da103c7a4690157b32b60acb45e333e6"}, + {file = "pyzmq-26.2.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7133d0a1677aec369d67dd78520d3fa96dd7f3dcec99d66c1762870e5ea1a50a"}, + {file = "pyzmq-26.2.0-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:6a96179a24b14fa6428cbfc08641c779a53f8fcec43644030328f44034c7f1f4"}, + {file = "pyzmq-26.2.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:4f78c88905461a9203eac9faac157a2a0dbba84a0fd09fd29315db27be40af9f"}, + {file = "pyzmq-26.2.0.tar.gz", hash = "sha256:070672c258581c8e4f640b5159297580a9974b026043bd4ab0470be9ed324f1f"}, ] [package.dependencies] @@ -3506,90 +3560,105 @@ rpds-py = ">=0.7.0" [[package]] name = "regex" -version = "2024.7.24" +version = "2024.9.11" description = "Alternative regular expression module, to replace re." optional = false python-versions = ">=3.8" files = [ - {file = "regex-2024.7.24-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:228b0d3f567fafa0633aee87f08b9276c7062da9616931382993c03808bb68ce"}, - {file = "regex-2024.7.24-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:3426de3b91d1bc73249042742f45c2148803c111d1175b283270177fdf669024"}, - {file = "regex-2024.7.24-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f273674b445bcb6e4409bf8d1be67bc4b58e8b46fd0d560055d515b8830063cd"}, - {file = "regex-2024.7.24-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:23acc72f0f4e1a9e6e9843d6328177ae3074b4182167e34119ec7233dfeccf53"}, - {file = "regex-2024.7.24-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:65fd3d2e228cae024c411c5ccdffae4c315271eee4a8b839291f84f796b34eca"}, - {file = "regex-2024.7.24-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c414cbda77dbf13c3bc88b073a1a9f375c7b0cb5e115e15d4b73ec3a2fbc6f59"}, - {file = "regex-2024.7.24-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bf7a89eef64b5455835f5ed30254ec19bf41f7541cd94f266ab7cbd463f00c41"}, - {file = "regex-2024.7.24-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:19c65b00d42804e3fbea9708f0937d157e53429a39b7c61253ff15670ff62cb5"}, - {file = "regex-2024.7.24-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:7a5486ca56c8869070a966321d5ab416ff0f83f30e0e2da1ab48815c8d165d46"}, - {file = "regex-2024.7.24-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:6f51f9556785e5a203713f5efd9c085b4a45aecd2a42573e2b5041881b588d1f"}, - {file = "regex-2024.7.24-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:a4997716674d36a82eab3e86f8fa77080a5d8d96a389a61ea1d0e3a94a582cf7"}, - {file = "regex-2024.7.24-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:c0abb5e4e8ce71a61d9446040c1e86d4e6d23f9097275c5bd49ed978755ff0fe"}, - {file = "regex-2024.7.24-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:18300a1d78cf1290fa583cd8b7cde26ecb73e9f5916690cf9d42de569c89b1ce"}, - {file = "regex-2024.7.24-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:416c0e4f56308f34cdb18c3f59849479dde5b19febdcd6e6fa4d04b6c31c9faa"}, - {file = "regex-2024.7.24-cp310-cp310-win32.whl", hash = "sha256:fb168b5924bef397b5ba13aabd8cf5df7d3d93f10218d7b925e360d436863f66"}, - {file = "regex-2024.7.24-cp310-cp310-win_amd64.whl", hash = "sha256:6b9fc7e9cc983e75e2518496ba1afc524227c163e43d706688a6bb9eca41617e"}, - {file = "regex-2024.7.24-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:382281306e3adaaa7b8b9ebbb3ffb43358a7bbf585fa93821300a418bb975281"}, - {file = "regex-2024.7.24-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4fdd1384619f406ad9037fe6b6eaa3de2749e2e12084abc80169e8e075377d3b"}, - {file = "regex-2024.7.24-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3d974d24edb231446f708c455fd08f94c41c1ff4f04bcf06e5f36df5ef50b95a"}, - {file = "regex-2024.7.24-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a2ec4419a3fe6cf8a4795752596dfe0adb4aea40d3683a132bae9c30b81e8d73"}, - {file = "regex-2024.7.24-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:eb563dd3aea54c797adf513eeec819c4213d7dbfc311874eb4fd28d10f2ff0f2"}, - {file = "regex-2024.7.24-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:45104baae8b9f67569f0f1dca5e1f1ed77a54ae1cd8b0b07aba89272710db61e"}, - {file = "regex-2024.7.24-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:994448ee01864501912abf2bad9203bffc34158e80fe8bfb5b031f4f8e16da51"}, - {file = "regex-2024.7.24-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3fac296f99283ac232d8125be932c5cd7644084a30748fda013028c815ba3364"}, - {file = "regex-2024.7.24-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:7e37e809b9303ec3a179085415cb5f418ecf65ec98cdfe34f6a078b46ef823ee"}, - {file = "regex-2024.7.24-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:01b689e887f612610c869421241e075c02f2e3d1ae93a037cb14f88ab6a8934c"}, - {file = "regex-2024.7.24-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:f6442f0f0ff81775eaa5b05af8a0ffa1dda36e9cf6ec1e0d3d245e8564b684ce"}, - {file = "regex-2024.7.24-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:871e3ab2838fbcb4e0865a6e01233975df3a15e6fce93b6f99d75cacbd9862d1"}, - {file = "regex-2024.7.24-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:c918b7a1e26b4ab40409820ddccc5d49871a82329640f5005f73572d5eaa9b5e"}, - {file = "regex-2024.7.24-cp311-cp311-win32.whl", hash = "sha256:2dfbb8baf8ba2c2b9aa2807f44ed272f0913eeeba002478c4577b8d29cde215c"}, - {file = "regex-2024.7.24-cp311-cp311-win_amd64.whl", hash = "sha256:538d30cd96ed7d1416d3956f94d54e426a8daf7c14527f6e0d6d425fcb4cca52"}, - {file = "regex-2024.7.24-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:fe4ebef608553aff8deb845c7f4f1d0740ff76fa672c011cc0bacb2a00fbde86"}, - {file = "regex-2024.7.24-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:74007a5b25b7a678459f06559504f1eec2f0f17bca218c9d56f6a0a12bfffdad"}, - {file = "regex-2024.7.24-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:7df9ea48641da022c2a3c9c641650cd09f0cd15e8908bf931ad538f5ca7919c9"}, - {file = "regex-2024.7.24-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6a1141a1dcc32904c47f6846b040275c6e5de0bf73f17d7a409035d55b76f289"}, - {file = "regex-2024.7.24-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:80c811cfcb5c331237d9bad3bea2c391114588cf4131707e84d9493064d267f9"}, - {file = "regex-2024.7.24-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7214477bf9bd195894cf24005b1e7b496f46833337b5dedb7b2a6e33f66d962c"}, - {file = "regex-2024.7.24-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d55588cba7553f0b6ec33130bc3e114b355570b45785cebdc9daed8c637dd440"}, - {file = "regex-2024.7.24-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:558a57cfc32adcf19d3f791f62b5ff564922942e389e3cfdb538a23d65a6b610"}, - {file = "regex-2024.7.24-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a512eed9dfd4117110b1881ba9a59b31433caed0c4101b361f768e7bcbaf93c5"}, - {file = "regex-2024.7.24-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:86b17ba823ea76256b1885652e3a141a99a5c4422f4a869189db328321b73799"}, - {file = "regex-2024.7.24-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:5eefee9bfe23f6df09ffb6dfb23809f4d74a78acef004aa904dc7c88b9944b05"}, - {file = "regex-2024.7.24-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:731fcd76bbdbf225e2eb85b7c38da9633ad3073822f5ab32379381e8c3c12e94"}, - {file = "regex-2024.7.24-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:eaef80eac3b4cfbdd6de53c6e108b4c534c21ae055d1dbea2de6b3b8ff3def38"}, - {file = "regex-2024.7.24-cp312-cp312-win32.whl", hash = "sha256:185e029368d6f89f36e526764cf12bf8d6f0e3a2a7737da625a76f594bdfcbfc"}, - {file = "regex-2024.7.24-cp312-cp312-win_amd64.whl", hash = "sha256:2f1baff13cc2521bea83ab2528e7a80cbe0ebb2c6f0bfad15be7da3aed443908"}, - {file = "regex-2024.7.24-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:66b4c0731a5c81921e938dcf1a88e978264e26e6ac4ec96a4d21ae0354581ae0"}, - {file = "regex-2024.7.24-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:88ecc3afd7e776967fa16c80f974cb79399ee8dc6c96423321d6f7d4b881c92b"}, - {file = "regex-2024.7.24-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:64bd50cf16bcc54b274e20235bf8edbb64184a30e1e53873ff8d444e7ac656b2"}, - {file = "regex-2024.7.24-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eb462f0e346fcf41a901a126b50f8781e9a474d3927930f3490f38a6e73b6950"}, - {file = "regex-2024.7.24-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a82465ebbc9b1c5c50738536fdfa7cab639a261a99b469c9d4c7dcbb2b3f1e57"}, - {file = "regex-2024.7.24-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:68a8f8c046c6466ac61a36b65bb2395c74451df2ffb8458492ef49900efed293"}, - {file = "regex-2024.7.24-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dac8e84fff5d27420f3c1e879ce9929108e873667ec87e0c8eeb413a5311adfe"}, - {file = "regex-2024.7.24-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ba2537ef2163db9e6ccdbeb6f6424282ae4dea43177402152c67ef869cf3978b"}, - {file = "regex-2024.7.24-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:43affe33137fcd679bdae93fb25924979517e011f9dea99163f80b82eadc7e53"}, - {file = "regex-2024.7.24-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:c9bb87fdf2ab2370f21e4d5636e5317775e5d51ff32ebff2cf389f71b9b13750"}, - {file = "regex-2024.7.24-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:945352286a541406f99b2655c973852da7911b3f4264e010218bbc1cc73168f2"}, - {file = "regex-2024.7.24-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:8bc593dcce679206b60a538c302d03c29b18e3d862609317cb560e18b66d10cf"}, - {file = "regex-2024.7.24-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:3f3b6ca8eae6d6c75a6cff525c8530c60e909a71a15e1b731723233331de4169"}, - {file = "regex-2024.7.24-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:c51edc3541e11fbe83f0c4d9412ef6c79f664a3745fab261457e84465ec9d5a8"}, - {file = "regex-2024.7.24-cp38-cp38-win32.whl", hash = "sha256:d0a07763776188b4db4c9c7fb1b8c494049f84659bb387b71c73bbc07f189e96"}, - {file = "regex-2024.7.24-cp38-cp38-win_amd64.whl", hash = "sha256:8fd5afd101dcf86a270d254364e0e8dddedebe6bd1ab9d5f732f274fa00499a5"}, - {file = "regex-2024.7.24-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:0ffe3f9d430cd37d8fa5632ff6fb36d5b24818c5c986893063b4e5bdb84cdf24"}, - {file = "regex-2024.7.24-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:25419b70ba00a16abc90ee5fce061228206173231f004437730b67ac77323f0d"}, - {file = "regex-2024.7.24-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:33e2614a7ce627f0cdf2ad104797d1f68342d967de3695678c0cb84f530709f8"}, - {file = "regex-2024.7.24-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d33a0021893ede5969876052796165bab6006559ab845fd7b515a30abdd990dc"}, - {file = "regex-2024.7.24-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:04ce29e2c5fedf296b1a1b0acc1724ba93a36fb14031f3abfb7abda2806c1535"}, - {file = "regex-2024.7.24-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b16582783f44fbca6fcf46f61347340c787d7530d88b4d590a397a47583f31dd"}, - {file = "regex-2024.7.24-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:836d3cc225b3e8a943d0b02633fb2f28a66e281290302a79df0e1eaa984ff7c1"}, - {file = "regex-2024.7.24-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:438d9f0f4bc64e8dea78274caa5af971ceff0f8771e1a2333620969936ba10be"}, - {file = "regex-2024.7.24-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:973335b1624859cb0e52f96062a28aa18f3a5fc77a96e4a3d6d76e29811a0e6e"}, - {file = "regex-2024.7.24-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:c5e69fd3eb0b409432b537fe3c6f44ac089c458ab6b78dcec14478422879ec5f"}, - {file = "regex-2024.7.24-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:fbf8c2f00904eaf63ff37718eb13acf8e178cb940520e47b2f05027f5bb34ce3"}, - {file = "regex-2024.7.24-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:ae2757ace61bc4061b69af19e4689fa4416e1a04840f33b441034202b5cd02d4"}, - {file = "regex-2024.7.24-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:44fc61b99035fd9b3b9453f1713234e5a7c92a04f3577252b45feefe1b327759"}, - {file = "regex-2024.7.24-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:84c312cdf839e8b579f504afcd7b65f35d60b6285d892b19adea16355e8343c9"}, - {file = "regex-2024.7.24-cp39-cp39-win32.whl", hash = "sha256:ca5b2028c2f7af4e13fb9fc29b28d0ce767c38c7facdf64f6c2cd040413055f1"}, - {file = "regex-2024.7.24-cp39-cp39-win_amd64.whl", hash = "sha256:7c479f5ae937ec9985ecaf42e2e10631551d909f203e31308c12d703922742f9"}, - {file = "regex-2024.7.24.tar.gz", hash = "sha256:9cfd009eed1a46b27c14039ad5bbc5e71b6367c5b2e6d5f5da0ea91600817506"}, + {file = "regex-2024.9.11-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:1494fa8725c285a81d01dc8c06b55287a1ee5e0e382d8413adc0a9197aac6408"}, + {file = "regex-2024.9.11-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0e12c481ad92d129c78f13a2a3662317e46ee7ef96c94fd332e1c29131875b7d"}, + {file = "regex-2024.9.11-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:16e13a7929791ac1216afde26f712802e3df7bf0360b32e4914dca3ab8baeea5"}, + {file = "regex-2024.9.11-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:46989629904bad940bbec2106528140a218b4a36bb3042d8406980be1941429c"}, + {file = "regex-2024.9.11-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a906ed5e47a0ce5f04b2c981af1c9acf9e8696066900bf03b9d7879a6f679fc8"}, + {file = "regex-2024.9.11-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e9a091b0550b3b0207784a7d6d0f1a00d1d1c8a11699c1a4d93db3fbefc3ad35"}, + {file = "regex-2024.9.11-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5ddcd9a179c0a6fa8add279a4444015acddcd7f232a49071ae57fa6e278f1f71"}, + {file = "regex-2024.9.11-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6b41e1adc61fa347662b09398e31ad446afadff932a24807d3ceb955ed865cc8"}, + {file = "regex-2024.9.11-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:ced479f601cd2f8ca1fd7b23925a7e0ad512a56d6e9476f79b8f381d9d37090a"}, + {file = "regex-2024.9.11-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:635a1d96665f84b292e401c3d62775851aedc31d4f8784117b3c68c4fcd4118d"}, + {file = "regex-2024.9.11-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:c0256beda696edcf7d97ef16b2a33a8e5a875affd6fa6567b54f7c577b30a137"}, + {file = "regex-2024.9.11-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:3ce4f1185db3fbde8ed8aa223fc9620f276c58de8b0d4f8cc86fd1360829edb6"}, + {file = "regex-2024.9.11-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:09d77559e80dcc9d24570da3745ab859a9cf91953062e4ab126ba9d5993688ca"}, + {file = "regex-2024.9.11-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:7a22ccefd4db3f12b526eccb129390942fe874a3a9fdbdd24cf55773a1faab1a"}, + {file = "regex-2024.9.11-cp310-cp310-win32.whl", hash = "sha256:f745ec09bc1b0bd15cfc73df6fa4f726dcc26bb16c23a03f9e3367d357eeedd0"}, + {file = "regex-2024.9.11-cp310-cp310-win_amd64.whl", hash = "sha256:01c2acb51f8a7d6494c8c5eafe3d8e06d76563d8a8a4643b37e9b2dd8a2ff623"}, + {file = "regex-2024.9.11-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:2cce2449e5927a0bf084d346da6cd5eb016b2beca10d0013ab50e3c226ffc0df"}, + {file = "regex-2024.9.11-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3b37fa423beefa44919e009745ccbf353d8c981516e807995b2bd11c2c77d268"}, + {file = "regex-2024.9.11-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:64ce2799bd75039b480cc0360907c4fb2f50022f030bf9e7a8705b636e408fad"}, + {file = "regex-2024.9.11-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a4cc92bb6db56ab0c1cbd17294e14f5e9224f0cc6521167ef388332604e92679"}, + {file = "regex-2024.9.11-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d05ac6fa06959c4172eccd99a222e1fbf17b5670c4d596cb1e5cde99600674c4"}, + {file = "regex-2024.9.11-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:040562757795eeea356394a7fb13076ad4f99d3c62ab0f8bdfb21f99a1f85664"}, + {file = "regex-2024.9.11-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6113c008a7780792efc80f9dfe10ba0cd043cbf8dc9a76ef757850f51b4edc50"}, + {file = "regex-2024.9.11-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8e5fb5f77c8745a60105403a774fe2c1759b71d3e7b4ca237a5e67ad066c7199"}, + {file = "regex-2024.9.11-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:54d9ff35d4515debf14bc27f1e3b38bfc453eff3220f5bce159642fa762fe5d4"}, + {file = "regex-2024.9.11-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:df5cbb1fbc74a8305b6065d4ade43b993be03dbe0f8b30032cced0d7740994bd"}, + {file = "regex-2024.9.11-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:7fb89ee5d106e4a7a51bce305ac4efb981536301895f7bdcf93ec92ae0d91c7f"}, + {file = "regex-2024.9.11-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:a738b937d512b30bf75995c0159c0ddf9eec0775c9d72ac0202076c72f24aa96"}, + {file = "regex-2024.9.11-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:e28f9faeb14b6f23ac55bfbbfd3643f5c7c18ede093977f1df249f73fd22c7b1"}, + {file = "regex-2024.9.11-cp311-cp311-win32.whl", hash = "sha256:18e707ce6c92d7282dfce370cd205098384b8ee21544e7cb29b8aab955b66fa9"}, + {file = "regex-2024.9.11-cp311-cp311-win_amd64.whl", hash = "sha256:313ea15e5ff2a8cbbad96ccef6be638393041b0a7863183c2d31e0c6116688cf"}, + {file = "regex-2024.9.11-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:b0d0a6c64fcc4ef9c69bd5b3b3626cc3776520a1637d8abaa62b9edc147a58f7"}, + {file = "regex-2024.9.11-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:49b0e06786ea663f933f3710a51e9385ce0cba0ea56b67107fd841a55d56a231"}, + {file = "regex-2024.9.11-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:5b513b6997a0b2f10e4fd3a1313568e373926e8c252bd76c960f96fd039cd28d"}, + {file = "regex-2024.9.11-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ee439691d8c23e76f9802c42a95cfeebf9d47cf4ffd06f18489122dbb0a7ad64"}, + {file = "regex-2024.9.11-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a8f877c89719d759e52783f7fe6e1c67121076b87b40542966c02de5503ace42"}, + {file = "regex-2024.9.11-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:23b30c62d0f16827f2ae9f2bb87619bc4fba2044911e2e6c2eb1af0161cdb766"}, + {file = "regex-2024.9.11-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:85ab7824093d8f10d44330fe1e6493f756f252d145323dd17ab6b48733ff6c0a"}, + {file = "regex-2024.9.11-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8dee5b4810a89447151999428fe096977346cf2f29f4d5e29609d2e19e0199c9"}, + {file = "regex-2024.9.11-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:98eeee2f2e63edae2181c886d7911ce502e1292794f4c5ee71e60e23e8d26b5d"}, + {file = "regex-2024.9.11-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:57fdd2e0b2694ce6fc2e5ccf189789c3e2962916fb38779d3e3521ff8fe7a822"}, + {file = "regex-2024.9.11-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:d552c78411f60b1fdaafd117a1fca2f02e562e309223b9d44b7de8be451ec5e0"}, + {file = "regex-2024.9.11-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:a0b2b80321c2ed3fcf0385ec9e51a12253c50f146fddb2abbb10f033fe3d049a"}, + {file = "regex-2024.9.11-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:18406efb2f5a0e57e3a5881cd9354c1512d3bb4f5c45d96d110a66114d84d23a"}, + {file = "regex-2024.9.11-cp312-cp312-win32.whl", hash = "sha256:e464b467f1588e2c42d26814231edecbcfe77f5ac414d92cbf4e7b55b2c2a776"}, + {file = "regex-2024.9.11-cp312-cp312-win_amd64.whl", hash = "sha256:9e8719792ca63c6b8340380352c24dcb8cd7ec49dae36e963742a275dfae6009"}, + {file = "regex-2024.9.11-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:c157bb447303070f256e084668b702073db99bbb61d44f85d811025fcf38f784"}, + {file = "regex-2024.9.11-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:4db21ece84dfeefc5d8a3863f101995de646c6cb0536952c321a2650aa202c36"}, + {file = "regex-2024.9.11-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:220e92a30b426daf23bb67a7962900ed4613589bab80382be09b48896d211e92"}, + {file = "regex-2024.9.11-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eb1ae19e64c14c7ec1995f40bd932448713d3c73509e82d8cd7744dc00e29e86"}, + {file = "regex-2024.9.11-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f47cd43a5bfa48f86925fe26fbdd0a488ff15b62468abb5d2a1e092a4fb10e85"}, + {file = "regex-2024.9.11-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9d4a76b96f398697fe01117093613166e6aa8195d63f1b4ec3f21ab637632963"}, + {file = "regex-2024.9.11-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0ea51dcc0835eea2ea31d66456210a4e01a076d820e9039b04ae8d17ac11dee6"}, + {file = "regex-2024.9.11-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b7aaa315101c6567a9a45d2839322c51c8d6e81f67683d529512f5bcfb99c802"}, + {file = "regex-2024.9.11-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:c57d08ad67aba97af57a7263c2d9006d5c404d721c5f7542f077f109ec2a4a29"}, + {file = "regex-2024.9.11-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:f8404bf61298bb6f8224bb9176c1424548ee1181130818fcd2cbffddc768bed8"}, + {file = "regex-2024.9.11-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:dd4490a33eb909ef5078ab20f5f000087afa2a4daa27b4c072ccb3cb3050ad84"}, + {file = "regex-2024.9.11-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:eee9130eaad130649fd73e5cd92f60e55708952260ede70da64de420cdcad554"}, + {file = "regex-2024.9.11-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6a2644a93da36c784e546de579ec1806bfd2763ef47babc1b03d765fe560c9f8"}, + {file = "regex-2024.9.11-cp313-cp313-win32.whl", hash = "sha256:e997fd30430c57138adc06bba4c7c2968fb13d101e57dd5bb9355bf8ce3fa7e8"}, + {file = "regex-2024.9.11-cp313-cp313-win_amd64.whl", hash = "sha256:042c55879cfeb21a8adacc84ea347721d3d83a159da6acdf1116859e2427c43f"}, + {file = "regex-2024.9.11-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:35f4a6f96aa6cb3f2f7247027b07b15a374f0d5b912c0001418d1d55024d5cb4"}, + {file = "regex-2024.9.11-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:55b96e7ce3a69a8449a66984c268062fbaa0d8ae437b285428e12797baefce7e"}, + {file = "regex-2024.9.11-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:cb130fccd1a37ed894824b8c046321540263013da72745d755f2d35114b81a60"}, + {file = "regex-2024.9.11-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:323c1f04be6b2968944d730e5c2091c8c89767903ecaa135203eec4565ed2b2b"}, + {file = "regex-2024.9.11-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:be1c8ed48c4c4065ecb19d882a0ce1afe0745dfad8ce48c49586b90a55f02366"}, + {file = "regex-2024.9.11-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b5b029322e6e7b94fff16cd120ab35a253236a5f99a79fb04fda7ae71ca20ae8"}, + {file = "regex-2024.9.11-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f6fff13ef6b5f29221d6904aa816c34701462956aa72a77f1f151a8ec4f56aeb"}, + {file = "regex-2024.9.11-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:587d4af3979376652010e400accc30404e6c16b7df574048ab1f581af82065e4"}, + {file = "regex-2024.9.11-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:079400a8269544b955ffa9e31f186f01d96829110a3bf79dc338e9910f794fca"}, + {file = "regex-2024.9.11-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:f9268774428ec173654985ce55fc6caf4c6d11ade0f6f914d48ef4719eb05ebb"}, + {file = "regex-2024.9.11-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:23f9985c8784e544d53fc2930fc1ac1a7319f5d5332d228437acc9f418f2f168"}, + {file = "regex-2024.9.11-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:ae2941333154baff9838e88aa71c1d84f4438189ecc6021a12c7573728b5838e"}, + {file = "regex-2024.9.11-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:e93f1c331ca8e86fe877a48ad64e77882c0c4da0097f2212873a69bbfea95d0c"}, + {file = "regex-2024.9.11-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:846bc79ee753acf93aef4184c040d709940c9d001029ceb7b7a52747b80ed2dd"}, + {file = "regex-2024.9.11-cp38-cp38-win32.whl", hash = "sha256:c94bb0a9f1db10a1d16c00880bdebd5f9faf267273b8f5bd1878126e0fbde771"}, + {file = "regex-2024.9.11-cp38-cp38-win_amd64.whl", hash = "sha256:2b08fce89fbd45664d3df6ad93e554b6c16933ffa9d55cb7e01182baaf971508"}, + {file = "regex-2024.9.11-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:07f45f287469039ffc2c53caf6803cd506eb5f5f637f1d4acb37a738f71dd066"}, + {file = "regex-2024.9.11-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:4838e24ee015101d9f901988001038f7f0d90dc0c3b115541a1365fb439add62"}, + {file = "regex-2024.9.11-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:6edd623bae6a737f10ce853ea076f56f507fd7726bee96a41ee3d68d347e4d16"}, + {file = "regex-2024.9.11-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c69ada171c2d0e97a4b5aa78fbb835e0ffbb6b13fc5da968c09811346564f0d3"}, + {file = "regex-2024.9.11-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:02087ea0a03b4af1ed6ebab2c54d7118127fee8d71b26398e8e4b05b78963199"}, + {file = "regex-2024.9.11-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:69dee6a020693d12a3cf892aba4808fe168d2a4cef368eb9bf74f5398bfd4ee8"}, + {file = "regex-2024.9.11-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:297f54910247508e6e5cae669f2bc308985c60540a4edd1c77203ef19bfa63ca"}, + {file = "regex-2024.9.11-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ecea58b43a67b1b79805f1a0255730edaf5191ecef84dbc4cc85eb30bc8b63b9"}, + {file = "regex-2024.9.11-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:eab4bb380f15e189d1313195b062a6aa908f5bd687a0ceccd47c8211e9cf0d4a"}, + {file = "regex-2024.9.11-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:0cbff728659ce4bbf4c30b2a1be040faafaa9eca6ecde40aaff86f7889f4ab39"}, + {file = "regex-2024.9.11-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:54c4a097b8bc5bb0dfc83ae498061d53ad7b5762e00f4adaa23bee22b012e6ba"}, + {file = "regex-2024.9.11-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:73d6d2f64f4d894c96626a75578b0bf7d9e56dcda8c3d037a2118fdfe9b1c664"}, + {file = "regex-2024.9.11-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:e53b5fbab5d675aec9f0c501274c467c0f9a5d23696cfc94247e1fb56501ed89"}, + {file = "regex-2024.9.11-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:0ffbcf9221e04502fc35e54d1ce9567541979c3fdfb93d2c554f0ca583a19b35"}, + {file = "regex-2024.9.11-cp39-cp39-win32.whl", hash = "sha256:e4c22e1ac1f1ec1e09f72e6c44d8f2244173db7eb9629cc3a346a8d7ccc31142"}, + {file = "regex-2024.9.11-cp39-cp39-win_amd64.whl", hash = "sha256:faa3c142464efec496967359ca99696c896c591c56c53506bac1ad465f66e919"}, + {file = "regex-2024.9.11.tar.gz", hash = "sha256:6c188c307e8433bcb63dc1915022deb553b4203a70722fc542c363bf120a01fd"}, ] [[package]] @@ -3668,13 +3737,13 @@ files = [ [[package]] name = "rich" -version = "13.7.1" +version = "13.8.1" description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal" optional = false python-versions = ">=3.7.0" files = [ - {file = "rich-13.7.1-py3-none-any.whl", hash = "sha256:4edbae314f59eb482f54e9e30bf00d33350aaa94f4bfcd4e9e3110e64d0d7222"}, - {file = "rich-13.7.1.tar.gz", hash = "sha256:9be308cb1fe2f1f57d67ce99e95af38a1e2bc71ad9813b0e247cf7ffbcc3a432"}, + {file = "rich-13.8.1-py3-none-any.whl", hash = "sha256:1760a3c0848469b97b558fc61c85233e3dafb69c7a071b4d60c38099d3cd4c06"}, + {file = "rich-13.8.1.tar.gz", hash = "sha256:8260cda28e3db6bf04d2d1ef4dbc03ba80a824c88b0e7668a0f23126a424844a"}, ] [package.dependencies] @@ -3799,29 +3868,29 @@ files = [ [[package]] name = "ruff" -version = "0.6.7" +version = "0.6.8" description = "An extremely fast Python linter and code formatter, written in Rust." optional = false python-versions = ">=3.7" files = [ - {file = "ruff-0.6.7-py3-none-linux_armv6l.whl", hash = "sha256:08277b217534bfdcc2e1377f7f933e1c7957453e8a79764d004e44c40db923f2"}, - {file = "ruff-0.6.7-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:c6707a32e03b791f4448dc0dce24b636cbcdee4dd5607adc24e5ee73fd86c00a"}, - {file = "ruff-0.6.7-py3-none-macosx_11_0_arm64.whl", hash = "sha256:533d66b7774ef224e7cf91506a7dafcc9e8ec7c059263ec46629e54e7b1f90ab"}, - {file = "ruff-0.6.7-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:17a86aac6f915932d259f7bec79173e356165518859f94649d8c50b81ff087e9"}, - {file = "ruff-0.6.7-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b3f8822defd260ae2460ea3832b24d37d203c3577f48b055590a426a722d50ef"}, - {file = "ruff-0.6.7-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9ba4efe5c6dbbb58be58dd83feedb83b5e95c00091bf09987b4baf510fee5c99"}, - {file = "ruff-0.6.7-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:525201b77f94d2b54868f0cbe5edc018e64c22563da6c5c2e5c107a4e85c1c0d"}, - {file = "ruff-0.6.7-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8854450839f339e1049fdbe15d875384242b8e85d5c6947bb2faad33c651020b"}, - {file = "ruff-0.6.7-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2f0b62056246234d59cbf2ea66e84812dc9ec4540518e37553513392c171cb18"}, - {file = "ruff-0.6.7-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6b1462fa56c832dc0cea5b4041cfc9c97813505d11cce74ebc6d1aae068de36b"}, - {file = "ruff-0.6.7-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:02b083770e4cdb1495ed313f5694c62808e71764ec6ee5db84eedd82fd32d8f5"}, - {file = "ruff-0.6.7-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:0c05fd37013de36dfa883a3854fae57b3113aaa8abf5dea79202675991d48624"}, - {file = "ruff-0.6.7-py3-none-musllinux_1_2_i686.whl", hash = "sha256:f49c9caa28d9bbfac4a637ae10327b3db00f47d038f3fbb2195c4d682e925b14"}, - {file = "ruff-0.6.7-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:a0e1655868164e114ba43a908fd2d64a271a23660195017c17691fb6355d59bb"}, - {file = "ruff-0.6.7-py3-none-win32.whl", hash = "sha256:a939ca435b49f6966a7dd64b765c9df16f1faed0ca3b6f16acdf7731969deb35"}, - {file = "ruff-0.6.7-py3-none-win_amd64.whl", hash = "sha256:590445eec5653f36248584579c06252ad2e110a5d1f32db5420de35fb0e1c977"}, - {file = "ruff-0.6.7-py3-none-win_arm64.whl", hash = "sha256:b28f0d5e2f771c1fe3c7a45d3f53916fc74a480698c4b5731f0bea61e52137c8"}, - {file = "ruff-0.6.7.tar.gz", hash = "sha256:44e52129d82266fa59b587e2cd74def5637b730a69c4542525dfdecfaae38bd5"}, + {file = "ruff-0.6.8-py3-none-linux_armv6l.whl", hash = "sha256:77944bca110ff0a43b768f05a529fecd0706aac7bcce36d7f1eeb4cbfca5f0f2"}, + {file = "ruff-0.6.8-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:27b87e1801e786cd6ede4ada3faa5e254ce774de835e6723fd94551464c56b8c"}, + {file = "ruff-0.6.8-py3-none-macosx_11_0_arm64.whl", hash = "sha256:cd48f945da2a6334f1793d7f701725a76ba93bf3d73c36f6b21fb04d5338dcf5"}, + {file = "ruff-0.6.8-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:677e03c00f37c66cea033274295a983c7c546edea5043d0c798833adf4cf4c6f"}, + {file = "ruff-0.6.8-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9f1476236b3eacfacfc0f66aa9e6cd39f2a624cb73ea99189556015f27c0bdeb"}, + {file = "ruff-0.6.8-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6f5a2f17c7d32991169195d52a04c95b256378bbf0de8cb98478351eb70d526f"}, + {file = "ruff-0.6.8-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:5fd0d4b7b1457c49e435ee1e437900ced9b35cb8dc5178921dfb7d98d65a08d0"}, + {file = "ruff-0.6.8-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f8034b19b993e9601f2ddf2c517451e17a6ab5cdb1c13fdff50c1442a7171d87"}, + {file = "ruff-0.6.8-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6cfb227b932ba8ef6e56c9f875d987973cd5e35bc5d05f5abf045af78ad8e098"}, + {file = "ruff-0.6.8-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6ef0411eccfc3909269fed47c61ffebdcb84a04504bafa6b6df9b85c27e813b0"}, + {file = "ruff-0.6.8-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:007dee844738c3d2e6c24ab5bc7d43c99ba3e1943bd2d95d598582e9c1b27750"}, + {file = "ruff-0.6.8-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:ce60058d3cdd8490e5e5471ef086b3f1e90ab872b548814e35930e21d848c9ce"}, + {file = "ruff-0.6.8-py3-none-musllinux_1_2_i686.whl", hash = "sha256:1085c455d1b3fdb8021ad534379c60353b81ba079712bce7a900e834859182fa"}, + {file = "ruff-0.6.8-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:70edf6a93b19481affd287d696d9e311388d808671bc209fb8907b46a8c3af44"}, + {file = "ruff-0.6.8-py3-none-win32.whl", hash = "sha256:792213f7be25316f9b46b854df80a77e0da87ec66691e8f012f887b4a671ab5a"}, + {file = "ruff-0.6.8-py3-none-win_amd64.whl", hash = "sha256:ec0517dc0f37cad14a5319ba7bba6e7e339d03fbf967a6d69b0907d61be7a263"}, + {file = "ruff-0.6.8-py3-none-win_arm64.whl", hash = "sha256:8d3bb2e3fbb9875172119021a13eed38849e762499e3cfde9588e4b4d70968dc"}, + {file = "ruff-0.6.8.tar.gz", hash = "sha256:a5bf44b1aa0adaf6d9d20f86162b34f7c593bfedabc51239953e446aefc8ce18"}, ] [[package]] @@ -3937,19 +4006,23 @@ win32 = ["pywin32"] [[package]] name = "setuptools" -version = "73.0.1" +version = "75.1.0" description = "Easily download, build, install, upgrade, and uninstall Python packages" optional = false python-versions = ">=3.8" files = [ - {file = "setuptools-73.0.1-py3-none-any.whl", hash = "sha256:b208925fcb9f7af924ed2dc04708ea89791e24bde0d3020b27df0e116088b34e"}, - {file = "setuptools-73.0.1.tar.gz", hash = "sha256:d59a3e788ab7e012ab2c4baed1b376da6366883ee20d7a5fc426816e3d7b1193"}, + {file = "setuptools-75.1.0-py3-none-any.whl", hash = "sha256:35ab7fd3bcd95e6b7fd704e4a1539513edad446c097797f2985e0e4b960772f2"}, + {file = "setuptools-75.1.0.tar.gz", hash = "sha256:d59a21b17a275fb872a9c3dae73963160ae079f1049ed956880cd7c09b120538"}, ] [package.extras] -core = ["importlib-metadata (>=6)", "importlib-resources (>=5.10.2)", "jaraco.text (>=3.7)", "more-itertools (>=8.8)", "packaging (>=24)", "platformdirs (>=2.6.2)", "tomli (>=2.0.1)", "wheel (>=0.43.0)"] +check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1)", "ruff (>=0.5.2)"] +core = ["importlib-metadata (>=6)", "importlib-resources (>=5.10.2)", "jaraco.collections", "jaraco.functools", "jaraco.text (>=3.7)", "more-itertools", "more-itertools (>=8.8)", "packaging", "packaging (>=24)", "platformdirs (>=2.6.2)", "tomli (>=2.0.1)", "wheel (>=0.43.0)"] +cover = ["pytest-cov"] doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "pyproject-hooks (!=1.1)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier", "towncrier (<24.7)"] -test = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "importlib-metadata", "ini2toml[lite] (>=0.14)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "jaraco.test", "mypy (==1.11.*)", "packaging (>=23.2)", "pip (>=19.1)", "pyproject-hooks (!=1.1)", "pytest (>=6,!=8.1.*)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-home (>=0.5)", "pytest-mypy", "pytest-perf", "pytest-ruff (<0.4)", "pytest-ruff (>=0.2.1)", "pytest-ruff (>=0.3.2)", "pytest-subprocess", "pytest-timeout", "pytest-xdist (>=3)", "tomli", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel (>=0.44.0)"] +enabler = ["pytest-enabler (>=2.2)"] +test = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "ini2toml[lite] (>=0.14)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "jaraco.test", "packaging (>=23.2)", "pip (>=19.1)", "pyproject-hooks (!=1.1)", "pytest (>=6,!=8.1.*)", "pytest-home (>=0.5)", "pytest-perf", "pytest-subprocess", "pytest-timeout", "pytest-xdist (>=3)", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel (>=0.44.0)"] +type = ["importlib-metadata (>=7.0.2)", "jaraco.develop (>=7.21)", "mypy (==1.11.*)", "pytest-mypy"] [[package]] name = "six" @@ -4184,24 +4257,24 @@ types-setuptools = "*" [[package]] name = "types-python-dateutil" -version = "2.9.0.20240316" +version = "2.9.0.20240906" description = "Typing stubs for python-dateutil" optional = false python-versions = ">=3.8" files = [ - {file = "types-python-dateutil-2.9.0.20240316.tar.gz", hash = "sha256:5d2f2e240b86905e40944dd787db6da9263f0deabef1076ddaed797351ec0202"}, - {file = "types_python_dateutil-2.9.0.20240316-py3-none-any.whl", hash = "sha256:6b8cb66d960771ce5ff974e9dd45e38facb81718cc1e208b10b1baccbfdbee3b"}, + {file = "types-python-dateutil-2.9.0.20240906.tar.gz", hash = "sha256:9706c3b68284c25adffc47319ecc7947e5bb86b3773f843c73906fd598bc176e"}, + {file = "types_python_dateutil-2.9.0.20240906-py3-none-any.whl", hash = "sha256:27c8cc2d058ccb14946eebcaaa503088f4f6dbc4fb6093d3d456a49aef2753f6"}, ] [[package]] name = "types-pytz" -version = "2024.1.0.20240417" +version = "2024.2.0.20240913" description = "Typing stubs for pytz" optional = true python-versions = ">=3.8" files = [ - {file = "types-pytz-2024.1.0.20240417.tar.gz", hash = "sha256:6810c8a1f68f21fdf0f4f374a432487c77645a0ac0b31de4bf4690cf21ad3981"}, - {file = "types_pytz-2024.1.0.20240417-py3-none-any.whl", hash = "sha256:8335d443310e2db7b74e007414e74c4f53b67452c0cb0d228ca359ccfba59659"}, + {file = "types-pytz-2024.2.0.20240913.tar.gz", hash = "sha256:4433b5df4a6fc587bbed41716d86a5ba5d832b4378e506f40d34bc9c81df2c24"}, + {file = "types_pytz-2024.2.0.20240913-py3-none-any.whl", hash = "sha256:a1eebf57ebc6e127a99d2fa2ba0a88d2b173784ef9b3defcc2004ab6855a44df"}, ] [[package]] @@ -4231,13 +4304,13 @@ urllib3 = ">=2" [[package]] name = "types-setuptools" -version = "73.0.0.20240822" +version = "75.1.0.20240917" description = "Typing stubs for setuptools" optional = false python-versions = ">=3.8" files = [ - {file = "types-setuptools-73.0.0.20240822.tar.gz", hash = "sha256:3a060681098eb3fbc2fea0a86f7f6af6aa1ca71906039d88d891ea2cecdd4dbf"}, - {file = "types_setuptools-73.0.0.20240822-py3-none-any.whl", hash = "sha256:b9eba9b68546031317a0fa506d4973641d987d74f79e7dd8369ad4f7a93dea17"}, + {file = "types-setuptools-75.1.0.20240917.tar.gz", hash = "sha256:12f12a165e7ed383f31def705e5c0fa1c26215dd466b0af34bd042f7d5331f55"}, + {file = "types_setuptools-75.1.0.20240917-py3-none-any.whl", hash = "sha256:06f78307e68d1bbde6938072c57b81cf8a99bc84bd6dc7e4c5014730b097dc0c"}, ] [[package]] @@ -4264,13 +4337,13 @@ files = [ [[package]] name = "tzdata" -version = "2024.1" +version = "2024.2" description = "Provider of IANA time zone data" optional = true python-versions = ">=2" files = [ - {file = "tzdata-2024.1-py2.py3-none-any.whl", hash = "sha256:9068bc196136463f5245e51efda838afa15aaeca9903f49050dfa2679db4d252"}, - {file = "tzdata-2024.1.tar.gz", hash = "sha256:2674120f8d891909751c38abcdfd386ac0a5a1127954fbc332af6b5ceae07efd"}, + {file = "tzdata-2024.2-py2.py3-none-any.whl", hash = "sha256:a48093786cdcde33cad18c2555e8532f34422074448fbc874186f0abd79565cd"}, + {file = "tzdata-2024.2.tar.gz", hash = "sha256:7d85cc416e9382e69095b7bdf4afd9e3880418a2413feec7069d533d6b4e31cc"}, ] [[package]] @@ -4289,12 +4362,13 @@ dev = ["flake8", "flake8-annotations", "flake8-bandit", "flake8-bugbear", "flake [[package]] name = "urllib3" -version = "2.2.2" +version = "2.2.3" description = "HTTP library with thread-safe connection pooling, file post, and more." optional = false python-versions = ">=3.8" files = [ - {file = "urllib3-2.2.2-py3-none-any.whl", hash = "sha256:a448b2f64d686155468037e1ace9f2d2199776e17f0a46610480d311f73e3472"}, + {file = "urllib3-2.2.3-py3-none-any.whl", hash = "sha256:ca899ca043dcb1bafa3e262d73aa25c465bfb49e0bd9dd5d59f1d0acba2f8fac"}, + {file = "urllib3-2.2.3.tar.gz", hash = "sha256:e7d814a81dad81e6caf2ec9fdedb284ecc9c73076b62654547cc64ccdcae26e9"}, ] [package.extras] @@ -4319,13 +4393,13 @@ test = ["coverage", "flake8 (>=3.7)", "mypy", "pretend", "pytest"] [[package]] name = "virtualenv" -version = "20.26.3" +version = "20.26.5" description = "Virtual Python Environment builder" optional = false python-versions = ">=3.7" files = [ - {file = "virtualenv-20.26.3-py3-none-any.whl", hash = "sha256:8cc4a31139e796e9a7de2cd5cf2489de1217193116a8fd42328f1bd65f434589"}, - {file = "virtualenv-20.26.3.tar.gz", hash = "sha256:4c43a2a236279d9ea36a0d76f98d84bd6ca94ac4e0f4a3b9d46d05e10fea542a"}, + {file = "virtualenv-20.26.5-py3-none-any.whl", hash = "sha256:4f3ac17b81fba3ce3bd6f4ead2749a72da5929c01774948e243db9ba41df4ff6"}, + {file = "virtualenv-20.26.5.tar.gz", hash = "sha256:ce489cac131aa58f4b25e321d6d186171f78e6cb13fafbf32a840cee67733ff4"}, ] [package.dependencies] @@ -4464,18 +4538,22 @@ files = [ [[package]] name = "zipp" -version = "3.20.0" +version = "3.20.2" description = "Backport of pathlib-compatible object wrapper for zip files" optional = false python-versions = ">=3.8" files = [ - {file = "zipp-3.20.0-py3-none-any.whl", hash = "sha256:58da6168be89f0be59beb194da1250516fdaa062ccebd30127ac65d30045e10d"}, - {file = "zipp-3.20.0.tar.gz", hash = "sha256:0145e43d89664cfe1a2e533adc75adafed82fe2da404b4bbb6b026c0157bdb31"}, + {file = "zipp-3.20.2-py3-none-any.whl", hash = "sha256:a817ac80d6cf4b23bf7f2828b7cabf326f15a001bea8b1f9b49631780ba28350"}, + {file = "zipp-3.20.2.tar.gz", hash = "sha256:bc9eb26f4506fda01b81bcde0ca78103b6e62f991b381fec825435c836edbc29"}, ] [package.extras] +check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1)"] +cover = ["pytest-cov"] doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] -test = ["big-O", "importlib-resources", "jaraco.functools", "jaraco.itertools", "jaraco.test", "more-itertools", "pytest (>=6,!=8.1.*)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-ignore-flaky", "pytest-mypy", "pytest-ruff (>=0.2.1)"] +enabler = ["pytest-enabler (>=2.2)"] +test = ["big-O", "importlib-resources", "jaraco.functools", "jaraco.itertools", "jaraco.test", "more-itertools", "pytest (>=6,!=8.1.*)", "pytest-ignore-flaky"] +type = ["pytest-mypy"] [extras] assets = ["requests", "tqdm"] @@ -4484,4 +4562,4 @@ metrics = ["pandas", "pandas-stubs"] [metadata] lock-version = "2.0" python-versions = "^3.8" -content-hash = "da42551ddf31248900f614b81bfeaa1e556fda742e5be86d74fa1d151b99fd57" +content-hash = "6619a49f1450ccc15a01215d156afbcf248619374ad2ba0576f48434d9b8720f" diff --git a/pyproject.toml b/pyproject.toml index 42e1e7fe..6be83783 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -86,8 +86,7 @@ docutils = [ [tool.poetry.group.docs.dependencies] mkdocs-material = { extras = ["imaging"], version = "^9.5.5" } -mkdocstrings = { extras = ["python"], version = ">=0.25.2,<0.27.0" } -# mkdocstrings-python shouldn't be required, but latest mkdocstrings[python] (0.26.1) includes version with warnings +mkdocstrings = ">=0.25.2,<0.27.0" mkdocstrings-python = "^1.10.9" mike = "^2.0.0" # For Documentation Development use Python 3.10 or above From 39d0e6559260024e98ec226672f51fb2686ac5b3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 27 Sep 2024 00:59:42 +0000 Subject: [PATCH 48/63] :arrow_up: Bump ruff from 0.6.7 to 0.6.8 Bumps [ruff](https://github.com/astral-sh/ruff) from 0.6.7 to 0.6.8. - [Release notes](https://github.com/astral-sh/ruff/releases) - [Changelog](https://github.com/astral-sh/ruff/blob/main/CHANGELOG.md) - [Commits](https://github.com/astral-sh/ruff/compare/0.6.7...0.6.8) --- updated-dependencies: - dependency-name: ruff dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- poetry.lock | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/poetry.lock b/poetry.lock index 8061b698..8c1f0121 100644 --- a/poetry.lock +++ b/poetry.lock @@ -3798,29 +3798,29 @@ files = [ [[package]] name = "ruff" -version = "0.6.7" +version = "0.6.8" description = "An extremely fast Python linter and code formatter, written in Rust." optional = false python-versions = ">=3.7" files = [ - {file = "ruff-0.6.7-py3-none-linux_armv6l.whl", hash = "sha256:08277b217534bfdcc2e1377f7f933e1c7957453e8a79764d004e44c40db923f2"}, - {file = "ruff-0.6.7-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:c6707a32e03b791f4448dc0dce24b636cbcdee4dd5607adc24e5ee73fd86c00a"}, - {file = "ruff-0.6.7-py3-none-macosx_11_0_arm64.whl", hash = "sha256:533d66b7774ef224e7cf91506a7dafcc9e8ec7c059263ec46629e54e7b1f90ab"}, - {file = "ruff-0.6.7-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:17a86aac6f915932d259f7bec79173e356165518859f94649d8c50b81ff087e9"}, - {file = "ruff-0.6.7-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b3f8822defd260ae2460ea3832b24d37d203c3577f48b055590a426a722d50ef"}, - {file = "ruff-0.6.7-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9ba4efe5c6dbbb58be58dd83feedb83b5e95c00091bf09987b4baf510fee5c99"}, - {file = "ruff-0.6.7-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:525201b77f94d2b54868f0cbe5edc018e64c22563da6c5c2e5c107a4e85c1c0d"}, - {file = "ruff-0.6.7-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8854450839f339e1049fdbe15d875384242b8e85d5c6947bb2faad33c651020b"}, - {file = "ruff-0.6.7-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2f0b62056246234d59cbf2ea66e84812dc9ec4540518e37553513392c171cb18"}, - {file = "ruff-0.6.7-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6b1462fa56c832dc0cea5b4041cfc9c97813505d11cce74ebc6d1aae068de36b"}, - {file = "ruff-0.6.7-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:02b083770e4cdb1495ed313f5694c62808e71764ec6ee5db84eedd82fd32d8f5"}, - {file = "ruff-0.6.7-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:0c05fd37013de36dfa883a3854fae57b3113aaa8abf5dea79202675991d48624"}, - {file = "ruff-0.6.7-py3-none-musllinux_1_2_i686.whl", hash = "sha256:f49c9caa28d9bbfac4a637ae10327b3db00f47d038f3fbb2195c4d682e925b14"}, - {file = "ruff-0.6.7-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:a0e1655868164e114ba43a908fd2d64a271a23660195017c17691fb6355d59bb"}, - {file = "ruff-0.6.7-py3-none-win32.whl", hash = "sha256:a939ca435b49f6966a7dd64b765c9df16f1faed0ca3b6f16acdf7731969deb35"}, - {file = "ruff-0.6.7-py3-none-win_amd64.whl", hash = "sha256:590445eec5653f36248584579c06252ad2e110a5d1f32db5420de35fb0e1c977"}, - {file = "ruff-0.6.7-py3-none-win_arm64.whl", hash = "sha256:b28f0d5e2f771c1fe3c7a45d3f53916fc74a480698c4b5731f0bea61e52137c8"}, - {file = "ruff-0.6.7.tar.gz", hash = "sha256:44e52129d82266fa59b587e2cd74def5637b730a69c4542525dfdecfaae38bd5"}, + {file = "ruff-0.6.8-py3-none-linux_armv6l.whl", hash = "sha256:77944bca110ff0a43b768f05a529fecd0706aac7bcce36d7f1eeb4cbfca5f0f2"}, + {file = "ruff-0.6.8-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:27b87e1801e786cd6ede4ada3faa5e254ce774de835e6723fd94551464c56b8c"}, + {file = "ruff-0.6.8-py3-none-macosx_11_0_arm64.whl", hash = "sha256:cd48f945da2a6334f1793d7f701725a76ba93bf3d73c36f6b21fb04d5338dcf5"}, + {file = "ruff-0.6.8-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:677e03c00f37c66cea033274295a983c7c546edea5043d0c798833adf4cf4c6f"}, + {file = "ruff-0.6.8-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9f1476236b3eacfacfc0f66aa9e6cd39f2a624cb73ea99189556015f27c0bdeb"}, + {file = "ruff-0.6.8-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6f5a2f17c7d32991169195d52a04c95b256378bbf0de8cb98478351eb70d526f"}, + {file = "ruff-0.6.8-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:5fd0d4b7b1457c49e435ee1e437900ced9b35cb8dc5178921dfb7d98d65a08d0"}, + {file = "ruff-0.6.8-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f8034b19b993e9601f2ddf2c517451e17a6ab5cdb1c13fdff50c1442a7171d87"}, + {file = "ruff-0.6.8-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6cfb227b932ba8ef6e56c9f875d987973cd5e35bc5d05f5abf045af78ad8e098"}, + {file = "ruff-0.6.8-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6ef0411eccfc3909269fed47c61ffebdcb84a04504bafa6b6df9b85c27e813b0"}, + {file = "ruff-0.6.8-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:007dee844738c3d2e6c24ab5bc7d43c99ba3e1943bd2d95d598582e9c1b27750"}, + {file = "ruff-0.6.8-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:ce60058d3cdd8490e5e5471ef086b3f1e90ab872b548814e35930e21d848c9ce"}, + {file = "ruff-0.6.8-py3-none-musllinux_1_2_i686.whl", hash = "sha256:1085c455d1b3fdb8021ad534379c60353b81ba079712bce7a900e834859182fa"}, + {file = "ruff-0.6.8-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:70edf6a93b19481affd287d696d9e311388d808671bc209fb8907b46a8c3af44"}, + {file = "ruff-0.6.8-py3-none-win32.whl", hash = "sha256:792213f7be25316f9b46b854df80a77e0da87ec66691e8f012f887b4a671ab5a"}, + {file = "ruff-0.6.8-py3-none-win_amd64.whl", hash = "sha256:ec0517dc0f37cad14a5319ba7bba6e7e339d03fbf967a6d69b0907d61be7a263"}, + {file = "ruff-0.6.8-py3-none-win_arm64.whl", hash = "sha256:8d3bb2e3fbb9875172119021a13eed38849e762499e3cfde9588e4b4d70968dc"}, + {file = "ruff-0.6.8.tar.gz", hash = "sha256:a5bf44b1aa0adaf6d9d20f86162b34f7c593bfedabc51239953e446aefc8ce18"}, ] [[package]] From 20c5f8da3df23be19ca33d34645e1f7270956ad2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 27 Sep 2024 01:01:49 +0000 Subject: [PATCH 49/63] :arrow_up: Bump mkdocs-material from 9.5.37 to 9.5.38 Bumps [mkdocs-material](https://github.com/squidfunk/mkdocs-material) from 9.5.37 to 9.5.38. - [Release notes](https://github.com/squidfunk/mkdocs-material/releases) - [Changelog](https://github.com/squidfunk/mkdocs-material/blob/master/CHANGELOG) - [Commits](https://github.com/squidfunk/mkdocs-material/compare/9.5.37...9.5.38) --- updated-dependencies: - dependency-name: mkdocs-material dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- poetry.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/poetry.lock b/poetry.lock index 8061b698..deafa7b6 100644 --- a/poetry.lock +++ b/poetry.lock @@ -2220,13 +2220,13 @@ pygments = ">2.12.0" [[package]] name = "mkdocs-material" -version = "9.5.37" +version = "9.5.38" description = "Documentation that simply works" optional = false python-versions = ">=3.8" files = [ - {file = "mkdocs_material-9.5.37-py3-none-any.whl", hash = "sha256:6e8a986abad77be5edec3dd77cf1ddf2480963fb297a8e971f87a82fd464b070"}, - {file = "mkdocs_material-9.5.37.tar.gz", hash = "sha256:2c31607431ec234db124031255b0a9d4f3e1c3ecc2c47ad97ecfff0460471941"}, + {file = "mkdocs_material-9.5.38-py3-none-any.whl", hash = "sha256:d4779051d52ba9f1e7e344b34de95449c7c366c212b388e4a2db9a3db043c228"}, + {file = "mkdocs_material-9.5.38.tar.gz", hash = "sha256:1843c5171ad6b489550aeaf7358e5b7128cc03ddcf0fb4d91d19aa1e691a63b8"}, ] [package.dependencies] From dec29200a9bc0de9eabba4d6ecbd7e8592349c9e Mon Sep 17 00:00:00 2001 From: LinasKo Date: Fri, 27 Sep 2024 11:29:16 +0300 Subject: [PATCH 50/63] Add repo of the day badge to readme --- README.md | 29 +++++++++++++++++------------ 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 9509a514..1e2fa6f3 100644 --- a/README.md +++ b/README.md @@ -23,6 +23,11 @@ [![gradio](https://img.shields.io/badge/%F0%9F%A4%97%20Hugging%20Face-Spaces-blue)](https://huggingface.co/spaces/Roboflow/Annotators) [![discord](https://img.shields.io/discord/1159501506232451173)](https://discord.gg/GbfgXGJ8Bk) [![built-with-material-for-mkdocs](https://img.shields.io/badge/Material_for_MkDocs-526CFE?logo=MaterialForMkDocs&logoColor=white)](https://squidfunk.github.io/mkdocs-material/) + +
+ roboflow%2Fsupervision | Trendshift +
+ ## 👋 hello @@ -67,21 +72,21 @@ len(detections) - inference - Running with [Inference](https://github.com/roboflow/inference) requires a [Roboflow API KEY](https://docs.roboflow.com/api-reference/authentication#retrieve-an-api-key). + Running with [Inference](https://github.com/roboflow/inference) requires a [Roboflow API KEY](https://docs.roboflow.com/api-reference/authentication#retrieve-an-api-key). - ```python - import cv2 - import supervision as sv - from inference import get_model + ```python + import cv2 + import supervision as sv + from inference import get_model - image = cv2.imread(...) - model = get_model(model_id="yolov8s-640", api_key=) - result = model.infer(image)[0] - detections = sv.Detections.from_inference(result) + image = cv2.imread(...) + model = get_model(model_id="yolov8s-640", api_key=) + result = model.infer(image)[0] + detections = sv.Detections.from_inference(result) - len(detections) - # 5 - ``` + len(detections) + # 5 + ``` From 78f17cba2eac78f4882fbcb7d5d651c5a5373994 Mon Sep 17 00:00:00 2001 From: Onuralp SEZER Date: Fri, 27 Sep 2024 14:17:34 +0300 Subject: [PATCH 51/63] =?UTF-8?q?chore:=20=F0=9F=A7=B9=20clean=20up=20docu?= =?UTF-8?q?mentation=20and=20improve=20formatting?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Onuralp SEZER --- .pre-commit-config.yaml | 15 ++ CODE_OF_CONDUCT.md | 41 +++-- CONTRIBUTING.md | 59 ++++--- README.md | 121 ++++++------- docs/assets.md | 1 - docs/changelog.md | 50 +----- docs/contributing.md | 2 +- docs/datasets/core.md | 1 - docs/deprecated.md | 3 + docs/how_to/detect_and_annotate.md | 15 -- docs/how_to/detect_small_objects.md | 10 -- docs/how_to/filter_detections.md | 16 -- docs/how_to/save_detections.md | 32 ++-- docs/how_to/track_objects.md | 8 - docs/index.md | 42 ++--- examples/count_people_in_zone/README.md | 137 ++++++++------- examples/heatmap_and_track/README.md | 39 ++-- examples/speed_estimation/README.md | 113 ++++++------ examples/time_in_zone/README.md | 225 ++++++++++++------------ examples/tracking/README.md | 125 ++++++------- examples/traffic_analysis/README.md | 143 ++++++++------- pyproject.toml | 6 + release_process.md | 24 +-- supervision/detection/line_zone.py | 2 +- test/dataset/formats/test_coco.py | 2 +- 25 files changed, 581 insertions(+), 651 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index ff62fd44..7fe9c6d0 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -38,3 +38,18 @@ repos: args: [--fix, --exit-non-zero-on-fix] - id: ruff-format types_or: [ python, pyi, jupyter ] + + # - repo: https://github.com/executablebooks/mdformat + # rev: 0.7.17 + # hooks: + # - id: mdformat + # additional_dependencies: + # - "mdformat-mkdocs[recommended]>=2.1.0" + # args: ["--number"] + + - repo: https://github.com/codespell-project/codespell + rev: v2.2.6 + hooks: + - id: codespell + additional_dependencies: + - tomli diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index 18c32736..aa1e72b8 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -1,4 +1,3 @@ - # Contributor Covenant Code of Conduct ## Our Pledge @@ -18,24 +17,24 @@ diverse, inclusive, and healthy community. Examples of behavior that contributes to a positive environment for our community include: -* Demonstrating empathy and kindness toward other people -* Being respectful of differing opinions, viewpoints, and experiences -* Giving and gracefully accepting constructive feedback -* Accepting responsibility and apologizing to those affected by our mistakes, - and learning from the experience -* Focusing on what is best not just for us as individuals, but for the overall - community +- Demonstrating empathy and kindness toward other people +- Being respectful of differing opinions, viewpoints, and experiences +- Giving and gracefully accepting constructive feedback +- Accepting responsibility and apologizing to those affected by our mistakes, + and learning from the experience +- Focusing on what is best not just for us as individuals, but for the overall + community Examples of unacceptable behavior include: -* The use of sexualized language or imagery, and sexual attention or advances of - any kind -* Trolling, insulting or derogatory comments, and personal or political attacks -* Public or private harassment -* Publishing others' private information, such as a physical or email address, - without their explicit permission -* Other conduct which could reasonably be considered inappropriate in a - professional setting +- The use of sexualized language or imagery, and sexual attention or advances of + any kind +- Trolling, insulting or derogatory comments, and personal or political attacks +- Public or private harassment +- Publishing others' private information, such as a physical or email address, + without their explicit permission +- Other conduct which could reasonably be considered inappropriate in a + professional setting ## Enforcement Responsibilities @@ -121,14 +120,14 @@ version 2.1, available at [https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1]. Community Impact Guidelines were inspired by -[Mozilla's code of conduct enforcement ladder][Mozilla CoC]. +[Mozilla's code of conduct enforcement ladder][mozilla coc]. For answers to common questions about this code of conduct, see the FAQ at -[https://www.contributor-covenant.org/faq][FAQ]. Translations are available at +[https://www.contributor-covenant.org/faq][faq]. Translations are available at [https://www.contributor-covenant.org/translations][translations]. +[faq]: https://www.contributor-covenant.org/faq [homepage]: https://www.contributor-covenant.org -[v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html -[Mozilla CoC]: https://github.com/mozilla/diversity -[FAQ]: https://www.contributor-covenant.org/faq +[mozilla coc]: https://github.com/mozilla/diversity [translations]: https://www.contributor-covenant.org/translations +[v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index dd6797c1..8a06e342 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -11,13 +11,13 @@ Please read and adhere to our [Code of Conduct](https://supervision.roboflow.com ## Table of Contents - [Contribution Guidelines](#contribution-guidelines) - - [Contributing Features](#contributing-features) + - [Contributing Features](#contributing-features) - [How to Contribute Changes](#how-to-contribute-changes) - [Installation for Contributors](#installation-for-contributors) - [Code Style and Quality](#code-style-and-quality) - - [Pre-commit tool](#pre-commit-tool) - - [Docstrings](#docstrings) - - [Type checking](#type-checking) + - [Pre-commit tool](#pre-commit-tool) + - [Docstrings](#docstrings) + - [Type checking](#type-checking) - [Documentation](#documentation) - [Cookbooks](#cookbooks) - [Tests](#tests) @@ -83,7 +83,7 @@ git push -u origin Use conventional commit messages to clearly describe your changes. The format is: -[optional scope]: +\[optional scope\]: Common types include: @@ -130,45 +130,46 @@ Before starting your work on the project, set up your development environment: 1. Clone your fork of the project: - ```bash - git clone https://github.com/YOUR_USERNAME/supervision.git - cd supervision - ``` + ```bash + git clone https://github.com/YOUR_USERNAME/supervision.git + cd supervision + ``` - Replace `YOUR_USERNAME` with your GitHub username. + Replace `YOUR_USERNAME` with your GitHub username. 2. Create and activate a virtual environment: - ```bash - python3 -m venv .venv - source .venv/bin/activate - ``` + ```bash + python3 -m venv .venv + source .venv/bin/activate + ``` 3. Install Poetry: - Using pip: + Using pip: - ```bash - pip install -U pip setuptools - pip install poetry - ``` + ```bash + pip install -U pip setuptools + pip install poetry + ``` - Or using pipx (recommended for global installation): + Or using pipx (recommended for global installation): - ```bash - pipx install poetry - ``` + ```bash + pipx install poetry + ``` 4. Install project dependencies: - ```bash - poetry install - ``` + ```bash + poetry install + ``` 5. Run pytest to verify the setup: - ```bash - poetry run pytest - ``` + + ```bash + poetry run pytest + ``` ## 🎨 Code Style and Quality diff --git a/README.md b/README.md index 9509a514..b47f9386 100644 --- a/README.md +++ b/README.md @@ -8,11 +8,11 @@

-
+
[notebooks](https://github.com/roboflow/notebooks) | [inference](https://github.com/roboflow/inference) | [autodistill](https://github.com/autodistill/autodistill) | [maestro](https://github.com/roboflow/multimodal-maestro) -
+
[![version](https://badge.fury.io/py/supervision.svg)](https://badge.fury.io/py/supervision) [![downloads](https://img.shields.io/pypi/dm/supervision)](https://pypistats.org/packages/supervision) @@ -23,6 +23,7 @@ [![gradio](https://img.shields.io/badge/%F0%9F%A4%97%20Hugging%20Face-Spaces-blue)](https://huggingface.co/spaces/Roboflow/Annotators) [![discord](https://img.shields.io/discord/1159501506232451173)](https://discord.gg/GbfgXGJ8Bk) [![built-with-material-for-mkdocs](https://img.shields.io/badge/Material_for_MkDocs-526CFE?logo=MaterialForMkDocs&logoColor=white)](https://squidfunk.github.io/mkdocs-material/) + ## 👋 hello @@ -54,7 +55,7 @@ import supervision as sv from ultralytics import YOLO image = cv2.imread(...) -model = YOLO('yolov8s.pt') +model = YOLO("yolov8s.pt") result = model(image)[0] detections = sv.Detections.from_ultralytics(result) @@ -97,10 +98,7 @@ image = cv2.imread(...) detections = sv.Detections(...) box_annotator = sv.BoxAnnotator() -annotated_frame = box_annotator.annotate( - scene=image.copy(), - detections=detections -) +annotated_frame = box_annotator.annotate(scene=image.copy(), detections=detections) ``` https://github.com/roboflow/supervision/assets/26109316/691e219c-0565-4403-9218-ab5644f39bce @@ -133,88 +131,69 @@ for path, image, annotation in ds: - load - ```python - dataset = sv.DetectionDataset.from_yolo( - images_directory_path=..., - annotations_directory_path=..., - data_yaml_path=... - ) + ```python + dataset = sv.DetectionDataset.from_yolo( + images_directory_path=..., annotations_directory_path=..., data_yaml_path=... + ) - dataset = sv.DetectionDataset.from_pascal_voc( - images_directory_path=..., - annotations_directory_path=... - ) + dataset = sv.DetectionDataset.from_pascal_voc( + images_directory_path=..., annotations_directory_path=... + ) - dataset = sv.DetectionDataset.from_coco( - images_directory_path=..., - annotations_path=... - ) - ``` + dataset = sv.DetectionDataset.from_coco(images_directory_path=..., annotations_path=...) + ``` - split - ```python - train_dataset, test_dataset = dataset.split(split_ratio=0.7) - test_dataset, valid_dataset = test_dataset.split(split_ratio=0.5) + ```python + train_dataset, test_dataset = dataset.split(split_ratio=0.7) + test_dataset, valid_dataset = test_dataset.split(split_ratio=0.5) - len(train_dataset), len(test_dataset), len(valid_dataset) - # (700, 150, 150) - ``` + len(train_dataset), len(test_dataset), len(valid_dataset) + # (700, 150, 150) + ``` - merge - ```python - ds_1 = sv.DetectionDataset(...) - len(ds_1) - # 100 - ds_1.classes - # ['dog', 'person'] + ```python + ds_1 = sv.DetectionDataset(...) + len(ds_1) + # 100 + ds_1.classes + # ['dog', 'person'] - ds_2 = sv.DetectionDataset(...) - len(ds_2) - # 200 - ds_2.classes - # ['cat'] + ds_2 = sv.DetectionDataset(...) + len(ds_2) + # 200 + ds_2.classes + # ['cat'] - ds_merged = sv.DetectionDataset.merge([ds_1, ds_2]) - len(ds_merged) - # 300 - ds_merged.classes - # ['cat', 'dog', 'person'] - ``` + ds_merged = sv.DetectionDataset.merge([ds_1, ds_2]) + len(ds_merged) + # 300 + ds_merged.classes + # ['cat', 'dog', 'person'] + ``` - save - ```python - dataset.as_yolo( - images_directory_path=..., - annotations_directory_path=..., - data_yaml_path=... - ) + ```python + dataset.as_yolo( + images_directory_path=..., annotations_directory_path=..., data_yaml_path=... + ) - dataset.as_pascal_voc( - images_directory_path=..., - annotations_directory_path=... - ) + dataset.as_pascal_voc(images_directory_path=..., annotations_directory_path=...) - dataset.as_coco( - images_directory_path=..., - annotations_path=... - ) - ``` + dataset.as_coco(images_directory_path=..., annotations_path=...) + ``` - convert - ```python - sv.DetectionDataset.from_yolo( - images_directory_path=..., - annotations_directory_path=..., - data_yaml_path=... - ).as_pascal_voc( - images_directory_path=..., - annotations_directory_path=... - ) - ``` + ```python + sv.DetectionDataset.from_yolo( + images_directory_path=..., annotations_directory_path=..., data_yaml_path=... + ).as_pascal_voc(images_directory_path=..., annotations_directory_path=...) + ``` @@ -266,7 +245,7 @@ We love your input! Please see our [contributing guide](https://github.com/robof
-
+
Aug 28, 2024 - Added [#930](https://github.com/roboflow/supervision/pull/930): `IconAnnotator`, a [new annotator](https://supervision.roboflow.com/0.23.0/detection/annotators/#supervision.annotators.core.IconAnnotator) that allows drawing icons on each detection. Useful if you want to draw a specific icon for each class. @@ -80,10 +82,7 @@ detections = sv.Detections.from_transformers( ```python import supervision as sv -from segment_anything import ( - sam_model_registry, - SamAutomaticMaskGenerator -) +from segment_anything import sam_model_registry, SamAutomaticMaskGenerator sam_model_reg = sam_model_registry[MODEL_TYPE] sam = sam_model_reg(checkpoint=CHECKPOINT_PATH).to(device=DEVICE) @@ -116,19 +115,15 @@ for frame in sv.get_video_frames_generator( - Fix [#1424](https://github.com/roboflow/supervision/pull/1424): `plot_image` function now clearly indicates that the size is in inches. !!! failure "Removed" - The `track_buffer`, `track_thresh`, and `match_thresh` parameters in [`ByteTrack`](trackers.md/#supervision.tracker.byte_tracker.core.ByteTrack) are deprecated and were removed as of `supervision-0.23.0`. Use `lost_track_buffer,` `track_activation_threshold`, and `minimum_matching_threshold` instead. !!! failure "Removed" - - The `triggering_position ` parameter in [`sv.PolygonZone`](detection/tools/polygon_zone.md/#supervision.detection.tools.polygon_zone.PolygonZone) was removed as of `supervision-0.23.0`. Use `triggering_anchors ` instead. + The `triggering_position` parameter in [`sv.PolygonZone`](detection/tools/polygon_zone.md/#supervision.detection.tools.polygon_zone.PolygonZone) was removed as of `supervision-0.23.0`. Use `triggering_anchors` instead. !!! failure "Deprecated" - `overlap_filter_strategy` in `InferenceSlicer.__init__` is deprecated and will be removed in `supervision-0.27.0`. Use `overlap_strategy` instead. !!! failure "Deprecated" - `overlap_ratio_wh` in `InferenceSlicer.__init__` is deprecated and will be removed in `supervision-0.27.0`. Use `overlap_wh` instead. ### 0.22.0 Jul 12, 2024 @@ -136,11 +131,9 @@ for frame in sv.get_video_frames_generator( - Added [#1326](https://github.com/roboflow/supervision/pull/1326): [`sv.DetectionsDataset`](https://supervision.roboflow.com/0.22.0/datasets/core/#supervision.dataset.core.DetectionDataset) and [`sv.ClassificationDataset`](https://supervision.roboflow.com/0.22.0/datasets/core/#supervision.dataset.core.ClassificationDataset) allowing to load the images into memory only when necessary (lazy loading). !!! failure "Deprecated" - Constructing `DetectionDataset` with parameter `images` as `Dict[str, np.ndarray]` is deprecated and will be removed in `supervision-0.26.0`. Please pass a list of paths `List[str]` instead. !!! failure "Deprecated" - The `DetectionDataset.images` property is deprecated and will be removed in `supervision-0.26.0`. Please loop over images with `for path, image, annotation in dataset:`, as that does not require loading all images into memory. ```python @@ -197,7 +190,7 @@ annotated_frame = mask_annotator.annotate(scene=image.copy(), detections=detecti ``` - Added [#1277](https://github.com/roboflow/supervision/pull/1277): if you provide a font that supports symbols of a language, [`sv.RichLabelAnnotator`](https://supervision.roboflow.com/0.22.0/detection/annotators/#supervision.annotators.core.LabelAnnotator.annotate) will draw them on your images. - - Various other annotators have been revised to ensure proper in-place functionality when used with `numpy` arrays. Additionally, we fixed a bug where `sv.ColorAnnotator` was filling boxes with solid color when used in-place. + - Various other annotators have been revised to ensure proper in-place functionality when used with `numpy` arrays. Additionally, we fixed a bug where `sv.ColorAnnotator` was filling boxes with solid color when used in-place. ```python import cv2 @@ -223,7 +216,7 @@ train_ds = sv.DetectionDataset.from_yolo( images_directory_path="/content/dataset/train/images", annotations_directory_path="/content/dataset/train/labels", data_yaml_path="/content/dataset/data.yaml", - is_obb=True + is_obb=True, ) _, image, detections in train_ds[0] @@ -235,11 +228,9 @@ annotated_image = obb_annotator.annotate(scene=image.copy(), detections=detectio - Fixed [#1312](https://github.com/roboflow/supervision/pull/1312): Fixed [`CropAnnotator`](https://supervision.roboflow.com/0.22.0/detection/annotators/#supervision.annotators.core.TraceAnnotator.annotate). !!! failure "Removed" - `BoxAnnotator` was removed, however `BoundingBoxAnnotator` has been renamed to `BoxAnnotator`. Use a combination of [`BoxAnnotator`](https://supervision.roboflow.com/0.22.0/detection/annotators/#supervision.annotators.core.BoxAnnotator) and [`LabelAnnotator`](https://supervision.roboflow.com/0.22.0/detection/annotators/#supervision.annotators.core.LabelAnnotator) to simulate old `BoundingBox` behavior. !!! failure "Deprecated" - The name `BoundingBoxAnnotator` has been deprecated and will be removed in `supervision-0.26.0`. It has been renamed to [`BoxAnnotator`](https://supervision.roboflow.com/0.22.0/detection/annotators/#supervision.annotators.core.BoxAnnotator). - Added [#975](https://github.com/roboflow/supervision/pull/975) 📝 New Cookbooks: serialize detections into [json](https://github.com/roboflow/supervision/blob/de896189b83a1f9434c0a37dd9192ee00d2a1283/docs/notebooks/serialise-detections-to-json.ipynb) and [csv](https://github.com/roboflow/supervision/blob/de896189b83a1f9434c0a37dd9192ee00d2a1283/docs/notebooks/serialise-detections-to-csv.ipynb). @@ -249,35 +240,27 @@ annotated_image = obb_annotator.annotate(scene=image.copy(), detections=detectio - Added [#1340](https://github.com/roboflow/supervision/pull/1340): Two new methods for converting between bounding box formats - [`xywh_to_xyxy`](https://supervision.roboflow.com/0.22.0/detection/utils/#supervision.detection.utils.xywh_to_xyxy) and [`xcycwh_to_xyxy`](https://supervision.roboflow.com/0.22.0/detection/utils/#supervision.detection.utils.xcycwh_to_xyxy) !!! failure "Removed" - `from_roboflow` method has been removed due to deprecation. Use [from_inference](https://supervision.roboflow.com/0.22.0/detection/core/#supervision.detection.core.Detections.from_inference) instead. !!! failure "Removed" - `Color.white()` has been removed due to deprecation. Use `color.WHITE` instead. !!! failure "Removed" - `Color.black()` has been removed due to deprecation. Use `color.BLACK` instead. !!! failure "Removed" - `Color.red()` has been removed due to deprecation. Use `color.RED` instead. !!! failure "Removed" - `Color.green()` has been removed due to deprecation. Use `color.GREEN` instead. !!! failure "Removed" - `Color.blue()` has been removed due to deprecation. Use `color.BLUE` instead. !!! failure "Removed" - `ColorPalette.default()` has been removed due to deprecation. Use [ColorPalette.DEFAULT](https://supervision.roboflow.com/0.22.0/utils/draw/#supervision.draw.color.ColorPalette.DEFAULT) instead. !!! failure "Removed" - `FPSMonitor.__call__` has been removed due to deprecation. Use the attribute [FPSMonitor.fps](https://supervision.roboflow.com/0.22.0/utils/video/#supervision.utils.video.FPSMonitor.fps) instead. ### 0.21.0 Jun 5, 2024 @@ -294,7 +277,7 @@ detections = sv.Detections.from_lmm( sv.LMM.PALIGEMMA, paligemma_result, resolution_wh=(1000, 1000), - classes=['cat', 'dog'] + classes=["cat", "dog"], ) detections.xyxy # array([[250., 250., 750., 750.]]) @@ -311,14 +294,8 @@ import supervision as sv image = ... key_points = sv.KeyPoints(...) -edge_annotator = sv.EdgeAnnotator( - color=sv.Color.GREEN, - thickness=5 -) -annotated_frame = edge_annotator.annotate( - scene=image.copy(), - key_points=key_points -) +edge_annotator = sv.EdgeAnnotator(color=sv.Color.GREEN, thickness=5) +annotated_frame = edge_annotator.annotate(scene=image.copy(), key_points=key_points) ``` - Added [#1147](https://github.com/roboflow/supervision/pull/1147): [`sv.KeyPoints.from_inference`](https://supervision.roboflow.com/0.21.0/keypoint/core/#supervision.keypoint.core.KeyPoints.from_inference) allowing to create [`sv.KeyPoints`](https://supervision.roboflow.com/0.21.0/keypoint/core/#supervision.keypoint.core.KeyPoints) from [Inference](https://github.com/roboflow/inference) result. @@ -386,7 +363,6 @@ annotated_image = edge_annotators.annotate(image.copy(), keypoints) - Changed [#1109](https://github.com/roboflow/supervision/pull/1109): [`sv.PolygonZone`](/0.20.0/detection/tools/polygon_zone/#supervision.detection.tools.polygon_zone.PolygonZone) such that the `frame_resolution_wh` argument is no longer required to initialize `sv.PolygonZone`. !!! failure "Deprecated" - The `frame_resolution_wh` parameter in `sv.PolygonZone` is deprecated and will be removed in `supervision-0.24.0`. - Changed [#1084](https://github.com/roboflow/supervision/pull/1084): [`sv.get_polygon_center`](/0.20.0/utils/geometry/#supervision.geometry.core.utils.get_polygon_center) to calculate a more accurate polygon centroid. @@ -492,13 +468,11 @@ annotated_frame = crop_annotator.annotate( - Changed [#787](https://github.com/roboflow/supervision/pull/787): [`sv.ByteTrack`](/0.19.0/trackers/#supervision.tracker.ByteTrack) input arguments and docstrings updated to improve readability and ease of use. !!! failure "Deprecated" - The `track_buffer`, `track_thresh`, and `match_thresh` parameters in `sv.ByteTrack` are deprecated and will be removed in `supervision-0.23.0`. Use `lost_track_buffer,` `track_activation_threshold`, and `minimum_matching_threshold` instead. - Changed [#910](https://github.com/roboflow/supervision/pull/910): [`sv.PolygonZone`](/0.19.0/detection/tools/polygon_zone/#supervision.detection.tools.polygon_zone.PolygonZone) to now accept a list of specific box anchors that must be in zone for a detection to be counted. !!! failure "Deprecated" - The `triggering_position ` parameter in `sv.PolygonZone` is deprecated and will be removed in `supervision-0.23.0`. Use `triggering_anchors` instead. - Changed [#875](https://github.com/roboflow/supervision/pull/875): annotators adding support for Pillow images. All supervision Annotators can now accept an image as either a numpy array or a Pillow Image. They automatically detect its type, draw annotations, and return the output in the same format as the input. @@ -562,7 +536,6 @@ ColorPalette(colors=[Color(r=68, g=1, b=84), Color(r=59, g=82, b=139), ...]) - Changed [#756](https://github.com/roboflow/supervision/pull/756): [`sv.Color`](/0.18.0/draw/color/#color)'s and [`sv.ColorPalette`](/0.18.0/draw/color/#colorpalette)'s method of accessing predefined colors, transitioning from a function-based approach (`sv.Color.red()`) to a more intuitive and conventional property-based method (`sv.Color.RED`). !!! failure "Deprecated" - `sv.ColorPalette.default()` is deprecated and will be removed in `supervision-0.22.0`. Use `sv.ColorPalette.DEFAULT` instead. - Changed [#769](https://github.com/roboflow/supervision/pull/769): [`sv.ColorPalette.DEFAULT`](/0.18.0/draw/color/#colorpalette) value, giving users a more extensive set of annotation colors. @@ -570,7 +543,6 @@ ColorPalette(colors=[Color(r=68, g=1, b=84), Color(r=59, g=82, b=139), ...]) - Changed [#677](https://github.com/roboflow/supervision/pull/677): `sv.Detections.from_roboflow` to [`sv.Detections.from_inference`](/0.18.0/detection/core/#supervision.detection.core.Detections.from_inference) streamlining its functionality to be compatible with both the both [inference](https://github.com/roboflow/inference) pip package and the Robloflow [hosted API](https://docs.roboflow.com/deploy/hosted-api). !!! failure "Deprecated" - `Detections.from_roboflow()` is deprecated and will be removed in `supervision-0.22.0`. Use `Detections.from_inference` instead. - Fixed [#735](https://github.com/roboflow/supervision/pull/735): [`sv.LineZone`](/0.18.0/detection/tools/line_zone/#linezone) functionality to accurately update the counter when an object crosses a line from any direction, including from the side. This enhancement enables more precise tracking and analytics, such as calculating individual in/out counts for each lane on the road. @@ -668,7 +640,6 @@ ColorPalette(colors=[Color(r=68, g=1, b=84), Color(r=59, g=82, b=139), ...]) - Fixed [#430](https://github.com/roboflow/supervision/pull/430): [`sv.ByteTrack`](/0.16.0/trackers/#supervision.tracker.byte_tracker.core.ByteTrack) to return `np.array([], dtype=int)` when `svDetections` is empty. !!! failure "Deprecated" - `sv.Detections.from_yolov8` and `sv.Classifications.from_yolov8` as those are now replaced by [`sv.Detections.from_ultralytics`](/0.16.0/detection/core/#supervision.detection.core.Detections.from_ultralytics) and [`sv.Classifications.from_ultralytics`](/0.16.0/classification/core/#supervision.classification.core.Classifications.from_ultralytics). ### 0.15.0 October 5, 2023 @@ -736,7 +707,6 @@ ColorPalette(colors=[Color(r=68, g=1, b=84), Color(r=59, g=82, b=139), ...]) - Added [#281](https://github.com/roboflow/supervision/pull/281): [`sv.Classifications.from_ultralytics`](/0.14.0/classification/core/#supervision.classification.core.Classifications.from_ultralytics) to enable seamless integration with [Ultralytics](https://github.com/ultralytics/ultralytics) framework. This will enable you to use supervision with all [models](https://docs.ultralytics.com/models/) that Ultralytics supports. !!! failure "Deprecated" - [sv.Detections.from_yolov8](/0.14.0/detection/core/#supervision.detection.core.Detections.from_yolov8) and [sv.Classifications.from_yolov8](/0.14.0/classification/core/#supervision.classification.core.Classifications.from_yolov8) are now deprecated and will be removed with `supervision-0.16.0` release. - Added [#341](https://github.com/roboflow/supervision/pull/341): First supervision usage example script showing how to detect and track objects on video using YOLOv8 + Supervision. @@ -774,7 +744,6 @@ ColorPalette(colors=[Color(r=68, g=1, b=84), Color(r=59, g=82, b=139), ...]) - Added [#222](https://github.com/roboflow/supervision/pull/222): [`sv.Detections.from_ultralytics`](/0.13.0/detection/core/#supervision.detection.core.Detections.from_ultralytics) to enable seamless integration with [Ultralytics](https://github.com/ultralytics/ultralytics) framework. This will enable you to use `supervision` with all [models](https://docs.ultralytics.com/models/) that Ultralytics supports. !!! failure "Deprecated" - [`sv.Detections.from_yolov8`](/0.13.0/detection/core/#supervision.detection.core.Detections.from_yolov8) is now deprecated and will be removed with `supervision-0.15.0` release. - Added [#191](https://github.com/roboflow/supervision/pull/191): [`sv.Detections.from_paddledet`](/0.13.0/detection/core/#supervision.detection.core.Detections.from_paddledet) to enable seamless integration with [PaddleDetection](https://github.com/PaddlePaddle/PaddleDetection) framework. @@ -784,7 +753,6 @@ ColorPalette(colors=[Color(r=68, g=1, b=84), Color(r=59, g=82, b=139), ...]) ### 0.12.0 July 24, 2023 !!! failure "Python 3.7. Support Terminated" - With the `supervision-0.12.0` release, we are terminating official support for Python 3.7. - Added [#177](https://github.com/roboflow/supervision/pull/177): initial support for object detection model benchmarking with [`sv.ConfusionMatrix`](/0.12.0/metrics/detection/#confusionmatrix). diff --git a/docs/contributing.md b/docs/contributing.md index ea38c9bf..4f79db8a 100644 --- a/docs/contributing.md +++ b/docs/contributing.md @@ -1 +1 @@ ---8<-- "CONTRIBUTING.md" +--8\<-- "CONTRIBUTING.md" diff --git a/docs/datasets/core.md b/docs/datasets/core.md index 73931515..4e04fada 100644 --- a/docs/datasets/core.md +++ b/docs/datasets/core.md @@ -5,7 +5,6 @@ comments: true # Datasets !!! warning - Dataset API is still fluid and may change. If you use Dataset API in your project until further notice, freeze the `supervision` version in your `requirements.txt` or `setup.py`. diff --git a/docs/deprecated.md b/docs/deprecated.md index 725e17d5..bdd95a9d 100644 --- a/docs/deprecated.md +++ b/docs/deprecated.md @@ -10,10 +10,13 @@ These features are phased out due to better alternatives or potential issues in - The `frame_resolution_wh ` parameter in [`sv.PolygonZone`](detection/tools/polygon_zone.md/#supervision.detection.tools.polygon_zone.PolygonZone) will be removed in `supervision-0.24.0`. - Constructing [`DetectionDataset`](https://supervision.roboflow.com/latest/datasets/core/#supervision.dataset.core.DetectionDataset) and [`ClassificationDataset`](https://supervision.roboflow.com/latest/datasets/core/#supervision.dataset.core.ClassificationDataset) with parameter `images` as `Dict[str, np.ndarray]` will be removed in `supervision-0.26.0`. Please pass a list of paths `List[str]` instead. + - The `DetectionDataset.images` property will be removed in `supervision-0.26.0`. Please loop over images with `for path, image, annotation in dataset:`, as that does not require loading all images into memory. + - `BoundingBoxAnnotator` has been renamed to `BoxAnnotator` after the old implementation of [`BoxAnnotator`](https://supervision.roboflow.com/latest/detection/annotators/#supervision.annotators.core.BoxAnnotator) has been removed. `BoundingBoxAnnotator` will be removed in `supervision-0.26.0`. - `overlap_filter_strategy` in [`InferenceSlicer.__init__`](https://supervision.roboflow.com/latest/detection/tools/inference_slicer/) is deprecated and will be removed in `supervision-0.27.0`. Use `overlap_strategy` instead. + - `overlap_ratio_wh` in [`InferenceSlicer.__init__`](https://supervision.roboflow.com/latest/detection/tools/inference_slicer/) is deprecated and will be removed in `supervision-0.27.0`. Use `overlap_wh` instead. # Removed diff --git a/docs/how_to/detect_and_annotate.md b/docs/how_to/detect_and_annotate.md index 95d095b6..6369cce3 100644 --- a/docs/how_to/detect_and_annotate.md +++ b/docs/how_to/detect_and_annotate.md @@ -20,7 +20,6 @@ First, you'll need to obtain predictions from your object detection or segmentat model. === "Inference" - ```python import cv2 from inference import get_model @@ -31,7 +30,6 @@ model. ``` === "Ultralytics" - ```python import cv2 from ultralytics import YOLO @@ -42,7 +40,6 @@ model. ``` === "Transformers" - ```python import torch from PIL import Image @@ -68,7 +65,6 @@ model. Now that we have predictions from a model, we can load them into Supervision. === "Inference" - We can do so using the [`sv.Detections.from_inference`](/latest/detection/core/#supervision.detection.core.Detections.from_inference) method, which accepts model results from both detection and segmentation models. ```{ .py hl_lines="2 8" } @@ -83,7 +79,6 @@ Now that we have predictions from a model, we can load them into Supervision. ``` === "Ultralytics" - We can do so using the [`sv.Detections.from_ultralytics`](/latest/detection/core/#supervision.detection.core.Detections.from_ultralytics) method, which accepts model results from both detection and segmentation models. ```{ .py hl_lines="2 8" } @@ -98,7 +93,6 @@ Now that we have predictions from a model, we can load them into Supervision. ``` === "Transformers" - We can do so using the [`sv.Detections.from_transformers`](/latest/detection/core/#supervision.detection.core.Detections.from_transformers) method, which accepts model results from both detection and segmentation models. ```{ .py hl_lines="2 19-21" } @@ -138,7 +132,6 @@ You can load predictions from other computer vision frameworks and libraries usi Finally, we can annotate the image with the predictions. Since we are working with an object detection model, we will use the [`sv.BoxAnnotator`](/latest/detection/annotators/#supervision.annotators.core.BoxAnnotator) and [`sv.LabelAnnotator`](/latest/detection/annotators/#supervision.annotators.core.LabelAnnotator) classes. === "Inference" - ```{ .py hl_lines="10-16" } import cv2 import supervision as sv @@ -159,7 +152,6 @@ Finally, we can annotate the image with the predictions. Since we are working wi ``` === "Ultralytics" - ```{ .py hl_lines="10-16" } import cv2 import supervision as sv @@ -180,7 +172,6 @@ Finally, we can annotate the image with the predictions. Since we are working wi ``` === "Transformers" - ```{ .py hl_lines="23-30" } import torch import supervision as sv @@ -222,7 +213,6 @@ will label each detection with its `class_name` (if possible) or `class_id`. You override this behavior by passing a list of custom `labels` to the `annotate` method. === "Inference" - ```{ .py hl_lines="13-17 22" } import cv2 import supervision as sv @@ -249,7 +239,6 @@ override this behavior by passing a list of custom `labels` to the `annotate` me ``` === "Ultralytics" - ```{ .py hl_lines="13-17 22" } import cv2 import supervision as sv @@ -276,7 +265,6 @@ override this behavior by passing a list of custom `labels` to the `annotate` me ``` === "Transformers" - ```{ .py hl_lines="26-30 35" } import torch import supervision as sv @@ -326,7 +314,6 @@ is a drop-in replacement for that will allow you to draw masks instead of boxes. === "Inference" - ```python import cv2 import supervision as sv @@ -347,7 +334,6 @@ that will allow you to draw masks instead of boxes. ``` === "Ultralytics" - ```python import cv2 import supervision as sv @@ -368,7 +354,6 @@ that will allow you to draw masks instead of boxes. ``` === "Transformers" - ```python import torch import supervision as sv diff --git a/docs/how_to/detect_small_objects.md b/docs/how_to/detect_small_objects.md index 604b4688..054bdabf 100644 --- a/docs/how_to/detect_small_objects.md +++ b/docs/how_to/detect_small_objects.md @@ -20,7 +20,6 @@ Small object detection in high-resolution images presents challenges due to the size relative to the image resolution. === "Inference" - ```python import cv2 import supervision as sv @@ -41,7 +40,6 @@ size relative to the image resolution. ``` === "Ultralytics" - ```python import cv2 import supervision as sv @@ -62,7 +60,6 @@ size relative to the image resolution. ``` === "Transformers" - ```python import torch import supervision as sv @@ -108,7 +105,6 @@ identification at the cost of processing speed and increased memory usage. This is less effective for ultra-high-resolution images (4K and above). === "Inference" - ```{ .py hl_lines="5" } import cv2 import supervision as sv @@ -129,7 +125,6 @@ is less effective for ultra-high-resolution images (4K and above). ``` === "Ultralytics" - ```{ .py hl_lines="7" } import cv2 import supervision as sv @@ -162,7 +157,6 @@ objects within each, and aggregating the results. === "Inference" - ```{ .py hl_lines="9-14" } import cv2 import numpy as np @@ -189,7 +183,6 @@ objects within each, and aggregating the results. ``` === "Ultralytics" - ```{ .py hl_lines="9-14" } import cv2 import numpy as np @@ -216,7 +209,6 @@ objects within each, and aggregating the results. ``` === "Transformers" - ```{ .py hl_lines="13-28" } import cv2 import torch @@ -269,7 +261,6 @@ objects within each, and aggregating the results. [`InferenceSlicer`](/latest/detection/tools/inference_slicer/#supervision.detection.tools.inference_slicer.InferenceSlicer) can perform segmentation tasks too. === "Inference" - ```{ .py hl_lines="6 16 19-20" } import cv2 import numpy as np @@ -296,7 +287,6 @@ objects within each, and aggregating the results. ``` === "Ultralytics" - ```{ .py hl_lines="6 16 19-20" } import cv2 import numpy as np diff --git a/docs/how_to/filter_detections.md b/docs/how_to/filter_detections.md index 64e26618..1624d815 100644 --- a/docs/how_to/filter_detections.md +++ b/docs/how_to/filter_detections.md @@ -15,7 +15,6 @@ the filters in their applications. Allows you to select detections that belong only to one selected class. === "After" - ```python import supervision as sv @@ -30,7 +29,6 @@ Allows you to select detections that belong only to one selected class.
=== "Before" - ```python import supervision as sv @@ -49,7 +47,6 @@ Allows you to select detections that belong only to one selected class. Allows you to select detections that belong only to selected set of classes. === "After" - ```python import numpy as np import supervision as sv @@ -66,7 +63,6 @@ Allows you to select detections that belong only to selected set of classes.
=== "Before" - ```python import numpy as np import supervision as sv @@ -87,7 +83,6 @@ Allows you to select detections that belong only to selected set of classes. Allows you to select detections with specific confidence value, for example higher than selected threshold. === "After" - ```python import supervision as sv @@ -102,7 +97,6 @@ Allows you to select detections with specific confidence value, for example high
=== "Before" - ```python import supervision as sv @@ -122,7 +116,6 @@ Allows you to select detections based on their size. We define the area as the n detection in the image. In the example below, we have sifted out the detections that are too small. === "After" - ```python import supervision as sv @@ -137,7 +130,6 @@ detection in the image. In the example below, we have sifted out the detections === "Before" - ```python import supervision as sv @@ -159,7 +151,6 @@ but small on a 3840x2160 image. In such cases, we can filter out detections base occupied by them. In the example below, we remove too large detections. === "After" - ```python import supervision as sv @@ -178,7 +169,6 @@ occupied by them. In the example below, we remove too large detections. === "Before" - ```python import supervision as sv @@ -203,7 +193,6 @@ can be criteria for rejecting detection. Implementing such filtering requires a simple and fast. === "After" - ```python import supervision as sv @@ -220,7 +209,6 @@ simple and fast. === "Before" - ```python import supervision as sv @@ -242,7 +230,6 @@ Allows you to use `Detections` in combination with `PolygonZone` to weed out bou zone. In the example below you can see how to filter out all detections located in the lower part of the image. === "After" - ```python import supervision as sv @@ -259,7 +246,6 @@ zone. In the example below you can see how to filter out all detections located === "Before" - ```python import supervision as sv @@ -280,7 +266,6 @@ zone. In the example below you can see how to filter out all detections located `Detections`' greatest strength, however, is that you can build arbitrarily complex logical conditions by simply combining separate conditions using `&` or `|`. === "After" - ```python import supervision as sv @@ -297,7 +282,6 @@ zone. In the example below you can see how to filter out all detections located === "Before" - ```python import supervision as sv diff --git a/docs/how_to/save_detections.md b/docs/how_to/save_detections.md index 05d5faad..e9e14849 100644 --- a/docs/how_to/save_detections.md +++ b/docs/how_to/save_detections.md @@ -19,7 +19,6 @@ model. You can learn more on this topic in our [How to Detect and Annotate](/latest/how_to/detect_and_annotate.md) guide. === "Inference" - ```python import supervision as sv from inference import get_model @@ -34,7 +33,6 @@ model. You can learn more on this topic in our ``` === "Ultralytics" - ```python import supervision as sv from ultralytics import YOLO @@ -49,7 +47,6 @@ model. You can learn more on this topic in our ``` === "Transformers" - ```python import torch import supervision as sv @@ -83,7 +80,6 @@ and then pass the object resulting from the inference to it. Its fields are parsed and saved on disk. === "Inference" - ```{ .py hl_lines="7 12" } import supervision as sv from inference import get_model @@ -100,7 +96,6 @@ object resulting from the inference to it. Its fields are parsed and saved on di ``` === "Ultralytics" - ```{ .py hl_lines="7 12" } import supervision as sv from ultralytics import YOLO @@ -117,7 +112,6 @@ object resulting from the inference to it. Its fields are parsed and saved on di ``` === "Transformers" - ```{ .py hl_lines="9 23" } import torch import supervision as sv @@ -144,11 +138,11 @@ object resulting from the inference to it. Its fields are parsed and saved on di sink.append(detections, {}) ``` -| x_min | y_min | x_max | y_max | class_id | confidence | tracker_id | class_name | -|---------|----------|---------|----------|----------|------------|------------|------------| -| 2941.14 | 1269.31 | 3220.77 | 1500.67 | 2 | 0.8517 | | car | -| 944.889 | 899.641 | 1235.42 | 1308.80 | 7 | 0.6752 | | truck | -| 1439.78 | 1077.79 | 1621.27 | 1231.40 | 2 | 0.6450 | | car | +| x_min | y_min | x_max | y_max | class_id | confidence | tracker_id | class_name | +| ------- | ------- | ------- | ------- | -------- | ---------- | ---------- | ---------- | +| 2941.14 | 1269.31 | 3220.77 | 1500.67 | 2 | 0.8517 | | car | +| 944.889 | 899.641 | 1235.42 | 1308.80 | 7 | 0.6752 | | truck | +| 1439.78 | 1077.79 | 1621.27 | 1231.40 | 2 | 0.6450 | | car | ## Custom Fields @@ -160,7 +154,6 @@ also allows you to add custom information to each row, which can be passed via t frame index from which the detections originate. === "Inference" - ```{ .py hl_lines="8 12" } import supervision as sv from inference import get_model @@ -177,7 +170,6 @@ frame index from which the detections originate. ``` === "Ultralytics" - ```{ .py hl_lines="8 12" } import supervision as sv from ultralytics import YOLO @@ -194,7 +186,6 @@ frame index from which the detections originate. ``` === "Transformers" - ```{ .py hl_lines="10 23" } import torch import supervision as sv @@ -221,11 +212,11 @@ frame index from which the detections originate. sink.append(detections, {"frame_index": frame_index}) ``` -| x_min | y_min | x_max | y_max | class_id | confidence | tracker_id | class_name | frame_index | -|---------|----------|---------|----------|----------|------------|------------|------------|-------------| -| 2941.14 | 1269.31 | 3220.77 | 1500.67 | 2 | 0.8517 | | car | 0 | -| 944.889 | 899.641 | 1235.42 | 1308.80 | 7 | 0.6752 | | truck | 0 | -| 1439.78 | 1077.79 | 1621.27 | 1231.40 | 2 | 0.6450 | | car | 0 | +| x_min | y_min | x_max | y_max | class_id | confidence | tracker_id | class_name | frame_index | +| ------- | ------- | ------- | ------- | -------- | ---------- | ---------- | ---------- | ----------- | +| 2941.14 | 1269.31 | 3220.77 | 1500.67 | 2 | 0.8517 | | car | 0 | +| 944.889 | 899.641 | 1235.42 | 1308.80 | 7 | 0.6752 | | truck | 0 | +| 1439.78 | 1077.79 | 1621.27 | 1231.40 | 2 | 0.6450 | | car | 0 | ## Save Detections as JSON @@ -236,7 +227,6 @@ with [`sv.JSONSink`](/latest/detection/tools/save_detections/#supervision.detection.tools.csv_sink.JSONSink). === "Inference" - ```{ .py hl_lines="7" } import supervision as sv from inference import get_model @@ -253,7 +243,6 @@ with ``` === "Ultralytics" - ```{ .py hl_lines="7" } import supervision as sv from ultralytics import YOLO @@ -270,7 +259,6 @@ with ``` === "Transformers" - ```{ .py hl_lines="9" } import torch import supervision as sv diff --git a/docs/how_to/track_objects.md b/docs/how_to/track_objects.md index 6812446d..464f6b8d 100644 --- a/docs/how_to/track_objects.md +++ b/docs/how_to/track_objects.md @@ -41,7 +41,6 @@ This `callback` function will be essential in the subsequent steps of the tutori it will be modified to include tracking, labeling, and trace annotations. === "Ultralytics" - ```{ .py } import numpy as np import supervision as sv @@ -63,7 +62,6 @@ it will be modified to include tracking, labeling, and trace annotations. ``` === "Inference" - ```{ .py } import numpy as np import supervision as sv @@ -97,7 +95,6 @@ functionality, each detected object is assigned a unique tracker ID, enabling the continuous following of the object's motion path across different frames. === "Ultralytics" - ```{ .py hl_lines="6 12" } import numpy as np import supervision as sv @@ -121,7 +118,6 @@ enabling the continuous following of the object's motion path across different f ``` === "Inference" - ```{ .py hl_lines="6 12" } import numpy as np import supervision as sv @@ -153,7 +149,6 @@ in Supervision, we can overlay the tracker IDs and class labels on the detected offering a clear visual representation of each object's class and unique identifier. === "Ultralytics" - ```{ .py hl_lines="8 15-19 23-24" } import numpy as np import supervision as sv @@ -188,7 +183,6 @@ offering a clear visual representation of each object's class and unique identif ``` === "Inference" - ```{ .py hl_lines="8 15-19 23-24" } import numpy as np import supervision as sv @@ -235,7 +229,6 @@ allows for visualizing the trajectories of objects, helping in understanding the movement patterns and interactions between objects in the video. === "Ultralytics" - ```{ .py hl_lines="9 26-27" } import numpy as np import supervision as sv @@ -273,7 +266,6 @@ movement patterns and interactions between objects in the video. ``` === "Inference" - ```{ .py hl_lines="9 26-27" } import numpy as np import supervision as sv diff --git a/docs/index.md b/docs/index.md index 4bce227d..08fdb240 100644 --- a/docs/index.md +++ b/docs/index.md @@ -34,9 +34,7 @@ You can install `supervision` in a [**Python>=3.8**](https://www.python.org/) environment. !!! example "pip install (recommended)" - === "pip" - [![version](https://badge.fury.io/py/supervision.svg)](https://badge.fury.io/py/supervision) [![downloads](https://img.shields.io/pypi/dm/supervision)](https://pypistats.org/packages/supervision) [![license](https://img.shields.io/pypi/l/supervision)](https://github.com/roboflow/supervision/blob/main/LICENSE.md) @@ -47,9 +45,7 @@ You can install `supervision` in a ``` !!! example "conda/mamba install" - === "conda" - [![conda-recipe](https://img.shields.io/badge/recipe-supervision-green.svg)](https://anaconda.org/conda-forge/supervision) [![conda-downloads](https://img.shields.io/conda/dn/conda-forge/supervision.svg)](https://anaconda.org/conda-forge/supervision) [![conda-version](https://img.shields.io/conda/vn/conda-forge/supervision.svg)](https://anaconda.org/conda-forge/supervision) [![conda-platforms](https://img.shields.io/conda/pn/conda-forge/supervision.svg)](https://anaconda.org/conda-forge/supervision) ```bash @@ -57,7 +53,6 @@ You can install `supervision` in a ``` === "mamba" - [![mamba-recipe](https://img.shields.io/badge/recipe-supervision-green.svg)](https://anaconda.org/conda-forge/supervision) [![mamba-downloads](https://img.shields.io/conda/dn/conda-forge/supervision.svg)](https://anaconda.org/conda-forge/supervision) [![mamba-version](https://img.shields.io/conda/vn/conda-forge/supervision.svg)](https://anaconda.org/conda-forge/supervision) [![mamba-platforms](https://img.shields.io/conda/pn/conda-forge/supervision.svg)](https://anaconda.org/conda-forge/supervision) ```bash @@ -65,9 +60,7 @@ You can install `supervision` in a ``` !!! example "git clone (for development)" - === "virtualenv" - ```bash # clone repository and navigate to root directory git clone https://github.com/roboflow/supervision.git @@ -83,7 +76,6 @@ You can install `supervision` in a ``` === "poetry" - ```bash # clone repository and navigate to root directory git clone https://github.com/roboflow/supervision.git @@ -103,48 +95,48 @@ You can install `supervision` in a - **Detect and Annotate** - *** + --- - Annotate predictions from a range of object detection and segmentation models + Annotate predictions from a range of object detection and segmentation models - [:octicons-arrow-right-24: Tutorial](how_to/detect_and_annotate.md) + [:octicons-arrow-right-24: Tutorial](how_to/detect_and_annotate.md) - **Track Objects** - *** + --- - Discover how to enhance video analysis by implementing seamless object tracking + Discover how to enhance video analysis by implementing seamless object tracking - [:octicons-arrow-right-24: Tutorial](how_to/track_objects.md) + [:octicons-arrow-right-24: Tutorial](how_to/track_objects.md) - **Detect Small Objects** - *** + --- - Learn how to detect small objects in images + Learn how to detect small objects in images - [:octicons-arrow-right-24: Tutorial](how_to/detect_small_objects.md) + [:octicons-arrow-right-24: Tutorial](how_to/detect_small_objects.md) - **Count Objects Crossing Line** - *** + --- - Explore methods to accurately count and analyze objects crossing a predefined line + Explore methods to accurately count and analyze objects crossing a predefined line - [:octicons-arrow-right-24: Notebook](https://supervision.roboflow.com/latest/notebooks/count-objects-crossing-the-line/) + [:octicons-arrow-right-24: Notebook](https://supervision.roboflow.com/latest/notebooks/count-objects-crossing-the-line/) - > **Filter Objects in Zone** - *** + --- - Master the techniques to selectively filter and focus on objects within a specific zone + Master the techniques to selectively filter and focus on objects within a specific zone - **Cheatsheet** - *** + --- - Access a quick reference guide to the most common `supervision` functions + Access a quick reference guide to the most common `supervision` functions - [:octicons-arrow-right-24: Cheatsheet](https://roboflow.github.io/cheatsheet-supervision/) + [:octicons-arrow-right-24: Cheatsheet](https://roboflow.github.io/cheatsheet-supervision/) diff --git a/examples/count_people_in_zone/README.md b/examples/count_people_in_zone/README.md index d9d3cdb2..0cb20955 100644 --- a/examples/count_people_in_zone/README.md +++ b/examples/count_people_in_zone/README.md @@ -16,67 +16,76 @@ https://github.com/roboflow/supervision/assets/26109316/f84db7b5-79e2-4142-a1da- - clone repository and navigate to example directory - ```bash - git clone https://github.com/roboflow/supervision.git - cd supervision/examples/count_people_in_zone - ``` + ```bash + git clone https://github.com/roboflow/supervision.git + cd supervision/examples/count_people_in_zone + ``` -- setup python environment and activate it [optional] +- setup python environment and activate it \[optional\] - ```bash - python3 -m venv venv - source venv/bin/activate - ``` + ```bash + python3 -m venv venv + source venv/bin/activate + ``` - install required dependencies - ```bash - pip install -r requirements.txt - ``` + ```bash + pip install -r requirements.txt + ``` - download `traffic_analysis.pt` and `traffic_analysis.mov` files - ```bash - ./setup.sh - ``` + ```bash + ./setup.sh + ``` ## 🛠️ script arguments - ultralytics - - `--source_weights_path` (optional): The path to the YOLO model's weights file. - Defaults to `"yolov8x.pt"` if not specified. + - `--source_weights_path` (optional): The path to the YOLO model's weights file. + Defaults to `"yolov8x.pt"` if not specified. - - `--zone_configuration_path`: Specifies the path to the JSON file containing zone - configurations. This file defines the polygonal areas in the video where objects will - be counted. - - `--source_video_path`: The path to the source video file that will be analyzed. - - `--target_video_path` (optional): The path to save the output video with annotations. - If not provided, the processed video will be displayed in real-time. - - `--confidence_threshold` (optional): Sets the confidence threshold for the YOLO model - to filter detections. Default is `0.3`. - - `--iou_threshold` (optional): Specifies the IOU (Intersection Over Union) threshold - for the model. Default is `0.7`. + - `--zone_configuration_path`: Specifies the path to the JSON file containing zone + configurations. This file defines the polygonal areas in the video where objects will + be counted. + + - `--source_video_path`: The path to the source video file that will be analyzed. + + - `--target_video_path` (optional): The path to save the output video with annotations. + If not provided, the processed video will be displayed in real-time. + + - `--confidence_threshold` (optional): Sets the confidence threshold for the YOLO model + to filter detections. Default is `0.3`. + + - `--iou_threshold` (optional): Specifies the IOU (Intersection Over Union) threshold + for the model. Default is `0.7`. - inference - - `--roboflow_api_key` (optional): The API key for Roboflow services. If not provided - directly, the script tries to fetch it from the `ROBOFLOW_API_KEY` environment - variable. Follow [this guide](https://docs.roboflow.com/api-reference/authentication#retrieve-an-api-key) - to acquire your `API KEY`. - - `--model_id` (optional): Designates the Roboflow model ID to be used. The default - value is `"yolov8x-1280"`. + - `--roboflow_api_key` (optional): The API key for Roboflow services. If not provided + directly, the script tries to fetch it from the `ROBOFLOW_API_KEY` environment + variable. Follow [this guide](https://docs.roboflow.com/api-reference/authentication#retrieve-an-api-key) + to acquire your `API KEY`. - - `--zone_configuration_path`: Specifies the path to the JSON file containing zone - configurations. This file defines the polygonal areas in the video where objects will - be counted. - - `--source_video_path`: The path to the source video file that will be analyzed. - - `--target_video_path` (optional): The path to save the output video with annotations. - If not provided, the processed video will be displayed in real-time. - - `--confidence_threshold` (optional): Sets the confidence threshold for the YOLO model - to filter detections. Default is `0.3`. - - `--iou_threshold` (optional): Specifies the IOU (Intersection Over Union) threshold - for the model. Default is `0.7`. + - `--model_id` (optional): Designates the Roboflow model ID to be used. The default + value is `"yolov8x-1280"`. + + - `--zone_configuration_path`: Specifies the path to the JSON file containing zone + configurations. This file defines the polygonal areas in the video where objects will + be counted. + + - `--source_video_path`: The path to the source video file that will be analyzed. + + - `--target_video_path` (optional): The path to save the output video with annotations. + If not provided, the processed video will be displayed in real-time. + + - `--confidence_threshold` (optional): Sets the confidence threshold for the YOLO model + to filter detections. Default is `0.3`. + + - `--iou_threshold` (optional): Specifies the IOU (Intersection Over Union) threshold + for the model. Default is `0.7`. ## 📌 zone configuration @@ -89,35 +98,35 @@ https://github.com/roboflow/supervision/assets/26109316/f84db7b5-79e2-4142-a1da- - ultralytics - ```bash - python ultralytics_example.py \ - --zone_configuration_path data/multi-zone-config.json \ - --source_video_path data/market-square.mp4 \ - --confidence_threshold 0.3 \ - --iou_threshold 0.5 - ``` + ```bash + python ultralytics_example.py \ + --zone_configuration_path data/multi-zone-config.json \ + --source_video_path data/market-square.mp4 \ + --confidence_threshold 0.3 \ + --iou_threshold 0.5 + ``` - inference - ```bash - python inference_example.py \ - --roboflow_api_key \ - --zone_configuration_path data/multi-zone-config.json \ - --source_video_path data/market-square.mp4 \ - --confidence_threshold 0.3 \ - --iou_threshold 0.5 - ``` + ```bash + python inference_example.py \ + --roboflow_api_key \ + --zone_configuration_path data/multi-zone-config.json \ + --source_video_path data/market-square.mp4 \ + --confidence_threshold 0.3 \ + --iou_threshold 0.5 + ``` ## © license This demo integrates two main components, each with its own licensing: - ultralytics: The object detection model used in this demo, YOLOv8, is distributed - under the [AGPL-3.0 license](https://github.com/ultralytics/ultralytics/blob/main/LICENSE). - You can find more details about this license here. + under the [AGPL-3.0 license](https://github.com/ultralytics/ultralytics/blob/main/LICENSE). + You can find more details about this license here. - supervision: The analytics code that powers the zone-based analysis in this demo is - based on the Supervision library, which is licensed under the - [MIT license](https://github.com/roboflow/supervision/blob/develop/LICENSE.md). This - makes the Supervision part of the code fully open source and freely usable in your - projects. + based on the Supervision library, which is licensed under the + [MIT license](https://github.com/roboflow/supervision/blob/develop/LICENSE.md). This + makes the Supervision part of the code fully open source and freely usable in your + projects. diff --git a/examples/heatmap_and_track/README.md b/examples/heatmap_and_track/README.md index 0c589841..2c2fc58c 100644 --- a/examples/heatmap_and_track/README.md +++ b/examples/heatmap_and_track/README.md @@ -6,7 +6,6 @@ This script performs heatmap and tracking analysis using YOLOv8, an object-detec ByteTrack, a simple yet effective online multi-object tracking method. It uses the supervision package for multiple tasks such as drawing heatmap annotations, tracking objects, etc. - ## 💻 install - clone repository and navigate to example directory @@ -16,7 +15,7 @@ supervision package for multiple tasks such as drawing heatmap annotations, trac cd supervision/examples/heatmap_and_track ``` -- setup python environment and activate it [optional] +- setup python environment and activate it \[optional\] ```bash python3 -m venv venv @@ -32,17 +31,17 @@ supervision package for multiple tasks such as drawing heatmap annotations, trac ## 🛠️ script arguments - `--source_weights_path`: Required. Specifies the path to the weights file for the -YOLO model. This file contains the trained model data necessary for object detection. + YOLO model. This file contains the trained model data necessary for object detection. - `--source_video_path` (optional): The path to the source video file that will be -analyzed. This is the input video on which crowd analysis will be performed. -If not specified default is `people-walking.mp4` from supervision assets + analyzed. This is the input video on which crowd analysis will be performed. + If not specified default is `people-walking.mp4` from supervision assets - `--target_video_path` (optional): The path to save the output.mp4 video with annotations. - `--confidence_threshold` (optional): Sets the confidence threshold for the YOLO model -to filter detections. Default is `0.3`. This determines how confident the model should -be to recognize an object in the video. + to filter detections. Default is `0.3`. This determines how confident the model should + be to recognize an object in the video. - `--iou_threshold` (optional): Specifies the IOU (Intersection Over Union) threshold -for the model. Default is 0.7. This value is used to manage object detection accuracy, -particularly in distinguishing between different objects. + for the model. Default is 0.7. This value is used to manage object detection accuracy, + particularly in distinguishing between different objects. - `--heatmap_alpha` (optional): Opacity of the overlay mask, between 0 and 1. - `--radius` (optional): Radius of the heat circle. - `--track_threshold` (optional): Detection confidence threshold for track activation. @@ -53,11 +52,11 @@ particularly in distinguishing between different objects. ```bash python script.py \ ---source_weights_path weight.pt \ ---source_video_path input_video.mp4 \ ---confidence_threshold 0.3 \ ---iou_threshold 0.5 \ ---target_video_path output_video.mp4 + --source_weights_path weight.pt \ + --source_video_path input_video.mp4 \ + --confidence_threshold 0.3 \ + --iou_threshold 0.5 \ + --target_video_path output_video.mp4 ``` ## © license @@ -65,11 +64,11 @@ python script.py \ This demo integrates two main components, each with its own licensing: - ultralytics: The object detection model used in this demo, YOLOv8, is distributed - under the [AGPL-3.0 license](https://github.com/ultralytics/ultralytics/blob/main/LICENSE). - You can find more details about this license here. + under the [AGPL-3.0 license](https://github.com/ultralytics/ultralytics/blob/main/LICENSE). + You can find more details about this license here. - supervision: The analytics code that powers the zone-based analysis in this demo is - based on the Supervision library, which is licensed under the - [MIT license](https://github.com/roboflow/supervision/blob/develop/LICENSE.md). This - makes the Supervision part of the code fully open source and freely usable in your - projects. + based on the Supervision library, which is licensed under the + [MIT license](https://github.com/roboflow/supervision/blob/develop/LICENSE.md). This + makes the Supervision part of the code fully open source and freely usable in your + projects. diff --git a/examples/speed_estimation/README.md b/examples/speed_estimation/README.md index 46a97e5c..08a2018b 100644 --- a/examples/speed_estimation/README.md +++ b/examples/speed_estimation/README.md @@ -11,7 +11,7 @@ supervision package for multiple tasks such as tracking, annotations, etc. https://github.com/roboflow/supervision/assets/26109316/d50118c1-2ae4-458d-915a-5d860fd36f71 -> [!IMPORTANT] +> \[!IMPORTANT\] > Adjust the [`SOURCE`](https://github.com/roboflow/supervision/blob/e32b05a636dab2ea1f39299e529c4b22b8baa8da/examples/speed_estimation/ultralytics_example.py#L10) > and [`TARGET`](https://github.com/roboflow/supervision/blob/e32b05a636dab2ea1f39299e529c4b22b8baa8da/examples/speed_estimation/ultralytics_example.py#L15) > configuration if you plan to run a speed estimation script on your video file. Those must be adjusted separately for each camera view. You can learn more @@ -21,97 +21,102 @@ https://github.com/roboflow/supervision/assets/26109316/d50118c1-2ae4-458d-915a- - clone repository and navigate to example directory - ```bash - git clone https://github.com/roboflow/supervision.git - cd supervision/examples/speed_estimation - ``` + ```bash + git clone https://github.com/roboflow/supervision.git + cd supervision/examples/speed_estimation + ``` -- setup python environment and activate it [optional] +- setup python environment and activate it \[optional\] - ```bash - python3.10 -m venv venv - source venv/bin/activate - ``` + ```bash + python3.10 -m venv venv + source venv/bin/activate + ``` - install required dependencies - ```bash - pip install -r requirements.txt - ``` + ```bash + pip install -r requirements.txt + ``` - download `vehicles.mp4` file - ```bash - python3.10 video_downloader.py - ``` + ```bash + python3.10 video_downloader.py + ``` ## 🛠️ script arguments - `--roboflow_api_key` (optional): The API key for Roboflow services. If not provided - directly, the script tries to fetch it from the `ROBOFLOW_API_KEY` environment - variable. Follow [this guide](https://docs.roboflow.com/api-reference/authentication#retrieve-an-api-key) - to acquire your `API KEY`. + directly, the script tries to fetch it from the `ROBOFLOW_API_KEY` environment + variable. Follow [this guide](https://docs.roboflow.com/api-reference/authentication#retrieve-an-api-key) + to acquire your `API KEY`. + - `--model_id` (optional): Designates the Roboflow model ID to be used. The default - value is `"yolov8x-1280"`. + value is `"yolov8x-1280"`. - `--source_weights_path`: Required. Specifies the path to the YOLO model's weights - file, which is essential for the object detection process. This file contains the - data that the model uses to identify objects in the video. + file, which is essential for the object detection process. This file contains the + data that the model uses to identify objects in the video. + - `--source_video_path`: Required. The path to the source video file that will be - analyzed. This is the input video on which traffic flow analysis will be performed. + analyzed. This is the input video on which traffic flow analysis will be performed. + - `--target_video_path`: The path to save the output video with - annotations. If not specified, the processed video will be displayed in real-time - without being saved. + annotations. If not specified, the processed video will be displayed in real-time + without being saved. + - `--confidence_threshold` (optional): Sets the confidence threshold for the YOLO - model to filter detections. Default is `0.3`. This determines how confident the - model should be to recognize an object in the video. + model to filter detections. Default is `0.3`. This determines how confident the + model should be to recognize an object in the video. + - `--iou_threshold` (optional): Specifies the IOU (Intersection Over Union) threshold - for the model. Default is 0.7. This value is used to manage object detection - accuracy, particularly in distinguishing between different objects. + for the model. Default is 0.7. This value is used to manage object detection + accuracy, particularly in distinguishing between different objects. ## ⚙️ run - yolo-nas - ```bash + ```bash python yolo_nas_example.py \ - --source_video_path data/vehicles.mp4 \ - --target_video_path data/vehicles-result.mp4 \ - --confidence_threshold 0.3 \ - --iou_threshold 0.5 - ``` + --source_video_path data/vehicles.mp4 \ + --target_video_path data/vehicles-result.mp4 \ + --confidence_threshold 0.3 \ + --iou_threshold 0.5 + ``` - inference - ```bash + ```bash python inference_example.py \ - --roboflow_api_key \ - --source_video_path data/vehicles.mp4 \ - --target_video_path data/vehicles-result.mp4 \ - --confidence_threshold 0.3 \ - --iou_threshold 0.5 - ``` + --roboflow_api_key \ + --source_video_path data/vehicles.mp4 \ + --target_video_path data/vehicles-result.mp4 \ + --confidence_threshold 0.3 \ + --iou_threshold 0.5 + ``` - ultralytics - ```bash + ```bash python ultralytics_example.py \ - --source_video_path data/vehicles.mp4 \ - --target_video_path data/vehicles-result.mp4 \ - --confidence_threshold 0.3 \ - --iou_threshold 0.5 - ``` + --source_video_path data/vehicles.mp4 \ + --target_video_path data/vehicles-result.mp4 \ + --confidence_threshold 0.3 \ + --iou_threshold 0.5 + ``` ## © license This demo integrates two main components, each with its own licensing: - ultralytics: The object detection model used in this demo, YOLOv8, is distributed - under the [AGPL-3.0 license](https://github.com/ultralytics/ultralytics/blob/main/LICENSE). - You can find more details about this license here. + under the [AGPL-3.0 license](https://github.com/ultralytics/ultralytics/blob/main/LICENSE). + You can find more details about this license here. - supervision: The analytics code that powers the zone-based analysis in this demo is - based on the Supervision library, which is licensed under the - [MIT license](https://github.com/roboflow/supervision/blob/develop/LICENSE.md). This - makes the Supervision part of the code fully open source and freely usable in your - projects. + based on the Supervision library, which is licensed under the + [MIT license](https://github.com/roboflow/supervision/blob/develop/LICENSE.md). This + makes the Supervision part of the code fully open source and freely usable in your + projects. diff --git a/examples/time_in_zone/README.md b/examples/time_in_zone/README.md index 0a366a94..b40e29eb 100644 --- a/examples/time_in_zone/README.md +++ b/examples/time_in_zone/README.md @@ -15,23 +15,23 @@ https://github.com/roboflow/supervision/assets/26109316/d051cc8a-dd15-41d4-aa36- - clone repository and navigate to example directory - ```bash - git clone https://github.com/roboflow/supervision.git - cd supervision/examples/time_in_zone - ``` + ```bash + git clone https://github.com/roboflow/supervision.git + cd supervision/examples/time_in_zone + ``` -- setup python environment and activate it [optional] +- setup python environment and activate it \[optional\] - ```bash - python3 -m venv venv - source venv/bin/activate - ``` + ```bash + python3 -m venv venv + source venv/bin/activate + ``` - install required dependencies - ```bash - pip install -r requirements.txt - ``` + ```bash + pip install -r requirements.txt + ``` ## 🛠 scripts @@ -45,16 +45,16 @@ This script allows you to download a video from YouTube. ```bash python scripts/download_from_youtube.py \ ---url "https://www.youtube.com/watch?v=-8zyEwAa50Q" \ ---output_path "data/checkout" \ ---file_name "video.mp4" + --url "https://www.youtube.com/watch?v=-8zyEwAa50Q" \ + --output_path "data/checkout" \ + --file_name "video.mp4" ``` ```bash python scripts/download_from_youtube.py \ ---url "https://www.youtube.com/watch?v=MNn9qKG2UFI" \ ---output_path "data/traffic" \ ---file_name "video.mp4" + --url "https://www.youtube.com/watch?v=MNn9qKG2UFI" \ + --output_path "data/traffic" \ + --file_name "video.mp4" ``` ### `stream_from_file` @@ -68,14 +68,14 @@ mock a live video stream for local testing. Video will be streamed in a loop und ```bash python scripts/stream_from_file.py \ ---video_directory "data/checkout" \ ---number_of_streams 1 + --video_directory "data/checkout" \ + --number_of_streams 1 ``` ```bash python scripts/stream_from_file.py \ ---video_directory "data/traffic" \ ---number_of_streams 1 + --video_directory "data/traffic" \ + --number_of_streams 1 ``` ### `draw_zones` @@ -86,24 +86,27 @@ window where you can draw polygons on the source image or video file. The polygo be saved as a JSON file. - `--source_path`: Path to the source image or video file for drawing polygons. + - `--zone_configuration_path`: Path where the polygon annotations will be saved as a JSON file. - - `enter` - finish drawing the current polygon. + - `escape` - cancel drawing the current polygon. + - `q` - quit the drawing window. + - `s` - save zone configuration to a JSON file. ```bash python scripts/draw_zones.py \ ---source_path "data/checkout/video.mp4" \ ---zone_configuration_path "data/checkout/config.json" + --source_path "data/checkout/video.mp4" \ + --zone_configuration_path "data/checkout/config.json" ``` ```bash python scripts/draw_zones.py \ ---source_path "data/traffic/video.mp4" \ ---zone_configuration_path "data/traffic/config.json" + --source_path "data/traffic/video.mp4" \ + --zone_configuration_path "data/traffic/config.json" ``` https://github.com/roboflow/supervision/assets/26109316/9d514c9e-2a61-418b-ae49-6ac1ad6ae5ac @@ -114,33 +117,33 @@ https://github.com/roboflow/supervision/assets/26109316/9d514c9e-2a61-418b-ae49- Script to run object detection on a video file using the Roboflow Inference model. - - `--zone_configuration_path`: Path to the zone configuration JSON file. - - `--source_video_path`: Path to the source video file. - - `--model_id`: Roboflow model ID. - - `--classes`: List of class IDs to track. If empty, all classes are tracked. - - `--confidence_threshold`: Confidence level for detections (`0` to `1`). Default is `0.3`. - - `--iou_threshold`: IOU threshold for non-max suppression. Default is `0.7`. +- `--zone_configuration_path`: Path to the zone configuration JSON file. +- `--source_video_path`: Path to the source video file. +- `--model_id`: Roboflow model ID. +- `--classes`: List of class IDs to track. If empty, all classes are tracked. +- `--confidence_threshold`: Confidence level for detections (`0` to `1`). Default is `0.3`. +- `--iou_threshold`: IOU threshold for non-max suppression. Default is `0.7`. ```bash python inference_file_example.py \ ---zone_configuration_path "data/checkout/config.json" \ ---source_video_path "data/checkout/video.mp4" \ ---model_id "yolov8x-640" \ ---classes 0 \ ---confidence_threshold 0.3 \ ---iou_threshold 0.7 + --zone_configuration_path "data/checkout/config.json" \ + --source_video_path "data/checkout/video.mp4" \ + --model_id "yolov8x-640" \ + --classes 0 \ + --confidence_threshold 0.3 \ + --iou_threshold 0.7 ``` https://github.com/roboflow/supervision/assets/26109316/d051cc8a-dd15-41d4-aa36-d38b86334c39 ```bash python inference_file_example.py \ ---zone_configuration_path "data/traffic/config.json" \ ---source_video_path "data/traffic/video.mp4" \ ---model_id "yolov8x-640" \ ---classes 2 5 6 7 \ ---confidence_threshold 0.3 \ ---iou_threshold 0.7 + --zone_configuration_path "data/traffic/config.json" \ + --source_video_path "data/traffic/video.mp4" \ + --model_id "yolov8x-640" \ + --classes 2 5 6 7 \ + --confidence_threshold 0.3 \ + --iou_threshold 0.7 ``` https://github.com/roboflow/supervision/assets/26109316/5ec896d7-4b39-4426-8979-11e71666878b @@ -149,31 +152,31 @@ https://github.com/roboflow/supervision/assets/26109316/5ec896d7-4b39-4426-8979- Script to run object detection on a video stream using the Roboflow Inference model. - - `--zone_configuration_path`: Path to the zone configuration JSON file. - - `--rtsp_url`: Complete RTSP URL for the video stream. - - `--model_id`: Roboflow model ID. - - `--classes`: List of class IDs to track. If empty, all classes are tracked. - - `--confidence_threshold`: Confidence level for detections (`0` to `1`). Default is `0.3`. - - `--iou_threshold`: IOU threshold for non-max suppression. Default is `0.7`. +- `--zone_configuration_path`: Path to the zone configuration JSON file. +- `--rtsp_url`: Complete RTSP URL for the video stream. +- `--model_id`: Roboflow model ID. +- `--classes`: List of class IDs to track. If empty, all classes are tracked. +- `--confidence_threshold`: Confidence level for detections (`0` to `1`). Default is `0.3`. +- `--iou_threshold`: IOU threshold for non-max suppression. Default is `0.7`. ```bash python inference_stream_example.py \ ---zone_configuration_path "data/checkout/config.json" \ ---rtsp_url "rtsp://localhost:8554/live0.stream" \ ---model_id "yolov8x-640" \ ---classes 0 \ ---confidence_threshold 0.3 \ ---iou_threshold 0.7 + --zone_configuration_path "data/checkout/config.json" \ + --rtsp_url "rtsp://localhost:8554/live0.stream" \ + --model_id "yolov8x-640" \ + --classes 0 \ + --confidence_threshold 0.3 \ + --iou_threshold 0.7 ``` ```bash python inference_stream_example.py \ ---zone_configuration_path "data/traffic/config.json" \ ---rtsp_url "rtsp://localhost:8554/live0.stream" \ ---model_id "yolov8x-640" \ ---classes 2 5 6 7 \ ---confidence_threshold 0.3 \ ---iou_threshold 0.7 + --zone_configuration_path "data/traffic/config.json" \ + --rtsp_url "rtsp://localhost:8554/live0.stream" \ + --model_id "yolov8x-640" \ + --classes 2 5 6 7 \ + --confidence_threshold 0.3 \ + --iou_threshold 0.7 ```
@@ -183,68 +186,68 @@ python inference_stream_example.py \ Script to run object detection on a video file using the Ultralytics YOLOv8 model. - - `--zone_configuration_path`: Path to the zone configuration JSON file. - - `--source_video_path`: Path to the source video file. - - `--weights`: Path to the model weights file. Default is `'yolov8s.pt'`. - - `--device`: Computation device (`'cpu'`, `'mps'` or `'cuda'`). Default is `'cpu'`. - - `--classes`: List of class IDs to track. If empty, all classes are tracked. - - `--confidence_threshold`: Confidence level for detections (`0` to `1`). Default is `0.3`. - - `--iou_threshold`: IOU threshold for non-max suppression. Default is `0.7`. +- `--zone_configuration_path`: Path to the zone configuration JSON file. +- `--source_video_path`: Path to the source video file. +- `--weights`: Path to the model weights file. Default is `'yolov8s.pt'`. +- `--device`: Computation device (`'cpu'`, `'mps'` or `'cuda'`). Default is `'cpu'`. +- `--classes`: List of class IDs to track. If empty, all classes are tracked. +- `--confidence_threshold`: Confidence level for detections (`0` to `1`). Default is `0.3`. +- `--iou_threshold`: IOU threshold for non-max suppression. Default is `0.7`. ```bash python ultralytics_file_example.py \ ---zone_configuration_path "data/checkout/config.json" \ ---source_video_path "data/checkout/video.mp4" \ ---weights "yolov8x.pt" \ ---device "cpu" \ ---classes 0 \ ---confidence_threshold 0.3 \ ---iou_threshold 0.7 + --zone_configuration_path "data/checkout/config.json" \ + --source_video_path "data/checkout/video.mp4" \ + --weights "yolov8x.pt" \ + --device "cpu" \ + --classes 0 \ + --confidence_threshold 0.3 \ + --iou_threshold 0.7 ``` ```bash python ultralytics_file_example.py \ ---zone_configuration_path "data/traffic/config.json" \ ---source_video_path "data/traffic/video.mp4" \ ---weights "yolov8x.pt" \ ---device "cpu" \ ---classes 2 5 6 7 \ ---confidence_threshold 0.3 \ ---iou_threshold 0.7 + --zone_configuration_path "data/traffic/config.json" \ + --source_video_path "data/traffic/video.mp4" \ + --weights "yolov8x.pt" \ + --device "cpu" \ + --classes 2 5 6 7 \ + --confidence_threshold 0.3 \ + --iou_threshold 0.7 ``` ### `ultralytics_stream_example` Script to run object detection on a video stream using the Ultralytics YOLOv8 model. - - `--zone_configuration_path`: Path to the zone configuration JSON file. - - `--rtsp_url`: Complete RTSP URL for the video stream. - - `--weights`: Path to the model weights file. Default is `'yolov8s.pt'`. - - `--device`: Computation device (`'cpu'`, `'mps'` or `'cuda'`). Default is `'cpu'`. - - `--classes`: List of class IDs to track. If empty, all classes are tracked. - - `--confidence_threshold`: Confidence level for detections (`0` to `1`). Default is `0.3`. - - `--iou_threshold`: IOU threshold for non-max suppression. Default is `0.7`. +- `--zone_configuration_path`: Path to the zone configuration JSON file. +- `--rtsp_url`: Complete RTSP URL for the video stream. +- `--weights`: Path to the model weights file. Default is `'yolov8s.pt'`. +- `--device`: Computation device (`'cpu'`, `'mps'` or `'cuda'`). Default is `'cpu'`. +- `--classes`: List of class IDs to track. If empty, all classes are tracked. +- `--confidence_threshold`: Confidence level for detections (`0` to `1`). Default is `0.3`. +- `--iou_threshold`: IOU threshold for non-max suppression. Default is `0.7`. ```bash python ultralytics_stream_example.py \ ---zone_configuration_path "data/checkout/config.json" \ ---rtsp_url "rtsp://localhost:8554/live0.stream" \ ---weights "yolov8x.pt" \ ---device "cpu" \ ---classes 0 \ ---confidence_threshold 0.3 \ ---iou_threshold 0.7 + --zone_configuration_path "data/checkout/config.json" \ + --rtsp_url "rtsp://localhost:8554/live0.stream" \ + --weights "yolov8x.pt" \ + --device "cpu" \ + --classes 0 \ + --confidence_threshold 0.3 \ + --iou_threshold 0.7 ``` ```bash python ultralytics_stream_example.py \ ---zone_configuration_path "data/traffic/config.json" \ ---rtsp_url "rtsp://localhost:8554/live0.stream" \ ---weights "yolov8x.pt" \ ---device "cpu" \ ---classes 2 5 6 7 \ ---confidence_threshold 0.3 \ ---iou_threshold 0.7 + --zone_configuration_path "data/traffic/config.json" \ + --rtsp_url "rtsp://localhost:8554/live0.stream" \ + --weights "yolov8x.pt" \ + --device "cpu" \ + --classes 2 5 6 7 \ + --confidence_threshold 0.3 \ + --iou_threshold 0.7 ```
@@ -254,11 +257,11 @@ python ultralytics_stream_example.py \ This demo integrates two main components, each with its own licensing: - ultralytics: The object detection model used in this demo, YOLOv8, is distributed - under the [AGPL-3.0 license](https://github.com/ultralytics/ultralytics/blob/main/LICENSE). - You can find more details about this license here. + under the [AGPL-3.0 license](https://github.com/ultralytics/ultralytics/blob/main/LICENSE). + You can find more details about this license here. - supervision: The analytics code that powers the zone-based analysis in this demo is - based on the Supervision library, which is licensed under the - [MIT license](https://github.com/roboflow/supervision/blob/develop/LICENSE.md). This - makes the Supervision part of the code fully open source and freely usable in your - projects. + based on the Supervision library, which is licensed under the + [MIT license](https://github.com/roboflow/supervision/blob/develop/LICENSE.md). This + makes the Supervision part of the code fully open source and freely usable in your + projects. diff --git a/examples/tracking/README.md b/examples/tracking/README.md index 5f258656..cb167712 100644 --- a/examples/tracking/README.md +++ b/examples/tracking/README.md @@ -9,93 +9,100 @@ detection and Supervision for tracking and annotation. - clone repository and navigate to example directory - ```bash - git clone https://github.com/roboflow/supervision.git - cd supervision/examples/tracking - ``` + ```bash + git clone https://github.com/roboflow/supervision.git + cd supervision/examples/tracking + ``` -- setup python environment and activate it [optional] +- setup python environment and activate it \[optional\] - ```bash - python3 -m venv venv - source venv/bin/activate - ``` + ```bash + python3 -m venv venv + source venv/bin/activate + ``` - install required dependencies - ```bash - pip install -r requirements.txt - ``` + ```bash + pip install -r requirements.txt + ``` ## 🛠️ script arguments - ultralytics - - `--source_weights_path`: Required. Specifies the path to the YOLO model's weights - file, which is essential for the object detection process. This file contains the data - that the model uses to identify objects in the video. + - `--source_weights_path`: Required. Specifies the path to the YOLO model's weights + file, which is essential for the object detection process. This file contains the data + that the model uses to identify objects in the video. - - `--source_video_path`: Required. The path to the source video file to be processed. - This is the video on which object detection and annotation will be performed. - - `--target_video_path`: Required. The path where the processed video, with annotations - added, will be saved. This is your output video file. - - `--confidence_threshold` (optional): Sets the confidence level at which the model - identifies objects in the video. Default is `0.3`. A higher threshold makes the model - more selective, while a lower threshold makes it more inclusive in identifying objects. - - `--iou_threshold` (optional): Specifies the IOU (Intersection Over Union) threshold - for the model, defaulting to `0.7`. This parameter helps in differentiating between - distinct objects, especially in crowded scenes. + - `--source_video_path`: Required. The path to the source video file to be processed. + This is the video on which object detection and annotation will be performed. + + - `--target_video_path`: Required. The path where the processed video, with annotations + added, will be saved. This is your output video file. + + - `--confidence_threshold` (optional): Sets the confidence level at which the model + identifies objects in the video. Default is `0.3`. A higher threshold makes the model + more selective, while a lower threshold makes it more inclusive in identifying objects. + + - `--iou_threshold` (optional): Specifies the IOU (Intersection Over Union) threshold + for the model, defaulting to `0.7`. This parameter helps in differentiating between + distinct objects, especially in crowded scenes. - inference - - `--roboflow_api_key` (optional): The API key for Roboflow services. If not provided - directly, the script tries to fetch it from the `ROBOFLOW_API_KEY` environment - variable. Follow [this guide](https://docs.roboflow.com/api-reference/authentication#retrieve-an-api-key) - to acquire your `API KEY`. - - `--model_id` (optional): Designates the Roboflow model ID to be used. The default - value is `"yolov8x-1280"`. + - `--roboflow_api_key` (optional): The API key for Roboflow services. If not provided + directly, the script tries to fetch it from the `ROBOFLOW_API_KEY` environment + variable. Follow [this guide](https://docs.roboflow.com/api-reference/authentication#retrieve-an-api-key) + to acquire your `API KEY`. - - `--source_video_path`: Required. The path to the source video file to be processed. - This is the video on which object detection and annotation will be performed. - - `--target_video_path`: Required. The path where the processed video, with annotations - added, will be saved. This is your output video file. - - `--confidence_threshold` (optional): Sets the confidence level at which the model - identifies objects in the video. Default is `0.3`. A higher threshold makes the model - more selective, while a lower threshold makes it more inclusive in identifying objects. - - `--iou_threshold` (optional): Specifies the IOU (Intersection Over Union) threshold - for the model, defaulting to `0.7`. This parameter helps in differentiating between - distinct objects, especially in crowded scenes. + - `--model_id` (optional): Designates the Roboflow model ID to be used. The default + value is `"yolov8x-1280"`. + + - `--source_video_path`: Required. The path to the source video file to be processed. + This is the video on which object detection and annotation will be performed. + + - `--target_video_path`: Required. The path where the processed video, with annotations + added, will be saved. This is your output video file. + + - `--confidence_threshold` (optional): Sets the confidence level at which the model + identifies objects in the video. Default is `0.3`. A higher threshold makes the model + more selective, while a lower threshold makes it more inclusive in identifying objects. + + - `--iou_threshold` (optional): Specifies the IOU (Intersection Over Union) threshold + for the model, defaulting to `0.7`. This parameter helps in differentiating between + distinct objects, especially in crowded scenes. ## ⚙️ run - inference - ```bash - python inference_example.py \ - --roboflow_api_key \ - --source_video_path input.mp4 \ - --target_video_path tracking_result.mp4 - ``` + ```bash + python inference_example.py \ + --roboflow_api_key \ + --source_video_path input.mp4 \ + --target_video_path tracking_result.mp4 + ``` - ultralytics - ```bash - python ultralytics_example.py \ - --source_weights_path yolov8s.pt \ - --source_video_path input.mp4 \ - --target_video_path tracking_result.mp4 - ``` + ```bash + python ultralytics_example.py \ + --source_weights_path yolov8s.pt \ + --source_video_path input.mp4 \ + --target_video_path tracking_result.mp4 + ``` ## © license This demo integrates two main components, each with its own licensing: - ultralytics: The object detection model used in this demo, YOLOv8, is distributed - under the [AGPL-3.0 license](https://github.com/ultralytics/ultralytics/blob/main/LICENSE). - You can find more details about this license here. + under the [AGPL-3.0 license](https://github.com/ultralytics/ultralytics/blob/main/LICENSE). + You can find more details about this license here. - supervision: The analytics code that powers the zone-based analysis in this demo is - based on the Supervision library, which is licensed under the - [MIT license](https://github.com/roboflow/supervision/blob/develop/LICENSE.md). This - makes the Supervision part of the code fully open source and freely usable in your - projects. + based on the Supervision library, which is licensed under the + [MIT license](https://github.com/roboflow/supervision/blob/develop/LICENSE.md). This + makes the Supervision part of the code fully open source and freely usable in your + projects. diff --git a/examples/traffic_analysis/README.md b/examples/traffic_analysis/README.md index 3c882805..feac8d91 100644 --- a/examples/traffic_analysis/README.md +++ b/examples/traffic_analysis/README.md @@ -12,105 +12,112 @@ https://github.com/roboflow/supervision/assets/26109316/c9436828-9fbf-4c25-ae8c- - clone repository and navigate to example directory - ```bash - git clone https://github.com/roboflow/supervision.git - cd supervision/examples/traffic_analysis - ``` + ```bash + git clone https://github.com/roboflow/supervision.git + cd supervision/examples/traffic_analysis + ``` -- setup python environment and activate it [optional] +- setup python environment and activate it \[optional\] - ```bash - python3 -m venv venv - source venv/bin/activate - ``` + ```bash + python3 -m venv venv + source venv/bin/activate + ``` - install required dependencies - ```bash - pip install -r requirements.txt - ``` + ```bash + pip install -r requirements.txt + ``` - download `traffic_analysis.pt` and `traffic_analysis.mov` files - ```bash - ./setup.sh - ``` + ```bash + ./setup.sh + ``` ## 🛠️ script arguments - ultralytics - - `--source_weights_path`: Required. Specifies the path to the YOLO model's weights - file, which is essential for the object detection process. This file contains the - data that the model uses to identify objects in the video. + - `--source_weights_path`: Required. Specifies the path to the YOLO model's weights + file, which is essential for the object detection process. This file contains the + data that the model uses to identify objects in the video. - - `--source_video_path`: Required. The path to the source video file that will be - analyzed. This is the input video on which traffic flow analysis will be performed. - - `--target_video_path` (optional): The path to save the output video with - annotations. If not specified, the processed video will be displayed in real-time - without being saved. - - `--confidence_threshold` (optional): Sets the confidence threshold for the YOLO - model to filter detections. Default is `0.3`. This determines how confident the - model should be to recognize an object in the video. - - `--iou_threshold` (optional): Specifies the IOU (Intersection Over Union) threshold - for the model. Default is 0.7. This value is used to manage object detection - accuracy, particularly in distinguishing between different objects. + - `--source_video_path`: Required. The path to the source video file that will be + analyzed. This is the input video on which traffic flow analysis will be performed. + + - `--target_video_path` (optional): The path to save the output video with + annotations. If not specified, the processed video will be displayed in real-time + without being saved. + + - `--confidence_threshold` (optional): Sets the confidence threshold for the YOLO + model to filter detections. Default is `0.3`. This determines how confident the + model should be to recognize an object in the video. + + - `--iou_threshold` (optional): Specifies the IOU (Intersection Over Union) threshold + for the model. Default is 0.7. This value is used to manage object detection + accuracy, particularly in distinguishing between different objects. - inference - - `--roboflow_api_key` (optional): The API key for Roboflow services. If not provided - directly, the script tries to fetch it from the `ROBOFLOW_API_KEY` environment - variable. Follow [this guide](https://docs.roboflow.com/api-reference/authentication#retrieve-an-api-key) - to acquire your `API KEY`. - - `--model_id` (optional): Designates the Roboflow model ID to be used. The default - value is `"vehicle-count-in-drone-video/6"`. + - `--roboflow_api_key` (optional): The API key for Roboflow services. If not provided + directly, the script tries to fetch it from the `ROBOFLOW_API_KEY` environment + variable. Follow [this guide](https://docs.roboflow.com/api-reference/authentication#retrieve-an-api-key) + to acquire your `API KEY`. - - `--source_video_path`: Required. The path to the source video file that will be - analyzed. This is the input video on which traffic flow analysis will be performed. - - `--target_video_path` (optional): The path to save the output video with - annotations. If not specified, the processed video will be displayed in real-time - without being saved. - - `--confidence_threshold` (optional): Sets the confidence threshold for the YOLO - model to filter detections. Default is `0.3`. This determines how confident the - model should be to recognize an object in the video. - - `--iou_threshold` (optional): Specifies the IOU (Intersection Over Union) threshold - for the model. Default is 0.7. This value is used to manage object detection - accuracy, particularly in distinguishing between different objects. + - `--model_id` (optional): Designates the Roboflow model ID to be used. The default + value is `"vehicle-count-in-drone-video/6"`. + + - `--source_video_path`: Required. The path to the source video file that will be + analyzed. This is the input video on which traffic flow analysis will be performed. + + - `--target_video_path` (optional): The path to save the output video with + annotations. If not specified, the processed video will be displayed in real-time + without being saved. + + - `--confidence_threshold` (optional): Sets the confidence threshold for the YOLO + model to filter detections. Default is `0.3`. This determines how confident the + model should be to recognize an object in the video. + + - `--iou_threshold` (optional): Specifies the IOU (Intersection Over Union) threshold + for the model. Default is 0.7. This value is used to manage object detection + accuracy, particularly in distinguishing between different objects. ## ⚙️ run - ultralytics - ```bash - python ultralytics_example.py \ - --source_weights_path data/traffic_analysis.pt \ - --source_video_path data/traffic_analysis.mov \ - --confidence_threshold 0.3 \ - --iou_threshold 0.5 \ - --target_video_path data/traffic_analysis_result.mov - ``` + ```bash + python ultralytics_example.py \ + --source_weights_path data/traffic_analysis.pt \ + --source_video_path data/traffic_analysis.mov \ + --confidence_threshold 0.3 \ + --iou_threshold 0.5 \ + --target_video_path data/traffic_analysis_result.mov + ``` - inference - ```bash - python inference_example.py \ - --roboflow_api_key \ - --source_video_path data/traffic_analysis.mov \ - --confidence_threshold 0.3 \ - --iou_threshold 0.5 \ - --target_video_path data/traffic_analysis_result.mov - ``` + ```bash + python inference_example.py \ + --roboflow_api_key \ + --source_video_path data/traffic_analysis.mov \ + --confidence_threshold 0.3 \ + --iou_threshold 0.5 \ + --target_video_path data/traffic_analysis_result.mov + ``` ## © license This demo integrates two main components, each with its own licensing: - ultralytics: The object detection model used in this demo, YOLOv8, is distributed - under the [AGPL-3.0 license](https://github.com/ultralytics/ultralytics/blob/main/LICENSE). - You can find more details about this license here. + under the [AGPL-3.0 license](https://github.com/ultralytics/ultralytics/blob/main/LICENSE). + You can find more details about this license here. - supervision: The analytics code that powers the zone-based analysis in this demo is - based on the Supervision library, which is licensed under the - [MIT license](https://github.com/roboflow/supervision/blob/develop/LICENSE.md). This - makes the Supervision part of the code fully open source and freely usable in your - projects. + based on the Supervision library, which is licensed under the + [MIT license](https://github.com/roboflow/supervision/blob/develop/LICENSE.md). This + makes the Supervision part of the code fully open source and freely usable in your + projects. diff --git a/pyproject.toml b/pyproject.toml index 6be83783..9f443b62 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -236,6 +236,12 @@ skip-magic-trailing-comma = false # Like Black, automatically detect the appropriate line ending. line-ending = "auto" +[tool.codespell] +skip = "*.ipynb,poetry.lock" +count = true +quiet-level = 3 +ignore-words-list = "STrack,sTrack,strack" + [tool.setuptools] include-package-data = false diff --git a/release_process.md b/release_process.md index 3130fc9c..effaa8dd 100644 --- a/release_process.md +++ b/release_process.md @@ -6,19 +6,19 @@ It assumes you already have the code changes, as well as a draft of the release 1. Make sure you have all required changes were merged into `develop`. 2. Create and merge a PR, merging `develop` into `main`, containing: - - A commit that updates the project version in `pyproject.toml`. - - All changes made during the release. + - A commit that updates the project version in `pyproject.toml`. + - All changes made during the release. 3. Tag the commit with the new supervision version. - - make sure to pull from `main` ! - - Verify that the latest merge commits exists. `git log`. - - Run `git tag x.y.z`, with your version - - Check with `git log`. - - Run `git push origin --tags` - - Upon pushing the tag, the [PyPi](https://pypi.org/project/supervision/) should update to the new version. Check this! + - make sure to pull from `main` ! + - Verify that the latest merge commits exists. `git log`. + - Run `git tag x.y.z`, with your version + - Check with `git log`. + - Run `git push origin --tags` + - Upon pushing the tag, the [PyPi](https://pypi.org/project/supervision/) should update to the new version. Check this! 4. Open and merge a PR, merging `main` into `develop`. 5. Update the docs by running the [Supervision Release Documentation Workflow 📚](https://github.com/roboflow/supervision/actions/workflows/publish-release-docs.yml) workflow from GitHub. - - Select the `main` branch from the dropdown. + - Select the `main` branch from the dropdown. 6. Create a release on GitHub. - - Go to releases - - Assign the release notes to the tag created in step 3. - - Publish the release. + - Go to releases + - Assign the release notes to the tag created in step 3. + - Publish the release. diff --git a/supervision/detection/line_zone.py b/supervision/detection/line_zone.py index 2131c14c..93c4150d 100644 --- a/supervision/detection/line_zone.py +++ b/supervision/detection/line_zone.py @@ -369,7 +369,7 @@ class LineZoneAnnotator: label is rectangular. Returns: - Tuple[int, int]: xy, pont in an image where the label will be placed. + Tuple[int, int]: xy, point in an image where the label will be placed. """ line_angle = self._get_line_angle(line_zone) diff --git a/test/dataset/formats/test_coco.py b/test/dataset/formats/test_coco.py index b023a260..3a68894a 100644 --- a/test/dataset/formats/test_coco.py +++ b/test/dataset/formats/test_coco.py @@ -308,7 +308,7 @@ def test_group_coco_annotations_by_image_id( ), ), DoesNotRaise(), - ), # two image annotations with mask, one mask as polygon ans second as RLE + ), # two image annotations with mask, one mask as polygon and second as RLE ( [ mock_coco_annotation( From 103669bf0fe5631d15d39dad067c73d2b18e7a7e Mon Sep 17 00:00:00 2001 From: Onuralp SEZER Date: Fri, 27 Sep 2024 15:19:00 +0300 Subject: [PATCH 52/63] =?UTF-8?q?docs:=20=F0=9F=93=9D=20minor=20adjustment?= =?UTF-8?q?s=20on=20readme=20and=20changelog?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Onuralp SEZER --- README.md | 39 ++++++++++++++++++++++++++++++--------- docs/changelog.md | 16 ++++++++++++---- docs/contributing.md | 2 +- 3 files changed, 43 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index 3c57b263..9a0cc305 100644 --- a/README.md +++ b/README.md @@ -102,7 +102,9 @@ image = cv2.imread(...) detections = sv.Detections(...) box_annotator = sv.BoxAnnotator() -annotated_frame = box_annotator.annotate(scene=image.copy(), detections=detections) +annotated_frame = box_annotator.annotate( + scene=image.copy(), + detections=detections) ``` https://github.com/roboflow/supervision/assets/26109316/691e219c-0565-4403-9218-ab5644f39bce @@ -137,14 +139,20 @@ for path, image, annotation in ds: ```python dataset = sv.DetectionDataset.from_yolo( - images_directory_path=..., annotations_directory_path=..., data_yaml_path=... + images_directory_path=..., + annotations_directory_path=..., + data_yaml_path=... ) dataset = sv.DetectionDataset.from_pascal_voc( - images_directory_path=..., annotations_directory_path=... + images_directory_path=..., + annotations_directory_path=... ) - dataset = sv.DetectionDataset.from_coco(images_directory_path=..., annotations_path=...) + dataset = sv.DetectionDataset.from_coco( + images_directory_path=..., + annotations_path=... + ) ``` - split @@ -183,20 +191,33 @@ for path, image, annotation in ds: ```python dataset.as_yolo( - images_directory_path=..., annotations_directory_path=..., data_yaml_path=... + images_directory_path=..., + annotations_directory_path=..., + data_yaml_path=... ) - dataset.as_pascal_voc(images_directory_path=..., annotations_directory_path=...) + dataset.as_pascal_voc( + images_directory_path=..., + annotations_directory_path=... + ) - dataset.as_coco(images_directory_path=..., annotations_path=...) + dataset.as_coco( + images_directory_path=..., + annotations_path=... + ) ``` - convert ```python sv.DetectionDataset.from_yolo( - images_directory_path=..., annotations_directory_path=..., data_yaml_path=... - ).as_pascal_voc(images_directory_path=..., annotations_directory_path=...) + images_directory_path=..., + annotations_directory_path=..., + data_yaml_path=... + ).as_pascal_voc( + images_directory_path=..., + annotations_directory_path=... + ) ``` diff --git a/docs/changelog.md b/docs/changelog.md index c04ef393..c9b55991 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -82,8 +82,10 @@ detections = sv.Detections.from_transformers( ```python import supervision as sv -from segment_anything import sam_model_registry, SamAutomaticMaskGenerator - +from segment_anything import ( + sam_model_registry, + SamAutomaticMaskGenerator +) sam_model_reg = sam_model_registry[MODEL_TYPE] sam = sam_model_reg(checkpoint=CHECKPOINT_PATH).to(device=DEVICE) mask_generator = SamAutomaticMaskGenerator(sam) @@ -294,8 +296,14 @@ import supervision as sv image = ... key_points = sv.KeyPoints(...) -edge_annotator = sv.EdgeAnnotator(color=sv.Color.GREEN, thickness=5) -annotated_frame = edge_annotator.annotate(scene=image.copy(), key_points=key_points) +edge_annotator = sv.EdgeAnnotator( + color=sv.Color.GREEN, + thickness=5 +) +annotated_frame = edge_annotator.annotate( + scene=image.copy(), + key_points=key_points +) ``` - Added [#1147](https://github.com/roboflow/supervision/pull/1147): [`sv.KeyPoints.from_inference`](https://supervision.roboflow.com/0.21.0/keypoint/core/#supervision.keypoint.core.KeyPoints.from_inference) allowing to create [`sv.KeyPoints`](https://supervision.roboflow.com/0.21.0/keypoint/core/#supervision.keypoint.core.KeyPoints) from [Inference](https://github.com/roboflow/inference) result. diff --git a/docs/contributing.md b/docs/contributing.md index 4f79db8a..ea38c9bf 100644 --- a/docs/contributing.md +++ b/docs/contributing.md @@ -1 +1 @@ ---8\<-- "CONTRIBUTING.md" +--8<-- "CONTRIBUTING.md" From 31a3cca5d5990493124b99417ba6982937efa167 Mon Sep 17 00:00:00 2001 From: Onuralp SEZER Date: Thu, 26 Sep 2024 20:52:59 +0300 Subject: [PATCH 53/63] =?UTF-8?q?ci:=20=F0=9F=91=B7=20trusted=20publisher?= =?UTF-8?q?=20configuration=20added?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Onuralp SEZER --- .github/workflows/publish-test.yml | 11 ++++++----- .github/workflows/publish.yml | 11 ++++++----- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/.github/workflows/publish-test.yml b/.github/workflows/publish-test.yml index 973c30c9..567a6aba 100644 --- a/.github/workflows/publish-test.yml +++ b/.github/workflows/publish-test.yml @@ -1,4 +1,4 @@ -name: Supervision Test Releases to PyPi +name: Publish Supervision Pre-Releases to PyPI and TestPyPI on: push: tags: @@ -9,9 +9,11 @@ on: workflow_dispatch: jobs: - build-n-publish: + build-and-publish-pre-release-pypi: name: Build and publish to PyPI runs-on: ubuntu-latest + permissions: + id-token: write strategy: matrix: python-version: ["3.10"] @@ -30,12 +32,11 @@ jobs: python -m pip install --upgrade build twine python -m build twine check --strict dist/* + - name: 🚀 Publish to PyPi uses: pypa/gh-action-pypi-publish@release/v1.10 - with: - password: ${{ secrets.PYPI_API_TOKEN }} + - name: 🚀 Publish to Test-PyPi uses: pypa/gh-action-pypi-publish@release/v1.10 with: repository-url: https://test.pypi.org/legacy/ - password: ${{ secrets.TEST_PYPI_API_TOKEN }} diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index aee59eba..f9a14d22 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -1,4 +1,4 @@ -name: Supervision Releases to PyPi +name: Publish Supervision Releases to PyPI and TestPyPI on: push: tags: @@ -7,8 +7,10 @@ on: workflow_dispatch: jobs: - build: + build-and-publish-pre-release: runs-on: ubuntu-latest + permissions: + id-token: write strategy: matrix: python-version: ["3.10"] @@ -27,12 +29,11 @@ jobs: python -m pip install --upgrade build twine python -m build twine check --strict dist/* + - name: 🚀 Publish to PyPi uses: pypa/gh-action-pypi-publish@release/v1.10 - with: - password: ${{ secrets.PYPI_API_TOKEN }} + - name: 🚀 Publish to Test-PyPi uses: pypa/gh-action-pypi-publish@release/v1.10 with: repository-url: https://test.pypi.org/legacy/ - password: ${{ secrets.TEST_PYPI_API_TOKEN }} From eebd57a73194085b94d7ffee57289b859c64d699 Mon Sep 17 00:00:00 2001 From: LinasKo Date: Sun, 29 Sep 2024 12:47:07 +0300 Subject: [PATCH 54/63] Add per-class counting to LineZone, create LineZoneAnnotatorMulticlass --- docs/detection/tools/line_zone.md | 6 + supervision/__init__.py | 6 +- supervision/detection/line_zone.py | 249 +++++++++++++++++++++++++++-- 3 files changed, 246 insertions(+), 15 deletions(-) diff --git a/docs/detection/tools/line_zone.md b/docs/detection/tools/line_zone.md index 8d13822c..8bca3cfd 100644 --- a/docs/detection/tools/line_zone.md +++ b/docs/detection/tools/line_zone.md @@ -13,3 +13,9 @@ comments: true :::supervision.detection.line_zone.LineZoneAnnotator + +
+

LineZoneAnnotatorMulticlass

+
+ +:::supervision.detection.line_zone.LineZoneAnnotatorMulticlass diff --git a/supervision/__init__.py b/supervision/__init__.py index 0b21fe8b..746b2f67 100644 --- a/supervision/__init__.py +++ b/supervision/__init__.py @@ -40,7 +40,11 @@ from supervision.dataset.core import ( ) from supervision.dataset.utils import mask_to_rle, rle_to_mask from supervision.detection.core import Detections -from supervision.detection.line_zone import LineZone, LineZoneAnnotator +from supervision.detection.line_zone import ( + LineZone, + LineZoneAnnotator, + LineZoneAnnotatorMulticlass, +) from supervision.detection.lmm import LMM from supervision.detection.overlap_filter import ( OverlapFilter, diff --git a/supervision/detection/line_zone.py b/supervision/detection/line_zone.py index 93c4150d..65aaa977 100644 --- a/supervision/detection/line_zone.py +++ b/supervision/detection/line_zone.py @@ -1,19 +1,23 @@ import math import warnings +from collections import Counter from functools import lru_cache -from typing import Any, Dict, Iterable, Optional, Tuple +from typing import Any, Dict, Iterable, List, Literal, Optional, Tuple import cv2 import numpy as np +from supervision.config import CLASS_NAME_DATA_FIELD from supervision.detection.core import Detections from supervision.detection.utils import cross_product from supervision.draw.color import Color -from supervision.draw.utils import draw_text -from supervision.geometry.core import Point, Position, Vector +from supervision.draw.utils import draw_rectangle, draw_text +from supervision.geometry.core import Point, Position, Rect, Vector from supervision.utils.image import overlay_image from supervision.utils.internal import SupervisionWarnings +TEXT_MARGIN = 10 + class LineZone: """ @@ -84,11 +88,44 @@ class LineZone: self.vector = Vector(start=start, end=end) self.limits = self.calculate_region_of_interest_limits(vector=self.vector) self.tracker_state: Dict[str, bool] = {} - self.in_count: int = 0 - self.out_count: int = 0 + self._in_count_per_class: Counter = Counter() + self._out_count_per_class: Counter = Counter() self.triggering_anchors = triggering_anchors if not list(self.triggering_anchors): raise ValueError("Triggering anchors cannot be empty.") + self.class_id_to_name: Dict[int, str] = {} + + @property + def in_count(self) -> int: + """ + Number of objects that have crossed the line from + outside to inside. + """ + return sum(self._in_count_per_class.values()) + + @property + def out_count(self) -> int: + """ + Number of objects that have crossed the line from + inside to outside. + """ + return sum(self._out_count_per_class.values()) + + @property + def in_count_per_class(self) -> Dict[int, int]: + """ + Number of objects of each class that have crossed + the line from outside to inside. + """ + return dict(self._in_count_per_class) + + @property + def out_count_per_class(self) -> Dict[int, int]: + """ + Number of objects of each class that have crossed the line + from inside to outside. + """ + return dict(self._out_count_per_class) @staticmethod def calculate_region_of_interest_limits(vector: Vector) -> Tuple[Vector, Vector]: @@ -173,7 +210,22 @@ class LineZone: has_any_left_trigger = np.any(triggers, axis=0) has_any_right_trigger = np.any(~triggers, axis=0) is_uniformly_triggered = ~(has_any_left_trigger & has_any_right_trigger) - for i, tracker_id in enumerate(detections.tracker_id): + + class_ids = ( + list(detections.class_id) + if detections.class_id is not None + else [None] * len(detections) + ) + tracker_ids = list(detections.tracker_id) + + if CLASS_NAME_DATA_FIELD in detections.data: + class_names = detections.data[CLASS_NAME_DATA_FIELD] + for class_id, class_name in zip(class_ids, class_names): + if class_id is None: + class_name = "No class" + self.class_id_to_name[class_id] = class_name + + for i, (class_ids, tracker_id) in enumerate(zip(class_ids, tracker_ids)): if not in_limits[i]: continue @@ -190,10 +242,10 @@ class LineZone: self.tracker_state[tracker_id] = tracker_state if tracker_state: - self.in_count += 1 + self._in_count_per_class[class_ids] += 1 crossed_in[i] = True else: - self.out_count += 1 + self._out_count_per_class[class_ids] += 1 crossed_out[i] = True return crossed_in, crossed_out @@ -265,7 +317,7 @@ class LineZoneAnnotator: that will be used to draw the line. Returns: - np.ndarray: The image with the line drawn on it. + (np.ndarray): The image with the line drawn on it. """ line_start = line_counter.vector.start.as_xy_int_tuple() @@ -332,7 +384,7 @@ class LineZoneAnnotator: line_zone (LineZone): The line zone object. Returns: - float: Line counter angle, in degrees. + (float): Line counter angle, in degrees. """ start_point = line_zone.vector.start.as_xy_int_tuple() end_point = line_zone.vector.end.as_xy_int_tuple() @@ -369,7 +421,7 @@ class LineZoneAnnotator: label is rectangular. Returns: - Tuple[int, int]: xy, point in an image where the label will be placed. + (Tuple[int, int]): xy, point in an image where the label will be placed. """ line_angle = self._get_line_angle(line_zone) @@ -432,7 +484,7 @@ class LineZoneAnnotator: or out count (below line). Returns: - np.ndarray: The scene with the label drawn on it. + (np.ndarray): The scene with the label drawn on it. """ _, text_height = cv2.getTextSize( text, cv2.FONT_HERSHEY_SIMPLEX, self.text_scale, self.text_thickness @@ -476,7 +528,7 @@ class LineZoneAnnotator: or out count (below line). Returns: - np.ndarray: The scene with the label drawn on it. + (np.ndarray): The scene with the label drawn on it. """ line_angle_degrees = self._get_line_angle(line_zone) @@ -535,7 +587,7 @@ class LineZoneAnnotator: line_angle_degrees (float): The angle of the line in degrees. Returns: - np.ndarray: The label of shape (H, W, 4), in BGRA format. + (np.ndarray): The label of shape (H, W, 4), in BGRA format. """ text_width, text_height = cv2.getTextSize( text, cv2.FONT_HERSHEY_SIMPLEX, text_scale, text_thickness @@ -580,3 +632,172 @@ class LineZoneAnnotator: annotation = cv2.warpAffine(annotation, rotation_matrix, annotation_shape) return annotation + + +class LineZoneAnnotatorMulticlass: + def __init__( + self, + *, + table_position: Literal[ + Position.TOP_LEFT, + Position.TOP_RIGHT, + Position.BOTTOM_LEFT, + Position.BOTTOM_RIGHT, + ] = Position.TOP_RIGHT, + table_color: Color = Color.WHITE, + table_margin: int = 10, + table_padding: int = 10, + table_max_width: int = 400, + text_color: Color = Color.BLACK, + text_scale: float = 0.75, + text_thickness: int = 1, + force_draw_class_ids: bool = False, + ): + """ + Draw a table showing how many items of each class crossed each line. + + Args: + table_position (Position): The position of the table. + table_color (Color): The color of the table. + table_margin (int): The margin of the table from the image border. + table_padding (int): The padding of the table. + table_max_width (int): The maximum width of the table. + text_color (Color): The color of the text. + text_scale (float): The scale of the text. + text_thickness (int): The thickness of the text. + force_draw_class_ids (bool): Instead of writing the class names, + on the table, write the class IDs. E.g. instead of `person: 6`, + write `0: 6`. + """ + + if table_position not in [ + Position.TOP_LEFT, + Position.TOP_RIGHT, + Position.BOTTOM_LEFT, + Position.BOTTOM_RIGHT, + ]: + raise ValueError( + "Invalid table position. Supported values are:" + " TOP_LEFT, TOP_RIGHT, BOTTOM_LEFT, BOTTOM_RIGHT." + ) + + self.table_position = table_position + self.table_color = table_color + self.table_margin = table_margin + self.table_padding = table_padding + self.table_max_width = table_max_width + self.text_color = text_color + self.text_scale = text_scale + self.text_thickness = text_thickness + self.force_draw_class_ids = force_draw_class_ids + + def annotate( + self, + frame: np.ndarray, + line_zones: List[LineZone], + line_zone_labels: Optional[List[str]] = None, + ) -> np.ndarray: + """ + Draw a table on the frame, showing how many items of each class + crossed each line. + + Args: + frame (np.ndarray): The image on which the table will be drawn. + line_zones (List[LineZone]): The line zones used to count + the objects. + line_zone_labels (Optional[List[str]]): The labels for each line + zone. If not specified, the labels will be `Line 1:`, + `Line 2:`, etc. + + Returns: + (np.ndarray): The image with the table drawn on it. + """ + if line_zone_labels is None: + line_zone_labels = [f"Line {i + 1}:" for i in range(len(line_zones))] + if len(line_zones) != len(line_zone_labels): + raise ValueError("The number of line zones and their labels must match.") + + text = "Line Crossings:\n" + for line_zone, line_zone_label in zip(line_zones, line_zone_labels): + text += f"{line_zone_label}\n" + class_id_to_name = line_zone.class_id_to_name + + if len(line_zone.in_count_per_class) > 0: + text += " In:\n" + for class_id, count in line_zone.in_count_per_class.items(): + if not self.force_draw_class_ids and class_id in class_id_to_name: + class_name = class_id_to_name[class_id] + else: + class_name = str(class_id) + text += f" {class_name}: {count}\n" + + if len(line_zone.out_count_per_class) > 0: + text += " Out:\n" + for class_id, count in line_zone.out_count_per_class.items(): + if class_id in class_id_to_name and not self.force_draw_class_ids: + class_name = class_id_to_name[class_id] + else: + class_name = str(class_id) + text += f" {class_name}: {count}\n" + + table_width = 0 + table_height = 0 + text_lines = text.split("\n") + for line in text_lines: + text_width, text_height = cv2.getTextSize( + line, cv2.FONT_HERSHEY_SIMPLEX, self.text_scale, self.text_thickness + )[0] + text_height += TEXT_MARGIN + table_width = max(table_width, text_width) + table_height += text_height + table_width += 2 * self.table_padding + table_height += 2 * self.table_padding + + table_max_height = frame.shape[0] - 2 * self.table_margin + table_height = min(table_height, table_max_height) + table_width = min(table_width, self.table_max_width) + + if self.table_position == Position.TOP_LEFT: + table_x1 = self.table_margin + table_y1 = self.table_margin + elif self.table_position == Position.TOP_RIGHT: + table_x1 = frame.shape[1] - table_width - self.table_margin + table_y1 = self.table_margin + elif self.table_position == Position.BOTTOM_LEFT: + table_x1 = self.table_margin + table_y1 = frame.shape[0] - table_height - self.table_margin + elif self.table_position == Position.BOTTOM_RIGHT: + table_x1 = frame.shape[1] - table_width - self.table_margin + table_y1 = frame.shape[0] - table_height - self.table_margin + + table_rect = Rect( + x=table_x1, y=table_y1, width=table_width, height=table_height + ) + + frame = draw_rectangle( + scene=frame, + rect=table_rect, + color=self.table_color, + thickness=-1, + ) + + for i, line in enumerate(text.split("\n")): + _, text_height = cv2.getTextSize( + line, cv2.FONT_HERSHEY_SIMPLEX, self.text_scale, self.text_thickness + )[0] + text_height += TEXT_MARGIN + anchor_x = table_x1 + self.table_padding + anchor_y = table_y1 + self.table_padding + (i + 1) * text_height + + cv2.putText( + img=frame, + text=line, + org=(anchor_x, anchor_y), + fontFace=cv2.FONT_HERSHEY_SIMPLEX, + fontScale=self.text_scale, + color=self.text_color.as_bgr(), + thickness=self.text_thickness, + lineType=cv2.LINE_AA, + ) + + return frame From 1d7786808075a800f62c5dc4b3933948dc1a7990 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 30 Sep 2024 00:08:42 +0000 Subject: [PATCH 55/63] :arrow_up: Bump mkdocs-material from 9.5.38 to 9.5.39 Bumps [mkdocs-material](https://github.com/squidfunk/mkdocs-material) from 9.5.38 to 9.5.39. - [Release notes](https://github.com/squidfunk/mkdocs-material/releases) - [Changelog](https://github.com/squidfunk/mkdocs-material/blob/master/CHANGELOG) - [Commits](https://github.com/squidfunk/mkdocs-material/compare/9.5.38...9.5.39) --- updated-dependencies: - dependency-name: mkdocs-material dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- poetry.lock | 26 ++++---------------------- 1 file changed, 4 insertions(+), 22 deletions(-) diff --git a/poetry.lock b/poetry.lock index 1fb50f7b..46f05d25 100644 --- a/poetry.lock +++ b/poetry.lock @@ -2252,13 +2252,13 @@ pygments = ">2.12.0" [[package]] name = "mkdocs-material" -version = "9.5.38" +version = "9.5.39" description = "Documentation that simply works" optional = false python-versions = ">=3.8" files = [ - {file = "mkdocs_material-9.5.38-py3-none-any.whl", hash = "sha256:d4779051d52ba9f1e7e344b34de95449c7c366c212b388e4a2db9a3db043c228"}, - {file = "mkdocs_material-9.5.38.tar.gz", hash = "sha256:1843c5171ad6b489550aeaf7358e5b7128cc03ddcf0fb4d91d19aa1e691a63b8"}, + {file = "mkdocs_material-9.5.39-py3-none-any.whl", hash = "sha256:0f2f68c8db89523cb4a59705cd01b4acd62b2f71218ccb67e1e004e560410d2b"}, + {file = "mkdocs_material-9.5.39.tar.gz", hash = "sha256:25faa06142afa38549d2b781d475a86fb61de93189f532b88e69bf11e5e5c3be"}, ] [package.dependencies] @@ -2678,8 +2678,8 @@ numpy = [ {version = ">=1.17.3", markers = "(platform_system != \"Darwin\" and platform_system != \"Linux\") and python_version >= \"3.8\" and python_version < \"3.9\" or platform_system != \"Darwin\" and python_version >= \"3.8\" and python_version < \"3.9\" and platform_machine != \"aarch64\" or platform_machine != \"arm64\" and python_version >= \"3.8\" and python_version < \"3.9\" and platform_system != \"Linux\" or (platform_machine != \"arm64\" and platform_machine != \"aarch64\") and python_version >= \"3.8\" and python_version < \"3.9\""}, {version = ">=1.21.4", markers = "python_version >= \"3.10\" and platform_system == \"Darwin\" and python_version < \"3.11\""}, {version = ">=1.21.2", markers = "platform_system != \"Darwin\" and python_version >= \"3.10\" and python_version < \"3.11\""}, - {version = ">=1.23.5", markers = "python_version >= \"3.11\" and python_version < \"3.12\""}, {version = ">=1.26.0", markers = "python_version >= \"3.12\""}, + {version = ">=1.23.5", markers = "python_version >= \"3.11\" and python_version < \"3.12\""}, ] [[package]] @@ -2800,24 +2800,6 @@ files = [ [package.dependencies] types-pytz = ">=2022.1.1" -[[package]] -name = "pandas-stubs" -version = "2.0.3.230814" -description = "Type annotations for pandas" -optional = true -python-versions = ">=3.8" -files = [ - {file = "pandas_stubs-2.0.3.230814-py3-none-any.whl", hash = "sha256:4b3dfc027d49779176b7daa031a3405f7b839bcb6e312f4b9f29fea5feec5b4f"}, - {file = "pandas_stubs-2.0.3.230814.tar.gz", hash = "sha256:1d5cc09e36e3d9f9a1ed9dceae4e03eeb26d1b898dd769996925f784365c8769"}, -] - -[package.dependencies] -numpy = [ - {version = "<=1.24.3", markers = "python_full_version <= \"3.8.0\""}, - {version = ">=1.25.0", markers = "python_version >= \"3.9\""}, -] -types-pytz = ">=2022.1.1" - [[package]] name = "pandocfilters" version = "1.5.1" From edda8dea0b843167a577763e0b89b46563e0425a Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 30 Sep 2024 17:53:37 +0000 Subject: [PATCH 56/63] =?UTF-8?q?chore(pre=5Fcommit):=20=E2=AC=86=20pre=5F?= =?UTF-8?q?commit=20autoupdate?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/PyCQA/bandit: 1.7.9 → 1.7.10](https://github.com/PyCQA/bandit/compare/1.7.9...1.7.10) - [github.com/astral-sh/ruff-pre-commit: v0.6.7 → v0.6.8](https://github.com/astral-sh/ruff-pre-commit/compare/v0.6.7...v0.6.8) - [github.com/codespell-project/codespell: v2.2.6 → v2.3.0](https://github.com/codespell-project/codespell/compare/v2.2.6...v2.3.0) --- .pre-commit-config.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 7fe9c6d0..d8e34a97 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -25,14 +25,14 @@ repos: - id: mixed-line-ending - repo: https://github.com/PyCQA/bandit - rev: '1.7.9' + rev: '1.7.10' hooks: - id: bandit args: ["-c", "pyproject.toml"] additional_dependencies: ["bandit[toml]"] - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.6.7 + rev: v0.6.8 hooks: - id: ruff args: [--fix, --exit-non-zero-on-fix] @@ -48,7 +48,7 @@ repos: # args: ["--number"] - repo: https://github.com/codespell-project/codespell - rev: v2.2.6 + rev: v2.3.0 hooks: - id: codespell additional_dependencies: From 125a85865494c618c930b2389768ff6ee9263366 Mon Sep 17 00:00:00 2001 From: Onuralp SEZER Date: Mon, 30 Sep 2024 16:53:36 +0300 Subject: [PATCH 57/63] Apply Onuralps review suggestions Signed-off-by: Onuralp SEZER --- supervision/detection/line_zone.py | 92 ++++++++---------------------- 1 file changed, 24 insertions(+), 68 deletions(-) diff --git a/supervision/detection/line_zone.py b/supervision/detection/line_zone.py index 65aaa977..7778c261 100644 --- a/supervision/detection/line_zone.py +++ b/supervision/detection/line_zone.py @@ -669,13 +669,12 @@ class LineZoneAnnotatorMulticlass: on the table, write the class IDs. E.g. instead of `person: 6`, write `0: 6`. """ - - if table_position not in [ + if table_position not in { Position.TOP_LEFT, Position.TOP_RIGHT, Position.BOTTOM_LEFT, Position.BOTTOM_RIGHT, - ]: + }: raise ValueError( "Invalid table position. Supported values are:" " TOP_LEFT, TOP_RIGHT, BOTTOM_LEFT, BOTTOM_RIGHT." @@ -697,52 +696,24 @@ class LineZoneAnnotatorMulticlass: line_zones: List[LineZone], line_zone_labels: Optional[List[str]] = None, ) -> np.ndarray: - """ - Draw a table on the frame, showing how many items of each class - crossed each line. - - Args: - frame (np.ndarray): The image on which the table will be drawn. - line_zones (List[LineZone]): The line zones used to count - the objects. - line_zone_labels (Optional[List[str]]): The labels for each line - zone. If not specified, the labels will be `Line 1:`, - `Line 2:`, etc. - - Returns: - (np.ndarray): The image with the table drawn on it. - """ if line_zone_labels is None: line_zone_labels = [f"Line {i + 1}:" for i in range(len(line_zones))] if len(line_zones) != len(line_zone_labels): raise ValueError("The number of line zones and their labels must match.") - text = "Line Crossings:\n" + text_lines = ["Line Crossings:"] for line_zone, line_zone_label in zip(line_zones, line_zone_labels): - text += f"{line_zone_label}\n" + text_lines.append(line_zone_label) class_id_to_name = line_zone.class_id_to_name - if len(line_zone.in_count_per_class) > 0: - text += " In:\n" - for class_id, count in line_zone.in_count_per_class.items(): - if not self.force_draw_class_ids and class_id in class_id_to_name: - class_name = class_id_to_name[class_id] - else: - class_name = str(class_id) - text += f" {class_name}: {count}\n" + for direction, count_per_class in [("In", line_zone.in_count_per_class), ("Out", line_zone.out_count_per_class)]: + if count_per_class: + text_lines.append(f" {direction}:") + for class_id, count in count_per_class.items(): + class_name = class_id_to_name.get(class_id, str(class_id)) if not self.force_draw_class_ids else str(class_id) + text_lines.append(f" {class_name}: {count}") - if len(line_zone.out_count_per_class) > 0: - text += " Out:\n" - for class_id, count in line_zone.out_count_per_class.items(): - if class_id in class_id_to_name and not self.force_draw_class_ids: - class_name = class_id_to_name[class_id] - else: - class_name = str(class_id) - text += f" {class_name}: {count}\n" - - table_width = 0 - table_height = 0 - text_lines = text.split("\n") + table_width, table_height = 0, 0 for line in text_lines: text_width, text_height = cv2.getTextSize( line, cv2.FONT_HERSHEY_SIMPLEX, self.text_scale, self.text_thickness @@ -750,41 +721,26 @@ class LineZoneAnnotatorMulticlass: text_height += TEXT_MARGIN table_width = max(table_width, text_width) table_height += text_height + table_width += 2 * self.table_padding table_height += 2 * self.table_padding - table_max_height = frame.shape[0] - 2 * self.table_margin table_height = min(table_height, table_max_height) table_width = min(table_width, self.table_max_width) - if self.table_position == Position.TOP_LEFT: - table_x1 = self.table_margin - table_y1 = self.table_margin - elif self.table_position == Position.TOP_RIGHT: - table_x1 = frame.shape[1] - table_width - self.table_margin - table_y1 = self.table_margin - elif self.table_position == Position.BOTTOM_LEFT: - table_x1 = self.table_margin - table_y1 = frame.shape[0] - table_height - self.table_margin - elif self.table_position == Position.BOTTOM_RIGHT: - table_x1 = frame.shape[1] - table_width - self.table_margin - table_y1 = frame.shape[0] - table_height - self.table_margin + position_map = { + Position.TOP_LEFT: (self.table_margin, self.table_margin), + Position.TOP_RIGHT: (frame.shape[1] - table_width - self.table_margin, self.table_margin), + Position.BOTTOM_LEFT: (self.table_margin, frame.shape[0] - table_height - self.table_margin), + Position.BOTTOM_RIGHT: (frame.shape[1] - table_width - self.table_margin, frame.shape[0] - table_height - self.table_margin), + } + table_x1, table_y1 = position_map[self.table_position] - table_rect = Rect( - x=table_x1, y=table_y1, width=table_width, height=table_height - ) + table_rect = Rect(x=table_x1, y=table_y1, width=table_width, height=table_height) + frame = draw_rectangle(scene=frame, rect=table_rect, color=self.table_color, thickness=-1) - frame = draw_rectangle( - scene=frame, - rect=table_rect, - color=self.table_color, - thickness=-1, - ) - - for i, line in enumerate(text.split("\n")): - _, text_height = cv2.getTextSize( - line, cv2.FONT_HERSHEY_SIMPLEX, self.text_scale, self.text_thickness - )[0] + for i, line in enumerate(text_lines): + _, text_height = cv2.getTextSize(line, cv2.FONT_HERSHEY_SIMPLEX, self.text_scale, self.text_thickness)[0] text_height += TEXT_MARGIN anchor_x = table_x1 + self.table_padding anchor_y = table_y1 + self.table_padding + (i + 1) * text_height @@ -800,4 +756,4 @@ class LineZoneAnnotatorMulticlass: lineType=cv2.LINE_AA, ) - return frame + return frame \ No newline at end of file From f01126fabbd9f73e36207487e1af3f5677de4dbe Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 1 Oct 2024 08:44:46 +0000 Subject: [PATCH 58/63] =?UTF-8?q?fix(pre=5Fcommit):=20=F0=9F=8E=A8=20auto?= =?UTF-8?q?=20format=20pre-commit=20hooks?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- supervision/detection/line_zone.py | 40 +++++++++++++++++++++++------- 1 file changed, 31 insertions(+), 9 deletions(-) diff --git a/supervision/detection/line_zone.py b/supervision/detection/line_zone.py index 7778c261..d38bb9dd 100644 --- a/supervision/detection/line_zone.py +++ b/supervision/detection/line_zone.py @@ -706,11 +706,18 @@ class LineZoneAnnotatorMulticlass: text_lines.append(line_zone_label) class_id_to_name = line_zone.class_id_to_name - for direction, count_per_class in [("In", line_zone.in_count_per_class), ("Out", line_zone.out_count_per_class)]: + for direction, count_per_class in [ + ("In", line_zone.in_count_per_class), + ("Out", line_zone.out_count_per_class), + ]: if count_per_class: text_lines.append(f" {direction}:") for class_id, count in count_per_class.items(): - class_name = class_id_to_name.get(class_id, str(class_id)) if not self.force_draw_class_ids else str(class_id) + class_name = ( + class_id_to_name.get(class_id, str(class_id)) + if not self.force_draw_class_ids + else str(class_id) + ) text_lines.append(f" {class_name}: {count}") table_width, table_height = 0, 0 @@ -730,17 +737,32 @@ class LineZoneAnnotatorMulticlass: position_map = { Position.TOP_LEFT: (self.table_margin, self.table_margin), - Position.TOP_RIGHT: (frame.shape[1] - table_width - self.table_margin, self.table_margin), - Position.BOTTOM_LEFT: (self.table_margin, frame.shape[0] - table_height - self.table_margin), - Position.BOTTOM_RIGHT: (frame.shape[1] - table_width - self.table_margin, frame.shape[0] - table_height - self.table_margin), + Position.TOP_RIGHT: ( + frame.shape[1] - table_width - self.table_margin, + self.table_margin, + ), + Position.BOTTOM_LEFT: ( + self.table_margin, + frame.shape[0] - table_height - self.table_margin, + ), + Position.BOTTOM_RIGHT: ( + frame.shape[1] - table_width - self.table_margin, + frame.shape[0] - table_height - self.table_margin, + ), } table_x1, table_y1 = position_map[self.table_position] - table_rect = Rect(x=table_x1, y=table_y1, width=table_width, height=table_height) - frame = draw_rectangle(scene=frame, rect=table_rect, color=self.table_color, thickness=-1) + table_rect = Rect( + x=table_x1, y=table_y1, width=table_width, height=table_height + ) + frame = draw_rectangle( + scene=frame, rect=table_rect, color=self.table_color, thickness=-1 + ) for i, line in enumerate(text_lines): - _, text_height = cv2.getTextSize(line, cv2.FONT_HERSHEY_SIMPLEX, self.text_scale, self.text_thickness)[0] + _, text_height = cv2.getTextSize( + line, cv2.FONT_HERSHEY_SIMPLEX, self.text_scale, self.text_thickness + )[0] text_height += TEXT_MARGIN anchor_x = table_x1 + self.table_padding anchor_y = table_y1 + self.table_padding + (i + 1) * text_height @@ -756,4 +778,4 @@ class LineZoneAnnotatorMulticlass: lineType=cv2.LINE_AA, ) - return frame \ No newline at end of file + return frame From 121190be503b58384cd3402c27f9ebd61f319cc7 Mon Sep 17 00:00:00 2001 From: LinasKo Date: Tue, 1 Oct 2024 11:50:50 +0300 Subject: [PATCH 59/63] minor code refactor, if mot -> continue --- supervision/detection/line_zone.py | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/supervision/detection/line_zone.py b/supervision/detection/line_zone.py index d38bb9dd..de6c1e0d 100644 --- a/supervision/detection/line_zone.py +++ b/supervision/detection/line_zone.py @@ -710,15 +710,17 @@ class LineZoneAnnotatorMulticlass: ("In", line_zone.in_count_per_class), ("Out", line_zone.out_count_per_class), ]: - if count_per_class: - text_lines.append(f" {direction}:") - for class_id, count in count_per_class.items(): - class_name = ( - class_id_to_name.get(class_id, str(class_id)) - if not self.force_draw_class_ids - else str(class_id) - ) - text_lines.append(f" {class_name}: {count}") + if not count_per_class: + continue + + text_lines.append(f" {direction}:") + for class_id, count in count_per_class.items(): + class_name = ( + class_id_to_name.get(class_id, str(class_id)) + if not self.force_draw_class_ids + else str(class_id) + ) + text_lines.append(f" {class_name}: {count}") table_width, table_height = 0, 0 for line in text_lines: From 865cdd3bd18260c97317978b09a8570e6e2b6052 Mon Sep 17 00:00:00 2001 From: LinasKo Date: Tue, 1 Oct 2024 12:10:29 +0300 Subject: [PATCH 60/63] Minor spelling change, code of conduct --- CODE_OF_CONDUCT.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index aa1e72b8..538c78ea 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -5,7 +5,7 @@ We as members, contributors, and leaders pledge to make participation in our community a harassment-free experience for everyone, regardless of age, body size, visible or invisible disability, ethnicity, sex characteristics, gender -identity and expression, level of experience, education, socio-economic status, +identity and expression, level of experience, education, socioeconomic status, nationality, personal appearance, race, caste, color, religion, or sexual identity and orientation. @@ -21,20 +21,20 @@ community include: - Being respectful of differing opinions, viewpoints, and experiences - Giving and gracefully accepting constructive feedback - Accepting responsibility and apologizing to those affected by our mistakes, - and learning from the experience + and learning from the experience - Focusing on what is best not just for us as individuals, but for the overall - community + community Examples of unacceptable behavior include: - The use of sexualized language or imagery, and sexual attention or advances of - any kind + any kind - Trolling, insulting or derogatory comments, and personal or political attacks - Public or private harassment - Publishing others' private information, such as a physical or email address, - without their explicit permission + without their explicit permission - Other conduct which could reasonably be considered inappropriate in a - professional setting + professional setting ## Enforcement Responsibilities From 23023e9764a01307a60e8a6eb204bbc6cde93a89 Mon Sep 17 00:00:00 2001 From: LinasKo Date: Tue, 1 Oct 2024 13:22:55 +0300 Subject: [PATCH 61/63] Revert default overlap_ratio_wh value, update docs --- docs/changelog.md | 2 +- supervision/detection/tools/inference_slicer.py | 9 +++++---- test/detection/tools/test_inference_slicer.py | 4 +--- 3 files changed, 7 insertions(+), 8 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index 0acf99ec..d4e4faf1 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -96,7 +96,7 @@ detections = sv.Detections.from_sam(sam_result=sam_result) - Added [#1409](https://github.com/roboflow/supervision/pull/1409): `text_color` option for [`VertexLabelAnnotator`](https://supervision.roboflow.com/0.23.0/keypoint/annotators/#supervision.keypoint.annotators.VertexLabelAnnotator) keypoint annotator. -- Changed [#1434](https://github.com/roboflow/supervision/pull/1434): [`InferenceSlicer`](https://supervision.roboflow.com/0.23.0/detection/tools/inference_slicer/) now features an `overlap_ratio_wh` parameter, making it easier to compute slice sizes when handling overlapping slices. +- Changed [#1434](https://github.com/roboflow/supervision/pull/1434): [`InferenceSlicer`](https://supervision.roboflow.com/0.23.0/detection/tools/inference_slicer/) now features an `overlap_wh` parameter, making it easier to compute slice sizes when handling overlapping slices. - Fix [#1448](https://github.com/roboflow/supervision/pull/1448): Various annotator type issues have been resolved, supporting expanded error handling. diff --git a/supervision/detection/tools/inference_slicer.py b/supervision/detection/tools/inference_slicer.py index 68455015..81092fe8 100644 --- a/supervision/detection/tools/inference_slicer.py +++ b/supervision/detection/tools/inference_slicer.py @@ -60,7 +60,8 @@ class InferenceSlicer: Args: slice_wh (Tuple[int, int]): Dimensions of each slice measured in pixels. The tuple should be in the format `(width, height)`. - overlap_ratio_wh (Optional[Tuple[float, float]]): A tuple representing the + overlap_ratio_wh (Optional[Tuple[float, float]]): [⚠️ Deprecated: please set + to `None` and use `overlap_wh`] A tuple representing the desired overlap ratio for width and height between consecutive slices. Each value should be in the range [0, 1), where 0 means no overlap and a value close to 1 means high overlap. @@ -87,14 +88,14 @@ class InferenceSlicer: new_parameter="overlap_filter", map_function=lambda x: x, warning_message="`{old_parameter}` in `{function_name}` is deprecated and will " - "be removed in `supervision-0.27.0`. Use '{new_parameter}' " - "instead.", + "be removed in `supervision-0.27.0`. Please set to `None` and use " + "'{new_parameter}' instead.", ) def __init__( self, callback: Callable[[np.ndarray], Detections], slice_wh: Tuple[int, int] = (320, 320), - overlap_ratio_wh: Optional[Tuple[float, float]] = None, + overlap_ratio_wh: Optional[Tuple[float, float]] = (0.2, 0.2), overlap_wh: Optional[Tuple[int, int]] = None, overlap_filter: Union[OverlapFilter, str] = OverlapFilter.NON_MAX_SUPPRESSION, iou_threshold: float = 0.5, diff --git a/test/detection/tools/test_inference_slicer.py b/test/detection/tools/test_inference_slicer.py index cccecfc2..812e7941 100644 --- a/test/detection/tools/test_inference_slicer.py +++ b/test/detection/tools/test_inference_slicer.py @@ -13,9 +13,7 @@ from supervision.detection.tools.inference_slicer import InferenceSlicer def mock_callback(): """Mock callback function for testing.""" - def callback(image_slice: np.ndarray) -> Detections: - # Here we mock the detection process, returning a mock detection - # Assume detections are just coordinates for simplicity + def callback(_: np.ndarray) -> Detections: return Detections(xyxy=np.array([[0, 0, 10, 10]])) return callback From 892bd87b6c4f44b48805706c1de9047929e8bfd2 Mon Sep 17 00:00:00 2001 From: LinasKo Date: Tue, 1 Oct 2024 13:30:39 +0300 Subject: [PATCH 62/63] overlap_ratio_wh: remove deprecated param decorator * We want users to set it to None manually, and the decorator would produce a warning every time. --- supervision/detection/tools/inference_slicer.py | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/supervision/detection/tools/inference_slicer.py b/supervision/detection/tools/inference_slicer.py index 81092fe8..05469dd6 100644 --- a/supervision/detection/tools/inference_slicer.py +++ b/supervision/detection/tools/inference_slicer.py @@ -11,7 +11,6 @@ from supervision.detection.utils import move_boxes, move_masks, move_oriented_bo from supervision.utils.image import crop_image from supervision.utils.internal import ( SupervisionWarnings, - deprecated_parameter, warn_deprecated, ) @@ -67,7 +66,8 @@ class InferenceSlicer: a value close to 1 means high overlap. overlap_wh (Optional[Tuple[int, int]]): A tuple representing the desired overlap for width and height between consecutive slices measured in pixels. - Each value should be greater than or equal to 0. + Each value should be greater than or equal to 0. Takes precedence over + `overlap_ratio_wh`. overlap_filter (Union[OverlapFilter, str]): Strategy for filtering or merging overlapping detections in slices. iou_threshold (float): Intersection over Union (IoU) threshold @@ -83,14 +83,6 @@ class InferenceSlicer: not a multiple of the slice's width or height minus the overlap. """ - @deprecated_parameter( - old_parameter="overlap_filter_strategy", - new_parameter="overlap_filter", - map_function=lambda x: x, - warning_message="`{old_parameter}` in `{function_name}` is deprecated and will " - "be removed in `supervision-0.27.0`. Please set to `None` and use " - "'{new_parameter}' instead.", - ) def __init__( self, callback: Callable[[np.ndarray], Detections], @@ -104,7 +96,8 @@ class InferenceSlicer: if overlap_ratio_wh is not None: warn_deprecated( "`overlap_ratio_wh` in `InferenceSlicer.__init__` is deprecated and " - "will be removed in `supervision-0.27.0`. Use `overlap_wh` instead." + "will be removed in `supervision-0.27.0`. Please manually set it to " + "`None` and use `overlap_wh` instead." ) self._validate_overlap(overlap_ratio_wh, overlap_wh) From 56c269a1ba3260063cd9d6f59d865787aa72b791 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 2 Oct 2024 00:35:02 +0000 Subject: [PATCH 63/63] :arrow_up: Bump tox from 4.20.0 to 4.21.0 Bumps [tox](https://github.com/tox-dev/tox) from 4.20.0 to 4.21.0. - [Release notes](https://github.com/tox-dev/tox/releases) - [Changelog](https://github.com/tox-dev/tox/blob/main/docs/changelog.rst) - [Commits](https://github.com/tox-dev/tox/compare/4.20.0...4.21.0) --- updated-dependencies: - dependency-name: tox dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- poetry.lock | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/poetry.lock b/poetry.lock index 46f05d25..81e11ec2 100644 --- a/poetry.lock +++ b/poetry.lock @@ -4141,30 +4141,31 @@ files = [ [[package]] name = "tox" -version = "4.20.0" +version = "4.21.0" description = "tox is a generic virtualenv management and test command line tool" optional = false python-versions = ">=3.8" files = [ - {file = "tox-4.20.0-py3-none-any.whl", hash = "sha256:21a8005e3d3fe5658a8e36b8ca3ed13a4230429063c5cc2a2fdac6ee5aa0de34"}, - {file = "tox-4.20.0.tar.gz", hash = "sha256:5b78a49b6eaaeab3ae4186415e7c97d524f762ae967c63562687c3e5f0ec23d5"}, + {file = "tox-4.21.0-py3-none-any.whl", hash = "sha256:693ac51378255d34ad7aab6dd2ce9ab6a1cf1924eb930183fde850ad503b681d"}, + {file = "tox-4.21.0.tar.gz", hash = "sha256:e64dd9847ff3a7ec90368be412d7efe61a39caf043222ffbe9ad638ea435f6f6"}, ] [package.dependencies] cachetools = ">=5.5" chardet = ">=5.2" colorama = ">=0.4.6" -filelock = ">=3.15.4" +filelock = ">=3.16.1" packaging = ">=24.1" -platformdirs = ">=4.2.2" +platformdirs = ">=4.3.6" pluggy = ">=1.5" -pyproject-api = ">=1.7.1" +pyproject-api = ">=1.8" tomli = {version = ">=2.0.1", markers = "python_version < \"3.11\""} -virtualenv = ">=20.26.3" +typing-extensions = {version = ">=4.12.2", markers = "python_version < \"3.11\""} +virtualenv = ">=20.26.6" [package.extras] -docs = ["furo (>=2024.8.6)", "sphinx (>=8.0.2)", "sphinx-argparse-cli (>=1.17)", "sphinx-autodoc-typehints (>=2.4)", "sphinx-copybutton (>=0.5.2)", "sphinx-inline-tabs (>=2023.4.21)", "sphinxcontrib-towncrier (>=0.2.1a0)", "towncrier (>=24.8)"] -testing = ["build[virtualenv] (>=1.2.2)", "covdefaults (>=2.3)", "detect-test-pollution (>=1.2)", "devpi-process (>=1)", "diff-cover (>=9.1.1)", "distlib (>=0.3.8)", "flaky (>=3.8.1)", "hatch-vcs (>=0.4)", "hatchling (>=1.25)", "psutil (>=6)", "pytest (>=8.3.2)", "pytest-cov (>=5)", "pytest-mock (>=3.14)", "pytest-xdist (>=3.6.1)", "re-assert (>=1.1)", "setuptools (>=74.1.2)", "time-machine (>=2.15)", "wheel (>=0.44)"] +docs = ["furo (>=2024.8.6)", "sphinx (>=8.0.2)", "sphinx-argparse-cli (>=1.18.2)", "sphinx-autodoc-typehints (>=2.4.4)", "sphinx-copybutton (>=0.5.2)", "sphinx-inline-tabs (>=2023.4.21)", "sphinxcontrib-towncrier (>=0.2.1a0)", "towncrier (>=24.8)"] +testing = ["build[virtualenv] (>=1.2.2)", "covdefaults (>=2.3)", "detect-test-pollution (>=1.2)", "devpi-process (>=1.0.2)", "diff-cover (>=9.2)", "distlib (>=0.3.8)", "flaky (>=3.8.1)", "hatch-vcs (>=0.4)", "hatchling (>=1.25)", "psutil (>=6)", "pytest (>=8.3.3)", "pytest-cov (>=5)", "pytest-mock (>=3.14)", "pytest-xdist (>=3.6.1)", "re-assert (>=1.1)", "setuptools (>=75.1)", "time-machine (>=2.15)", "wheel (>=0.44)"] [[package]] name = "tqdm" @@ -4375,13 +4376,13 @@ test = ["coverage", "flake8 (>=3.7)", "mypy", "pretend", "pytest"] [[package]] name = "virtualenv" -version = "20.26.5" +version = "20.26.6" description = "Virtual Python Environment builder" optional = false python-versions = ">=3.7" files = [ - {file = "virtualenv-20.26.5-py3-none-any.whl", hash = "sha256:4f3ac17b81fba3ce3bd6f4ead2749a72da5929c01774948e243db9ba41df4ff6"}, - {file = "virtualenv-20.26.5.tar.gz", hash = "sha256:ce489cac131aa58f4b25e321d6d186171f78e6cb13fafbf32a840cee67733ff4"}, + {file = "virtualenv-20.26.6-py3-none-any.whl", hash = "sha256:7345cc5b25405607a624d8418154577459c3e0277f5466dd79c49d5e492995f2"}, + {file = "virtualenv-20.26.6.tar.gz", hash = "sha256:280aede09a2a5c317e409a00102e7077c6432c5a38f0ef938e643805a7ad2c48"}, ] [package.dependencies]