Merge branch 'develop' into feat/moondream-vlm-object-detection
This commit is contained in:
commit
eb330ac1c1
|
|
@ -51,7 +51,7 @@ jobs:
|
|||
|
||||
- name: 🏗️ Install dependencies
|
||||
run: |
|
||||
uv pip install -r pyproject.toml --extra docs
|
||||
uv pip install -r pyproject.toml --group docs
|
||||
# Install mkdocs-material-insiders using the GitHub App token
|
||||
uv pip install "git+https://roboflow:${{ steps.mkdocs_token.outputs.token }}@github.com/roboflow/mkdocs-material-insiders.git@9.5.49-insiders-4.53.14#egg=mkdocs-material[imaging]"
|
||||
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ jobs:
|
|||
|
||||
- name: 🏗️ Build source and wheel distributions
|
||||
run: |
|
||||
uv pip install -r pyproject.toml --extra build
|
||||
uv pip install -r pyproject.toml --group build
|
||||
uv build
|
||||
uv run twine check --strict dist/*
|
||||
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ jobs:
|
|||
|
||||
- name: 🏗️ Build source and wheel distributions
|
||||
run: |
|
||||
uv pip install -r pyproject.toml --extra build
|
||||
uv pip install -r pyproject.toml --group build
|
||||
uv build
|
||||
uv run twine check --strict dist/*
|
||||
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ jobs:
|
|||
|
||||
- name: 🏗️ Build source and wheel distributions
|
||||
run: |
|
||||
uv pip install -r pyproject.toml --extra build
|
||||
uv pip install -r pyproject.toml --group build
|
||||
uv build
|
||||
uv run twine check --strict dist/*
|
||||
|
||||
|
|
|
|||
|
|
@ -29,11 +29,9 @@ jobs:
|
|||
python-version: ${{ matrix.python-version }}
|
||||
activate-environment: true
|
||||
|
||||
|
||||
- name: 🏗️ Install dependencies
|
||||
run: |
|
||||
uv pip install -r pyproject.toml --extra docs
|
||||
|
||||
uv pip install -r pyproject.toml --group docs
|
||||
|
||||
- name: 🧪 Test Docs Build
|
||||
run: uv run mkdocs build --verbose
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ jobs:
|
|||
|
||||
|
||||
- name: 🚀 Install Packages
|
||||
run: uv pip install -r pyproject.toml --extra dev --extra docs --extra metrics
|
||||
run: uv pip install -r pyproject.toml --group dev --group docs --extra metrics
|
||||
|
||||
- name: 🧪 Run the Import test
|
||||
run: uv run python -c "import supervision; from supervision import assets; from supervision import metrics; print(supervision.__version__)"
|
||||
|
|
|
|||
|
|
@ -107,6 +107,7 @@ status: new
|
|||
|
||||
:::supervision.detection.utils.denormalize_boxes
|
||||
|
||||
<div class="md-typeset">
|
||||
<h2><a href="#supervision.detection.utils.xyxy_to_xcycarh">xyxy_to_xcycarh</a></h2>
|
||||
</div>
|
||||
|
||||
|
|
|
|||
|
|
@ -93,7 +93,7 @@
|
|||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"!pip install -q inference-gpu \"supervision[assets]\""
|
||||
"!pip install -q inference-gpu \"supervision\""
|
||||
]
|
||||
},
|
||||
{
|
||||
|
|
@ -156,7 +156,7 @@
|
|||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 4,
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"colab": {
|
||||
"base_uri": "https://localhost:8080/"
|
||||
|
|
@ -207,7 +207,6 @@
|
|||
],
|
||||
"source": [
|
||||
"import supervision as sv\n",
|
||||
"from supervision.assets import download_assets, VideoAssets\n",
|
||||
"from inference.models.utils import get_roboflow_model\n",
|
||||
"\n",
|
||||
"\n",
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
|
|
@ -17,7 +17,7 @@
|
|||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 10,
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"vscode": {
|
||||
"languageId": "shellscript"
|
||||
|
|
@ -25,7 +25,7 @@
|
|||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"!pip install -q \"supervision[assets]\""
|
||||
"pip install -q \"supervision\""
|
||||
]
|
||||
},
|
||||
{
|
||||
|
|
|
|||
|
|
@ -101,7 +101,7 @@
|
|||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"!pip install -q torch diffusers accelerate inference-gpu[yolo-world] dill git+https://github.com/openai/CLIP.git supervision==0.19.0rc5"
|
||||
"!pip install -q torch diffusers accelerate inference-gpu[yolo-world] dill git+https://github.com/openai/CLIP.git supervision"
|
||||
]
|
||||
},
|
||||
{
|
||||
|
|
|
|||
|
|
@ -92,7 +92,7 @@
|
|||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"!pip install -q inference-gpu \"supervision[assets]\""
|
||||
"!pip install -q inference-gpu \"supervision\""
|
||||
]
|
||||
},
|
||||
{
|
||||
|
|
|
|||
|
|
@ -72,7 +72,7 @@
|
|||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"!pip install roboflow supervision==0.19.0 -q"
|
||||
"!pip install roboflow supervision -q"
|
||||
]
|
||||
},
|
||||
{
|
||||
|
|
|
|||
|
|
@ -300,7 +300,7 @@
|
|||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"!pip install -q \"ultralytics<=8.3.40\""
|
||||
"!pip install -q ultralytics"
|
||||
]
|
||||
},
|
||||
{
|
||||
|
|
@ -495,7 +495,7 @@
|
|||
"source": [
|
||||
"from ultralytics import YOLO\n",
|
||||
"\n",
|
||||
"model = YOLO(\"yolov8x.pt\")\n",
|
||||
"model = YOLO(\"yolo11x.pt\")\n",
|
||||
"result = model(image, verbose=False)[0]\n",
|
||||
"detections = sv.Detections.from_ultralytics(result)"
|
||||
]
|
||||
|
|
@ -594,7 +594,7 @@
|
|||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 33,
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "yM6dmicTRGl6"
|
||||
},
|
||||
|
|
@ -602,7 +602,7 @@
|
|||
"source": [
|
||||
"from ultralytics import YOLO\n",
|
||||
"\n",
|
||||
"model = YOLO(\"yolov8x-seg.pt\")\n",
|
||||
"model = YOLO(\"yolo11x-seg.pt\")\n",
|
||||
"result = model(image, verbose=False)[0]\n",
|
||||
"detections = sv.Detections.from_ultralytics(result)"
|
||||
]
|
||||
|
|
@ -926,7 +926,7 @@
|
|||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"!pip install -q supervision[assets]"
|
||||
"!pip install -q supervision"
|
||||
]
|
||||
},
|
||||
{
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
|
|
@ -32,7 +32,7 @@
|
|||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"!pip install -q inference requests tqdm supervision==0.21.0"
|
||||
"!pip install -q inference requests tqdm supervision"
|
||||
]
|
||||
},
|
||||
{
|
||||
|
|
@ -695,12 +695,12 @@
|
|||
"source": [
|
||||
"###\u00a0Annotate Image with Detections\n",
|
||||
"\n",
|
||||
"Finally, we can annotate the image with the predictions. Since we are working with an object detection model, we will use the [`sv.BoundingBoxAnnotator`](https://supervision.roboflow.com/latest/detection/annotators/#supervision.annotators.core.BoundingBoxAnnotator) and [`sv.LabelAnnotator`](https://supervision.roboflow.com/latest/detection/annotators/#supervision.annotators.core.LabelAnnotator) classes."
|
||||
"Finally, we can annotate the image with the predictions. Since we are working with an object detection model, we will use the [`sv.BoxAnnotator`](https://supervision.roboflow.com/latest/detection/annotators/#supervision.annotators.core.BoxAnnotator) and [`sv.LabelAnnotator`](https://supervision.roboflow.com/latest/detection/annotators/#supervision.annotators.core.LabelAnnotator) classes."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 49,
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"colab": {
|
||||
"base_uri": "https://localhost:8080/",
|
||||
|
|
@ -722,7 +722,7 @@
|
|||
}
|
||||
],
|
||||
"source": [
|
||||
"bounding_box_annotator = sv.BoundingBoxAnnotator()\n",
|
||||
"bounding_box_annotator = sv.BoxAnnotator()\n",
|
||||
"label_annotator = sv.LabelAnnotator()\n",
|
||||
"\n",
|
||||
"annotated_frame = frame.copy()\n",
|
||||
|
|
|
|||
|
|
@ -12,15 +12,15 @@
|
|||
</div>
|
||||
<div class="custom-grid">
|
||||
<a href="/develop/notebooks/quickstart"> <p class="card repo-card" data-name="Supervision Quickstart" data-labels="ANNOTATOR,DETECTION,SAM"
|
||||
data-version="v0.18.0" data-author="SkalskiP,onuralpszr"></p>
|
||||
data-version="v0.26.0" data-author="SkalskiP,onuralpszr"></p>
|
||||
</a>
|
||||
<a href="/develop/notebooks/count-objects-crossing-the-line">
|
||||
<p class="card repo-card" data-name="Count Objects Crossing the Line"
|
||||
data-labels="ANNOTATORS,LINE ZONE,TRACKING" data-version="v0.18.0" data-author="SkalskiP"></p>
|
||||
data-labels="ANNOTATORS,LINE ZONE,TRACKING" data-version="v0.26.0" data-author="SkalskiP"></p>
|
||||
</a>
|
||||
<a href="/develop/notebooks/zero-shot-object-detection-with-yolo-world">
|
||||
<p class="card repo-card" data-name="Zero-Shot Object Detection with YOLO-World"
|
||||
data-labels="ANNOTATORS,DETECTION,INFERENCE" data-version="v0.19.0" data-author="SkalskiP"></p>
|
||||
data-labels="ANNOTATORS,DETECTION,INFERENCE" data-version="v0.26.0" data-author="SkalskiP"></p>
|
||||
</a>
|
||||
<a href="/develop/notebooks/download-supervision-assets">
|
||||
<p class="card repo-card" data-name="Downloading Supervision Assets" data-labels="ASSETS" data-version="v0.18.0"
|
||||
|
|
@ -28,7 +28,7 @@
|
|||
</a>
|
||||
<a href="/develop/notebooks/annotate-video-with-detections">
|
||||
<p class="card repo-card" data-name="Annotate Video with Detections" data-labels="INFERENCE,YOLOV8"
|
||||
data-version="v0.18.0" data-author="nickherrig"></p>
|
||||
data-version="v0.26.0" data-author="nickherrig"></p>
|
||||
</a>
|
||||
<a href="/develop/notebooks/object-tracking">
|
||||
<p class="card repo-card" data-name="Object Tracking" data-labels="TRACKING, ANNOTATOR" data-version="v0.18.0"
|
||||
|
|
@ -36,23 +36,23 @@
|
|||
</a>
|
||||
<a href="/develop/notebooks/occupancy_analytics">
|
||||
<p class="card repo-card" data-name="Analyzing Zone Occupancy" data-labels="ANNOTATOR,DETECTION,ZONES"
|
||||
data-version="v0.19.0" data-author="stellasphere"></p>
|
||||
data-version="v0.26.0" data-author="stellasphere"></p>
|
||||
</a>
|
||||
<a href="/develop/notebooks/evaluating-alignment-of-text-to-image-diffusion-models">
|
||||
<p class="card repo-card" data-name="Evaluating Alignment of Text-to-image Diffusion Models"
|
||||
data-labels="ANNOTATORS,YOLO WORLD" data-version="v0.19.0rc5" data-author="iamhatesz"></p>
|
||||
data-labels="ANNOTATORS,YOLO WORLD" data-version="v0.26.0" data-author="iamhatesz"></p>
|
||||
</a>
|
||||
<a href="/develop/notebooks/serialise-detections-to-csv">
|
||||
<p class="card repo-card" data-name="Serialise Detections to a CSV File"
|
||||
data-labels="DETECTIONS,CSV SINK,INFERENCE" data-version="v0.21.0" data-author="onuralpszr"></p>
|
||||
data-labels="DETECTIONS,CSV SINK,INFERENCE" data-version="v0.26.0" data-author="onuralpszr"></p>
|
||||
</a>
|
||||
<a href="/develop/notebooks/serialise-detections-to-json">
|
||||
<p class="card repo-card" data-name="Serialise Detections to a JSON File"
|
||||
data-labels="DETECTIONS,JSON SINK,INFERENCE" data-version="v0.21.0" data-author="onuralpszr"></p>
|
||||
data-labels="DETECTIONS,JSON SINK,INFERENCE" data-version="v0.26.0" data-author="onuralpszr"></p>
|
||||
</a>
|
||||
<a href="/develop/notebooks/small-object-detection-with-sahi">
|
||||
<p class="card repo-card" data-name="Small Object Detection with SAHI"
|
||||
data-labels="DETECTIONS,SAHI,SMALL,OBJECT,INFERENCE" data-version="v0.23.0" data-author="ediardo"></p>
|
||||
data-labels="DETECTIONS,SAHI,SMALL,OBJECT,INFERENCE" data-version="v0.26.0" data-author="ediardo"></p>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -58,6 +58,8 @@ Documentation = "https://supervision.roboflow.com/latest/"
|
|||
metrics = [
|
||||
"pandas>=2.0.0",
|
||||
]
|
||||
|
||||
[dependency-groups]
|
||||
dev = [
|
||||
"pytest>=7.2.2,<9.0.0",
|
||||
"tox>=4.11.4",
|
||||
|
|
|
|||
|
|
@ -864,6 +864,7 @@ class Detections:
|
|||
qwen_2_5_vl_result,
|
||||
input_wh=(1000, 1000),
|
||||
resolution_wh=(1000, 1000),
|
||||
classes=['cat', 'dog'],
|
||||
)
|
||||
detections.xyxy
|
||||
# array([[139., 768., 315., 954.], [366., 679., 536., 849.]])
|
||||
|
|
@ -873,6 +874,9 @@ class Detections:
|
|||
|
||||
detections.data
|
||||
# {'class_name': array(['cat', 'dog'], dtype='<U10')}
|
||||
|
||||
detections.class_id
|
||||
# array([0, 1])
|
||||
```
|
||||
|
||||
Examples:
|
||||
|
|
@ -890,6 +894,7 @@ class Detections:
|
|||
sv.LMM.GOOGLE_GEMINI_2_0,
|
||||
gemini_response_text,
|
||||
resolution_wh=(1000, 1000),
|
||||
classes=['cat', 'dog'],
|
||||
)
|
||||
|
||||
detections.xyxy
|
||||
|
|
@ -900,6 +905,9 @@ class Detections:
|
|||
|
||||
detections.data
|
||||
# {'class_name': array(['cat', 'dog'], dtype='<U26')}
|
||||
|
||||
detections.class_id
|
||||
# array([0, 1])
|
||||
```
|
||||
|
||||
Examples:
|
||||
|
|
@ -1022,6 +1030,7 @@ class Detections:
|
|||
qwen_2_5_vl_result,
|
||||
input_wh=(1000, 1000),
|
||||
resolution_wh=(1000, 1000),
|
||||
classes=['cat', 'dog'],
|
||||
)
|
||||
detections.xyxy
|
||||
# array([[139., 768., 315., 954.], [366., 679., 536., 849.]])
|
||||
|
|
@ -1031,6 +1040,9 @@ class Detections:
|
|||
|
||||
detections.data
|
||||
# {'class_name': array(['cat', 'dog'], dtype='<U10')}
|
||||
|
||||
detections.class_id
|
||||
# array([0, 1])
|
||||
```
|
||||
|
||||
Examples:
|
||||
|
|
@ -1048,6 +1060,7 @@ class Detections:
|
|||
sv.VLM.GOOGLE_GEMINI_2_0,
|
||||
gemini_response_text,
|
||||
resolution_wh=(1000, 1000),
|
||||
classes=['cat', 'dog'],
|
||||
)
|
||||
|
||||
detections.xyxy
|
||||
|
|
@ -1058,6 +1071,9 @@ class Detections:
|
|||
|
||||
detections.data
|
||||
# {'class_name': array(['cat', 'dog'], dtype='<U26')}
|
||||
|
||||
detections.class_id
|
||||
# array([0, 1])
|
||||
```
|
||||
|
||||
Examples:
|
||||
|
|
@ -1119,10 +1135,13 @@ class Detections:
|
|||
|
||||
return cls(xyxy=xyxy, mask=mask, data=data)
|
||||
|
||||
if vlm == VLM.GOOGLE_GEMINI_2_0 or vlm == VLM.GOOGLE_GEMINI_2_5:
|
||||
xyxy, class_name = from_google_gemini(result, **kwargs)
|
||||
if (
|
||||
vlm == VLM.GOOGLE_GEMINI_2_0
|
||||
or vlm == VLM.GOOGLE_GEMINI_2_5
|
||||
):
|
||||
xyxy, class_id, class_name = from_google_gemini(result, **kwargs)
|
||||
data = {CLASS_NAME_DATA_FIELD: class_name}
|
||||
return cls(xyxy=xyxy, data=data)
|
||||
return cls(xyxy=xyxy, class_id=class_id, data=data)
|
||||
|
||||
if vlm == VLM.MOONDREAM:
|
||||
xyxy = from_moondream(result, **kwargs)
|
||||
|
|
|
|||
|
|
@ -56,9 +56,8 @@ ALLOWED_ARGUMENTS: Dict[VLM, List[str]] = {
|
|||
VLM.PALIGEMMA: ["resolution_wh", "classes"],
|
||||
VLM.FLORENCE_2: ["resolution_wh"],
|
||||
VLM.QWEN_2_5_VL: ["input_wh", "resolution_wh", "classes"],
|
||||
VLM.GOOGLE_GEMINI_2_0: ["resolution_wh"],
|
||||
VLM.GOOGLE_GEMINI_2_5: ["resolution_wh"],
|
||||
VLM.MOONDREAM: ["resolution_wh"],
|
||||
VLM.GOOGLE_GEMINI_2_0: ["resolution_wh", "classes"],
|
||||
VLM.GOOGLE_GEMINI_2_5: ["resolution_wh", "classes"],
|
||||
}
|
||||
|
||||
SUPPORTED_TASKS_FLORENCE_2 = [
|
||||
|
|
@ -339,7 +338,8 @@ def from_florence_2(
|
|||
def from_google_gemini(
|
||||
result: str,
|
||||
resolution_wh: Tuple[int, int],
|
||||
) -> Tuple[np.ndarray, np.ndarray]:
|
||||
classes: Optional[List[str]] = None,
|
||||
) -> Tuple[np.ndarray, Optional[np.ndarray], np.ndarray]:
|
||||
"""
|
||||
Parse and scale bounding boxes from Google Gemini style
|
||||
[JSON output](https://ai.google.dev/gemini-api/docs/vision?lang=python).
|
||||
|
|
@ -363,11 +363,16 @@ def from_google_gemini(
|
|||
Args:
|
||||
result: String containing the JSON snippet enclosed by triple backticks.
|
||||
resolution_wh: (output_width, output_height) to which we rescale the boxes.
|
||||
classes: Optional list of valid class names. If provided, returned boxes/labels
|
||||
are filtered to only those classes found here.
|
||||
|
||||
Returns:
|
||||
xyxy (np.ndarray): An array of shape `(n, 4)` containing
|
||||
the bounding boxes coordinates in format `[x1, y1, x2, y2]`
|
||||
labels: (np.ndarray): An array of shape `(n,)` containing
|
||||
class_id (Optional[np.ndarray]): An array of shape `(n,)` containing
|
||||
the class indices for each bounding box (or None if `classes` is not
|
||||
provided)
|
||||
class_name (np.ndarray): An array of shape `(n,)` containing
|
||||
the class labels for each bounding box
|
||||
|
||||
"""
|
||||
|
|
@ -388,7 +393,7 @@ def from_google_gemini(
|
|||
try:
|
||||
data = json.loads(result)
|
||||
except json.JSONDecodeError:
|
||||
return np.empty((0, 4)), np.empty((0,), dtype=str)
|
||||
return np.empty((0, 4)), None, np.empty((0,), dtype=str)
|
||||
|
||||
labels = []
|
||||
xyxy = []
|
||||
|
|
@ -407,9 +412,20 @@ def from_google_gemini(
|
|||
)
|
||||
|
||||
if not xyxy:
|
||||
return np.empty((0, 4)), np.empty((0,), dtype=str)
|
||||
return np.empty((0, 4)), None, np.empty((0,), dtype=str)
|
||||
|
||||
xyxy = np.array(xyxy)
|
||||
class_name = np.array(labels)
|
||||
class_id = None
|
||||
|
||||
if classes is not None:
|
||||
mask = np.array([name in classes for name in class_name], dtype=bool)
|
||||
xyxy = xyxy[mask]
|
||||
class_name = class_name[mask]
|
||||
class_id = np.array([classes.index(name) for name in class_name])
|
||||
|
||||
return xyxy, class_id, class_name
|
||||
|
||||
return np.array(xyxy), np.array(labels)
|
||||
|
||||
|
||||
def from_moondream(
|
||||
|
|
|
|||
|
|
@ -360,119 +360,140 @@ def test_from_qwen_2_5_vl(
|
|||
np.testing.assert_array_equal(class_name, expected_results[2])
|
||||
|
||||
|
||||
def test_from_google_gemini() -> None:
|
||||
result = """```json
|
||||
[
|
||||
{"box_2d": [10, 20, 110, 120], "label": "cat"},
|
||||
{"box_2d": [50, 100, 150, 200], "label": "dog"}
|
||||
]
|
||||
```"""
|
||||
resolution_wh = (640, 480)
|
||||
xyxy, class_name = from_google_gemini(
|
||||
result=result,
|
||||
resolution_wh=resolution_wh,
|
||||
)
|
||||
np.testing.assert_array_equal(
|
||||
xyxy, np.array([[12.8, 4.8, 76.8, 52.8], [64.0, 24.0, 128.0, 72.0]])
|
||||
)
|
||||
np.testing.assert_array_equal(class_name, np.array(["cat", "dog"]))
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"exception, result, resolution_wh, expected_results",
|
||||
"exception, result, resolution_wh, classes, expected_results",
|
||||
[
|
||||
(
|
||||
does_not_raise(),
|
||||
{},
|
||||
(640, 480),
|
||||
np.empty((0, 4)),
|
||||
), # empty dict
|
||||
"random text",
|
||||
(1000, 1000),
|
||||
None,
|
||||
(np.empty((0, 4)), None, np.empty(0, dtype=str)),
|
||||
), # random text without JSON format
|
||||
(
|
||||
does_not_raise(),
|
||||
{"objects": []},
|
||||
(640, 480),
|
||||
np.empty((0, 4)),
|
||||
), # empty objects list
|
||||
"```json\ninvalid json\n```",
|
||||
(1000, 1000),
|
||||
None,
|
||||
(np.empty((0, 4)), None, np.empty(0, dtype=str)),
|
||||
), # invalid JSON within code blocks
|
||||
(
|
||||
does_not_raise(),
|
||||
{"objects": "not a list"},
|
||||
(640, 480),
|
||||
np.empty((0, 4)),
|
||||
), # objects is not a list
|
||||
"```json\n[]\n```",
|
||||
(1000, 1000),
|
||||
None,
|
||||
(np.empty((0, 4)), None, np.empty(0, dtype=str)),
|
||||
), # empty JSON array
|
||||
(
|
||||
does_not_raise(),
|
||||
{
|
||||
"objects": [
|
||||
{"x_min": 0.1, "y_min": 0.2, "x_max": 0.3, "y_max": 0.4},
|
||||
]
|
||||
},
|
||||
(640, 480),
|
||||
np.array([[64.0, 96.0, 192.0, 192.0]]),
|
||||
), # single box
|
||||
"""```json
|
||||
[
|
||||
{"box_2d": [100, 200, 300, 400], "label": "cat"}
|
||||
]
|
||||
```""",
|
||||
(1000, 500),
|
||||
None,
|
||||
(
|
||||
np.array([[200.0, 50.0, 400.0, 150.0]]),
|
||||
None,
|
||||
np.array(["cat"], dtype=str),
|
||||
),
|
||||
), # single valid box with coordinate scaling
|
||||
(
|
||||
does_not_raise(),
|
||||
{
|
||||
"objects": [
|
||||
{"x_min": 0.1, "y_min": 0.2, "x_max": 0.3, "y_max": 0.4},
|
||||
{"x_min": 0.5, "y_min": 0.6, "x_max": 0.7, "y_max": 0.8},
|
||||
]
|
||||
},
|
||||
"""```json
|
||||
[
|
||||
{"box_2d": [10, 20, 110, 120], "label": "cat"},
|
||||
{"box_2d": [50, 100, 150, 200], "label": "dog"}
|
||||
]
|
||||
```""",
|
||||
(640, 480),
|
||||
np.array([[64.0, 96.0, 192.0, 192.0], [320.0, 288.0, 448.0, 384.0]]),
|
||||
), # multiple boxes
|
||||
None,
|
||||
(
|
||||
np.array([[12.8, 4.8, 76.8, 52.8], [64.0, 24.0, 128.0, 72.0]]),
|
||||
None,
|
||||
np.array(["cat", "dog"], dtype=str),
|
||||
),
|
||||
), # multiple valid boxes without class filtering
|
||||
(
|
||||
does_not_raise(),
|
||||
{
|
||||
"objects": [
|
||||
{"x_min": 0.1, "y_min": 0.2}, # missing x_max, y_max
|
||||
{"x_min": 0.5, "y_min": 0.6, "x_max": 0.7, "y_max": 0.8},
|
||||
]
|
||||
},
|
||||
"""```json
|
||||
[
|
||||
{"box_2d": [10, 20, 110, 120], "label": "cat"}
|
||||
]
|
||||
```""",
|
||||
(640, 480),
|
||||
np.array([[320.0, 288.0, 448.0, 384.0]]),
|
||||
), # partial valid boxes
|
||||
["dog", "person"],
|
||||
(np.empty((0, 4)), np.empty(0, dtype=int), np.empty(0, dtype=str)),
|
||||
), # class mismatch with filter
|
||||
(
|
||||
does_not_raise(),
|
||||
{
|
||||
"objects": [
|
||||
{"x_min": 0.0, "y_min": 0.0, "x_max": 1.0, "y_max": 1.0},
|
||||
]
|
||||
},
|
||||
(1000, 800),
|
||||
np.array([[0.0, 0.0, 1000.0, 800.0]]),
|
||||
), # full image box
|
||||
"""```json
|
||||
[
|
||||
{"box_2d": [10, 20, 110, 120], "label": "cat"},
|
||||
{"box_2d": [50, 100, 150, 200], "label": "dog"}
|
||||
]
|
||||
```""",
|
||||
(640, 480),
|
||||
["person", "dog"],
|
||||
(
|
||||
np.array([[64.0, 24.0, 128.0, 72.0]]),
|
||||
np.array([1]),
|
||||
np.array(["dog"], dtype=str),
|
||||
),
|
||||
), # partial class filtering
|
||||
(
|
||||
does_not_raise(),
|
||||
"""```json
|
||||
[
|
||||
{"box_2d": [10, 20, 110, 120], "label": "cat"},
|
||||
{"box_2d": [50, 100, 150, 200], "label": "dog"}
|
||||
]
|
||||
```""",
|
||||
(640, 480),
|
||||
["cat", "dog"],
|
||||
(
|
||||
np.array([[12.8, 4.8, 76.8, 52.8], [64.0, 24.0, 128.0, 72.0]]),
|
||||
np.array([0, 1]),
|
||||
np.array(["cat", "dog"]),
|
||||
),
|
||||
), # complete class filtering with multiple boxes
|
||||
(
|
||||
pytest.raises(ValueError),
|
||||
{
|
||||
"objects": [
|
||||
{"x_min": 0.1, "y_min": 0.2, "x_max": 0.3, "y_max": 0.4},
|
||||
]
|
||||
},
|
||||
"""```json
|
||||
[
|
||||
{"box_2d": [10, 20, 110, 120], "label": "cat"}
|
||||
]
|
||||
```""",
|
||||
(0, 480),
|
||||
None,
|
||||
), # zero width -> ValueError
|
||||
None,
|
||||
), # zero resolution width -> ValueError
|
||||
(
|
||||
pytest.raises(ValueError),
|
||||
{
|
||||
"objects": [
|
||||
{"x_min": 0.1, "y_min": 0.2, "x_max": 0.3, "y_max": 0.4},
|
||||
]
|
||||
},
|
||||
"""```json
|
||||
[
|
||||
{"box_2d": [10, 20, 110, 120], "label": "cat"}
|
||||
]
|
||||
```""",
|
||||
(640, -100),
|
||||
None,
|
||||
), # negative height -> ValueError
|
||||
None,
|
||||
), # negative resolution height -> ValueError
|
||||
],
|
||||
)
|
||||
def test_from_moondream(
|
||||
def test_from_google_gemini(
|
||||
exception,
|
||||
result: dict,
|
||||
result: str,
|
||||
resolution_wh: Tuple[int, int],
|
||||
expected_results,
|
||||
classes: Optional[List[str]],
|
||||
expected_results: Tuple[np.ndarray, Optional[np.ndarray], np.ndarray],
|
||||
) -> None:
|
||||
with exception:
|
||||
xyxy = from_moondream(
|
||||
result=result,
|
||||
resolution_wh=resolution_wh,
|
||||
xyxy, class_id, class_name = from_google_gemini(
|
||||
result=result, resolution_wh=resolution_wh, classes=classes
|
||||
)
|
||||
if expected_results is not None:
|
||||
np.testing.assert_array_equal(xyxy, expected_results)
|
||||
np.testing.assert_array_equal(xyxy, expected_results[0])
|
||||
np.testing.assert_array_equal(class_id, expected_results[1])
|
||||
np.testing.assert_array_equal(class_name, expected_results[2])
|
||||
|
|
|
|||
Loading…
Reference in New Issue