How to plot 3D voxels?

I am having trouble plotting the 3D voxels through streamlit. I am able to plot it using matplotlib in jupyter notebook but I am not sure how to translate that to streamlit.

This is the code snippet I am using to plot voxels in matplotlib:

volume is a 3 dimesional tensor

volume = generated_volume.reshape(32,32,32)
%matplotlib notebook
fig = plt.figure()
ax = fig.gca(projection=‘3d’)
#ax.set_aspect(‘equal’)
volume = volume.squeeze().ge(0.5)
ax.voxels(volume, edgecolor=“k”)
ax.view_init(30, 240)
plt.show()

Hello and welcome to the forums @sidhartha :slight_smile:

You should use st.pyplot() in place of plt.show() to show the plot in Streamlit.

Since I don’t have a generated_volume variable, I rebuilt an example with data from matplotlib’s help where I cache some voxel data, display it on matplotlib and use Streamlit sliders to rotate axis without recomputing the data each time you interact with the slider. You can put this into an app.py script and run streamlit run app.py to see the results.

"""From https://matplotlib.org/3.1.0/gallery/mplot3d/voxels.html in Streamlit"""
import matplotlib.pyplot as plt
import numpy as np
import streamlit as st

# This import registers the 3D projection, but is otherwise unused.
from mpl_toolkits.mplot3d import Axes3D  # noqa: F401 unused import


@st.cache
def generate_data():
    """Let's put the data in cache so it doesn't reload each time we rerun the script when modifying the slider"""
    # prepare some coordinates
    x, y, z = np.indices((8, 8, 8))

    # draw cuboids in the top left and bottom right corners, and a link between them
    cube1 = (x < 3) & (y < 3) & (z < 3)
    cube2 = (x >= 5) & (y >= 5) & (z >= 5)
    link = abs(x - y) + abs(y - z) + abs(z - x) <= 2

    # combine the objects into a single boolean array
    voxels = cube1 | cube2 | link

    colors = np.empty(voxels.shape, dtype=object)
    colors[link] = 'red'
    colors[cube1] = 'blue'
    colors[cube2] = 'green'

    return voxels, colors

voxels, colors = generate_data()

# let's put sliders to modify view init, each time you move that the script is rerun, but voxels are not regenerated
# TODO : not sure that's the most optimized way to rotate axis but well, demo purpose
azim = st.sidebar.slider("azim", 0, 90, 30, 1)
elev = st.sidebar.slider("elev", 0, 360, 240, 1)

# and plot everything
fig = plt.figure()
ax = fig.gca(projection='3d')
ax.voxels(voxels, facecolors=colors, edgecolor='k')
ax.view_init(azim, elev)

st.pyplot()

1 Like

Thanks a lot for your reply @andfanilo. Your code works quite well. The only thing is that I have to toggle is the angle toggle bars to visualize the plot. However, this is a very good solution for now.

1 Like