2

I am trying to compute a local maxima filter on a matrix, using a circular kernel. The output should be the cells that are local maximas. For each pixel in the input 'data', I need to see if it is a local maximum by a circular window, thus returning a value of 1, otherwise 0.

I have this code, built upon answers from here: How to apply a disc shaped mask to a numpy array?

import numpy as np
import scipy.ndimage as sc

radius = 2
kernel = np.zeros((2*radius+1, 2*radius+1))
y,x = np.ogrid[-radius:radius+1, -radius:radius+1]
mask2 = x**2 + y**2 <= radius**2
kernel[mask2] = 1

def local_maxima(matrix, window_size):
    loc_max = sc.maximum_filter(matrix, window_size, mode='constant')
    return loc_max


data = np.array([(1, 1, 1, 1, 1, 1, 1, 1, 1), (1, 1, 1, 1, 1, 1, 1, 1, 1), (1, 1, 1, 1, 1, 1, 1, 1, 1),
                 (1, 1, 1, 1, 1, 1, 1, 1, 1), (1, 1, 1, 1, 4, 1, 1, 1, 1), (1, 1, 1, 1, 1, 1, 1, 1, 1),
                 (1, 1, 1, 1, 1, 1, 1, 1, 1), (1, 1, 1, 1, 1, 1, 1, 1, 1), (1, 1, 1, 1, 1, 1, 1, 1, 1),
                 (1, 1, 1, 1, 1, 1, 1, 1, 1)])

loc_max = sc.filters.generic_filter(data, local_maxima(data, np.shape(kernel)), footprint=kernel)
max_matrix = np.where(loc_max == data, 1, 0)
np.savetxt('.....\Local\Test_Local_Max.txt', max_matrix, delimiter='\t')

The kernel has this shape:

[[ 0.  0.  1.  0.  0.]
 [ 0.  1.  1.  1.  0.]
 [ 1.  1.  1.  1.  1.]
 [ 0.  1.  1.  1.  0.]
 [ 0.  0.  1.  0.  0.]]

So the search cells will be only the ones that have value 1. The cells with 0 should be excluded from the local maxima search.

But the script gives the error below on line 21:

RuntimeError: function parameter is not callable

Thanks for any help!

Community
  • 1
  • 1
Litwos
  • 1,278
  • 4
  • 19
  • 44
  • The problem is that the function you give to `sc.filters.generic_filter` is applied to each element of the input array you give: `data`, which is not possible as the function local_maxima takes an array as argument and not a float or integer... I don't know exactly the goal of your code but why don't you just apply that: `loc_max = sc.maximum_filter(data, kernel.shape, mode='constant', footprint=kernel)` – bougui Sep 01 '16 at 13:39
  • Because the footprint kernel should only take the positions that are 1 in the kernel matrix, not the ones with 0 also. – Litwos Sep 01 '16 at 14:10

2 Answers2

2

The second parameter of sc.filters.generic_filter() should be a function, you are passing it the value returned by the local_maxima(data, np.shape(kernel)) call, i.e. a matrix.

I'm a bit confused as to what exactly you have done here, but I think you do not need the generic_filter call at all, maximum_filter should do what you want:

import numpy as np
import scipy.ndimage as sc

radius = 2
kernel = np.zeros((2*radius+1, 2*radius+1))
y,x = np.ogrid[-radius:radius+1, -radius:radius+1]
mask2 = x**2 + y**2 <= radius**2
kernel[mask2] = 1

data = np.array([(1, 1, 1, 1, 1, 1, 1, 1, 1), 
                 (1, 1, 1, 1, 1, 1, 1, 1, 1),  
                 (1, 1, 1, 1, 1, 1, 1, 1, 1),
                 (1, 1, 1, 1, 1, 1, 1, 1, 1),  
                 (1, 1, 1, 1, 4, 1, 1, 1, 1),  
                 (1, 1, 1, 1, 1, 1, 1, 1, 1),
                 (1, 1, 1, 1, 1, 1, 1, 1, 1),  
                 (1, 1, 1, 1, 1, 1, 1, 1, 1),  
                 (1, 1, 1, 1, 1, 1, 1, 1, 1),
                 (1, 1, 1, 1, 1, 1, 1, 1, 1)])

loc_max = sc.maximum_filter(data, footprint=kernel, mode='constant')
max_matrix = np.where(loc_max == data, 1, 0)
np.savetxt('.....\Local\Test_Local_Max.txt', max_matrix, delimiter='\t')

(I do not have python installed on this computer so I have not tested this out, sorry)

Edit: I've tested it and it seems to give the correct result:

[[1, 1, 1, 1, 1, 1, 1, 1, 1],
 [1, 1, 1, 1, 1, 1, 1, 1, 1],
 [1, 1, 1, 1, 0, 1, 1, 1, 1],
 [1, 1, 1, 0, 0, 0, 1, 1, 1],
 [1, 1, 0, 0, 1, 0, 0, 1, 1],
 [1, 1, 1, 0, 0, 0, 1, 1, 1],
 [1, 1, 1, 1, 0, 1, 1, 1, 1],
 [1, 1, 1, 1, 1, 1, 1, 1, 1],
 [1, 1, 1, 1, 1, 1, 1, 1, 1],
 [1, 1, 1, 1, 1, 1, 1, 1, 1]]
pseudoDust
  • 1,336
  • 1
  • 10
  • 18
  • It runs, but it's not the result I expect. For each pixel in the input 'data', I need to see if it is a local maximum by a circular window, thus returning a value of 1, otherwise 0. The output from your answer is doesn't add 0 when the cell is not a local maxima. – Litwos Sep 01 '16 at 14:16
  • I've now tested it, seems to be doing what you want, I'll add the result I'm getting to the answer – pseudoDust Sep 02 '16 at 10:49
2

You can use the code below that return 1 if the cell visited is a local maximum by a circular window defined by kernel (I just used %pylab to plot the results as an illustration):

%pylab
import scipy.ndimage as sc
data = np.array([(1, 1, 1, 1, 1, 1, 1, 1, 1), (1, 1, 1, 1, 1, 1, 1, 1, 1), (1, 1, 1, 1, 1, 1, 1, 1, 1),
                 (1, 1, 1, 1, 1, 1, 1, 1, 1), (1, 1, 1, 1, 4, 1, 1, 1, 1), (1, 1, 1, 1, 1, 1, 1, 1, 1),
                 (1, 1, 1, 1, 1, 1, 1, 1, 1), (1, 1, 1, 1, 1, 1, 1, 1, 1), (1, 1, 1, 1, 1, 1, 1, 1, 1),
                 (1, 1, 1, 1, 1, 1, 1, 1, 1)])
matshow(data)
colorbar()

data

radius = 2
kernel = np.zeros((2*radius+1, 2*radius+1))
y,x = np.ogrid[-radius:radius+1, -radius:radius+1]
mask2 = x**2 + y**2 <= radius**2
kernel[mask2] = 1
matshow(kernel)
colorbar()

kernel

def filter_func(a):
    return a[len(a)/2] == a.max()
out = sc.generic_filter(data, filter_func, footprint=kernel)
matshow(out)
colorbar()

output

Below is the result with a random input data array:

data = np.random.random(size=data.shape)
matshow(data)

random array

out = sc.generic_filter(data, filter_func, footprint=kernel)
matshow(out)
colorbar()

output on random array

bougui
  • 3,507
  • 4
  • 22
  • 27
  • Thanks for the answer. It is what I needed. Nice plots! :) – Litwos Sep 02 '16 at 11:20
  • I' don't know which answer to accept. pseudoDust's answer was first, but yours is more easy to follow... – Litwos Sep 02 '16 at 11:26
  • Always select the better answers (and not the ones who were first), so that people who later look this up see the better answer first. In this case I agree that @bougui 's answer is better :) – pseudoDust Sep 02 '16 at 14:57