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

New planar pixel formats: Y_U_V24/Y_U_V16/Y_U_V12 - _LimitedRange/FullRange #7666

Merged
merged 16 commits into from
Oct 10, 2024

Conversation

Wumpf
Copy link
Member

@Wumpf Wumpf commented Oct 9, 2024

What

Adds a whole bunch of new pixel formats with limited & full range YUV variants. All of these are needed by our AV1 decoder (branch is a misnomer though, this is coming in another pr!)


Naming & categorizing these formats is quite the struggle just as expected:
If limited to YUV variants, the overall picutre becomes sharper (ironic, eh) and it's possible to specify the combinatorics in a deceptively simple pattern:

  • planarity & downsample ratio
  • limited/full value range
  • bit depth (we only do 8 bit right now)
  • color primaries

That's what is happening in re_renderer's YUV converter now and is very useful for writing the conversions themselves!

However (!!!), in the wild names make these properties often convoluted with each other, making the categorization a lot harder. E.g. NV12 is almost always (afaik just almost) implying LimitedRange YUV (which is generally a common default unless you're dealing with jpeg derived data [...]).
This, and the fact that we already set out with PixelFormat for a flat list of names, strongly compelled me to continue the current naming scheme (and Ocean's numbering as far as possible) in the user facing API. Worth revisiting in the future.

The primary motivation to expose these formats right away was to be able to easily test these variants. However, I refrained from throwing color primaries into the mix here because I think they should in fact be separately defined, causing a bigger expansion of the APIs. The docs are intentionally kept vague on color space for the moment (trying hard to not further get entangled here).
As a result, our internal processing is now fairly explicit on the matter, but the input interpretation is rather vague.

image (screenshot of the release checklist which is part of this pr (moved from a previous test). I manually tested the color inspection feature against a reference color picker - slight rounding errors occasionally, but they are hard to avoid since GPU/CPU don't always do the exact same thing :/)

https://rerun.io/viewer/pr/7666?url=https%3A%2F%2Fstatic.rerun.io%2Frrd%2F0.19.0%2Fchroma_4688549ca3c20018fa2fb658b56482b955817b6a.rrd

Checklist

  • chrome/firefox windows
  • chrome/firefox/safari mac
  • chrome/firefox linux
  • I have read and agree to Contributor Guide and the Code of Conduct
  • I've included a screenshot or gif (if applicable)
  • I have tested the web demo (if applicable):
  • The PR title and labels are set such as to maximize their usefulness for the next release's CHANGELOG
  • If applicable, add a new check to the release checklist!
  • If have noted any breaking changes to the log API in CHANGELOG.md and the migration guide

To run all checks from main, comment on the PR with @rerun-bot full-check.

@Wumpf Wumpf added 🔺 re_renderer affects re_renderer itself include in changelog 🪵 Log & send APIs Affects the user-facing API for all languages labels Oct 9, 2024
@Wumpf Wumpf changed the title New pixel formats: Y_U_V24/Y_U_V16/Y_U_V12_LimitedRange/FullRange New pixel formats: Y_U_V24/Y_U_V16/Y_U_V12 - _LimitedRange/FullRange Oct 9, 2024
@Wumpf Wumpf changed the title New pixel formats: Y_U_V24/Y_U_V16/Y_U_V12 - _LimitedRange/FullRange New planar pixel formats: Y_U_V24/Y_U_V16/Y_U_V12 - _LimitedRange/FullRange Oct 9, 2024
Copy link

github-actions bot commented Oct 9, 2024

Deployed docs

Commit Link
cfe85fd https://landing-963lg4x1c-rerun.vercel.app/docs

@Wumpf Wumpf force-pushed the andreas/av1-chroma-upsampling-gpu branch from 92cfcda to a25cc74 Compare October 9, 2024 17:09
/// This uses limited range YUV, i.e. Y is expected to be within [16, 235] and U/V within [16, 240].
///
/// First comes entire image in Y in one plane, followed by the U and V planes.
Y_U_V24_LimitedRange = 39,
Copy link
Member Author

Choose a reason for hiding this comment

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

the ordering in respect to the numbers in here is rather dubious
I tried to order this logically rather than by enum numbers (which are derived from Ocean). But that makes it hard for the codes beyond Ocean :/

@@ -31,6 +31,7 @@ _deps
# Python build artifacts:
__pycache__
*.pyc
*.pyd
Copy link
Member Author

Choose a reason for hiding this comment

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

not sure why this didn't come up before, but while working on this I accidentally pushed a .pyd file which wasn't on the ignore list. better safe than sorry (again), so I added this here

@Wumpf Wumpf force-pushed the andreas/av1-chroma-upsampling-gpu branch from f276ba0 to f296688 Compare October 10, 2024 09:40
@Wumpf Wumpf marked this pull request as ready for review October 10, 2024 09:51
@emilk emilk self-requested a review October 10, 2024 09:58
Copy link
Member

@emilk emilk left a comment

Choose a reason for hiding this comment

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

Niiiice!

///
/// This uses entire range YUV, i.e. Y is expected to be within [0, 255].
/// (as opposed to "limited range" YUV as used e.g. in NV12).
Y8_FullRange = 30,
Copy link
Member

Choose a reason for hiding this comment

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

At some point it would be nice if LimitedRange/FullRange was its own component. Maybe.

Copy link
Member Author

Choose a reason for hiding this comment

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

yeah... maybe. See PR description 😄

Comment on lines 77 to 78
// TODO(andreas): This shouldn't be ColorModel::RGB, but our YUV converter can't do anything else right now.
Self::Y8_LimitedRange | Self::Y8_FullRange => ColorModel::RGB,
Copy link
Member

Choose a reason for hiding this comment

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

Can you elaborate on this?

You mean the GPU always outputs RGB, so we consider Y to be a very stupid way of compressing RGB?

What is color_model used for? Is there anything that will break due to this "lie"?

Copy link
Member Author

Choose a reason for hiding this comment

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

(will add to comment)
GPU doesn't have to always output RGB, but having it sometimes output R(8) specifically for the YUV converter requires me to do more bookkeeping (needs a new renderpipeline and I expect other ripples) which I just don't want to do right now.
We do use this property mostly for things in the hover logic afaik.

very stupid way of compressing RGB

hm well. no. That's just because our gpu conversion pipeline is stupid atm. But it still doesn't take up extra CPU memory and GPU textures are only held as long as they are in use (image cache for instance doesn't cache gpu transfer step!)

Copy link
Member Author

@Wumpf Wumpf Oct 10, 2024

Choose a reason for hiding this comment

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

a fun way to see this short-lived-cacheing in action is to observe what happens when you change the shader code in yuv_converter.wgsl in a debug build:
It will reload the shader which has no effect unless you show and hide the respective space views at which point the shader runs again.

(was very very very useful for developing that shader! I ended up having a separate dummy-recording that I switched to and back :D)

Comment on lines +163 to +167
/// Returns true if the format is a YUV format using
/// limited range YUV.
///
/// I.e. for 8bit data, Y is valid in [16, 235] and U/V [16, 240], rather than 0-255.
pub fn is_limited_yuv_range(&self) -> bool {
Copy link
Member

Choose a reason for hiding this comment

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

I think this would be nicer as fn yuv_range(&self) -> Option<YuvRange> so we can return None for non-yuv formats in the future. Also, enums is nicer than bools.

Copy link
Member Author

Choose a reason for hiding this comment

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

yeah but I didn't want to introduce this enum a third time (1x each in re_renderer, re_types and re_video -.-)

Copy link
Member

Choose a reason for hiding this comment

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

If it was a good idea in the other places, why isn't it a good idea here?

Comment on lines +124 to +125
Some(_) => {
// We do the lazy thing here since we convert everything to RGB8 right now anyways.
Copy link
Member

Choose a reason for hiding this comment

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

Maybe it would be smart to prepare ourself for HDR though?

Suggested change
Some(_) => {
// We do the lazy thing here since we convert everything to RGB8 right now anyways.
Some(pixel_format) => {
let max_value = pixel_format.max_component_value();

Copy link
Member Author

Choose a reason for hiding this comment

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

Unfortunately this isn't about the max component value of the pixel format but rather what we show which right now is a bit different. max_component_value would be an interesting one also for limited range YUV stuff 🤔
I'm leaving an additional note on this for the next person!

Comment on lines 16 to 27
def bgra2y_u_v24(bgra: Any, full_range: bool) -> np.ndarray:
if full_range:
yvu = cv2.cvtColor(bgra, cv2.COLOR_BGR2YCrCb)
y, v, u = cv2.split(yvu)
else:
yuv = cv2.cvtColor(bgra, cv2.COLOR_BGR2YUV)
y, u, v = cv2.split(yuv)
y = np.array(y).flatten()
u = np.array(u).flatten()
v = np.array(v).flatten()
yuv24 = np.concatenate((y, u, v))
return yuv24.astype(np.uint8)
Copy link
Member

Choose a reason for hiding this comment

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

is there no python library for this? :/

Copy link
Member Author

Choose a reason for hiding this comment

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

dunno, surely but looking and comparing this would have taken me more time than just writing it out myself

scripts/ci/main.py Outdated Show resolved Hide resolved
@Wumpf
Copy link
Member Author

Wumpf commented Oct 10, 2024

having an eye on the missing testing, but will delay a bit here in order to move on to the next thing!

@Wumpf Wumpf merged commit 06cfa9e into main Oct 10, 2024
39 of 40 checks passed
@Wumpf Wumpf deleted the andreas/av1-chroma-upsampling-gpu branch October 10, 2024 15:00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
include in changelog 🪵 Log & send APIs Affects the user-facing API for all languages 🔺 re_renderer affects re_renderer itself
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants