Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding mask functions and tests to the test branch #239

Closed
wants to merge 8 commits into from
126 changes: 126 additions & 0 deletions code/utils/functions/mask_functions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
"""mask_functions.py

A collection of functions to make masks on data.
See test_* functions in this directory for nose tests
"""
from __future__ import print_function, division

import sys, os, pdb
import numpy as np
import nibabel as nib
import numpy.linalg as npl

from os.path import splitext
from numpy.testing import assert_array_equal
from scipy.ndimage import affine_transform


def make_mask_filtered_data(func_path, mask_path):
"""Return the masked filtered data

Parameters
----------
func_path: string
path to the 4D data

mask_path: string
path to the mask function

Return
------
masked_func: 4D array
masked filtered data

"""
func_img = nib.load(func_path)
mask_img = nib.load(mask_path)
mask = mask_img.get_data()
func_data = func_img.get_data()
# Make data 4D to prepare for "broadcasting"
mask = np.reshape(mask, mask.shape + (1,))
# "Broadcasting" expands the final length 1 dimension to match the func data
masked_func = nib.Nifti1Image(func_data, func_img.affine, func_img.header)
# nib.save(masked_func, 'masked_' + img_name )
return masked_func

def make_binary_mask(data, mask_bool):
"""Return a numpy array with 0 and 1
Parameters
----------

Return
------
"""
data = np.asarray(data)
mask_bool = np.asarray(mask_bool)
assert(len(data.shape) == len(mask_bool.shape)),\
"Data and mask shape differ \n" \
+ "Data dim is: %s\nMask dim is: %s" \
%(len(data.shape), len(mask_bool.shape))
assert(all(data.shape[i] >= mask_bool.shape[i] \
for i in range(len(data.shape)))),\
"Data and mask shape are not compatible"\
+"Data shape is: %s\nMask shape is: %s"\
%(data.shape, mask_bool.shape)
new_mask = np.zeros(data.shape)
new_mask[mask_bool] = 1
return new_mask


def resample_filtered_data(func_path, template_path):
"""Resample template to filtered functional dataset

Parameters
----------
func_path: string
path of the 4D filtered data to resample
template_path: string
path to the template to apply on the data

"""
filtered_func = nib.load(func_path)
filtered_shape = filtered_func.shape[:3]
template_img = nib.load(mni_fname)
template_data = template_img.get_data()
vox2vox = npl.inv(template_img.affine).dot(filtered_func.affine)
M, trans = nib.affines.to_matvec(vox2vox)
resampled = affine_transform(template_data, M, trans,
output_shape=filtered_shape)
froot, ext = splitext(mni_fname)
new_name = froot + '_2mm' + ext
new_img = nib.Nifti1Image(resampled, filtered_func.affine,
template_img.header)
#nib.save(new_img, new_name)
return new_img

def apply_mask(data, mask):
"""Apply mask on an image and return the masked data

Parameters
----------
data: numpy array
The subject's run image data
mask: numpy array same shape as data
The mask for the corresponding data_3d
has 1 for the positions to select and
0 for the positions to filter out.
Return
------
masked_data: numpy array
Array with the values of the data at the selected positions
and 0 for the position filtered out by the mask.
"""
assert(data.shape == mask.shape), "Data and mask shape differ \n" \
+ "Data shape i: %s\nMask shape is: %s" %(data.shape, mask.shape)
return data * mask

if __name__=='__main__':
slab0 = np.reshape(np.arange(9), (3, 3))
slab1 = np.reshape(np.arange(100, 109), (3, 3))
arr_3d = np.zeros((2, 3, 3))
arr_3d[0, :, :] = slab0
arr_3d[1, :, :] = slab1
arr_2d = np.arange(9).reshape((3,3))
mask_bool2d = arr_2d < 10
make_binary_mask(arr_3d,mask_bool2d)
pdb.set_trace()
51 changes: 51 additions & 0 deletions code/utils/tests/test_mask.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
"""test_mask.py
Tests for the functions in the mask_functions.py

Run with:
nosetests test_mask.py
"""
from __future__ import print_function
import os, sys
import numpy as np
from numpy.testing import assert_array_equal

#Append path to functions
sys.path.append(os.path.join(os.path.dirname(__file__), "../functions/"))
from mask_functions import *


def test_apply_mask():
# We make a 3D array of shape (3,3,2)
slab0 = np.reshape(np.arange(9), (3, 3))
slab1 = np.reshape(np.arange(100, 109), (3, 3))
arr_3d = np.zeros((2, 3, 3))
arr_3d[0, :, :] = slab0
arr_3d[1, :, :] = slab1
# We make a mask as a 3D array of shape (2,3,3)
# with zeros on the 2nd component of the 1st dimension
mask_3d = np.zeros((2, 3, 3))
mask_3d[0] = np.ones((3,3))
# Defined the resulting masked array
masked_arr = np.zeros((2,3,3))
masked_arr[0, :, :] = slab0
assert_array_equal(apply_mask(arr_3d, mask_3d),masked_arr)


def test_make_binary_mask():
# We make a 3D array of shape (3,3,2)
slab0 = np.reshape(np.arange(9), (3, 3))
slab1 = np.reshape(np.arange(100, 109), (3, 3))
arr_3d = np.zeros((2, 3, 3))
arr_3d[0, :, :] = slab0
arr_3d[1, :, :] = slab1
# We make a mask boolean as a 3D array of shape (2,3,3)
# that filtered the values below 100
mask_bool = arr_3d < 100
mask_3d = np.zeros((2, 3, 3))
mask_3d[0] = np.ones((3,3))
assert_array_equal(make_binary_mask(arr_3d,mask_bool), mask_3d)
arr_2d = np.arange(9).reshape((3,3))
mask_bool2d = arr_2d < 10
# make_binary_mask(arr_3d,mask_bool2d)