# -*- coding: utf-8 -*-
from dipy.io.utils import is_header_compatible
import logging
import nibabel as nib
import numpy as np
import os
from scilpy.utils import is_float
[docs]
def load_img(arg):
"""
Function to create the variable for scil_volume_math main function.
It can be a float or an image and if image it checks if it contains
integer values and its declared data type is integer or if it is containing
float values but declared as integer, in which case a warning is raised.
Parameters
"""
if is_float(arg):
img = float(arg)
dtype = np.float64
else:
if not os.path.isfile(arg):
raise ValueError('Input file {} does not exist.'.format(arg))
img = nib.load(arg)
shape = img.header.get_data_shape()
dtype = img.header.get_data_dtype()
logging.info('Loaded {} of shape {} and data_type {}.'.format(
arg, shape, dtype))
data_as_float = img.get_fdata()
sum_float = float(np.sum(data_as_float))
if not sum_float.is_integer():
logging.warning('Image {} has an integer type but contains '
'non-integer values. Loading, computating and saving '
'will be done as float. Using an integer dtype '
'will lead to data loss.'.format(arg))
dtype = np.float64
img.header.set_data_dtype(dtype)
if len(shape) > 3:
logging.warning('{} has {} dimensions, be careful.'.format(
arg, len(shape)))
elif len(shape) < 3:
raise ValueError('{} has {} dimensions, not valid.'.format(
arg, len(shape)))
return img, dtype
[docs]
def assert_same_resolution(images):
"""
Check the resolution of multiple images.
Parameters
----------
images : list of string or string
List of images or an image.
"""
if isinstance(images, str):
images = [images]
if len(images) == 0:
raise Exception("Can't check if images are of the same "
"resolution/affine. No image has been given")
for curr_image in images[1:]:
if not is_header_compatible(images[0], curr_image):
raise Exception(f"Images are not of the same resolution/affine : "
f"({curr_image}) vs ({images[0]})")
[docs]
def get_data_as_mask(mask_img, dtype=np.uint8):
"""
Get data as mask (force type np.uint8 or bool), check data type before
casting.
Parameters
----------
mask_img: nibabel.nifti1.Nifti1Image
Mask image.
dtype: type or str
Data type for the output data (default: uint8)
Return
------
data: numpy.ndarray
Data (dtype : np.uint8 or bool).
"""
# Verify that out data type is ok
if not (issubclass(np.dtype(dtype).type, np.uint8) or
issubclass(np.dtype(dtype).type, np.dtype(bool).type)):
raise IOError('Output data type must be uint8 or bool. '
'Current data type is {}.'.format(dtype))
# Verify that loaded datatype is ok
curr_type = mask_img.get_data_dtype().type
basename = os.path.basename(mask_img.get_filename())
if np.issubdtype(curr_type, np.signedinteger) or \
np.issubdtype(curr_type, np.unsignedinteger) \
or np.issubdtype(curr_type, np.dtype(bool).type):
data = np.asanyarray(mask_img.dataobj).astype(dtype)
# Verify that it contains only 0 and 1.
unique_vals = np.unique(data)
if len(unique_vals) == 2:
if np.all(unique_vals != np.array([0, 1])):
logging.warning('The two unique values in mask were not 0 and'
' 1. Binarizing the mask now.')
data[data != 0] = 1
elif len(unique_vals) == 1:
data[data != 0] = 1
else:
raise IOError('The image {} contains more than 2 values. '
'It can\'t be loaded as mask.'.format(basename))
else:
raise IOError('The image {} cannot be loaded as mask because '
'its type {} is not compatible '
'with a mask.\n'
'To convert your data, you may use tools like mrconvert '
'or \n'
'>> scil_volume_math.py convert IMG IMG '
'--data_type uint8 -f'.format(basename, curr_type))
return data