Skip to content
Merged
Binary file added data/fakeDM.vnd.viam.dep
Binary file not shown.
3 changes: 3 additions & 0 deletions src/viam/components/camera/camera.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,9 @@ async def get_image(self, mime_type: str = "", *, timeout: Optional[float] = Non
"""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.RawImage.bytes_to_depth_array`
to convert the data to a standard representation.

Args:
mime_type (str): The desired mime type of the image. This does not guarantee output type

Expand Down
27 changes: 25 additions & 2 deletions src/viam/media/video.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
from array import array
from enum import Enum
from io import BytesIO
from typing import NamedTuple, Tuple, Union

from PIL.Image import Image
from typing import List, NamedTuple, Tuple, Union
from typing_extensions import Self

from viam.errors import NotSupportedError

from .viam_rgba_plugin import RGBA_FORMAT_LABEL

LAZY_SUFFIX = "+lazy"
Expand Down Expand Up @@ -34,6 +36,27 @@ def close(self):
"""Close the image and release resources. For RawImage, this is a noop."""
return

def bytes_to_depth_array(self) -> List[List[int]]:
"""Decode the data of an image that has the custom depth MIME type ``image/vnd.viam.dep`` into
a standard representation.

Raises:
NotSupportedError: Raised if given an image that is not of MIME type `image/vnd.viam.dep`.

Returns:
List[List[int]]: The standard representation of the image.
"""
if self.mime_type != "image/vnd.viam.dep":
raise NotSupportedError("Type must be `image/vnd.viam.dep` to use bytes_to_depth_array()")

width = int.from_bytes(self.data[8:16], "big")
height = int.from_bytes(self.data[16:24], "big")
depth_arr = array("H", self.data[24:])
Copy link

@zaporter-work zaporter-work May 9, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am surprised you dont specify the endianness of these unsigned shorts but you do for the other values.

depth_arr.byteswap()

depth_arr_2d = [[depth_arr[row * width + col] for col in range(width)] for row in range(height)]
return depth_arr_2d


class CameraMimeType(str, Enum):
VIAM_RGBA = "image/vnd.viam.rgba"
Expand Down
23 changes: 23 additions & 0 deletions tests/test_media.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
from array import array
import os
import pytest

from viam.media.video import RawImage


class TestRawImage:
@pytest.mark.asyncio
async def test_bytes_to_depth_array(self):
with open(f"{os.path.dirname(__file__)}/../data/fakeDM.vnd.viam.dep", "rb") as depth_map:
image = RawImage(depth_map.read(), "image/vnd.viam.dep")
assert isinstance(image, RawImage)
standard_data = image.bytes_to_depth_array()
assert len(standard_data) == 10
assert len(standard_data[0]) == 20
data_arr = array("H", image.data[24:])
data_arr.byteswap()
assert len(data_arr) == 200
assert standard_data[0][0] == data_arr[0]
assert standard_data[-1][3] == data_arr[183]
assert standard_data[-1][3] == 9 * 3
assert standard_data[4][4] == 4 * 4