Extend docstrings to test cases for enhanced readability (#2130)
* Extend docstrings to test cases for enhanced readability and consistency across testing modules * Extend test case docstrings across multiple modules for improved clarity and consistency * Refactor test case docstrings for clarity and consistency in `test_annotators.py` --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
This commit is contained in:
parent
d89ebde4d1
commit
2d0404e67c
|
|
@ -59,17 +59,33 @@ def gradient_image() -> np.ndarray:
|
|||
|
||||
|
||||
class TestBoxAnnotator:
|
||||
"""Tests for BoxAnnotator class"""
|
||||
"""
|
||||
Verify that BoxAnnotator correctly draws bounding boxes on an image.
|
||||
|
||||
def test_annotate_with_no_detections(self, test_image):
|
||||
"""Test that annotate method returns unmodified image when no detections"""
|
||||
Ensures that `BoxAnnotator` correctly draws bounding boxes on an image, which is
|
||||
essential for users to visualize detection results.
|
||||
"""
|
||||
|
||||
def test_annotate_with_no_detections(self, test_image: np.ndarray) -> None:
|
||||
"""
|
||||
Verify that annotation with no detections does not change the image.
|
||||
|
||||
Scenario: Annotating an image with an empty set of detections.
|
||||
Expected: The scene remains unchanged, ensuring no ghost boxes are drawn.
|
||||
"""
|
||||
detections = Detections.empty()
|
||||
annotator = BoxAnnotator()
|
||||
result = annotator.annotate(scene=test_image.copy(), detections=detections)
|
||||
assert np.array_equal(test_image, result)
|
||||
|
||||
def test_annotate_with_single_detection(self, test_image):
|
||||
"""Test that annotate method correctly draws a single bounding box"""
|
||||
def test_annotate_with_single_detection(self, test_image: np.ndarray) -> None:
|
||||
"""
|
||||
Verify that annotation with a single detection draws a bounding box.
|
||||
|
||||
Scenario: Annotating an image with a single bounding box.
|
||||
Expected: The scene is modified by drawing a box, allowing users to identify
|
||||
a single detected object.
|
||||
"""
|
||||
detections = _create_detections(xyxy=[[10, 10, 90, 90]], class_id=[0])
|
||||
annotator = BoxAnnotator(
|
||||
color=Color.WHITE, thickness=2, color_lookup=ColorLookup.INDEX
|
||||
|
|
@ -77,8 +93,14 @@ class TestBoxAnnotator:
|
|||
result = annotator.annotate(scene=test_image.copy(), detections=detections)
|
||||
assert_image_mostly_same(test_image, result, similarity_threshold=0.85)
|
||||
|
||||
def test_annotate_with_multiple_detections(self, test_image):
|
||||
"""Test that annotate method correctly draws multiple bounding boxes"""
|
||||
def test_annotate_with_multiple_detections(self, test_image: np.ndarray) -> None:
|
||||
"""
|
||||
Verify that annotation with multiple detections draws all bounding boxes.
|
||||
|
||||
Scenario: Annotating an image with multiple bounding boxes of different classes.
|
||||
Expected: All boxes are drawn, enabling visualization of complex scenes with
|
||||
multiple objects.
|
||||
"""
|
||||
detections = _create_detections(
|
||||
xyxy=[[10, 10, 40, 40], [60, 60, 90, 90], [10, 60, 40, 90]],
|
||||
class_id=[0, 1, 2],
|
||||
|
|
@ -89,8 +111,14 @@ class TestBoxAnnotator:
|
|||
result = annotator.annotate(scene=test_image.copy(), detections=detections)
|
||||
assert_image_mostly_same(test_image, result, similarity_threshold=0.85)
|
||||
|
||||
def test_annotate_with_numpy_color_lookup(self, test_image):
|
||||
"""Test that annotate works when color lookup is a NumPy array"""
|
||||
def test_annotate_with_numpy_color_lookup(self, test_image: np.ndarray) -> None:
|
||||
"""
|
||||
Verify that annotation respects custom NumPy color lookup array.
|
||||
|
||||
Scenario: Providing a custom NumPy array for color lookup instead of class IDs.
|
||||
Expected: Annotator respects the custom mapping, giving users flexible control
|
||||
over box colors (e.g., coloring by tracking ID or custom criteria).
|
||||
"""
|
||||
detections = Detections(
|
||||
xyxy=np.array([[10, 10, 20, 20], [30, 30, 40, 40]], dtype=np.float32),
|
||||
confidence=np.array([0.38, 0.21], dtype=np.float32),
|
||||
|
|
|
|||
|
|
@ -177,6 +177,13 @@ def test_dataset_merge(
|
|||
expected_result: DetectionDataset | None,
|
||||
exception: Exception,
|
||||
) -> None:
|
||||
"""
|
||||
Verify that multiple DetectionDataset objects can be successfully merged.
|
||||
|
||||
Ensures that multiple `DetectionDataset` objects can be merged into single dataset.
|
||||
This is vital for users who need to combine data from different sources or
|
||||
augment their datasets with additional labeled examples.
|
||||
"""
|
||||
with exception:
|
||||
result = DetectionDataset.merge(dataset_list=dataset_list)
|
||||
assert result == expected_result
|
||||
|
|
|
|||
|
|
@ -132,6 +132,8 @@ TEST_DET_DIFFERENT_METADATA = Detections(
|
|||
@pytest.mark.parametrize(
|
||||
("detections", "index", "expected_result", "exception"),
|
||||
[
|
||||
# Scenario: Filter detections by class ID using a boolean mask.
|
||||
# Expected: Only detections matching the class ID are retained.
|
||||
(
|
||||
DETECTIONS,
|
||||
DETECTIONS.class_id == 0,
|
||||
|
|
@ -139,7 +141,9 @@ TEST_DET_DIFFERENT_METADATA = Detections(
|
|||
xyxy=[[2254, 906, 2447, 1353]], confidence=[0.90538], class_id=[0]
|
||||
),
|
||||
DoesNotRaise(),
|
||||
), # take only detections with class_id = 0
|
||||
),
|
||||
# Scenario: Filter detections by confidence score threshold.
|
||||
# Expected: Only high-confidence detections are kept, filtering out noise.
|
||||
(
|
||||
DETECTIONS,
|
||||
DETECTIONS.confidence > 0.5,
|
||||
|
|
@ -153,7 +157,9 @@ TEST_DET_DIFFERENT_METADATA = Detections(
|
|||
class_id=[0, 56, 39],
|
||||
),
|
||||
DoesNotRaise(),
|
||||
), # take only detections with confidence > 0.5
|
||||
),
|
||||
# Scenario: Select all detections using a full boolean mask.
|
||||
# Expected: Result is identical to input.
|
||||
(
|
||||
DETECTIONS,
|
||||
np.array(
|
||||
|
|
@ -161,7 +167,9 @@ TEST_DET_DIFFERENT_METADATA = Detections(
|
|||
),
|
||||
DETECTIONS,
|
||||
DoesNotRaise(),
|
||||
), # take all detections
|
||||
),
|
||||
# Scenario: Select no detections using an empty boolean mask.
|
||||
# Expected: An empty Detections object with correct shapes.
|
||||
(
|
||||
DETECTIONS,
|
||||
np.array(
|
||||
|
|
@ -174,7 +182,9 @@ TEST_DET_DIFFERENT_METADATA = Detections(
|
|||
class_id=np.array([], dtype=int),
|
||||
),
|
||||
DoesNotRaise(),
|
||||
), # take no detections
|
||||
),
|
||||
# Scenario: Select specific detections using a list of integer indices.
|
||||
# Expected: Only requested indices are returned in specified order.
|
||||
(
|
||||
DETECTIONS,
|
||||
[0, 2],
|
||||
|
|
@ -184,7 +194,9 @@ TEST_DET_DIFFERENT_METADATA = Detections(
|
|||
class_id=[0, 39],
|
||||
),
|
||||
DoesNotRaise(),
|
||||
), # take only first and third detection using List[int] index
|
||||
),
|
||||
# Scenario: Select specific detections using a NumPy array of indices.
|
||||
# Expected: Only requested indices are returned.
|
||||
(
|
||||
DETECTIONS,
|
||||
np.array([0, 2]),
|
||||
|
|
@ -194,7 +206,9 @@ TEST_DET_DIFFERENT_METADATA = Detections(
|
|||
class_id=[0, 39],
|
||||
),
|
||||
DoesNotRaise(),
|
||||
), # take only first and third detection using np.ndarray index
|
||||
),
|
||||
# Scenario: Select a single detection using an integer index.
|
||||
# Expected: A Detections object containing only that element.
|
||||
(
|
||||
DETECTIONS,
|
||||
0,
|
||||
|
|
@ -202,7 +216,9 @@ TEST_DET_DIFFERENT_METADATA = Detections(
|
|||
xyxy=[[2254, 906, 2447, 1353]], confidence=[0.90538], class_id=[0]
|
||||
),
|
||||
DoesNotRaise(),
|
||||
), # take only first detection by index
|
||||
),
|
||||
# Scenario: Select a range of detections using a slice.
|
||||
# Expected: Detections within the slice range are returned.
|
||||
(
|
||||
DETECTIONS,
|
||||
slice(1, 3),
|
||||
|
|
@ -212,9 +228,11 @@ TEST_DET_DIFFERENT_METADATA = Detections(
|
|||
class_id=[56, 39],
|
||||
),
|
||||
DoesNotRaise(),
|
||||
), # take only first detection by index slice (1, 3)
|
||||
(DETECTIONS, 10, None, pytest.raises(IndexError)), # index out of range
|
||||
(DETECTIONS, [0, 2, 10], None, pytest.raises(IndexError)), # index out of range
|
||||
),
|
||||
# Scenario: Index out of range.
|
||||
# Expected: IndexError is raised.
|
||||
(DETECTIONS, 10, None, pytest.raises(IndexError)),
|
||||
(DETECTIONS, [0, 2, 10], None, pytest.raises(IndexError)),
|
||||
(DETECTIONS, np.array([0, 2, 10]), None, pytest.raises(IndexError)),
|
||||
(
|
||||
DETECTIONS,
|
||||
|
|
@ -224,12 +242,14 @@ TEST_DET_DIFFERENT_METADATA = Detections(
|
|||
None,
|
||||
pytest.raises(IndexError),
|
||||
),
|
||||
# Scenario: Filter an empty Detections object.
|
||||
# Expected: Returns an empty Detections object without crashing.
|
||||
(
|
||||
Detections.empty(),
|
||||
np.isin(Detections.empty()["class_name"], ["cat", "dog"]),
|
||||
Detections.empty(),
|
||||
DoesNotRaise(),
|
||||
), # Filter an empty detections by specific class names
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_getitem(
|
||||
|
|
@ -238,6 +258,11 @@ def test_getitem(
|
|||
expected_result: Detections | None,
|
||||
exception: Exception,
|
||||
) -> None:
|
||||
"""
|
||||
Ensures that `Detections.__getitem__` (indexing/slicing) works correctly for various
|
||||
input types. This is a core feature that allows users to filter and manipulate
|
||||
detection results easily.
|
||||
"""
|
||||
with exception:
|
||||
result = detections[index]
|
||||
assert result == expected_result
|
||||
|
|
|
|||
|
|
@ -28,6 +28,14 @@ from supervision.draw.color import Color
|
|||
def test_color_from_hex(
|
||||
color_hex, expected_result: Color | None, exception: Exception
|
||||
) -> None:
|
||||
"""
|
||||
Verify that Color.from_hex correctly parses various hex string formats.
|
||||
|
||||
Scenario: Creating a `Color` object from various hex string formats (3-digit,
|
||||
6-digit, with/without # prefix).
|
||||
Expected: Correct RGB values are parsed, and invalid hex strings raise `ValueError`.
|
||||
This allows users to define colors using familiar web formats.
|
||||
"""
|
||||
with exception:
|
||||
result = Color.from_hex(color_hex=color_hex)
|
||||
assert result == expected_result
|
||||
|
|
@ -47,6 +55,13 @@ def test_color_from_hex(
|
|||
def test_color_as_hex(
|
||||
color: Color, expected_result: str | None, exception: Exception
|
||||
) -> None:
|
||||
"""
|
||||
Verify that Color.as_hex correctly converts Color objects to hex strings.
|
||||
|
||||
Scenario: Converting a `Color` object back to a hex string.
|
||||
Expected: Correct 6-digit hex string with # prefix is returned, ensuring
|
||||
round-trip consistency for color definitions.
|
||||
"""
|
||||
with exception:
|
||||
result = color.as_hex()
|
||||
assert result == expected_result
|
||||
|
|
|
|||
|
|
@ -29,6 +29,13 @@ from supervision.geometry.core import Point, Vector
|
|||
def test_vector_cross_product(
|
||||
vector: Vector, point: Point, expected_result: float
|
||||
) -> None:
|
||||
"""
|
||||
Verify that Vector.cross_product correctly calculates the scalar value.
|
||||
|
||||
Scenario: Computing the cross product between a vector and a point.
|
||||
Expected: Correct scalar value is returned, which is used to determine which side
|
||||
of a line a point lies on (essential for line crossing counting).
|
||||
"""
|
||||
result = vector.cross_product(point=point)
|
||||
assert result == expected_result
|
||||
|
||||
|
|
@ -55,5 +62,12 @@ def test_vector_cross_product(
|
|||
],
|
||||
)
|
||||
def test_vector_magnitude(vector: Vector, expected_result: float) -> None:
|
||||
"""
|
||||
Verify that Vector.magnitude correctly calculates Euclidean distance.
|
||||
|
||||
Scenario: Calculating the magnitude (length) of a vector.
|
||||
Expected: Correct Euclidean distance between start and end points is returned,
|
||||
fundamental for various spatial calculations.
|
||||
"""
|
||||
result = vector.magnitude
|
||||
assert result == expected_result
|
||||
|
|
|
|||
|
|
@ -48,5 +48,13 @@ def generate_test_polygon(n: int) -> np.ndarray:
|
|||
],
|
||||
)
|
||||
def test_get_polygon_center(polygon: np.ndarray, expected_result: Point) -> None:
|
||||
"""
|
||||
Verify that get_polygon_center correctly calculates the centroid of a polygon.
|
||||
|
||||
Scenario: Calculating the center point (centroid) of various polygons.
|
||||
Expected: The returned `Point` correctly represents the average position of all
|
||||
polygon vertices, which is used for placing labels or markers at the center
|
||||
of detected objects.
|
||||
"""
|
||||
result = get_polygon_center(polygon)
|
||||
assert result == expected_result
|
||||
|
|
|
|||
|
|
@ -5,8 +5,20 @@ from tests.helpers import assert_image_mostly_same
|
|||
|
||||
|
||||
class TestVertexAnnotator:
|
||||
"""
|
||||
Verify that VertexAnnotator correctly draws keypoints on an image.
|
||||
|
||||
Ensures that `VertexAnnotator` correctly draws keypoints (vertices) on an image,
|
||||
which is essential for human pose estimation or similar tasks.
|
||||
"""
|
||||
|
||||
def test_annotate_with_default_parameters(self, scene, sample_key_points):
|
||||
"""Test annotation with default parameters."""
|
||||
"""
|
||||
Verify that VertexAnnotator correctly draws keypoints with default parameters.
|
||||
|
||||
Scenario: Annotating a scene using default vertex parameters.
|
||||
Expected: Scene is modified, showing keypoints at their detected locations.
|
||||
"""
|
||||
annotator = sv.VertexAnnotator()
|
||||
result = annotator.annotate(scene=scene.copy(), key_points=sample_key_points)
|
||||
|
||||
|
|
@ -16,7 +28,13 @@ class TestVertexAnnotator:
|
|||
)
|
||||
|
||||
def test_annotate_with_custom_color_and_radius(self, scene, sample_key_points):
|
||||
"""Test annotation with custom color and radius."""
|
||||
"""
|
||||
Verify that VertexAnnotator respects custom color and radius settings.
|
||||
|
||||
Scenario: Annotating a scene with user-specified color and radius.
|
||||
Expected: Scene is modified according to custom style, allowing users to
|
||||
distinguish keypoints more clearly or match specific branding.
|
||||
"""
|
||||
color = sv.Color.RED
|
||||
radius = 5
|
||||
annotator = sv.VertexAnnotator(color=color, radius=radius)
|
||||
|
|
@ -28,7 +46,12 @@ class TestVertexAnnotator:
|
|||
)
|
||||
|
||||
def test_annotate_empty_key_points(self, scene, empty_key_points):
|
||||
"""Test annotation with empty key points returns unchanged scene."""
|
||||
"""
|
||||
Verify that VertexAnnotator handles empty keypoints without modifying the scene.
|
||||
|
||||
Scenario: Annotating a scene with no key points detected.
|
||||
Expected: Original scene is returned untouched, preventing phantom annotations.
|
||||
"""
|
||||
annotator = sv.VertexAnnotator()
|
||||
result = annotator.annotate(scene=scene.copy(), key_points=empty_key_points)
|
||||
|
||||
|
|
@ -37,8 +60,20 @@ class TestVertexAnnotator:
|
|||
|
||||
|
||||
class TestEdgeAnnotator:
|
||||
"""
|
||||
Verify that EdgeAnnotator correctly draws skeleton edges between keypoints.
|
||||
|
||||
Ensures that `EdgeAnnotator` correctly draws connections (edges) between keypoints,
|
||||
forming skeletons that help users interpret spatial relationships.
|
||||
"""
|
||||
|
||||
def test_annotate_with_default_parameters(self, scene, sample_key_points):
|
||||
"""Test annotation with default parameters using COCO skeleton."""
|
||||
"""
|
||||
Verify correctly draw skeleton edges with default parameters.
|
||||
|
||||
Scenario: Annotating a scene with default skeleton (e.g., COCO).
|
||||
Expected: Skeleton edges are drawn between corresponding keypoints.
|
||||
"""
|
||||
annotator = sv.EdgeAnnotator()
|
||||
result = annotator.annotate(scene=scene.copy(), key_points=sample_key_points)
|
||||
|
||||
|
|
@ -48,7 +83,13 @@ class TestEdgeAnnotator:
|
|||
)
|
||||
|
||||
def test_annotate_with_custom_edges(self, scene, sample_key_points):
|
||||
"""Test annotation with custom edge definitions."""
|
||||
"""
|
||||
Verify that EdgeAnnotator respects custom-defined skeleton structures.
|
||||
|
||||
Scenario: Annotating a scene with a custom-defined skeleton structure.
|
||||
Expected: Only the specified connections are drawn, giving users flexibility
|
||||
for non-standard keypoint models.
|
||||
"""
|
||||
edges = [(1, 2), (2, 3)]
|
||||
annotator = sv.EdgeAnnotator(edges=edges)
|
||||
result = annotator.annotate(scene=scene.copy(), key_points=sample_key_points)
|
||||
|
|
@ -59,7 +100,12 @@ class TestEdgeAnnotator:
|
|||
)
|
||||
|
||||
def test_annotate_empty_key_points(self, scene, empty_key_points):
|
||||
"""Test annotation with empty key points returns unchanged scene."""
|
||||
"""
|
||||
Verify that EdgeAnnotator handles empty keypoints without modifying the scene.
|
||||
|
||||
Scenario: Annotating a scene with no key points for edge drawing.
|
||||
Expected: Original scene is returned untouched.
|
||||
"""
|
||||
annotator = sv.EdgeAnnotator()
|
||||
result = annotator.annotate(scene=scene.copy(), key_points=empty_key_points)
|
||||
|
||||
|
|
@ -67,7 +113,13 @@ class TestEdgeAnnotator:
|
|||
assert np.array_equal(result, scene)
|
||||
|
||||
def test_annotate_no_edges_found(self, scene):
|
||||
"""Test annotation when no matching skeleton is found."""
|
||||
"""
|
||||
Verify returning unmodified scene when no known skeleton matches.
|
||||
|
||||
Scenario: Key points provided don't match any known or provided skeleton.
|
||||
Expected: No edges are drawn, and the original scene is returned, avoiding
|
||||
incorrect or nonsensical connections.
|
||||
"""
|
||||
# Key points with more vertices than any skeleton
|
||||
large_key_points = sv.KeyPoints(
|
||||
xy=np.array([[[i * 10, i * 10] for i in range(100)]], dtype=np.float32),
|
||||
|
|
|
|||
|
|
@ -16,6 +16,14 @@ from tests.helpers import _create_detections, assert_almost_equal
|
|||
|
||||
|
||||
class TestDetectionMetrics:
|
||||
"""
|
||||
Verify that detection metrics are computed accurately.
|
||||
|
||||
Ensures that detection metrics (mAP, Conf. Matrix, etc.) are computed accurately.
|
||||
These metrics are the primary way users evaluate the performance of their models
|
||||
within the `supervision` ecosystem.
|
||||
"""
|
||||
|
||||
CLASSES = np.arange(80)
|
||||
NUM_CLASSES = len(CLASSES)
|
||||
|
||||
|
|
@ -188,7 +196,14 @@ class TestDetectionMetrics:
|
|||
with_confidence: bool,
|
||||
expected_result: np.ndarray | None,
|
||||
exception: Exception,
|
||||
):
|
||||
) -> None:
|
||||
"""
|
||||
Verify that Detections objects are correctly converted to NumPy tensors.
|
||||
|
||||
Scenario: Converting Detections objects to NumPy tensors.
|
||||
Expected: Tensors are correctly formatted for consumption by metric functions,
|
||||
preserving coordinates, class IDs, and optionally confidence scores.
|
||||
"""
|
||||
with exception:
|
||||
result = detections_to_tensor(
|
||||
detections=detections, with_confidence=with_confidence
|
||||
|
|
@ -436,6 +451,13 @@ class TestDetectionMetrics:
|
|||
expected_result: float,
|
||||
exception: Exception,
|
||||
) -> None:
|
||||
"""
|
||||
Verify that Average Precision is correctly calculated from PR curve points.
|
||||
|
||||
Scenario: Computing Average Precision (AP) from PR curve points.
|
||||
Expected: AP is correctly calculated using the area under the curve, which is
|
||||
the standard for evaluating detection models (mAP components).
|
||||
"""
|
||||
with exception:
|
||||
result = MeanAveragePrecision.compute_average_precision(
|
||||
recall=recall, precision=precision
|
||||
|
|
|
|||
|
|
@ -20,6 +20,13 @@ def dummy_video_path(tmp_path):
|
|||
|
||||
|
||||
def test_process_video_exception_handling(dummy_video_path, tmp_path):
|
||||
"""
|
||||
Verify that process_video correctly propagates exceptions from the callback.
|
||||
|
||||
Scenario: Processing a video where the callback raises an exception.
|
||||
Expected: `process_video` should propagate the exception, allowing users to
|
||||
handle errors during video processing.
|
||||
"""
|
||||
target_path = str(tmp_path / "target.mp4")
|
||||
|
||||
def callback_with_exception(frame, index):
|
||||
|
|
@ -36,6 +43,13 @@ def test_process_video_exception_handling(dummy_video_path, tmp_path):
|
|||
|
||||
|
||||
def test_process_video_success(dummy_video_path, tmp_path):
|
||||
"""
|
||||
Verify successful video processing with a pass-through callback.
|
||||
|
||||
Scenario: Successfully processing a video with a simple pass-through callback.
|
||||
Expected: The video is processed without error and the target file is created,
|
||||
verifying the core functionality of `process_video`.
|
||||
"""
|
||||
target_path = str(tmp_path / "target_success.mp4")
|
||||
|
||||
def callback_success(frame, index):
|
||||
|
|
@ -50,6 +64,12 @@ def test_process_video_success(dummy_video_path, tmp_path):
|
|||
|
||||
|
||||
def test_process_video_exception_with_small_buffer(dummy_video_path, tmp_path):
|
||||
"""
|
||||
Verify that process_video handles exceptions correctly even with small buffers.
|
||||
|
||||
Scenario: Processing a video with minimal buffering where an exception occurs.
|
||||
Expected: The exception is still correctly propagated even with low memory settings.
|
||||
"""
|
||||
target_path = str(tmp_path / "target_exception_small_buffer.mp4")
|
||||
|
||||
def callback_with_exception(frame, index):
|
||||
|
|
@ -68,6 +88,13 @@ def test_process_video_exception_with_small_buffer(dummy_video_path, tmp_path):
|
|||
|
||||
|
||||
def test_process_video_max_frames(dummy_video_path, tmp_path):
|
||||
"""
|
||||
Verify that process_video respects the max_frames parameter.
|
||||
|
||||
Scenario: Processing only a limited number of frames using `max_frames`.
|
||||
Expected: Only the specified number of frames are processed, which is useful for
|
||||
quick testing or sampling.
|
||||
"""
|
||||
target_path = str(tmp_path / "target_max_frames.mp4")
|
||||
processed_indices = []
|
||||
|
||||
|
|
@ -87,6 +114,13 @@ def test_process_video_max_frames(dummy_video_path, tmp_path):
|
|||
|
||||
|
||||
def test_process_video_custom_params(dummy_video_path, tmp_path):
|
||||
"""
|
||||
Verify that process_video works correctly with custom performance parameters.
|
||||
|
||||
Scenario: Processing video with custom prefetch and buffer parameters.
|
||||
Expected: Video is processed successfully, showing that these performance-tuning
|
||||
parameters are correctly handled.
|
||||
"""
|
||||
target_path = str(tmp_path / "target_custom_params.mp4")
|
||||
|
||||
def callback(frame, index):
|
||||
|
|
@ -105,6 +139,13 @@ def test_process_video_custom_params(dummy_video_path, tmp_path):
|
|||
|
||||
|
||||
def test_video_info(dummy_video_path):
|
||||
"""
|
||||
Verify that VideoInfo correctly retrieves metadata from a video file.
|
||||
|
||||
Scenario: Retrieving metadata from a video file using `VideoInfo`.
|
||||
Expected: Correct width, height, fps, and frame count are returned, which is
|
||||
essential for initializing annotators or calculating statistics.
|
||||
"""
|
||||
video_info = VideoInfo.from_video_path(dummy_video_path)
|
||||
assert video_info.width == 640
|
||||
assert video_info.height == 480
|
||||
|
|
@ -114,6 +155,13 @@ def test_video_info(dummy_video_path):
|
|||
|
||||
|
||||
def test_get_video_frames_generator(dummy_video_path):
|
||||
"""
|
||||
Verify that get_video_frames_generator yields frames with correct shapes.
|
||||
|
||||
Scenario: Iterating over video frames using a generator.
|
||||
Expected: All frames are yielded in order as NumPy arrays with correct shapes,
|
||||
enabling frame-by-frame processing loops.
|
||||
"""
|
||||
generator = get_video_frames_generator(dummy_video_path)
|
||||
frames = list(generator)
|
||||
assert len(frames) == 10
|
||||
|
|
@ -122,12 +170,26 @@ def test_get_video_frames_generator(dummy_video_path):
|
|||
|
||||
|
||||
def test_get_video_frames_generator_with_stride(dummy_video_path):
|
||||
"""
|
||||
Verify that get_video_frames_generator correctly handles the stride parameter.
|
||||
|
||||
Scenario: Iterating over video frames with specified stride (e.g., every 2nd frame).
|
||||
Expected: The generator correctly skips frames according to the stride, allowing
|
||||
for faster processing of high-FPS videos.
|
||||
"""
|
||||
generator = get_video_frames_generator(dummy_video_path, stride=2)
|
||||
frames = list(generator)
|
||||
assert len(frames) == 5
|
||||
|
||||
|
||||
def test_get_video_frames_generator_with_start_end(dummy_video_path):
|
||||
"""
|
||||
Verify that get_video_frames_generator respects start and end frame indices.
|
||||
|
||||
Scenario: Iterating over a specific range of video frames using `start` and `end`.
|
||||
Expected: Only frames within the specified range are yielded, enabling targeted
|
||||
analysis of video segments.
|
||||
"""
|
||||
generator = get_video_frames_generator(dummy_video_path, start=2, end=5)
|
||||
frames = list(generator)
|
||||
assert len(frames) == 3
|
||||
|
|
|
|||
Loading…
Reference in New Issue