Search code examples
pythonnumpy

How to use values in a 3D Python numpy array to change values in a 2D numpy array using 2D array values as the extra dimension index?


Say I have a 3D (dimension order = zyx) numpy array of objects where each z dimension represents a set of 2D objects and the first z position is all zeros (reason for this will be explained soon):

objects = np.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, 0, 0, 0, 0, 0, 0],
                     [0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
                     [0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
                     [0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
                     [0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
                     [0, 0, 0, 0, 0, 0, 0, 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, 1, 0, 0, 0, 0, 0, 0, 0],
                     [0, 1, 1, 0, 0, 0, 0, 0, 0, 0],
                     [0, 1, 1, 0, 0, 0, 3, 3, 0, 0],
                     [0, 1, 1, 0, 0, 0, 3, 3, 0, 0],
                     [0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
                     [0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
                     [0, 0, 2, 2, 2, 0, 0, 0, 0, 0],
                     [0, 0, 2, 2, 2, 0, 0, 0, 0, 0],
                     [0, 0, 0, 2, 2, 0, 0, 0, 0, 0],
                     [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]],
                    [[0, 4, 4, 0, 0, 0, 0, 0, 0, 0],
                     [0, 4, 4, 4, 0, 0, 0, 0, 0, 0],
                     [0, 4, 4, 4, 0, 0, 6, 0, 0, 0],
                     [0, 4, 4, 0, 0, 0, 6, 0, 0, 0],
                     [0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
                     [0, 0, 0, 5, 5, 0, 0, 0, 0, 0],
                     [0, 0, 5, 5, 5, 0, 0, 0, 0, 0],
                     [0, 0, 5, 5, 5, 0, 0, 0, 0, 0],
                     [0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
                     [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]]])

I also have another 3D array of the same shape where each value is related to the probability of each pixel belonging to the corresponding object at the same coordinates. For simplicity I used values of 1 and 2 (2 is higher probability than 1):

probs = np.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, 0, 0, 0, 0, 0, 0],
                   [0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
                   [0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
                   [0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
                   [0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
                   [0, 0, 0, 0, 0, 0, 0, 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, 1, 0, 0, 0, 0, 0, 0, 0],
                   [0, 1, 1, 0, 0, 0, 0, 0, 0, 0],
                   [0, 1, 1, 0, 0, 0, 1, 1, 0, 0],
                   [0, 1, 1, 0, 0, 0, 1, 1, 0, 0],
                   [0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
                   [0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
                   [0, 0, 2, 2, 2, 0, 0, 0, 0, 0],
                   [0, 0, 2, 2, 2, 0, 0, 0, 0, 0],
                   [0, 0, 0, 2, 2, 0, 0, 0, 0, 0],
                   [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]],
                  [[0, 2, 2, 0, 0, 0, 0, 0, 0, 0],
                   [0, 2, 2, 2, 0, 0, 0, 0, 0, 0], 
                   [0, 2, 2, 2, 0, 0, 2, 0, 0, 0],
                   [0, 2, 2, 0, 0, 0, 2, 0, 0, 0],
                   [0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
                   [0, 0, 0, 1, 1, 0, 0, 0, 0, 0],
                   [0, 0, 1, 1, 1, 0, 0, 0, 0, 0],
                   [0, 0, 1, 1, 1, 0, 0, 0, 0, 0],
                   [0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
                   [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]]])

From the probs array, I can calculate the z position with the highest probability value for each xy position using:

max_chs = np.argmax(probs, axis=0)

array([[0, 2, 2, 0, 0, 0, 0, 0, 0, 0],
       [0, 2, 2, 2, 0, 0, 0, 0, 0, 0],
       [0, 2, 2, 2, 0, 0, 2, 1, 0, 0],
       [0, 2, 2, 0, 0, 0, 2, 1, 0, 0],
       [0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
       [0, 0, 0, 2, 2, 0, 0, 0, 0, 0],
       [0, 0, 1, 1, 1, 0, 0, 0, 0, 0],
       [0, 0, 1, 1, 1, 0, 0, 0, 0, 0],
       [0, 0, 0, 1, 1, 0, 0, 0, 0, 0],
       [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]])

(The reason objects[0] and probs[0] are all zeros is so when this calculation is done we're able to distinguish between the background coordinates which have value 0 and the coordinates where the first z position has the maximum value which would also be zero otherwise).

What I want is a 2D array where each value is pulled from the objects array where the z coordinate is chosen based on the max_chs array value. So for example, max_chs[0, 1] = 2 so the [0, 1] coordinate of the result should equal objects[2, 0, 1] and in general result[x, y] = objects[max_chs[x, y], x, y] This should be the final result:

result = np.array([[0, 4, 4, 0, 0, 0, 0, 0, 0, 0],
                   [0, 4, 4, 4, 0, 0, 0, 0, 0, 0],
                   [0, 4, 4, 4, 0, 0, 6, 3, 0, 0],
                   [0, 4, 4, 0, 0, 0, 6, 3, 0, 0],
                   [0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
                   [0, 0, 0, 5, 5, 0, 0, 0, 0, 0],
                   [0, 0, 2, 2, 2, 0, 0, 0, 0, 0],
                   [0, 0, 2, 2, 2, 0, 0, 0, 0, 0],
                   [0, 0, 0, 2, 2, 0, 0, 0, 0, 0],
                   [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]])

result array

Is there a fast/efficient numpy method that could be used to accomplish this? The real data is much larger.


Solution

  • You can use argmax(), arange() and meshgrid():

    import numpy as np
    
    
    def _modify(objects, probs):
        max_chs = np.argmax(probs, axis=0)
        x, y = np.arange(max_chs.shape[0]), np.arange(max_chs.shape[1])
        xx, yy = np.meshgrid(x, y, indexing='ij')
        return objects[max_chs, xx, yy]
    
    
    objects = np.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, 0, 0, 0, 0, 0, 0],
                         [0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
                         [0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
                         [0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
                         [0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
                         [0, 0, 0, 0, 0, 0, 0, 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, 1, 0, 0, 0, 0, 0, 0, 0],
                         [0, 1, 1, 0, 0, 0, 0, 0, 0, 0],
                         [0, 1, 1, 0, 0, 0, 3, 3, 0, 0],
                         [0, 1, 1, 0, 0, 0, 3, 3, 0, 0],
                         [0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
                         [0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
                         [0, 0, 2, 2, 2, 0, 0, 0, 0, 0],
                         [0, 0, 2, 2, 2, 0, 0, 0, 0, 0],
                         [0, 0, 0, 2, 2, 0, 0, 0, 0, 0],
                         [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]],
                        [[0, 4, 4, 0, 0, 0, 0, 0, 0, 0],
                         [0, 4, 4, 4, 0, 0, 0, 0, 0, 0],
                         [0, 4, 4, 4, 0, 0, 6, 0, 0, 0],
                         [0, 4, 4, 0, 0, 0, 6, 0, 0, 0],
                         [0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
                         [0, 0, 0, 5, 5, 0, 0, 0, 0, 0],
                         [0, 0, 5, 5, 5, 0, 0, 0, 0, 0],
                         [0, 0, 5, 5, 5, 0, 0, 0, 0, 0],
                         [0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
                         [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]]])
    
    probs = np.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, 0, 0, 0, 0, 0, 0],
                       [0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
                       [0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
                       [0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
                       [0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
                       [0, 0, 0, 0, 0, 0, 0, 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, 1, 0, 0, 0, 0, 0, 0, 0],
                       [0, 1, 1, 0, 0, 0, 0, 0, 0, 0],
                       [0, 1, 1, 0, 0, 0, 1, 1, 0, 0],
                       [0, 1, 1, 0, 0, 0, 1, 1, 0, 0],
                       [0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
                       [0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
                       [0, 0, 2, 2, 2, 0, 0, 0, 0, 0],
                       [0, 0, 2, 2, 2, 0, 0, 0, 0, 0],
                       [0, 0, 0, 2, 2, 0, 0, 0, 0, 0],
                       [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]],
                      [[0, 2, 2, 0, 0, 0, 0, 0, 0, 0],
                       [0, 2, 2, 2, 0, 0, 0, 0, 0, 0],
                       [0, 2, 2, 2, 0, 0, 2, 0, 0, 0],
                       [0, 2, 2, 0, 0, 0, 2, 0, 0, 0],
                       [0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
                       [0, 0, 0, 1, 1, 0, 0, 0, 0, 0],
                       [0, 0, 1, 1, 1, 0, 0, 0, 0, 0],
                       [0, 0, 1, 1, 1, 0, 0, 0, 0, 0],
                       [0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
                       [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]]])
    
    
    print(_modify(objects, probs))
    
    

    Prints

    [[0 4 4 0 0 0 0 0 0 0]
     [0 4 4 4 0 0 0 0 0 0]
     [0 4 4 4 0 0 6 3 0 0]
     [0 4 4 0 0 0 6 3 0 0]
     [0 0 0 0 0 0 0 0 0 0]
     [0 0 0 5 5 0 0 0 0 0]
     [0 0 2 2 2 0 0 0 0 0]
     [0 0 2 2 2 0 0 0 0 0]
     [0 0 0 2 2 0 0 0 0 0]
     [0 0 0 0 0 0 0 0 0 0]]
    

    Comment

    • Rather than np.aranges + meshgrid, there is conveniently xx, yy = np.indices(max_chs.shape, sparse=True)! – Chrysophylaxs
    import numpy as np
    
    def _modify(objects, probs):
        max_chs = np.argmax(probs, axis=0)
        xx, yy = np.indices(max_chs.shape, sparse=True)
        return objects[max_chs, xx, yy]