numpy conditional format of elements - python

I have a 3D numpy array and want to change a particular element based on a conditional test of another element. (The applications is to change the 'alpha' of a RGBA image array to play with the transparency in a 3D pyqtgraph image - should ideally be pretty fast).
a= np.ones((2,4,5),dtype=np.int) #create a ones array
a[0,0,0] = 3 #change a few values
a[0,2,0] = 3
a[1,2,0] = 3
print(a)
>>>[[[3 1 1 1 1]
[1 1 1 1 1]
[3 1 1 1 1]]
[[1 1 1 1 1]
[1 1 1 1 1]
[3 1 1 1 1]]]
Now I want to conditionally test the first element of the lowest dimension(??) and then change the last element based on the result.
if a[0,:,:] > 1: #this does not work - for example only - if first element > 1
a[3,:,:]=255 #then change the last element in the same dimension to 255
print(a) #this is my idealized result
>>>[[[3 1 1 1 255]
[1 1 1 1 1]
[3 1 1 1 255]]
[[1 1 1 1 1]
[1 1 1 1 1]
[3 1 1 1 255]]]

This seems to do the trick:
mask = a[:,:,0] > 1
a[:,:,4][mask] = 255
So the indexing just needed to be a little different and then it's just standard practice of applying a mask.
edit
#Ophion showed this is much better written as:
a[mask,:,-1] = 255

Related

I have a 2d array. Need to make a loop to replace first 2 rows, then next 2 only rows and so on by ones and print till the loop ends

b = np.random.randint(0,10, (6,3))
I tried this code, but it gives the `ValueError: operands could not be broadcast together with shapes (2,3) () (6,3)
step = 2
r1 = 0
r2 = 2
while r2 <= len(b):
c = np.where(b[r1:r2] >= 0, 1, b)
print(c)
r1+ = step
r2+ = step
I think the problem is in a condition of np.where. It creates an array wih a shape that is incompatible with b array
What i need is for the code to receive array b and to return 3 arrays of the same size of b but with two rows been substituted by 1´s. Like this:
[[1 1 1]
[1 1 1]
[6 3 4]
[2 9 3]
[6 9 2]
[8 1 0]]
[[3 2 8]
[3 8 5]
[1 1 1]
[1 1 1]
[6 9 2]
[8 1 0]]
[[3 2 8]
[3 8 5]
[6 3 4]
[2 9 3]
[1 1 1]
[1 1 1]]
My tutor told me to try it with 'np.where' function.But it seems that this function doesnt support this type of condition i´m trying to feed to it. May be there is another way to get the desired output. All examples I googled work with random values of the array and not precisely rows. In pandas it easier. But i need numpy code to feed the output to the neural network. The ones will be treated by it as an empty values, but the size of the array will be always the same, thus not producing errors
You are getting a ValueError because the size of b[0:2] is not the same as the size of b.
print(b.shape)
# (6, 3)
print(b[0:2].shape)
# (2, 3)
The documentation for numpy.where states that the way the condition works is "Where True, yield x, otherwise yield y." Thus, you need to be able to broadcast x and y onto the size of your condition. In your example, you can't broadcast (6,3) onto (2,3) and hence the error.
You need things to be the same size. For example, c = np.where(b[0:2] >= 0, 1, b[0:2]) would not give you an error.
However, if you want to step through your array b, then you need something other than b[0:2]. Otherwise it will just keep repeating that first part your array. I think you probably want b[r1:r2].
Also, I notice that you have r1+ = step instead of r1 += step, which will also spit out an error. Note that you don't actually need both r1 and r2 since their offset is step.
Putting all this together, we can adjust your code to give you something that works:
import numpy as np
b = np.random.randint(0,5, (6,3))
step = 2
r1 = 0
while r1 <= len(b) - step:
c = np.copy(b)
c[r1:r1+step] = np.where(b[r1:r1+step] >= 0, 1, b[r1:r1+step])
print(c)
r1 += step
Or you could instead do it with a for loop instead of a while loop:
import numpy as np
b = np.random.randint(0,5, (6,3))
step = 2
for r1 in range(0, len(b), step):
c = np.copy(b)
c[r1:r1+step] = np.where(b[r1:r1+step] >= 0, 1, b[r1:r1+step])
print(c)
Resulting output:
[[1 1 1]
[1 1 1]
[3 2 2]
[1 1 2]
[3 3 0]
[3 2 2]]
[[4 0 2]
[4 0 0]
[1 1 1]
[1 1 1]
[3 3 0]
[3 2 2]]
[[4 0 2]
[4 0 0]
[3 2 2]
[1 1 2]
[1 1 1]
[1 1 1]]

how to form a cross pattern of one between zeros using numpy

i want a good method to insert the one inside zeros in a cross pattern with ones on top rows and bottom rows.
import numpy as np
a = np.zeros((n,n), dtype=int)
a[0,:] = 1
a[-1,:] = 1
for i in range(1,n):
a[i,-i-1] = 1
print(a)
Output:
[[1 1 1]
[0 1 0]
[1 1 1]]
You can use np.eye and reverse the rows, then assign with slices:
a = np.eye(n, dtype=int)[::-1]
a[[0,-1]] = 1
print(a)
Output:
[[1 1 1 1]
[0 0 1 0]
[0 1 0 0]
[1 1 1 1]]

using 1's in the arrays to make a shape like '+' using numpy in python

Numpy Three Four Five Dimensional Array in Python
Input 1: 3
Output 1:
[[0 1 0]
[1 1 1]
[0 1 0]]
Input 2:5
Output 1:
[[0 0 1 0 0]
[0 0 1 0 0]
[1 1 1 1 1]
[0 0 1 0 0]
[0 0 1 0 0]]
Notice that the 1s in the arrays make a shape like +.
My logic is shown below
a=np.zeros((n,n),dtype='int')
a[-3,:] = 1
a[:,-3] = 1 print(a)
This logic is only working for five dimensional array but not for three dimensional array.
can someone assist me to get the expected output for both three and five dimensional array using np.zeros & integer division //
As you can see, n//2 = 3 when n=5. So, that's the solution to your question as see here:
import numpy as np
def create_plus_matrix(n):
a = np.zeros((n,n),dtype='int')
a[-n//2,:] = 1
a[:,-n//2] = 1
return a
So, let's try it out:
>>> create_plus_matrix(3)
[[0 1 0]
[1 1 1]
[0 1 0]]
>> create_plus_matrix(5)
[[0 0 1 0 0]
[0 0 1 0 0]
[1 1 1 1 1]
[0 0 1 0 0]
[0 0 1 0 0]]
Do this
import numpy as np
def plus(size):
a = np.zeros([size,size], dtype = int)
a[int(size/2)] = np.ones(size)
for i in a:
i[int(size/2)] = 1
return a
print(plus(3)) //3 is the size
//Output
[[0 1 0]
[1 1 1]
[0 1 0]]

How to find numpy array shape in a larger array?

big_array = np.array((
[0,1,0,0,1,0,0,1],
[0,1,0,0,0,0,0,0],
[0,1,0,0,1,0,0,0],
[0,0,0,0,1,0,0,0],
[1,0,0,0,1,0,0,0]))
print(big_array)
[[0 1 0 0 1 0 0 1]
[0 1 0 0 0 0 0 0]
[0 1 0 0 1 0 0 0]
[0 0 0 0 1 0 0 0]
[1 0 0 0 1 0 0 0]]
Is there a way to iterate over this numpy array and for each 2x2 cluster of 0s, set all values within that cluster = 5? This is what the output would look like.
[[0 1 5 5 1 5 5 1]
[0 1 5 5 0 5 5 0]
[0 1 5 5 1 5 5 0]
[0 0 5 5 1 5 5 0]
[1 0 5 5 1 5 5 0]]
My thoughts are to use advanced indexing to set the 2x2 shape = to 5, but I think it would be really slow to simply iterate like:
1) check if array[x][y] is 0
2) check if adjacent array elements are 0
3) if all elements are 0, set all those values to 5.
big_array = [1, 7, 0, 0, 3]
i = 0
p = 0
while i <= len(big_array) - 1 and p <= len(big_array) - 2:
if big_array[i] == big_array[p + 1]:
big_array[i] = 5
big_array[p + 1] = 5
print(big_array)
i = i + 1
p = p + 1
Output:
[1, 7, 5, 5, 3]
It is a example, not whole correct code.
Here's a solution by viewing the array as blocks.
First you need to define this function rolling_window from here https://gist.github.com/seberg/3866040/revisions
Then break the array big, your starting array, into 2x2 blocks using this function.
Also generate an array which has indices of every element in big and break it similarly into 2x2 blocks.
Then generate a boolean mask where the 2x2 blocks of big are all zero, and use the index array to get those elements.
blks = rolling_window(big,window=(2,2)) # 2x2 blocks of original array
inds = np.indices(big.shape).transpose(1,2,0) # array of indices into big
blkinds = rolling_window(inds,window=(2,2,0)).transpose(0,1,4,3,2) # 2x2 blocks of indices into big
mask = blks == np.zeros((2,2)) # generate a mask of every 2x2 block which is all zero
mask = mask.reshape(*mask.shape[:-2],-1).all(-1) # still generating the mask
# now blks[mask] is every block which is zero..
# but you actually want the original indices in the array 'big' instead
inds = blkinds[mask].reshape(-1,2).T # indices into big where elements need replacing
big[inds[0],inds[1]] = 5 #reassign
You need to test this: I did not. But the idea is to break the array into blocks, and an array of indices into blocks, then develop a boolean condition on the blocks, use those to get the indices, and then reassign.
An alternative would be to iterate through indblks as defined here, then test the 2x2 obtained from big at each indblk element and reassign if necessary.
This is my attempt to help you solve your problem. My solution may be subject to fair criticism.
import numpy as np
from itertools import product
m = np.array((
[0,1,0,0,1,0,0,1],
[0,1,0,0,0,0,0,0],
[0,1,0,0,1,0,0,0],
[0,0,0,0,1,0,0,0],
[1,0,0,0,1,0,0,0]))
h = 2
w = 2
rr, cc = tuple(d + 1 - q for d, q in zip(m.shape, (h, w)))
slices = [(slice(r, r + h), slice(c, c + w))
for r, c in product(range(rr), range(cc))
if not m[r:r + h, c:c + w].any()]
for s in slices:
m[s] = 5
print(m)
[[0 1 5 5 1 5 5 1]
[0 1 5 5 0 5 5 5]
[0 1 5 5 1 5 5 5]
[0 5 5 5 1 5 5 5]
[1 5 5 5 1 5 5 5]]

OpenCV FloodFill with multiple seeds

Is there a floodFill function for python/openCV that takes a list of seeds and starts changing the color of its neighbours? I know that simplecv as a function like that SimpleCV floodFill. OpenCV says it has two floodFill functions when that uses a mask and another one that doesn't, documentation, I'm not being able to use the opencv floodfill function without a mask and with a list of seeds. Any help?
This is what I'm trying to do so far:
A=array([[0,1,1,0],[0,0,0,0],[1,1,1,1],[1,1,1,1]],np.uint8)
mask = np.ones((A.shape[0]+2,A.shape[0]+2),np.uint8)
mask[1:-1,1:-1] = np.zeros((A.shape))
cv.floodFill(A, mask, (3,0), 0,0,0, flags=4|cv.FLOODFILL_MASK_ONLY)
print mask
returned mask:
[[1 1 1 1 1 1]
[1 1 0 0 1 1]
[1 1 1 1 1 1]
[1 0 0 0 0 1]
[1 0 0 0 0 1]
[1 1 1 1 1 1]]
Expected mask:
[[1 1 1 1 1 1]
[1 0 0 0 0 1]
[1 0 0 0 0 1]
[1 1 1 1 1 1]
[1 1 1 1 1 1]
[1 1 1 1 1 1]]
Original Image:
[[0 1 1 0]
[0 0 0 0]
[1 1 1 1]
[1 1 1 1]]
If you look closely at the documentation, that's one of the purpose of mask. You can call multiple times the function (2nd version) every time with a different seed, and at the end mask will contain the area that has been floodfilled. If a new seed belongs to an area already floodfilled, your function call will return immediately.
Use the FLOODFILL_MASK_ONLY flag, and then use this mask to paint your input image with the desidered filling color at the end with a setTo() (You'll have to use a subimage of Mask! Removing first and last row and column). Note that your floodfill might produce different results depending on the order you process your seed points if you set loDiff or upDiff to something different than the default value zero.
Take also a look at this.

Categories