Skip to content

Commit 93293e0

Browse files
hexbabeSean YuSean Yu
authored
RSDK-4440: Add extra struct in method signatures for camera (#413)
Co-authored-by: Sean Yu <[email protected]> Co-authored-by: Sean Yu <[email protected]>
1 parent 79cd1ad commit 93293e0

File tree

7 files changed

+56
-16
lines changed

7 files changed

+56
-16
lines changed

examples/server/v1/components.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -351,13 +351,13 @@ def __init__(self, name: str):
351351
def __del__(self):
352352
self.image.close()
353353

354-
async def get_image(self, mime_type: str = "", **kwargs) -> Image.Image:
354+
async def get_image(self, mime_type: str = "", extra: Optional[Dict[str, Any]] = None, **kwargs) -> Image.Image:
355355
return self.image.copy()
356356

357357
async def get_images(self, timeout: Optional[float] = None, **kwargs) -> Tuple[List[NamedImage], ResponseMetadata]:
358358
raise NotImplementedError()
359359

360-
async def get_point_cloud(self, **kwargs) -> Tuple[bytes, str]:
360+
async def get_point_cloud(self, extra: Optional[Dict[str, Any]] = None, **kwargs) -> Tuple[bytes, str]:
361361
raise NotImplementedError()
362362

363363
async def get_properties(self, **kwargs) -> Camera.Properties:

src/viam/components/camera/camera.py

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import abc
2-
from typing import Final, List, NamedTuple, Optional, Tuple, Union
2+
from typing import Final, List, NamedTuple, Optional, Tuple, Union, Any, Dict
33

44
from PIL.Image import Image
55

@@ -35,7 +35,14 @@ class Properties(NamedTuple):
3535
"""The distortion parameters of the camera"""
3636

3737
@abc.abstractmethod
38-
async def get_image(self, mime_type: str = "", *, timeout: Optional[float] = None, **kwargs) -> Union[Image, RawImage]:
38+
async def get_image(
39+
self,
40+
mime_type: str = "",
41+
*,
42+
extra: Optional[Dict[str, Any]] = None,
43+
timeout: Optional[float] = None,
44+
**kwargs
45+
) -> Union[Image, RawImage]:
3946
"""Get the next image from the camera as an Image or RawImage.
4047
Be sure to close the image when finished.
4148
@@ -66,7 +73,12 @@ async def get_images(self, *, timeout: Optional[float] = None, **kwargs) -> Tupl
6673
...
6774

6875
@abc.abstractmethod
69-
async def get_point_cloud(self, *, timeout: Optional[float] = None, **kwargs) -> Tuple[bytes, str]:
76+
async def get_point_cloud(self,
77+
*,
78+
extra: Optional[Dict[str, Any]] = None,
79+
timeout: Optional[float] = None,
80+
**kwargs
81+
) -> Tuple[bytes, str]:
7082
"""
7183
Get the next point cloud from the camera. This will be
7284
returned as bytes with a mimetype describing

src/viam/components/camera/client.py

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,15 @@ def __init__(self, name: str, channel: Channel):
4343
self.client = CameraServiceStub(channel)
4444
super().__init__(name)
4545

46-
async def get_image(self, mime_type: str = "", *, timeout: Optional[float] = None) -> Union[Image.Image, RawImage]:
47-
request = GetImageRequest(name=self.name, mime_type=mime_type)
46+
async def get_image(self,
47+
mime_type: str = "",
48+
*,
49+
extra: Optional[Dict[str, Any]] = None,
50+
timeout: Optional[float] = None
51+
) -> Union[Image.Image, RawImage]:
52+
if extra is None:
53+
extra = {}
54+
request = GetImageRequest(name=self.name, mime_type=mime_type, extra=dict_to_struct(extra))
4855
response: GetImageResponse = await self.client.GetImage(request, timeout=timeout)
4956
return get_image_from_response(response.image, response_mime_type=response.mime_type, request_mime_type=request.mime_type)
5057

@@ -59,8 +66,10 @@ async def get_images(self, *, timeout: Optional[float] = None) -> Tuple[List[Nam
5966
resp_metadata: ResponseMetadata = response.response_metadata
6067
return imgs, resp_metadata
6168

62-
async def get_point_cloud(self, *, timeout: Optional[float] = None) -> Tuple[bytes, str]:
63-
request = GetPointCloudRequest(name=self.name, mime_type=CameraMimeType.PCD)
69+
async def get_point_cloud(self, *, extra: Optional[Dict[str, Any]] = None, timeout: Optional[float] = None) -> Tuple[bytes, str]:
70+
if extra is None:
71+
extra = {}
72+
request = GetPointCloudRequest(name=self.name, mime_type=CameraMimeType.PCD, extra=dict_to_struct(extra))
6473
response: GetPointCloudResponse = await self.client.GetPointCloud(request, timeout=timeout)
6574
return (response.point_cloud, response.mime_type)
6675

src/viam/components/camera/service.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ async def GetImage(self, stream: Stream[GetImageRequest, GetImageResponse]) -> N
3939
camera = self.get_resource(name)
4040

4141
timeout = stream.deadline.time_remaining() if stream.deadline else None
42-
image = await camera.get_image(request.mime_type, timeout=timeout, metadata=stream.metadata)
42+
image = await camera.get_image(request.mime_type, extra=struct_to_dict(request.extra), timeout=timeout, metadata=stream.metadata)
4343
try:
4444
if not request.mime_type:
4545
if camera.name not in self._camera_mime_types:
@@ -106,7 +106,7 @@ async def GetPointCloud(self, stream: Stream[GetPointCloudRequest, GetPointCloud
106106
name = request.name
107107
camera = self.get_resource(name)
108108
timeout = stream.deadline.time_remaining() if stream.deadline else None
109-
pc, mimetype = await camera.get_point_cloud(timeout=timeout, metadata=stream.metadata)
109+
pc, mimetype = await camera.get_point_cloud(timeout=timeout, extra=struct_to_dict(request.extra), metadata=stream.metadata)
110110
response = GetPointCloudResponse(mime_type=mimetype, point_cloud=pc)
111111
await stream.send_message(response)
112112

src/viam/gen/component/camera/v1/camera_pb2.pyi

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -359,4 +359,4 @@ class DistortionParameters(google.protobuf.message.Message):
359359

360360
def ClearField(self, field_name: typing_extensions.Literal['model', b'model', 'parameters', b'parameters']) -> None:
361361
...
362-
global___DistortionParameters = DistortionParameters
362+
global___DistortionParameters = DistortionParameters

tests/mocks/components.py

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -394,6 +394,7 @@ def __init__(self, name: str):
394394
self.image = Image.new("RGBA", (100, 100), "#AABBCCDD")
395395
self.geometries = GEOMETRIES
396396
self.point_cloud = b"THIS IS A POINT CLOUD"
397+
self.extra = None
397398
self.props = Camera.Properties(
398399
False,
399400
IntrinsicParameters(width_px=1, height_px=2, focal_x_px=3, focal_y_px=4, center_x_px=5, center_y_px=6),
@@ -405,7 +406,13 @@ def __init__(self, name: str):
405406
self.metadata = ResponseMetadata(captured_at=ts)
406407
super().__init__(name)
407408

408-
async def get_image(self, mime_type: str = "", timeout: Optional[float] = None, **kwargs) -> Union[Image.Image, RawImage]:
409+
async def get_image(self,
410+
mime_type: str = "",
411+
extra: Optional[Dict[str, Any]] = None,
412+
timeout: Optional[float] = None,
413+
**kwargs
414+
) -> Union[Image.Image, RawImage]:
415+
self.extra = extra
409416
self.timeout = timeout
410417
mime_type, is_lazy = CameraMimeType.from_lazy(mime_type)
411418
if is_lazy or (not CameraMimeType.is_supported(mime_type)):
@@ -425,7 +432,13 @@ async def get_images(self, timeout: Optional[float] = None, **kwargs) -> Tuple[L
425432
)
426433
], self.metadata
427434

428-
async def get_point_cloud(self, *, timeout: Optional[float] = None, **kwargs) -> Tuple[bytes, str]:
435+
async def get_point_cloud(self,
436+
*,
437+
extra: Optional[Dict[str, Any]] = None,
438+
timeout: Optional[float] = None,
439+
**kwargs
440+
) -> Tuple[bytes, str]:
441+
self.extra = extra
429442
self.timeout = timeout
430443
return self.point_cloud, CameraMimeType.PCD
431444

tests/test_camera.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -85,10 +85,13 @@ def generic_service(camera: Camera) -> GenericRPCService:
8585

8686
class TestCamera:
8787
@pytest.mark.asyncio
88-
async def test_get_image(self, camera: Camera, image: Image.Image):
88+
async def test_get_image(self, camera: MockCamera, image: Image.Image):
8989
img = await camera.get_image(CameraMimeType.PNG)
9090
assert img == image
9191

92+
img = await camera.get_image(CameraMimeType.PNG, {"1": 1})
93+
assert camera.extra == {"1": 1}
94+
9295
img = await camera.get_image(CameraMimeType.VIAM_RGBA)
9396
assert isinstance(img, Image.Image)
9497

@@ -104,10 +107,13 @@ async def test_get_images(self, camera: Camera, image: Image.Image, metadata: Re
104107
assert md == metadata
105108

106109
@pytest.mark.asyncio
107-
async def test_get_point_cloud(self, camera: Camera, point_cloud: bytes):
110+
async def test_get_point_cloud(self, camera: MockCamera, point_cloud: bytes):
108111
pc, _ = await camera.get_point_cloud()
109112
assert pc == point_cloud
110113

114+
await camera.get_point_cloud(extra={"1": 1})
115+
assert camera.extra == {"1": 1}
116+
111117
@pytest.mark.asyncio
112118
async def test_get_properties(self, camera: Camera, properties: Camera.Properties):
113119
props = await camera.get_properties()

0 commit comments

Comments
 (0)