Nick Lothian
Nick Lothian

Reputation: 1443

PIL Image from numpy array indexing

This turned out to be a very specific bug in a pre-7.0 version of PIL. I'm leaving it here because I've seen other hit the same issue and there isn't a good overview of what you see. There is no programming error here - the solution is "upgrade PIL"

I'm converting a numpy boolean array (ie, a mask) to a PIL image, and seeing a very odd behaviour.

# using PIL pixel access - works as expected
img = Image.new('1', (100, 100)) # '1' for 1-bit image mode
img_pixels = img.load()

for x in range(1, 99):
    for y in range(1, 50):
        img_pixels[x,y] = True

img

PIL Pixel Access version

# Numpy array, turning into a PIL image. 
ary = np.zeros((100,100), dtype=np.bool)

for x in range(1, 99):
    for y in range(1, 50):
        ary[x,y] = True
      
Image.fromarray(ary)

numpy version

If I inspect the values of ary it appears the True values are appropriately set.

So why are these images different?

Edit:

If I reverse x and y for the numpy version I get this. It's clear that something else is going on because if it was just rows and columns reversed I should get the same shape but vertically aligned.

ary = np.zeros((100,100), dtype=np.bool)

for x in range(1, 99):
    for y in range(1, 50):
        ary[y,x] = True # note that rows and columns are reversed
      
Image.fromarray(ary)

numpy using column,row ordering

Upvotes: 2

Views: 1611

Answers (2)

Nick Lothian
Nick Lothian

Reputation: 1443

This turned out to be a bug in an earlier version of PIL that was fixed by at least version 7.0

With

  • PIL: 7.2.0
  • numpy: 1.19.1

the behaviour is as expected. Thanks to @furas for his help testing.

Upvotes: 0

Ehsan
Ehsan

Reputation: 12417

PIL has reverse x and y locations. To fix it, exchange x and y in your code:

ary = np.zeros((100,100), dtype=np.bool)

for x in range(1, 99):
    for y in range(1, 50):
        ary[y,x] = True
      
Image.fromarray(ary)

enter image description here

Upvotes: 1

Related Questions