user1179317
user1179317

Reputation: 2903

Fill in a section out of bounds in a numpy array

Not entirely sure how I should title this question. But how can I get a section of a numpy array that will have some of the values outside the boundary?

See my simple example code below:

def get_section(a, center, square_radius):
    return a[center[0] - square_radius:center[0] + square_radius + 1, \
              center[1] - square_radius:center[1] + square_radius + 1]



array = [
    [1, 2, 3, 4, 5, 6, 7, 8, 9],
    [2, 3, 4, 5, 6, 7, 8, 9, 1],
    [3, 4, 5, 0, 7, 8, 9, 1, 2],
    [4, 5, 6, 7, 8, 9, 1, 2, 3],
    [5, 0, 7, 8, 9, 4, 5, 6, 7],
    [6, 7, 8, 9, 1, 2, 3, 4, 5]
]
a = np.asarray(array)
square_radius = 2


center = [2,3]
print(get_section(a, center, square_radius))

center = [4,1]
print(get_section(a, center, square_radius))

Say I have an array, but I would like to get a small section of it, by specifying the center and the radius of the section I'd like.

The first one will print out:

[[2 3 4 5 6]
 [3 4 5 6 7]
 [4 5 0 7 8]
 [5 6 7 8 9]
 [0 7 8 9 4]]

Which is exactly what I wanted. For simplicity, I placed '0' on the center of the 2 examples identified.

But the second one will print out []

For the second one I'd like to fill in the outside values with -1. Thus I'd like it to return:

[[-1  3  4  5  0]
 [-1  4  5  6  7]
 [-1  5  0  7  8]
 [-1  6  7  8  9]
 [-1 -1 -1 -1 -1]]

How can I do this in numpy?

Upvotes: 2

Views: 512

Answers (1)

Joe Iddon
Joe Iddon

Reputation: 20414

Finally! I've done it using np.pad. I'm not sure if there is a better way of doing this as it ended up being quite complicated, nevertheless, it works fine:

def get_section(a, center, square_radius):
    tp = max(0, -(center[0] - square_radius))
    bp = max(0, -((a.shape[0]-center[0]-1) - square_radius))
    lp = max(0, -(center[1] - square_radius))
    rp = max(0, -((a.shape[1]-center[1]-1) - square_radius))
    a = np.pad(a, [[tp, bp], [lp, rp]], 'constant', constant_values=-1)
    return a[center[0] - square_radius + tp:center[0] + square_radius + 1 + tp, \
              center[1] - square_radius + lp:center[1] + square_radius + 1 + lp]

and testing with your examples:

>>> center = [2,3]
>>> print(get_section(a, center, square_radius))
[[2 3 4 5 6]
 [3 4 5 6 7]
 [4 5 0 7 8]
 [5 6 7 8 9]
 [0 7 8 9 4]]
>>> center = [4,1]
>>> print(get_section(a, center, square_radius))
[[-1  3  4  5  0]
 [-1  4  5  6  7]
 [-1  5  0  7  8]
 [-1  6  7  8  9]
 [-1 -1 -1 -1 -1]]

Why?

First, let's define an array to test the np.pad() function:

>>> a
array([[1, 2],
       [3, 4]])

And then we can do a quick demonstration of how the padding works, it should be fairly self-explanatory from this example:

>>> np.pad(a, [[1, 2], [0, 3]], 'constant', constant_values=-1)
array([[-1, -1, -1, -1, -1],
       [ 1,  2, -1, -1, -1],
       [ 3,  4, -1, -1, -1],
       [-1, -1, -1, -1, -1],
       [-1, -1, -1, -1, -1]])

So we now know that we can add the -1s to whatever edge we want, we now just need to work out if we do (the square overlaps the edge as in the second example) and, if so, how many -1s we need to add to each edge.

These padding distances are defined at the top of the function (tp, bp, ... for top pad, bottom pad... ) which we work out by doing some calculations.

The calculations are basically just taking the distance between their edge and the centre and subtracting the square_radius. However, if we left it at this then we would often get negative padding distances (in the case where the radius was less than the distance to the edge, so to get around this, we just use the max() function with 0 to make the padding distance only positive (padding is required) or 0 (padding is not required on that edge).

Then, with all these defined, we just call the pad function on a with these values.

Finally, we use your original 'square extraction' technique to get the square around the centre coordinate. The only difference is that we need to offset all the indexes here by the top pads and left pads. We need to do this as if we have just padded say a left pad of 3, then what would originally have been a centre of say 4 would now be a centre of 1 (as the indexing starts from the padding edge). So to get around this, we just offset the indexes back by adding the padding differences onto all the indexes.

Hopefully you've got it now!

Upvotes: 1

Related Questions