Expand description
Read and write MRC-2014 files — the standard in cryo-EM and structural biology. Handles byte-order, type conversion, and compression so you can focus on your science. SIMD-accelerated, mmap-enabled.
See the README for installation.
The companion mrc-cli crate provides
a command-line tool for inspection, validation, conversion, and export.
§Quick example
use mrc::{read_as, write_as};
// Read an entire volume in one call
let (header, data): (_, Vec<f32>) = read_as("density.mrc")?;
println!("{}×{}×{} volume, {} voxels",
header.nx, header.ny, header.nz, data.len());
// Write a volume in one call
write_as("output.mrc", &data, [512, 512, 256])?;§Reading files
Open any MRC file with open() or Reader::open. Compression is
detected from magic bytes — no need to hint 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, each returning DataBlock chunks
whose DataView variant is determined by the file’s mode — no
compile-time type guessing, no ModeMismatch errors at runtime:
slices— one Z-plane at a timeslabs— batches ofkZ-planestiles— arbitrary 3D blocksvolumes— sub-volumes in a volume stacksubregion— a single block by coordinateread_volume— the entire volume as one block
The file’s voxel type is known at runtime via reader.mode():
for slice in reader.slices() {
let block = slice?;
match block.data() {
mrc::DataView::Float32(data) => { /* process f32 slice */ }
mrc::DataView::Int16(data) => { /* process i16 slice */ }
mrc::DataView::Uint16(data) => { /* process u16 slice */ }
mrc::DataView::Int8(data) => { /* process i8 slice */ }
other => panic!("unhandled mode: {:?}", other),
}
}Or just use convert::<f32>() — the fire-and-forget
option that reads any mode as f32:
for slice in reader.convert::<f32>().slices() {
let block = slice?;
println!("z={}: {} voxels", block.offset[2], block.data.len());
}
// Full volume in one call:
let block = reader.convert::<f32>().read_volume()?;The same iteration methods — slabs,
tiles, subregion — plus
with_complex_strategy and
with_m0_interpretation
are all available on the returned converter.
§Special-mode reads (Packed4Bit and Mode 0)
These modes have no Voxel implementation — there is no single Rust
type that maps to them safely at compile time — so dedicated methods read
directly as u8 or f32:
slices_u8/slabs_u8— unpack Packed4Bit nibbles, or narrow Uint16, tou8read_volume_u8— full volume asu8(Packed4Bit only)slices_mode0/slabs_mode0— read Mode 0 asf32, choosing signed or unsigned interpretation
§Reading from memory or streams
When your data is already in memory (e.g. from a camera readout, network
stream, or embedded resource), use Reader::from_reader or
Reader::from_bytes:
use mrc::Reader;
use std::io::Cursor;
let bytes = std::fs::read("density.mrc")?;
let reader = Reader::from_reader(Cursor::new(bytes))?;Permissive variants Reader::from_reader_permissive and
Reader::from_bytes_permissive accept non-critical header issues as
warnings without failing, mirroring Reader::open_permissive.
§Large files
When the file does not fit in RAM, Reader::open automatically uses
memory-mapped I/O (requires the mmap feature). Same iterator API,
zero-copy DataBlock views, OS-managed paging.
For buffered readers (in-memory buffers, compressed files), the default reader methods also return zero-copy views when the requested block is a native-endian contiguous full-row slab.
§Quirky files
Common microscope quirks (NVERSION left at 0, "MAP\0" instead of "MAP ")
are handled transparently by open() — no special flags needed.
For esoteric or severely non-standard files, use
Reader::open_permissive which turns non-critical header issues into
warnings instead of hard errors, and allows opening files that are shorter
than the header declares. Use is_truncated to
detect truncated data after a permissive open:
let (reader, warnings) = Reader::open_permissive("legacy.mrc")?;
if reader.is_truncated() {
eprintln!("warning: file is incomplete");
}
for w in &warnings { eprintln!("note: {w}"); }§Writing files
Use create() to get a WriterBuilder, set the shape and voxel type,
then call finish.
For the simplest case — write an entire volume — use write_as() or
Writer::set_data:
use mrc::write_as;
let data = vec![0.0f32; 256 * 256 * 128];
write_as("output.mrc", &data, [256, 256, 128])?;For streaming writes (one slice at a time), use the builder API:
use mrc::{create, VoxelBlock};
let mut writer = create("output.mrc")
.shape([256, 256, 128])
.mode::<f32>()
.finish()?;
// Write some data
writer.write_block(&VoxelBlock::new(
[0, 0, 0], [256, 256, 1],
vec![0.0f32; 256 * 256],
)?)?;
// Optionally compute and store density statistics
writer.update_header_stats()?;
// Finalize — required, rewrites header with final metadata
writer.finalize()?;The lifecycle:
-
Write blocks with
write_block. The typeTmatches the file’s mode — a compile-time check that prevents accidentally treating bytes as the wrong kind of number. Usewrite_block_asfor automatic conversion (e.g. writef32data to an Int16 or Float16 file)For Uint16 files,
write_u8_blockauto-widensu8data; for Packed4Bit files,write_u4_blockpacksu8values (0–15) two-per-byte.
convert_u8_slice_to_u16— widen&[u8]toVec<u16>for writing to Uint16 files (used internally bywrite_u8_block)convert_u16_slice_to_u8— narrow&[u16]toVec<u8>(returnsErrif any value exceeds 255)reinterpret_m0— reinterpret Mode 0 data as signed or unsignedf32
- Optionally call
update_header_statsto fill indmin/dmax/dmean/rms. - Finalize with
finalizeto rewrite the header with final metadata. Required — without it the header is stale.
Four backends through the same builder, plus in-memory output:
| Backend | Builder method | Best for |
|---|---|---|
Writer | finish() | General use, writes straight to disk |
Writer (in-memory) | finish_buffer() | Memory buffer, e.g. testing or in-memory processing |
Writer (mmap) | finish_mmap() | Very large files (mmap feature) |
Writer (gzip) | finish_gzip() | Compressed output (gzip feature) |
Writer (bzip2) | finish_bzip2() | Compressed output (bzip2 feature) |
Note: The builder is the recommended path. The lower-level
Writer::from_writer,Writer::from_writer_mmap,Writer::from_writer_gzip, and [Writer::from_writer_bzip2] constructors are also available for callers who already have aHeaderor a custom I/O target — see their respective docs for details.
Compressed writers support configurable CompressionLevel level via
WriterBuilder::compression:
use mrc::{CompressionLevel, create};
let mut writer = create("output.mrc.gz")
.shape([256, 256, 128])
.mode::<f32>()
.compression(CompressionLevel::Best)
.finish_gzip()?;§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 flows.
| Mode | Rust type | Typical use |
|---|---|---|
Int8 (0) | i8 | Binary masks |
Int16 (1) | i16 | Raw cryo-EM density |
Float32 (2) | f32 | Processed / reconstructed density |
Int16Complex (3) | Int16Complex | Complex data (i16 real + i16 imag) |
Float32Complex (4) | Float32Complex | Complex data (f32 real + f32 imag) |
Uint16 (6) | u16 | Segmentation labels |
Float16 (12) | f16 | Half-precision storage (feature f16) |
Packed4Bit (101) | u8 via slices_u8 | 4-bit packed data; no Voxel impl |
Packed 4-bit data is handled transparently by the unified API:
convert::<f32>() unpacks nibbles to f32,
slices_u8 / slabs_u8 unpack
to u8 (0–15), and write_u4_block packs
u8 values back.
When you don’t know the mode ahead of time, use
convert::<f32>() which converts any mode to f32.
§Feature flags
| Feature | Description | Default |
|---|---|---|
mmap | Memory-mapped readers and writers | ✅ |
f16 | Half-precision float via the half crate | ✅ |
simd | AVX2 / NEON acceleration for integer↔f32, f16↔f32, byte-swap, stats, and f32→integer clamping | ✅ |
parallel | Parallel decode/convert/encode via rayon — transparent for any block ≥512³ | ✅ |
gzip | Gzip-compressed I/O | ✅ |
bzip2 | Bzip2-compressed I/O | ❌ |
ndarray | Return volumes as ndarray::Array3<T> via to_ndarray() | ❌ |
serde | Serialize/Deserialize support via serde | ❌ |
use ndarray::Array3;
let arr: Array3<f32> = reader.convert::<f32>().to_ndarray()?;
println!("{}×{}×{} array", arr.shape()[0], arr.shape()[1], arr.shape()[2]);§Headers
The Header struct mirrors the 1024-byte MRC-2014 fixed header.
Every field is a typed public member — 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>()
.cell_lengths(1.0, 1.0, 1.0)
.cell_angles(90.0, 90.0, 90.0)
.origin([0.0, 0.0, 0.0])
.nstart([0, 0, 0])
.sampling([512, 512, 256])
.axis_mapping([1, 2, 3])
.add_label("reconstructed map")
.build()?;Three validation levels:
validate— quick yes / novalidate_detailed— tells you exactly what is wrong viaHeaderValidationErrorvalidate_permissive— warnings for non-critical issues
§Volume type helpers
The header distinguishes four volume types. Configure them explicitly when creating files that are not single 3D volumes:
let mut h = Header::new();
h.nx = 64; h.ny = 64; h.nz = 120;
h.mx = 64; h.my = 64; h.mz = 30;
h.set_volume_stack(30); // ispg = 401, mz = 30
assert!(h.is_volume_stack());
assert_eq!(h.logical_shape(), [4, 30, 64, 64]); // 4 sub-volumes§Convenience API
The Header provides computed properties for common queries:
| Method | Returns | Description |
|---|---|---|
validate() | bool | Quick validity check |
validate_detailed() | Result<(), HeaderValidationError> | Full structural validation with diagnostics |
validate_permissive() | Result<Vec<String>> | Lenient validation, returns warnings |
is_single_image() | bool | nz == 1 |
is_image_stack() | bool | ispg == 0 |
is_volume() | bool | Not a stack and not an image stack |
is_volume_stack() | bool | ispg in 401–630 |
set_image_stack() | () | Set as image stack (ispg = 0, mz = 1) |
set_volume() | () | Set as single volume (ispg = 1, mz = nz) |
set_volume_stack(mz) | () | Set as volume stack with sub-volume size mz |
logical_shape() | [usize; 4] | [nvolumes, mz, ny, nx] |
exttyp() | [u8; 4] | Extended header type from extra[8..12] |
exttyp_str() | Result<&str> | Extended header type as string (UTF-8 decoded) |
nversion() | i32 | NVERSION from extra[12..16] |
get_labels() | Vec<String> | Read up to nlabl non-empty labels |
label_at(i) | Option<&str> | Trimmed label at index i, or None if empty |
add_label(text) | () | Append a text label (FIFO when full) |
density_stats() | (f32, f32, f32, f32) | (dmin, dmax, dmean, rms) |
sampling() | [i32; 3] | [mx, my, mz] |
voxel_size() | [f32; 3] | Å/pixel = cella / mxyz |
cell_lengths() | [f32; 3] | [xlen, ylen, zlen] |
cell_angles() | [f32; 3] | [alpha, beta, gamma] |
cell_volume() | f64 | Unit cell volume in ų (triclinic formula) |
nstart() | [i32; 3] | [nxstart, nystart, nzstart] |
detect_endian() | FileEndian | Detect byte order from MACHST |
set_file_endian(endian) | () | Set MACHST and re-encode NVERSION |
is_standard_map() | bool | MAP field is exactly "MAP " |
detect_imod() | Option<ImodInfo> | Detect IMOD stamp in extra bytes |
is_y_inverted() | bool | true when mapr == -2 (IMOD convention) |
decode_from_bytes(bytes) | Header | Parse from raw 1024 bytes (auto endian) |
decode_from_bytes_with_info(bytes) | (Header, Option<EndianFallbackWarning>) | Parse with endian fallback diagnostics |
encode_to_bytes(&mut [u8; 1024]) | () | Encode to raw bytes |
§Manual header parsing
Decode a raw 1024-byte header block with automatic endianness detection:
use mrc::Header;
let raw = [0u8; 1024];
// ... fill raw bytes from file ...
let header = Header::decode_from_bytes(&raw);When the MACHST byte-order stamp is wrong (common in some EPU files), the decoder tries the opposite endianness automatically:
let (header, warning) = Header::decode_from_bytes_with_info(&raw);
if let Some(w) = warning {
eprintln!("byte-order fallback used: {w}");
}§Extended headers
Many MRC files carry additional metadata after the 1024-byte fixed header
in an extended header region. The type is identified by the 4-byte
exttyp field in the header’s extra[8..12].
The ExtHeaderType enum identifies the format without parsing:
use mrc::{Header, ExtHeaderType};
let header = Header::new();
match ExtHeaderType::from_header(&header) {
ExtHeaderType::Fei1 => println!("FEI Type 1"),
ExtHeaderType::Ccp4 => println!("CCP4"),
ExtHeaderType::Unknown(id) => {
println!("Unknown: {:?}", std::str::from_utf8(&id));
}
_ => {}
}Instead of calling individual parser functions, use the auto-dispatch method on any open reader:
use mrc::ExtHeaderData;
match reader.parse_extended_header() {
ExtHeaderData::Fei1(records) => {
println!("FEI1 tilt series ({} records)", records.len());
for r in &records {
println!(" tilt {:.1}°, defocus {:.1} µm",
r.alpha_tilt, r.defocus);
}
}
ExtHeaderData::Ccp4(records) => {
println!("CCP4 symmetry ({} records)", records.len());
}
ExtHeaderData::Seri(records) => {
println!(" first tilt: {:.1}°", records[0].alpha_tilt);
}
ExtHeaderData::None => println!("No recognized extended header"),
_ => {}
}Typed convenience methods give direct access without pattern matching:
if let Some(records) = reader.fei1_metadata() {
println!("{} FEI1 records", records.len());
}
if let Some(imod) = reader.imod_metadata() {
println!("IMOD type {:?}, tilt increment {:.1}°",
imod.image_type, imod.tilt_increment);
}Available: fei1_metadata,
fei2_metadata,
ccp4_records,
mrco_records,
seri_records,
agar_records,
imod_metadata.
§Error handling
Fallible functions return Result<T, Error>. Match on specific variants
to handle different failure modes:
use mrc::Error;
fn describe(err: &Error) -> &str {
match err {
Error::Io(_) => "I/O failure",
Error::InvalidHeader => "not a valid MRC file",
Error::ModeMismatch { .. } => "wrong voxel type for this file",
Error::BoundsError { .. } => "block outside volume",
Error::FileSizeMismatch { .. } => "file truncated or has trailing data",
_ => "other",
}
}The errors you will actually hit in practice:
Io— the file could not be read or writtenInvalidHeader— not a valid MRC fileModeMismatch— writing aVoxelBlock<i16>to a Float32 file; usewrite_block_asinsteadBoundsError— read or write outside the volumeNotAVolumeStack— callingvolumes()on a file that is not a volume stackFileSizeMismatch— 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 at file offset 212.
FileEndian represents the detected byte order:
use mrc::FileEndian;
let stamp: [u8; 4] = [0x44, 0x44, 0x00, 0x00]; // little-endian
assert_eq!(FileEndian::from_machst(&stamp), FileEndian::LittleEndian);Use FileEndian::native to query the host platform, and
reader.endian() to get a file’s actual byte order.
New files are always little-endian, matching modern hardware and the Python
mrcfile library.
The crate has a fallback: if the MODE field is invalid under the detected endianness, the opposite byte order is tried. This handles files with a wrong MACHST stamp but correct data.
§Compression auto-detection
Reader::open reads the first two bytes of the file:
| Magic bytes | Format |
|---|---|
\x1f\x8b | Gzip |
BZ | Bzip2 |
| anything else | Plain |
Plain MRC files are memory-mapped or buffered directly. Compressed files
are fully decompressed into memory on open, with a hard cap of
DEFAULT_MAX_DECOMPRESSED_BYTES (256 GiB) to prevent bombs.
Use Reader::open_gzip_with_limit or
[Reader::open_bzip2_with_limit] for a custom limit.
Large compressed files: If the uncompressed data exceeds available RAM, decompress with
gunziporbunzip2first, then useReader::openfor zero-copy access — the OS pages data on demand without loading the whole file into memory.
§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 categorized issues:
use mrc::validate::{validate_full, Severity};
let report = validate_full("protein.mrc", false)?;
if !report.is_valid() {
for issue in &report.issues {
if issue.severity == Severity::Error {
eprintln!("[{}] {}", issue.category, issue.message);
}
}
}If you already have an open Reader, use
validate_reader to avoid re-opening
the file.
§Real-world workflows
§1. Process a tilt series
A common cryo-EM workflow: open a tilt series, read the FEI metadata, then iterate over slices:
use mrc::open;
let reader = open("tiltseries.mrc")?;
println!("{}×{}×{} voxels, mode {:?}",
reader.shape().nx, reader.shape().ny, reader.shape().nz,
reader.mode());
// Read FEI extended header metadata via convenience method
if let Some(records) = reader.fei1_metadata() {
for (i, r) in records.iter().enumerate() {
println!("tilt {i}: α={:.1}°, defocus={:.1} µm",
r.alpha_tilt, r.defocus);
}
}
// Process each slice
for slice in reader.convert::<f32>().slices() {
let block = slice?;
// block.data: Vec<f32> — ready for filtering, CTF correction, etc.
}If a file fails to open, try open_permissive
for lenient header handling, or validate_full
to diagnose the issue.
§2. Write a processed map
Always call finalize — without it the header is
stale and density statistics will be wrong (tools display wrong contrast).
use mrc::create;
let mut writer = create("reconstructed.mrc")
.shape([512, 512, 256])
.mode::<f32>()
.finish()?;
for z in 0..256 {
let slice = vec![0.0f32; 512 * 512];
writer.write_block(&mrc::VoxelBlock::new(
[0, 0, z], [512, 512, 1], slice,
)?)?;
}
writer.update_header_stats()?;
writer.finalize()?;§3. Read subtomogram averages from a volume stack
Volume stacks (ISPG 401–630) pack multiple sub-volumes into one file,
each mz slices thick. Use volumes to iterate:
for volume in reader.volumes()? {
let vol = volume?;
println!("sub-volume at z={} ({}×{}×{} voxels)",
vol.offset()[2], vol.shape()[0], vol.shape()[1], vol.shape()[2]);
}§Troubleshooting
| Error | Likely cause | What to try |
|---|---|---|
InvalidHeader | Not an MRC file, or header corruption | Run mrc validate file.mrc; try open_permissive |
FileSizeMismatch | File truncated or has trailing garbage | Re-download or check mrc validate output |
ModeMismatch | Writing a VoxelBlock<i16> to an Float32 file | Use write_block_as — auto-converts any mode |
NotAVolumeStack | Calling volumes() on a non-stack file | Check reader.is_volume_stack() first |
BoundsError | Block outside volume | Check offset + shape against dimensions |
BlockShapeMismatch | Data length doesn’t match block shape | Verify sx * sy * sz * sizeof(T) matches data length |
UnsupportedMode | Unrecognized mode, or mode needs the f16 feature | Enable f16 feature or convert with another tool |
Io error | File permissions, filesystem issue | Check the file path and permissions |
| Values look wrong | Endianness mismatch | The endianness fallback handles most cases; try mrc validate |
§Philosophy
This crate does one thing — read and write MRC files. It does no array
arithmetic, image processing, or type conversion beyond MRC-specific
shortcuts (convert::<f32>(), slices_mode0, slices_u8).
Leave those to crates like ndarray, or your own code.
Modules§
- validate
- MRC file validation infrastructure.
Structs§
- Agar
Record - Agard extended header record.
- Ccp4
Record - A single CCP4 symmetry record — an 80-character text line containing space group symmetry operators.
- Convert
Reader - Auto-conversion wrapper returned by
Reader::convert. A reader wrapper that auto-converts all voxel data to typeT. - Fei1
Metadata - Common FEI1 metadata fields.
- Fei2
Metadata - FEI2 metadata extends FEI1 with additional v2 fields.
- Float32
Complex - A complex number with 32-bit float real and imaginary components.
- Header
- Mirror of the 1024-byte MRC-2014 fixed header.
- Header
Builder - Builder for constructing validated MRC headers.
- Imod
Info - IMOD-specific metadata parsed from the
extrablock (bytes 56-63). - Imod
Metadata - IMOD-specific metadata parsed from the main header’s
extrabytes. - Int16
Complex - A complex number with 16-bit signed integer real and imaginary components.
- Mrco
Record - A legacy MRCO extended header record.
- Reader
- Consolidated MRC reader with automatic mmap/buffered backend selection. MRC file reader with automatic backend selection.
- Seri
Record - SerialEM extended header record.
- Volume
Shape - Volume geometry in voxels.
- Voxel
Block - 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.
- Writer
Builder - MRC file writer and its builder. Builder for configuring and creating a new MRC file writer.
- f16
- Half-precision floating point type (requires
f16feature). A 16-bit floating point type implementing the IEEE 754-2008 standardbinary16a.k.a “half” format.
Enums§
- Complex
ToReal Strategy - Strategy for converting complex numbers to real values.
- Compression
Level - Compression level for compressed MRC writers.
- Data
Block - A block of voxel data with a 3D offset and shape, returned by the default (non-convert) reader methods.
- Data
View - Borrowed typed slice into an MRC volume’s raw data.
- Error
- The top-level error type for MRC I/O operations.
- ExtHeader
Data - Parsed extended header data, dispatched by
ExtHeaderType. - ExtHeader
Type - Known extended header types identified by the 4-byte EXTTYP field.
- File
Endian - Endianness of MRC file data. Endianness of MRC file data.
- Header
Validation Error - Errors that can occur during detailed header validation.
- Imod
Image Type - IMOD image type classification from the
idtypefield. - M0Interpretation
- Interpretation of Mode 0 (8-bit) data for legacy files.
- Mode
- MRC data mode defining the on-disk representation of voxel values.
- Owned
Data - Owned typed data — returned when a copy is unavoidable (sub-block scatter/gather, endian mismatch).
Constants§
- AGAR_
RECORD_ SIZE - Size of a single AGAR record, in bytes.
- CCP4_
RECORD_ SIZE - Size of a single CCP4 symmetry record, in bytes.
- DEFAULT_
MAX_ DECOMPRESSED_ BYTES - Default decompression safety limit for gzip/bzip2 files (256 GiB).
- FEI1_
RECORD_ SIZE - Size of a single FEI1 metadata record, in bytes.
- FEI2_
RECORD_ SIZE - Size of a single FEI2 metadata record, in bytes.
- MRCO_
RECORD_ SIZE - Size of a single MRCO record, in bytes.
- SERI_
RECORD_ SIZE - Size of a single SERI (SerialEM) record, in bytes.
Traits§
- Voxel
- Trait for MRC voxel types with compile-time mode tracking.
Functions§
- convert_
u8_ slice_ to_ u16 - Widen a
u8slice tou16for writing as Mode 6 (Uint16). - convert_
u16_ slice_ to_ u8 - Narrow a
u16slice tou8, returningErrif 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_
agar_ records - Parse extended header bytes as typed records.
- parse_
ccp4_ records - Parse extended header bytes as typed records.
- parse_
fei1_ records - Parse a raw extended header byte slice as a vector of FEI1 records.
- parse_
fei2_ records - Parse a raw extended header byte slice as a vector of FEI2 records.
- parse_
imod_ metadata - Parse IMOD metadata from the main header’s
extrabytes. - parse_
mrco_ records - Parse extended header bytes as typed records.
- parse_
seri_ records - Parse extended header bytes as typed records.
- read_as
- Read an entire MRC volume into a
Vec<T>with auto-mode detection. - reinterpret_
m0 - Reinterpret Mode 0 (8-bit) data as signed or unsigned and convert to
f32. - write_
as - Write an entire MRC volume from a
&[T]with a single call.