Skip to content

fix: split large channel masks to handle cv2.resize 512 limitations#21947

Merged
Laughing-q merged 3 commits into
ultralytics:mainfrom
ShuaiLYU:fix-cv2-resize-when-channel-large-than-512
Sep 5, 2025
Merged

fix: split large channel masks to handle cv2.resize 512 limitations#21947
Laughing-q merged 3 commits into
ultralytics:mainfrom
ShuaiLYU:fix-cv2-resize-when-channel-large-than-512

Conversation

@ShuaiLYU

@ShuaiLYU ShuaiLYU commented Sep 4, 2025

Copy link
Copy Markdown
Contributor
  • Add batch processing for masks with >512 channels to avoid cv2.resize limitation
  • Process masks in batches of 512 channels and concatenate results
  • Fixes Error when max_det>512 causes wong result returned

🛠️ PR Summary

Made with ❤️ by Ultralytics Actions

🌟 Summary

Fixes a bug in mask resizing by handling OpenCV’s 512-channel limit, ensuring large multi-channel masks resize reliably without errors. ✅

📊 Key Changes

  • Updated scale_image in ultralytics/utils/ops.py to:
    • Split mask arrays into chunks of ≤512 channels.
    • Resize each chunk with cv2.resize and concatenate the results.
  • Added an inline comment referencing the limitation and related discussion in the PR Handle cv2.resize 512-channel limit.

🎯 Purpose & Impact

  • Prevents crashes or errors when resizing masks with many channels (e.g., large-class segmentation outputs). 🛡️
  • Improves robustness for advanced segmentation workflows and large models. 🚀
  • No API changes; behavior is transparent to users and backwards compatible. 🔄
  • Slight, negligible performance overhead from chunking and concatenation. ⏱️

- Add batch processing for masks with >512 channels to avoid cv2.resize limitation
- Process masks in batches of 512 channels and concatenate results
- Fixes Error when max_det>512 causes wong result returned
@UltralyticsAssistant UltralyticsAssistant added bug Something isn't working as intended in the official Ultralytics package. python Pull requests that update python code segment Instance Segmentation issues, PR's labels Sep 4, 2025
@UltralyticsAssistant

Copy link
Copy Markdown
Member

👋 Hello @ShuaiLYU, thank you for submitting an ultralytics/ultralytics 🚀 PR! This is an automated response — an Ultralytics engineer will review and assist soon. To ensure a seamless integration of your work, please review the following checklist:

  • Define a Purpose: Clearly explain the purpose of your fix or feature in your PR description, and link to any relevant issues. Ensure your commit messages are clear, concise, and adhere to the project's conventions.
  • Synchronize with Source: Confirm your PR is synchronized with the ultralytics/ultralytics main branch. If it's behind, update it by clicking the 'Update branch' button or by running git pull and git merge main locally.
  • Ensure CI Checks Pass: Verify all Ultralytics Continuous Integration (CI) checks are passing. If any checks fail, please address the issues.
  • Update Documentation: Update the relevant documentation for any new or modified features.
  • Add Tests: If applicable, include or update tests to cover your changes, and confirm that all tests are passing.
  • Sign the CLA: Please ensure you have signed our Contributor License Agreement if this is your first Ultralytics PR by writing "I have read the CLA Document and I sign the CLA" in a new message.
  • Minimize Changes: Limit your changes to the minimum necessary for your bug fix or feature addition. "It is not daily increase but daily decrease, hack away the unessential. The closer to the source, the less wastage there is." — Bruce Lee

Since this PR addresses a bug (OpenCV resize limitation with masks having >512 channels in ultralytics/utils/ops.py::scale_image) 🧩:

  • 🧪 Please include a Minimum Reproducible Example (MRE) that demonstrates the failure case (e.g., a small snippet that constructs a mask with >512 channels and triggers scale_image, along with expected vs. actual behavior and environment details). This will help CI and reviewers validate the fix reliably.

Example MRE template (feel free to adapt):

import numpy as np
import cv2
from ultralytics.utils.ops import scale_image

# Synthetic mask with >512 channels to reproduce the issue
h, w, c = 8, 8, 600
masks = np.random.rand(h, w, c).astype(np.float32)
im0_shape = (64, 64)  # target size

# Call the function under test
out = scale_image(masks, im0_shape, ratio_pad=(1.0, (0, 0)))
print(out.shape)

For more guidance, please refer to our Contributing Guide. Don’t hesitate to leave a comment if you have any questions. Thank you for contributing to Ultralytics! 🚀

@codecov

codecov Bot commented Sep 4, 2025

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@ShuaiLYU

ShuaiLYU commented Sep 4, 2025

Copy link
Copy Markdown
Contributor Author
import cv2
import numpy as np

# 测试512channel
masks_512 = np.random.randint(0, 255, (100, 100, 512), dtype=np.uint8)
print(f"512channel input: {masks_512.shape}")
result_512 = cv2.resize(masks_512, (50, 50))
print(f"512channel result: {result_512.shape}")

# 测试513channel  
masks_513 = np.random.randint(0, 255, (100, 100, 513), dtype=np.uint8)
print(f"513channel: {masks_513.shape}")
try:
    result_513 = cv2.resize(masks_513, (50, 50))
    print(f"513channel result: {result_513.shape}")
except Exception as e:
    print(f"513channel failed: {e}")

# 测试1000channel
masks_1000 = np.random.randint(0, 255, (100, 100, 1000), dtype=np.uint8)
print(f"1000channel input: {masks_1000.shape}")
try:
    result_1000 = cv2.resize(masks_1000, (50, 50))
    print(f"1000channel result: {result_1000.shape}")
except Exception as e:
    print(f"1000channel failed: {e}")

result

512channel input: (100, 100, 512)
512channel result: (50, 50, 512)
513channel: (100, 100, 513)
513channel failed: OpenCV(4.12.0) /io/opencv/modules/imgproc/src/resize.cpp:3845: error: (-215:Assertion failed) !dsize.empty() in function 'resize'

1000channel input: (100, 100, 1000)
1000channel failed: OpenCV(4.12.0) /io/opencv/modules/imgproc/src/resize.cpp:3845: error: (-215:Assertion failed) !dsize.empty() in function 'resize'

@Laughing-q Laughing-q changed the title fix: handle cv2.resize >512 channels limitation in scale_image fix: split large channel masks to handle cv2.resize 512 limitations Sep 5, 2025
@Laughing-q

Laughing-q commented Sep 5, 2025

Copy link
Copy Markdown
Member

this issue is found by @ShuaiLYU that cv2.resize can not handle arrays with the 3rd dimension greater than 512, reproduce example:

from ultralytics import YOLO

model = YOLO("yolo11n-seg.pt")
model.val(data="coco128-seg.yaml", max_det=513, save_json=True)  # it works with max_det=512
Traceback (most recent call last):
  File "/home/laughing/codes/ultralytics/1.py", line 4, in <module>
    model.val(data="coco128-seg.yaml", max_det=513, save_json=True)
  File "/home/laughing/codes/ultralytics/ultralytics/engine/model.py", line 635, in val
    validator(model=self.model)
  File "/home/laughing/anaconda3/lib/python3.11/site-packages/torch/utils/_contextlib.py", line 116, in decorate_context
    return func(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^
  File "/home/laughing/codes/ultralytics/ultralytics/engine/validator.py", line 221, in __call__
    self.update_metrics(preds, batch)
  File "/home/laughing/codes/ultralytics/ultralytics/models/yolo/detect/val.py", line 210, in update_metrics
    predn_scaled = self.scale_preds(predn, pbatch)
                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/laughing/codes/ultralytics/ultralytics/models/yolo/segment/val.py", line 242, in scale_preds
    "masks": ops.scale_image(
             ^^^^^^^^^^^^^^^^
  File "/home/laughing/codes/ultralytics/ultralytics/utils/ops.py", line 247, in scale_image
    masks = cv2.resize(masks, (im0_w, im0_h))
            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
cv2.error: OpenCV(4.10.0) /io/opencv/modules/imgproc/src/resize.cpp:3789: error: (-215:Assertion failed) !dsize.empty() in function 'resize'

@Laughing-q

Copy link
Copy Markdown
Member

@ShuaiLYU I checked the code again, no worries about the case that there's no 3rd dimension as input masks, the input masks would always have shape (H, W, N). And this two lines of code of for the case when N=1, cv2.resize would eliminate the 3rd dimension and output resized image with shape (H, W).

if len(masks.shape) == 2:
masks = masks[:, :, None]

@Laughing-q Laughing-q merged commit e843b4c into ultralytics:main Sep 5, 2025
18 checks passed
@UltralyticsAssistant

Copy link
Copy Markdown
Member

🎉 Fantastic work on this merge, @ShuaiLYU — and big kudos to @Laughing-q for the collaboration!

“Details make perfection, and perfection is not a detail.” — Leonardo da Vinci. Your fix for OpenCV’s 512-channel limit in mask resizing is exactly that kind of detail: a precise improvement that makes our segmentation workflows more robust, reliable, and future-proof for large multi-channel outputs.

Thank you for strengthening scale_image and keeping the API seamless. This is a meaningful quality boost for the community. 🙌

PR: Handle cv2.resize 512-channel limit

khatami-mehrdad pushed a commit to DeGirum/compiler_ultralytics that referenced this pull request Apr 24, 2026
…ultralytics#21947)

Co-authored-by: Jing Qiu <61612323+Laughing-q@users.noreply.github.com>
Co-authored-by: Laughing-q <1185102784@qq.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working as intended in the official Ultralytics package. python Pull requests that update python code segment Instance Segmentation issues, PR's

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants