Reputation: 13
I'm trying to achieve a similar goal to this question. The objective is to count the number of 1s with at least two direct neighbours (up, down, left, right), the only difference with my version is that there must be at least one cross or corner in the sequence.
For example,
array([[0, 0, 0, 0, 0, 1],
[0, 1,*1, 1, 0, 0],
[0, 0, 0, 0, 0, 0],
[1, 0, 0, 0, 0, 0],
[1, 1, 0, 1, 1, 0],
[1, 0, 0, 0, 1, 0]])
The starred *1 does not count as its neighbours do not form a corner or a cross. Nor does the top right 1 as it doesn't have two neighbours. The answer would be 2 (bottom right and left).
Here's what I tried:
import numpy as np
a = np.zeros((6,6), dtype=np.int)
a[0,5] = 1
a[1,1] = a[1,2] = a[1,3] = 1
a[4,4] = a[5,4] = a[4,3] = 1
a[3,0] = a[4,0] = a[5,0] = a[4,1] = 1
from scipy import ndimage
kernel = ndimage.generate_binary_structure(2, 1)
kernel[1, 1] = 0
b = convolve(a, kernel, mode="constant")
c = b[a>0]
# At least 2 neighbours
return len(c[c >= 2])
But it still counts the starred *1 from the second row (returns 3 instead of 2)!
Thanks for any help!
Upvotes: 1
Views: 158
Reputation: 53069
Your criterion can be restated "at least one neighbor horizontally and at least one neighbor vertically". So do whatever neighbor detection you'll end up choosing not in 2D but twice in 1D, once horizontally and once vertically:
For example, using scipy.ndimage
which provides convolve1d
:
import scipy.ndimage as sn
(sn.convolve1d(in_,(1,3,1),axis=0,mode="constant")>>2) & (sn.convolve1d(in_,(1,3,1),axis=1,mode="constant")>>2)
# 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],
# [1, 0, 0, 0, 1, 0],
# [0, 0, 0, 0, 0, 0]])
(in_
is the example array in OP.)
Upvotes: 2
Reputation: 591
You are attempting a 2d-convolution. You may want to try a "cross-shaped" kernel, which would result in a 3 or more (=1+2, 1 from the 1 at the center, 2 from the neighbors) for the relevant cells. signal.convolve2d
from scipy
accomplishes this, like so:
import numpy as np
from scipy import ndimage
from scipy import signal
a = np.zeros((6,6), dtype=np.int)
a[0,5] = 1
a[1,1] = a[1,2] = a[1,3] = 1
a[4,4] = a[5,4] = a[4,3] = 1
a[3,0] = a[4,0] = a[5,0] = a[4,1] = 1
print(a)
#kernel = ndimage.generate_binary_structure(2, 1)
#kernel[1, 1] = 0
kernel = np.array([[0,1,0], [1,1,1], [0,1,0]])
#b = np.convolve(a, kernel, mode="constant")
#c = b[a>0]
b = signal.convolve2d(a, kernel, mode='same')
# At least 2 neighbours
#return len(c[c >= 2])
print(b)
print(np.where(b > 2, b, 0))
which produces (I have filtered the results with np.where
):
[[0 0 0 0 0 1]
[0 1 1 1 0 0]
[0 0 0 0 0 0]
[1 0 0 0 0 0]
[1 1 0 1 1 0]
[1 0 0 0 1 0]]
[[0 1 1 1 1 1]
[1 2 3 2 1 1]
[1 1 1 1 0 0]
[2 2 0 1 1 0]
[4 2 2 2 3 1]
[2 2 0 2 2 1]]
[[0 0 0 0 0 0]
[0 0 3 0 0 0]
[0 0 0 0 0 0]
[0 0 0 0 0 0]
[4 0 0 0 3 0]
[0 0 0 0 0 0]]
The locations of the 1s have been correctly identified (3 or 4s).
Upvotes: 1