refactor(cv2): add optional backend facade (#2430)
* refactor(cv2): add optional backend facade * test: guard real cv2 oracle import for cv2-less environments * test: preserve existing PYTHONPATH in subprocess import tests * lint: auto-fix violations after resolve cycle * fix(typing): remove obsolete suppressions * test(cv2): parametrize constant alignment tests and refactor fallback validation * test(cv2): simplify constant grouping and optimize REQUIRED_SYMBOLS validation --------- Co-authored-by: Codex <codex@openai.com> Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com> Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
This commit is contained in:
parent
16814acff3
commit
8ecd9a6680
|
|
@ -0,0 +1,232 @@
|
|||
"""Private OpenCV compatibility surface used by Supervision."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import NoReturn
|
||||
|
||||
|
||||
class BackendUnavailableError(RuntimeError):
|
||||
"""Raised when an OpenCV operation is used without an available backend."""
|
||||
|
||||
|
||||
try:
|
||||
import cv2
|
||||
except (ImportError, OSError):
|
||||
_IS_CV2_AVAILABLE = False
|
||||
else:
|
||||
_IS_CV2_AVAILABLE = True
|
||||
|
||||
_BORDER_CONSTANT = 0
|
||||
_CAP_PROP_FPS = 5
|
||||
_CAP_PROP_FRAME_COUNT = 7
|
||||
_CAP_PROP_FRAME_HEIGHT = 4
|
||||
_CAP_PROP_FRAME_WIDTH = 3
|
||||
_CAP_PROP_POS_FRAMES = 1
|
||||
_CC_STAT_AREA = 4
|
||||
_CHAIN_APPROX_SIMPLE = 2
|
||||
_COLOR_BGR2GRAY = 6
|
||||
_COLOR_BGR2RGB = 4
|
||||
_COLOR_GRAY2BGR = 8
|
||||
_COLOR_HSV2BGR = 54
|
||||
_COLOR_RGB2BGR = 4
|
||||
_DIST_L2 = 2
|
||||
_FONT_HERSHEY_SIMPLEX = 0
|
||||
_IMREAD_COLOR = 1
|
||||
_IMREAD_UNCHANGED = -1
|
||||
_INTER_LINEAR = 1
|
||||
_INTER_NEAREST = 0
|
||||
_LINE_4 = 4
|
||||
_LINE_AA = 16
|
||||
_RETR_CCOMP = 2
|
||||
_RETR_TREE = 3
|
||||
|
||||
if _IS_CV2_AVAILABLE:
|
||||
from cv2 import (
|
||||
BORDER_CONSTANT,
|
||||
CAP_PROP_FPS,
|
||||
CAP_PROP_FRAME_COUNT,
|
||||
CAP_PROP_FRAME_HEIGHT,
|
||||
CAP_PROP_FRAME_WIDTH,
|
||||
CAP_PROP_POS_FRAMES,
|
||||
CC_STAT_AREA,
|
||||
CHAIN_APPROX_SIMPLE,
|
||||
COLOR_BGR2GRAY,
|
||||
COLOR_BGR2RGB,
|
||||
COLOR_GRAY2BGR,
|
||||
COLOR_HSV2BGR,
|
||||
COLOR_RGB2BGR,
|
||||
DIST_L2,
|
||||
FONT_HERSHEY_SIMPLEX,
|
||||
IMREAD_COLOR,
|
||||
IMREAD_UNCHANGED,
|
||||
INTER_LINEAR,
|
||||
INTER_NEAREST,
|
||||
LINE_4,
|
||||
LINE_AA,
|
||||
RETR_CCOMP,
|
||||
RETR_TREE,
|
||||
VideoCapture,
|
||||
VideoWriter,
|
||||
VideoWriter_fourcc,
|
||||
addWeighted,
|
||||
approxPolyDP,
|
||||
blur,
|
||||
circle,
|
||||
connectedComponents,
|
||||
connectedComponentsWithStats,
|
||||
contourArea,
|
||||
convertScaleAbs,
|
||||
copyMakeBorder,
|
||||
cvtColor,
|
||||
distanceTransform,
|
||||
drawContours,
|
||||
ellipse,
|
||||
fillPoly,
|
||||
findContours,
|
||||
flip,
|
||||
getRotationMatrix2D,
|
||||
getTextSize,
|
||||
imread,
|
||||
imwrite,
|
||||
intersectConvexConvex,
|
||||
line,
|
||||
mean,
|
||||
merge,
|
||||
polylines,
|
||||
putText,
|
||||
rectangle,
|
||||
resize,
|
||||
split,
|
||||
warpAffine,
|
||||
)
|
||||
|
||||
BACKEND_NAME = "opencv"
|
||||
else:
|
||||
BACKEND_NAME = "fallback"
|
||||
|
||||
BORDER_CONSTANT = _BORDER_CONSTANT
|
||||
CAP_PROP_FPS = _CAP_PROP_FPS
|
||||
CAP_PROP_FRAME_COUNT = _CAP_PROP_FRAME_COUNT
|
||||
CAP_PROP_FRAME_HEIGHT = _CAP_PROP_FRAME_HEIGHT
|
||||
CAP_PROP_FRAME_WIDTH = _CAP_PROP_FRAME_WIDTH
|
||||
CAP_PROP_POS_FRAMES = _CAP_PROP_POS_FRAMES
|
||||
CC_STAT_AREA = _CC_STAT_AREA
|
||||
CHAIN_APPROX_SIMPLE = _CHAIN_APPROX_SIMPLE
|
||||
COLOR_BGR2GRAY = _COLOR_BGR2GRAY
|
||||
COLOR_BGR2RGB = _COLOR_BGR2RGB
|
||||
COLOR_GRAY2BGR = _COLOR_GRAY2BGR
|
||||
COLOR_HSV2BGR = _COLOR_HSV2BGR
|
||||
COLOR_RGB2BGR = _COLOR_RGB2BGR
|
||||
DIST_L2 = _DIST_L2
|
||||
FONT_HERSHEY_SIMPLEX = _FONT_HERSHEY_SIMPLEX
|
||||
IMREAD_COLOR = _IMREAD_COLOR
|
||||
IMREAD_UNCHANGED = _IMREAD_UNCHANGED
|
||||
INTER_LINEAR = _INTER_LINEAR
|
||||
INTER_NEAREST = _INTER_NEAREST
|
||||
LINE_4 = _LINE_4
|
||||
LINE_AA = _LINE_AA
|
||||
RETR_CCOMP = _RETR_CCOMP
|
||||
RETR_TREE = _RETR_TREE
|
||||
|
||||
def _unavailable(*args: object, **kwargs: object) -> NoReturn:
|
||||
"""Fail clearly until the corresponding fallback is implemented."""
|
||||
del args, kwargs
|
||||
raise BackendUnavailableError(
|
||||
"OpenCV is not installed and this operation has no fallback yet."
|
||||
)
|
||||
|
||||
VideoCapture = _unavailable
|
||||
VideoWriter = _unavailable
|
||||
VideoWriter_fourcc = _unavailable
|
||||
addWeighted = _unavailable
|
||||
approxPolyDP = _unavailable
|
||||
blur = _unavailable
|
||||
circle = _unavailable
|
||||
connectedComponents = _unavailable
|
||||
connectedComponentsWithStats = _unavailable
|
||||
contourArea = _unavailable
|
||||
convertScaleAbs = _unavailable
|
||||
copyMakeBorder = _unavailable
|
||||
cvtColor = _unavailable
|
||||
distanceTransform = _unavailable
|
||||
drawContours = _unavailable
|
||||
ellipse = _unavailable
|
||||
fillPoly = _unavailable
|
||||
findContours = _unavailable
|
||||
flip = _unavailable
|
||||
getRotationMatrix2D = _unavailable
|
||||
getTextSize = _unavailable
|
||||
imread = _unavailable
|
||||
imwrite = _unavailable
|
||||
intersectConvexConvex = _unavailable
|
||||
line = _unavailable
|
||||
mean = _unavailable
|
||||
merge = _unavailable
|
||||
polylines = _unavailable
|
||||
putText = _unavailable
|
||||
rectangle = _unavailable
|
||||
resize = _unavailable
|
||||
split = _unavailable
|
||||
warpAffine = _unavailable
|
||||
|
||||
|
||||
__all__ = [
|
||||
"BACKEND_NAME",
|
||||
"BORDER_CONSTANT",
|
||||
"CAP_PROP_FPS",
|
||||
"CAP_PROP_FRAME_COUNT",
|
||||
"CAP_PROP_FRAME_HEIGHT",
|
||||
"CAP_PROP_FRAME_WIDTH",
|
||||
"CAP_PROP_POS_FRAMES",
|
||||
"CC_STAT_AREA",
|
||||
"CHAIN_APPROX_SIMPLE",
|
||||
"COLOR_BGR2GRAY",
|
||||
"COLOR_BGR2RGB",
|
||||
"COLOR_GRAY2BGR",
|
||||
"COLOR_HSV2BGR",
|
||||
"COLOR_RGB2BGR",
|
||||
"DIST_L2",
|
||||
"FONT_HERSHEY_SIMPLEX",
|
||||
"IMREAD_COLOR",
|
||||
"IMREAD_UNCHANGED",
|
||||
"INTER_LINEAR",
|
||||
"INTER_NEAREST",
|
||||
"LINE_4",
|
||||
"LINE_AA",
|
||||
"RETR_CCOMP",
|
||||
"RETR_TREE",
|
||||
"BackendUnavailableError",
|
||||
"VideoCapture",
|
||||
"VideoWriter",
|
||||
"VideoWriter_fourcc",
|
||||
"addWeighted",
|
||||
"approxPolyDP",
|
||||
"blur",
|
||||
"circle",
|
||||
"connectedComponents",
|
||||
"connectedComponentsWithStats",
|
||||
"contourArea",
|
||||
"convertScaleAbs",
|
||||
"copyMakeBorder",
|
||||
"cvtColor",
|
||||
"distanceTransform",
|
||||
"drawContours",
|
||||
"ellipse",
|
||||
"fillPoly",
|
||||
"findContours",
|
||||
"flip",
|
||||
"getRotationMatrix2D",
|
||||
"getTextSize",
|
||||
"imread",
|
||||
"imwrite",
|
||||
"intersectConvexConvex",
|
||||
"line",
|
||||
"mean",
|
||||
"merge",
|
||||
"polylines",
|
||||
"putText",
|
||||
"rectangle",
|
||||
"resize",
|
||||
"split",
|
||||
"warpAffine",
|
||||
]
|
||||
|
|
@ -3,13 +3,13 @@ from functools import lru_cache
|
|||
from math import sqrt
|
||||
from typing import Any, ClassVar, cast
|
||||
|
||||
import cv2
|
||||
import numpy as np
|
||||
import numpy.typing as npt
|
||||
from deprecate import deprecated, void # type: ignore[import-untyped,unused-ignore]
|
||||
from PIL import Image, ImageDraw, ImageFont
|
||||
from scipy.interpolate import splev, splprep
|
||||
|
||||
from supervision import _cv2 as cv2
|
||||
from supervision.annotators.base import BaseAnnotator
|
||||
from supervision.annotators.utils import (
|
||||
PENDING_TRACK_ID,
|
||||
|
|
@ -330,7 +330,7 @@ class OrientedBoxAnnotator(BaseAnnotator):
|
|||
|
||||
Example:
|
||||
```python
|
||||
import cv2
|
||||
from supervision import _cv2 as cv2
|
||||
import supervision as sv
|
||||
from ultralytics import YOLO
|
||||
|
||||
|
|
|
|||
|
|
@ -115,7 +115,7 @@ class Classifications:
|
|||
|
||||
Example:
|
||||
```python
|
||||
import cv2
|
||||
from supervision import _cv2 as cv2
|
||||
from ultralytics import YOLO
|
||||
import supervision as sv
|
||||
|
||||
|
|
|
|||
|
|
@ -9,11 +9,11 @@ from itertools import chain
|
|||
from pathlib import Path
|
||||
from typing import cast
|
||||
|
||||
import cv2
|
||||
import numpy as np
|
||||
import numpy.typing as npt
|
||||
from tqdm.auto import tqdm
|
||||
|
||||
from supervision import _cv2 as cv2
|
||||
from supervision.classification.core import Classifications
|
||||
from supervision.config import CLASS_NAME_DATA_FIELD
|
||||
from supervision.dataset.formats.coco import (
|
||||
|
|
|
|||
|
|
@ -6,13 +6,13 @@ from xml.etree.ElementTree import Element, SubElement
|
|||
if TYPE_CHECKING:
|
||||
from supervision.dataset.core import DetectionDataset
|
||||
|
||||
import cv2
|
||||
import numpy as np
|
||||
import numpy.typing as npt
|
||||
from defusedxml.ElementTree import parse, tostring
|
||||
from defusedxml.minidom import parseString
|
||||
from tqdm.auto import tqdm
|
||||
|
||||
from supervision import _cv2 as cv2
|
||||
from supervision.dataset.utils import (
|
||||
approximate_mask_with_polygons,
|
||||
check_no_basename_collisions,
|
||||
|
|
|
|||
|
|
@ -10,12 +10,12 @@ from collections.abc import Callable
|
|||
from pathlib import Path
|
||||
from typing import TYPE_CHECKING, TypeVar, cast
|
||||
|
||||
import cv2
|
||||
import numpy as np
|
||||
import numpy.typing as npt
|
||||
from deprecate import deprecated, void # type: ignore[import-untyped,unused-ignore]
|
||||
from tqdm.auto import tqdm
|
||||
|
||||
from supervision import _cv2 as cv2
|
||||
from supervision.detection.core import Detections
|
||||
from supervision.detection.utils.converters import mask_to_polygons
|
||||
from supervision.detection.utils.converters import (
|
||||
|
|
|
|||
|
|
@ -542,7 +542,7 @@ def _resize_crop(
|
|||
Returns:
|
||||
int32 RLE array for the resized crop.
|
||||
"""
|
||||
import cv2
|
||||
from supervision import _cv2 as cv2
|
||||
|
||||
# All-False: skip decode entirely.
|
||||
if _rle_area(rle) == 0:
|
||||
|
|
|
|||
|
|
@ -100,7 +100,7 @@ class Detections:
|
|||
method, which accepts model results from both detection and segmentation models.
|
||||
|
||||
```python
|
||||
import cv2
|
||||
from supervision import _cv2 as cv2
|
||||
import supervision as sv
|
||||
from inference import get_model
|
||||
|
||||
|
|
@ -116,7 +116,7 @@ class Detections:
|
|||
method, which accepts model results from both detection and segmentation models.
|
||||
|
||||
```python
|
||||
import cv2
|
||||
from supervision import _cv2 as cv2
|
||||
import supervision as sv
|
||||
from ultralytics import YOLO
|
||||
|
||||
|
|
@ -275,7 +275,7 @@ class Detections:
|
|||
|
||||
Example:
|
||||
```python
|
||||
import cv2
|
||||
from supervision import _cv2 as cv2
|
||||
import torch
|
||||
import supervision as sv
|
||||
|
||||
|
|
@ -314,7 +314,7 @@ class Detections:
|
|||
|
||||
Example:
|
||||
```python
|
||||
import cv2
|
||||
from supervision import _cv2 as cv2
|
||||
import supervision as sv
|
||||
from ultralytics import YOLO
|
||||
|
||||
|
|
@ -396,7 +396,7 @@ class Detections:
|
|||
|
||||
Example:
|
||||
```python
|
||||
import cv2
|
||||
from supervision import _cv2 as cv2
|
||||
from super_gradients.training import models
|
||||
import supervision as sv
|
||||
|
||||
|
|
@ -451,7 +451,7 @@ class Detections:
|
|||
import tensorflow as tf
|
||||
import tensorflow_hub as hub
|
||||
import numpy as np
|
||||
import cv2
|
||||
from supervision import _cv2 as cv2
|
||||
|
||||
module_handle = "https://tfhub.dev/tensorflow/centernet/hourglass_512x512_kpts/1"
|
||||
model = hub.load(module_handle)
|
||||
|
|
@ -528,7 +528,7 @@ class Detections:
|
|||
|
||||
Example:
|
||||
```python
|
||||
import cv2
|
||||
from supervision import _cv2 as cv2
|
||||
import supervision as sv
|
||||
from mmdet.apis import init_detector, inference_detector
|
||||
|
||||
|
|
@ -648,7 +648,7 @@ class Detections:
|
|||
|
||||
Example:
|
||||
```python
|
||||
import cv2
|
||||
from supervision import _cv2 as cv2
|
||||
import supervision as sv
|
||||
from detectron2.engine import DefaultPredictor
|
||||
from detectron2.config import get_cfg
|
||||
|
|
@ -742,7 +742,7 @@ class Detections:
|
|||
|
||||
Example:
|
||||
```python
|
||||
import cv2
|
||||
from supervision import _cv2 as cv2
|
||||
import supervision as sv
|
||||
from inference import get_model
|
||||
|
||||
|
|
@ -874,7 +874,7 @@ class Detections:
|
|||
|
||||
Example:
|
||||
```python
|
||||
import cv2
|
||||
from supervision import _cv2 as cv2
|
||||
import supervision as sv
|
||||
from inference.models.sam3 import SegmentAnything3
|
||||
from inference.core.entities.requests.sam3 import Sam3Prompt
|
||||
|
|
@ -2207,7 +2207,7 @@ class Detections:
|
|||
|
||||
Example:
|
||||
```python
|
||||
import cv2
|
||||
from supervision import _cv2 as cv2
|
||||
from ncnn.model_zoo import get_model
|
||||
import supervision as sv
|
||||
|
||||
|
|
@ -2750,7 +2750,7 @@ class Detections:
|
|||
|
||||
Example:
|
||||
```python
|
||||
import cv2
|
||||
from supervision import _cv2 as cv2
|
||||
import supervision as sv
|
||||
from ultralytics import YOLO
|
||||
|
||||
|
|
@ -3329,7 +3329,7 @@ def merge_inner_detection_object_pair(
|
|||
|
||||
Example:
|
||||
```python
|
||||
import cv2
|
||||
from supervision import _cv2 as cv2
|
||||
import supervision as sv
|
||||
from inference import get_model
|
||||
|
||||
|
|
|
|||
|
|
@ -5,10 +5,10 @@ from collections.abc import Iterable
|
|||
from functools import lru_cache
|
||||
from typing import Literal
|
||||
|
||||
import cv2
|
||||
import numpy as np
|
||||
import numpy.typing as npt
|
||||
|
||||
from supervision import _cv2 as cv2
|
||||
from supervision.config import CLASS_NAME_DATA_FIELD
|
||||
from supervision.detection.core import Detections
|
||||
from supervision.detection.utils.internal import cross_product
|
||||
|
|
|
|||
|
|
@ -183,7 +183,7 @@ class InferenceSlicer:
|
|||
|
||||
Example:
|
||||
```python
|
||||
import cv2
|
||||
from supervision import _cv2 as cv2
|
||||
import supervision as sv
|
||||
from rfdetr import RFDETRMedium
|
||||
|
||||
|
|
@ -237,7 +237,7 @@ class InferenceSlicer:
|
|||
that benefit from batched forward passes:
|
||||
|
||||
```python
|
||||
import cv2
|
||||
from supervision import _cv2 as cv2
|
||||
import numpy as np
|
||||
import supervision as sv
|
||||
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
from collections.abc import Iterable
|
||||
from typing import Any, cast
|
||||
|
||||
import cv2
|
||||
import numpy as np
|
||||
import numpy.typing as npt
|
||||
|
||||
from supervision import Detections
|
||||
from supervision import _cv2 as cv2
|
||||
from supervision.detection.utils.converters import polygon_to_mask
|
||||
from supervision.draw.color import Color
|
||||
from supervision.draw.utils import draw_filled_polygon, draw_polygon, draw_text
|
||||
|
|
|
|||
|
|
@ -2,10 +2,11 @@ from __future__ import annotations
|
|||
|
||||
from typing import Any, Literal, cast
|
||||
|
||||
import cv2
|
||||
import numpy as np
|
||||
import numpy.typing as npt
|
||||
|
||||
from supervision import _cv2 as cv2
|
||||
|
||||
MIN_POLYGON_POINT_COUNT = 3
|
||||
CoordinateConvention = Literal["inclusive", "exclusive"]
|
||||
|
||||
|
|
|
|||
|
|
@ -2,10 +2,10 @@ import logging
|
|||
from itertools import chain
|
||||
from typing import Any, Literal, cast, overload
|
||||
|
||||
import cv2
|
||||
import numpy as np
|
||||
import numpy.typing as npt
|
||||
|
||||
from supervision import _cv2 as cv2
|
||||
from supervision.config import CLASS_NAME_DATA_FIELD
|
||||
from supervision.detection.compact_mask import CompactMask
|
||||
from supervision.detection.utils._typing import _DetectionDataType, _MetadataType
|
||||
|
|
|
|||
|
|
@ -5,10 +5,10 @@ from collections.abc import Callable, Sequence
|
|||
from enum import Enum
|
||||
from typing import Any, cast
|
||||
|
||||
import cv2
|
||||
import numpy as np
|
||||
import numpy.typing as npt
|
||||
|
||||
from supervision import _cv2 as cv2
|
||||
from supervision.detection.compact_mask import CompactMask
|
||||
from supervision.detection.utils.converters import mask_to_xyxy
|
||||
from supervision.utils.internal import warn_deprecated
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
from typing import Any, Literal, cast
|
||||
|
||||
import cv2
|
||||
import numpy as np
|
||||
import numpy.typing as npt
|
||||
|
||||
from supervision import _cv2 as cv2
|
||||
from supervision.detection.compact_mask import CompactMask
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
import cv2
|
||||
import numpy as np
|
||||
import numpy.typing as npt
|
||||
|
||||
from supervision import _cv2 as cv2
|
||||
|
||||
|
||||
def filter_polygons_by_area(
|
||||
polygons: list[npt.NDArray[np.number]],
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
import os
|
||||
from typing import cast
|
||||
|
||||
import cv2
|
||||
import numpy as np
|
||||
import numpy.typing as npt
|
||||
|
||||
from supervision import _cv2 as cv2
|
||||
from supervision.draw.color import Color
|
||||
from supervision.geometry.core import Point, Rect
|
||||
|
||||
|
|
|
|||
|
|
@ -2,10 +2,10 @@ from abc import ABC, abstractmethod
|
|||
from collections.abc import Sequence
|
||||
from typing import cast
|
||||
|
||||
import cv2
|
||||
import numpy as np
|
||||
import numpy.typing as npt
|
||||
|
||||
from supervision import _cv2 as cv2
|
||||
from supervision.detection.utils.boxes import pad_boxes, spread_out_boxes
|
||||
from supervision.draw.base import ImageType
|
||||
from supervision.draw.color import Color
|
||||
|
|
|
|||
|
|
@ -83,7 +83,7 @@ class KeyPoints:
|
|||
conversion is needed.
|
||||
|
||||
```python
|
||||
import cv2
|
||||
from supervision import _cv2 as cv2
|
||||
import supervision as sv
|
||||
from rfdetr import RFDETRKeypointPreview
|
||||
|
||||
|
|
@ -100,7 +100,7 @@ class KeyPoints:
|
|||
[pose](https://docs.ultralytics.com/tasks/pose/) result.
|
||||
|
||||
```python
|
||||
import cv2
|
||||
from supervision import _cv2 as cv2
|
||||
import supervision as sv
|
||||
from ultralytics import YOLO
|
||||
|
||||
|
|
@ -117,7 +117,7 @@ class KeyPoints:
|
|||
method, which accepts [Inference](https://inference.roboflow.com/) pose result.
|
||||
|
||||
```python
|
||||
import cv2
|
||||
from supervision import _cv2 as cv2
|
||||
import supervision as sv
|
||||
from inference import get_model
|
||||
|
||||
|
|
@ -136,7 +136,7 @@ class KeyPoints:
|
|||
|
||||
|
||||
```python
|
||||
import cv2
|
||||
from supervision import _cv2 as cv2
|
||||
import mediapipe as mp
|
||||
import supervision as sv
|
||||
|
||||
|
|
@ -398,7 +398,7 @@ class KeyPoints:
|
|||
|
||||
Examples:
|
||||
```python
|
||||
import cv2
|
||||
from supervision import _cv2 as cv2
|
||||
import supervision as sv
|
||||
from inference import get_model
|
||||
|
||||
|
|
@ -410,7 +410,7 @@ class KeyPoints:
|
|||
```
|
||||
|
||||
```python
|
||||
import cv2
|
||||
from supervision import _cv2 as cv2
|
||||
import supervision as sv
|
||||
from inference_sdk import InferenceHTTPClient
|
||||
|
||||
|
|
@ -489,7 +489,7 @@ class KeyPoints:
|
|||
|
||||
Examples:
|
||||
```python
|
||||
import cv2
|
||||
from supervision import _cv2 as cv2
|
||||
import mediapipe as mp
|
||||
import supervision as sv
|
||||
|
||||
|
|
@ -515,7 +515,7 @@ class KeyPoints:
|
|||
```
|
||||
|
||||
```python
|
||||
import cv2
|
||||
from supervision import _cv2 as cv2
|
||||
import mediapipe as mp
|
||||
import supervision as sv
|
||||
|
||||
|
|
@ -610,7 +610,7 @@ class KeyPoints:
|
|||
|
||||
Examples:
|
||||
```python
|
||||
import cv2
|
||||
from supervision import _cv2 as cv2
|
||||
import supervision as sv
|
||||
from ultralytics import YOLO
|
||||
|
||||
|
|
@ -647,7 +647,7 @@ class KeyPoints:
|
|||
|
||||
Examples:
|
||||
```python
|
||||
import cv2
|
||||
from supervision import _cv2 as cv2
|
||||
import torch
|
||||
import supervision as sv
|
||||
import super_gradients
|
||||
|
|
@ -706,7 +706,7 @@ class KeyPoints:
|
|||
|
||||
Examples:
|
||||
```python
|
||||
import cv2
|
||||
from supervision import _cv2 as cv2
|
||||
import supervision as sv
|
||||
from detectron2.engine import DefaultPredictor
|
||||
from detectron2.config import get_cfg
|
||||
|
|
@ -1112,7 +1112,7 @@ class KeyPoints:
|
|||
|
||||
Examples:
|
||||
```python
|
||||
import cv2
|
||||
from supervision import _cv2 as cv2
|
||||
import supervision as sv
|
||||
from ultralytics import YOLO
|
||||
|
||||
|
|
@ -1323,7 +1323,7 @@ class KeyPoints:
|
|||
|
||||
Examples:
|
||||
```python
|
||||
import cv2
|
||||
from supervision import _cv2 as cv2
|
||||
import supervision as sv
|
||||
from rfdetr import RFDETRKeypointPreview
|
||||
|
||||
|
|
|
|||
|
|
@ -453,8 +453,9 @@ def _annotate_detection_panel(
|
|||
Returns:
|
||||
Annotated copy of ``scene`` as a ``np.uint8`` array.
|
||||
"""
|
||||
import cv2 # lazy: only needed when save_directory_path is set
|
||||
|
||||
from supervision import (
|
||||
_cv2 as cv2, # lazy: only needed when save_directory_path is set
|
||||
)
|
||||
from supervision.annotators.core import BoxAnnotator, LabelAnnotator
|
||||
from supervision.annotators.utils import ColorLookup
|
||||
from supervision.draw.color import ColorPalette
|
||||
|
|
@ -543,7 +544,9 @@ def _save_detection_validation_visualization(
|
|||
class_names: Optional list mapping class integer ids to name strings.
|
||||
metric_target: Coordinate representation used for IoU matching.
|
||||
"""
|
||||
import cv2 # lazy: only needed when save_directory_path is set
|
||||
from supervision import (
|
||||
_cv2 as cv2, # lazy: only needed when save_directory_path is set
|
||||
)
|
||||
|
||||
tp_predictions, fp_predictions, fn_targets = _split_detections_by_outcome(
|
||||
predictions=predictions,
|
||||
|
|
|
|||
|
|
@ -2,12 +2,12 @@ import functools
|
|||
from collections.abc import Callable
|
||||
from typing import Any, TypeVar, cast
|
||||
|
||||
import cv2
|
||||
import numpy as np
|
||||
import numpy.typing as npt
|
||||
from deprecate import deprecated, void # type: ignore[import-untyped,unused-ignore]
|
||||
from PIL import Image
|
||||
|
||||
from supervision import _cv2 as cv2
|
||||
from supervision.draw.base import ImageType
|
||||
|
||||
F = TypeVar("F", bound=Callable[..., Any])
|
||||
|
|
|
|||
|
|
@ -9,7 +9,6 @@ from functools import partial
|
|||
from types import TracebackType
|
||||
from typing import Literal, cast
|
||||
|
||||
import cv2
|
||||
import numpy as np
|
||||
import numpy.typing as npt
|
||||
from deprecate import ( # type: ignore[import-untyped,unused-ignore]
|
||||
|
|
@ -18,6 +17,7 @@ from deprecate import ( # type: ignore[import-untyped,unused-ignore]
|
|||
)
|
||||
from PIL import Image
|
||||
|
||||
from supervision import _cv2 as cv2
|
||||
from supervision.draw.base import ImageType
|
||||
from supervision.draw.color import Color, unify_to_bgr
|
||||
from supervision.draw.utils import calculate_optimal_text_scale, draw_text
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
import cv2
|
||||
import numpy as np
|
||||
import numpy.typing as npt
|
||||
from PIL import Image
|
||||
|
||||
from supervision import _cv2 as cv2
|
||||
from supervision.draw.base import ImageType
|
||||
from supervision.utils.conversion import pillow_to_cv2
|
||||
|
||||
|
|
|
|||
|
|
@ -13,11 +13,11 @@ from queue import Empty, Full, Queue
|
|||
from types import TracebackType
|
||||
from typing import cast
|
||||
|
||||
import cv2
|
||||
import numpy as np
|
||||
import numpy.typing as npt
|
||||
from tqdm.auto import tqdm
|
||||
|
||||
from supervision import _cv2 as cv2
|
||||
from supervision.utils.logger import _get_logger
|
||||
|
||||
logger = _get_logger(__name__)
|
||||
|
|
@ -293,7 +293,7 @@ def get_video_frames_generator(
|
|||
sources; `cv2.VideoCapture` must be released by the caller when done:
|
||||
|
||||
```python
|
||||
import cv2
|
||||
from supervision import _cv2 as cv2
|
||||
|
||||
cap = cv2.VideoCapture(0) # 0 = default webcam
|
||||
try:
|
||||
|
|
|
|||
|
|
@ -6,12 +6,12 @@ import warnings
|
|||
from collections.abc import Iterator
|
||||
from typing import Any, cast
|
||||
|
||||
import cv2
|
||||
import numpy as np
|
||||
import pytest
|
||||
from PIL import Image
|
||||
|
||||
import supervision.annotators.core as annotators_core
|
||||
from supervision import _cv2 as cv2
|
||||
from supervision.annotators.base import BaseAnnotator
|
||||
from supervision.annotators.core import (
|
||||
BackgroundOverlayAnnotator,
|
||||
|
|
|
|||
|
|
@ -0,0 +1 @@
|
|||
"""Tests for the private media backend boundary."""
|
||||
|
|
@ -0,0 +1,184 @@
|
|||
"""Tests for the private OpenCV compatibility surface."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import importlib
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
import numpy as np
|
||||
import pytest
|
||||
|
||||
from supervision import _cv2
|
||||
|
||||
# Use the real OpenCV module as the oracle for compatibility comparisons.
|
||||
try:
|
||||
cv2 = importlib.import_module("cv2")
|
||||
except (ImportError, OSError):
|
||||
pytest.skip(
|
||||
"OpenCV is required as the reference implementation for this test module",
|
||||
allow_module_level=True,
|
||||
)
|
||||
|
||||
|
||||
OPENCV_CONSTANTS = [
|
||||
"BORDER_CONSTANT",
|
||||
"CAP_PROP_FPS",
|
||||
"CAP_PROP_FRAME_COUNT",
|
||||
"CAP_PROP_FRAME_HEIGHT",
|
||||
"CAP_PROP_FRAME_WIDTH",
|
||||
"CAP_PROP_POS_FRAMES",
|
||||
"CC_STAT_AREA",
|
||||
"CHAIN_APPROX_SIMPLE",
|
||||
"COLOR_BGR2GRAY",
|
||||
"COLOR_BGR2RGB",
|
||||
"COLOR_GRAY2BGR",
|
||||
"COLOR_HSV2BGR",
|
||||
"COLOR_RGB2BGR",
|
||||
"DIST_L2",
|
||||
"FONT_HERSHEY_SIMPLEX",
|
||||
"IMREAD_COLOR",
|
||||
"IMREAD_UNCHANGED",
|
||||
"INTER_LINEAR",
|
||||
"INTER_NEAREST",
|
||||
"LINE_4",
|
||||
"LINE_AA",
|
||||
"RETR_CCOMP",
|
||||
"RETR_TREE",
|
||||
]
|
||||
|
||||
REQUIRED_SYMBOLS = {
|
||||
"VideoCapture",
|
||||
"VideoWriter",
|
||||
"VideoWriter_fourcc",
|
||||
"addWeighted",
|
||||
"approxPolyDP",
|
||||
"blur",
|
||||
"circle",
|
||||
"connectedComponents",
|
||||
"connectedComponentsWithStats",
|
||||
"contourArea",
|
||||
"convertScaleAbs",
|
||||
"copyMakeBorder",
|
||||
"cvtColor",
|
||||
"distanceTransform",
|
||||
"drawContours",
|
||||
"ellipse",
|
||||
"fillPoly",
|
||||
"findContours",
|
||||
"flip",
|
||||
"getRotationMatrix2D",
|
||||
"getTextSize",
|
||||
"imread",
|
||||
"imwrite",
|
||||
"intersectConvexConvex",
|
||||
"line",
|
||||
"mean",
|
||||
"merge",
|
||||
"polylines",
|
||||
"putText",
|
||||
"rectangle",
|
||||
"resize",
|
||||
"split",
|
||||
"warpAffine",
|
||||
} | set(OPENCV_CONSTANTS)
|
||||
|
||||
|
||||
def test_facade_exports_the_required_opencv_surface() -> None:
|
||||
"""Expose every OpenCV symbol used by production call sites."""
|
||||
assert REQUIRED_SYMBOLS <= set(_cv2.__all__)
|
||||
assert all(hasattr(_cv2, symbol) for symbol in REQUIRED_SYMBOLS)
|
||||
assert _cv2.BACKEND_NAME == "opencv"
|
||||
|
||||
|
||||
@pytest.mark.parametrize("name", OPENCV_CONSTANTS)
|
||||
def test_fallback_constant_matches_opencv(name: str) -> None:
|
||||
"""Keep each private fallback constant aligned with the OpenCV reference."""
|
||||
actual = getattr(_cv2, f"_{name}")
|
||||
expected = getattr(cv2, name)
|
||||
|
||||
assert actual == expected
|
||||
|
||||
|
||||
def test_facade_calls_the_package_imported_surface() -> None:
|
||||
"""Route production-style calls through the Supervision facade."""
|
||||
image = np.array([[[10, 20, 30], [40, 50, 60]]], dtype=np.uint8)
|
||||
|
||||
np.testing.assert_array_equal(
|
||||
_cv2.cvtColor(image, _cv2.COLOR_BGR2RGB),
|
||||
cv2.cvtColor(image, cv2.COLOR_BGR2RGB),
|
||||
)
|
||||
np.testing.assert_array_equal(
|
||||
_cv2.resize(image, (4, 2), interpolation=_cv2.INTER_NEAREST),
|
||||
cv2.resize(image, (4, 2), interpolation=cv2.INTER_NEAREST),
|
||||
)
|
||||
|
||||
|
||||
def test_facade_imports_without_opencv() -> None:
|
||||
"""Keep fallback imports and constants valid when cv2 is unavailable."""
|
||||
env = os.environ.copy()
|
||||
source_path = str(Path(__file__).resolve().parents[2] / "src")
|
||||
env["PYTHONPATH"] = os.pathsep.join(
|
||||
filter(None, (source_path, env.get("PYTHONPATH")))
|
||||
)
|
||||
expected_constants = {name: getattr(cv2, name) for name in OPENCV_CONSTANTS}
|
||||
code = f"""
|
||||
import sys
|
||||
|
||||
|
||||
class BlockCv2:
|
||||
def find_spec(self, fullname, path=None, target=None):
|
||||
if fullname == "cv2":
|
||||
raise ModuleNotFoundError("blocked for test")
|
||||
return None
|
||||
|
||||
|
||||
sys.meta_path.insert(0, BlockCv2())
|
||||
from supervision import _cv2
|
||||
|
||||
assert _cv2._IS_CV2_AVAILABLE is False
|
||||
assert _cv2.BACKEND_NAME == "fallback"
|
||||
expected = {expected_constants!r}
|
||||
actual = {{name: getattr(_cv2, name) for name in expected}}
|
||||
fallback = {{name: getattr(_cv2, f"_{{name}}") for name in expected}}
|
||||
assert fallback == expected
|
||||
assert actual == expected
|
||||
try:
|
||||
_cv2.resize(None, (1, 1), interpolation=_cv2.INTER_NEAREST)
|
||||
except _cv2.BackendUnavailableError:
|
||||
pass
|
||||
else:
|
||||
raise AssertionError("missing OpenCV must fail explicitly")
|
||||
"""
|
||||
subprocess.run( # noqa: S603
|
||||
[sys.executable, "-c", code],
|
||||
check=True,
|
||||
env=env,
|
||||
)
|
||||
|
||||
|
||||
def test_facade_does_not_hide_a_breaking_opencv_import() -> None:
|
||||
"""Raise when cv2 imports but no longer exposes a required symbol."""
|
||||
env = os.environ.copy()
|
||||
source_path = str(Path(__file__).resolve().parents[2] / "src")
|
||||
env["PYTHONPATH"] = os.pathsep.join(
|
||||
filter(None, (source_path, env.get("PYTHONPATH")))
|
||||
)
|
||||
code = """
|
||||
import sys
|
||||
import types
|
||||
|
||||
sys.modules["cv2"] = types.ModuleType("cv2")
|
||||
from supervision import _cv2
|
||||
"""
|
||||
result = subprocess.run( # noqa: S603
|
||||
[sys.executable, "-c", code],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
env=env,
|
||||
)
|
||||
|
||||
assert result.returncode != 0
|
||||
assert "cannot import name" in result.stderr
|
||||
|
|
@ -1,10 +1,10 @@
|
|||
import warnings
|
||||
|
||||
import cv2
|
||||
import numpy as np
|
||||
import pytest
|
||||
from PIL import Image, ImageChops
|
||||
|
||||
from supervision import _cv2 as cv2
|
||||
from supervision.utils.image import (
|
||||
ImageSink,
|
||||
_overlay_image,
|
||||
|
|
|
|||
|
|
@ -6,10 +6,10 @@ from queue import Queue as StdQueue
|
|||
from types import SimpleNamespace
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
import cv2
|
||||
import numpy as np
|
||||
import pytest
|
||||
|
||||
from supervision import _cv2 as cv2
|
||||
from supervision.utils.video import (
|
||||
FPSMonitor,
|
||||
VideoInfo,
|
||||
|
|
|
|||
2
uv.lock
2
uv.lock
|
|
@ -3426,7 +3426,7 @@ requires-dist = [
|
|||
{ name = "defusedxml", specifier = ">=0.7.1" },
|
||||
{ name = "matplotlib", specifier = ">=3.6" },
|
||||
{ name = "numpy", specifier = ">=1.21.2" },
|
||||
{ name = "opencv-python", specifier = ">=4.5.5.64" },
|
||||
{ name = "opencv-python", specifier = ">=4.5.5.64,<5" },
|
||||
{ name = "pandas", marker = "extra == 'metrics'", specifier = ">=2" },
|
||||
{ name = "pillow", specifier = ">=9.4" },
|
||||
{ name = "pydeprecate", specifier = ">=0.9,<0.11" },
|
||||
|
|
|
|||
Loading…
Reference in New Issue