I have a numpy array like this:
x = np.array([[1,2,3],[4,5,6],[7,8,9]])
I need to create a function let's call it "neighbors" with the following input parameter:
As output I want to get the neighbors of the cell i,j
with a given distance d
.
So if I run
neighbors(im, i, j, d=1) with i = 1 and j = 1 (element value = 5)
I should get the indices of the following values: [1,2,3,4,6,7,8,9]
. I hope I make it clear.
Is there any library like scipy which deal with this?
I've done something working but it's a rough solution.
def pixel_neighbours(self, p):
rows, cols = self.im.shape
i, j = p[0], p[1]
rmin = i - 1 if i - 1 >= 0 else 0
rmax = i + 1 if i + 1 < rows else i
cmin = j - 1 if j - 1 >= 0 else 0
cmax = j + 1 if j + 1 < cols else j
neighbours = []
for x in xrange(rmin, rmax + 1):
for y in xrange(cmin, cmax + 1):
neighbours.append([x, y])
neighbours.remove([p[0], p[1]])
return neighbours
How can I improve this?
Have a look at scipy.ndimage.generic_filter
.
As an example:
import numpy as np
import scipy.ndimage as ndimage
def test_func(values):
print(values)
return values.sum()
x = np.array([[1,2,3],[4,5,6],[7,8,9]])
footprint = np.array([[1,1,1],
[1,0,1],
[1,1,1]])
results = ndimage.generic_filter(x, test_func, footprint=footprint)
By default, it will "reflect" the values at the boundaries. You can control this with the mode
keyword argument.
However, if you're wanting to do something like this, there's a good chance that you can express your problem as a convolution of some sort. If so, it will be much faster to break it down into convolutional steps and use more optimized functions (e.g. most of scipy.ndimage
).
EDIT: ah crap, my answer is just writing im[i-d:i+d+1, j-d:j+d+1].flatten()
but written in a incomprehensible way :)
The good old sliding window trick may help here:
import numpy as np
from numpy.lib.stride_tricks import as_strided
def sliding_window(arr, window_size):
""" Construct a sliding window view of the array"""
arr = np.asarray(arr)
window_size = int(window_size)
if arr.ndim != 2:
raise ValueError("need 2-D input")
if not (window_size > 0):
raise ValueError("need a positive window size")
shape = (arr.shape[0] - window_size + 1,
arr.shape[1] - window_size + 1,
window_size, window_size)
if shape[0] <= 0:
shape = (1, shape[1], arr.shape[0], shape[3])
if shape[1] <= 0:
shape = (shape[0], 1, shape[2], arr.shape[1])
strides = (arr.shape[1]*arr.itemsize, arr.itemsize,
arr.shape[1]*arr.itemsize, arr.itemsize)
return as_strided(arr, shape=shape, strides=strides)
def cell_neighbors(arr, i, j, d):
"""Return d-th neighbors of cell (i, j)"""
w = sliding_window(arr, 2*d+1)
ix = np.clip(i - d, 0, w.shape[0]-1)
jx = np.clip(j - d, 0, w.shape[1]-1)
i0 = max(0, i - d - ix)
j0 = max(0, j - d - jx)
i1 = w.shape[2] - max(0, d - i + ix)
j1 = w.shape[3] - max(0, d - j + jx)
return w[ix, jx][i0:i1,j0:j1].ravel()
x = np.arange(8*8).reshape(8, 8)
print x
for d in [1, 2]:
for p in [(0,0), (0,1), (6,6), (8,8)]:
print "-- d=%d, %r" % (d, p)
print cell_neighbors(x, p[0], p[1], d=d)
Didn't do any timings here, but it's possible this version has reasonable performance.
For more info, search the net with phrases "rolling window numpy" or "sliding window numpy".
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With