Home > Blockchain >  np.add is giving me a headache. How do I use the where option in np.add?
np.add is giving me a headache. How do I use the where option in np.add?

Time:04-26

I have two arrays that are loaded from images, I need to add these two images, I.e. if there's a circle of pixel value 1 in the center, I need to add it with a triangle of pixel value 2 in the top left corner. The rule I want to set is that if the 1 is already in that index, it will only add pixel value 2 to the pixels that are blank (pixel value is 0)

How do I do that? I keep trying with np.add and the where option

mask_test = master_array == 0
master_array = np.add(master_array, new_pic, where = mask_test)

But it keeps screwing up and master_array just ends up being new_pic instead of the sum. Online searches of how 'where' works has been fruitless because everyone doesn't give an example, some even just go "oh it's not used much so I won't go over it".

This code:

master_array = np.add(master_array, new_pic, where = mask_test)

gives me this:

enter image description here

But the problem is when the pixels do overlap I get a pixel value of 3 instead of it retaining the value of 1 as it should.

CodePudding user response:

As explained in the docs, the out array will retain its "original value" in cases where the condition in the where parameter is false. This implies that you need to specify an out array to which the function will output if you are going to set the where parameter. Otherwise the function tries to get the original values from an uninitialized array, which has odd results. If you're happy to overwrite master_array, you can do that like this:

np.add(master_array, new_pic, out=master_array, where=master_array == 0)

(You don't need to assign the returned value here - specifying the output array is sufficient.)

It is probably less of a headache to use with np.where instead:

master_array  = np.where(master_array == 0, new_pic, 0)

But since you are only adding in cases where pixel value is 0 in the master, there is no need to add in the first place. You could just use np.where without any addition.

master_array = np.where(master_array == 0, new_pic, master_array)

CodePudding user response:

Use of where in np.add (or other ufunc) is not common - especially compared to the use of the np.where function. And at least when I answered SO, I stress the out needs to be included.

The docs talk about "uninitialized" values when out=None, the default. That may be unclear, but effectively it means, an array such as that created by np.empty.

This may contain anything, such as:

In [263]: res = np.empty((5,5),int)
In [264]: res
Out[264]: 
array([[            50999536,                    0,      140274438367024,
        -6315338160082163841,      140273540789184],
       [                 161,             55839504,      140274448227440,
             140273575343728,   358094631352936090],
       [     140273564120384,      140273575343344, -7783537013977118542,
             140273543024256,      140273575343200],
       [-6522034781934541837,      140273620247296,      140273575343776,
         1387433780369843801,      140273560270848],
       [     140273561761968, -3190833100527581043,      140273563628672,
             140273561762640,                  480]])

Define an initial array:

In [265]: x1 = np.random.randint(0,5,(5,5))
In [266]: x1
Out[266]: 
array([[3, 2, 0, 1, 3],
       [3, 2, 4, 0, 3],
       [2, 3, 3, 4, 3],
       [3, 2, 0, 2, 2],
       [1, 2, 1, 1, 2]])
In [267]: x2=x1.copy()

Without out, we get values much like res above. Only the x1==0 elements are set to 10:

In [268]: np.add(x1, 10, where=x1==0)
Out[268]: 
array([[51108864,        0,       10, 47780512, 51193856],
       [51213024, 51245760, 51252528,       10, 51260336],
       [51261168, 51261920, 51264176, 51298864, 51270656],
       [51271040, 51274864,       10, 51276640, 51277024],
       [51277808, 51278528, 51279104, 51284496, 51286448]])

Or we could set the out to np.zeros:

In [269]: np.add(x1, 10, where=x1==0, out=np.zeros((5,5),int))
Out[269]: 
array([[ 0,  0, 10,  0,  0],
       [ 0,  0,  0, 10,  0],
       [ 0,  0,  0,  0,  0],
       [ 0,  0, 10,  0,  0],
       [ 0,  0,  0,  0,  0]])

But if we set it to x1, or a copy of x1 (which is probably what you want):

In [270]: np.add(x1, 10, where=x1==0, out=x2)
Out[270]: 
array([[ 3,  2, 10,  1,  3],
       [ 3,  2,  4, 10,  3],
       [ 2,  3,  3,  4,  3],
       [ 3,  2, 10,  2,  2],
       [ 1,  2,  1,  1,  2]])

But we could do the same with masked addition:

In [271]: x1[x1==0]  = 10
In [272]: x1
Out[272]: 
array([[ 3,  2, 10,  1,  3],
       [ 3,  2,  4, 10,  3],
       [ 2,  3,  3,  4,  3],
       [ 3,  2, 10,  2,  2],
       [ 1,  2,  1,  1,  2]])

Or using the more commonly use np.where function:

In [273]: np.where(x1==10, 20, x1)
Out[273]: 
array([[ 3,  2, 20,  1,  3],
       [ 3,  2,  4, 20,  3],
       [ 2,  3,  3,  4,  3],
       [ 3,  2, 20,  2,  2],
       [ 1,  2,  1,  1,  2]])

In my experience with SO, the where/out is most useful when evaluation at certain values can give rise to errors, for example division by 0, or log of negatives.

In np.where(A,B,C), the 3 arguments are evaluated in full, and result just selects from B and C based on A. With the np.add(x, y, where=A, out=C), the x y addition is only done where the condition is true. The evaluation is selective. The distinction may be hard to grasp, and may not matter when using np.add.

CodePudding user response:

You could simply use the normal addition:

mask_test = master_array == 0
master_array  = new_pic * mask_test
  • Related