Repository navigation
Inconsistent Image.fromarray() behaviour with mode '1' #5723
Description
Activity
Hi. This seems to be a duplicate of #5465. Try having a read through the comments there.
Mode for fromarray is the "Mode to use". I think this is the mode to use when reading the numpy data - if so, then it makes sense that the values change when it is told to interpret the input data directly.
By default, the boolean data is inferred to have a mode of 1 and a rawmode of 1;8. Because you are explicitly setting it, they both become 1, and so the output data is different.
I think that the mode to use parameter is a footgun. It’s not an implicit conversion, it’s a cast.
Yes, it's definitely the same issue, thanks. I'm closing in order not to duplicate the discussion.
Edit Before I close, could you explain what is the rawmode and why boolean data is interpreted with a rawmode of 1;8? I can't find any information on the documentation
The raw mode is the mode of the incoming data.
Pillow converts (unpacks) the data from therawmodeto themodeas a way of normalising it, reducing the number of modes that we have to support operations for.Here is the unpack method from 1;8 to 1 -
Pillow/src/libImaging/Unpack.c
Lines 246 to 255 in ffde039
static void unpack18(UINT8 *out, const UINT8 *in, int pixels) { /* Unpack a '|b1' image, which is a numpy boolean. 1 == true, 0==false, in bytes */ int i; for (i = 0; i < pixels; i++) { out[i] = in[i] > 0 ? 255 : 0; } } As opposed to our method for just unpacking 1 to 1 -
Pillow/src/libImaging/Unpack.c
Lines 110 to 142 in ffde039
static void unpack1(UINT8 *out, const UINT8 *in, int pixels) { /* bits (msb first, white is non-zero) */ while (pixels > 0) { UINT8 byte = *in++; switch (pixels) { default: *out++ = (byte & 128) ? 255 : 0; byte <<= 1; case 7: *out++ = (byte & 128) ? 255 : 0; byte <<= 1; case 6: *out++ = (byte & 128) ? 255 : 0; byte <<= 1; case 5: *out++ = (byte & 128) ? 255 : 0; byte <<= 1; case 4: *out++ = (byte & 128) ? 255 : 0; byte <<= 1; case 3: *out++ = (byte & 128) ? 255 : 0; byte <<= 1; case 2: *out++ = (byte & 128) ? 255 : 0; byte <<= 1; case 1: *out++ = (byte & 128) ? 255 : 0; } pixels -= 8; } } Ok, now it is starting to get more clear, thanks. Do you know if there's a table of all the possible rawmodes and the implicit conversions that users can read?
For
fromarray?Lines 2877 to 2902 in d76fd93
_fromarray_typemap = { # (shape, typestr) => mode, rawmode # first two members of shape are set to one ((1, 1), "|b1"): ("1", "1;8"), ((1, 1), "|u1"): ("L", "L"), ((1, 1), "|i1"): ("I", "I;8"), ((1, 1), "<u2"): ("I", "I;16"), ((1, 1), ">u2"): ("I", "I;16B"), ((1, 1), "<i2"): ("I", "I;16S"), ((1, 1), ">i2"): ("I", "I;16BS"), ((1, 1), "<u4"): ("I", "I;32"), ((1, 1), ">u4"): ("I", "I;32B"), ((1, 1), "<i4"): ("I", "I;32S"), ((1, 1), ">i4"): ("I", "I;32BS"), ((1, 1), "<f4"): ("F", "F;32F"), ((1, 1), ">f4"): ("F", "F;32BF"), ((1, 1), "<f8"): ("F", "F;64F"), ((1, 1), ">f8"): ("F", "F;64BF"), ((1, 1, 2), "|u1"): ("LA", "LA"), ((1, 1, 3), "|u1"): ("RGB", "RGB"), ((1, 1, 4), "|u1"): ("RGBA", "RGBA"), } # shortcuts _fromarray_typemap[((1, 1), _ENDIAN + "i4")] = ("I", "I") _fromarray_typemap[((1, 1), _ENDIAN + "f4")] = ("F", "F") Thanks
What did you do?
The following code can reproduce the issue:
Output:
What did you expect to happen?
When initialising an image from a boolean NumPy array, it is expected that
Truevalues are assigned to white pixels andFalsevalues to black ones. The resulting image should havemode'1'and if, converted back to an array, its contents should be equal to the initial input array.What actually happened?
If
Image.fromarray()is called withmode=None, the code works as expected. If instead it is called withmode='1', the array results in allFalsevalues. This is an issue for two reasons.Image.fromarray()is inconsistent: given that when called withmode=Noneit detects the data as havingmode='1', passingmode='1'as an input argument should not change the result.What are your OS, Python and Pillow versions?
I have run the script with two different conda virtual environments. The OS is macOS Big Sur v11.5.2 (20G95).