///|
/// Parse the bounded NIfTI-1 single-file scalar 3D/4D subset.
pub fn read(bytes : Bytes) -> Image raise NiftiError {
require(bytes.length() >= 352, "truncated", offset=0)
require(bytes.length() <= 268435456, "input_size_limit")
let le : Reader = { bytes, endian: Little, }
let be : Reader = { bytes, endian: Big, }
let endian = if le.i32(0) == 348 {
Little
} else if be.i32(0) == 348 {
Big
} else {
raise NiftiError("not_nifti1", 0)
}
let r : Reader = { bytes, endian, }
require(
copy_bytes(bytes, 344, 4) == b"n+1\x00",
"single_file_required",
offset=344,
)
let ndim = r.i16(40)
require(ndim == 3 || ndim == 4, "dimensions_3d_4d_only", offset=40)
let shape = FixedArray::make(4, 1)
let spacing = FixedArray::make(4, 1.0)
let mut count = 1
for i in 0.. 0,
"invalid_spacing",
offset=80 + i * 4,
)
}
let dtype = r.i16(70)
let width = match dtype {
2 | 256 => 1
4 | 512 => 2
8 | 768 | 16 => 4
64 => 8
_ => raise NiftiError("unsupported_datatype", 70)
}
require(r.i16(72) == width * 8, "bitpix_mismatch", offset=72)
let size = checked_product(count, width, 268435456)
let offset_float = r.f32(108)
require(
finite(offset_float) &&
offset_float >= 352 &&
offset_float <= 1048576 &&
offset_float.floor() == offset_float,
"invalid_vox_offset",
offset=108,
)
let offset = offset_float.to_int()
require(offset % 16 == 0, "unaligned_vox_offset", offset=108)
require(
offset <= bytes.length() && size == bytes.length() - offset,
"payload_length",
offset~,
)
require(
bytes[349] == b'\x00' && bytes[350] == b'\x00' && bytes[351] == b'\x00',
"reserved_extension_flags",
offset=349,
)
let mut extension_count = 0
if bytes[348] != b'\x00' {
let mut p = 352
while p < offset {
let length = r.i32(p)
require(
length >= 16 && length % 16 == 0 && length <= offset - p,
"invalid_extension_size",
offset=p,
)
require(r.i32(p + 4) >= 0, "invalid_extension_code", offset=p + 4)
extension_count += 1
require(extension_count <= 4096, "extension_count_limit", offset=p)
p += length
}
require(extension_count > 0, "missing_extension", offset=348)
}
let slope_raw = r.f32(112)
let slope = if slope_raw == 0 || slope_raw.is_nan() { 1.0 } else { slope_raw }
let intercept = if slope_raw == 0 || slope_raw.is_nan() {
0.0
} else {
r.f32(116)
}
require(finite(slope) && finite(intercept), "invalid_scaling", offset=112)
let units = bytes[123].to_int()
let spatial_unit = units & 7
let time_unit = units & 56
require(
spatial_unit <= 3 && time_unit <= 48 && (units & 192) == 0,
"invalid_units",
offset=123,
)
let qcode = r.i16(252)
let scode = r.i16(254)
require(qcode >= 0 && scode >= 0, "invalid_form_code", offset=252)
let qform = if qcode > 0 {
let qfac = r.f32(76)
require(qfac == -1 || qfac == 0 || qfac == 1, "invalid_qfac", offset=76)
Some(
quaternion_affine(
r.f32(256),
r.f32(260),
r.f32(264),
spacing[0],
spacing[1],
spacing[2],
qfac,
r.f32(268),
r.f32(272),
r.f32(276),
),
)
} else {
None
}
let sform = if scode > 0 {
let m = Array::make(16, 0.0)
m[15] = 1
for i in 0..<12 {
m[i] = r.f32(280 + i * 4)
}
Some(Affine::new(m))
} else {
None
}
{
header: copy_bytes(bytes, 0, offset),
payload: copy_bytes(bytes, offset, size),
shape,
ndim,
dtype,
width,
endian,
slope,
intercept,
spacing,
qform,
sform,
qcode,
scode,
spatial_unit,
time_unit,
extension_count,
intent: r.i16(68),
}
}