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')

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:
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.
- By default,
imshow interpolates values, leading to a blurry image.
- 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.