4

So Im using this very simple image to understand how image is being stored in an array and how to manipulate it.8x8 pixel image

SIDE NOTE
Can anyone suggest me a book/blog which starts with these basics, as most of them skipping this part and even though trivial but I cant progress with these small nuances eating my head

Its 8x8 pixel image ,with a single dot in fist top left pixel.

#import modules
import matplotlib.pyplot as plt
from PIL import Image
%matplotlib inline
import numpy as np

#Load image as array
i=Image.open("dot.png")
iar=np.asarray(i)

Now for the analysis, first Im starting with displaying some arrays then Ill finally display the "dot.png"

LINK TO dot.png image (use ctrl+s to save it as its very small 2x2 pixel hence not visible ) https://i.sstatic.net/y2ot9.png dot.png image 2x2 pixel

plt.imshow([0])
TypeError: Invalid dimensions for image data

But

plt.imshow([[0]])

first_image

plt.imshow([[255]])

second_image

 plt.imshow([[255,0]])

third_image

plt.imshow([[255,0,128]])

fourth_image

plt.imshow([[255,0],[23,128]])

fifth_image

plt.imshow(iar[0])

sixth_image

plt.imshow(iar[0:1])

seventh_image

# Checking attributes of iar[0]
subset1=iar[0]
subset1.shape
Out[144]: (8L, 4L)
In [140]:iar[0]
Out[140]: 
array([[  0,   0,   0, 255],
   [255, 255, 255, 255],
   [255, 255, 255, 255],
   [255, 255, 255, 255],
   [255, 255, 255, 255],
   [255, 255, 255, 255],
   [255, 255, 255, 255],
   [255, 255, 255, 255]], dtype=uint8)

# Checking attributes of iar[0:1]
subset2=iar[0:1]
subset2.shape
Out[146]: (1L, 8L, 4L)
In [142]:iar[0:1]
Out[141]: 
array([[[  0,   0,   0, 255],
    [255, 255, 255, 255],
    [255, 255, 255, 255],
    [255, 255, 255, 255],
    [255, 255, 255, 255],
    [255, 255, 255, 255],
    [255, 255, 255, 255],
    [255, 255, 255, 255]]], dtype=uint8)

My questions :-

  1. Why is plt.imshow([0]) failing but plt.imshow([[0]]) works ?
  2. Why is it that both plt.imshow([[0]]) & plt.imshow([[255]]) showing blue color only ? Even though 0 doesnt mean blue, nor does 255 mean blue.
  3. Where is the pixel in these images. plt.imshow([[0]]) is showing an image of size 1x1. Is it one pixel image ? If so then plt.imshow([[255,0]]) means 2 pixels ?
  4. Why are the colors blurred and not well defined, I didnt give any information on alpha/blurring. But still plt.imshow([[255,0,128]]) is showing all colors as blurred on boundaries. Whats happening here ?
  5. Why is it that iar[0] is coming as color image even though dot.png is simply black & white image. But iar[0:1] shows the first row of dot.png exactly as its stored there.
1
  • I think that some questions comes because you are trying to print images, but instead you are plotting lists i.e.: In the last images you are passing a 3 values list, and thus the program is plotting the 3 values one after another (At the left, center and right positions) and assigning colours. Then, it is making progressive step between them. Maybe it is easier to see if you plot an straight joining the given values Commented Oct 26, 2016 at 8:07

1 Answer 1

6

OK, so some of your questions are about the behaviour of pyplot.imshow.

imshow displays an image, and takes as an argument something that is "array-like", with a shape of (n, m) (for greyscale images), (n, m, 3) (for colour images), or (n, m, 4) (for colour images with transparency, or alpha, information).

When you do plt.imshow([0]), you're passing an argument with a shape of (1,), which is why you get the error. The list [0] is one-dimensional, not two, and so cannot be displayed by imshow. By contrast, [[0]] is a two-dimensional array, representing a single pixel, and so can be displayed.

Both [[0]] and [[255]] will be displayed as blue squares. This is because matplotlib colour-maps the pixel values for greyscale images. Colour mapping means that matplotlib finds the range of values in the data you pass it, and makes the lowest value one colour, the highest value another colour, and all the values in between map to some range of colours designed either to look pretty, or to be useful in interpreting your data. If your image only has one pixel, there's no colour range, so the whole image will be displayed in the same colour. Note that matplotlib defaults to using the jet colourmap, if you don't specify a different value to the argument cmap.

plt.imshow([[255,0]]) is displaying a two-pixel image; the array you've passed has one row with two columns. Matplotlib shows this as two colours: we can see that red represents high (255), and blue low (0).

The image is blurred because matplotlib interpolates the image data. You can change this behaviour by giving a value for the interpolation argument to imshow (matplotlib defaults to using 'bilinear', which gets you the "anti-aliased" look).

plt.imshow([[255,0]], interpolation='none')

No interpolation

Next, you're displaying a 3-pixel greyscale image ([[255,0,128]]). This shows up as a gradient from red to blue to green; those colours are again taken from the jet colour map (red is high, green is middle, and blue is low). [[255,128,128]] will be red, blue, blue, because the range of this data is from 128 to 255, so 128 will be the new low (blue).

OK, and finally, we come to your 8x8 image. Your PNG image might contain only black and white pixels, but it is stored in RGB format. We can see this, for example, using ImageMagick:

$ identify ~/Downloads/y2ot9.png
/home/user/Downloads/y2ot9.png PNG 8x8 8x8+0+0 8-bit sRGB 138B 0.010u 0:00.059

So this means that each pixel is represented by three bytes (for red, green, and blue). PIL opens this in RGBA mode, adding another byte for the alpha channel:

>>> from PIL import Image
>>> import os
>>> i = Image.open(os.path.expanduser('~/Downloads/y2ot9.png'))
>>> i.size
(8, 8)
>>> i.mode
'RGBA'

The first pixel is black, and the others are white. We can see that black is the tuple (0, 0, 0, 255) (that is, 0 red, 0 green, 0 blue, and 255, or full, alpha). Similarly, white is (255, 255, 255, 255):

>>> i.getpixel((0,0))
(0, 0, 0, 255)
>>> i.getpixel((1,0))
(255, 255, 255, 255)

When you put this image into a numpy array, you'll get these same pixel values. The array is of shape (8, 8, 4):

>>> import numpy as np
>>> a = np.asarray(i)
>>> a.shape
(8, 8, 4)

This means 8 rows, 8 columns, and 4 components for each pixel (red, green, blue, alpha).

Taking a[0] gives you the first row of this array, which is of shape (8, 4). There's 8 columns of pixels, which have 4 components each. When you send this to imshow, it gets interpreted as a greyscale image of size 8x4, because you've got two dimensional data. You'll get a colour-mapped display of the first row of your image. You can see the first pixel as three blue values (the three 0s of the black pixel). The last column of the image is red everywhere, because your image has full alpha everywhere.

a[0:1] gives you a slice of the array; this will be of shape (1, 8, 4). When you send this to imshow, it gets interpreted as a colour image of size 1x8 (because it's three-dimensional data, and the last dimension is 4, signifying colour with alpha information). That's why showing this gives you a black-and-white bar, which is the picture you're likely expecting to see. You can get the same result by displaying [a[0]].

Because you're dealing with black-and-white image data, it might be more intuitive to represent your data in greyscale. You can do this by changing the PNG file to be greyscale, or you can convert the image in Python using PIL:

>>> i2 = i.convert('L')
>>> a2 = np.asarray(i2)
>>> a2.shape
(8, 8)

If you work with greyscale data, imshow will always colour-map everything, so you won't have this unpredictable switching between colour-mapped greyscale information and full-colour information (which depends on the shape of the array data you're sending to imshow).

Summary:

  1. imshow displays values from arrays. The shape of the array is important, because imshow will automatically switch between displaying colour-mapped greyscale data, or displaying full-colour data.
  2. By default, imshow interpolates values, leading to a blurry image.
  3. Be aware of the way that image data is stored, both in your image files, and in the Python data structures. imshow is usually used to show two-dimensional data with a single component (greyscale); the colour-mapping helps make the plots more appealing, and show variation that would be harder to see if everything was just grey. Image data, on the other hand, quite often includes colour information.

I'm not sure where you can get a beginner's overview of image processing (maybe try reading through How do I get started with image processing?). I've heard Lecture 1 Introduction to Digital Image Processing recommended before, and you can also continue exploring, as you've been doing, for instance, by reading up on image data formats like the simple Netpbm format.

Sign up to request clarification or add additional context in comments.

3 Comments

can u pls explain whats happening with plt.imshow([[255,128,0]]) & plt.imshow([[255,128,128]]). Its a 1x3 array and according to you 3 pixels in a row. But first command is showing (red,green & blue) whereas second is showing (red,blue,blue) but I didnt change the second value of array. Then why did it turn to blue from green ?
Another question which I forgot to add was "Why is it that dot.png" Details/properties is showing 8X8 pixels but all the other images which Ive saved from terminal which even though are 3x1, 2x3 etc are not exactly 3x1 pixel in their Details/Properties?" For example even your image i.sstatic.net/edNGz.png is showing 800x600 pixels , but youd have represented in python terminal using 1x2 array.
dot.png is an 8x8 pixel image, and contains 64 pixels. The edNGz.png image is a picture produced by matplotlib. It's a visualisation of 8x8 data, but it's using a lot more pixels to show that information. There's pixels there to show the axes, axis labels, and quite a bit of whitespace, for example.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.