byronV999
byronV999

Reputation: 53

Setting a surrounding block based on center index in a numpy array

I have a 2D numpy array of zeros representing some flat surface:

field = np.zeros((10,10))

field
Out[165]: 
array([[0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
       [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
       [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
       [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
       [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
       [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
       [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
       [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
       [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
       [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.]])

Then I have an array of coordinates in the form [row,column] such as:

In [166]:c = np.array([[1,2],[4,5],[7,3],[2,6]])

In [167]:c
Out[167]: 
array([[1, 2],
       [4, 5],
       [7, 3],
       [2, 6]])

What I would like to do is populate a block of the field array around the coordinates in c.

Out[192]: 
array([[0., 1., 1., 1., 0., 0., 0., 0., 0., 0.],
       [0., 1., 1., 1., 0., 1., 1., 1., 0., 0.],
       [0., 1., 1., 1., 0., 1., 1., 1., 0., 0.],
       [0., 0., 0., 0., 1., 1., 1., 1., 0., 0.],
       [0., 0., 0., 0., 1., 1., 1., 0., 0., 0.],
       [0., 0., 0., 0., 1., 1., 1., 0., 0., 0.],
       [0., 0., 1., 1., 1., 0., 0., 0., 0., 0.],
       [0., 0., 1., 1., 1., 0., 0., 0., 0., 0.],
       [0., 0., 1., 1., 1., 0., 0., 0., 0., 0.],
       [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.]])

My initial attempt using numpy vectorization was:

In [168]:field[c[:,0]-1:c[:,0]+1,c[:,1]-1:c[:,1]+1] = 10
Traceback (most recent call last):

  File "<ipython-input-168-5433a2f4a5cf>", line 1, in <module>
    field[c[:,0]-1:c[:,0]+1,c[:,1]-1:c[:,1]+1] = 10

TypeError: only integer scalar arrays can be converted to a scalar index

I then tried first creating the c[:,0]-1 and c[:,1]+1 arrays before hand but got the same error which leads me to the conclusion that, that type of ranged indexing cannot be done in numpy.

I have also had a look at the np.ix_() but cannot set surrounding blocks of multiple coordinates without a for loop using this method.

I am able to achieve the desired output however using the for loop:

for row,column in c:
        field[row-1:row+2,column-1:column+2] = 1

But would not like to use a for loop because both c and f will be large and multidimensional in my final application and I feel I can capitalize on the speed improvements made by numpy vectorization.

Also, I know in image processing this could be seen as a dilation or erosion problem but I already have the coordinates for an erosion/dilation kernel to be placed at and again, multiple dimensions and very large arrays.

Upvotes: 2

Views: 451

Answers (1)

John Zwinck
John Zwinck

Reputation: 249153

Here's a simple way which only has a small amount of Python looping and a lot of vectorized work:

x, y = c[:,0], c[:,1]
for i in -1,0,1:
    for j in -1,0,1:
        field[x+i,y+j] = 1

A more complicated way which may be faster:

offsets = np.array([[-1,-1],[-1,0],[-1,1], [0,-1],[0,0],[0,1], [1,-1],[1,0],[1,1]])
fill = (offsets + c[:,None]).reshape(-1,2)
field[fill[:,0], fill[:,1]] = 1

Upvotes: 2

Related Questions