Skip to content

[Video] Fix convert_to_rgb channel slicing and alpha blending for RGBA videos - #48053

Merged
zucchini-nlp merged 2 commits into
huggingface:mainfrom
Ultron09:fix/video-convert-to-rgb-rgba-shape
Aug 19, 2026
Merged

[Video] Fix convert_to_rgb channel slicing and alpha blending for RGBA videos#48053
zucchini-nlp merged 2 commits into
huggingface:mainfrom
Ultron09:fix/video-convert-to-rgb-rgba-shape

Conversation

@Ultron09

@Ultron09 Ultron09 commented Aug 18, 2026

Copy link
Copy Markdown
Contributor

CI

What does this PR do?

Fixes #48051

Problem

  1. In src/transformers/video_utils.py::convert_to_rgb, infer_channel_dimension_format(video) was called without num_channels=(1, 3, 4), which raised ValueError: Unable to infer channel dimension format for 4-channel RGBA videos.
  2. In the alpha-blending formula of video_utils.py::convert_to_rgb, video[..., 3, :, :] (the 1-channel alpha tensor) was mistakenly multiplied as the foreground colors instead of video[..., :3, :, :] (the 3 RGB channels). This caused incorrect shapes (e.g. (2, 2, 1, 1) instead of (2, 3, 1, 1)) and corrupted pixel values.
  3. In both video_utils.py::convert_to_rgb and video_processing_utils.py::BaseVideoProcessor.convert_to_rgb, fully opaque RGBA inputs returned video with 4 channels rather than stripping the alpha channel to return 3 RGB channels (video[..., :3, :, :]).

Solution

  • Updated infer_channel_dimension_format(video, num_channels=(1, 3, 4)) in video_utils.py::convert_to_rgb.
  • Fixed the alpha blending formula to use video[..., :3, :, :] as foreground.
  • Return video[..., :3, :, :] for opaque 4-channel RGBA inputs across both numpy (video_utils.py) and torch (video_processing_utils.py) implementations.
  • Added unit tests in tests/utils/test_video_utils.py covering transparent and opaque RGBA numpy and torch inputs.

Before submitting

  • This PR fixes a typo or improves the docs (you can dismiss the other checks if that's the case).
  • Did you make sure to update the documentation with your changes?
  • Did you write any new necessary tests?

@Ultron09

Copy link
Copy Markdown
Contributor Author

Hi @molbap @guarin, CI is fully green (16/16 jobs, 0 failures). Ready for review when you have a moment! Fixes #48051.

@guarin guarin left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Thanks for the PR! Looks like a legit issue, cc @zucchini-nlp

Comment thread tests/utils/test_video_utils.py Outdated
Comment on lines 215 to 220
rgb_video = video_processor.convert_to_rgb(torch.cat([video, video[:, :1]], dim=1))
# Test torch tensor with alpha channel
rgba_torch = torch.cat([video, torch.full_like(video[:, :1], 128)], dim=1)
rgb_video = video_processor.convert_to_rgb(rgba_torch)
self.assertEqual(rgb_video.shape, (8, 3, 20, 20))

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I think we can leave these lines unchanged from the original. The changed test doesn't really test anything different no?

Comment thread tests/utils/test_video_utils.py Outdated
],
dtype=np.uint8,
)
rgb_np = convert_to_rgb(video_np_transparent, input_data_format="channels_last")

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Please call video_processor.convert_to_rgb here and pass the enum as input_data_format. Let's call the output also rgb_video as further above.

Comment thread tests/utils/test_video_utils.py Outdated
Comment on lines +235 to +243
video_np_opaque = np.array(
[
[[[255, 0, 0, 255]]],
[[[0, 255, 0, 255]]],
],
dtype=np.uint8,
)
rgb_np_opaque = convert_to_rgb(video_np_opaque, input_data_format="channels_last")
self.assertEqual(rgb_np_opaque.shape, (2, 3, 1, 1))

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

same here

@HuggingFaceDocBuilderDev

Copy link
Copy Markdown

The docs for this PR live here. All of your documentation changes will be reflected on that endpoint. The docs are available until 30 days after the last update.

@carlszk

carlszk commented Aug 18, 2026

Copy link
Copy Markdown
Contributor

Thanks for the fix! I'm the reporter of #48051 — confirmed the new tests cover my original repro, and the output shape/pixel values match what I expected.

One small gap: the tests cover alpha=128 (semi-transparent) and alpha=255 (opaque), but not alpha=0 (fully transparent). With alpha=0 the blended result should be pure white [255, 255, 255] — might be worth adding a case to lock in the behavior at the other end of the range.

Also noticed the torch RGBA test only asserts shape, while the numpy one uses assert_allclose for pixel values. Since the early-return logic in BaseVideoProcessor.convert_to_rgb was also changed here, a value-level assertion on the torch side would give it the same coverage.

@zucchini-nlp zucchini-nlp left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Agreed with Guarin on tests that we need to check video_processor.convert_rbg as well. TBH i need to make video-processor call the existing helper, and do some general maintenance
For now we can keep as is and only fix the bug

Comment on lines -126 to +131
if video.shape[-3] == 3 or not (video[..., 3, :, :] < 255).any():
if video.shape[-3] == 3:
return video

if not (video[..., 3, :, :] < 255).any():
return video[..., :3, :, :]

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

nit:

if video.shape[-3] == 3 or not (video[..., 3, :, :] < 255).any():
    return video[..., :3, :, :]

Comment thread src/transformers/video_processing_utils.py
@Ultron09

Copy link
Copy Markdown
Contributor Author

Thanks @guarin, @zucchini-nlp, and @carlszk for the review and suggestions!

Pushed an update addressing all points:

  1. Simplified early-return condition in BaseVideoProcessor.convert_to_rgb to if video.shape[-3] == 3 or not (video[..., 3, :, :] < 255).any(): return video[..., :3, :, :].
  2. Switched string format to ChannelDimension.LAST enum in convert_to_rgb.
  3. Added tests for fully transparent �lpha=0 ([255, 0, 0, 0] blending to [255, 255, 255]) across both torch and numpy implementations.
  4. Added value-level assertions with orch.testing.assert_close for �ideo_processor.convert_to_rgb covering �lpha=0, �lpha=128, and �lpha=255.

@github-actions

Copy link
Copy Markdown
Contributor

CI recap

Dashboard: View test results in Grafana
Latest run: 32114768667:2
Result: success | Jobs: 16 | Tests: 182,071 | Failures: 0 | Duration: 13h 11m

@zucchini-nlp zucchini-nlp left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

LGMT

@zucchini-nlp
zucchini-nlp added this pull request to the merge queue Aug 19, 2026
Merged via the queue into huggingface:main with commit e7e8b7f Aug 19, 2026
114 checks passed
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.

convert_to_rgb gives wrong output shape for RGBA numpy videos

5 participants