2

I have two numpy ndarrays of the same shape (15081, 56724, 3, 3). What I want to do is as follows:

Say we have a cross section of the first array, array1[1, 1, :, :], looks like this:

[[120, 110, 220], [ 85, 99, 72], [197, 80, 75]] 

I want to convert it to a boolean in a way that the max of each row is True and the rest is False. In the whole array, this corresponds to axis=3. So the array looks like this after conversion:

[[False, False, True], [False, True, False], [ True, False, False]] 

Now I want to filter the other array, array2, using this boolean array to have something that looks like below. I only want to keeping those values of array2 that correspond to True in array1 and set the rest to zero.

[[ 0, 0, 65], [ 0, 179, 0], [125, 0, 0]] 

I can do this using a loop but it takes an age (even more). I expect something like numpy.where(array1.is_max(axis=3), True, False), but there is no function like is_max in python, besides this way the axis 3 is collapsed and I cannot filter array2 using array1.

    2 Answers 2

    3

    In numpy, is_max is approximately argmax:

    indices = array1.argmax(axis=-1) 

    Instead of a mask, this will give you a linear index, so you can do

    array2b = np.zeros_like(array2) index = np.indices(array2.shape[:-1], sparse=True) + (indices,) array2b[index] = array1[index] 

    You can do something similar with np.take_along_axis and np.put_along_axis:

    indices = np.expand_dims(array1.argmax(-1), array1.ndim - 1) array2b = np.zeros_like(array2) np.put_along_axis(array2b, indices, np.take_along_axis(array2, indices, -1), -1) 

    If you want a mask-based approach, you can create the mask like this:

    mask = array1 != array1.max(-1, keepdims=True) 

    Now you can set all elements to zero directly:

    array2[mask] = 0 

    Alternatively you can do something like

    mask = array1 == array1.max(-1, keepdims=True) array2 *= mask 

    Update

    From your description in the comments, you are looking for a different operation entirely. You can start by thresholding array1 (which I assume represents the difference between the blurred and original image):

    mask = array1 >= 100 # find a threshold that works array2 *= mask 

    OR

    mask = array1 < 100 array2[mask] = 0 

    You may also be looking for local minima in an image. You can get those by finding pixels that are bigger than their surroundings. To do that, run a non-linear filter on the image, like scipy.ndimage.maximum_filter:

     mask = array1 == scipy.ndimage.maximum_filter(array1, 5) array2 *= mask 
    6
    • Thanks for your solutions, they worked perfectly but my original problem (not mentioned here) remained unsolved. I wanna do focus stacking of 3 large z-stack layers. I already tried available functions and using freeware from inside python. But either the quality of output was bad or they took an age. What I tried here was to calculate the blurred pixels of each layer and find the difference with the original layers. The larger the difference, the sharper the original pixel. Then as you suggested I create mask from the diff matrix and .... But there are many missing pixels in output image.CommentedAug 12, 2021 at 19:56
    • @SinaALizadeh. You should ask a separate question about that. It's not something I can answer without seeing concrete data, and certainly not within the scope of a comment. I would recommend finding a small region that encapsulates a missing pixel, and trying to isolate which steps cause a discrepancy. Ping me if you end up with a question, although I suspect you will end up solving your issue once you inspect the data more closely.CommentedAug 12, 2021 at 20:07
    • @SinaALizadeh. Also, don't forget to accept the answer so your question can be removed from the unanswered queue.CommentedAug 12, 2021 at 20:09
    • @SinaALizadeh. Now that I think about it, you probably want to threshold the data, not search for the maxima. Your mask should be array1 > 100 or so, not arrayy == array1.max(-1, keepdims=True). I've updated the answer.CommentedAug 12, 2021 at 20:11
    • Thanks for helping me. Here is where I asked somehow my problem. There is a link to original data stackoverflow.com/questions/68188852/…CommentedAug 12, 2021 at 20:50
    0

    Can be done with

    array2[array1 != np.tile(np.max(array1,axis=1), (array1.shape[0],1)).T] = 0 

    Here is how.

    For your particular example, i would suggest to use np.tile, where you can create a repeating pattern out of the elements in your 1-D mask.

    Suppose we have your matrix a with values

    array1 = [[120, 110, 220] [85, 99, 72] [197, 80, 75]] 

    Then the maximum values in every row of your matrix would be

    maxes = np.max(array1,axis=1) 

    which will have the values

    array([220, 99, 197]) 

    now, what we need is to use each of these values in each row per each row element. Simply put, we need to compare 220 with each element in the first row. This means that actually we need to repeat the value 220, to be able to perform the operation in a single vector, which can be done with:

    np.tile(maxes, (array1.shape[0],1)).T 

    which gives us

    array([[220, 220, 220], [ 99, 99, 99], [197, 197, 197]]) 

    filtering is rather straightforward:

    a != np.tile(maxes, (3,1)).T 

    is sufficient to create the mask.

    Long story short, the operation can be done in a single liner as following:

    array2[array1 != np.tile(maxes, (a.shape[0],1)).T] = 0 

    which enables a to have following values

    array2 = array([[0, 0, 65] [0, 179, 0] [125, 0, 0]]) 
    2
    • You don't need to tile since broadcasting does that for you more efficientlyCommentedAug 12, 2021 at 14:56
    • I see. I will read your answer more carefully then.CommentedAug 12, 2021 at 15:36

    Start asking to get answers

    Find the answer to your question by asking.

    Ask question

    Explore related questions

    See similar questions with these tags.