Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

RSDK-4089: use viam image #567

Merged

Conversation

purplenicole730
Copy link
Member

@purplenicole730 purplenicole730 commented Mar 27, 2024

BREAKING CHANGE!!!!
Please note that this is going to be merged into rc-0.21.0 to give time for the community to get used to the changes.

Jira Ticket

Changes:

  • return ViamImage when calling `camera.get_image()
  • use ViamImage for the vision service
  • remove SDK support for image processing
  • remove CameraMimeType functions and lazy concept
  • remove PIL.Image from the Python SDK (barring image examples and helper functions)
  • add a new media/utils file to contain helper functions
  • helper functions for converting ViamImage to PIL.Image and getting image dimensions

Testing:
Tested using a webcam and called both get_image_dimensions() as well as viam_to_pil_image().
test_image

Copy link
Contributor

Warning your change may break code samples. If your change modifies any of the following functions please contact @viamrobotics/fleet-management. Thanks!

component function
base is_moving
board gpio_pin_by_name
camera get_image
encoder get_position
motor is_moving
sensor get_readings
servo get_position
arm get_end_position
gantry get_lengths
gripper is_moving
movement_sensor get_linear_acceleration
input_controller get_controls
audio get_properties
pose_tracker get_poses
power_sensor get_power
motion get_pose
vision get_classifications_from_camera

@purplenicole730 purplenicole730 marked this pull request as ready for review April 4, 2024 18:50
@purplenicole730 purplenicole730 requested a review from a team as a code owner April 4, 2024 18:50
Copy link
Member

@stuqdog stuqdog left a comment

Choose a reason for hiding this comment

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

Nice, a couple small things but otherwise looks good to me!

I'm leaving this un-approved for now however because I believe (based on earlier conversation with @njooma) that we don't want to merge this into main but rather into a separate release branch 3 or 4 cycles from now, so that we give people plenty of time to prepare for the breaking change.

NotSupportedError: Raised if given an image that is not of MIME type `image/vnd.viam.dep`.
"""
if image.mime_type not in [CameraMimeType.JPEG, CameraMimeType.PNG, CameraMimeType.VIAM_RGBA]:
NotSupportedError("Type must be `image/jpeg`, `image/png`, or `image/vnd.viam.rgba` to use get_image_dimensions()")
Copy link
Member

Choose a reason for hiding this comment

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

do we need to raise this error?

Copy link
Member Author

Choose a reason for hiding this comment

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

I wrote this whole paragraph why I thought having this error was necessary. And then realized you quite literally meant "hey, you forgot a raise keyword". 🤦

Copy link
Member Author

Choose a reason for hiding this comment

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

Thank you for reminding me I should be writing tests to make sure an error is raised if someone tries to call this function with the wrong mime type!

return depth_arr_2d


def get_image_dimensions(image: ViamImage) -> None:
Copy link
Member

Choose a reason for hiding this comment

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

(minor) When I see a method named get_foo I typically expect that it will return a foo, I think it's a bit strange that this returns None. But it'd be weird to name it set_image_dimensions too because it's taking no image_dimension arguments! Can we rename to determine_image_dimensions or something like that?

Copy link
Member Author

Choose a reason for hiding this comment

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

Yes, this is a very good point!

@purplenicole730 purplenicole730 changed the base branch from main to rc-0.21.0 April 11, 2024 13:50
Copy link
Member

@njooma njooma left a comment

Choose a reason for hiding this comment

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

Great work so far! This is looking good

Comment on lines 23 to 27
def get_image_from_response(data: bytes, response_mime_type: str, request_mime_type: str) -> ViamImage:
if not request_mime_type:
request_mime_type = response_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)
mime_type = CameraMimeType.from_string(request_mime_type)
return ViamImage(data, mime_type)
Copy link
Member

Choose a reason for hiding this comment

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

This function is not needed anymore and is actually incorrect. We should just move this to the get_image function:

request = ...
response: GetImageResponse = await self.client.GetImage(request, timeout=timeout)
return ViamImage(response.image, response.mime_type)

Comment on lines 45 to 51
if not request.mime_type:
if camera.name not in self._camera_mime_types:
self._camera_mime_types[camera.name] = CameraMimeType.JPEG

request.mime_type = self._camera_mime_types[camera.name]

response = GetImageResponse(mime_type=request.mime_type, image=image.data)
Copy link
Member

Choose a reason for hiding this comment

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

We actually don't need any of this anymore -- this was useful when we were encoding images. But because we're not doing that anymore, just sending raw bytes back, we should actually use the mime type returned from the camera (image.mime_type) instead of request.mime_type

finally:
image.close()
response = HttpBody(data=img, content_type=image.mime_type if isinstance(image, RawImage) else mimetype) # type: ignore
response = HttpBody(data=image.data, content_type=image.mime_type if isinstance(image, ViamImage) else mimetype) # type: ignore
Copy link
Member

Choose a reason for hiding this comment

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

This long check isn't needed anymore since image CAN ONLY ever be ViamImage. We can also remove the try/except block that attempts to get the mimetype.

Returns:
List[List[int]]: The standard representation of the image.
"""
if image.mime_type != CameraMimeType.VIAM_RAW_DEPTH.value:
Copy link
Member

Choose a reason for hiding this comment

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

I don't think you need the .value at the end of CameraMimeType.VIAM_RAW_DEPTH.value (but I'm not 100% sure)

@@ -159,10 +56,10 @@ class ViamImage:
Provides the raw data and the mime type, as well as lazily loading and caching the PIL.Image representation.
Copy link
Member

Choose a reason for hiding this comment

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

This comment needs to be updated to remove the PIL reference

Comment on lines 85 to 87
def width(self) -> Union[int, None]:
"""The width of the image"""
return self._width
Copy link
Member

Choose a reason for hiding this comment

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

Continuing from my earlier comment around keeping the _image and _image_decoded properties, the width and height getters should now be something like:

# First, check if we already have a value
if self._width is not None:
    return self._width
# If we don't have a value, check to see if we have already tried to decode the image
# If we have not, then try to decode the image now
if not self._image_decoded:
    try:
        self._image = Image.open(BytesIO(self.data), formats=LIBRARY_SUPPORTED_FORMATS)
    except UnidentifiedImageError:
        self._image = None
    self._image_decoded = True
# If we have decoded the image and the image is not none, then set the width so we don't have to do this again
if self._image_decoded and self._image is not None:
    self._width = self._image.width
return self._width

Comment on lines 79 to 81
# Get image dimensions
determine_image_dimensions(img)

Copy link
Member

Choose a reason for hiding this comment

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

Remove this -- the user should never be calling this

Comment on lines 151 to 153
# Get image dimensions
determine_image_dimensions(img)

Copy link
Member

Choose a reason for hiding this comment

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

Same as above -- the user should never be calling this

img.close()
assert img._mime_type == CameraMimeType.PNG
pil_img = viam_to_pil_image(img)
assert pil_img.tobytes() == i.tobytes()

def test_mime_type_update(self):
Copy link
Member

Choose a reason for hiding this comment

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

We shouldn't allow users to update mime types anymore

tests/test_media.py Outdated Show resolved Hide resolved
Copy link
Member

@njooma njooma left a comment

Choose a reason for hiding this comment

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

Minor update to the comments, but otherwise this is great

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`
NOTE: If the mime type is ``image/vnd.viam.dep`` you can use :func:`viam.media.utils.bytes_to_depth_array`
Copy link
Member

Choose a reason for hiding this comment

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

Revert this comment change, as bytes_to_depth_array is on ViamImage

@purplenicole730 purplenicole730 merged commit 68dd7f9 into viamrobotics:rc-0.21.0 Apr 17, 2024
11 checks passed
@purplenicole730 purplenicole730 deleted the RSDK-4089-use-ViamImage branch April 17, 2024 16:44
njooma pushed a commit that referenced this pull request Apr 18, 2024
njooma pushed a commit to njooma/viam-python-sdk that referenced this pull request Apr 18, 2024
njooma pushed a commit to njooma/viam-python-sdk that referenced this pull request Apr 30, 2024
njooma pushed a commit that referenced this pull request May 14, 2024
njooma pushed a commit that referenced this pull request May 20, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants