Skip to content

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

Merged
radarhere merged 4 commits into
python-pillow:mainfrom
ybai08:gaussian-blur-i16
Oct 20, 2026
Merged

radarhere merged 4 commits into
python-pillow:mainfrom
ybai08:gaussian-blur-i16

Conversation

@ybai08

@ybai08 ybai08 commented Oct 17, 2026

Copy link
Copy Markdown
Contributor

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
@radarhere
radarhere merged commit 38e3104 into python-pillow:main Oct 20, 2026
112 of 130 checks passed
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