'Export .stl or .obj from 3D spectrogram using python
I am very new to python and programming in general. However, for an art project, I have spent the last week learning to program using python and found the plotly library. So amazing! It improved my code so much! I have written a code which lets me load in .wav files, creating a 3D spectrogram and exporting the 3D image to either a .png or html file. However, what I actually wanted to have is an .stl or .obj file of the 3D spectrogram (see image attached). I have already tried a few things, eg. I found a page which turns .png in .stl. It is a good alternative but it’s not what I was looking for. I had another idea where I would make x .png slices of the 3D spectrogram and then build a .stl from the slices.
I was wondering, maybe someone of you has another idea or advice to export the 3D plot into a .stl/.obj file or even plot a 3D spectrogram and immediately export it to .stl or .obj.
Thank you very much in advance for your help =) Bests, Sweetheart90 3D spectrogram plot
Solution 1:[1]
Not that easy. You need to connect your vertices to a triangle mesh (shape n, 3, 3) Then you can use mk_stl_mesh to generate and export_stl_mesh to save:
def export_stl_mesh(*args, fileName:str, **kwargs):
"""
exports geoms mesh as stl file
call like: im_ex_port.export_stl_mesh( np.flip(d.vtx[0][d.geoms[0].mesh], -1 ),
fileName=f"{g.name}_{str(g.gId)}")
"""
content = mk_stl_mesh(*args, **kwargs)
exportPath = os.path.join(sts.mediaPath, 'stls', f"{fileName}.stl")
with open(exportPath, 'wb') as f:
f.write(content.encode('ascii'))
print(f"stl saved to exportPath: \n{exportPath}")
def mk_stl_mesh(mesh, *args, **kwargs):
bounds = f"solid Exported from geometries.py" + '\n'
content = bounds
for triangle in mesh.reshape(-1, 3, 3):
content += ('facet normal 0.000000 0.000000 1.000000' + '\n')
content += ('outer loop' + '\n')
for point in triangle:
content += (f"vertex {' '.join([f'{t:6f}' for t in point])}" + '\n')
content += ('endloop' + '\n')
content += ('endfacet' + '\n')
content += ('end' + bounds)
return content
I forgot to mention a couple of important points to complete the answer:
- There are some variables in the functions like mediaPath which you must change to whatever you want.
- The most critical part is not obviously to create the mesh. There is a coding challenge 25 from "the coding train" that can help you there. It's JavaScript but the idea is the same.
- You should use numpy to do this. There is also a numpy.stl library which might have what you need.
Have fun
Solution 2:[2]
I have further tried things. I found the mtri function in matplob lib which should plot a 3D surface with triangles. so far so good. this is my code
import matplotlib.pyplot as plt
from scipy.io import wavfile
import numpy as np
from scipy import signal # spectrogram function
from matplotlib import cm # colour map
import matplotlib.tri as mtri
filename="/Users/sebastiankuhz/Desktop/kunst/Assemblypart1.wav"
samplingFrequency, signalData = wavfile.read(filename)
# basic config
sample_rate = samplingFrequency
sig_len_secs = signalData.shape[0]/samplingFrequency
# generate the signal
timestamps_secs = np.arange(sample_rate*sig_len_secs) / sample_rate
mysignal = signalData
# extract the spectrum
freq_bins, timestamps, spec = signal.spectrogram(mysignal,sample_rate)
z=10.0*np.log10(spec)
tri = mtri.Triangulation(freq_bins,timestamps)
# 3d plot
fig = plt.figure()
ax = plt.axes(projection='3d')
ax.plot_surface(freq_bins[:, None], timestamps[None, :], z, cmap=cm.coolwarm)
plt.show()
Now, I receive the error: "ValueError: x and y must be equal-length 1D arrays".
Question: From a programming point of view, I understand the error, as the length of the arrays of the freq_bins and the timestamps are not the same. However in the plot I see that every timestamps value has "dedicated" freq_bins values and also z values. Thus, why do I then receive this error?
Solution 3:[3]
layout += [[sg.Column(col, scrollable=True, size=sz)]]
It means add a new row in layout, not a new column.
Try
layout = [[]]
...
layout[0] += [sg.Column(col, scrollable=True, size=sz)]
Demo code
import PySimpleGUI as sg
data = [(f'folder {j+1}', [f'File {i+1:0>2d}' for i in range(10)]) for j in range(5)]
layout = [[]]
for dir, files in data:
column = [[sg.Text(dir)]] + [
[sg.Checkbox(file, default=True)] for file in files
]
layout[0] += [sg.Column(column, scrollable=True, vertical_scroll_only=True, size=(100, 200))]
sg.Window("test", layout, finalize=True).read(close=True)
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | |
| Solution 2 | |
| Solution 3 | Jason Yang |

