From c9ac5e15b31a9f5f78f5cbf5cce53228425d5118 Mon Sep 17 00:00:00 2001 From: engrosamaali91 Date: Wed, 14 Feb 2024 17:39:23 +0100 Subject: [PATCH] Recon_surf and CerebNet API's --- CerebNet/apply_warp.py | 44 +++++++++- CerebNet/inference.py | 12 +-- CerebNet/run_prediction.py | 21 +++++ doc/api/index.rst | 2 +- doc/api/recon_surf.rst | 11 +++ doc/conf.py | 2 + recon_surf/N4_bias_correct.py | 97 ++++++++++++----------- recon_surf/fs_balabels.py | 22 ++--- recon_surf/lta.py | 18 ++--- recon_surf/map_surf_label.py | 67 ++++++++-------- recon_surf/paint_cc_into_pred.py | 19 ++--- recon_surf/rewrite_mc_surface.py | 19 +++-- recon_surf/rotate_sphere.py | 17 ++-- recon_surf/sample_parc.py | 34 ++++---- recon_surf/smooth_aparc.py | 42 +++++----- recon_surf/spherically_project_wrapper.py | 29 +++---- 16 files changed, 275 insertions(+), 181 deletions(-) diff --git a/CerebNet/apply_warp.py b/CerebNet/apply_warp.py index 5518b363d..86f1f15d7 100644 --- a/CerebNet/apply_warp.py +++ b/CerebNet/apply_warp.py @@ -14,14 +14,34 @@ # limitations under the License. # IMPORTS -from os.path import join import numpy as np import nibabel as nib +from os.path import join from CerebNet.datasets import utils def save_nii_image(img_data, save_path, header, affine): + """ + Save an image data array as a NIfTI file. + + Parameters + ---------- + img_data : ndarray + The image data to be saved. + save_path : str + The path (including file name) where the image will be saved. + header : nibabel.Nifti1Header + The header information for the NIfTI file. + affine : ndarray + The affine matrix for the NIfTI file. + + Returns + ------- + None + This function returns nothing. + """ + img_out = nib.Nifti1Image(img_data, header=header, affine=affine) print(f"Saving {save_path}") nib.save(img_out, save_path) @@ -29,6 +49,28 @@ def save_nii_image(img_data, save_path, header, affine): def store_warped_data(img_path, lbl_path, warp_path, result_path, patch_size): + """ + Load, warp, crop, and save both an image and its corresponding label based on a given warp field. + + Parameters + ---------- + img_path : str + Path to the T1-weighted MRI image to be warped. + lbl_path : str + Path to the label image corresponding to the T1 image, to be warped similarly. + warp_path : str + Path to the warp field file used to warp the images. + result_path : str + Directory path where the warped and cropped images will be saved. + patch_size : tuple of int + The dimensions (height, width, depth) cropped images after warping. + + Returns + ------- + None + This function returns nothing. + """ + img, img_file = utils.load_reorient_rescale_image(img_path) lbl_file = nib.load(lbl_path) diff --git a/CerebNet/inference.py b/CerebNet/inference.py index b470d630f..d7165b0cd 100644 --- a/CerebNet/inference.py +++ b/CerebNet/inference.py @@ -14,17 +14,16 @@ # IMPORTS import time +import nibabel as nib +import numpy as np +import torch + from os import makedirs from os.path import join, dirname, isfile from typing import Dict, List, Tuple, Optional from concurrent.futures import Future, ThreadPoolExecutor - -import nibabel as nib -import numpy as np -import torch from torch.utils.data import DataLoader from tqdm import tqdm - from FastSurferCNN.utils import logging from FastSurferCNN.utils.threads import get_num_threads from FastSurferCNN.utils.mapper import JsonColorLookupTable, TSVLookupTable @@ -44,6 +43,9 @@ class Inference: + """ + Manages inference operations, including batch processing, data loading, and model predictions for neuroimaging data. + """ def __init__( self, cfg: "yacs.ConfigNode", diff --git a/CerebNet/run_prediction.py b/CerebNet/run_prediction.py index 5eae17764..566c9021b 100644 --- a/CerebNet/run_prediction.py +++ b/CerebNet/run_prediction.py @@ -30,6 +30,14 @@ def setup_options(): + """ + Configure and return an argument parser for the segmentation script. + + Returns + ------- + argparse.ArgumentParser + The configured argument parser. + """ # Training settings parser = argparse.ArgumentParser(description="Segmentation") @@ -94,6 +102,19 @@ def setup_options(): def main(args): + """ + Main function to run the inference based on the given command line arguments. + + Parameters + ---------- + args : argparse.Namespace + Command line arguments parsed by `argparse.ArgumentParser`. + + Returns + ------- + str or None + A message indicating the failure reason in case of an exception, otherwise `None`. + """ cfg = get_config(args) cfg.TEST.ENABLE = True cfg.TRAIN.ENABLE = False diff --git a/doc/api/index.rst b/doc/api/index.rst index 40ac8b389..be1995707 100644 --- a/doc/api/index.rst +++ b/doc/api/index.rst @@ -9,4 +9,4 @@ FastSurfer API FastSurferCNN.utils.rst FastSurferCNN.data_loader.rst recon_surf.rst - + CerebNet.rst \ No newline at end of file diff --git a/doc/api/recon_surf.rst b/doc/api/recon_surf.rst index 4e6839702..44febda7d 100644 --- a/doc/api/recon_surf.rst +++ b/doc/api/recon_surf.rst @@ -10,6 +10,17 @@ API recon_surf References align_points align_seg create_annotation + fs_balabels + lta + map_surf_label + N4_bias_correct + paint_cc_into_pred + rewrite_mc_surface + rotate_sphere + sample_parc + smooth_aparc + spherically_project_wrapper + diff --git a/doc/conf.py b/doc/conf.py index 6a8c744f5..7771edbce 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -21,6 +21,8 @@ import os sys.path.append(os.path.dirname(__file__) + '/..') sys.path.append(os.path.dirname(__file__) + '/../recon_surf') +# sys.path.append(os.path.dirname(__file__) + '/../CerebNet') + # sys.path.insert(0, '..') #after # autodoc_mock_imports = ["torch", "yacs"] diff --git a/recon_surf/N4_bias_correct.py b/recon_surf/N4_bias_correct.py index 252cdf6ce..db957ae6e 100644 --- a/recon_surf/N4_bias_correct.py +++ b/recon_surf/N4_bias_correct.py @@ -15,18 +15,16 @@ # IMPORTS +import image_io as iio import argparse import sys -from pathlib import Path -from typing import Optional, cast, Tuple import logging - import SimpleITK as sitk import numpy as np -from numpy import typing as npt - -import image_io as iio +from pathlib import Path +from typing import Optional, cast, Tuple +from numpy import typing as npt HELPTEXT = """ @@ -100,13 +98,13 @@ def options_parse(): - """Command line option parser. + """ + Command line option parser. Returns ------- options - object holding options - + Object holding options. """ parser = argparse.ArgumentParser( description=HELPTEXT, @@ -146,28 +144,28 @@ def itk_n4_bfcorrection( numiter: int = 50, thres: float = 0.0, ) -> sitk.Image: - """Perform the bias field correction. + """ + Perform the bias field correction. Parameters ---------- itk_image : sitk.Image - n-dimensional image + N-dimensional image. itk_mask : Optional[sitk.Image] - Image mask. Defaults to None. Optional + Image mask. Defaults to None. Optional. shrink : int - Shrink factors. Defaults to 4 + Shrink factors. Defaults to 4. levels : int - Number of levels for maximum number of iterations. Defaults to 4 + Number of levels for maximum number of iterations. Defaults to 4. numiter : int - Maximum number if iterations. Defaults 50 + Maximum number if iterations. Defaults 50. thres : float - Convergence threshold. Defaults to 0.0 + Convergence threshold. Defaults to 0.0. Returns ------- itk_bfcorr_image - Bias field corrected image - + Bias field corrected image. """ _logger = logging.getLogger(__name__ + ".itk_n4_bfcorrection") # if no mask is passed, create a simple mask from the image @@ -214,28 +212,28 @@ def normalize_wm_mask_ball( target_wm: float = 110., target_bg: float = 3. ) -> sitk.Image: - """Normalize WM image by Mask and optionally ball around talairach center. + """ + Normalize WM image by Mask and optionally ball around talairach center. Parameters ---------- itk_image : sitk.Image - n-dimensional itk image + N-dimensional itk image. itk_mask : sitk.Image, optional Image mask. radius : float | int - Defaults to 50 [MISSING] + Defaults to 50 [MISSING]. centroid : np.ndarray - brain centroid. + Brain centroid. target_wm : float | int Target white matter intensity. Defaults to 110. target_bg : float | int - target background intensity. Defaults to 3 (1% of 255) + Target background intensity. Defaults to 3 (1% of 255). Returns ------- normalized_image : sitk.Image - Normalized WM image - + Normalized WM image. """ _logger = logging.getLogger(__name__ + ".normalize_wm_mask_ball") _logger.info(f"- centroid: {centroid}") @@ -278,30 +276,30 @@ def normalize_wm_aseg( target_wm: float = 110., target_bg: float = 3. ) -> sitk.Image: - """Normalize WM image [MISSING]. + """ + Normalize WM image [MISSING]. Parameters ---------- itk_image : sitk.Image - n-dimensional itk image + N-dimensional itk image. itk_mask : sitk.Image | None Image mask. itk_aseg : sitk.Image - aseg-like segmentation image to find WM. + Aseg-like segmentation image to find WM. radius : float | int - Defaults to 50 [MISSING] + Defaults to 50 [MISSING]. centroid : Optional[np.ndarray] - Image centroid. Defaults to None + Image centroid. Defaults to None. target_wm : float | int - target white matter intensity. Defaults to 110 + Target white matter intensity. Defaults to 110. target_bg : float | int - target background intensity Defaults to 3 (1% of 255) + Target background intensity Defaults to 3 (1% of 255). Returns ------- normed : sitk.Image - Normalized WM image - + Normalized WM image. """ _logger = logging.getLogger(__name__ + ".normalize_wm_aseg") @@ -334,13 +332,18 @@ def normalize_img( Parameters ---------- itk_image : sitk.Image + Input image to be normalized. itk_mask : sitk.Image | None + [MISSING]. source_intensity : Tuple[float, float] + Source intensity range. target_intensity : Tuple[float, float] + Target intensity range. Returns ------- - Rescaled image + sitk.Image + Rescaled image. """ _logger = logging.getLogger(__name__ + ".normalize_wm") # compute intensity transformation @@ -359,23 +362,23 @@ def normalize_img( def read_talairach_xfm(fname: Path | str) -> np.ndarray: - """Read Talairach transform. + """ + Read Talairach transform. Parameters ---------- fname : str - Filename to Talairach transform + Filename to Talairach transform. Returns ------- tal - Talairach transform matrix + Talairach transform matrix. Raises ------ ValueError if the file is of an invalid format. - """ _logger = logging.getLogger(__name__ + ".read_talairach_xfm") _logger.info(f"reading talairach transform from {fname}") @@ -398,7 +401,8 @@ def read_talairach_xfm(fname: Path | str) -> np.ndarray: def get_tal_origin_voxel(tal: npt.ArrayLike, image: sitk.Image) -> np.ndarray: - """Get the origin of Talairach space in voxel coordinates. + """ + Get the origin of Talairach space in voxel coordinates. Parameters ---------- @@ -410,8 +414,7 @@ def get_tal_origin_voxel(tal: npt.ArrayLike, image: sitk.Image) -> np.ndarray: Returns ------- vox_origin : np.ndarray - Voxel coordinate of Talairach origin - + Voxel coordinate of Talairach origin. """ tal_inv = np.linalg.inv(tal) tal_origin = np.array(tal_inv[0:3, 3]).ravel() @@ -453,13 +456,14 @@ def get_image_mean(image: sitk.Image, mask: Optional[sitk.Image] = None) -> floa Parameters ---------- image : sitk.Image - image to get mean of + Image to get mean of. mask : sitk.Image, optional - optional mask to apply first + Optional mask to apply first. Returns ------- mean : float + The mean value of the image. """ img = sitk.GetArrayFromImage(image) if mask is not None: @@ -475,11 +479,12 @@ def get_brain_centroid(itk_mask: sitk.Image) -> np.ndarray: Parameters ---------- itk_mask : sitk.Image + [MISSING]. Returns ------- - brain centroid - + np.ndarray + Brain centroid. """ _logger = logging.getLogger(__name__ + ".get_brain_centroid") _logger.debug("No talairach center passed, estimating center from mask.") diff --git a/recon_surf/fs_balabels.py b/recon_surf/fs_balabels.py index 69cb11ab4..86194f689 100755 --- a/recon_surf/fs_balabels.py +++ b/recon_surf/fs_balabels.py @@ -74,13 +74,13 @@ def options_parse(): - """Command line option parser. + """ + Command line option parser. Returns ------- options - object holding options - + Object holding options. """ parser = optparse.OptionParser( version="$Id:fs_balabels.py,v 1.0 2022/08/24 21:22:08 mreuter Exp $", @@ -110,27 +110,27 @@ def read_colortables( colappend: List[str], drop_unknown: bool = True ) -> Tuple[List, List, List]: - """Read multiple colortables and appends extensions, drops unknown by default. + """ + Read multiple colortables and appends extensions, drops unknown by default. Parameters ---------- colnames : List[str] - List of color-names + List of color-names. colappend : List[str] - List of appends for names + List of appends for names. drop_unknown : bool True if unknown colors should be dropped. - Defaults to True + Defaults to True. Returns ------- all_ids - List of all ids + List of all ids. all_names - List of all names + List of all names. all_cols - List of all colors - + List of all colors. """ pos = 0 all_names = [] diff --git a/recon_surf/lta.py b/recon_surf/lta.py index dd38e1dc3..361182035 100755 --- a/recon_surf/lta.py +++ b/recon_surf/lta.py @@ -28,28 +28,28 @@ def writeLTA( dst_fname: str, dst_header: Dict ) -> None: - """Write linear transform array info to a .lta file. + """ + Write linear transform array info to a .lta file. Parameters ---------- filename : str - File to write on + File to write on. T : npt.ArrayLike - Linear transform array to be saved + Linear transform array to be saved. src_fname : str - Source filename + Source filename. src_header : Dict - Source header + Source header. dst_fname : str - Destination filename + Destination filename. dst_header : Dict - Destination header + Destination header. Raises ------ ValueError - Header format missing field (Source or Destination) - + Header format missing field (Source or Destination). """ from datetime import datetime import getpass diff --git a/recon_surf/map_surf_label.py b/recon_surf/map_surf_label.py index 0e6b6737e..4e985e424 100755 --- a/recon_surf/map_surf_label.py +++ b/recon_surf/map_surf_label.py @@ -59,13 +59,13 @@ def options_parse(): - """Command line option parser. + """ + Command line option parser. Returns ------- options - object holding options - + Object holding options. """ parser = optparse.OptionParser( version="$Id:map_surf_label.py,v 1.0 2022/08/24 21:22:08 mreuter Exp $", @@ -99,30 +99,30 @@ def writeSurfLabel( values: npt.NDArray, surf: npt.NDArray ) -> None: - """Write a FS surface label file to filename (e.g. lh.labelname.label). + """ + Write a FS surface label file to filename (e.g. lh.labelname.label). Stores sid string in the header, then number of vertices and table of vertex index, RAS wm-surface coords (taken from surf) - and values (which can be zero) + and values (which can be zero). Parameters ---------- filename : str - File there surface label is written + File there surface label is written. sid : str - Subject id + Subject id. label : npt.NDArray[str] - List of label names + List of label names. values : npt.NDArray - List of values + List of values. surf : npt.NDArray - Surface coordinations + Surface coordinations. Raises ------ ValueError - Label and values should have same sizes - + Label and values should have same sizes. """ if values is None: values = np.zeros(label.shape) @@ -151,32 +151,32 @@ def getSurfCorrespondence( trg_sphere: Union[str, Tuple, np.ndarray], tree: Optional[KDTree] = None ) -> Tuple[np.ndarray, np.ndarray, KDTree]: - """For each vertex in src_sphere find the closest vertex in trg_sphere. + """ + For each vertex in src_sphere find the closest vertex in trg_sphere. - Spheres are Nx3 arrays of coordinates on the sphere (usually R=100 FS format) + Spheres are Nx3 arrays of coordinates on the sphere (usually R=100 FS format). *_sphere can also be a file name of the sphere.reg files, then we load it. - The KDtree can be passed in cases where src moves around and trg stays fixed + The KDtree can be passed in cases where src moves around and trg stays fixed. Parameters ---------- src_sphere : Union[str, Tuple, np.ndarray] Either filepath (as str) or surface vertices - of source sphere + of source sphere. trg_sphere : Union[str, Tuple, np.ndarray] Either filepath (as str) or surface vertices - of target sphere + of target sphere. tree : Optional[KDTree] - Defaults to None + Defaults to None. Returns ------- mapping : np.ndarray - Surface mapping of the trg surface + Surface mapping of the trg surface. distances : np.ndarray - Surface distance of the trg surface + Surface distance of the trg surface. tree : KDTree - KDTree of the trg surface - + KDTree of the trg surface. """ # We can also work with file names instead of surface vertices if isinstance(src_sphere, str): @@ -204,36 +204,37 @@ def mapSurfLabel( trg_sid: str, rev_mapping: np.ndarray ) -> Tuple[np.ndarray, np.ndarray]: - """Map a label from src surface according to the correspondence. + """ + Map a label from src surface according to the correspondence. trg_surf is passed so that the label file will list the - correct coordinates (usually the white surface), can be vetrices or filename + correct coordinates (usually the white surface), can be vetrices or filename. Parameters ---------- src_label_name : str - Path to label file of source + Path to label file of source. out_label_name : str - Path to label file of output + Path to label file of output. trg_surf : Union[str, np.ndarray] - Numpy array of vertex coordinates or filepath to target surface + Numpy array of vertex coordinates or filepath to target surface. trg_sid : str - Subject id of the target subject (as stored in the output label file header) + Subject id of the target subject (as stored in the output label file header). rev_mapping : np.ndarray - a mapping from target to source, listing the corresponding src vertex for each vertex on the trg surface + A mapping from target to source, listing the corresponding src vertex + for each vertex on the trg surface. Returns ------- trg_label : np.ndarray - target labels + Target labels. trg_values : np.ndarray - target values + Target values. Raises ------ ValueError - Label and trg vertices should have same sizes - + Label and trg vertices should have same sizes. """ print("Mapping label: {} ...".format(src_label_name)) src_label, src_values = fs.read_label(src_label_name, read_scalars=True) diff --git a/recon_surf/paint_cc_into_pred.py b/recon_surf/paint_cc_into_pred.py index eb8059780..9617e6be3 100644 --- a/recon_surf/paint_cc_into_pred.py +++ b/recon_surf/paint_cc_into_pred.py @@ -15,11 +15,12 @@ # IMPORTS import numpy as np -from numpy import typing as npt import nibabel as nib import sys import argparse +from numpy import typing as npt + HELPTEXT = """ Script to add corpus callosum segmentation (CC, FreeSurfer IDs 251-255) to deep-learning prediction (e.g. aparc.DKTatlas+aseg.deep.mgz). @@ -42,13 +43,13 @@ def argument_parse(): - """Command line option parser. + """ + Command line option parser. Returns ------- options - object holding options - + Object holding options. """ parser = argparse.ArgumentParser(usage=HELPTEXT) parser.add_argument( @@ -79,22 +80,22 @@ def argument_parse(): def paint_in_cc(pred: npt.ArrayLike, aseg_cc: npt.ArrayLike) -> npt.ArrayLike: - """Paint corpus callosum segmentation into prediction. + """ + Paint corpus callosum segmentation into prediction. Note, that this function modifies the original array and does not create a copy. Parameters ---------- pred : npt.ArrayLike - Deep-learning prediction + Deep-learning prediction. aseg_cc : npt.ArrayLike - Aseg segmentation with CC + Aseg segmentation with CC. Returns ------- pred - Prediction with added CC - + Prediction with added CC. """ cc_mask = (aseg_cc >= 251) & (aseg_cc <= 255) pred[cc_mask] = aseg_cc[cc_mask] diff --git a/recon_surf/rewrite_mc_surface.py b/recon_surf/rewrite_mc_surface.py index 8372f94fe..ee86743a9 100644 --- a/recon_surf/rewrite_mc_surface.py +++ b/recon_surf/rewrite_mc_surface.py @@ -21,14 +21,13 @@ def options_parse(): - """Command line option parser. + """ + Command line option parser. Returns ------- options - object holding options - - + Object holding options. """ parser = optparse.OptionParser( version="$Id: rewrite_mc_surface,v 1.1 2020/06/23 15:42:08 henschell $", @@ -54,19 +53,19 @@ def options_parse(): def resafe_surface(insurf: str, outsurf: str, pretess: str) -> None: - """Take path to insurf and rewrite it to outsurf thereby fixing vertex locs flag error. + """ + Take path to insurf and rewrite it to outsurf thereby fixing vertex locs flag error. - (scannerRAS instead of surfaceRAS after marching cube) + (scannerRAS instead of surfaceRAS after marching cube). Parameters ---------- insurf : str - Path and name of input surface + Path and name of input surface. outsurf : str - Path and name of output surface + Path and name of output surface. pretess : str - Path and name of file the input surface was created on (e.g. filled-pretess127.mgz) - + Path and name of file the input surface was created on (e.g. filled-pretess127.mgz). """ surf = fs.read_geometry(insurf, read_metadata=True) diff --git a/recon_surf/rotate_sphere.py b/recon_surf/rotate_sphere.py index 2d61f0c94..74ebfc65c 100644 --- a/recon_surf/rotate_sphere.py +++ b/recon_surf/rotate_sphere.py @@ -19,11 +19,12 @@ # IMPORTS import optparse import numpy as np -from numpy import typing as npt import sys import nibabel.freesurfer.io as fs import align_points as align +from numpy import typing as npt + HELPTEXT = """ @@ -66,13 +67,13 @@ def options_parse(): - """Command line option parser. + """ + Command line option parser. Returns ------- options - object holding options - + Object holding options. """ parser = optparse.OptionParser( version="$Id: rotate_sphere.py,v 1.0 2022/03/18 21:22:08 mreuter Exp $", @@ -106,7 +107,8 @@ def align_aparc_centroids( labels_dst: npt.ArrayLike, label_ids: npt.ArrayLike = [] ) -> np.ndarray: - """Align centroid of aparc parcels on the sphere (Attention mapping back to sphere!). + """ + Align centroid of aparc parcels on the sphere (Attention mapping back to sphere!). Parameters ---------- @@ -119,13 +121,12 @@ def align_aparc_centroids( labels_dst : npt.ArrayLike Labels of aparc parcelation for rotation destination. label_ids : npt.ArrayLike - Ids of the centroid to be aligned. Defaults to [] + Ids of the centroid to be aligned. Defaults to []. Returns ------- R - Rotation Matrix - + Rotation Matrix. """ #nferiorparietal, inferiortemporal, lateraloccipital, postcentral, posteriorsingulate # precentral, precuneus, superiorfrontal, supramarginal diff --git a/recon_surf/sample_parc.py b/recon_surf/sample_parc.py index 322fa2e7d..eaeeb2626 100644 --- a/recon_surf/sample_parc.py +++ b/recon_surf/sample_parc.py @@ -20,13 +20,13 @@ import optparse import sys import numpy as np -from numpy import typing as npt import nibabel.freesurfer.io as fs import nibabel as nib from scipy import sparse from scipy.sparse.csgraph import connected_components from lapy import TriaMesh from smooth_aparc import smooth_aparc +from numpy import typing as npt HELPTEXT = """ @@ -64,13 +64,13 @@ def options_parse(): - """Command line option parser. + """ + Command line option parser. Returns ------- options - object holding options - + Object holding options. """ parser = optparse.OptionParser( version="$Id: smooth_aparc,v 1.0 2018/06/24 11:34:08 mreuter Exp $", @@ -101,7 +101,8 @@ def options_parse(): return options def construct_adj_cluster(tria, annot): - """Adjacency matrix of edges from same annotation label only. + """ + Adjacency matrix of edges from same annotation label only. Operates only on triangles and removes edges that cross annotation label boundaries. @@ -132,7 +133,8 @@ def construct_adj_cluster(tria, annot): return sparse.csc_matrix((dat, (i, j)), shape=(n, n)) def find_all_islands(surf, annot): - """Find vertices in disconnected islands for all labels in surface annotation. + """ + Find vertices in disconnected islands for all labels in surface annotation. Parameters ---------- @@ -169,7 +171,8 @@ def find_all_islands(surf, annot): return vidx def sample_nearest_nonzero(img, vox_coords, radius=3.0): - """Sample closest non-zero value in a ball of radius around vox_coords. + """ + Sample closest non-zero value in a ball of radius around vox_coords. Parameters ---------- @@ -182,8 +185,8 @@ def sample_nearest_nonzero(img, vox_coords, radius=3.0): Returns ------- - samples : np.ndarray (n,) - Sampled values. Retruns zero for vertices where values are zero in ball. + samples : np.ndarray(n,) + Sampled values, returns zero for vertices where values are zero in ball. """ # check for isotropic voxels voxsize = img.header.get_zooms() @@ -248,7 +251,8 @@ def sample_nearest_nonzero(img, vox_coords, radius=3.0): def sample_img(surf, img, cortex=None, projmm=0.0, radius=None): - """Sample volume at a distance from the surface. + """ + Sample volume at a distance from the surface. Parameters ---------- @@ -261,7 +265,7 @@ def sample_img(surf, img, cortex=None, projmm=0.0, radius=None): Image to sample. If type is str, read image from file. cortex : np.ndarray | str - Filename of cortex label or np.array with cortex indices + Filename of cortex label or np.array with cortex indices. projmm : float Sample projmm mm along the surface vertex normals (default=0). radius : float [optional] | None @@ -326,7 +330,8 @@ def sample_img(surf, img, cortex=None, projmm=0.0, radius=None): def replace_labels(img_labels, img_lut, surf_lut): - """Replace image labels with corresponding surface labels or unknown. + """ + Replace image labels with corresponding surface labels or unknown. Parameters ---------- @@ -361,7 +366,8 @@ def replace_labels(img_labels, img_lut, surf_lut): def sample_parc (surf, seg, imglut, surflut, outaparc, cortex=None, projmm=0.0, radius=None): - """Sample cortical GM labels from image to surface and smooth. + """ + Sample cortical GM labels from image to surface and smooth. Parameters ---------- @@ -380,7 +386,7 @@ def sample_parc (surf, seg, imglut, surflut, outaparc, cortex=None, projmm=0.0, outaparc : str Filename for output surface parcellation. cortex : np.ndarray | str - Filename of cortex label or np.ndarray with cortex indices + Filename of cortex label or np.ndarray with cortex indices. projmm : float Sample projmm mm along the surface vertex normals (default=0). radius : float [optional] | None diff --git a/recon_surf/smooth_aparc.py b/recon_surf/smooth_aparc.py index 672f15c0a..0265ca947 100644 --- a/recon_surf/smooth_aparc.py +++ b/recon_surf/smooth_aparc.py @@ -20,8 +20,8 @@ import optparse import sys import numpy as np -from numpy import typing as npt import nibabel.freesurfer.io as fs +from numpy import typing as npt from scipy import sparse @@ -54,13 +54,13 @@ def options_parse(): - """Command line option parser. + """ + Command line option parser. Returns ------- options - object holding options - + Object holding options. """ parser = optparse.OptionParser( version="$Id: smooth_aparc,v 1.0 2018/06/24 11:34:08 mreuter Exp $", @@ -79,11 +79,13 @@ def options_parse(): def get_adjM(trias: npt.NDArray, n: int): - """Create symmetric sparse adjacency matrix of triangle mesh. + """ + Create symmetric sparse adjacency matrix of triangle mesh. Parameters ---------- trias : np.ndarray shape (m, 3) int + [MISSING]. n : int Shape of output (n,n) adjaceny matrix, where n>=m. @@ -92,7 +94,6 @@ def get_adjM(trias: npt.NDArray, n: int): ------- adjM : np.ndarray (bool) shape (n,n) Symmetric sparse CSR adjacency matrix, true corresponds to an edge. - """ I = trias J = I[:, [1, 2, 0]] @@ -108,7 +109,8 @@ def get_adjM(trias: npt.NDArray, n: int): def bincount2D_vectorized(a: npt.NDArray) -> np.ndarray: - """Count number of occurrences of each value in array of non-negative ints. + """ + Count number of occurrences of each value in array of non-negative ints. Parameters ---------- @@ -119,7 +121,6 @@ def bincount2D_vectorized(a: npt.NDArray) -> np.ndarray: ------- np.ndarray Array of counted values. - """ N = a.max() + 1 a_offs = a + np.arange(a.shape[0])[:, None] * N @@ -132,15 +133,16 @@ def mode_filter( fillonlylabel = None, novote: npt.ArrayLike = [] ) -> npt.NDArray[int]: - """Apply mode filter (smoothing) to integer labels on mesh vertices. + """ + Apply mode filter (smoothing) to integer labels on mesh vertices. Parameters ---------- adjM : sparse.csr_matrix[bool] - Symmetric adjacency matrix defining edges between vertices. - This determines what edges can vote so usually one adds the + Symmetric adjacency matrix defining edges between vertices, + this determines what edges can vote so usually one adds the identity to the adjacency matrix so that each vertex is included - in its own vote. + in its own vote. labels : npt.NDArray[int] List of integer labels at each vertex of the mesh. fillonlylabel : int @@ -152,7 +154,6 @@ def mode_filter( ------- labels_new : npt.NDArray[int] New smoothed labels. - """ # make sure labels lengths equals adjM dimension n = labels.shape[0] @@ -250,7 +251,8 @@ def mode_filter( def smooth_aparc(surf, labels, cortex = None): - """Smoothes aparc and fills holes. + """ + Smoothes aparc and fills holes. First all labels with 0 and -1 unside cortex are filled via repeated mode filtering, then all labels are smoothed first with a wider and @@ -368,19 +370,19 @@ def smooth_aparc_files( incortexname: str, outaparcname: str ) -> None: - """Smoothes aparc. + """ + Smoothes aparc. Parameters ---------- insurfname : str - Suface filepath and name of source + Suface filepath and name of source. inaparcname : str - Annotation filepath and name of source + Annotation filepath and name of source. incortexname : str - Label filepath and name of source + Label filepath and name of source. outaparcname : str - Suface filepath and name of destination - + Suface filepath and name of destination. """ # read input files print("Reading in surface: {} ...".format(insurfname)) diff --git a/recon_surf/spherically_project_wrapper.py b/recon_surf/spherically_project_wrapper.py index e65904a7b..be9bd9f11 100644 --- a/recon_surf/spherically_project_wrapper.py +++ b/recon_surf/spherically_project_wrapper.py @@ -14,20 +14,20 @@ # IMPORTS -from subprocess import Popen, PIPE import shlex import argparse from typing import Any +from subprocess import Popen, PIPE def setup_options(): - """Command line option parser. + """ + Command line option parser. Returns ------- options - object holding options - + Object holding options. """ # Validation settings parser = argparse.ArgumentParser(description="Wrapper for spherical projection") @@ -46,22 +46,23 @@ def setup_options(): def call(command: str, **kwargs: Any) -> int: - """Run command with arguments. + """ + Run command with arguments. Wait for command to complete. Sends output to logging module. Parameters ---------- command : str - Command to call + Command to call. **kwargs : Any + Keyword arguments. Returns ------- int - Returncode of called command - + Returncode of called command. """ kwargs["stdout"] = PIPE kwargs["stderr"] = PIPE @@ -78,22 +79,22 @@ def call(command: str, **kwargs: Any) -> int: def spherical_wrapper(command1: str, command2: str, **kwargs: Any) -> int: - """Run the first command. If it fails the fallback command is run as well. + """ + Run the first command. If it fails the fallback command is run as well. Parameters ---------- command1 : str - Command to call + Command to call. command2 : str - Fallback command to call + Fallback command to call. **kwargs : Any - Arguments. The same as for the Popen constructor + Arguments. The same as for the Popen constructor. Returns ------- code_1 - Return code of command1. If command1 failed return code of command2 - + Return code of command1. If command1 failed return code of command2. """ # First try to run standard spherical project print("Running command: {}".format(command1))