imodfile 0.2.2

A pure-Rust IMOD model file decoder/encoder — binary & ASCII, with Python bindings
Documentation

imodfile

Decode, encode, and inspect IMOD model files — from Rust and Python. Read and write .mod files used by the IMOD electron microscopy toolkit, in both binary and ASCII formats.


Install

Rust crate

cargo add imodfile

Python package

pip install git+https://github.com/elemeng/imodfile.git

# Or build from source:
pip install maturin
maturin build --features python
pip install target/wheels/imodfile-*.whl

CLI tool

cargo install imodfile

Quick start (Rust)

use imodfile::Imod;

let model = Imod::load("cells.mod").unwrap();
println!("{} objects, {} views", model.obj.len(), model.view.len());

// Iterate over every point with indices
for (oi, ci, pi, pt) in model.points() {
    println!("[obj {oi}][cont {ci}][pt {pi}]  {} {} {}", pt.x, pt.y, pt.z);
}

// Dump all points as text
model.save_points("points.txt").unwrap();

// Save back as binary or ASCII (chosen by extension)
model.save("output.mod").unwrap();
model.save("output.txt").unwrap();

Quick start (Python)

import imodfile
import numpy as np

model = imodfile.load("cells.mod")
print(model.name, model.image_size)

for obj in model.objects:
    for cont in obj.contours:
        pts = cont.points                 # (N, 3) float32 array
        xs, ys, zs = pts[:, 0], pts[:, 1], pts[:, 2]

# Create new objects and contours
obj = model.add_object()
obj.name = "mitochondria"
obj.color = (1.0, 0.0, 0.0)

cont = obj.add_contour()
cont.points = np.array([[0,0,0],[1,0,0],[0,1,0]], dtype=np.float32)

model.save("output.mod")
model.save("output.txt")          # ASCII format
model.save_points("points.txt")   # point dump

Testing

# Full test suite (Rust + Python)
bash tests/test.sh

# Rust tests only
cargo test

# Python tests only (requires built wheel)
python -m pytest tests/test_python.py -v

# Property-based tests (randomized model generation)
cargo test --test proptests

# Fuzz the binary parser (runs indefinitely)
cargo fuzz run fuzz_binary

The test suite covers:

Suite Tests
Rust integration 13 tests — binary/ASCII round-trip, labels, point sizes, clip planes, views, edge cases, real-file round-trip
Property-based 2 tests — randomized model generation verifies write→read consistency and field invariants
Doc-tests 9 tests — API documentation examples that compile and run
Raw byte verification Binary writer magic, tags, field layout, IEOF marker
Python bindings 15 tests — load, read, create, modify, save, error handling, round-trip
Fuzz testing cargo-fuzz target for the binary parser (crash-resistant input handling)

The CI pipeline (GitHub Actions) runs all Rust tests, clippy (zero-warnings enforced), Python bindings build + pytest, and documentation build on every push and pull request.

Data model

Imod  (top-level model)
 ├── name, flags, pixsize, units
 ├── obj: Vec<Iobj>
 │    ├── Iobj  (object)
 │    │   ├── name, color, flags, drawmode
 │    │   ├── cont: Vec<Icont>
 │    │   │   └── Icont  (contour)
 │    │   │       ├── pts: Vec<Ipoint>  ← 3-D coordinates
 │    │   │       ├── sizes, flags, time, surf
 │    │   │       └── label, store
 │    │   ├── mesh: Vec<Imesh>
 │    │   │   └── Imesh  (mesh)
 │    │   │       ├── vert: Vec<Ipoint>
 │    │   │       ├── list: Vec<i32>  (indices + sentinels)
 │    │   │       └── flag, time, surf
 │    │   ├── clips: IclipPlanes
 │    │   ├── label, mesh_param
 │    │   └── store: Istore
 │    └── ...
 ├── view: Vec<Iview>     (camera positions)
 ├── slicer_angles: Vec<SlicerAngles>
 ├── ref_image: Option<IrefImage>
 └── store: Istore

Ipoint { x: f32, y: f32, z: f32 }

Key types

Most users primarily work with Imod (model), Iobj (object), Icont (contour), and Ipoint (3-D coordinate). All types, flag constants, and reader/writer functions are re-exported at the crate root for convenience, and also available under their respective modules for qualified access.

Type Description
[Imod] / Model Top-level model — image dimensions, transforms, views, objects
[Iobj] / Object A named object with colour, material, contours, and meshes
[Icont] / Contour A contour — open or closed series of [Ipoint]s
[Imesh] / Mesh Triangle/quad mesh with vertex and index lists
[Ipoint] Single 3-D coordinate { x, y, z }

For secondary types (Iview, IclipPlanes, Ilabel, Istore, ImeshParams, IrefImage, SlicerAngles, flag constants, and the low-level read_binary / write_binary / read_ascii / write_ascii functions), import from imodfile::model or imodfile::binary / imodfile::ascii as needed.

Rust API

Use case API
Load a file Imod::load(path) — auto-detects binary vs ASCII
Save a file model.save(path) — binary, or ASCII if path ends with .txt
Iterate points model.points()(obj, cont, pt, &Ipoint)
Format as text model.to_points()String
Write to writer model.write_points(&mut writer)
Low-level read read_binary(&mut reader) / read_ascii(&mut reader)
Low-level write write_binary(&mut writer, &model) / write_ascii(...)

Python API

Python What it does
imodfile.load(path)Model Load a file (auto-detect format)
model.save(path) Save (binary, or ASCII if .txt)
model.objectslist[Object] All objects
obj.contourslist[Contour] All contours in an object
cont.pointsndarray(N, 3) Point coordinates (read/write)
cont.surface, cont.time, cont.flags Contour metadata (read/write)
obj.add_contour()Contour Create a new contour
model.add_object()Object Create a new object

The .pyi stub is bundled in the wheel for IDE autocomplete.

CLI

imodfile <input.mod>                     # dump model info
imodfile <input.mod> <output.mod>        # copy / convert
imodfile <input.mod> output.txt          # convert to ASCII
imodfile <input.mod> output.txt --points # extract point coordinates
imodfile <input.mod> --points            # print points to stdout

Format support

Format Read Write
Binary (big-endian)
ASCII text
Legacy V0.1

All chunk types are supported: objects, contours, meshes, materials, clip planes, labels, point sizes, views, slicer angles, reference transforms, meshing parameters, and general-purpose storage.

The binary parser is fuzz-tested via cargo-fuzz for crash-resistant handling of arbitrary or corrupted input. Property-based tests with proptest verify write→read round-trip correctness across randomly generated models.

Read/write flow

                ┌──────────────────┐
                │  imodfile crate  │
                └─────────┬────────┘
                          │
            ┌─────────────┴─────────────┐
            ▼                           ▼
    ┌───────────────┐           ┌───────────────┐
    │  Imod::load() │           │  Imod::save() │
    │    / read()   │           │    / write()  │
    └───────┬───────┘           └───────┬───────┘
            │                           │
    ┌───────┴───────┐           ┌───────┴───────┐
    │  detect       │           │  path ends    │
    │  format       │           │  with .txt?   │
    └───────┬───────┘           └───────┬───────┘
            │                           │
    ┌───────┴───────┐           ┌───────┴───────┐
    ▼               ▼           ▼               ▼
 ┌──────┐    ┌──────────┐  ┌──────────┐      ┌──────┐
 │binary│    │  ASCII   │  │  ASCII   │      │binary│
 │reader│    │  reader  │  │  writer  │      │writer│
 └──────┘    └──────────┘  └──────────┘      └──────┘
      │            │             │               │
      ▼            ▼             ▼               ▼
  .mod file   .txt file      .txt file       .mod file
   (binary)    (ASCII)       (ASCII)          (binary)

Modules

Crate module Contents
[model] Data types — Imod, Iobj, Icont, Imesh, Ipoint, ...
[binary] Binary format reader and writer
[ascii] ASCII format reader and writer
[chunk_ids] 4-byte magic constants
[error] ImodError / ImodResult
[store] General-purpose storage helpers

License

MIT — see LICENSE for the full text.