Existe-t-il un moyen plus idiomatique d'afficher une grille d'images comme dans l'exemple ci-dessous?
import numpy as np
def gallery(array, ncols=3):
nrows = np.math.ceil(len(array)/float(ncols))
cell_w = array.shape[2]
cell_h = array.shape[1]
channels = array.shape[3]
result = np.zeros((cell_h*nrows, cell_w*ncols, channels), dtype=array.dtype)
for i in range(0, nrows):
for j in range(0, ncols):
result[i*cell_h:(i+1)*cell_h, j*cell_w:(j+1)*cell_w, :] = array[i*ncols+j]
return result
J'ai essayé d'utiliser hstack
et reshape
etc, mais je n'ai pas pu obtenir le bon comportement.
Je suis intéressé à utiliser numpy pour ce faire car il y a une limite au nombre d'images que vous pouvez tracer avec des appels matplotlib à subplot
et imshow
.
Si vous avez besoin d'échantillons de données à tester, vous pouvez utiliser votre webcam comme suit:
import cv2
import matplotlib.pyplot as plt
_, img = cv2.VideoCapture(0).read()
plt.imshow(gallery(np.array([img]*6)))
import numpy as np
import matplotlib.pyplot as plt
def gallery(array, ncols=3):
nindex, height, width, intensity = array.shape
nrows = nindex//ncols
assert nindex == nrows*ncols
# want result.shape = (height*nrows, width*ncols, intensity)
result = (array.reshape(nrows, ncols, height, width, intensity)
.swapaxes(1,2)
.reshape(height*nrows, width*ncols, intensity))
return result
def make_array():
from PIL import Image
return np.array([np.asarray(Image.open('face.png').convert('RGB'))]*12)
array = make_array()
result = gallery(array)
plt.imshow(result)
plt.show()
Nous avons un tableau de formes (nrows*ncols, height, weight, intensity)
. Nous voulons un tableau de forme (height*nrows, width*ncols, intensity)
.
L'idée ici est donc d'abord d'utiliser reshape
pour diviser le premier axe en deux axes, l'un de longueur nrows
et l'autre de longueur ncols
:
array.reshape(nrows, ncols, height, width, intensity)
Cela nous permet d'utiliser swapaxes(1,2)
pour réorganiser les axes afin que la forme devienne (nrows, height, ncols, weight, intensity)
. Notez que cela place nrows
à côté de height
et ncols
à côté de width
.
Puisque reshape
ne change pas l'ordre défilé des données, reshape(height*nrows, width*ncols, intensity)
produit maintenant le tableau souhaité.
C'est (dans l'esprit) le même que l'idée utilisée dans la fonction unblockshaped
.
Une autre façon est d'utiliser view_as_blocks . Ensuite, vous évitez de permuter les axes à la main:
from skimage.util import view_as_blocks
import numpy as np
def refactor(im_in,ncols=3):
n,h,w,c = im_in.shape
dn = (-n)%ncols # trailing images
im_out = (np.empty((n+dn)*h*w*c,im_in.dtype)
.reshape(-1,w*ncols,c))
view=view_as_blocks(im_out,(h,w,c))
for k,im in enumerate( list(im_in) + dn*[0] ):
view[k//ncols,k%ncols,0] = im
return im_out
Cette réponse est basée sur @ unutbu, mais elle concerne les tenseurs ordonnés HWC
. En outre, il affiche des tuiles noires pour tous les canaux qui ne sont pas répartis de manière égale dans les lignes/colonnes données.
def tile(arr, nrows, ncols):
"""
Args:
arr: HWC format array
nrows: number of tiled rows
ncols: number of tiled columns
"""
h, w, c = arr.shape
out_height = nrows * h
out_width = ncols * w
chw = np.moveaxis(arr, (0, 1, 2), (1, 2, 0))
if c < nrows * ncols:
chw = chw.reshape(-1).copy()
chw.resize(nrows * ncols * h * w)
return (chw
.reshape(nrows, ncols, h, w)
.swapaxes(1, 2)
.reshape(out_height, out_width))
Voici une fonction detiling correspondante pour la direction inverse:
def detile(arr, nrows, ncols, c, h, w):
"""
Args:
arr: tiled array
nrows: number of tiled rows
ncols: number of tiled columns
c: channels (number of tiles to keep)
h: height of tile
w: width of tile
"""
chw = (arr
.reshape(nrows, h, ncols, w)
.swapaxes(1, 2)
.reshape(-1)[:c*h*w]
.reshape(c, h, w))
return np.moveaxis(chw, (0, 1, 2), (2, 0, 1)).reshape(h, w, c)