From ad54afc9e5a449e90a970b3d03a054a5ad01eae2 Mon Sep 17 00:00:00 2001 From: Jake Howard Date: Wed, 24 Nov 2021 22:11:28 +0000 Subject: [PATCH] Raise exceptions when trying to construct invalid cameras --- tests/test_camera/test_camera.py | 18 ++++++++++++++++++ tests/test_camera/test_camera_module.py | 14 ++++++++++---- tests/test_camera/test_file_camera.py | 21 +++++++++++++++++++++ zoloto/cameras/camera.py | 9 ++++++++- zoloto/cameras/file.py | 13 ++++++++++--- zoloto/exceptions.py | 4 ++++ 6 files changed, 71 insertions(+), 8 deletions(-) create mode 100644 tests/test_camera/test_file_camera.py diff --git a/tests/test_camera/test_camera.py b/tests/test_camera/test_camera.py index 8e9c5105..5aa93749 100644 --- a/tests/test_camera/test_camera.py +++ b/tests/test_camera/test_camera.py @@ -6,6 +6,7 @@ import zoloto.cameras from tests.strategies import marker_types +from zoloto.exceptions import CameraOpenError from zoloto.marker_type import MarkerType @@ -57,3 +58,20 @@ def test_get_no_camera_ids(mocker: MockerFixture) -> None: VideoCapture.return_value.isOpened.return_value = False discovered_ids = list(zoloto.cameras.camera.find_camera_ids()) assert len(discovered_ids) == 0 + + +def test_cannot_create_unopened_camera(mocker: MockerFixture) -> None: + VideoCapture = mocker.patch("zoloto.cameras.camera.VideoCapture") + VideoCapture.return_value.isOpened.return_value = False + with pytest.raises(CameraOpenError): + zoloto.cameras.Camera(0, marker_type=MarkerType.APRILTAG_36H11) + + +def test_cannot_create_unopened_snapshotcamera(mocker: MockerFixture) -> None: + VideoCapture = mocker.patch("zoloto.cameras.camera.VideoCapture") + VideoCapture.return_value.isOpened.return_value = False + camera = zoloto.cameras.camera.SnapshotCamera( + 0, marker_type=MarkerType.APRILTAG_36H11 + ) + with pytest.raises(CameraOpenError): + camera.capture_frame() diff --git a/tests/test_camera/test_camera_module.py b/tests/test_camera/test_camera_module.py index 4c19adba..d4486290 100644 --- a/tests/test_camera/test_camera_module.py +++ b/tests/test_camera/test_camera_module.py @@ -20,9 +20,15 @@ def test_exposes_file_camera(camera_name: str) -> None: @given(marker_types()) -def test_camera_requires_marker_size(marker_type: MarkerType) -> None: +def test_camera_requires_marker_size( + marker_camera: zoloto.cameras.marker.MarkerCamera, + temp_image_file: Path, + marker_type: MarkerType, +) -> None: + marker_camera.save_frame(temp_image_file) + camera = zoloto.cameras.file.ImageFileCamera( - Path("test.png"), marker_type=marker_type + temp_image_file, marker_type=marker_type ) with pytest.raises(ValueError): camera.get_marker_size(0) @@ -31,11 +37,11 @@ class TestCamera(zoloto.cameras.file.ImageFileCamera): def get_marker_size(self, marker_id: int) -> int: return 200 - camera = TestCamera(Path("test.png"), marker_type=marker_type) + camera = TestCamera(temp_image_file, marker_type=marker_type) assert camera.get_marker_size(0) == 200 camera = zoloto.cameras.file.ImageFileCamera( - Path("test.png"), + temp_image_file, marker_type=marker_type, marker_size=200, ) diff --git a/tests/test_camera/test_file_camera.py b/tests/test_camera/test_file_camera.py new file mode 100644 index 00000000..1e5d5b76 --- /dev/null +++ b/tests/test_camera/test_file_camera.py @@ -0,0 +1,21 @@ +from pathlib import Path + +import pytest + +from zoloto.cameras.file import ImageFileCamera, VideoFileCamera +from zoloto.exceptions import CameraOpenError +from zoloto.marker_type import MarkerType + + +def test_video_camera_unknown_file() -> None: + with pytest.raises(CameraOpenError): + VideoFileCamera( + Path.cwd() / "missing.mp4", marker_type=MarkerType.APRILTAG_36H11 + ) + + +def test_image_camera_unknown_file() -> None: + with pytest.raises(CameraOpenError): + ImageFileCamera( + Path.cwd() / "missing.png", marker_type=MarkerType.APRILTAG_36H11 + ) diff --git a/zoloto/cameras/camera.py b/zoloto/cameras/camera.py index eaee7b60..0ed0262f 100644 --- a/zoloto/cameras/camera.py +++ b/zoloto/cameras/camera.py @@ -4,6 +4,7 @@ from cv2 import CAP_PROP_BUFFERSIZE, VideoCapture from numpy import ndarray +from zoloto.exceptions import CameraOpenError from zoloto.marker_type import MarkerType from .base import BaseCamera @@ -41,6 +42,9 @@ def __init__( self.camera_id = camera_id self.video_capture = self.get_video_capture(self.camera_id) + if not self.video_capture.isOpened(): + raise CameraOpenError(f"Failed to open camera {self.camera_id}") + def __repr__(self) -> str: return f"<{self.__class__.__name__}: {self.camera_id}>" @@ -90,7 +94,10 @@ def __repr__(self) -> str: return f"<{self.__class__.__name__}: {self.camera_id}>" def get_video_capture(self, camera_id: int) -> VideoCapture: - return VideoCapture(camera_id) + capture = VideoCapture(camera_id) + if not capture.isOpened(): + raise CameraOpenError(f"Failed to open camera {self.camera_id}") + return capture def capture_frame(self) -> ndarray: self.video_capture = self.get_video_capture(self.camera_id) diff --git a/zoloto/cameras/file.py b/zoloto/cameras/file.py index 6d762fa8..4e2be8b4 100644 --- a/zoloto/cameras/file.py +++ b/zoloto/cameras/file.py @@ -4,7 +4,7 @@ from cv2 import VideoCapture, imread from numpy import ndarray -from zoloto.exceptions import CameraReadError +from zoloto.exceptions import CameraOpenError, CameraReadError from zoloto.marker_type import MarkerType from .base import BaseCamera @@ -20,18 +20,22 @@ def __init__( marker_type: MarkerType, calibration_file: Optional[Path] = None, ) -> None: - self.image_path = image_path super().__init__( marker_size=marker_size, marker_type=marker_type, calibration_file=calibration_file, ) + self.image_path = image_path + self._frame = imread(str(self.image_path)) + + if self._frame is None: + raise CameraOpenError(f"Failed to read file {self.image_path}") def __repr__(self) -> str: return f"<{self.__class__.__name__}: {self.image_path}>" def capture_frame(self) -> ndarray: - return imread(str(self.image_path)) + return self._frame class VideoFileCamera( @@ -53,6 +57,9 @@ def __init__( self.video_path = video_path self.video_capture = VideoCapture(str(self.video_path)) + if not self.video_capture.isOpened(): + raise CameraOpenError(f"Failed to read file {self.video_path}") + def __repr__(self) -> str: return f"<{self.__class__.__name__}: {self.video_path}>" diff --git a/zoloto/exceptions.py b/zoloto/exceptions.py index 93941c0d..f3050d59 100644 --- a/zoloto/exceptions.py +++ b/zoloto/exceptions.py @@ -15,3 +15,7 @@ class CameraReadError(ZolotoException): def __init__(self, frame: Optional[ndarray]): self.frame = frame super().__init__() + + +class CameraOpenError(ZolotoException): + pass