Skip to content

Commit

Permalink
Revert camera API change for future implementation (#517)
Browse files Browse the repository at this point in the history
  • Loading branch information
njooma authored Jan 4, 2024
1 parent ef20c68 commit 93c63c8
Show file tree
Hide file tree
Showing 4 changed files with 34 additions and 24 deletions.
2 changes: 1 addition & 1 deletion docs/examples/example.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@
"robot = await connect_with_channel()\n",
"camera = Camera.from_robot(robot, \"camera0\")\n",
"image = await camera.get_image(CameraMimeType.JPEG)\n",
"image.image.save(\"foo.png\")\n",
"image.save(\"foo.png\")\n",
"\n",
"# Don't forget to close the robot when you're done!\n",
"await robot.close()"
Expand Down
13 changes: 8 additions & 5 deletions src/viam/components/camera/camera.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
import abc
import sys
from typing import Any, Dict, Final, List, Optional, Tuple
from typing import Any, Dict, Final, List, Optional, Tuple, Union

from viam.media.video import NamedImage, ViamImage
from PIL.Image import Image

from viam.media.video import NamedImage
from viam.proto.common import ResponseMetadata
from viam.proto.component.camera import GetPropertiesResponse
from viam.resource.types import RESOURCE_NAMESPACE_RDK, RESOURCE_TYPE_COMPONENT, Subtype

from ..component_base import ComponentBase
from . import RawImage

if sys.version_info >= (3, 10):
from typing import TypeAlias
Expand All @@ -33,8 +36,8 @@ class Camera(ComponentBase):
@abc.abstractmethod
async def get_image(
self, mime_type: str = "", *, extra: Optional[Dict[str, Any]] = None, timeout: Optional[float] = None, **kwargs
) -> ViamImage:
"""Get the next image from the camera as a ViamImage.
) -> Union[Image, RawImage]:
"""Get the next image from the camera as an Image or RawImage.
Be sure to close the image when finished.
NOTE: If the mime type is ``image/vnd.viam.dep`` you can use :func:`viam.media.video.ViamImage.bytes_to_depth_array`
Expand All @@ -44,7 +47,7 @@ async def get_image(
mime_type (str): The desired mime type of the image. This does not guarantee output type
Returns:
ViamImage: The frame
Image | RawImage: The frame
"""
...

Expand Down
19 changes: 12 additions & 7 deletions src/viam/components/camera/client.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
from typing import Any, Dict, List, Mapping, Optional, Tuple
from io import BytesIO
from typing import Any, Dict, List, Mapping, Optional, Tuple, Union

from grpclib.client import Channel
from PIL import Image

from viam.media.video import CameraMimeType, NamedImage, ViamImage
from viam.media.video import LIBRARY_SUPPORTED_FORMATS, CameraMimeType, NamedImage
from viam.proto.common import DoCommandRequest, DoCommandResponse, Geometry, ResponseMetadata
from viam.proto.component.camera import (
CameraServiceStub,
Expand All @@ -17,14 +19,17 @@
from viam.resource.rpc_client_base import ReconfigurableResourceRPCClientBase
from viam.utils import ValueTypes, dict_to_struct, get_geometries, struct_to_dict

from . import Camera
from . import Camera, RawImage


def get_image_from_response(data: bytes, response_mime_type: str, request_mime_type: str) -> ViamImage:
def get_image_from_response(data: bytes, response_mime_type: str, request_mime_type: str) -> Union[Image.Image, RawImage]:
if not request_mime_type:
request_mime_type = response_mime_type
mime_type, _ = CameraMimeType.from_lazy(request_mime_type)
return ViamImage(data, mime_type)
mime_type, is_lazy = CameraMimeType.from_lazy(request_mime_type)
if is_lazy or mime_type._should_be_raw:
image = RawImage(data=data, mime_type=response_mime_type)
return image
return Image.open(BytesIO(data), formats=LIBRARY_SUPPORTED_FORMATS)


class CameraClient(Camera, ReconfigurableResourceRPCClientBase):
Expand All @@ -44,7 +49,7 @@ async def get_image(
extra: Optional[Dict[str, Any]] = None,
timeout: Optional[float] = None,
**__,
) -> ViamImage:
) -> Union[Image.Image, RawImage]:
if extra is None:
extra = {}
request = GetImageRequest(name=self.name, mime_type=mime_type, extra=dict_to_struct(extra))
Expand Down
24 changes: 13 additions & 11 deletions tests/test_camera.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
from viam.components.camera import Camera, CameraClient
from viam.components.camera.service import CameraRPCService
from viam.components.generic.service import GenericRPCService
from viam.media.video import LIBRARY_SUPPORTED_FORMATS, CameraMimeType, NamedImage, RawImage, ViamImage
from viam.media.video import LIBRARY_SUPPORTED_FORMATS, CameraMimeType, NamedImage, RawImage
from viam.proto.common import DoCommandRequest, DoCommandResponse, GetGeometriesRequest, GetGeometriesResponse, ResponseMetadata
from viam.proto.component.camera import (
CameraServiceStub,
Expand Down Expand Up @@ -253,29 +253,30 @@ async def test_get_image(self, camera: MockCamera, service: CameraRPCService, im

# Test known mime type
png_img = await client.get_image(timeout=1.82, mime_type=CameraMimeType.PNG)
assert isinstance(png_img.image, Image.Image)
assert png_img.image.tobytes() == image.tobytes()
assert isinstance(png_img, Image.Image)
assert png_img.tobytes() == image.tobytes()
assert isinstance(png_img, Image.Image)
assert png_img.tobytes() == image.tobytes()
assert camera.timeout == loose_approx(1.82)

# Test raw mime type
rgba_img = await client.get_image(CameraMimeType.VIAM_RGBA)
assert isinstance(rgba_img.image, Image.Image)
rgba_bytes = rgba_img.image.tobytes()
assert isinstance(rgba_img, Image.Image)
rgba_bytes = rgba_img.tobytes()
assert isinstance(rgba_img, Image.Image)
rgba_bytes = rgba_img.tobytes()
assert rgba_bytes == image.copy().convert("RGBA").tobytes()

# Test lazy mime type
raw_img = await client.get_image(CameraMimeType.PNG.with_lazy_suffix)
assert isinstance(raw_img, ViamImage)
assert raw_img.image is None
assert isinstance(raw_img, RawImage)
assert raw_img.data == image.tobytes()
assert raw_img.mime_type == CameraMimeType.PNG

# Test unknown mime type
raw_img = await client.get_image("unknown")
assert isinstance(raw_img, ViamImage)
assert raw_img.image is None
assert raw_img.data == image.tobytes()
assert raw_img.mime_type == CameraMimeType.UNSUPPORTED
assert isinstance(raw_img, RawImage)
assert raw_img.mime_type == "unknown"

@pytest.mark.asyncio
async def test_get_images(self, camera: MockCamera, service: CameraRPCService, image: Image.Image, metadata: ResponseMetadata):
Expand All @@ -286,6 +287,7 @@ async def test_get_images(self, camera: MockCamera, service: CameraRPCService, i
imgs, md = await client.get_images(timeout=1.82)
assert isinstance(imgs[0], NamedImage)
assert imgs[0].name == camera.name
assert imgs[0].image is not None
assert imgs[0].image.tobytes() == image.tobytes()
assert md == metadata
assert camera.timeout == loose_approx(1.82)
Expand Down

0 comments on commit 93c63c8

Please sign in to comment.