Turn cryo-ET tomograms (.mrc) into clean, watertight, 3D-printable relief plates —
"moon plate" style, where dense biological features (capsids, cells, viruses) read as craters
carved by contrast through depth.
A tomogram is collapsed to a 2D heightmap (a single slice by default, or a slab / min·mean·max projection), conditioned (denoise, contrast, optional invert), and turned into a solid plate: a relief top surface + flat base + side walls, guaranteed manifold and watertight so it prints as-is. Drive it from a scriptable library + CLI, or the desktop GUI with live sliders and a 3D preview.
tomogram (Z,Y,X) ──reduce──▶ heightmap (Y,X) ──contrast/invert──▶ z = base + relief·v
│
watertight relief plate ◀──mesh────┘
(relief top + base + walls) ──▶ STL / OBJ / PLY
Base Python 3.14 is too new for the VTK/Qt stack, so use a dedicated python=3.11 env:
micromamba create -y -n tomoprint -c conda-forge \
python=3.11 \
"numpy>=1.26,<2.3" "scipy>=1.13,<2" "mrcfile>=1.5,<2" "scikit-image>=0.24,<0.27" \
"vtk>=9.3,<9.7" "pyvista>=0.44,<0.46" "pyvistaqt>=0.11,<0.12" "pyside6>=6.7,<6.10" \
"pytest>=8" "pytest-cov>=5" "ruff>=0.6"
micromamba run -n tomoprint python -m pip install -e ".[dev]"
# Optional: enable mesh decimation
# micromamba run -n tomoprint python -m pip install "manifold3d>=2.5"The pure-core extra omits VTK/Qt (pip install -e ".[test]") if you only need the library + CLI.
# CLI: middle slice, 200 mm footprint, 6 mm relief, 2 mm base -> STL
micromamba run -n tomoprint tomoprint data/capsid_trim.mrc capsid.stl --report
# Min-intensity projection over +/-5 slices (emphasizes dense capsids), features popped out
micromamba run -n tomoprint tomoprint data/capsid_trim.mrc caps_min.stl \
--mode min --half-thickness 5 --invert
# Large tomogram: bin x2 to ~256k triangles
micromamba run -n tomoprint tomoprint data/bot_left_trim.mrc bot.stl --bin 2 --report
# Desktop GUI
micromamba run -n tomoprint tomoprint-gui# Library
from tomoprint import run_from_file, ReduceParams, GeometryParams
mesh, diag = run_from_file(
"data/capsid_trim.mrc",
reduce_p=ReduceParams(mode="slice", index=50),
geo_p=GeometryParams(footprint_mm=200, relief_depth_mm=6, base_thickness_mm=2),
out="capsid.stl",
)
print(diag) # {'watertight': True, 'euler_number': 2, 'volume_mm3': ..., 'n_faces': ...}| Group | Option | Default | Meaning |
|---|---|---|---|
| Source | --mode |
slice |
slice · slab_mean · min · mean · max |
--index |
middle | slice / slab centre | |
--half-thickness |
0 | ± N slices for slab/projection | |
--bin |
1 | integer downsample (simplify + denoise) | |
| Contrast | --sigma |
1.0 | gaussian blur (px) |
--pclip-low/high |
1 / 99 | contrast percentile clip | |
--invert |
off | flip craters ↔ bumps | |
| Relief | --footprint |
200 | longest plate side (mm) |
--relief |
6 | relief depth (mm) | |
--base |
2 | solid base thickness (mm) | |
| Mesh | --taubin |
0 | Taubin smoothing iterations |
--decimate |
off | keep-fraction (0–1) |
Run tomoprint --help for the full list. The GUI exposes the same parameters as live sliders.
micromamba run -n tomoprint pytest -m "not slow" # fast unit tests (no VTK/Qt)
micromamba run -n tomoprint pytest -m slow # real-MRC end-to-end (writes an STL)
micromamba run -n tomoprint ruff check src testsArchitecture: a pure algorithm layer (numpy/scipy/scikit-image/trimesh, no VTK/Qt) under
src/tomoprint/, with cli.py and gui/ as thin frontends that call pipeline.py. See
pyproject.toml extras [viz]/[gui]/[test]/[dev].