NOTE: tf.image.non_max_suppression does NOT do what I'm looking for!
I'm trying to perform non-maximum suppression (NMS) similar to the Canny edge detector. Specifically, NMS on an 2D array will keep a value if it is the maximum within a window, otherwise suppress it (set to 0).
For example, consider the matrix
[[3 2 1 4 2 3]
[1 4 2 1 5 2]
[2 2 3 2 1 3]]
If we consider a window size of 3 x 3, then the result should be
[[0 0 0 0 0 0]
[0 4 0 0 5 0]
[0 0 0 0 0 0]]
I've searched around and couldn't find anything that performs this operation in tf.image and tf.nn. Is there code somewhere that performs NMS? If not, how can I efficiently implement NMS in Tensorflow (Python)?
Thanks!
EDIT: I came up with one way to solve this but I'm not sure if there are better ways: take a max pool with 1 stride (i.e. no downsampling) and the window size, then use tf.where to check if the value is equal to the max pooled value and set to 0 if not. Is there a better way?
Answering my own question (though open to better solutions):
import tensorflow as tf
import numpy as np
def non_max_suppression(input, window_size):
# input: B x W x H x C
pooled = tf.nn.max_pool(input, ksize=[1, window_size, window_size, 1], strides=[1,1,1,1], padding='SAME')
output = tf.where(tf.equal(input, pooled), input, tf.zeros_like(input))
# NOTE: if input has negative values, the suppressed values can be higher than original
return output # output: B X W X H x C
sess = tf.InteractiveSession()
x = np.array([[3,2,1,4,2,3],[1,4,2,1,5,2],[2,2,3,2,1,3]], dtype=np.float32).reshape([1,3,6,1])
inp = tf.Variable(x)
out = non_max_suppression(inp, 3)
sess.run(tf.global_variables_initializer())
print out.eval().reshape([3,6])
'''
[[ 0. 0. 0. 0. 0. 0.]
[ 0. 4. 0. 0. 5. 0.]
[ 0. 0. 0. 0. 0. 0.]]
'''
sess.close()
Related
Python: get all possible array attributions of nd arrays. Use itertools.product?
If so, how?
In Python, I have two n dimensions numpy arrays A and B (B is a zero array).
Such way A.shape[i]<=B.shape[i], for any i between 0 and n.
I want to create a for loop in such way every iteration I attribute A to a different subset of B, in such way every possible position in occupied until the end of the for loop.
for instance, with A = np.array([[1,1,1],[1,1,1]]) and B = np.zeros((3,4)), I would get these(one of these for each iteration):
1 1 1 0 0 1 1 1 0 0 0 0 0 0 0 0
1 1 1 0 0 1 1 1 1 1 1 0 0 1 1 1
0 0 0 0 0 0 0 0 1 1 1 0 0 1 1 1
For a fixed n dimension it is trivial, just use nested for loops for each dimension.
However, I want it for a generic n dimensions.
My approach was to use the itertools.product to get all combinations of indexes.
In the above example, product([0,1],[0,1]), would iterate over (0,0),(0,1),(1,0),(1,1), and I would have my indexes.
However, I don't know how to pass the values of the parameters to product function for a generic n.
Any idea? There are better ways of doing so?
itertools product should work.
import numpy as np
from itertools import product
A = np.ones((2,3))
B = np.zeros((3,4))
r_rng = range(B.shape[0]-A.shape[0]+1)
c_rng = range(B.shape[1]-A.shape[1]+1)
for i,j in product(r_rng, c_rng):
C = B.copy()
C[i:i+A.shape[0],j:j+A.shape[1]]=A
print(C,'\n')
Output:
[[1. 1. 1. 0.]
[1. 1. 1. 0.]
[0. 0. 0. 0.]]
[[0. 1. 1. 1.]
[0. 1. 1. 1.]
[0. 0. 0. 0.]]
[[0. 0. 0. 0.]
[1. 1. 1. 0.]
[1. 1. 1. 0.]]
[[0. 0. 0. 0.]
[0. 1. 1. 1.]
[0. 1. 1. 1.]]
Here is an example. You can use the * operator to unpack a variable number of argument from a list and give it to itertools.product():
import itertools
size1 = (3,5,6)
size2 = (2,2,2)
N = len(size1)
coords = []
for i in range(N):
delta = size1[i]-size2[i]
coords.append(list(range(delta)))
print(coords)
it = itertools.product(*coords)
arr = np.array(list(it))
print(arr)
Output:
[[0 0 0]
[0 0 1]
[0 0 2]
[0 0 3]
[0 1 0]
[0 1 1]
[0 1 2]
[0 1 3]
[0 2 0]
[0 2 1]
[0 2 2]
[0 2 3]]
Im going to post the solution I obtained:
import numpy as np
from itertools import product
A=np.ones((2,3,2))
B=np.zeros((3,4,4))
coords=[]
for i in range(len(B.shape)):
delta = B.shape[i]-A.shape[i]+1
coords.append(list(range(delta)))
print(coords)
for start_idx in product(*coords):
idx=tuple(slice(start_idx[i], start_idx[i]+A.shape[i]) for i in range(len(A.shape)))
m=np.zeros(B.shape)
m.__setitem__(tuple(idx), A)
print(m)
ps: Indexing the nd arrays was very tricky
import numpy as np
x = np.ones((5,5))
print(x)
x[1:-1,1:-1] = 0
print(x)
I am getting the output as shown below:
[[1. 1. 1. 1. 1.]
[1. 0. 0. 0. 1.]
[1. 0. 0. 0. 1.]
[1. 0. 0. 0. 1.]
[1. 1. 1. 1. 1.]]
You can do it using astype, setting it to int:
print(x.astype(int))
Result:
[[1 1 1 1 1]
[1 0 0 0 1]
[1 0 0 0 1]
[1 0 0 0 1]
[1 1 1 1 1]]
I think you refer to 1. When you see a dot sign, you know that that number is float type.
If you don't want floats, you should cast your list to integer:
x.astype(int)
Other things you should do in python console to understand things a little:
print(type(1))
print(type(1.))
print(x.dtype)
print(x.astype(int).dtype)
I am trying to execute the following code:
def calculate_squared_dist_sliced_data(self, data, output, proc_numb):
for k in range(1, self.calc_border):
print("Calculating",k, "of", self.calc_border, "\n", (self.calc_border - k), "to go!")
kmeans = KMeansClusterer.KMeansClusterer(k, data)
print("inertia in round", k, ": ", kmeans.calc_custom_params(data, k).inertia_)
output.put( proc_numb, (kmeans.calc_custom_params(self.data, k).inertia_))
def calculate_squared_dist_mp(self):
length = np.shape(self.data)[0]
df_array = []
df_array[0] = self.data[int(length/4), :]
df_array[1] = self.data[int((length/4)+1):int(length/2), :]
df_array[2] = self.data[int((length/2)+1):int(3*length/4), :]
df_array[3] = self.data[int((3*length/4)+1):int(length/4), :]
output = mp.Queue()
processes = [mp.Process(target=self.calculate_squared_dist_sliced_data, args=(df_array[x], output, x)) for x in range(4)]
for p in processes:
p.start()
for p in processes:
p.join()
results = [output.get() for p in processes]
When executing df_array[0] = self.data[int(length/4), :], I get the following error:
IndexError: list assignment index out of range
The variable lentgh has the value 20195 (which is correct). I want to do the method calculate_squared_dist_sliced_data by multiprocessing, so I need to split the array data that is passed to this class.
Here is an example of how this numpy array looks:
[[ 0. 0. 0.02072968 ..., -0.07872599 -0.10147049 -0.44589 ]
[ 0. -0.11091352 0.11208243 ..., 0.08164318 -0.02754813
-0.44921876]
[ 0. -0.10642599 0.0028097 ..., 0.1185457 -0.22482443
-0.25121125]
...,
[ 0. 0. 0. ..., -0.03617197 0.00921685 0. ]
[ 0. 0. 0. ..., -0.08241634 -0.05494423
-0.10988845]
[ 0. 0. 0. ..., -0.03010139 -0.0925091
-0.02145017]]
Now I want to split this hole array into four equal pieces to give each one to a process. However, when selecting the rows I get the exception mentioned above. Can someone help me?
Maybe for a more theroretical approach of what I want to do:
A B C D
1 2 3 4
5 6 7 8
9 5 4 3
1 8 4 3
As a result I want to have for example two arrays, each containing two rows:
A B C D
1 2 3 4
5 6 7 8
and
A B C D
9 5 4 3
1 8 4 3
Can someone help me?
The left side of the assignment is not allowed as you list has length 0.
Either fix it to:
df_array = [None, None, None, None]
or use
df_array.append(self.data[int(length/4), :])
...
instead.
I just noticed that I tried to use a list like an array...
I am trying to create an n-by-m matrix of 0s and 1s with a very simple structure:
[[1 0 0 0 0 0 0 ...],
[1 1 0 0 0 0 0 ...],
[1 1 1 0 0 0 0 ...],
[1 1 1 1 0 0 0 ...],
[0 1 1 1 1 0 0 ...],
[0 1 1 1 1 1 0 ...],
...
[... 0 0 0 1 1 1 1],
[... 0 0 0 0 1 1 1],
[... 0 0 0 0 0 1 1],
[... 0 0 0 0 0 0 1]]
However, I don't want to start writing loops as this is probably achievable using something built in: A = tf.constant(???,shape(n,m))
Note that after the first 3 rows there is simply a repetition of four 1s, followed by m-3 0s, until the last 3 rows.
So I am thinking something along the lines of a repeat of repeat, but I have no idea what syntax to use.
You're looking for tf.matrix_band_part(). As per the manual, it's function is to
Copy a tensor setting everything outside a central band in each innermost matrix to zero.
So in your case you'd create a matrix with ones, and then take a 4-wide band like this:
tf.matrix_band_part( tf.ones( shape = ( 1, n, m ) ), 3, 0 )
Tested code:
import tensorflow as tf
x = tf.ones( shape = ( 1, 9, 6 ) )
y = tf.matrix_band_part( x, 3, 0 )
with tf.Session() as sess:
res = sess.run( y )
print ( res )
Output:
[[[1. 0. 0. 0. 0. 0.]
[1. 1. 0. 0. 0. 0.]
[1. 1. 1. 0. 0. 0.]
[1. 1. 1. 1. 0. 0.]
[0. 1. 1. 1. 1. 0.]
[0. 0. 1. 1. 1. 1.]
[0. 0. 0. 1. 1. 1.]
[0. 0. 0. 0. 1. 1.]
[0. 0. 0. 0. 0. 1.]]]
this is a followup question arising from this solution.
The solution to count adjacent cells works pretty well unless you have multiple patches in the array.
So this time the array for instance looks like this.
import numpy
from scipy import ndimage
s = ndimage.generate_binary_structure(2,2)
a = numpy.zeros((6,6), dtype=numpy.int) # example array
a[1:3, 1:3] = 1;a[2:4,4:5] = 1
print a
[0 0 0 0 0 0]
[0 1 1 0 0 0]
[0 1 1 0 1 0]
[0 0 0 0 1 0]
[0 0 0 0 0 0]
[0 0 0 0 0 0]
# Number of nonoverlapping cells
c = ndimage.binary_dilation(a,s).astype(a.dtype)
b = c - a
numpy.sum(b) # returns 19
# However the correct number of non overlapping cells should be 22 (12+10)
Is there any smart solution to solve this dilemma without using any loops or iterating through the array? The reason is that the array could be quite big.
idea 1:
Just thought over it and a way to do it might be to check for more than one patch in the iterating structure. For the total count number to be correct those cells below have to be equal 2 (or more) in the dilation. Anyone got any idea how to turn this thought into code?
[1 1 1 1 0 0]
[1 0 0 2 1 1]
[1 0 0 2 0 1]
[1 1 1 2 0 1]
[0 0 0 1 1 1]
[0 0 0 0 0 0]
You can use label from ndimage to segment each patch of ones.
Then you just ask where the returned array equals 1, 2, 3 etc and perform your algoritm on it (or you just use the ndimage.distance_transform_cdt but with inverting your forground/background for each labeled segment.
Edit 1:
This code will take your array a and do what you ask:
b, c = ndimage.label(a)
e = numpy.zeros(a.shape)
for i in xrange(c):
e += ndimage.distance_transform_cdt((b == i + 1) == 0) == 1
print e
I realize it is a bit ugly with all the equals there but it outputs:
In [41]: print e
[[ 1. 1. 1. 1. 0. 0.]
[ 1. 0. 0. 2. 1. 1.]
[ 1. 0. 0. 2. 0. 1.]
[ 1. 1. 1. 2. 0. 1.]
[ 0. 0. 0. 1. 1. 1.]
[ 0. 0. 0. 0. 0. 0.]]
Edit 2 (Alternative solution):
This code should do the same stuff and hopefully faster (however it will not find the where
two patches only touch corners).
b = ndimage.binary_closing(a) - a
b = ndimage.binary_dilation(b.astype(bool))
c = ndimage.distance_transform_cdt(a == 0) == 1
e = c.astype(numpy.int) * b + c
print e