-
Notifications
You must be signed in to change notification settings - Fork 413
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
Add back rr.ImageEncoded
for backwards compatibility
#7096
Merged
Merged
Changes from 3 commits
Commits
Show all changes
5 commits
Select commit
Hold shift + click to select a range
77f50ba
Make it a function that returns either an `Image` or an `EncodedImage`
emilk ba5f3aa
Add test of `rr.ImageEncoded`
emilk a41e886
Update changelog and migration guide
emilk fe4d89b
Merge branch 'main' into emilk/image_encoded.py
jleibs 35375ed
Fix ImageEncoded tests
jleibs File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,227 @@ | ||
""" | ||
Deprecated helpers. | ||
|
||
Use `Image` and `EncodedImage` instead. | ||
""" | ||
|
||
from __future__ import annotations | ||
|
||
import io | ||
import pathlib | ||
import warnings | ||
from typing import IO | ||
|
||
from .archetypes import EncodedImage, Image | ||
from .datatypes import Float32Like | ||
|
||
|
||
class ImageFormat: | ||
"""⚠️ DEPRECATED ⚠️ Image file format.""" | ||
|
||
name: str | ||
|
||
BMP: ImageFormat | ||
""" | ||
BMP file format. | ||
""" | ||
|
||
GIF: ImageFormat | ||
""" | ||
JPEG/JPG file format. | ||
""" | ||
|
||
JPEG: ImageFormat | ||
""" | ||
JPEG/JPG file format. | ||
""" | ||
|
||
PNG: ImageFormat | ||
""" | ||
PNG file format. | ||
""" | ||
|
||
TIFF: ImageFormat | ||
""" | ||
TIFF file format. | ||
""" | ||
|
||
NV12: type[NV12] | ||
""" | ||
Raw NV12 encoded image. | ||
|
||
The type comes with a `size_hint` attribute, a tuple of (height, width) | ||
which has to be specified specifying in order to set the RGB size of the image. | ||
""" | ||
|
||
YUY2: type[YUY2] | ||
""" | ||
Raw YUY2 encoded image. | ||
|
||
YUY2 is a YUV422 encoding with bytes ordered as `yuyv`. | ||
|
||
The type comes with a `size_hint` attribute, a tuple of (height, width) | ||
which has to be specified specifying in order to set the RGB size of the image. | ||
""" | ||
|
||
def __init__(self, name: str): | ||
self.name = name | ||
|
||
def __str__(self) -> str: | ||
return self.name | ||
|
||
|
||
class NV12(ImageFormat): | ||
"""⚠️ DEPRECATED ⚠️ NV12 format.""" | ||
|
||
name = "NV12" | ||
size_hint: tuple[int, int] | ||
|
||
def __init__(self, size_hint: tuple[int, int]) -> None: | ||
""" | ||
An NV12 encoded image. | ||
|
||
Parameters | ||
---------- | ||
size_hint: | ||
A tuple of (height, width), specifying the RGB size of the image | ||
|
||
""" | ||
self.size_hint = size_hint | ||
|
||
|
||
class YUY2(ImageFormat): | ||
"""⚠️ DEPRECATED ⚠️ YUY2 format.""" | ||
|
||
name = "YUY2" | ||
size_hint: tuple[int, int] | ||
|
||
def __init__(self, size_hint: tuple[int, int]) -> None: | ||
""" | ||
An YUY2 encoded image. | ||
|
||
YUY2 is a YUV422 encoding with bytes ordered as `yuyv`. | ||
|
||
Parameters | ||
---------- | ||
size_hint: | ||
A tuple of (height, width), specifying the RGB size of the image | ||
|
||
""" | ||
self.size_hint = size_hint | ||
|
||
|
||
# Assign the variants | ||
# This allows for rust like enums, for example: | ||
# ImageFormat.NV12(width=1920, height=1080) | ||
# isinstance(ImageFormat.NV12, ImageFormat) == True and isinstance(ImageFormat.NV12, NV12) == True | ||
ImageFormat.BMP = ImageFormat("BMP") | ||
ImageFormat.GIF = ImageFormat("GIF") | ||
ImageFormat.JPEG = ImageFormat("JPEG") | ||
ImageFormat.PNG = ImageFormat("PNG") | ||
ImageFormat.TIFF = ImageFormat("TIFF") | ||
ImageFormat.NV12 = NV12 | ||
ImageFormat.YUY2 = YUY2 | ||
|
||
|
||
def ImageEncoded( | ||
*, | ||
path: str | pathlib.Path | None = None, | ||
contents: bytes | IO[bytes] | None = None, | ||
format: ImageFormat | None = None, | ||
draw_order: Float32Like | None = None, | ||
) -> Image | EncodedImage: | ||
""" | ||
⚠️ DEPRECATED ⚠️ - Use [`Image`][rerun.archetypes.Image] (NV12, YUYV, …) and [`EncodedImage`][rerun.archetypes.EncodedImage] (PNG, JPEG, …) instead. | ||
|
||
A monochrome or color image encoded with a common format (PNG, JPEG, etc.). | ||
|
||
The encoded image can be loaded from either a file using its `path` or | ||
provided directly via `contents`. | ||
|
||
Parameters | ||
---------- | ||
path: | ||
A path to a file stored on the local filesystem. Mutually | ||
exclusive with `contents`. | ||
contents: | ||
The contents of the file. Can be a BufferedReader, BytesIO, or | ||
bytes. Mutually exclusive with `path`. | ||
format: | ||
The format of the image file or image encoding. | ||
If not provided, it will be inferred from the file extension if a path is specified. | ||
Note that encodings like NV12 and YUY2 can not be inferred from the file extension. | ||
draw_order: | ||
An optional floating point value that specifies the 2D drawing | ||
order. Objects with higher values are drawn on top of those with | ||
lower values. | ||
|
||
""" | ||
|
||
warnings.warn( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This warning makes the unit tests fail. I have no idea how to fix that. |
||
message=( | ||
"`ImageEncoded` is deprecated. Use `Image` (for NV12 and YUY2) or `EncodedImage` (for PNG, JPEG, …) instead." | ||
), | ||
category=DeprecationWarning, | ||
) | ||
|
||
if (path is None) == (contents is None): | ||
raise ValueError("Must provide exactly one of 'path' or 'contents'") | ||
|
||
if format is not None: | ||
if isinstance(format, NV12) or isinstance(format, YUY2): | ||
buffer: IO[bytes] | None | ||
if path is not None: | ||
buffer = io.BytesIO(pathlib.Path(path).read_bytes()) | ||
elif isinstance(contents, bytes): | ||
buffer = io.BytesIO(contents) | ||
else: | ||
assert ( | ||
# For the type-checker - we've already ensured that either `path` or `contents` must be set | ||
contents is not None | ||
) | ||
buffer = contents | ||
|
||
contentx_bytes = buffer.read() | ||
|
||
if isinstance(format, NV12): | ||
return Image( | ||
bytes=contentx_bytes, | ||
width=format.size_hint[1], | ||
height=format.size_hint[0], | ||
pixel_format="NV12", | ||
draw_order=draw_order, | ||
) | ||
elif isinstance(format, YUY2): | ||
return Image( | ||
bytes=contentx_bytes, | ||
width=format.size_hint[1], | ||
height=format.size_hint[0], | ||
pixel_format="YUY2", | ||
draw_order=draw_order, | ||
) | ||
|
||
media_type = None | ||
if format is not None: | ||
if str(format) == "BMP": | ||
media_type = "image/bmp" | ||
elif str(format) == "GIF": | ||
media_type = "image/gif" | ||
elif str(format) == "JPEG": | ||
media_type = "image/jpeg" | ||
elif str(format) == "PNG": | ||
media_type = "image/png" | ||
elif str(format) == "TIFF": | ||
media_type = "image/tiff" | ||
|
||
if path is not None: | ||
return EncodedImage( | ||
path=path, | ||
media_type=media_type, | ||
draw_order=draw_order, | ||
) | ||
else: | ||
return EncodedImage( | ||
contents=contents, | ||
media_type=media_type, | ||
draw_order=draw_order, | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,99 @@ | ||
"""Testing of the deprecated `ImageEncoded` helper.""" | ||
|
||
from __future__ import annotations | ||
|
||
import io | ||
import tempfile | ||
|
||
import cv2 | ||
import numpy as np | ||
import rerun as rr # pip install rerun-sdk | ||
from PIL import Image | ||
|
||
|
||
def test_image_encoded_png() -> None: | ||
_, file_path = tempfile.mkstemp(suffix=".png") | ||
|
||
image = Image.new("RGBA", (300, 200), color=(0, 0, 0, 0)) | ||
image.save(file_path) | ||
|
||
img = rr.ImageEncoded(path=file_path) | ||
|
||
assert img.media_type == "image/png" | ||
|
||
|
||
def test_image_encoded_jpg() -> None: | ||
_, file_path = tempfile.mkstemp(suffix=".jpg") | ||
|
||
image = Image.new("RGB", (300, 200), color=(0, 0, 0)) | ||
image.save(file_path) | ||
|
||
img = rr.ImageEncoded(path=file_path) | ||
|
||
assert img.media_type == "image/jpeg" | ||
|
||
|
||
def test_image_encoded_mono_jpg() -> None: | ||
_, file_path = tempfile.mkstemp(suffix=".jpg") | ||
|
||
image = Image.new("L", (300, 200), color=0) | ||
image.save(file_path) | ||
|
||
img = rr.ImageEncoded(path=file_path) | ||
|
||
assert img.media_type == "image/jpeg" | ||
|
||
|
||
def test_image_encoded_jpg_from_bytes() -> None: | ||
bin = io.BytesIO() | ||
|
||
image = Image.new("RGB", (300, 200), color=(0, 0, 0)) | ||
image.save(bin, format="jpeg") | ||
|
||
img = rr.ImageEncoded(contents=bin) | ||
|
||
assert img.media_type == "image/jpeg" | ||
|
||
bin.seek(0) | ||
img = rr.ImageEncoded(contents=bin.read()) | ||
|
||
assert img.media_type == "image/jpeg" | ||
|
||
|
||
def test_image_encoded_mono_jpg_from_bytes() -> None: | ||
bin = io.BytesIO() | ||
|
||
image = Image.new("L", (300, 200), color=0) | ||
image.save(bin, format="jpeg") | ||
|
||
img = rr.ImageEncoded(contents=bin) | ||
|
||
assert img.media_type == "image/jpeg" | ||
|
||
bin.seek(0) | ||
img = rr.ImageEncoded(contents=bin.read()) | ||
|
||
assert img.media_type == "image/jpeg" | ||
|
||
|
||
def test_image_encoded_nv12() -> None: | ||
def bgr2nv12(bgr: cv2.typing.MatLike) -> cv2.typing.MatLike: | ||
yuv = cv2.cvtColor(bgr, cv2.COLOR_BGR2YUV_I420) | ||
uv_row_cnt = yuv.shape[0] // 3 | ||
uv_plane = np.transpose(yuv[uv_row_cnt * 2 :].reshape(2, -1), [1, 0]) | ||
yuv[uv_row_cnt * 2 :] = uv_plane.reshape(uv_row_cnt, -1) | ||
return yuv | ||
|
||
img_bgr = np.random.randint(0, 255, (480, 640, 3), dtype=np.uint8) | ||
|
||
img = ( | ||
rr.ImageEncoded( | ||
contents=bytes(bgr2nv12(img_bgr)), | ||
format=rr.ImageFormat.NV12((480, 640)), | ||
draw_order=42, | ||
), | ||
) | ||
|
||
assert img.resolution == rr.Resolution2D(640, 480) | ||
assert img.pixel_format == rr.PixelFormat.NV12 | ||
assert img.draw_order == 42 |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ImageEncoded
used to be aclass
, but is now a function so that it can return either anImage
or anEncodedImage
. Maybe a Python guru has a better idea?