civet.extraction
Submodule for more specific types which are relevant to surface-extraction subroutines of the CIVET pipeline.
1""" 2Submodule for more specific types which are relevant to 3surface-extraction subroutines of the CIVET pipeline. 4""" 5 6from civet.extraction.side import Side 7from civet.extraction.hemisphere import HemisphereMask 8from civet.extraction.surfaces import RegularSurface, IrregularSurface, Tetra 9from civet.extraction.starting_models import SurfaceModel 10 11__all__ = [ 12 'HemisphereMask', 13 'Side', 14 'RegularSurface', 15 'IrregularSurface', 16 'Tetra', 17 'SurfaceModel' 18]
19class HemisphereMask(GenericMask['HemisphereMask']): 20 """ 21 Represents a binary mask of a brain hemisphere (either left or right). 22 """ 23 24 def just_sphere_mesh(self, side: Optional[Side] = None, subsample: bool = False) -> IrregularSurface: 25 """ 26 Just run `sphere_mesh`, which produces a mesh with non-standard connectivity. 27 """ 28 model = self.get_model_for(side) 29 return self.prepare_for_sphere_mesh(model).sphere_mesh(subsample) 30 31 def smoothen_using_mincmorph(self, iterations: int = 5, lower: float = 2.5, upper: float = 4.5) -> 'HemisphereMask': 32 """ 33 Simplify connectivity of this mask using mincmorph. 34 This is a preprocessing step to `sphere_mesh`. 35 36 https://github.com/aces/surface-extraction/blob/7c9c5987a2f8f5fdeb8d3fd15f2f9b636401d9a1/scripts/marching_cubes.pl.in#L166-L184 37 """ 38 if not isinstance(iterations, int) or iterations < 0: 39 raise ValueError(f'iterations {iterations} < 0') 40 41 smoother = self 42 43 for _ in range(iterations): 44 ngh_count = smoother.mincmorph_convolve_u8() 45 expression = f'if(A[1]<{lower})' r'{0}else{' f'if(A[1]>{upper})' r'{1}else{A[0]}}' 46 smoother = self.minccalc_u8(expression, ngh_count) 47 48 return smoother 49 50 def prepare_for_sphere_mesh(self, initial_model: SurfaceModel) -> 'SphereMeshMask': 51 """ 52 Prepare this mask for `sphere_mesh`. 53 54 https://github.com/aces/surface-extraction/blob/7c9c5987a2f8f5fdeb8d3fd15f2f9b636401d9a1/scripts/marching_cubes.pl.in#L189-L207 55 56 Parameters 57 ---------- 58 initial_model: SurfaceModel 59 bounds 60 """ 61 filled = self.minccalc_u8('out=1') 62 surface_mask_vol = initial_model.surface_mask2(filled) 63 resampled = surface_mask_vol.mincresample(filled) 64 overlap = self.minccalc_u8('if(A[0]>0.5||A[1]>0.5){1}else{0}', resampled) 65 dilated = overlap.dilate_volume(1, 26, 1) 66 added = self.minccalc_u8('A[0]+A[1]', dilated) 67 defragged = added.reshape255().mincdefrag(2, 19) 68 return SphereMeshMask(defragged) 69 70 @staticmethod 71 def get_model_for(side: Optional[Side] = None) -> SurfaceModel: 72 """ 73 Transform `WHITE_MODEL_320` as necessary in preparation for use with `sphere_mesh`. 74 75 https://github.com/aces/surface-extraction/blob/7c9c5987a2f8f5fdeb8d3fd15f2f9b636401d9a1/scripts/marching_cubes.pl.in#L118-L135 76 """ 77 initial_model = WHITE_MODEL_320 78 if side is Side.LEFT: 79 return initial_model.slide_left() 80 elif side is Side.RIGHT: 81 return initial_model.flip_x().slide_right() 82 else: 83 return initial_model
Represents a binary mask of a brain hemisphere (either left or right).
24 def just_sphere_mesh(self, side: Optional[Side] = None, subsample: bool = False) -> IrregularSurface: 25 """ 26 Just run `sphere_mesh`, which produces a mesh with non-standard connectivity. 27 """ 28 model = self.get_model_for(side) 29 return self.prepare_for_sphere_mesh(model).sphere_mesh(subsample)
Just run sphere_mesh
, which produces a mesh with non-standard connectivity.
31 def smoothen_using_mincmorph(self, iterations: int = 5, lower: float = 2.5, upper: float = 4.5) -> 'HemisphereMask': 32 """ 33 Simplify connectivity of this mask using mincmorph. 34 This is a preprocessing step to `sphere_mesh`. 35 36 https://github.com/aces/surface-extraction/blob/7c9c5987a2f8f5fdeb8d3fd15f2f9b636401d9a1/scripts/marching_cubes.pl.in#L166-L184 37 """ 38 if not isinstance(iterations, int) or iterations < 0: 39 raise ValueError(f'iterations {iterations} < 0') 40 41 smoother = self 42 43 for _ in range(iterations): 44 ngh_count = smoother.mincmorph_convolve_u8() 45 expression = f'if(A[1]<{lower})' r'{0}else{' f'if(A[1]>{upper})' r'{1}else{A[0]}}' 46 smoother = self.minccalc_u8(expression, ngh_count) 47 48 return smoother
Simplify connectivity of this mask using mincmorph.
This is a preprocessing step to sphere_mesh
.
50 def prepare_for_sphere_mesh(self, initial_model: SurfaceModel) -> 'SphereMeshMask': 51 """ 52 Prepare this mask for `sphere_mesh`. 53 54 https://github.com/aces/surface-extraction/blob/7c9c5987a2f8f5fdeb8d3fd15f2f9b636401d9a1/scripts/marching_cubes.pl.in#L189-L207 55 56 Parameters 57 ---------- 58 initial_model: SurfaceModel 59 bounds 60 """ 61 filled = self.minccalc_u8('out=1') 62 surface_mask_vol = initial_model.surface_mask2(filled) 63 resampled = surface_mask_vol.mincresample(filled) 64 overlap = self.minccalc_u8('if(A[0]>0.5||A[1]>0.5){1}else{0}', resampled) 65 dilated = overlap.dilate_volume(1, 26, 1) 66 added = self.minccalc_u8('A[0]+A[1]', dilated) 67 defragged = added.reshape255().mincdefrag(2, 19) 68 return SphereMeshMask(defragged)
Prepare this mask for sphere_mesh
.
Parameters
initial_model: SurfaceModel bounds
70 @staticmethod 71 def get_model_for(side: Optional[Side] = None) -> SurfaceModel: 72 """ 73 Transform `WHITE_MODEL_320` as necessary in preparation for use with `sphere_mesh`. 74 75 https://github.com/aces/surface-extraction/blob/7c9c5987a2f8f5fdeb8d3fd15f2f9b636401d9a1/scripts/marching_cubes.pl.in#L118-L135 76 """ 77 initial_model = WHITE_MODEL_320 78 if side is Side.LEFT: 79 return initial_model.slide_left() 80 elif side is Side.RIGHT: 81 return initial_model.flip_x().slide_right() 82 else: 83 return initial_model
Transform WHITE_MODEL_320
as necessary in preparation for use with sphere_mesh
.
Inherited Members
Brain hemisphere side.
Inherited Members
- enum.Enum
- name
- value
31class RegularSurface(GenericSurface[_RS], Generic[_RS]): 32 """ 33 Represents a mesh (`.obj`) with standard connectivity. 34 35 Typically, standard connectivity means 81,920 triangles, 41,962 36 vertices. By convention, the file name for such a surface should 37 have the suffix `_81920.obj`. 38 39 A general definition for "standard connectivity" would be a 40 polygonal mesh of *N* triangles where 320 and 4 are common 41 denominators of *N*. 42 """ 43 @classmethod 44 def create_tetra(cls, tetra: Tetra) -> 'RegularSurface[RegularSurface]': 45 return cls(tetra)
Represents a mesh (.obj
) with standard connectivity.
Typically, standard connectivity means 81,920 triangles, 41,962
vertices. By convention, the file name for such a surface should
have the suffix _81920.obj
.
A general definition for "standard connectivity" would be a polygonal mesh of N triangles where 320 and 4 are common denominators of N.
48class IrregularSurface(GenericSurface[_IS], Generic[_IS]): 49 """ 50 Represents a mesh (`.obj`) with irregular connectivity. 51 """ 52 def interpolate_with_sphere( 53 self, 54 side: Optional[Side] = None, 55 n_inflate: Optional[int] = None, 56 n_smooth: Optional[int] = None 57 ) -> RegularSurface: 58 """ 59 Resample this surface to have a standard number of 81,920 triangles. 60 61 If `n_inflate` and `n_smooth` are given, use `inflate_to_sphere_implicit` instead 62 of `inflate_to_sphere`. 63 """ 64 options = [] 65 if side is not None: 66 options += ['-' + side.value] 67 if (n_inflate is None) ^ (n_smooth is None): 68 raise ValueError('both n_inflate and n_smooth must be specified') 69 if n_inflate is not None and n_smooth is not None: 70 options += ['-inflate', str(n_inflate), str(n_smooth)] 71 72 class InterpolatedFromSphere(RegularSurface): 73 def command(self, output: str | PathLike 74 ) -> Sequence[str | PathLike | AbstractDataCommand]: 75 return 'interpolate_surface_with_sphere.pl', *options, self.input, output 76 return InterpolatedFromSphere(self)
Represents a mesh (.obj
) with irregular connectivity.
52 def interpolate_with_sphere( 53 self, 54 side: Optional[Side] = None, 55 n_inflate: Optional[int] = None, 56 n_smooth: Optional[int] = None 57 ) -> RegularSurface: 58 """ 59 Resample this surface to have a standard number of 81,920 triangles. 60 61 If `n_inflate` and `n_smooth` are given, use `inflate_to_sphere_implicit` instead 62 of `inflate_to_sphere`. 63 """ 64 options = [] 65 if side is not None: 66 options += ['-' + side.value] 67 if (n_inflate is None) ^ (n_smooth is None): 68 raise ValueError('both n_inflate and n_smooth must be specified') 69 if n_inflate is not None and n_smooth is not None: 70 options += ['-inflate', str(n_inflate), str(n_smooth)] 71 72 class InterpolatedFromSphere(RegularSurface): 73 def command(self, output: str | PathLike 74 ) -> Sequence[str | PathLike | AbstractDataCommand]: 75 return 'interpolate_surface_with_sphere.pl', *options, self.input, output 76 return InterpolatedFromSphere(self)
Resample this surface to have a standard number of 81,920 triangles.
If n_inflate
and n_smooth
are given, use inflate_to_sphere_implicit
instead
of inflate_to_sphere
.
20@dataclass(frozen=True) 21class Tetra(DataSource): 22 23 center: tuple[float, float, float] = (0.0, 0.0, 0.0) 24 radius: tuple[float, float, float] = (1.0, 1.0, 1.0) 25 triangles: int = 81920 26 27 def command(self, output: str | PathLike) -> Sequence[str | PathLike]: 28 return 'create_tetra', output, *self.center, *self.radius
Inherited Members
12class SurfaceModel(RegularSurface['SurfaceModel']): 13 """ 14 Represents a surface data file from the `$MNI_DATAPATH/surface-extraction` directory. 15 """ 16 @classmethod 17 def get_model(cls, name: str) -> Optional['SurfaceModel']: 18 data_paths = MNI_DATAPATH.split(':') 19 possible_models = map(lambda b: Path(b) / 'surface-extraction' / name, data_paths) 20 actual_model_paths = filter(lambda p: p.is_file(), possible_models) 21 actual_models = map(cls, actual_model_paths) 22 return next(actual_models, None)
Represents a surface data file from the $MNI_DATAPATH/surface-extraction
directory.
16 @classmethod 17 def get_model(cls, name: str) -> Optional['SurfaceModel']: 18 data_paths = MNI_DATAPATH.split(':') 19 possible_models = map(lambda b: Path(b) / 'surface-extraction' / name, data_paths) 20 actual_model_paths = filter(lambda p: p.is_file(), possible_models) 21 actual_models = map(cls, actual_model_paths) 22 return next(actual_models, None)