Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion nltools/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
from .cross_validation import set_cv
from .data import Brain_Data, Adjacency, Groupby, Design_Matrix, Design_Matrix_Series
from .simulator import Simulator
from .prefs import MNI_Template, resolve_mni_path
from .prefs import MNI_Template
from .version import __version__
from .mask import expand_mask, collapse_mask, create_sphere
from .external import SRM, DetSRM
15 changes: 6 additions & 9 deletions nltools/data/brain_data.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@
)
from nltools.stats import regress as regression
from .adjacency import Adjacency
from nltools.prefs import MNI_Template, resolve_mni_path
from nltools.prefs import MNI_Template
from nilearn.decoding import SearchLight
from pathlib import Path
import warnings
Expand All @@ -82,7 +82,6 @@


class Brain_Data(object):

"""
Brain_Data is a class to represent neuroimaging data in python as a vector
rather than a 3-dimensional matrix.This makes it easier to perform data
Expand All @@ -106,7 +105,7 @@ def __init__(self, data=None, Y=None, X=None, mask=None, **kwargs):
# Setup default or specified nifti masker
if mask is None:
# Load default mask
self.mask = nib.load(resolve_mni_path(MNI_Template)["mask"])
self.mask = nib.load(MNI_Template.mask)
elif isinstance(mask, (str, Path)):
self.mask = nib.load(str(mask))
elif isinstance(mask, nib.Nifti1Image):
Expand Down Expand Up @@ -653,9 +652,8 @@ def plot(
else:
raise ValueError("anatomical is not a nibabel instance")
else:
# anatomical = nib.load(resolve_mni_path(MNI_Template)['plot'])
anatomical = get_mni_from_img_resolution(self, img_type="plot")

anatomical = get_mni_from_img_resolution(self, img_type="plot")
if self.data.ndim == 1:
if axes is None:
_, axes = plt.subplots(nrows=1, figsize=figsize)
Expand Down Expand Up @@ -728,7 +726,6 @@ def iplot(self, threshold=0, surface=False, anatomical=None, **kwargs):
else:
raise ValueError("anatomical is not a nibabel instance")
else:
# anatomical = nib.load(resolve_mni_path(MNI_Template)['brain'])
anatomical = get_mni_from_img_resolution(self, img_type="brain")
return plot_interactive_brain(
self, threshold=threshold, surface=surface, anatomical=anatomical, **kwargs
Expand Down Expand Up @@ -1269,9 +1266,9 @@ def predict(self, algorithm=None, cv_dict=None, plot=True, verbose=True, **kwarg
self.data[test]
)
else:
output["dist_from_hyperplane_xval"][
test
] = predictor_cv.decision_function(self.data[test])
output["dist_from_hyperplane_xval"][test] = (
predictor_cv.decision_function(self.data[test])
)
if (
predictor_settings["algorithm"] == "svm"
and predictor_cv.probability
Expand Down
4 changes: 2 additions & 2 deletions nltools/mask.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@

import os
import nibabel as nib
from nltools.prefs import MNI_Template, resolve_mni_path
from nltools.prefs import MNI_Template
import pandas as pd
import numpy as np
import warnings
Expand Down Expand Up @@ -42,7 +42,7 @@ def create_sphere(coordinates, radius=5, mask=None):
)

else:
mask = nib.load(resolve_mni_path(MNI_Template)["mask"])
mask = nib.load(MNI_Template.mask)

def sphere(r, p, mask):
"""create a sphere of given radius at some point p in the brain mask
Expand Down
14 changes: 6 additions & 8 deletions nltools/plotting.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,8 @@
from numpy.fft import fft, fftfreq
from nltools.stats import two_sample_permutation, one_sample_permutation
from nilearn.plotting import plot_glass_brain, plot_stat_map, view_img, view_img_on_surf
from nltools.prefs import MNI_Template, resolve_mni_path
from nltools.utils import attempt_to_import
from nltools.prefs import MNI_Template
from nltools.utils import attempt_to_import, get_mni_from_img_resolution
import warnings
import sklearn
import os
Expand Down Expand Up @@ -220,7 +220,7 @@ def plot_t_brain(
cut_coords=c,
display_mode=v,
cmap=cmap,
bg_img=resolve_mni_path(MNI_Template)["brain"],
bg_img=MNI_Template.brain,
**kwargs,
)
elif how == "glass":
Expand All @@ -239,7 +239,7 @@ def plot_t_brain(
cut_coords=c,
display_mode=v,
cmap=cmap,
bg_img=resolve_mni_path(MNI_Template)["brain"],
bg_img=MNI_Template.brain,
**kwargs,
)
del obj
Expand Down Expand Up @@ -314,8 +314,7 @@ def plot_brain(objIn, how="full", thr_upper=None, thr_lower=None, save=False, **
cut_coords=c,
display_mode=v,
cmap=cmap,
bg_img=resolve_mni_path(MNI_Template)["brain"],
**kwargs,
bg_img=get_mni_from_img_resolution(obj, img_type="brain") ** kwargs,
)
if save:
plt.savefig(savefile, bbox_inches="tight")
Expand All @@ -337,8 +336,7 @@ def plot_brain(objIn, how="full", thr_upper=None, thr_lower=None, save=False, **
cut_coords=c,
display_mode=v,
cmap=cmap,
bg_img=resolve_mni_path(MNI_Template)["brain"],
**kwargs,
bg_img=get_mni_from_img_resolution(obj, img_type="brain") ** kwargs,
)
if save:
plt.savefig(savefile, bbox_inches="tight")
Expand Down
191 changes: 87 additions & 104 deletions nltools/prefs.py
Original file line number Diff line number Diff line change
@@ -1,37 +1,56 @@
import os
from nltools.utils import get_resource_path

__all__ = ["MNI_Template", "resolve_mni_path"]
__all__ = ["MNI_Template"]


class MNI_Template_Factory(dict):
"""Class to build the default MNI_Template dictionary. This should never be used
class MNI_Template_Factory(object):
"""Class to build the default MNI_Template instance. This should never be used
directly, instead just `from nltools.prefs import MNI_Template` and update that
object's attributes to change MNI templates."""

def __init__(
self,
resolution="2mm",
mask_type="with_ventricles",
mask=os.path.join(get_resource_path(), "MNI152_T1_2mm_brain_mask.nii.gz"),
plot=os.path.join(get_resource_path(), "MNI152_T1_2mm.nii.gz"),
brain=os.path.join(get_resource_path(), "MNI152_T1_2mm_brain.nii.gz"),
mni_version="nonlin_6thgen",
):
self._supported_resolutions = ["2mm", "3mm"]
self._supported_mni_versions = ["nonlin_6thgen", "2009a", "2009c"]
# Only applies to nonlin_6thgen
self._supported_mask_types = ["with_ventricles", "no_ventricles"]

self._resolution = resolution
self._mask_type = mask_type
self._mask = mask
self._plot = plot
self._brain = brain

self.update(
{
"resolution": self.resolution,
"mask_type": self.mask_type,
"mask": self.mask,
"plot": self.plot,
"brain": self.brain,
}
)
self._mni_version = mni_version

# Auto-populated (derive) mask, brain, plot
# This also always called on attribute access so the latest paths
# are resolved and can be safely used like nib.load(MNI_Template.mask)
# after updating an attribute, e.g. MNI_Template.resolution = 3
# Avoids having to do nib.load(resolve_mni_path(MNI_Template).mask) like
# we were previously
self.resolve_paths()

def __repr__(self) -> str:
if self.mni_version == "nonlin_6thgen":
return f"Current global template:\nresolution={self.resolution}\nmni_version={self.mni_version}\nmask_type={self.mask_type}\nmask={self.mask}\nbrain={self.brain}\nplot={self.plot}"
else:
return f"Current global template:\nresolution={self.resolution}\nmni_version={self.mni_version}\nmask={self.mask}\nbrain={self.brain}\nplot={self.plot}"

@property
def mask(self):
self.resolve_paths()
return self._mask

@property
def plot(self):
self.resolve_paths()
return self._plot

@property
def brain(self):
self.resolve_paths()
return self._brain

@property
def resolution(self):
Expand All @@ -41,108 +60,72 @@ def resolution(self):
def resolution(self, resolution):
if isinstance(resolution, (int, float)):
resolution = f"{int(resolution)}mm"
if resolution not in ["2mm", "3mm"]:
if resolution not in self._supported_resolutions:
raise NotImplementedError(
"Only 2mm and 3mm resolutions are currently supported"
f"Nltools currently supports the following MNI template resolutions: {self._supported_resolutions}"
)
self._resolution = resolution
self.update({"resolution": self._resolution})
self.resolve_paths()

@property
def mask_type(self):
return self._mask_type

@mask_type.setter
def mask_type(self, mask_type):
if mask_type not in ["with_ventricles", "no_ventricles"]:
if mask_type not in self._supported_mask_types:
raise NotImplementedError(
"Only 'with_ventricles' and 'no_ventricles' mask_types are currently supported"
f"Nltools currently supports the following MNI mask_types (only applies to nonlin_6thgen): {self._supported_mask_types}"
)
self._mask_type = mask_type
self.update({"mask_type": self._mask_type})

@property
def mask(self):
return self._mask

@mask.setter
def mask(self, mask):
self._mask = mask
self.update({"mask": self._mask})
self.resolve_paths()

@property
def plot(self):
return self._plot
def mni_version(self):
return self._mni_version

@plot.setter
def plot(self, plot):
self._plot = plot
self.update({"plot": self._plot})

@property
def brain(self):
return self._brain

@brain.setter
def brain(self, brain):
self._brain = brain
self.update({"brain": self._brain})
@mni_version.setter
def mni_version(self, mni_version):
if mni_version not in self._supported_mni_versions:
raise ValueError(
f"Nltools currently supports the following MNI template versions: {self._supported_mni_versions}"
)
self._mni_version = mni_version
self.resolve_paths()

def resolve_paths(self):
base_path = os.path.join(os.path.dirname(__file__), "resources") + os.path.sep
if self._mni_version == "nonlin_6thgen":
if self._resolution == "3mm":
if self._mask_type == "with_ventricles":
self._mask = os.path.join(
base_path, "MNI152_T1_3mm_brain_mask.nii.gz"
)
elif self._mask_type == "no_ventricles":
self._mask = os.path.join(
base_path,
"MNI152_T1_3mm_brain_mask_no_ventricles.nii.gz",
)
self._plot = os.path.join(base_path, "MNI152_T1_3mm.nii.gz")
self._brain = os.path.join(base_path, "MNI152_T1_3mm_brain.nii.gz")
elif self._resolution == "2mm":
if self._mask_type == "with_ventricles":
self._mask = os.path.join(
base_path, "MNI152_T1_2mm_brain_mask.nii.gz"
)
elif self._mask_type == "no_ventricles":
self._mask = os.path.join(
base_path,
"MNI152_T1_2mm_brain_mask_no_ventricles.nii.gz",
)
self._plot = os.path.join(base_path, "MNI152_T1_2mm.nii.gz")
self._brain = os.path.join(base_path, "MNI152_T1_2mm_brain.nii.gz")
elif self._mni_version == "2009c":
pass
elif self._mni_version == "2009a":
pass


# NOTE: We export this from the module and expect users to interact with it instead of
# the class constructor above
MNI_Template = MNI_Template_Factory()


def resolve_mni_path(MNI_Template):
"""Helper function to resolve MNI path based on MNI_Template prefs setting."""

res = MNI_Template["resolution"]
m = MNI_Template["mask_type"]
if not isinstance(res, str):
raise ValueError("resolution must be provided as a string!")
if not isinstance(m, str):
raise ValueError("mask_type must be provided as a string!")

if res == "3mm":
if m == "with_ventricles":
MNI_Template["mask"] = os.path.join(
get_resource_path(), "MNI152_T1_3mm_brain_mask.nii.gz"
)
elif m == "no_ventricles":
MNI_Template["mask"] = os.path.join(
get_resource_path(), "MNI152_T1_3mm_brain_mask_no_ventricles.nii.gz"
)
else:
raise ValueError(
"Available mask_types are 'with_ventricles' or 'no_ventricles'"
)

MNI_Template["plot"] = os.path.join(get_resource_path(), "MNI152_T1_3mm.nii.gz")

MNI_Template["brain"] = os.path.join(
get_resource_path(), "MNI152_T1_3mm_brain.nii.gz"
)

elif res == "2mm":
if m == "with_ventricles":
MNI_Template["mask"] = os.path.join(
get_resource_path(), "MNI152_T1_2mm_brain_mask.nii.gz"
)
elif m == "no_ventricles":
MNI_Template["mask"] = os.path.join(
get_resource_path(), "MNI152_T1_2mm_brain_mask_no_ventricles.nii.gz"
)
else:
raise ValueError(
"Available mask_types are 'with_ventricles' or 'no_ventricles'"
)

MNI_Template["plot"] = os.path.join(get_resource_path(), "MNI152_T1_2mm.nii.gz")

MNI_Template["brain"] = os.path.join(
get_resource_path(), "MNI152_T1_2mm_brain.nii.gz"
)
else:
raise ValueError("Available templates are '2mm' or '3mm'")
return MNI_Template
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
4 changes: 2 additions & 2 deletions nltools/simulator.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
from scipy.stats import multivariate_normal, binom, ttest_1samp
from nltools.data import Brain_Data
from nltools.stats import fdr, one_sample_permutation
from nltools.prefs import MNI_Template, resolve_mni_path
from nltools.prefs import MNI_Template
import csv
from copy import deepcopy
from sklearn.utils import check_random_state
Expand All @@ -39,7 +39,7 @@ def __init__(
if isinstance(brain_mask, str):
brain_mask = nib.load(brain_mask)
elif brain_mask is None:
brain_mask = nib.load(resolve_mni_path(MNI_Template)["mask"])
brain_mask = nib.load(MNI_Template.mask)
elif ~isinstance(brain_mask, nib.nifti1.Nifti1Image):
raise ValueError("brain_mask is not a string or a nibabel instance")
self.brain_mask = brain_mask
Expand Down
Loading