Repository navigation
Issue with PIL.Image.fromarray(..., mode="1") returning a transposed image #9006
Description
Activity
- changed the title
[-]Issue with Image.fromarray(..., mode="1") returning a transposed image.[/-][+]Issue with PIL.Image.fromarray(..., mode="1") returning a transposed image.[/+]on Jun 9, 2025 - changed the title
[-]Issue with PIL.Image.fromarray(..., mode="1") returning a transposed image.[/-][+]Issue with PIL.Image.fromarray(..., mode="1") returning a transposed image[/+]on Jun 9, 2025 Hi. This question has been raised before as #5723. I suggest having a read of that.
In terms of our code, when determining a mode and rawmode to use for the array, mode 1 is the only one of your scenarios that uses a different rawmode.
Lines 3405 to 3408 in 05636dc
_fromarray_typemap = { # (shape, typestr) => mode, rawmode # first two members of shape are set to one ((1, 1), "|b1"): ("1", "1;8"), Because you're setting the mode, that is also used as the rawmode.
See also #5465 (comment)
I think that the mode to use parameter is a footgun. It’s not an implicit conversion, it’s a cast.
It seems like you already have a solution, simply to not set the mode in
fromarray().guillaume-rochette-oxb commented
on Jun 10, 2025 AuthorMore actionsHi,
Thank you for your response, however I do not think this is a satisfying solution.
Why not make a special case for it?
Implicit is not better than explicit, c.f. PEP 20.
Do you think it would be good to createfromarray2()then?def fromarray2(obj: SupportsArrayInterface, mode: str | None = None) -> Image: if mode == "1": mode = None return fromarray(obj, mode)
Why not make a special case for it?
There is often a reluctance to change things, for the sake of backwards compatibility. It looks like this behaviour has been in place since before we forked from PIL, about 15 years ago.
I can understand that the
modeargument doesn't work how you'd expect. However, I'm still not clear on why you need to pass it in.Implicit is not better than explicit, c.f. PEP 20.
I don't personally think that's a sufficient reason to for the user to provide a mode. The array being passed in has a shape and a type. It more or less has a mode, in the same way that images have modes.
There might actually be a stronger appetite for deprecating the argument altogether - #5465 (comment)
I’m not sure if there is a good use case at all for the mode parameter here, as we need to be pretty sure what we’re getting from numpy to interpret the array correctly.
guillaume-rochette-oxb commented
on Jun 10, 2025 AuthorMore actionsPardon me, but this seems like you're implying that "it's not a bug, it's a feature" :/
I disagree, there is no bijection between
modeanddtype, for example both modesRGBandHSVare represented withuint8, maybe instead we should have instead `fromarray(..., array_mode, image_mode) instead?I'm trying to say that regardless of whether it is a bug, it's long-standing behaviour, and it's possible that users have workarounds that rely on it.
Feel free to disagree with me and create a PR, if you'd like to move forward and see what others think.
guillaume-rochette-oxb commented
on Jun 11, 2025 AuthorMore actionsOkay.
To try and get another opinion, @TheRealQuantam, you created #6561 where you were using both L and P mode with
fromarray(). Do you have any thoughts on this issue or on deprecating themodeparameter altogether?We've had #2856 (with 12 likes), #3781, #4887, #5227, #5465 and #5723 - all issues were this parameter has caused confusion. Since then, #5849 improved the documentation, but as per this issue, it's not done causing problems.
I've created #9018 to suggest deprecating
mode.
What did you do?
I have an
np.ndarraywithshape=(h, w)anddtype=boolthat I want to convert to aPIL.Image.Image(and later save on disk disk, although that is not relevant).I have noticed that
PIL.Image.fromarray(array, mode="1")does not work correctly, whilePIL.Image.fromarray(array, mode=None)does behave correctly.What did you expect to happen?
Referring to the sample code below, I expect that all the 3 arrays would be equal for all modes.
What actually happened?
It does not work for
mode="1".What are your OS, Python and Pillow versions?
Linux:
macOS:
Sample code
image_1:image_2:image_3: