Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Matrix calculations on equidistant elements without for loops

I have 2D numpy arrays and a center with some coordinates (i, j) (coordinates mean row and column). I need to sum up all array elements at the same distance from the center (simple euklidean distance) for each possible distance from 0 to the edge of the image, i.e. result is an 1D array where the 0th element gives the sum of pixels in distance 0 from center (i.e. just the center), 1st element is the sum of all pixels at distance 1 pixel and so on.

My feeling is that this should be possible to do without a for loop, but unfortunately I don't know enough matrix python tricks to figure this out.

Thank you very much!

like image 471
user673592 Avatar asked Sep 18 '12 15:09

user673592


3 Answers

I'm not sure if this is what you want, but it may be helpful:

import numpy as np
#create data arrays to work with.
x = np.arange(10)
y = np.arange(10)
v = np.arange(100).reshape(10,10) 
#indices of the "center" of the grid
i,j = 5,5 

#Now calculated the distance of each point to the center point on the grid
#Note that `None` is the same as `np.newaxis`
r = np.sqrt((x[:,None] - x[i])**2 + (y[None,:]-y[j])**2)

#Now sum all the points that are closer than 10 units away.
np.sum(v[r<=10])
like image 174
mgilson Avatar answered Oct 15 '22 04:10

mgilson


You can use np.bincount...

a = np.random.random((20, 22))

def distance(array, xpos, ypos):
    # probably a gazillion methods to create the actual distances...
    # if you array is large and you are only interested to a certain size
    # you sould probably slice out a smaller one first of course.
    dists = np.sqrt(np.arange(-xpos, array.shape [0]-xpos, dtype=float)[:,None]**2
          + np.arange(-ypos, array.shape [1]-ypos, dtype=float)[None,:]**2)
    return dists

# Prepare which bins to use:
dists = distance(a, 10, 11).astype(int)

# Do a bincount with weights.
result = np.bincount(dists.flat, weights=a.flat)
# and add them up:
result = np.add.accumulate(result)

And result is an array with result[distance] giving sum over all values with smaller or equal distances.

like image 2
seberg Avatar answered Oct 15 '22 06:10

seberg


I'll suggest a strategy, but have no time now to suggest working code. Do the following: (note that you cannot chose an exact distance, but consecutive, continuous RANGES of distances, since very few points will be at a precise given distance).

  1. Create a KDTree from the array;
  2. Take the sum of every point INSIDE a given circle, for circles increasing linearly;
  3. Calculate the value for the "shell" between each circle by subtracting the inner circle from the outer circle.

It should be something like this (of course you'll have to fill some blanks):

import numpy
from scipy.spatial import KDTree

tree = scipy.spatial.KDTree(reshaped_array)
sums = [numpy.sum(tree.query_ball_point(dist)) for dist in xrange(desired_distances)]
desired_result = numpy.diff(sums)

Hope this helps!

like image 2
heltonbiker Avatar answered Oct 15 '22 05:10

heltonbiker