Crates.io | mrc |
lib.rs | mrc |
version | 0.1.1 |
created_at | 2025-08-13 18:15:12.355828+00 |
updated_at | 2025-08-27 08:49:57.272909+00 |
description | Zero-copy, zero-allocation MRC-2014 file format reader/writer for Rust |
homepage | https://github.com/elemeng/mrc |
repository | https://github.com/elemeng/mrc |
max_upload_size | |
id | 1793932 |
size | 153,125 |
Zero-copy, zero-allocation, no_std-friendly MRC-2014 file format reader/writer for Rust
A high-performance, memory-efficient library for reading and writing MRC (Medical Research Council) format files used in cryo-electron microscopy and structural biology. Designed for scientific computing with safety and performance as top priorities.
๐ Zero-copy: Direct memory mapping with no intermediate buffers
๐ฆ no_std: Works in embedded environments and WebAssembly
โก Blazing fast: Optimized for cache locality and branch prediction
๐ 100% safe: No unsafe blocks in public API
๐ Complete: All MRC-2014 data types and header fields
๐งช Production ready: Used in cryo-EM processing pipelines
[dependencies]
mrc = "0.1"
# For full features
mrc = { version = "0.1", features = ["std", "mmap", "f16"] }
use mrc::MrcView;
// Read from memory
let data = std::fs::read("protein.mrc")?;
let view = MrcView::new(data)?;
// Get dimensions
let (nx, ny, nz) = view.dimensions();
println!("Volume: {}ร{}ร{} voxels", nx, ny, nz);
// Access data based on type
match view.mode() {
Some(Mode::Float32) => {
let floats = view.view::<f32>()?;
println!("First pixel: {}", floats[0]);
}
Some(Mode::Int16) => {
let ints = view.view::<i16>()?;
println!("First pixel: {}", ints[0]);
}
_ => println!("Unsupported data type"),
}
use mrc::{Header, Mode, MrcFile};
// Create header for 3D volume
let mut header = Header::new();
header.nx = 512;
header.ny = 512;
header.nz = 256;
header.mode = Mode::Float32 as i32;
// Set physical dimensions (ร
ngstrรถms)
header.xlen = 256.0;
header.ylen = 256.0;
header.zlen = 128.0;
// Write to file
let mut file = MrcFile::create("output.mrc", header)?;
file.write_data(&your_data)?;
๐ Issues & Bugs
Found a bug? Open an issue โ weโll triage fast.
๐ก Feature Requests & Ideas
Tag your issue with [Feature request]
โ the community helps shape the roadmap.
๐ฆ Pull Requests
Ready to code? See Contributing below. Fork โ branch โ PR. All skill levels welcome; CI and review keep quality high.
Built with โค๏ธ for every cryo-EM enthusiast.
Type | Purpose | Example |
---|---|---|
[Header ] |
1024-byte MRC header | let header = Header::new(); |
[Mode ] |
Data type enumeration | Mode::Float32 |
[MrcView ] |
Read-only data view | MrcView::new(data)? |
[MrcViewMut ] |
Mutable data view | MrcViewMut::new(data)? |
[MrcFile ] |
File-backed access | MrcFile::open("file.mrc")? |
[MrcMmap ] |
Memory-mapped access | MrcMmap::open("large.mrc")? |
The MRC header contains 56 fields (1024 bytes total) with complete metadata:
use mrc::Header;
let mut header = Header::new();
// Basic dimensions
header.nx = 2048; // X dimension
header.ny = 2048; // Y dimension
header.nz = 512; // Z dimension
// Data type (see Mode enum)
header.mode = Mode::Float32 as i32;
// Physical dimensions in ร
ngstrรถms
header.xlen = 204.8; // Physical X length
header.ylen = 204.8; // Physical Y length
header.zlen = 102.4; // Physical Z length
// Cell angles for crystallography
header.alpha = 90.0;
header.beta = 90.0;
header.gamma = 90.0;
// Axis mapping (1=X, 2=Y, 3=Z)
header.mapc = 1; // Fastest changing axis
header.mapr = 2; // Second fastest axis
header.maps = 3; // Slowest changing axis
// Data statistics
header.dmin = 0.0; // Minimum data value
header.dmax = 1.0; // Maximum data value
header.dmean = 0.5; // Mean data value
header.rms = 0.1; // RMS deviation
Field | Type | Description | Range |
---|---|---|---|
nx, ny, nz |
i32 |
Image dimensions | > 0 |
mode |
i32 |
Data type | 0-4, 6, 12, 101 |
mx, my, mz |
i32 |
Map dimensions | Usually same as nx/ny/nz |
xlen, ylen, zlen |
f32 |
Cell dimensions (ร ) | > 0 |
alpha, beta, gamma |
f32 |
Cell angles (ยฐ) | 0-180 |
mapc, mapr, maps |
i32 |
Axis mapping | 1, 2, 3 |
amin, amax, amean |
f32 |
Origin coordinates | -โ to โ |
ispg |
i32 |
Space group number | 0-230 |
nsymbt |
i32 |
Extended header size | โฅ 0 |
extra |
[u8; 100] |
Extra space | - |
origin |
[i32; 3] |
Origin coordinates | - |
map |
[u8; 4] |
Map string | "MAP " |
machst |
[u8; 4] |
Machine stamp | - |
rms |
f32 |
RMS deviation | โฅ 0 |
nlabl |
i32 |
Number of labels | 0-10 |
label |
[[u8; 80]; 10] |
Text labels | - |
[Mode ] |
Value | Rust Type | Bytes | Description | Use Case |
---|---|---|---|---|---|
Int8 |
0 | i8 |
1 | Signed 8-bit integer | Binary masks |
Int16 |
1 | i16 |
2 | Signed 16-bit integer | Cryo-EM density |
Float32 |
2 | f32 |
4 | 32-bit float | Standard density |
Int16Complex |
3 | i16 |
2ร2 | Complex 16-bit | Phase data |
Float32Complex |
4 | f32 |
4ร2 | Complex 32-bit | Fourier transforms |
Uint16 |
6 | u16 |
2 | Unsigned 16-bit | Segmentation |
Float16 |
12 | f16 |
2 | 16-bit float | Memory efficiency |
use mrc::{MrcView, Error, Mode};
// From byte slice
let view = MrcView::new(header, data)?;
// Type-safe access
match view.mode() {
Some(Mode::Float32) => {
let floats = view.view::<f32>()?;
// floats: &[f32] - zero-copy slice
let sum: f32 = floats.iter().sum();
println!("Total intensity: {}", sum);
}
Some(Mode::Int16) => {
let ints = view.view::<i16>()?;
// Process 16-bit integer data
let max = ints.iter().max().unwrap_or(&0);
}
_ => return Err(Error::TypeMismatch),
}
// Raw byte access
let raw_bytes = view.data(); // &[u8]
let slice = view.slice_bytes(0..1024)?; // &[u8]
use mrc::{MrcViewMut, Header};
// Create mutable view
let mut view = MrcViewMut::new(header, &mut data)?;
// Modify data
{
let mut floats = view.view_mut::<f32>()?;
for val in floats.iter_mut() {
*val = val.max(0.0); // Remove negative values
}
}
// Update header statistics
view.update_statistics()?;
// Modify header
let mut new_header = view.header().clone();
new_header.dmin = 0.0;
new_header.dmax = 1.0;
view.set_header(new_header)?;
use mrc::{MrcFile, MrcMmap, Mode};
// Standard file I/O
let file = MrcFile::open("data.mrc")?;
let view = file.view()?;
// Memory-mapped for large files (requires mmap feature)
#[cfg(feature = "mmap")]
let mmap = MrcMmap::open("large_volume.mrc")?;
#[cfg(feature = "mmap")]
let view = mmap.view()?;
// Write new file
let header = Header {
nx: 512, ny: 512, nz: 256,
mode: Mode::Float32 as i32,
..Header::new()
};
let mut file = MrcFile::create("output.mrc", header)?;
file.write_data(&your_float_data)?;
use mrc::MrcView;
struct Volume3D<'a> {
view: MrcView<'a>,
nx: usize,
ny: usize,
nz: usize,
}
impl<'a> Volume3D<'a> {
fn new(view: MrcView<'a>) -> Result<Self, mrc::Error> {
let (nx, ny, nz) = view.dimensions();
Ok(Self { view, nx, ny, nz })
}
fn get_slice(&self, z: usize) -> Result<&[f32], mrc::Error> {
if z >= self.nz {
return Err(mrc::Error::InvalidDimensions);
}
let slice_size = self.nx * self.ny;
let start = z * slice_size;
let floats = self.view.view::<f32>()?;
floats.get(start..start + slice_size)
.ok_or(mrc::Error::InvalidDimensions)
}
fn get_voxel(&self, x: usize, y: usize, z: usize) -> Result<f32, mrc::Error> {
let index = z * self.nx * self.ny + y * self.nx + x;
let floats = self.view.view::<f32>()?;
floats.get(index).copied()
.ok_or(mrc::Error::InvalidDimensions)
}
}
use mrc::{MrcFile, Mode};
use rayon::prelude::*;
fn process_directory(dir: &str) -> Result<(), Box<dyn std::error::Error>> {
use std::fs;
let entries = fs::read_dir(dir)?
.filter_map(|e| e.ok())
.filter(|e| e.path().extension().map_or(false, |ext| ext == "mrc"));
entries.par_bridge().try_for_each(|entry| {
let path = entry.path();
let file = MrcFile::open(&path)?;
let view = file.view()?;
if let Some(Mode::Float32) = view.mode() {
let data = view.view::<f32>()?;
let stats = calculate_statistics(data);
println!("{:?}: RMS={:.3}", path.file_name(), stats.rms);
}
Ok::<_, Box<dyn std::error::Error>>(())
})?;
Ok(())
}
#[derive(Debug)]
struct Statistics {
min: f32,
max: f32,
mean: f32,
rms: f32,
}
fn calculate_statistics(data: &[f32]) -> Statistics {
let min = data.iter().fold(f32::INFINITY, |a, &b| a.min(b));
let max = data.iter().fold(f32::NEG_INFINITY, |a, &b| a.max(b));
let mean = data.iter().sum::<f32>() / data.len() as f32;
let rms = (data.iter().map(|&x| x * x).sum::<f32>() / data.len() as f32).sqrt();
Statistics { min, max, mean, rms }
}
Feature | Description | Default | Example |
---|---|---|---|
std |
Standard library support | โ | File I/O, Error trait |
mmap |
Memory-mapped I/O | โ | Large file processing |
file |
File operations | โ | MrcFile::open() |
f16 |
Half-precision support | โ | Requires nightly Rust |
[dependencies]
mrc = { version = "0.1", default-features = false }
use mrc::{Header, MrcView, Mode};
// Works without std library
let view = MrcView::new(header, data)?;
let floats = view.view::<f32>()?;
// Process data in embedded/RTOS environments
Awayls using only features that you need to minimize sizes of the package
// Use memory mapping for large files
#[cfg(feature = "mmap")]
let view = MrcMmap::open("large.mrc")?.view()?;
// Cache data size calculations
let data_size = view.header().data_size();
// Use aligned access for SIMD
let aligned = view.data_aligned::<f32>()?;
# Run all tests
cargo test --all-features
# Run specific test
cargo test test_header_validation
# Run with coverage
# install tarpaulin if not existed: cargo install cargo-tarpaulin
cargo tarpaulin --all-features
# Test with real MRC files
cargo test --test real_mrc_files
# Benchmark performance
cargo bench
# Generate test MRC files
cargo run --example generate_mrc_files
# Validate MRC files
cargo run --example validate -- data/*.mrc
We welcome contributions! Here's how to get started:
git checkout -b feature/amazing-feature
git commit -m 'Add amazing feature'
git push origin feature/amazing-feature
# Clone repository
git clone https://github.com/your-org/mrc.git
cd mrc
# Install nightly Rust (required for f16)
rustup install nightly
rustup override set nightly
# Install dependencies
cargo build --all-features
# Run tests
cargo test --all-features
# Check formatting
cargo fmt --check
# Run clippy
cargo clippy --all-features
MIT License
Copyright (c) 2024 mrc contributors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
Made with โค๏ธ by the cryo-EM community for the scientific computing world
[Zero-copy โข Zero-allocation โข 100% safe Rust]