Skip to content

Commit 0669c03

Browse files
committed
Cast mime type to str always in SDK tests; Fix failing tests and vision logic
1 parent 625086f commit 0669c03

File tree

14 files changed

+97
-49
lines changed

14 files changed

+97
-49
lines changed

docs/examples/example.ipynb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,7 @@
151151
},
152152
{
153153
"cell_type": "code",
154-
"execution_count": 5,
154+
"execution_count": null,
155155
"metadata": {
156156
"tags": [
157157
"hide-output"
@@ -165,7 +165,7 @@
165165
"\n",
166166
"robot = await connect_with_channel()\n",
167167
"camera = Camera.from_robot(robot, \"camera0\")\n",
168-
"image = await camera.get_image(CameraMimeType.JPEG)\n",
168+
"image = await camera.get_image(CameraMimeType.JPEG.value)\n",
169169
"pil = viam_to_pil_image(image)\n",
170170
"pil.save(\"foo.png\")\n",
171171
"\n",

examples/server/v1/client.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ async def client():
3434

3535
print("\n#### CAMERA ####")
3636
camera = Camera.from_robot(robot, "camera0")
37-
img = await camera.get_image(mime_type=CameraMimeType.PNG)
37+
img = await camera.get_image(mime_type=CameraMimeType.PNG.value)
3838
assert isinstance(img, Image)
3939
img.show()
4040
await asyncio.sleep(1)

examples/server/v1/components.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -322,7 +322,7 @@ def __init__(self, name: str):
322322
img = Image.open(p.parent.absolute().joinpath("viam.jpeg"))
323323
buf = BytesIO()
324324
img.copy().save(buf, format="JPEG")
325-
self.image = ViamImage(buf.getvalue(), CameraMimeType.JPEG)
325+
self.image = ViamImage(buf.getvalue(), CameraMimeType.JPEG.value)
326326
img.close()
327327
super().__init__(name)
328328

src/viam/components/camera/camera.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,9 @@ async def get_images(self, *, timeout: Optional[float] = None, **kwargs) -> Tupl
6767
"""Get simultaneous images from different imagers, along with associated metadata.
6868
This should not be used for getting a time series of images from the same imager.
6969
70+
The extra parameter can be used to pass additional options to the camera resource. The filter_source_names parameter can be used to filter
71+
only the images from the specified source names. When unspecified, all images are returned.
72+
7073
::
7174
7275
my_camera = Camera.from_robot(robot=machine, name="my_camera")

src/viam/components/camera/client.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,11 @@ async def get_images(
5454
response: GetImagesResponse = await self.client.GetImages(request, timeout=timeout, metadata=md)
5555
imgs = []
5656
for img_data in response.images:
57-
mime_type = CameraMimeType.from_proto(img_data.format)
57+
if img_data.mime_type:
58+
mime_type = img_data.mime_type
59+
else:
60+
# TODO(RSDK-11728): remove this once we deleted the format field
61+
mime_type = CameraMimeType.from_proto(img_data.format).value
5862
img = NamedImage(img_data.source_name, img_data.image, mime_type)
5963
imgs.append(img)
6064
resp_metadata: ResponseMetadata = response.response_metadata
@@ -68,7 +72,7 @@ async def get_point_cloud(
6872
**kwargs,
6973
) -> Tuple[bytes, str]:
7074
md = kwargs.get("metadata", self.Metadata()).proto
71-
request = GetPointCloudRequest(name=self.name, mime_type=CameraMimeType.PCD, extra=dict_to_struct(extra))
75+
request = GetPointCloudRequest(name=self.name, mime_type=CameraMimeType.PCD.value, extra=dict_to_struct(extra))
7276
response: GetPointCloudResponse = await self.client.GetPointCloud(request, timeout=timeout, metadata=md)
7377
return (response.point_cloud, response.mime_type)
7478

src/viam/components/camera/service.py

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
from viam.proto.common import DoCommandRequest, DoCommandResponse, GetGeometriesRequest, GetGeometriesResponse
77
from viam.proto.component.camera import (
88
CameraServiceBase,
9+
Format,
910
GetImageRequest,
1011
GetImageResponse,
1112
GetImagesRequest,
@@ -20,6 +21,8 @@
2021
from viam.resource.rpc_service_base import ResourceRPCServiceBase
2122
from viam.utils import dict_to_struct, struct_to_dict
2223

24+
from viam.media.video import CameraMimeType
25+
2326
from . import Camera
2427

2528

@@ -51,9 +54,16 @@ async def GetImages(self, stream: Stream[GetImagesRequest, GetImagesResponse]) -
5154
images, metadata = await camera.get_images(timeout=timeout, metadata=stream.metadata)
5255
img_bytes_lst = []
5356
for img in images:
54-
fmt = img.mime_type.to_proto()
57+
# TODO(RSDK-11728): remove this try except logic once we deleted the format field
58+
try:
59+
mime_type = CameraMimeType.from_string(img.mime_type) # this can ValueError if the mime_type is not a CameraMimeType
60+
fmt = mime_type.to_proto()
61+
except ValueError:
62+
mime_type = img.mime_type
63+
fmt = Format.FORMAT_UNSPECIFIED
64+
5565
img_bytes = img.data
56-
img_bytes_lst.append(Image(source_name=name, format=fmt, image=img_bytes))
66+
img_bytes_lst.append(Image(source_name=name, format=fmt, mime_type=img.mime_type, image=img_bytes))
5767
response = GetImagesResponse(images=img_bytes_lst, response_metadata=metadata)
5868
await stream.send_message(response)
5969

src/viam/media/utils/pil/__init__.py

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ def viam_to_pil_image(image: ViamImage) -> Image.Image:
2525
return Image.open(BytesIO(image.data), formats=LIBRARY_SUPPORTED_FORMATS)
2626

2727

28-
def pil_to_viam_image(image: Image.Image, mime_type: CameraMimeType) -> ViamImage:
28+
def pil_to_viam_image(image: Image.Image, mime_type: str) -> ViamImage:
2929
"""
3030
Convert a PIL.Image to a ViamImage.
3131
@@ -34,11 +34,16 @@ def pil_to_viam_image(image: Image.Image, mime_type: CameraMimeType) -> ViamImag
3434
3535
Args:
3636
image (Image.Image): The image to convert.
37-
mime_type (CameraMimeType): The mime type to convert the image to.
37+
mime_type (str): The mime type to convert the image to. Must be of type `CameraMimeType`.
3838
3939
Returns:
4040
ViamImage: The resulting ViamImage
4141
"""
42+
try:
43+
mime_type = CameraMimeType.from_string(mime_type)
44+
except ValueError as e:
45+
raise ValueError(f"Unsupported mimetype str: {mime_type}") from e
46+
4247
if mime_type.name in LIBRARY_SUPPORTED_FORMATS:
4348
buf = BytesIO()
4449
if image.mode == "RGBA" and mime_type == CameraMimeType.JPEG:
@@ -48,4 +53,4 @@ def pil_to_viam_image(image: Image.Image, mime_type: CameraMimeType) -> ViamImag
4853
else:
4954
raise ValueError(f"Cannot encode image to {mime_type}")
5055

51-
return ViamImage(data, mime_type)
56+
return ViamImage(data, mime_type.value)

src/viam/media/video.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@ class CameraMimeType(str, Enum):
1717
PNG = "image/png"
1818
PCD = "pointcloud/pcd"
1919

20+
def __str__(self) -> str:
21+
return self.value
22+
2023
@classmethod
2124
def from_string(cls, value: str) -> Self:
2225
"""Return the mimetype from a string.

src/viam/services/vision/client.py

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,11 @@ async def capture_all_from_camera(
6969
result = CaptureAllResult()
7070
result.extra = struct_to_dict(response.extra)
7171
if return_image:
72-
mime_type = CameraMimeType.from_proto(response.image.format)
72+
# TODO(RSDK-11728): remove this branching logic once we deleted the format field
73+
if response.image.mime_type:
74+
mime_type = response.image.mime_type
75+
else:
76+
mime_type = CameraMimeType.from_proto(response.image.format).value
7377
img = ViamImage(response.image.image, mime_type)
7478
result.image = img
7579
if return_classifications:
@@ -102,7 +106,7 @@ async def get_detections(
102106
**kwargs,
103107
) -> List[Detection]:
104108
md = kwargs.get("metadata", self.Metadata()).proto
105-
mime_type = CameraMimeType.JPEG
109+
mime_type = CameraMimeType.JPEG.value
106110

107111
if image.width is None or image.height is None:
108112
raise ViamError(f"image {image} needs to have a specified width and height")
@@ -145,7 +149,7 @@ async def get_classifications(
145149
) -> List[Classification]:
146150
md = kwargs.get("metadata", self.Metadata()).proto
147151

148-
mime_type = CameraMimeType.JPEG
152+
mime_type = CameraMimeType.JPEG.value
149153
if image.width is None or image.height is None:
150154
raise ViamError(f"image {image} needs to have a specified width and height")
151155
request = GetClassificationsRequest(
@@ -172,7 +176,7 @@ async def get_object_point_clouds(
172176
request = GetObjectPointCloudsRequest(
173177
name=self.name,
174178
camera_name=camera_name,
175-
mime_type=CameraMimeType.PCD,
179+
mime_type=CameraMimeType.PCD.value,
176180
extra=dict_to_struct(extra),
177181
)
178182
response: GetObjectPointCloudsResponse = await self.client.GetObjectPointClouds(request, timeout=timeout, metadata=md)

src/viam/services/vision/service.py

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
from viam.media.video import CameraMimeType, ViamImage
44
from viam.proto.common import DoCommandRequest, DoCommandResponse
5-
from viam.proto.component.camera import Image
5+
from viam.proto.component.camera import Format, Image
66
from viam.proto.service.vision import (
77
CaptureAllFromCameraRequest,
88
CaptureAllFromCameraResponse,
@@ -36,7 +36,7 @@ class VisionRPCService(UnimplementedVisionServiceBase, ResourceRPCServiceBase):
3636
async def CaptureAllFromCamera(self, stream: Stream[CaptureAllFromCameraRequest, CaptureAllFromCameraResponse]) -> None:
3737
request = await stream.recv_message()
3838
assert request is not None
39-
vision = self.get_resource(request.name)
39+
vision: Vision = self.get_resource(request.name)
4040
extra = struct_to_dict(request.extra)
4141
timeout = stream.deadline.time_remaining() if stream.deadline else None
4242
result = await vision.capture_all_from_camera(
@@ -50,9 +50,15 @@ async def CaptureAllFromCamera(self, stream: Stream[CaptureAllFromCameraRequest,
5050
)
5151
img = None
5252
if result.image is not None:
53-
fmt = result.image.mime_type.to_proto()
53+
# TODO(RSDK-11728): remove this try except logic once we deleted the format field
54+
try:
55+
mime_type = CameraMimeType.from_string(result.image.mime_type) # this can ValueError if mime_type is not a CameraMimeType
56+
fmt = mime_type.to_proto()
57+
except ValueError:
58+
mime_type = result.image.mime_type
59+
fmt = Format.FORMAT_UNSPECIFIED
5460
img_bytes = result.image.data
55-
img = Image(source_name=request.camera_name, format=fmt, image=img_bytes)
61+
img = Image(source_name=request.camera_name, mime_type=result.image.mime_type, format=fmt, image=img_bytes)
5662
response = CaptureAllFromCameraResponse(
5763
image=img,
5864
detections=result.detections,

0 commit comments

Comments
 (0)