diff --git a/src/motion_detector.py b/src/motion_detector.py index 4b23d79..f90ecf8 100644 --- a/src/motion_detector.py +++ b/src/motion_detector.py @@ -1,5 +1,5 @@ import math -from typing import Any, ClassVar, Dict, List, Mapping, Optional, Sequence +from typing import Any, ClassVar, Dict, List, Mapping, Optional, Sequence, Tuple import cv2 import numpy as np @@ -51,7 +51,7 @@ def new_service( # Validates JSON Configuration @classmethod - def validate_config(cls, config: ServiceConfig) -> Sequence[str]: + def validate_config(cls, config: ServiceConfig) -> Tuple[Sequence[str], Sequence[str]]: validate_cam_name = config.attributes.fields["cam_name"].string_value validate_camera_name = config.attributes.fields["camera_name"].string_value @@ -126,7 +126,7 @@ def validate_config(cls, config: ServiceConfig) -> Sequence[str]: raise ValueError("x1_rel should be less than x2_rel") if y1_rel > y2_rel: raise ValueError("y1_rel should be less than y2_rel") - return [source_cam] + return [source_cam], [] # Handles attribute reconfiguration def reconfigure( @@ -172,7 +172,10 @@ async def get_classifications( **kwargs, ) -> List[Classification]: # Grab and grayscale 2 images - input1 = await self.camera.get_image(mime_type=CameraMimeType.JPEG) + images, _ = await self.camera.get_images() + if images is None or len(images) == 0: + raise ValueError("No images returned by get_images") + input1 = images[0] if input1.mime_type not in [CameraMimeType.JPEG, CameraMimeType.PNG]: raise ValueError( "image mime type must be PNG or JPEG, not ", input1.mime_type @@ -181,7 +184,10 @@ async def get_classifications( img1, _, _ = self.crop_image(img1) gray1 = cv2.cvtColor(np.array(img1), cv2.COLOR_BGR2GRAY) - input2 = await self.camera.get_image() + camera_images, _ = await self.camera.get_images() + if camera_images is None or len(camera_images) == 0: + raise ValueError("No images were returned by get_images") + input2 = camera_images[0] if input2.mime_type not in [CameraMimeType.JPEG, CameraMimeType.PNG]: raise ValueError( "image mime type must be PNG or JPEG, not ", input2.mime_type @@ -222,7 +228,10 @@ async def get_detections( **kwargs, ) -> List[Detection]: # Grab and grayscale 2 images - input1 = await self.camera.get_image(mime_type=CameraMimeType.JPEG) + images, _ = await self.camera.get_images() + if images is None or len(images) == 0: + raise ValueError("No images returned by get_images") + input1 = images[0] if input1.mime_type not in [CameraMimeType.JPEG, CameraMimeType.PNG]: raise ValueError( "image mime type must be PNG or JPEG, not ", input1.mime_type @@ -231,7 +240,10 @@ async def get_detections( img1, width, height = self.crop_image(img1) gray1 = cv2.cvtColor(np.array(img1), cv2.COLOR_BGR2GRAY) - input2 = await self.camera.get_image() + camera_images2, _ = await self.camera.get_images() + if camera_images2 is None or len(camera_images2) == 0: + raise ValueError("No images were returned by get_images") + input2 = camera_images2[0] if input2.mime_type not in [CameraMimeType.JPEG, CameraMimeType.PNG]: raise ValueError( "image mime type must be PNG or JPEG, not ", input2.mime_type @@ -302,7 +314,11 @@ async def capture_all_from_camera( # pylint: disable=too-many-positional-argume "is not the configured 'cam_name':", self.cam_name, ) - img = await self.camera.get_image(mime_type=CameraMimeType.JPEG) + imgs, _ = await self.camera.get_images() + if (imgs is None or len(imgs) == 0) and \ + (return_image or return_classifications or return_detections): + raise ValueError("No images returned by get_images") + img = imgs[0] if return_image: result.image = img if return_classifications: diff --git a/tests/fakecam.py b/tests/fakecam.py index 37600be..ddca911 100644 --- a/tests/fakecam.py +++ b/tests/fakecam.py @@ -22,7 +22,8 @@ async def get_image(self, mime_type: str = "") -> Coroutine[Any, Any, ViamImage] return pil.pil_to_viam_image(self.images[self.count%2], CameraMimeType.JPEG) async def get_images(self) -> Coroutine[Any, Any, Tuple[List[NamedImage] | ResponseMetadata]]: - raise NotImplementedError + self.count +=1 + return [pil.pil_to_viam_image(self.images[self.count%2], CameraMimeType.JPEG)], None async def get_properties(self) -> Coroutine[Any, Any, GetPropertiesResponse]: raise NotImplementedError diff --git a/tests/test_motiondetector.py b/tests/test_motiondetector.py index 3d955b5..e882338 100644 --- a/tests/test_motiondetector.py +++ b/tests/test_motiondetector.py @@ -44,7 +44,7 @@ def test_empty(self): with pytest.raises( ValueError, match=pytest.source_camera_name_none_defined_error_message ): - response = md.validate_config(config=empty_config) + response, _ = md.validate_config(config=empty_config) # For each way to specify a valid min/max size, have a test that checks it's valid. @parameterized.expand( @@ -74,7 +74,7 @@ def test_valid(self, unused_test_name, extra_config_values): raw_config = {"cam_name": "test"} raw_config.update(extra_config_values) config = make_component_config(raw_config) - response = md.validate_config(config=config) + response, _ = md.validate_config(config=config) assert response == ["test"] # For each type of invalid config, test that the expected error is raised. @@ -120,7 +120,7 @@ def test_invalid(self, error_message, extra_config_values): raw_config.update(extra_config_values) config = make_component_config(raw_config) with pytest.raises(ValueError, match=error_message): - response = md.validate_config(config=config) + response, _ = md.validate_config(config=config) def test_empty_config_name(self): md = getMD() @@ -129,7 +129,7 @@ def test_empty_config_name(self): with pytest.raises( ValueError, match=pytest.source_camera_name_none_defined_error_message ): - response = md.validate_config(config=config) + response, _ = md.validate_config(config=config) # For each way to specify a valid camera name, test that the return is valid. @parameterized.expand( @@ -141,7 +141,7 @@ def test_empty_config_name(self): def test_valid_camera_names(self, unused_test_name, cam_config): md = getMD() config = make_component_config(cam_config) - response = md.validate_config(config=config) + response, _ = md.validate_config(config=config) assert response == ["test"] # For each way to spedify an invalid camera name, test that the expected error is raised. @@ -168,7 +168,7 @@ def test_invalid_camera_names(self, unused_test_name, cam_config, error_message) md = getMD() config = make_component_config(cam_config) with pytest.raises(ValueError, match=error_message): - response = md.validate_config(config=config) + response, _ = md.validate_config(config=config) class TestMotionDetector: