Divide matrix into submatrices python

Vectors describing the distributions of input array elements along each dimension, specified as numeric vectors.

For example, if A is a 60-by-50 array, then you can specify this argument as [10 20 30],[25 25] to divide A as shown in the code and figure. C is a cell array that contains the six subarrays split out of A.

C = mat2cell(A,[10 20 30],[25 25])

Divide matrix into submatrices python

For the Kth dimension of A, specify the elements of the corresponding vector dimKDist so that sum(dimKDist) equals the size of the Kth dimension.

If the Kth dimension of A has a size of zero, then specify the corresponding vector dimKDist as the empty array, [], as shown in the code.

A = rand(3,0,4);
C = mat2cell(A,[1 2],[],[2 1 1]);

Efficiently splitting an image into tiles in Python using NumPy

Developing intuition on array strides and numpy.reshape()

Divide matrix into submatrices python

Fig. 1. Problem illustration. Image by Author.

There are plenty of situations where you would need to break down a large image in tiles (e.g. as part of a ML preprocessing pipeline) for batch processing. This article aims to explore lower-level ways of doing just that, in Python.

1. Nested for-loops: The inefficient way.

One of the most common ways of creating a stack of image tiles I have seen around is with nested for-loops, as in instantiating a new array with the desired dimensions and populating it within those loops. However, this method is particularly inefficient (especially) in Python and does not scale well, at all, with large datasets (nor smaller tiles, for that matter).

A nested for-loops approach would look something like the snippet below:

# Nested for-loops method.
# Creating a stack of 2 x 2 tiles out of a 4 x 4
# RGB image with channels on the last axis.

import numpy as np

image = np.random.randn(4, 4, 3)
tiles = np.zeros((4, 2, 2, 3))

c = 0

for i in range(0, image.shape[1], 2):

for j in range(0, image.shape[2], 2):

tiles[c] = image[i:i+2, j:j+2, :]
c += 1

As the iterations grow (image size over tile size ratio), not to mention iterating over large number of images (3rd for-loop), this can occupy resources and quickly create a bottleneck in your pipeline.

2. Alternatively: Memory views

Divide matrix into submatrices python

Fig. 2.1. Array memory layout. Image by Author.

What if we could just use a different view to the same chunk of memory and present it as tiles? Let’s talk a bit about arrays.

Numpy arrays, and arrays in general, are laid out in memory as contiguous chunks of bytes. That contiguous piece of memory can then be viewed in different shapes using strides.

On the memory level of arrays, the stride represents the number of bytes you need to jump forward in order to get to the next element contained in the array. This depends on the byte-length of the data types contained in the array.

e.g. A 1-dimensional array of 16-bit integers (2 bytes length) will have a stride of 2 bytes.

Something else to note is that arrays have to contain elements of the same data-type, so as to guarantee equal byte-length intervals. There are ways of representing different data types in a single array, of course, but that is outside the scope of this article.

2.1. Array dimensions

So, what are the elements in an array? Just bytes laid out in order. Elements can vary in length, depending on the number of bytes needed to represent each of them.

An element, then, is an aggregation of bytes, which in turn are an aggregation of bits, 8 bits in particular. All living within the same physical space. So, is it possible to aggregate an array further into larger chunks? Yes it is. That is exactly what array dimensions do.

Divide matrix into submatrices python

Fig. 2.2. Illustration of a C-order 2-dimensional array in memory. Image by Author.

In order to organize your array elements into rows and create a second dimension, all you need to do is to define a second stride to point to the beginning of each row. On that dimension, then, the individual elements are the rows. This can also be thought of as splitting an array into multiple equal-sized sub-arrays.

In the array dimension sizes returned by numpy.shape(), the above depicted example would be presented as a 2-dimensional shape (3,3). The order of the dimensions returned always start (left-most) from the highest level going towards the lowest one (right-most). This means the size 3of the first dimension in the tuple indicates the number of rows, while the size 3 of the second dimension indicates the number of individual elements per row. As we will see later on, there are numpy methods that can change the dimension hierarchy, but the above generally stands for array views formed using numpy.reshape() as well as newly instantiated arrays in numpy.

Note 1: The dimension order above is referring to the order accessed in the tuple returned by numpy.shape(), with the first dimension being at index position 0 (shape[0]) and the second dimension being at index position 1 (shape[1]).

Note 2: There are multiple layouts (row/column major), but this article only discusses row-major a.k.a. C order, as it is the one I am most familiar with. More information on layouts here.

Divide matrix into submatrices python

Fig. 2.3. Illustration of contiguous array dimensionality. Image by Author.

For our purposes, we will give a generic definition of a dimension size as follows:

Using an 4-dimensional array of shape (m, n, i, j) as an example, the 1st (highest & left-most) dimension holds m number of elements organized in shapes (n, i, j). The 2nd dimension, holds n elements of shape (i, j), while the 3rd holds i elements of sizes ( j ). The 4th and last dimension is simply a single row of j elements. Then:

Dimension size:The number of elements described by the subsequent (lower) dimensions.

2.2. Splitting a 2D numpy image array into tiles, by specifying custom strides

Now, a 2D image represented as a numpy array will have shape (m,n), where m would indicate the image height in pixels, while n would indicate the image width in pixels. As an example, let’s take a 6 by 4, 8-bit grayscale image array and aim to divide it in 2 by 2 tiles by creating a new memory view using strides. Remember, the elements have to be of equal length, hence, both array dimensions have to be divisible by 2.

Our image can then be thought of as being organized in 6 rows of elements of 4. The visualization of our example would look something like the following:

Divide matrix into submatrices python

Fig. 2.4. Illustration of our tile problem. Image by Author.

So, how do we get there?

Let’s take it one step at a time. Seeing that we need 6 tiles all together, organized in 3 rows and 2 columns, lets first try to split our current rows in 2 columns. Our current shape is (6, 4), thus, we need to halve the last dimension and add a higher dimension of 2, representing the columns that we want to form. Our target 3-dimensional shape for this step, then, is (2,6,2): 2 columns by 6 rows by 2 array elements in each row. Now that we know the shape we need. We can go ahead to figure out the strides.

Let’s observe the image below. The strides for our 2D image were (4,1). Stride 1 of the lowest dimension has to remain intact, as messing with it would make us lose the spatial relationship at the pixel level (e.g. increasing it would mean to skip pixels). Stride 4 of the rows dimension will also stay constant, since the number of desired rows remain 6. Generally speaking, if you changed any of those two strides, you would end up distorting the image. Then, all we need to do is figure out the stride for our highest dimension of size 2, which represents the new higher level columns we are defining. Having in mind that we split our original 4 per row elements in half so they can be included in separate columns, gives us a hint.

Divide matrix into submatrices python

Fig. 2.5. Illustration of column strides solution. Image by Author.

Check figure 2.5. All pixels in our array are numbered in the order they are laid out in memory (C order). Before, every 4 elements we had to change a row, a rule that still stands. We want to include an additional rule: In each row, change columns every 2 elements. So the stride in our highest dimension is 2 x element_byte_size. In our case of 8-bit integers, the stride is 2x1 byte = 2.

Let’s open up a terminal and test it in code:

Divide matrix into submatrices python

Fig. 2.6. Shape and size of our example array. Image by Author.

In numpy, you can manipulate the strides of an array using numpy.lib.stride_tricks.as_strided(). We need to specify the array we want to manipulate, the shape we want it in, and the stride we want for each dimension:

Divide matrix into submatrices python

Fig. 2.7. Splitting example image in 2 columns. Image by Author.

We got our two columns with the elements we wanted! We can continue now to divide each column in 3 groups of 2 rows, giving us a total of 6 tiles.

Our current shape is (2, 6, 2), with 6 being our number of rows in the image. Similarly to the previous step where we needed our 3rd dimension elements (the 2 columns) to hold rows with just 2 array elements in each (1st dimension elements) row, we now need our 4th dimension elements in the top hierarchy to hold only 2 image rows each. So, we need to decrease the “image rows” dimension from 6 down to 2. Our target shape then will be (3, 2, 2, 2).

In summary, the lower level elements remain 2, our rows decrease to 2, the columns at the 3rd dimension remain 2, and we now add a 4th dimension to split those columns in 3 parts.

Divide matrix into submatrices python

Fig. 2.8. Illustration of tile strides solution. Image by Author.

Our definition of this 4-dimensional array dictates that the highest dimension will now change elements every 2 image rows, instead of 1. So, in conclusion, the new stride has to be twice that of an image row. Then, the stride for our new dimension will be 2 x row_stride = 2x4 = 8 bytes.

Testing it in code:

Divide matrix into submatrices python

Fig. 2.9. Implementation of custom strides solution. Image by Author.

And we’re done! All 6 tiles formed. Now we can merge the 2 highest dimensions using numpy.reshape() to make our array in the 3-dimensional shape of (number_of_tiles, tile_height, tile_width) if we wanted to, since this is the basic format for image batches. The reason we needed 4 dimensions instead of 3 in the first place, is because the location of every tile in memory cannot be represented by a single stride. Our image has 2 dimensions after all.

What we just implemented to split a 6 x 4 image of 8-bit integers in 2 x 2 tiles using strides, can be generalized to any C ordered 2D image like this:

Divide matrix into submatrices python

Eq. 2.1. Shape and strides for splitting a grayscale image into tiles. Image by Author.

As it turns out, we can have a similar formula for specifically formatted multi-channel images as well. Specific in a way that, they are contiguous C ordered arrays with channels at the lowest dimension (having same pixel RGB values next to one another), otherwise the strides will differ in each occasion. Some libraries load images directly like that, some don’t, but it’s not particularly hard to reshape them.

GDAL, for example, will load images with channels as the highest (left-most) dimension.

When our multi-channel array dimensions are formatted that way, the lowest dimension will be our per-channel values for each pixel, which we can navigate by doing only a single byte-length step. Since, instead of 1 single pixel value, we will have n pixel values for n channels, all we need to do is to multiply all other strides by the number of channels. Summarizing, everything remains the same, but we now have multiple values per pixel to jump, 1 for each channel:

Divide matrix into submatrices python

Eq. 2.2. Shape and strides for splitting a multi-channel image into tiles. Image by Author.

Not a particularly clean approach, but we will see in the next section that there is a cleaner way. Dealing with strides is a very sensitive procedure and requires extreme caution. Every time you would have to verify contiguity and take necessary steps.

Luckily enough, numpy provides a few higher level methods to save us most of the trouble.

2.3. Numpy.reshape( )

Divide matrix into submatrices python

Fig. 2.10. Output of numpy.reshape() giving the wrong result. Image by Author.

Numpy.reshape() is the standard, most common way of manipulating an array shape in numpy. However, if we simply stated in our previous 6 x 4 image example that we wanted an array of shape (3, 2, 2, 2) it would not have worked.

That happens because numpy.reshape(), by default, tries to maintain contiguity of subsequent values. It means that sequential indexes of your array, used through your program, will continue to be in adjacent blocks of memory. Unfortunately, in order to get our result we need to break contiguity. Although, we can make it contiguous again after breaking it in tiles, for performance reasons.

Divide matrix into submatrices python

Fig. 2.11. Strides of numpy.reshape() output. Strides differ from our desired solution in previous section. Image by Author.

<< Strides differ from our previous solution.

How does it work? It basically takes a dimension (at any level), breaks it down in equal adjacent pieces and stacks them together at a higher level. In our case, it does something like the following:

Divide matrix into submatrices python

Fig. 2.12. Illustration of numpy.reshape() output. Image by Author.

Instead, what we want is this:

Divide matrix into submatrices python

Fig. 2.13. Illustration of our desired output layout in memory. Image by Author.

So, how do we fix it?

As a simple comparison of the two arrays’ strides hints, all we need to do is swap strides between axis 1 and axis 2 (1st and 2nd dimensions). Specifically, the strides we need are (8, 2, 4, 1), but numpy.reshape() returns an array with strides (8, 4, 2, 1). Numpy arrays expose a method just for that, called swapaxes:

Divide matrix into submatrices python

Fig. 2.14. Correct solution using numpy.reshape() and swapaxes method. Image by Author.

The numpy.reshape() implementation, then, can be generalized for any 2D or 3D image with channels in the last dimension as follows:

Divide matrix into submatrices python

Eq. 2.3. Shape dimensions and swapaxes implementation for splitting a multi-channel image into tiles. For grayscale images you can omit the channel dimension. Image by Author.

3. Comparison & Conclusions

Let’s compare executions!

We’ll use an RGB JPEG of this cool looking fellow right here:

Divide matrix into submatrices python

Fig. 3.1. Test image. Photo by Anastasiia Tarasova on Unsplash

Divide matrix into submatrices python

Fig. 3.2. Image inspection. Image by Author.

After we explore our image’s dimensions, we can see they are divisible by 25 and 12 accordingly, so let’s just use these for our tile-size.

We’ll also use this decorator to time each execution:

Divide matrix into submatrices python

Fig. 3.3. Our timer decorator and dependencies. It will be used to time each approach. Image by Author.

Our for-loop implementation looks like this:

Divide matrix into submatrices python

Fig. 3.4. For-loop implementation function decorated to be timed. Image by Author.

The strides implementation is:

Divide matrix into submatrices python

Fig. 3.5. Strides implementation function decorated to be timed. Image by Author.

And the numpy.reshape() implementation is:

Divide matrix into submatrices python

Fig. 3.6. Numpy.reshape() implementation decorated to be timed. Image by Author.

We will load our image as a numpy array, set tile dimensions from command line, run each function and check for equality of arrays.

Divide matrix into submatrices python

Fig. 3.7. Our code including equality test. Image by Author.

The moment of truth:

Divide matrix into submatrices python

Fig. 3.8. Results. Numpy.reshape() is outperforming other approaches. Image by Author.

The numpy.reshape() approach appears to be the cleanest as well as the most efficient. Additionally, it is apparent that the for-loop implementation can’t really compare to the memory view methods. It’s also noteworthy that memory view performance is barely affected by the “amount” of tiles that must be generated, while the for-loop’s time of execution would blow up with an increasing workload.

Final note: There’s an additional step to bringing our tiles in a batch processing form as expected by most frameworks. To merge our tiles in a single index all we have to do is tiled_arr.reshape(-1, *tile_dimensions) and use numpy.moveaxis() to relocate the channels dimension where appropriate. Usually as (n_tiles,n_channels,t_height,t_width).

Thank you for reading!

I hope this article was insightful and you can implement what was demonstrated here in your own computer vision workflows!

Illustrations and formulas were created in LibreOffice.

Code: You can find the testing script here.

Feel free to contact me with any recommendations or corrections at

You can connect with me on LinkedIn.

How do you divide a matrix into Submatrices in Python?

hsplit() function. The hsplit() function is used to split an array into multiple sub-arrays horizontally (column-wise). hsplit is equivalent to split with axis=1, the array is always split along the second axis regardless of the array dimension. >>> import numpy as np >>> a = np.

How do you divide a matrix into a Submatrice?

c = mat2cell(x, m, n) divides the two-dimensional matrix x into adjacent submatrices, each contained in a cell of the returned cell array c . Vectors m and n specify the number of rows and columns, respectively, to be assigned to the submatrices in c .

How do you split a 1d array in Python?

Use the array_split() method, pass in the array you want to split and the number of splits you want to do.

How do you separate values from an array in Python?

To split a list into n parts in Python, use the numpy. array_split() function. The np. split() function splits the array into multiple sub-arrays.