How to perform max / average union in 2d array using numpy

Given a 2D matrix (M x N) and a two-dimensional core (K x L), how to return a matrix that is the result of a maximum or average pool using a given core above the image?

I would use numpy if possible.

Note: M, N, K, L can be even or odd, and they do not have to be shared with each other, for example: a 7x5 matrix and a 2x2 core.

for example, the maximum union:

matrix: array([[ 20, 200, -5, 23], [ -13, 134, 119, 100], [ 120, 32, 49, 25], [-120, 12, 09, 23]]) kernel: 2 x 2 soln: array([[ 200, 119], [ 120, 49]]) 
+7
python arrays numpy matrix max-pooling
source share
3 answers

You can use scikit-image block_reduce :

 import numpy as np import skimage.measure a = np.array([ [ 20, 200, -5, 23], [ -13, 134, 119, 100], [ 120, 32, 49, 25], [-120, 12, 9, 23] ]) skimage.measure.block_reduce(a, (2,2), np.max) 

gives:

 array([[200, 119], [120, 49]]) 
+12
source share

If the image size is evenly divided by the kernel size, you can resize the array and use max or mean as you wish

 import numpy as np mat = np.array([[ 20, 200, -5, 23], [ -13, 134, 119, 100], [ 120, 32, 49, 25], [-120, 12, 9, 23]]) M, N = mat.shape K = 2 L = 2 MK = M // K NL = N // L print(mat[:MK*K, :NL*L].reshape(MK, K, NL, L).max(axis=(1, 3))) # [[200, 119], [120, 49]] 

If you do not have an even number of cores, you will have to handle the borders separately. (As indicated in the comments, this leads to a copy of the matrix, which will affect performance).

 mat = np.array([[20, 200, -5, 23, 7], [-13, 134, 119, 100, 8], [120, 32, 49, 25, 12], [-120, 12, 9, 23, 15], [-57, 84, 19, 17, 82], ]) # soln # [200, 119, 8] # [120, 49, 15] # [84, 19, 82] M, N = mat.shape K = 2 L = 2 MK = M // K NL = N // L # split the matrix into 'quadrants' Q1 = mat[:MK * K, :NL * L].reshape(MK, K, NL, L).max(axis=(1, 3)) Q2 = mat[MK * K:, :NL * L].reshape(-1, NL, L).max(axis=2) Q3 = mat[:MK * K, NL * L:].reshape(MK, K, -1).max(axis=1) Q4 = mat[MK * K:, NL * L:].max() # compose the individual quadrants into one new matrix soln = np.vstack([np.c_[Q1, Q3], np.c_[Q2, Q4]]) print(soln) # [[200 119 8] # [120 49 15] # [ 84 19 82]] 
+4
source share

Instead of making “quadrants,” as Elliott’s answer showed, we could pad it to make it evenly divisible, and then execute either max or middle pool.

Because combining in CNN is often used, the input array is usually 3D. Thus, I created a function that works on two-dimensional or three-dimensional arrays.

 def pooling(mat,ksize,method='max',pad=False): '''Non-overlapping pooling on 2D or 3D data. <mat>: ndarray, input array to pool. <ksize>: tuple of 2, kernel size in (ky, kx). <method>: str, 'max for max-pooling, 'mean' for mean-pooling. <pad>: bool, pad <mat> or not. If no pad, output has size n//f, n being <mat> size, f being kernel size. if pad, output has size ceil(n/f). Return <result>: pooled matrix. ''' m, n = mat.shape[:2] ky,kx=ksize _ceil=lambda x,y: int(numpy.ceil(x/float(y))) if pad: ny=_ceil(m,ky) nx=_ceil(n,kx) size=(ny*ky, nx*kx)+mat.shape[2:] mat_pad=numpy.full(size,numpy.nan) mat_pad[:m,:n,...]=mat else: ny=m//ky nx=n//kx mat_pad=mat[:ny*ky, :nx*kx, ...] new_shape=(ny,ky,nx,kx)+mat.shape[2:] if method=='max': result=numpy.nanmax(mat_pad.reshape(new_shape),axis=(1,3)) else: result=numpy.nanmean(mat_pad.reshape(new_shape),axis=(1,3)) return result 

Sometimes you may need to execute an overlapping pool in increments not equal to the size of the kernel. Here is a function that does this with or without adding:

 def asStride(arr,sub_shape,stride): '''Get a strided sub-matrices view of an ndarray. See also skimage.util.shape.view_as_windows() ''' s0,s1=arr.strides[:2] m1,n1=arr.shape[:2] m2,n2=sub_shape view_shape=(1+(m1-m2)//stride[0],1+(n1-n2)//stride[1],m2,n2)+arr.shape[2:] strides=(stride[0]*s0,stride[1]*s1,s0,s1)+arr.strides[2:] subs=numpy.lib.stride_tricks.as_strided(arr,view_shape,strides=strides) return subs def poolingOverlap(mat,ksize,stride=None,method='max',pad=False): '''Overlapping pooling on 2D or 3D data. <mat>: ndarray, input array to pool. <ksize>: tuple of 2, kernel size in (ky, kx). <stride>: tuple of 2 or None, stride of pooling window. If None, same as <ksize> (non-overlapping pooling). <method>: str, 'max for max-pooling, 'mean' for mean-pooling. <pad>: bool, pad <mat> or not. If no pad, output has size (nf)//s+1, n being <mat> size, f being kernel size, s stride. if pad, output has size ceil(n/s). Return <result>: pooled matrix. ''' m, n = mat.shape[:2] ky,kx=ksize if stride is None: stride=(ky,kx) sy,sx=stride _ceil=lambda x,y: int(numpy.ceil(x/float(y))) if pad: ny=_ceil(m,sy) nx=_ceil(n,sx) size=((ny-1)*sy+ky, (nx-1)*sx+kx) + mat.shape[2:] mat_pad=numpy.full(size,numpy.nan) mat_pad[:m,:n,...]=mat else: mat_pad=mat[:(m-ky)//sy*sy+ky, :(n-kx)//sx*sx+kx, ...] view=asStride(mat_pad,ksize,stride) if method=='max': result=numpy.nanmax(view,axis=(2,3)) else: result=numpy.nanmean(view,axis=(2,3)) return result 
0
source share

All Articles