Introduction
In this tutorial we will learn how to tile an image so it repeats itself both vertically and horizontally the number of times we specify. We will do this using Python, OpenCV and numpy.
This tutorial was tested on Windows 8.1, using Python version 3.7.2 and OpenCV version 4.1.2.
Tiling a colored image
As usual, we will start our code by importing the modules we need. We will need numpy and cv2.
import numpy
import cv2
After that we will read the image with the imread function from the cv2 module. This image will be returned as a ndarray, like we have seen in previous tutorials.
img = cv2.imread('C:/Users/N/Desktop/Test.jpg')
Once we have our image as a ndarray, we can use the tile function from the numpy module to do a tiling of our image horizontally and vertically.
In short, the tile function allows to construct a new ndarray by repeating the original array across its axes [1]. The function allows us to specify the number of repetitions along each axis [1].
In our case, we have read an image in the BGR format, which means that it has 3 dimensions: the height, the width and the three color channels of the image.
In our case, we want only the repetition to occur in the first and second axes (height and width) and that the third axis remains unchanged.
Note that concatenating an image only vertically or horizontally is a particular case where there is no repetition over one of these axis.
So, the tile function receives as first input the ndarray (our original image) and as second input a tuple where each element corresponds to the number of repetitions along that axis.
Looking into more detail to this second parameter, we have the following tuple format:
(nº of repetitions vertically, nº of repetitions horizontally, 1)
Take in consideration that the value 1 means no repetition, just the original values across that axis.
So, if we want the image to be repeated 2 times vertically and 3 times horizontally, we have the following tuple:
(2,3,1)
Putting all together with the tile function invocation, we have:
tile = numpy.tile(img,(2,3,1));
Note: The tile function can be generically used to work with ndarrays, which means it is not an image processing dedicated function and it can be used outside of this scope.
To finalize, we will display the tiled image in a window.
cv2.imshow('Tile', tile)
cv2.waitKey(0)
cv2.destroyAllWindows()
The final code can be seen below.
import numpy
import cv2
img = cv2.imread('C:/Users/N/Desktop/Test.jpg')
tile = numpy.tile(img,(2,3,1));
cv2.imshow('Tile', tile)
cv2.waitKey(0)
cv2.destroyAllWindows()
To test the code, simply run it in an environment of your choice. In my case I’m running it on IDLE, a Python IDE.
Upon running the code, you should obtain a result similar to figure 1. As can be seen, the original image was tilled both vertically and horizontally, as expected.

Just as a note, if you wanted to tile the image 3 times only horizontally or only vertically, you could use the following:
tileHorizontally = numpy.tile(img,(1,3,1))
tileVertically = numpy.tile(img,(3,1,1))
Tilling a grey scale image
As mentioned in the previous section, since we were working with a BGR image, then we had to specify the repetition over the three axis, even if there was no need to repeat anything in the third dimension.
In case we are working with an image in grey scale, the the corresponding ndarray only has two dimensions. Consequently, when calling the tile function, the second argument (the repetitions tuple) should only have two entries:
(nº of repetitions vertically, nº of repetitions horizontally)
If mistakenly we add the third parameter with the value 1 to the tuple, then a new axis will be prepended to the resulting ndarray [1], which will give wrong results when we try to display it as an image.
Below you can see the full code to do the tiling procedure to a gray scale image:
import numpy
import cv2
img = cv2.imread('C:/Users/N/Desktop/Test.jpg')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
tile = numpy.tile(gray,(2,2))
cv2.imshow('Tile', tile)
cv2.waitKey(0)
cv2.destroyAllWindows()
If you run the previous code, you should get an output similar to figure 2.

References
[1] https://docs.scipy.org/doc/numpy/reference/generated/numpy.tile.html