viso-sdk-python 1.1.17__tar.gz → 1.1.18__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {viso_sdk_python-1.1.17 → viso_sdk_python-1.1.18}/CHANGELOG.md +3 -0
- {viso_sdk_python-1.1.17 → viso_sdk_python-1.1.18}/PKG-INFO +1 -1
- {viso_sdk_python-1.1.17 → viso_sdk_python-1.1.18}/tests/unit/test_model_fetch.py +0 -1
- viso_sdk_python-1.1.18/tests/unit/test_nms.py +231 -0
- {viso_sdk_python-1.1.17 → viso_sdk_python-1.1.18}/tests/unit/test_stream_cleanup.py +1 -3
- {viso_sdk_python-1.1.17 → viso_sdk_python-1.1.18}/tests/unit/test_trace_malloc.py +0 -1
- {viso_sdk_python-1.1.17 → viso_sdk_python-1.1.18}/tests/unit/test_viso_data.py +0 -2
- {viso_sdk_python-1.1.17 → viso_sdk_python-1.1.18}/viso_sdk/_version.c +7 -7
- {viso_sdk_python-1.1.17 → viso_sdk_python-1.1.18}/viso_sdk/_version.py +1 -1
- viso_sdk_python-1.1.18/viso_sdk/detection/nms.c +12493 -0
- viso_sdk_python-1.1.18/viso_sdk/detection/nms.py +114 -0
- {viso_sdk_python-1.1.17 → viso_sdk_python-1.1.18}/viso_sdk/detection/object.c +40 -40
- {viso_sdk_python-1.1.17 → viso_sdk_python-1.1.18}/viso_sdk/detection/object.py +1 -1
- {viso_sdk_python-1.1.17 → viso_sdk_python-1.1.18}/viso_sdk_python.egg-info/PKG-INFO +1 -1
- {viso_sdk_python-1.1.17 → viso_sdk_python-1.1.18}/viso_sdk_python.egg-info/SOURCES.txt +3 -4
- viso_sdk_python-1.1.17/viso_sdk/detection/utils/__init__.c +0 -4589
- viso_sdk_python-1.1.17/viso_sdk/detection/utils/nms.c +0 -13329
- viso_sdk_python-1.1.17/viso_sdk/detection/utils/nms.py +0 -272
- viso_sdk_python-1.1.17/viso_sdk/model/__init__.py +0 -0
- {viso_sdk_python-1.1.17 → viso_sdk_python-1.1.18}/AUTHORS +0 -0
- {viso_sdk_python-1.1.17 → viso_sdk_python-1.1.18}/COPYING +0 -0
- {viso_sdk_python-1.1.17 → viso_sdk_python-1.1.18}/MANIFEST.in +0 -0
- {viso_sdk_python-1.1.17 → viso_sdk_python-1.1.18}/README.md +0 -0
- {viso_sdk_python-1.1.17 → viso_sdk_python-1.1.18}/docs/Makefile +0 -0
- {viso_sdk_python-1.1.17 → viso_sdk_python-1.1.18}/docs/__init__.py +0 -0
- {viso_sdk_python-1.1.17 → viso_sdk_python-1.1.18}/docs/conf.py +0 -0
- {viso_sdk_python-1.1.17 → viso_sdk_python-1.1.18}/docs/ext/__init__.py +0 -0
- {viso_sdk_python-1.1.17 → viso_sdk_python-1.1.18}/docs/ext/docstrings.py +0 -0
- {viso_sdk_python-1.1.17 → viso_sdk_python-1.1.18}/docs/ext/manager_tmpl.j2 +0 -0
- {viso_sdk_python-1.1.17 → viso_sdk_python-1.1.18}/docs/index.rst +0 -0
- {viso_sdk_python-1.1.17 → viso_sdk_python-1.1.18}/docs/make.bat +0 -0
- {viso_sdk_python-1.1.17 → viso_sdk_python-1.1.18}/docs/source/modules.rst +0 -0
- {viso_sdk_python-1.1.17 → viso_sdk_python-1.1.18}/docs/source/viso_sdk._version.rst +0 -0
- {viso_sdk_python-1.1.17 → viso_sdk_python-1.1.18}/docs/source/viso_sdk.constants.constants.rst +0 -0
- {viso_sdk_python-1.1.17 → viso_sdk_python-1.1.18}/docs/source/viso_sdk.constants.modules.rst +0 -0
- {viso_sdk_python-1.1.17 → viso_sdk_python-1.1.18}/docs/source/viso_sdk.constants.rst +0 -0
- {viso_sdk_python-1.1.17 → viso_sdk_python-1.1.18}/docs/source/viso_sdk.constants.variables.rst +0 -0
- {viso_sdk_python-1.1.17 → viso_sdk_python-1.1.18}/docs/source/viso_sdk.edge.common.rst +0 -0
- {viso_sdk_python-1.1.17 → viso_sdk_python-1.1.18}/docs/source/viso_sdk.edge.rst +0 -0
- {viso_sdk_python-1.1.17 → viso_sdk_python-1.1.18}/docs/source/viso_sdk.logging.logger.rst +0 -0
- {viso_sdk_python-1.1.17 → viso_sdk_python-1.1.18}/docs/source/viso_sdk.logging.rst +0 -0
- {viso_sdk_python-1.1.17 → viso_sdk_python-1.1.18}/docs/source/viso_sdk.model.download.rst +0 -0
- {viso_sdk_python-1.1.17 → viso_sdk_python-1.1.18}/docs/source/viso_sdk.model.rst +0 -0
- {viso_sdk_python-1.1.17 → viso_sdk_python-1.1.18}/docs/source/viso_sdk.mqtt.mqtt_wrapper.rst +0 -0
- {viso_sdk_python-1.1.17 → viso_sdk_python-1.1.18}/docs/source/viso_sdk.mqtt.rst +0 -0
- {viso_sdk_python-1.1.17 → viso_sdk_python-1.1.18}/docs/source/viso_sdk.mqtt.utils.rst +0 -0
- {viso_sdk_python-1.1.17 → viso_sdk_python-1.1.18}/docs/source/viso_sdk.nodered.flow.rst +0 -0
- {viso_sdk_python-1.1.17 → viso_sdk_python-1.1.18}/docs/source/viso_sdk.nodered.rst +0 -0
- {viso_sdk_python-1.1.17 → viso_sdk_python-1.1.18}/docs/source/viso_sdk.redis.redis_wrapper.rst +0 -0
- {viso_sdk_python-1.1.17 → viso_sdk_python-1.1.18}/docs/source/viso_sdk.redis.rst +0 -0
- {viso_sdk_python-1.1.17 → viso_sdk_python-1.1.18}/docs/source/viso_sdk.rst +0 -0
- {viso_sdk_python-1.1.17 → viso_sdk_python-1.1.18}/docs/source/viso_sdk.status.rst +0 -0
- {viso_sdk_python-1.1.17 → viso_sdk_python-1.1.18}/docs/source/viso_sdk.status.status_logger.rst +0 -0
- {viso_sdk_python-1.1.17 → viso_sdk_python-1.1.18}/pyproject.toml +0 -0
- {viso_sdk_python-1.1.17 → viso_sdk_python-1.1.18}/requirements-dev.txt +0 -0
- {viso_sdk_python-1.1.17 → viso_sdk_python-1.1.18}/requirements-docker.txt +0 -0
- {viso_sdk_python-1.1.17 → viso_sdk_python-1.1.18}/requirements-docs.txt +0 -0
- {viso_sdk_python-1.1.17 → viso_sdk_python-1.1.18}/requirements-lint.txt +0 -0
- {viso_sdk_python-1.1.17 → viso_sdk_python-1.1.18}/requirements-test.txt +0 -0
- {viso_sdk_python-1.1.17 → viso_sdk_python-1.1.18}/requirements.txt +0 -0
- {viso_sdk_python-1.1.17 → viso_sdk_python-1.1.18}/setup.cfg +0 -0
- {viso_sdk_python-1.1.17 → viso_sdk_python-1.1.18}/setup.py +0 -0
- {viso_sdk_python-1.1.17 → viso_sdk_python-1.1.18}/tests/__init__.py +0 -0
- {viso_sdk_python-1.1.17 → viso_sdk_python-1.1.18}/tests/conftest.py +0 -0
- {viso_sdk_python-1.1.17 → viso_sdk_python-1.1.18}/tests/fixtures/flow.json +0 -0
- {viso_sdk_python-1.1.17 → viso_sdk_python-1.1.18}/tests/integration/__init__.py +0 -0
- {viso_sdk_python-1.1.17 → viso_sdk_python-1.1.18}/tests/integration/test_status.py +0 -0
- {viso_sdk_python-1.1.17 → viso_sdk_python-1.1.18}/tests/meta/__init__.py +0 -0
- {viso_sdk_python-1.1.17 → viso_sdk_python-1.1.18}/tests/meta/test_ensure_type_hints.py +0 -0
- {viso_sdk_python-1.1.17 → viso_sdk_python-1.1.18}/tests/smoke/__init__.py +0 -0
- {viso_sdk_python-1.1.17 → viso_sdk_python-1.1.18}/tests/smoke/test_dists.py +0 -0
- {viso_sdk_python-1.1.17 → viso_sdk_python-1.1.18}/tests/unit/__init__.py +0 -0
- {viso_sdk_python-1.1.17 → viso_sdk_python-1.1.18}/tests/unit/test_flow.py +0 -0
- {viso_sdk_python-1.1.17 → viso_sdk_python-1.1.18}/tests/unit/test_logger.py +0 -0
- {viso_sdk_python-1.1.17 → viso_sdk_python-1.1.18}/tests/unit/test_redis_utils.py +0 -0
- {viso_sdk_python-1.1.17 → viso_sdk_python-1.1.18}/tests/unit/test_redis_wrapper.py +0 -0
- {viso_sdk_python-1.1.17 → viso_sdk_python-1.1.18}/tox.ini +0 -0
- {viso_sdk_python-1.1.17 → viso_sdk_python-1.1.18}/viso_sdk/__init__.c +0 -0
- {viso_sdk_python-1.1.17 → viso_sdk_python-1.1.18}/viso_sdk/__init__.py +0 -0
- {viso_sdk_python-1.1.17 → viso_sdk_python-1.1.18}/viso_sdk/constants/__init__.c +0 -0
- {viso_sdk_python-1.1.17 → viso_sdk_python-1.1.18}/viso_sdk/constants/__init__.py +0 -0
- {viso_sdk_python-1.1.17 → viso_sdk_python-1.1.18}/viso_sdk/constants/constants.c +0 -0
- {viso_sdk_python-1.1.17 → viso_sdk_python-1.1.18}/viso_sdk/constants/constants.py +0 -0
- {viso_sdk_python-1.1.17 → viso_sdk_python-1.1.18}/viso_sdk/constants/modules.c +0 -0
- {viso_sdk_python-1.1.17 → viso_sdk_python-1.1.18}/viso_sdk/constants/modules.py +0 -0
- {viso_sdk_python-1.1.17 → viso_sdk_python-1.1.18}/viso_sdk/constants/variables.c +0 -0
- {viso_sdk_python-1.1.17 → viso_sdk_python-1.1.18}/viso_sdk/constants/variables.py +0 -0
- {viso_sdk_python-1.1.17 → viso_sdk_python-1.1.18}/viso_sdk/debug/__init__.c +0 -0
- {viso_sdk_python-1.1.17 → viso_sdk_python-1.1.18}/viso_sdk/debug/__init__.py +0 -0
- {viso_sdk_python-1.1.17 → viso_sdk_python-1.1.18}/viso_sdk/debug/trace_malloc.c +0 -0
- {viso_sdk_python-1.1.17 → viso_sdk_python-1.1.18}/viso_sdk/debug/trace_malloc.py +0 -0
- {viso_sdk_python-1.1.17 → viso_sdk_python-1.1.18}/viso_sdk/detection/__init__.c +0 -0
- {viso_sdk_python-1.1.17 → viso_sdk_python-1.1.18}/viso_sdk/detection/__init__.py +0 -0
- {viso_sdk_python-1.1.17 → viso_sdk_python-1.1.18}/viso_sdk/detection/model_utils.c +0 -0
- {viso_sdk_python-1.1.17 → viso_sdk_python-1.1.18}/viso_sdk/detection/model_utils.py +0 -0
- {viso_sdk_python-1.1.17 → viso_sdk_python-1.1.18}/viso_sdk/edge/__init__.c +0 -0
- {viso_sdk_python-1.1.17/viso_sdk/detection/utils → viso_sdk_python-1.1.18/viso_sdk/edge}/__init__.py +0 -0
- {viso_sdk_python-1.1.17 → viso_sdk_python-1.1.18}/viso_sdk/edge/common.c +0 -0
- {viso_sdk_python-1.1.17 → viso_sdk_python-1.1.18}/viso_sdk/edge/common.py +0 -0
- {viso_sdk_python-1.1.17 → viso_sdk_python-1.1.18}/viso_sdk/logging/__init__.c +0 -0
- {viso_sdk_python-1.1.17 → viso_sdk_python-1.1.18}/viso_sdk/logging/__init__.py +0 -0
- {viso_sdk_python-1.1.17 → viso_sdk_python-1.1.18}/viso_sdk/logging/log_id.c +0 -0
- {viso_sdk_python-1.1.17 → viso_sdk_python-1.1.18}/viso_sdk/logging/log_id.py +0 -0
- {viso_sdk_python-1.1.17 → viso_sdk_python-1.1.18}/viso_sdk/logging/logger.c +0 -0
- {viso_sdk_python-1.1.17 → viso_sdk_python-1.1.18}/viso_sdk/logging/logger.py +0 -0
- {viso_sdk_python-1.1.17 → viso_sdk_python-1.1.18}/viso_sdk/model/__init__.c +0 -0
- {viso_sdk_python-1.1.17/viso_sdk/edge → viso_sdk_python-1.1.18/viso_sdk/model}/__init__.py +0 -0
- {viso_sdk_python-1.1.17 → viso_sdk_python-1.1.18}/viso_sdk/model/fetch.c +0 -0
- {viso_sdk_python-1.1.17 → viso_sdk_python-1.1.18}/viso_sdk/model/fetch.py +0 -0
- {viso_sdk_python-1.1.17 → viso_sdk_python-1.1.18}/viso_sdk/mqtt/__init__.c +0 -0
- {viso_sdk_python-1.1.17 → viso_sdk_python-1.1.18}/viso_sdk/mqtt/__init__.py +0 -0
- {viso_sdk_python-1.1.17 → viso_sdk_python-1.1.18}/viso_sdk/mqtt/mqtt_wrapper.c +0 -0
- {viso_sdk_python-1.1.17 → viso_sdk_python-1.1.18}/viso_sdk/mqtt/mqtt_wrapper.py +0 -0
- {viso_sdk_python-1.1.17 → viso_sdk_python-1.1.18}/viso_sdk/mqtt/utils.c +0 -0
- {viso_sdk_python-1.1.17 → viso_sdk_python-1.1.18}/viso_sdk/mqtt/utils.py +0 -0
- {viso_sdk_python-1.1.17 → viso_sdk_python-1.1.18}/viso_sdk/nodered/__init__.c +0 -0
- {viso_sdk_python-1.1.17 → viso_sdk_python-1.1.18}/viso_sdk/nodered/__init__.py +0 -0
- {viso_sdk_python-1.1.17 → viso_sdk_python-1.1.18}/viso_sdk/nodered/flow.c +0 -0
- {viso_sdk_python-1.1.17 → viso_sdk_python-1.1.18}/viso_sdk/nodered/flow.py +0 -0
- {viso_sdk_python-1.1.17 → viso_sdk_python-1.1.18}/viso_sdk/py.typed +0 -0
- {viso_sdk_python-1.1.17 → viso_sdk_python-1.1.18}/viso_sdk/redis/__init__.c +0 -0
- {viso_sdk_python-1.1.17 → viso_sdk_python-1.1.18}/viso_sdk/redis/__init__.py +0 -0
- {viso_sdk_python-1.1.17 → viso_sdk_python-1.1.18}/viso_sdk/redis/redis_wrapper.c +0 -0
- {viso_sdk_python-1.1.17 → viso_sdk_python-1.1.18}/viso_sdk/redis/redis_wrapper.py +0 -0
- {viso_sdk_python-1.1.17 → viso_sdk_python-1.1.18}/viso_sdk/redis/stream_cleanup.c +0 -0
- {viso_sdk_python-1.1.17 → viso_sdk_python-1.1.18}/viso_sdk/redis/stream_cleanup.py +0 -0
- {viso_sdk_python-1.1.17 → viso_sdk_python-1.1.18}/viso_sdk/redis/utils.c +0 -0
- {viso_sdk_python-1.1.17 → viso_sdk_python-1.1.18}/viso_sdk/redis/utils.py +0 -0
- {viso_sdk_python-1.1.17 → viso_sdk_python-1.1.18}/viso_sdk/redis/viso_data.c +0 -0
- {viso_sdk_python-1.1.17 → viso_sdk_python-1.1.18}/viso_sdk/redis/viso_data.py +0 -0
- {viso_sdk_python-1.1.17 → viso_sdk_python-1.1.18}/viso_sdk/roi/__init__.c +0 -0
- {viso_sdk_python-1.1.17 → viso_sdk_python-1.1.18}/viso_sdk/roi/__init__.py +0 -0
- {viso_sdk_python-1.1.17 → viso_sdk_python-1.1.18}/viso_sdk/roi/polygon_roi_manager.c +0 -0
- {viso_sdk_python-1.1.17 → viso_sdk_python-1.1.18}/viso_sdk/roi/polygon_roi_manager.py +0 -0
- {viso_sdk_python-1.1.17 → viso_sdk_python-1.1.18}/viso_sdk/status/__init__.c +0 -0
- {viso_sdk_python-1.1.17 → viso_sdk_python-1.1.18}/viso_sdk/status/__init__.py +0 -0
- {viso_sdk_python-1.1.17 → viso_sdk_python-1.1.18}/viso_sdk/status/status_logger.c +0 -0
- {viso_sdk_python-1.1.17 → viso_sdk_python-1.1.18}/viso_sdk/status/status_logger.py +0 -0
- {viso_sdk_python-1.1.17 → viso_sdk_python-1.1.18}/viso_sdk/structures/__init__.c +0 -0
- {viso_sdk_python-1.1.17 → viso_sdk_python-1.1.18}/viso_sdk/structures/__init__.py +0 -0
- {viso_sdk_python-1.1.17 → viso_sdk_python-1.1.18}/viso_sdk/structures/rollingq.c +0 -0
- {viso_sdk_python-1.1.17 → viso_sdk_python-1.1.18}/viso_sdk/structures/rollingq.py +0 -0
- {viso_sdk_python-1.1.17 → viso_sdk_python-1.1.18}/viso_sdk_python.egg-info/dependency_links.txt +0 -0
- {viso_sdk_python-1.1.17 → viso_sdk_python-1.1.18}/viso_sdk_python.egg-info/requires.txt +0 -0
- {viso_sdk_python-1.1.17 → viso_sdk_python-1.1.18}/viso_sdk_python.egg-info/top_level.txt +0 -0
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
"""Unit tests for NMS (Non-Maximum Suppression) utilities."""
|
|
2
|
+
from viso_sdk.detection.nms import non_max_suppression_cv, non_max_suppression_cv_per_class
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class TestNonMaxSuppressionCV:
|
|
6
|
+
"""Tests for standard NMS."""
|
|
7
|
+
|
|
8
|
+
def test_empty_input_returns_empty(self):
|
|
9
|
+
"""Test that empty boxes list returns empty result."""
|
|
10
|
+
result = non_max_suppression_cv([], [], score_threshold=0.5, iou_threshold=0.5)
|
|
11
|
+
assert result == []
|
|
12
|
+
|
|
13
|
+
def test_single_box_is_kept(self):
|
|
14
|
+
"""Test that a single box is always kept."""
|
|
15
|
+
boxes = [[10, 10, 50, 50]]
|
|
16
|
+
scores = [0.9]
|
|
17
|
+
result = non_max_suppression_cv(boxes, scores, score_threshold=0.5, iou_threshold=0.5)
|
|
18
|
+
assert result == [0]
|
|
19
|
+
|
|
20
|
+
def test_non_overlapping_boxes_all_kept(self):
|
|
21
|
+
"""Test that non-overlapping boxes are all kept."""
|
|
22
|
+
boxes = [
|
|
23
|
+
[10, 10, 30, 30], # Box 0
|
|
24
|
+
[100, 100, 30, 30], # Box 1 - far away
|
|
25
|
+
[200, 200, 30, 30], # Box 2 - far away
|
|
26
|
+
]
|
|
27
|
+
scores = [0.9, 0.85, 0.8]
|
|
28
|
+
result = non_max_suppression_cv(boxes, scores, score_threshold=0.5, iou_threshold=0.5)
|
|
29
|
+
assert len(result) == 3
|
|
30
|
+
assert set(result) == {0, 1, 2}
|
|
31
|
+
|
|
32
|
+
def test_overlapping_boxes_highest_score_kept(self):
|
|
33
|
+
"""Test that overlapping boxes keep only the highest score."""
|
|
34
|
+
boxes = [
|
|
35
|
+
[10, 10, 50, 50], # Box 0 - score 0.9
|
|
36
|
+
[15, 15, 50, 50], # Box 1 - score 0.8 (overlaps with 0)
|
|
37
|
+
]
|
|
38
|
+
scores = [0.9, 0.8]
|
|
39
|
+
result = non_max_suppression_cv(boxes, scores, score_threshold=0.5, iou_threshold=0.3)
|
|
40
|
+
assert result == [0] # Box 0 kept, box 1 suppressed
|
|
41
|
+
|
|
42
|
+
def test_score_threshold_filters_low_scores(self):
|
|
43
|
+
"""Test that boxes below score_threshold are filtered out."""
|
|
44
|
+
boxes = [
|
|
45
|
+
[10, 10, 50, 50], # High score
|
|
46
|
+
[100, 100, 30, 30], # Low score
|
|
47
|
+
]
|
|
48
|
+
scores = [0.9, 0.3]
|
|
49
|
+
result = non_max_suppression_cv(boxes, scores, score_threshold=0.5, iou_threshold=0.5)
|
|
50
|
+
assert result == [0]
|
|
51
|
+
|
|
52
|
+
def test_high_iou_threshold_keeps_more_boxes(self):
|
|
53
|
+
"""Test that higher IoU threshold allows more overlapping boxes."""
|
|
54
|
+
boxes = [
|
|
55
|
+
[10, 10, 50, 50],
|
|
56
|
+
[20, 20, 50, 50], # Moderate overlap
|
|
57
|
+
]
|
|
58
|
+
scores = [0.9, 0.85]
|
|
59
|
+
|
|
60
|
+
# With low IoU threshold, should suppress
|
|
61
|
+
result_low = non_max_suppression_cv(boxes, scores, score_threshold=0.5, iou_threshold=0.1)
|
|
62
|
+
assert len(result_low) == 1
|
|
63
|
+
|
|
64
|
+
# With high IoU threshold, should keep both
|
|
65
|
+
result_high = non_max_suppression_cv(boxes, scores, score_threshold=0.5, iou_threshold=0.9)
|
|
66
|
+
assert len(result_high) == 2
|
|
67
|
+
|
|
68
|
+
def test_three_overlapping_boxes_keeps_best(self):
|
|
69
|
+
"""Test NMS with three overlapping boxes."""
|
|
70
|
+
boxes = [
|
|
71
|
+
[10, 10, 50, 50], # score 0.95
|
|
72
|
+
[12, 12, 50, 50], # score 0.9 (high overlap with 0)
|
|
73
|
+
[14, 14, 50, 50], # score 0.85 (high overlap with 0 and 1)
|
|
74
|
+
]
|
|
75
|
+
scores = [0.95, 0.9, 0.85]
|
|
76
|
+
result = non_max_suppression_cv(boxes, scores, score_threshold=0.5, iou_threshold=0.3)
|
|
77
|
+
assert result == [0] # Only highest score kept
|
|
78
|
+
|
|
79
|
+
def test_float_boxes_and_scores(self):
|
|
80
|
+
"""Test that float boxes and scores are handled correctly."""
|
|
81
|
+
boxes = [[10.5, 10.5, 50.3, 50.7]]
|
|
82
|
+
scores = [0.934]
|
|
83
|
+
result = non_max_suppression_cv(boxes, scores, score_threshold=0.5, iou_threshold=0.5)
|
|
84
|
+
assert result == [0]
|
|
85
|
+
|
|
86
|
+
def test_mixed_overlapping_and_non_overlapping(self):
|
|
87
|
+
"""Test mix of overlapping and non-overlapping boxes."""
|
|
88
|
+
boxes = [
|
|
89
|
+
[10, 10, 50, 50], # Group 1 - score 0.9
|
|
90
|
+
[15, 15, 50, 50], # Group 1 - score 0.8 (overlaps with 0)
|
|
91
|
+
[200, 200, 30, 30], # Group 2 - score 0.95 (isolated)
|
|
92
|
+
]
|
|
93
|
+
scores = [0.9, 0.8, 0.95]
|
|
94
|
+
result = non_max_suppression_cv(boxes, scores, score_threshold=0.5, iou_threshold=0.3)
|
|
95
|
+
assert len(result) == 2
|
|
96
|
+
assert 2 in result # Isolated box kept
|
|
97
|
+
assert 0 in result # Highest score from group 1 kept
|
|
98
|
+
assert 1 not in result # Suppressed by box 0
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
class TestNonMaxSuppressionCVPerClass:
|
|
102
|
+
"""Tests for per-class NMS."""
|
|
103
|
+
|
|
104
|
+
def test_empty_input_returns_empty(self):
|
|
105
|
+
"""Test that empty inputs return empty result."""
|
|
106
|
+
result = non_max_suppression_cv_per_class(
|
|
107
|
+
[], [], [], score_threshold=0.5, iou_threshold=0.5
|
|
108
|
+
)
|
|
109
|
+
assert result == []
|
|
110
|
+
|
|
111
|
+
def test_single_box_is_kept(self):
|
|
112
|
+
"""Test that a single box is always kept."""
|
|
113
|
+
boxes = [[10, 10, 50, 50]]
|
|
114
|
+
scores = [0.9]
|
|
115
|
+
class_ids = [1]
|
|
116
|
+
result = non_max_suppression_cv_per_class(
|
|
117
|
+
boxes, scores, class_ids, score_threshold=0.5, iou_threshold=0.5
|
|
118
|
+
)
|
|
119
|
+
assert result == [0]
|
|
120
|
+
|
|
121
|
+
def test_different_classes_overlap_both_kept(self):
|
|
122
|
+
"""Test that overlapping boxes of different classes are both kept."""
|
|
123
|
+
boxes = [
|
|
124
|
+
[10, 10, 50, 50], # Class 1
|
|
125
|
+
[15, 15, 50, 50], # Class 2 (overlaps with class 1)
|
|
126
|
+
]
|
|
127
|
+
scores = [0.9, 0.85]
|
|
128
|
+
class_ids = [1, 2]
|
|
129
|
+
result = non_max_suppression_cv_per_class(
|
|
130
|
+
boxes, scores, class_ids, score_threshold=0.5, iou_threshold=0.3
|
|
131
|
+
)
|
|
132
|
+
assert len(result) == 2
|
|
133
|
+
assert set(result) == {0, 1}
|
|
134
|
+
|
|
135
|
+
def test_same_class_overlap_highest_kept(self):
|
|
136
|
+
"""Test that overlapping boxes of same class keep highest score."""
|
|
137
|
+
boxes = [
|
|
138
|
+
[10, 10, 50, 50], # Class 1 - score 0.9
|
|
139
|
+
[15, 15, 50, 50], # Class 1 - score 0.8 (overlaps)
|
|
140
|
+
]
|
|
141
|
+
scores = [0.9, 0.8]
|
|
142
|
+
class_ids = [1, 1]
|
|
143
|
+
result = non_max_suppression_cv_per_class(
|
|
144
|
+
boxes, scores, class_ids, score_threshold=0.5, iou_threshold=0.3
|
|
145
|
+
)
|
|
146
|
+
assert result == [0]
|
|
147
|
+
|
|
148
|
+
def test_multiple_classes_with_overlap(self):
|
|
149
|
+
"""Test complex scenario with multiple classes and overlaps."""
|
|
150
|
+
boxes = [
|
|
151
|
+
[10, 10, 50, 50], # Class 1 - score 0.9
|
|
152
|
+
[15, 15, 50, 50], # Class 1 - score 0.8 (overlaps with 0)
|
|
153
|
+
[12, 12, 48, 48], # Class 2 - score 0.85 (overlaps with 0 and 1)
|
|
154
|
+
[200, 200, 30, 30], # Class 1 - score 0.95 (isolated)
|
|
155
|
+
]
|
|
156
|
+
scores = [0.9, 0.8, 0.85, 0.95]
|
|
157
|
+
class_ids = [1, 1, 2, 1]
|
|
158
|
+
result = non_max_suppression_cv_per_class(
|
|
159
|
+
boxes, scores, class_ids, score_threshold=0.5, iou_threshold=0.3
|
|
160
|
+
)
|
|
161
|
+
|
|
162
|
+
assert len(result) == 3
|
|
163
|
+
assert 0 in result # Class 1, highest in overlapping group
|
|
164
|
+
assert 2 in result # Class 2, only box
|
|
165
|
+
assert 3 in result # Class 1, isolated
|
|
166
|
+
assert 1 not in result # Class 1, suppressed by box 0
|
|
167
|
+
|
|
168
|
+
def test_string_class_ids(self):
|
|
169
|
+
"""Test that string class IDs work correctly."""
|
|
170
|
+
boxes = [
|
|
171
|
+
[10, 10, 50, 50], # Class 'person'
|
|
172
|
+
[15, 15, 50, 50], # Class 'car' (overlaps)
|
|
173
|
+
]
|
|
174
|
+
scores = [0.9, 0.85]
|
|
175
|
+
class_ids = ['person', 'car']
|
|
176
|
+
result = non_max_suppression_cv_per_class(
|
|
177
|
+
boxes, scores, class_ids, score_threshold=0.5, iou_threshold=0.3
|
|
178
|
+
)
|
|
179
|
+
assert len(result) == 2
|
|
180
|
+
assert set(result) == {0, 1}
|
|
181
|
+
|
|
182
|
+
def test_three_classes_no_suppression(self):
|
|
183
|
+
"""Test that different classes at same location are all kept."""
|
|
184
|
+
boxes = [
|
|
185
|
+
[10, 10, 50, 50], # Class 1
|
|
186
|
+
[10, 10, 50, 50], # Class 2 (exact same location)
|
|
187
|
+
[10, 10, 50, 50], # Class 3 (exact same location)
|
|
188
|
+
]
|
|
189
|
+
scores = [0.9, 0.85, 0.8]
|
|
190
|
+
class_ids = [1, 2, 3]
|
|
191
|
+
result = non_max_suppression_cv_per_class(
|
|
192
|
+
boxes, scores, class_ids, score_threshold=0.5, iou_threshold=0.3
|
|
193
|
+
)
|
|
194
|
+
assert len(result) == 3
|
|
195
|
+
assert set(result) == {0, 1, 2}
|
|
196
|
+
|
|
197
|
+
def test_score_threshold_filters_per_class(self):
|
|
198
|
+
"""Test that score threshold is applied per class."""
|
|
199
|
+
boxes = [
|
|
200
|
+
[10, 10, 50, 50], # Class 1 - high score
|
|
201
|
+
[100, 100, 30, 30], # Class 1 - low score
|
|
202
|
+
[200, 200, 30, 30], # Class 2 - low score
|
|
203
|
+
]
|
|
204
|
+
scores = [0.9, 0.3, 0.4]
|
|
205
|
+
class_ids = [1, 1, 2]
|
|
206
|
+
result = non_max_suppression_cv_per_class(
|
|
207
|
+
boxes, scores, class_ids, score_threshold=0.5, iou_threshold=0.5
|
|
208
|
+
)
|
|
209
|
+
assert result == [0] # Only box 0 passes score threshold
|
|
210
|
+
|
|
211
|
+
def test_per_class_nms_order_independence(self):
|
|
212
|
+
"""Test that result is independent of input order."""
|
|
213
|
+
boxes = [
|
|
214
|
+
[10, 10, 50, 50], # Class 1 - score 0.8
|
|
215
|
+
[15, 15, 50, 50], # Class 2 - score 0.9
|
|
216
|
+
[12, 12, 48, 48], # Class 1 - score 0.95 (overlaps with box 0)
|
|
217
|
+
]
|
|
218
|
+
scores = [0.8, 0.9, 0.95]
|
|
219
|
+
class_ids = [1, 2, 1]
|
|
220
|
+
|
|
221
|
+
result = non_max_suppression_cv_per_class(
|
|
222
|
+
boxes, scores, class_ids, score_threshold=0.5, iou_threshold=0.3
|
|
223
|
+
)
|
|
224
|
+
|
|
225
|
+
# Box 2 (class 1, score 0.95) should be kept
|
|
226
|
+
# Box 0 (class 1, score 0.8) should be suppressed
|
|
227
|
+
# Box 1 (class 2, score 0.9) should be kept
|
|
228
|
+
assert len(result) == 2
|
|
229
|
+
assert 1 in result # Class 2
|
|
230
|
+
assert 2 in result # Class 1 with higher score
|
|
231
|
+
assert 0 not in result # Class 1 with lower score suppressed
|
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
# -*- coding: utf-8 -*-
|
|
2
2
|
"""Tests for viso_sdk.redis.stream_cleanup"""
|
|
3
|
-
from unittest.mock import MagicMock
|
|
4
|
-
|
|
5
|
-
import pytest
|
|
3
|
+
from unittest.mock import MagicMock
|
|
6
4
|
|
|
7
5
|
from viso_sdk.redis.stream_cleanup import StreamCleanupManager, clear_stream_redis_keys
|
|
8
6
|
|
|
@@ -1994,7 +1994,7 @@ static __pyx_mstatetype * const __pyx_mstate_global = &__pyx_mstate_global_stati
|
|
|
1994
1994
|
#endif
|
|
1995
1995
|
/* #### Code section: constant_name_defines ### */
|
|
1996
1996
|
#define __pyx_kp_u_ __pyx_string_tab[0]
|
|
1997
|
-
#define
|
|
1997
|
+
#define __pyx_kp_u_1_1_18 __pyx_string_tab[1]
|
|
1998
1998
|
#define __pyx_kp_u_Copyright_2020 __pyx_string_tab[2]
|
|
1999
1999
|
#define __pyx_kp_u_Viso_ai_team __pyx_string_tab[3]
|
|
2000
2000
|
#define __pyx_kp_u__2 __pyx_string_tab[4]
|
|
@@ -2605,16 +2605,16 @@ __Pyx_RefNannySetupContext("PyInit__version", 0);
|
|
|
2605
2605
|
* __license__ = "LGPL3"
|
|
2606
2606
|
* __title__ = "viso-sdk-python" # <<<<<<<<<<<<<<
|
|
2607
2607
|
*
|
|
2608
|
-
* __version__ = "1.1.
|
|
2608
|
+
* __version__ = "1.1.18"
|
|
2609
2609
|
*/
|
|
2610
2610
|
if (PyDict_SetItem(__pyx_mstate_global->__pyx_d, __pyx_mstate_global->__pyx_n_u_title, __pyx_mstate_global->__pyx_kp_u_viso_sdk_python) < (0)) __PYX_ERR(0, 15, __pyx_L1_error)
|
|
2611
2611
|
|
|
2612
2612
|
/* "viso_sdk/_version.py":17
|
|
2613
2613
|
* __title__ = "viso-sdk-python"
|
|
2614
2614
|
*
|
|
2615
|
-
* __version__ = "1.1.
|
|
2615
|
+
* __version__ = "1.1.18" # <<<<<<<<<<<<<<
|
|
2616
2616
|
*/
|
|
2617
|
-
if (PyDict_SetItem(__pyx_mstate_global->__pyx_d, __pyx_mstate_global->__pyx_n_u_version, __pyx_mstate_global->
|
|
2617
|
+
if (PyDict_SetItem(__pyx_mstate_global->__pyx_d, __pyx_mstate_global->__pyx_n_u_version, __pyx_mstate_global->__pyx_kp_u_1_1_18) < (0)) __PYX_ERR(0, 17, __pyx_L1_error)
|
|
2618
2618
|
|
|
2619
2619
|
/* "viso_sdk/_version.py":1
|
|
2620
2620
|
* """ # <<<<<<<<<<<<<<
|
|
@@ -2689,7 +2689,7 @@ static int __Pyx_InitConstants(__pyx_mstatetype *__pyx_mstate) {
|
|
|
2689
2689
|
{
|
|
2690
2690
|
const struct { const unsigned int length: 5; } index[] = {{1},{6},{15},{12},{1},{18},{26},{15},{5},{7},{10},{18},{13},{8},{8},{7},{9},{8},{11},{8},{10},{8},{3},{2},{4},{12},{10},{8},{9},{11},{4}};
|
|
2691
2691
|
#if (CYTHON_COMPRESS_STRINGS) == 2 /* compression: bz2 (242 bytes) */
|
|
2692
|
-
const char* const cstring = "BZh91AY&
|
|
2692
|
+
const char* const cstring = "BZh91AY&SY_\303Rz\000\000\007\337\200@\000@\003xE\350\204A\000\277\357\377\2400\000\315[\014\246\251\261\032\217\005<\240\r\007\244\323@DM6\223LM2\000\000\001\240\324\3656\251\246\241\232\020\304\310\3204\321\241B}\317\023\177kg\260 \035\340 Z\376\rG[']N7\363d\253B\010\313\310Z\267(\347m\323\314\021\232\010\010\024@c\311w\270\016\003\344b\354\037\236\203J\027\241\211a\270\276(\315:U\024$b\337\332]'j\372\314\025\256\316\316e`Tm\0023)E\244\027\351\362\025\rH\213YH\275X\250Gz{\240\367\332\304\326\244\302\251\344\341_\034a\254\230\376\276!0f\271\314\347\255\367\004GrU*\004\201\212\020\326\026\203r\226\221\215=K\030Y%%\037\225\001,<\037\342\356H\247\n\022\013\370jO@";
|
|
2693
2693
|
PyObject *data = __Pyx_DecompressString(cstring, 242, 2);
|
|
2694
2694
|
if (unlikely(!data)) __PYX_ERR(0, 1, __pyx_L1_error)
|
|
2695
2695
|
const char* const bytes = __Pyx_PyBytes_AsString(data);
|
|
@@ -2697,7 +2697,7 @@ const char* const cstring = "BZh91AY&SYI\035\357\304\000\000\007\337\200@\000@\0
|
|
|
2697
2697
|
if (likely(bytes)); else { Py_DECREF(data); __PYX_ERR(0, 1, __pyx_L1_error) }
|
|
2698
2698
|
#endif
|
|
2699
2699
|
#elif (CYTHON_COMPRESS_STRINGS) != 0 /* compression: zlib (196 bytes) */
|
|
2700
|
-
const char* const cstring = "x\3325\216\275n\3030\014\204_\305/`#M\
|
|
2700
|
+
const char* const cstring = "x\3325\216\275n\3030\014\204_\305/`#M\227\016A\177\320!K\206N]\tF\242c\"2\351JT\002\277}\3518\006\207;\334\007\362\330\275t>o\337:\315\231/\2035\373\335~\327\376r\321\016\2711\302\361\343\322\005\315\016Q>o\317|\323\257cs`\351u\003\357\213\266%^\333i\266A\345t\3749\275\342\271Lh\003\000V\3172@H,\004,`\031\003\2351\\=\333\036\000\3675C\344\034\321\310x$\267\202#\001\320\210\234\234C\317\211\026M\034H\312\303:\222\207j\254+\\wD\357\372\254\377\253\230\326\260\220E\352\261&o3*K'\030\333\272w\243\\X\375\330L\230\377\001:Ym\356";
|
|
2701
2701
|
PyObject *data = __Pyx_DecompressString(cstring, 196, 1);
|
|
2702
2702
|
if (unlikely(!data)) __PYX_ERR(0, 1, __pyx_L1_error)
|
|
2703
2703
|
const char* const bytes = __Pyx_PyBytes_AsString(data);
|
|
@@ -2705,7 +2705,7 @@ const char* const cstring = "x\3325\216\275n\3030\014\204_\305/`#M\207.A\177\320
|
|
|
2705
2705
|
if (likely(bytes)); else { Py_DECREF(data); __PYX_ERR(0, 1, __pyx_L1_error) }
|
|
2706
2706
|
#endif
|
|
2707
2707
|
#else /* compression: none (287 bytes) */
|
|
2708
|
-
const char* const bytes = ".1.1.
|
|
2708
|
+
const char* const bytes = ".1.1.18Copyright 2020-Viso.ai team?g.corrigan@viso.ai viso.ai AG <info@viso.ai>viso-sdk-pythonLGPL3abspath__author__cline_in_traceback__copyright___cur_dirdatetimedirname__email____file____license____main____module____name__nowospath__qualname__setdefault__test____title____version__year";
|
|
2709
2709
|
PyObject *data = NULL;
|
|
2710
2710
|
CYTHON_UNUSED_VAR(__Pyx_DecompressString);
|
|
2711
2711
|
#endif
|