Skip to content

Support I;16 modes in BoxBlur and GaussianBlur - #10232

Open
ybai08 wants to merge 1 commit into
python-pillow:mainfrom
ybai08:gaussian-blur-i16
Open

ybai08 wants to merge 1 commit into
python-pillow:mainfrom
ybai08:gaussian-blur-i16

Conversation

@ybai08

@ybai08 ybai08 commented Oct 17, 2026

Copy link
Copy Markdown

Fixes #6667.

Changes proposed in this pull request:

  • Add ImagingLineBoxBlur16() to BoxBlur.c, and allow I;16, I;16L, I;16B and I;16N images in ImagingBoxBlur(). Since GaussianBlur is built from box blurs, this means that both ImageFilter.BoxBlur and ImageFilter.GaussianBlur now work with I;16 images.
    • The line blur uses the same fixed point arithmetic as the 8-bit version, but with 64-bit accumulators, as acc * ww can be as large as 65535 << 24.
    • Byte order is handled in the same way as kernel_i16() in Filter.c, so I;16B (and I;16N on big-endian machines) is read and written big-endian.
  • UnsharpMask also calls ImagingGaussianBlur(), but its sharpening loop only handles 8-bit data. So that it doesn't silently produce garbage now that the blur accepts I;16, it explicitly rejects non-8-bit images, with the same "image has wrong mode" ValueError as before.
  • Tests
    • assert_blur() in test_box_blur.py now also checks the four I;16 modes, so every existing radius/passes case runs against them. As those values are all below 256, the results should match L exactly.
    • test_i16_high_values uses values up to 65535 and compares a radius 1 blur against a 3x3 mean computed in Python, within 1.
    • test_i16_max_value checks that a 65535 image stays 65535 with a large radius and 3 passes. This fails if the accumulators are 32-bit.
    • test_blur_i16 in test_image_filter.py checks that GaussianBlur and BoxBlur on hopper("L").convert(mode) give the same result as on hopper("L").
    • test_unsharp_mask_i16 checks that UnsharpMask still raises ValueError for I;16.
  • Release note in a new docs/releasenotes/13.1.0.rst.

Testing:

  • The new tests fail on main (41 failures, all ValueError: image has wrong mode) and pass with this change.
  • python3 -m pytest Tests -n 8: everything passes except test_imagegrab.py::TestImageGrab::test_grab, which fails locally because screencapture isn't permitted in the environment it was run in. That is unrelated to this change.
  • pre-commit run --all-files and mypy (with .ci/requirements-mypy.txt, as in tox -e mypy) pass.
  • Only built and tested on macOS arm64 (little-endian), so the big-endian I;16N path has not been run natively; I;16B exercises the same big-endian code on little-endian machines.

@radarhere radarhere added the 🤖-assisted AI-assisted label Oct 17, 2026

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

🤖-assisted AI-assisted

Projects

None yet

Development

Successfully merging this pull request may close these issues.

GaussianBlur does not work with I;16

2 participants