EMI-Mesh generates tetrahedral meshes of brain tissue at the cellular scale, including the extracellular space (ECS), from segmented electron microscopy data. The meshes are suitable for numerical simulations.
Install snakemake (using conda):
conda create -c conda-forge -c bioconda -n snakemake snakemake snakemake-storage-plugin-http snakemake-executor-plugin-cluster-generic
conda activate snakemake
Then run the pipeline on an example configuration:
snakemake --configfile config_files/cortical_mm3.yml --use-conda --cores 8
- A. Data download of the segmentation (cloud-volume or webknossos). Take a look at the microns explorer gallery, and copy cloudpath and position from the interactive neuroglancer view.
- B. Image processing: resampling and a sequence of morphological operations on the labels. These set the rough ECS share, separate the cells and repair topology.
- C. Surface extraction: one multi-label surface of all cells and the ECS, made with surface nets and smoothed. Optionally, the ECS share is fine-tuned on the surface.
- D. Tetrahedral mesh: the surface is simplified and tetrahedralized with fTetWild. Optionally, the mesh is curved.
A configuration file has the sections raw, processing, surface (optional) and meshing. For example:
name : cortical_mm3 # name of the output folder
raw:
cloudpath : "precomputed://gs://iarpa_microns/minnie/minnie65/seg"
mip : 2 # resolution level (0 = finest)
position : "225182-107314-22000" # center, in voxels of the dataset
size : 5000 # side length in nm, or "x-y-z"
processing:
dx : 20 # isotropic voxel size (nm) for resampling
operation :
- "smooth iterations=1 radius=1"
- "erode radius=1"
meshing:
envelopsize : 18 # max. deviation of the mesh from the surface (nm)| Section | Option | Description |
|---|---|---|
processing |
ncells |
Keep only the N largest cells. |
surface |
smoothing_scale |
Maximal displacement of the surface points during smoothing, in voxels (default: 1.2). |
smoothing_iterations |
Number of smoothing iterations (default: 16). | |
ecs_share |
Target ECS volume share (0–1); see below. | |
ecs_min_width |
Minimal thickness of cells and ECS gaps (nm) during the ECS adjustment (default: 10). | |
meshing |
stopquality |
fTetWild quality score (default: 10). Higher values stop earlier, giving lower quality. |
simplify_eps |
Maximal deviation of the simplified surface (nm, default: 0.5 voxels). 0 disables simplification. |
|
edge_length_fac |
fTetWild target edge length, relative to the bounding box diagonal (default: 0.05). | |
curved |
true for curved 10-node tetrahedra (default: false). |
Operations are listed under processing: operation as strings of the form "command arg1=value1 ..." and are applied in order. They are based on nbmorph and are multithreaded. Any labels argument refers to the cell IDs of the original segmentation.
| Operation | Arguments | Description |
|---|---|---|
merge |
labels |
Merge all labels into the first one. |
ncells |
ncells, keep_cell_labels |
Keep the N largest cells, plus the listed ones. |
dilate / erode |
radius, labels |
Grow or shrink all cells, or only the listed ones. erode also takes struct_sequence (default: B for radius=1, DDB otherwise). |
smooth |
radius, iterations, labels |
Morphological opening and closing. |
removeislands |
minsize |
Remove components smaller than minsize voxels. |
fill_handles |
thickness, max_loop_length, workers |
Close handles (tunnels) of cells with thin membranes. |
roigenerate / roiapply |
labels |
Create a mask from the listed cells, and later set everything outside it to 0. |
roi<op> |
as <op> |
Apply an operation to the mask, e.g. roidilate radius=80. |
See config_files/ for complete examples, e.g. cappilary1.yml for an ROI around a capillary.
fill_handles closes handles (tunnels) through a cell with a thin membrane, spanned along the shortest loop around each tunnel:
With surface: ecs_share, all cell–ECS interfaces are moved by a common offset until the ECS takes that share of the volume. The bounding box is kept, and cells and gaps stay at least ecs_min_width thick. Below, the ECS share of a dense crop is lowered from 23 % to 18 % (right: zoomed region, with the voxel grid).
This adjustment is limited in range: Set the rough ECS share with the voxel operations (e.g. erode, dilate), and use ecs_share to fine-tune it.
fTetWild meshes the simplified surface with straight-sided tetrahedra, which cut the corners of curved membranes. With meshing: curved: true, the mesh is converted to quadratic (10-node) tetrahedra, and the nodes on the membranes and on the boundary are moved onto the input surface of the mesh step (the fine surface before simplification), onto the part with the same two labels. Nodes move step by step, and a step that would push a cell below half its initial quality, or below 0.1 (whichever is higher, but at most the initial quality), is shortened or skipped, so no cell is inverted. The number of cells is unchanged.
The figure shows a dendritic spine from an 800 nm crop (20 nm voxels): its input surface, and the straight and curved meshes colored by the distance to it, with whole tetrahedra (gray) at the crop box. Over all membranes of the crop, curving reduces the mean distance from 3.4 nm to 1.0 nm, and the 95th percentile from 7.8 nm to 3.6 nm. The mean volume error of the cells drops from 7.5 % to 1.9 % (max. 15 % to 6 %). A few nodes cannot reach the surface without inverting a cell, some larger distances remain. Curving costs some cell quality: the mean minimal scaled Jacobian decreases from 0.59 to 0.51, and the minimum from 0.13 to 0.10.
The pipeline steps can also be called from Python:
import pyvista as pv
from imagemesh.image import np2pv
from imagemesh.tetmesh import mesh_surface
from emimesh.clients.generate_mesh import curve_mesh
from emimesh.download_data import download_cloudvolume
from emimesh.ecs_share import adjust_ecs_share
from emimesh.extract_surfaces import extract_surface
from emimesh.process_image_data import process_image
# a 2 µm cube of the MICrONS segmentation (position in voxels, size in nm)
url = "precomputed://gs://iarpa_microns/minnie/minnie65/seg_m1300"
img, res = download_cloudvolume(url, mip=1, pos=[162258, 200928, 20530], physical_size=[2000] * 3)
raw = np2pv(img, res)
ops = [
["smooth", "iterations=1", "radius=2"],
["erode", "radius=1"],
["removeislands", "minsize=150"], # fragments too small to mesh
]
img, *_ = process_image(raw, dx=20, operations=ops) # cells get the labels 2, 3, ...
surf = extract_surface(img) # multi-label surface, ECS = 1
surf, _, share = adjust_ecs_share(surf, 0.3) # optional, takes a few minutes
mesh, _ = mesh_surface(surf, envelopsize=8, simplify_eps=3)
mesh = curve_mesh(mesh, surf) # optional: curved 10-node tetrahedra
pv.save_meshio("mesh.xdmf", mesh)Results are written to results/<name>/:
raw/,processed/: the downloaded and the processed label image (.vti).processed/imagestatistic.ymlmaps the mesh labels to the original cell IDs.surfaces/: the multi-label surfaces (.vtk), with the labels on both sides of each face inboundary_labels. These aresurf_smooth.vtk,surf_ecs.vtk(only withecs_share) andsurf_dec.vtk(simplified, the input of fTetWild).meshes/: the tetrahedral mesh (mesh.xdmf) with cell datalabel(1 = ECS, 2…N = cells), plus mesh statistics and a screenshot. Withcurved: true, the mesh has quadratic tetrahedra (tetra10), and the volumes and areas in the statistics are integrated over the curved cells.




