How to calculate all 24 rotations of 3d array?

后端 未结 9 741
清酒与你
清酒与你 2021-02-12 10:45

I have a 3d numpy array describing a polycube (imagine a 3d tetris piece). How can I calculate all 24 rotations?

Numpy\'s array manipulation routines includes a rot90 me

9条回答
  •  迷失自我
    2021-02-12 11:24

    We would start off with the intention of getting all 48 combinations so that we get the general idea about solving it for a n-dim array. Later on we would filter out the unwanted 24 ones.

    Generic idea to solve for all rotations

    The idea to solve for a generic case would be to basically do two things - flip along every axis and permute axes with all combinations for the given number of axes.

    Flip : To flip, we would use the stepsize parameter for slicing, i.e. array[::stepsize]. So, to flip, it would be : [::-1] and without flipping, simply : [::1]. That stepsize could be assigned as a variable varying between 1 and -1 for the two combinations simpl. For a ndarray, simply extend this to all axes.

    Permute axes : To achieve this, we can use np.transpose and specify the required permuted order as the axes parameter with it. We will generate all possible orders with itertools.permutations.

    That's all there is! Let's implement it with a as the input 3D array -

    import itertools
    
    def rotations48(a):
        # Get all combinations of axes that are permutable
        n = a.ndim
        axcomb = np.array(list(itertools.permutations(range(n), n)))
        
        # Initialize output array
        out = np.zeros((6,2,2,2,) + a.shape,dtype=a.dtype)
        
        # Run loop through all axes for flipping and permuting each axis
        for i,ax in enumerate(axcomb):
            for j,fx in enumerate([1,-1]):
                for k,fy in enumerate([1,-1]):
                    for l,fz in enumerate([1,-1]):
                        out[i,j,k,l] = np.transpose(a[::fx,::fy,::fz],ax) 
        return out
    

    We could simplify for the flipping nested loops with one loop -

    def rotations48(a):
        n = a.ndim
        axcomb = list(itertools.permutations(range(n), n)) # all axes combinations    
        pcomb = list(itertools.product([1,-1], repeat=n)) # all permuted orders
        out = np.zeros((6,8,) + a.shape,dtype=a.dtype) # Initialize output array    
        for i,ax in enumerate(axcomb): #loop through all axes for permuting
            for j,(fx,fy,fz) in enumerate(pcomb): # all flipping combinations
                out[i,j] = np.transpose(a[::fx,::fy,::fz],ax) 
        return out
    

    So, this gets us all the 48 combinations.

    Extend to more dimensions : If we want to extend this to a 4D array, simply edit the initialization part to extend by 2 and slice along one more axis.


    Solve for 24 rotations

    Now, as OP is claiming to have a working solution to get the desired 24 combinations, we need to filter out from our proposed solution. I couldn't find a generic pattern for the filtering, but got the indices required for indexing -

    idx = np.array([ 0,  3,  5,  6,  9, 10, 12, 15, 17, 18, 20, 23, 24, \
                    27, 29, 30, 32, 35, 37, 38, 41, 42, 44, 47])
    

    If you care about the order to have the output same output as with rotations24, we would have -

    idx = np.array([ 0, 10,  3,  9,  5, 15,  6, 12, 41, 27, 42, 24, 44, \
                    30, 47, 29, 18, 35, 17, 32, 20, 37, 23, 38])
    

    Hence, get the required 24 ones with indexing -

    final_out = out.reshape(48,-1)[idx]
    

    This works for 3D arrays with any generic lengths.

    Sample run for verification

    # From https://stackoverflow.com/a/33190472/ @Colonel Panic
    def rotations24_array(a):
        out0 = np.zeros((6,2,2,2,) + a.shape,dtype=a.dtype)    
        p = [list(i) for i in rotations24(a)]
        out0 = np.zeros((6,4,m,m,m),dtype=a.dtype)
        for i in range(6):
            for j in range(4):
                out0[i,j] = p[i][j]   
        return out0
    

    Verify -

    In [486]: # Setup    
         ...: np.random.seed(0)
         ...: m = 3
         ...: a = np.random.randint(11,99,(m,m,m))
         ...: 
         ...: # Verify results
         ...: idx = np.array([ 0, 10,  3,  9,  5, 15,  6, 12, 41, 27, 42, 24, 44, \
         ...:                 30, 47, 29, 18, 35, 17, 32, 20, 37, 23, 38])
         ...: out1 = rotations24_array(a).reshape(-1,m**3)
         ...: out2 = rotations48(a).reshape(48,-1)[idx]
         ...: print np.allclose(out1, out2)
    True
    

提交回复
热议问题