Skip to main content

Crate mrc

Crate mrc 

Source
Expand description

Read and write MRC-2014 files — the standard in cryo-EM and structural biology.

This crate handles file I/O, byte-order detection, and type-safe data access so you can focus on your science. It’s fast (SIMD, parallel encoding) and works with plain, gzip, and bzip2 files out of the box.

§Quick example

use mrc::{open, create, VoxelBlock};

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let reader = open("protein.mrc")?;          // auto-detects compression
    for slice in reader.slices_f32() {           // converts to f32 for you
        let block = slice?;
    }

    let mut writer = create("output.mrc")
        .shape([512, 512, 256])
        .mode::<f32>()
        .finish()?;
    writer.write_block(&VoxelBlock::new(
        [0, 0, 0], [512, 512, 1],
        vec![0.0f32; 512 * 512],
    )?)?;
    writer.finalize()?;
    Ok(())
}

§Reading files

Open any MRC file with open() or Reader::open. Compression is detected from the file’s magic bytes — no need to tell it gzip or bzip2.

use mrc::Reader;
let reader = Reader::open("tilt_series.mrc")?;
println!("{}×{}×{} voxels, mode {:?}",
    reader.shape().nx, reader.shape().ny, reader.shape().nz,
    reader.mode());

Then pick an iteration method:

  • slices — one Z-plane at a time
  • slabs — batches of k Z-planes
  • tiles — arbitrary 3D blocks
  • subregion — a single block by coordinate

Or grab the full volume in one call:

Each yields VoxelBlock<T> — a data chunk with its offset and shape, so you always know where it belongs.

For density maps stored as integers, use slices_f32 or read_volume_f32() to get f32 with automatic mode conversion (no need to match the file’s storage type): It converts every MRC mode to f32 (integer widening, complex→magnitude, the works):

for slice in reader.slices_f32() {
    let block = slice?;
    println!("slice {} mean density: {:.2}",
        block.offset[2],
        block.data.iter().sum::<f32>() / block.data.len() as f32);
}

Or read the full volume at once with read_volume_f32() and wrap with ndarray for numpy-like slicing:

let block = reader.read_volume_f32()?;
let array = ndarray::Array3::from_shape_vec(
    [reader.shape().nz, reader.shape().ny, reader.shape().nx],
    block.data,
).unwrap();

§Large files

When the file does not fit in RAM, use MmapReader (requires the mmap feature). Same iterator API, zero-copy slab_as, OS-managed paging.

§Quirky files

Reader::open_permissive opens files with minor header issues as warnings instead of hard errors — handy for data from older instruments.

let (reader, warnings) = Reader::open_permissive("legacy.mrc")?;
for w in &warnings { eprintln!("note: {w}"); }

§Writing files

Use create() to get a WriterBuilder, set the shape and voxel type, then call finish.

use mrc::create;
let mut writer = create("output.mrc")
    .shape([256, 256, 128])
    .mode::<f32>()
    .finish()?;

The lifecycle:

  1. Write blocks with write_block. The type T matches the file’s mode — a compile-time check that prevents accidentally treating bytes as the wrong kind of number.
  2. Finalize with finalize to rewrite the header.
  3. Optionally call update_header_stats before finalize to fill in dmin/dmax/dmean/rms.

Four backends through the same builder:

BackendBuilder methodBest for
Writerfinish()General use, writes straight to disk
MmapWriterfinish_mmap()Very large files (mmap feature)
GzipWriterfinish_gzip()Compressed output (gzip feature)
[Bzip2Writer]finish_bzip2()Compressed output (bzip2 feature)

§Data modes

MRC files encode voxels in one of several numeric modes. Mode represents them at runtime; Voxel ties each Rust type to its mode at compile time, catching mismatches before any data is read or written.

ModeRust typeTypical use
Int8 (0)i8Binary masks
Int16 (1)i16Raw cryo-EM density
Float32 (2)f32Processed / reconstructed density
Uint16 (6)u16Segmentation labels
Float16 (12)f16Half-precision storage (feature f16)

Complex (Int16Complex, Float32Complex) and packed 4-bit (Packed4Bit) modes are also available — see their individual docs.

When you don’t know the mode ahead of time, use slices_f32 which converts any mode to f32.

§Headers

The Header struct mirrors the 1024-byte MRC-2014 fixed header. Every field is a typed public field — dimensions, cell parameters, axis mapping, density statistics, text labels, and more.

use mrc::Header;
let h = Header::new();
assert_eq!(h.map, *b"MAP ");

For fluent construction with validation, use HeaderBuilder:

use mrc::HeaderBuilder;
let header = HeaderBuilder::new()
    .shape([512, 512, 256])
    .mode::<f32>()
    .build()?;

Three validation levels:

§Philosophy

This crate does one thing — read and write MRC files. It does not do array arithmetic, image processing, or type conversion beyond a few MRC-specific shortcuts (slices_f32, slices_mode0, slices_u8). Leave those to crates like ndarray, or your own code.

§Feature flags

FeatureDescriptionDefault
mmapMemory-mapped readers and writers
f16Half-precision float via the half crate
simdAVX2 / NEON acceleration for integer→f32
parallelParallel encoding via rayon
gzipGzip-compressed I/O
bzip2Bzip2-compressed I/O

§Advanced topics

§Error handling

Fallible functions return Result<T, Error>. The errors you will actually hit in practice:

  • Io — the file could not be read or written
  • InvalidHeader — not a valid MRC file
  • ModeMismatch — calling slices::<f32>() on an Int16 file; use slices_f32() instead
  • BoundsError — read or write outside the volume
  • FileSizeMismatch — file truncated or has trailing garbage

HeaderValidationError gives fine-grained diagnostics for header problems (bad dimensions, wrong MAP field, invalid NVERSION …).

§Endianness

MRC files encode byte order via a 4-byte MACHST stamp. FileEndian handles detection and conversion automatically. New files are always little-endian, matching modern hardware and the Python mrcfile library.

§FEI extended headers

Data from Thermo Fisher / FEI microscopes often carries FEI1 or FEI2 extended headers — one metadata record per image section. Fei1Metadata and Fei2Metadata parse these into named fields (dose, defocus, stage position, pixel size, magnification …).

use mrc::parse_fei1_records;
let bytes = reader.ext_header_bytes();
if let Some(records) = parse_fei1_records(bytes) {
    for r in &records {
        println!("tilt {:.1}°, defocus {:.1} µm", r.alpha_tilt, r.defocus);
    }
}

§File validation

validate_full runs comprehensive checks on a file — header, size, endianness, data statistics (1 % tolerance), and NaN / Inf scanning. Returns a ValidationReport with categorised issues.

If you already have an open Reader, use validate_reader to avoid re-opening the file.

Modules§

validate
MRC file validation infrastructure.

Structs§

Fei1Metadata
FEI extended header metadata types and parsers. Common FEI1 metadata fields.
Fei2Metadata
FEI extended header metadata types and parsers. FEI2 metadata extends FEI1 with additional v2 fields.
Float32Complex
Header
HeaderBuilder
Builder for constructing validated MRC headers.
Int16Complex
MmapReader
Memory-mapped MRC reader (requires mmap feature). Memory-mapped MRC file reader.
MmapWriter
Memory-mapped MRC writer (requires mmap feature). Memory-mapped MRC file writer.
Packed4Bit
4-bit data packed two values per byte (mode 101).
Reader
Buffered MRC reader with lazy slice/slab iterators. In-memory buffered MRC file reader.
RegionIter
Lazy iterator over MRC voxel blocks. Lazy iterator over a volume as a sequence of VoxelBlocks.
SlabStepper
Stepping strategies for RegionIter. Step k contiguous Z-slices at a time ([nx, ny, k]).
SliceStepper
Stepping strategies for RegionIter. Step one Z-plane at a time ([nx, ny, 1]).
TileStepper
Stepping strategies for RegionIter. Step arbitrary 3D tiles across a volume.
VolumeShape
Volume geometry in voxels.
VoxelBlock
A contiguous chunk of voxel data with a 3D offset and shape.
Writer
MRC file writer and its builder. MRC file writer using standard file I/O.
WriterBuilder
MRC file writer and its builder. Builder for configuring and creating a new MRC file writer.
f16
Half-precision floating point type (requires f16 feature). A 16-bit floating point type implementing the IEEE 754-2008 standard binary16 a.k.a “half” format.

Enums§

ComplexToRealStrategy
Strategy for converting complex numbers to real values.
Error
The top-level error type for MRC I/O operations.
FileEndian
Endianness of MRC file data. Endianness of MRC file data.
HeaderValidationError
Errors that can occur during detailed header validation.
M0Interpretation
Interpretation of Mode 0 (8-bit) data for legacy files.
Mode
MRC data mode defining the on-disk representation of voxel values.

Constants§

FEI1_RECORD_SIZE
FEI extended header metadata types and parsers. Size of a single FEI1 metadata record, in bytes.
FEI2_RECORD_SIZE
FEI extended header metadata types and parsers. Size of a single FEI2 metadata record, in bytes.

Traits§

Voxel
Trait for MRC voxel types with compile-time mode tracking.

Functions§

convert_u8_slice_to_u16
Widen a u8 slice to u16 for writing as Mode 6 (Uint16).
convert_u16_slice_to_u8
Narrow a u16 slice to u8, returning Err if any value exceeds 255.
create
Create a new MRC file for writing.
open
Open an MRC file for reading, auto-detecting gzip or bzip2 compression.
parse_fei1_records
FEI extended header metadata types and parsers. Parse a raw extended header byte slice as a vector of FEI1 records.
parse_fei2_records
FEI extended header metadata types and parsers. Parse a raw extended header byte slice as a vector of FEI2 records.
reinterpret_m0
Reinterpret Mode 0 (8-bit) data as signed or unsigned and convert to f32.

Type Aliases§

GzipWriter
Gzip-compressed MRC writer (requires gzip feature). Gzip-compressed MRC file writer.