Condensed HKS Pipeline¶
The Heat Kernel Signature (HKS)
is a spectral shape descriptor that captures local geometry at multiple spatial
scales. condensed_hks_pipeline computes HKS features across a mesh, then
agglomerates nearby vertices into a compact graph of "condensed nodes" — each
with a feature vector summarizing HKS values within its region.
This tutorial walks through:
- Loading a sample dendrite mesh
- Running
condensed_hks_pipeline - Inspecting the condensed feature output
- Visualizing a single HKS feature on the mesh
import base64
import warnings
import pyvista as pv
from IPython.display import HTML
from meshmash import condensed_hks_pipeline, fetch_sample_mesh
Load a sample mesh¶
fetch_sample_mesh downloads a pre-packaged dendrite mesh from the
MICrONs dataset (cached locally after
the first call).
vertices, faces = fetch_sample_mesh("microns_dendrite_sample")
print(f"Vertices: {len(vertices):,} Faces: {len(faces):,}")
Downloading data from 'https://github.com/bdpedigo/meshmash/releases/download/data-v1/microns_dendrite_sample.ply' to file '/home/runner/.cache/meshmash/microns_dendrite_sample.ply'.
0%| | 0.00/1.23M [00:00<?, ?B/s]
0%| | 0.00/1.23M [00:00<?, ?B/s]
100%|█████████████████████████████████████| 1.23M/1.23M [00:00<00:00, 2.09GB/s]
Vertices: 32,441 Faces: 64,832
Run the pipeline¶
result = condensed_hks_pipeline((vertices, faces), verbose=True)
Subdividing mesh... Meshes in queue: 1
Finding overlapping submeshes... Computing HKS across submeshes...
Inspect condensed features¶
result.condensed_features is a DataFrame with one row per condensed node
and one column per HKS timescale (short to long).
result.condensed_features.head()
| hks_0 | hks_1 | hks_2 | hks_3 | hks_4 | hks_5 | hks_6 | hks_7 | hks_8 | hks_9 | ... | hks_22 | hks_23 | hks_24 | hks_25 | hks_26 | hks_27 | hks_28 | hks_29 | hks_30 | hks_31 | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| -1 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | ... | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN |
| 0 | -14.385658 | -14.431233 | -14.485003 | -14.548036 | -14.621345 | -14.705779 | -14.801884 | -14.909724 | -15.028725 | -15.157562 | ... | -16.859406 | -16.967730 | -17.072580 | -17.173508 | -17.270723 | -17.365082 | -17.457726 | -17.549759 | -17.642195 | -17.736143 |
| 1 | -14.364993 | -14.412881 | -14.469562 | -14.536244 | -14.614092 | -14.704091 | -14.806849 | -14.922342 | -15.049617 | -15.186545 | ... | -16.811813 | -16.923306 | -17.035780 | -17.150280 | -17.267025 | -17.385151 | -17.503065 | -17.619148 | -17.732439 | -17.842937 |
| 2 | -13.221722 | -13.261107 | -13.307681 | -13.362417 | -13.426250 | -13.499960 | -13.584015 | -13.678332 | -13.782001 | -13.892975 | ... | -14.922030 | -15.024227 | -15.144602 | -15.286610 | -15.454020 | -15.650618 | -15.879692 | -16.143141 | -16.439947 | -16.763842 |
| 3 | -13.275511 | -13.289325 | -13.305492 | -13.324290 | -13.345988 | -13.370837 | -13.399064 | -13.430880 | -13.466506 | -13.506241 | ... | -15.134044 | -15.446198 | -15.793377 | -16.164434 | -16.537655 | -16.882639 | -17.172083 | -17.397886 | -17.573887 | -17.722248 |
5 rows × 32 columns
Visualize on the mesh¶
Map one feature column back to the simplified mesh vertices using
result.simple_labels — an integer array (one entry per vertex) indicating
which condensed node each vertex belongs to.
poly = pv.make_tri_mesh(*result.simple_mesh)
col = result.condensed_features.columns[10]
scalars = result.condensed_features[col].reindex(result.simple_labels).values
plotter = pv.Plotter(off_screen=True, window_size=(800, 500))
plotter.add_mesh(poly, scalars=scalars, cmap="coolwarm", show_scalar_bar=False)
plotter.view_isometric()
plotter.export_html("hks_feature.html")
b64 = base64.b64encode(open("hks_feature.html", "rb").read()).decode()
with warnings.catch_warnings():
warnings.simplefilter("ignore", UserWarning)
display(
HTML(
f'<iframe src="data:text/html;base64,{b64}" style="width:100%;height:500px;border:none;"></iframe>'
)
)
/home/runner/work/meshmash/meshmash/.venv/lib/python3.12/site-packages/pyvista/plotting/plotter.py:159: UserWarning: This system does not appear to be running an xserver. PyVista will likely segfault when rendering. Try starting a virtual frame buffer with xvfb, or using ``pyvista.start_xvfb()`` warnings.warn(
Using CloudVolume for any neuron¶
To run on an arbitrary neuron from a segmentation dataset, fetch the mesh
with CloudVolume and pass it to
condensed_hks_pipeline in the same way:
from cloudvolume import CloudVolume
cv = CloudVolume(
"graphene://https://minnie.microns-daf.com/segmentation/table/minnie3_v1",
progress=False,
)
root_id = 864691136618675163 # MICrONs pyramidal neuron
raw = cv.mesh.get(root_id)[root_id]
raw = raw.deduplicate_vertices(is_chunk_aligned=True)
vertices, faces = raw.vertices, raw.faces
result = condensed_hks_pipeline((vertices, faces), verbose=True)
Full neurons are considerably larger than the dendrite sample; the pipeline will take longer and use more RAM, but the call is otherwise identical.