Based on your comment here is an answer. It is this example mapped to your problem. 
Here is what I did.
To represent color of a pixel, Images use RGB colors. It is basically representing each Red, Green and Blue using a integer from 0(black) to 255(white). Since you wanted black and shades of black and white, or grayscale, all RGB values are same. As your number go from 0 to 20, we map this range 0 to 20 to 0 to 255. 0 meaning black and 20 meaning white. This is based on another stackoverflow question.
def map(input):
input_start = 0
input_end = 20
output_start = 0
output_end = 255
output = output_start + ((output_end - output_start) // (input_end - input_start)) * (input - input_start)
return output
- Next, we create an image and color each square with the color we want. I am using random number but in your case, it will be the array. Read the above blog to understand.
def next_shape(im, num_squares, pixel_per_square,image_width ):
pix = im.load() #initialise plot location
startx, starty = 0, 0
for i in range(num_squares):
startx += pixel_per_square
for j in range(num_squares):
starty += pixel_per_square
color = np.random.randint(0, 21)
colorRGB = map(color)
for x in range(pixel_per_square):
for y in range(pixel_per_square):
value = (colorRGB, colorRGB, colorRGB)
pix[(startx + x) % image_width, (starty + y) % image_width] = value
return list(im.getdata())
Rest see the blog on details of implementation.