Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Pixel neighbors in 2d array (image) using Python

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:

  • x: a numpy 2d array
  • (i,j): the index of an element in a 2d array
  • d: neighborhood radius

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?

like image 256
blueSurfer Avatar asked Jun 12 '12 12:06

blueSurfer


2 Answers

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).

like image 65
Joe Kington Avatar answered Oct 17 '22 06:10

Joe Kington


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".

like image 40
pv. Avatar answered Oct 17 '22 06:10

pv.