Fix convert() from I;16N to I or F clipping 16-bit values - #9931
Merged
Conversation
I;16N is native endian, so it should convert like I;16L on little endian machines and I;16B on big endian ones, as the L and RGB rows in the converter table already do. There were no I;16N rows for I or F, so convert() fell back to normalizing through L and clipped values above 255. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BCtY1NAjuJ1jRvq5P6vSED
Member
|
Hi. Would you mind letting us know a bit of the backstory for why you created this PR? I just want to check this was motivated by a need, rather than purely by ensuring all modes can be converted to all other modes. |
Contributor
Author
|
Fair question. No user report behind it, I found it comparing the four 16-bit modes against each other. The reason I opened it anyway is that it isn't a missing conversion, it's a silent wrong answer. Same bytes, different result: from PIL import Image
raw = (4660).to_bytes(2, "little")
for m in ("I;16L", "I;16N"):
im = Image.frombytes(m, (1, 1), raw)
print(m, im.tobytes().hex(), im.convert("I").getpixel((0, 0)))
# I;16L 3412 4660
# I;16N 3412 255No exception, no warning, and Thanks. |
radarhere
approved these changes
Aug 31, 2026
radarhere
reviewed
Aug 31, 2026
Co-authored-by: Andrew Murray <3112309+radarhere@users.noreply.github.com>
hugovk
enabled auto-merge (squash)
August 31, 2026 11:27
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Changes proposed in this pull request:
I;16Nrows for theIandFconversions in theConvert.ctable, dispatching to the little or big endian handler the same way the existingLandRGBrows do.I,Fand the reverse direction for all four 16-bit modes.I;16Nis native endian, so on a little endian machine it stores the same bytes asI;16L. The table has hadLrows for it since #6834 and anRGBrow since #7920, but neverIorF, soconvert()fell back to normalizing throughLand clipped anything above 255:Same for
convert("F"), and forItoI;16N. This is the problem #274 reported forI;16, still present forI;16N.The existing
test_convertalready callsconvert("I;16N").convert("I"), but its source image only holds values up to 255, so it cannot catch this.