Skip to content

Fix void segmentation map label reduction#45423

Closed
Yanis01682 wants to merge 4 commits intohuggingface:mainfrom
Yanis01682:fix/issue-30064-void-segmentation-maps
Closed

Fix void segmentation map label reduction#45423
Yanis01682 wants to merge 4 commits intohuggingface:mainfrom
Yanis01682:fix/issue-30064-void-segmentation-maps

Conversation

@Yanis01682
Copy link
Copy Markdown

Summary

  • preserve existing void/ignore label 255 when
    educe_label is applied
  • only reduce non-ignore class ids by 1 for both torch and PIL image processors
  • add a SegFormer regression test covering

@github-actions
Copy link
Copy Markdown
Contributor

[For maintainers] Suggested jobs to run (before merge)

run-slow: beit, chmv2, dpt, mobilevit, segformer

@Rocketknight1
Copy link
Copy Markdown
Member

cc @molbap @NielsRogge maybe?

@notrudyyy
Copy link
Copy Markdown

Hi @Yanis01682! Could you help me understand what benefits your change to the logic brings?

Unless I am missing something, although the original logic isn't explicit about the fact that it doesn't touch existing 255 labels, it works fine.

If the concern is performance, your proposed method is 2x slower than the original, primarily due to the clone/copy used. If removed, it is still slightly slower. (I could be doing this benchmark wrong, so please correct me if so)

Old code vs new code:

import torch
import timeit

image_base = torch.tensor([0, 1, 255])[torch.randint(0, 3, (32, 32, 3))]
image_base = image_base.to(dtype=torch.uint8)

N = 10_000
images = [image_base.clone() for _ in range(N)]
idx = 0

def method1():
    global idx
    image = images[idx]
    idx += 1
    image[image == 0] = 255
    image -= 1
    image[image == 254] = 255

time1 = timeit.timeit(method1, number=N)

method2_stmt = """
image = image_base.clone()
ignore_mask = (image == 0) | (image == 255)
image[ignore_mask] = 255
image[~ignore_mask] -= 1
"""

time2 = timeit.timeit(
    stmt=method2_stmt,
    globals={"image_base": image_base},
    number=N
)

print(f"Original: {time1:.6f} seconds")
print(f"New: {time2:.6f} seconds")

# Original: 0.672214 seconds
# New: 1.805491 seconds

Old code vs modified new code

import torch
import timeit

image_base = torch.tensor([0, 1, 255])[torch.randint(0, 3, (32, 32, 3))]
image_base = image_base.to(dtype=torch.uint8)

N = 10_000
images = [image_base.clone() for _ in range(N)]
idx = 0

def method1():
    global idx
    image = images[idx]
    idx += 1
    image[image == 0] = 255
    image -= 1
    image[image == 254] = 255

time1 = timeit.timeit(method1, number=N)

images = [image_base.clone() for _ in range(N)]
idx = 0

def method2():
    global idx
    image = images[idx]
    ignore_mask = (image == 0) | (image == 255)
    image[ignore_mask] = 255
    image[~ignore_mask] -= 1

time2 = timeit.timeit(method2, number=N)

print(f"Original: {time1:.6f} seconds")
print(f"New (clone removed): {time2:.6f} seconds")

# Original: 0.705085 seconds
# New (clone removed): 0.777739 seconds

@Yanis01682
Copy link
Copy Markdown
Author

Thanks for taking a close look here. I revisited the original logic and you are right: it already preserves existing 255 labels, so my change does not provide a behavioral improvement there. I also re-checked issue #30064, and the original failure mode was in the Mask2Former void-mask path rather than this SegFormer/related reduce_label logic, and that path appears to have been addressed on main already. Since this PR is not fixing a real remaining issue and adds a slower implementation, I am going to close it. Thanks again for the careful review.

@Yanis01682 Yanis01682 closed this Apr 29, 2026
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