Uso eficiente de múltiples numpy slices para recorte de imagen aleatorio

Tengo una matriz numpy 4-D, con la primera dimensión que representa el número de imágenes en un conjunto de datos, la segunda y la tercera son la anchura y la altura (iguales) y la cuarta la cantidad de canales (3). Por ejemplo, digamos que tengo 4 imágenes en color que son 28 * 28, por lo que mis datos de imagen se ven así:

X = np.reshape(np.arange(4*28*28*3), (4,28,28,3)) 

Me gustaría seleccionar un recorte aleatorio de 16 * 16 de ancho x alto de cada una de las 4 imágenes. Críticamente, quiero que el recorte sea diferente por imagen, es decir, quiero generar 4 pares aleatorios (x_offset, y_offset). Al final quiero acceder a un conjunto de formas (4, 16, 16, 3).

Si tuviera que escribir esto en un bucle for, se vería algo así:

 x = np.random.randint(0,12,4) y = np.random.randint(0,12,4) for i in range(X.shape[0]): cropped_image = X[i, x[i]:x[i]+16, y[i]:y[i]+16, :] #Add cropped image to a list or something 

Pero me gustaría hacerlo de la manera más eficiente posible y me pregunto si hay una manera de hacerlo con zancadas e indexación elegante. He visto las respuestas a esta pregunta, pero no puedo comprender cómo podría combinar algo como stride_tricks con puntos de inicio aleatorios para los pasos en el segundo y tercer eje (ancho y alto).

Aproveche el método strided-based para la extracción eficiente de parches

Podemos aprovechar np.lib.stride_tricks.as_strided scikit-image's view_as_windows para obtener ventanas deslizantes que serían simplemente views en la matriz de entrada y, por lo tanto, no incurrirán en una sobrecarga de memoria adicional y prácticamente gratis. Seguramente podemos usar np.lib.stride_tricks.as_strided directamente, pero el trabajo de configuración requerido es difícil de administrar, especialmente en arreglos con dimensiones más altas. Si scikit-image no está disponible, podemos usar directamente el source code que funciona de forma independiente.

Explicación sobre el uso de view_as_windows

La idea con view_as_windows es que view_as_windows en la entrada arg window_shape como una tupla de longitud igual a la cantidad de dimensiones en la matriz de entrada cuyas ventanas deslizantes son necesarias. Los ejes a lo largo de los cuales debemos deslizarnos se alimentan con las longitudes de ventana respectivas y el rest se alimenta con 1s . Esto crearía una matriz de views con singleton dims/axes es decir, ejes con lengths=1 correspondientes a los 1s en window_shape arg. Por lo tanto, para esos casos podríamos querer indexar en el elemento cero correspondiente a los ejes que se alimentan como longitudes de las ventanas deslizantes para tener una versión comprimida de las ventanas deslizantes.

Por lo tanto, tendríamos una solución, como tal –

 # Get sliding windows from skimage.util.shape import view_as_windows w = view_as_windows(X, (1,16,16,1))[...,0,:,:,0] # Index and get our specific windows out = w[np.arange(X.shape[0]),x,y] # If you need those in the same format as in the posted loopy code out = out.transpose(0,2,3,1)