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.