pub struct MotionParams {
pub reference: MotionReference,
pub rigid: RigidParams,
pub chain: bool,
pub chain_levels: usize,
pub declared_b0: Option<(f64, f64, f64)>,
pub min_ncc: f64,
}Expand description
Motion-correction parameters.
Fields§
§reference: MotionReferenceWhich volume the others are aligned onto.
rigid: RigidParamsParameters for each pairwise rigid registration.
RigidParams::initial is overwritten when Self::chain is set, which is the
default; set chain: false to have it respected as a fixed seed for every volume.
chain: boolSeed each volume’s search from the previous volume’s solution rather than from the identity.
A series is acquired in order, so volume t usually sits close to volume t−1 — the
strong prior RigidParams::initial exists for. The walk starts at the reference and
moves outward in both directions, so the seed is always the nearest already-solved
neighbour. All volumes register against the same fixed volume, so the parameters are
defined about one rotation centre and carry between pairs without conversion.
The reference’s own two neighbours are excepted: their nearest solved neighbour is the reference, whose solution is the identity by construction and therefore not a prior at all, so they run the full pyramid rather than trade capture range for nothing.
Worth turning off if the series order is not acquisition order, or if one volume is so corrupted that its solution would be a bad seed for its neighbour rather than a good one.
On its own a seed buys almost nothing — see Self::chain_levels, which is what makes it
pay.
chain_levels: usizePyramid levels to use for a volume that has a real chained seed, replacing
RigidParams::levels. Ignored for the reference’s two neighbours, which have no real
seed — see Self::chain.
A seed alone is nearly free of benefit, which is worth stating because it is not what the
hint on RigidParams::initial suggests. Measured on the 164x205x205 phantom, a
four-volume drifting series registered against one reference:
| metric evaluations | worst rotation error | |
|---|---|---|
| cold start, 5 levels | 4372 | 0.012° |
| chained seed, 5 levels | 4310 | 0.012° |
| chained seed, 3 levels | 2331 | 0.012° |
| chained seed, 1 level | 938 | 0.013° |
The coarse levels throw the seed away: they smooth and decimate until the cost surface’s own minimum is somewhere else, walk to it, and arrive at the finer levels having learned nothing from the prior. A seed only pays once the coarse search it is meant to replace is removed.
Evaluations are the honest unit here, and they are not all priced the same: a level below
the third costs a few thousand samples against the 200k cap
(RigidParams::max_samples), so dropping those levels removes evaluations that were
nearly free. Expect the wall-clock saving to be smaller than the ratio above — the figures
are deliberately not quoted in seconds, because the machine they were measured on was not
quiet enough for that to mean anything.
3 is the default because the pyramid’s job is capture range, and the capture range a
seeded registration needs is only the motion between neighbouring volumes — not the
whole head rotation. Three levels was measured at 40° in RigidParams::levels, which
covers any inter-volume motion that is not an outright jump, at half the cost of the full
pyramid. Drop it to 1 for a series known to drift smoothly; raise it to
RigidParams::levels to pay the full price for the full capture range, which is what
chain: false does anyway.
declared_b0: Option<(f64, f64, f64)>B0 direction in each volume’s own voxel frame, in the convention
b0_direction_from_affine returns, when a sidecar declares it. None reads it off each
volume’s affine, which is right whenever that affine is the scanner’s own.
One value for the series, not one per volume: a series is one prescription, so every volume was acquired with B0 in the same place relative to its own grid. What differs between them is where the head was, which is what the registration recovers.
min_ncc: f64NCC below which an estimate is marked VolumeMotion::suspect.
A floor for “something went wrong”, not a quality target. Two repeats that genuinely
align land above ~0.99, but echoes do not, and the default is set for echoes because they
are the common case: on the four-echo 7 T phantom, echoes at TE 12, 20 and 28 ms
registered to a 4 ms reference scored NCC 0.84, 0.68 and 0.55 — while recovering a known
2.5-7.5 degree rotation to 0.04 degrees every time. See estimate_motion for why.
0.3 sits below that and still well clear of the ~0.2 a genuinely failed search returns;
0.5 would have flagged that last echo, whose transform was right to four decimal
places. Raise it to ~0.9 for a series of repeats, where a low value really would mean
something was wrong.
Trait Implementations§
Source§impl Clone for MotionParams
impl Clone for MotionParams
Source§fn clone(&self) -> MotionParams
fn clone(&self) -> MotionParams
1.0.0 (const: unstable) · Source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
source. Read moreSource§impl Debug for MotionParams
impl Debug for MotionParams
Source§impl Default for MotionParams
impl Default for MotionParams
Auto Trait Implementations§
impl Freeze for MotionParams
impl RefUnwindSafe for MotionParams
impl Send for MotionParams
impl Sync for MotionParams
impl Unpin for MotionParams
impl UnsafeUnpin for MotionParams
impl UnwindSafe for MotionParams
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
impl<T> CloneAny for T
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
§impl<T> Downcast for Twhere
T: Any,
impl<T> Downcast for Twhere
T: Any,
§fn into_any(self: Box<T>) -> Box<dyn Any>
fn into_any(self: Box<T>) -> Box<dyn Any>
Box<dyn Trait> (where Trait: Downcast) to Box<dyn Any>, which can then be
downcast into Box<dyn ConcreteType> where ConcreteType implements Trait.§fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>
fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>
Rc<Trait> (where Trait: Downcast) to Rc<Any>, which can then be further
downcast into Rc<ConcreteType> where ConcreteType implements Trait.§fn as_any(&self) -> &(dyn Any + 'static)
fn as_any(&self) -> &(dyn Any + 'static)
&Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot
generate &Any’s vtable from &Trait’s.§fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)
fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)
&mut Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot
generate &mut Any’s vtable from &mut Trait’s.§impl<T> DowncastSend for T
impl<T> DowncastSend for T
§impl<T> DowncastSync for T
impl<T> DowncastSync for T
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read more