Representando voxels con matplotlib

En Python, dada una matriz N_1 x N_2 x N_3 que contiene 0s o 1s, estaría buscando una manera de mostrar los datos en 3D como un volumen N_1 x N_2 x N_3 con píxeles N_1 x N_2 x N_3 (voxels) en la ubicación de 1s.

Por ejemplo, si las coordenadas de 1s fueran [[1, 1, 1], [4, 1, 2], [3, 4, 1]] , la salida deseada se vería así

Parece que el módulo mplot3D de matplotlib podría tener el potencial para lograr esto, pero no he encontrado ningún ejemplo de este tipo de ttwig. ¿Alguien estaría al tanto de una solución simple para abordar este problema?

De antemano muchas gracias por tu ayuda.

A. usando voxels

A partir de matplotlib 2.1, hay una función de Axes3D.voxels disponible, que hace prácticamente lo que se solicita aquí. Sin embargo, no es muy fácil de personalizar para diferentes tamaños, posiciones o colores.

 from mpl_toolkits.mplot3d import Axes3D import numpy as np import matplotlib.pyplot as plt N1 = 10 N2 = 10 N3 = 10 ma = np.random.choice([0,1], size=(N1,N2,N3), p=[0.99, 0.01]) fig = plt.figure() ax = fig.gca(projection='3d') ax.set_aspect('equal') ax.voxels(ma, edgecolor="k") plt.show() 

introduzca la descripción de la imagen aquí

B. Usando Poly3DCollection

La creación manual de los voxels puede hacer que el proceso sea un poco más transparente y permite cualquier tipo de personalización de los tamaños, posiciones y colores de los voxels. Otra ventaja es que aquí creamos una única Poly3DCollection en lugar de muchas, haciendo que esta solución sea más rápida que los voxels incorporados.

 from mpl_toolkits.mplot3d import Axes3D import numpy as np import matplotlib.pyplot as plt from mpl_toolkits.mplot3d.art3d import Poly3DCollection def cuboid_data(o, size=(1,1,1)): X = [[[0, 1, 0], [0, 0, 0], [1, 0, 0], [1, 1, 0]], [[0, 0, 0], [0, 0, 1], [1, 0, 1], [1, 0, 0]], [[1, 0, 1], [1, 0, 0], [1, 1, 0], [1, 1, 1]], [[0, 0, 1], [0, 0, 0], [0, 1, 0], [0, 1, 1]], [[0, 1, 0], [0, 1, 1], [1, 1, 1], [1, 1, 0]], [[0, 1, 1], [0, 0, 1], [1, 0, 1], [1, 1, 1]]] X = np.array(X).astype(float) for i in range(3): X[:,:,i] *= size[i] X += np.array(o) return X def plotCubeAt(positions,sizes=None,colors=None, **kwargs): if not isinstance(colors,(list,np.ndarray)): colors=["C0"]*len(positions) if not isinstance(sizes,(list,np.ndarray)): sizes=[(1,1,1)]*len(positions) g = [] for p,s,c in zip(positions,sizes,colors): g.append( cuboid_data(p, size=s) ) return Poly3DCollection(np.concatenate(g), facecolors=np.repeat(colors,6, axis=0), **kwargs) N1 = 10 N2 = 10 N3 = 10 ma = np.random.choice([0,1], size=(N1,N2,N3), p=[0.99, 0.01]) x,y,z = np.indices((N1,N2,N3))-.5 positions = np.c_[x[ma==1],y[ma==1],z[ma==1]] colors= np.random.rand(len(positions),3) fig = plt.figure() ax = fig.gca(projection='3d') ax.set_aspect('equal') pc = plotCubeAt(positions, colors=colors,edgecolor="k") ax.add_collection3d(pc) ax.set_xlim([0,10]) ax.set_ylim([0,10]) ax.set_zlim([0,10]) #plotMatrix(ax, ma) #ax.voxels(ma, edgecolor="k") plt.show() 

introduzca la descripción de la imagen aquí

C. Usando plot_surface

Al adaptar un código de esta respuesta (que se basa en parte en esta respuesta ), uno puede trazar fácilmente los cuboides como gráficos de superficie .

Luego se puede iterar sobre la matriz de entrada y, al encontrar un gráfico 1 se muestra un cuboide en la posición correspondiente a los índices de la matriz.

La ventaja aquí es que obtiene un buen sombreado en las superficies, lo que se sum al efecto 3D. Una desventaja puede ser que los cubos no se comporten físicamente en algunos casos, por ejemplo, pueden superponerse para ciertos angularjs de visión.

 from mpl_toolkits.mplot3d import Axes3D import numpy as np import matplotlib.pyplot as plt def cuboid_data(pos, size=(1,1,1)): # code taken from # https://stackoverflow.com/a/35978146/4124317 # suppose axis direction: x: to left; y: to inside; z: to upper # get the (left, outside, bottom) point o = [a - b / 2 for a, b in zip(pos, size)] # get the length, width, and height l, w, h = size x = [[o[0], o[0] + l, o[0] + l, o[0], o[0]], [o[0], o[0] + l, o[0] + l, o[0], o[0]], [o[0], o[0] + l, o[0] + l, o[0], o[0]], [o[0], o[0] + l, o[0] + l, o[0], o[0]]] y = [[o[1], o[1], o[1] + w, o[1] + w, o[1]], [o[1], o[1], o[1] + w, o[1] + w, o[1]], [o[1], o[1], o[1], o[1], o[1]], [o[1] + w, o[1] + w, o[1] + w, o[1] + w, o[1] + w]] z = [[o[2], o[2], o[2], o[2], o[2]], [o[2] + h, o[2] + h, o[2] + h, o[2] + h, o[2] + h], [o[2], o[2], o[2] + h, o[2] + h, o[2]], [o[2], o[2], o[2] + h, o[2] + h, o[2]]] return return np.array(x), np.array(y), np.array(z) def plotCubeAt(pos=(0,0,0),ax=None): # Plotting a cube element at position pos if ax !=None: X, Y, Z = cuboid_data( pos ) ax.plot_surface(X, Y, Z, color='b', rstride=1, cstride=1, alpha=1) def plotMatrix(ax, matrix): # plot a Matrix for i in range(matrix.shape[0]): for j in range(matrix.shape[1]): for k in range(matrix.shape[2]): if matrix[i,j,k] == 1: # to have the plotCubeAt(pos=(i-0.5,j-0.5,k-0.5), ax=ax) N1 = 10 N2 = 10 N3 = 10 ma = np.random.choice([0,1], size=(N1,N2,N3), p=[0.99, 0.01]) fig = plt.figure() ax = fig.gca(projection='3d') ax.set_aspect('equal') plotMatrix(ax, ma) plt.show() 

introduzca la descripción de la imagen aquí

La próxima versión de matplotlib 2.1 tiene una función y ejemplos para voxels 3D .

Si usas anaconda, puedes instalarlo a través del canal conda-forge.

 conda install -c conda-forge matplotlib