Skip to content

MRG: some refactoring of PARREC API #264

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

Merged
merged 6 commits into from
Oct 24, 2014
Merged
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
4 changes: 3 additions & 1 deletion bin/parrec2nii
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,9 @@ def proc_file(infile, opts):
# load the PAR header and data
scaling = None if opts.scaling == 'off' else opts.scaling
infile = fname_ext_ul_case(infile)
pr_img = pr.load(infile, opts.permit_truncated, scaling)
pr_img = pr.load(infile,
permit_truncated=opts.permit_truncated,
scaling=scaling)
pr_hdr = pr_img.header
raw_data = pr_img.dataobj.get_unscaled()
affine = pr_hdr.get_affine(origin=opts.origin)
Expand Down
27 changes: 27 additions & 0 deletions nibabel/keywordonly.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
""" Decorator for labeling keyword arguments as keyword only
"""

from functools import wraps

def kw_only_func(n):
""" Return function decorator enforcing maximum of `n` positional arguments
"""
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
if len(args) > n:
raise TypeError(
'{0} takes at most {1} positional argument{2}'.format(
func.__name__, n, 's' if n > 1 else ''))
return func(*args, **kwargs)
return wrapper
return decorator


def kw_only_meth(n):
""" Return method decorator enforcing maximum of `n` positional arguments

The method has at least one positional argument ``self`` or ``cls``; allow
for that.
"""
return kw_only_func(n+1)
35 changes: 29 additions & 6 deletions nibabel/parrec.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@
from copy import deepcopy
import re

from .keywordonly import kw_only_meth
from .spatialimages import SpatialImage, Header
from .eulerangles import euler2mat
from .volumeutils import Recoder, array_from_file, BinOpener
Expand Down Expand Up @@ -462,6 +463,18 @@ def _data_from_rec(rec_fileobj, in_shape, dtype, slice_indices, out_shape,

class PARRECArrayProxy(object):
def __init__(self, file_like, header, scaling):
""" Initialize PARREC array proxy

Parameters
----------
file_like : file-like object
Filename or object implementing ``read, seek, tell``
header : PARRECHeader instance
Implementing ``get_data_shape, get_data_dtype``,
``get_sorted_slice_indices``, ``get_data_scaling``
scaling : {'fp', 'dv'}
Type of scaling to use - see header ``get_data_scaling`` method.
"""
self.file_like = file_like
# Copies of values needed to read array
self._shape = header.get_data_shape()
Expand Down Expand Up @@ -512,6 +525,7 @@ def __init__(self, info, image_defs, permit_truncated=False):
"""
self.general_info = info.copy()
self.image_defs = image_defs.copy()
self.permit_truncated = permit_truncated
_truncation_checks(info, image_defs, permit_truncated)
# charge with basic properties to be able to use base class
# functionality
Expand All @@ -538,7 +552,8 @@ def from_fileobj(klass, fileobj, permit_truncated=False):

def copy(self):
return PARRECHeader(deepcopy(self.general_info),
self.image_defs.copy())
self.image_defs.copy(),
self.permit_truncated)

def as_analyze_map(self):
"""Convert PAR parameters to NIFTI1 format"""
Expand Down Expand Up @@ -886,7 +901,8 @@ class PARRECImage(SpatialImage):
ImageArrayProxy = PARRECArrayProxy

@classmethod
def from_file_map(klass, file_map, permit_truncated, scaling):
@kw_only_meth(1)
def from_file_map(klass, file_map, permit_truncated=False, scaling='dv'):
pt = permit_truncated
with file_map['header'].get_prepare_fileobj('rt') as hdr_fobj:
hdr = klass.header_class.from_fileobj(hdr_fobj,
Expand All @@ -896,8 +912,15 @@ def from_file_map(klass, file_map, permit_truncated, scaling):
return klass(data, hdr.get_affine(), header=hdr, extra=None,
file_map=file_map)

@classmethod
@kw_only_meth(1)
def from_filename(klass, filename, permit_truncated=False, scaling='dv'):
file_map = klass.filespec_to_file_map(filename)
return klass.from_file_map(file_map,
permit_truncated=permit_truncated,
scaling=scaling)

load = from_filename


def load(filename, permit_truncated=False, scaling='dv'):
file_map = PARRECImage.filespec_to_file_map(filename)
return PARRECImage.from_file_map(file_map, permit_truncated, scaling)
load.__doc__ = PARRECImage.load.__doc__
load = PARRECImage.load
8 changes: 8 additions & 0 deletions nibabel/tests/data/README.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
##################
Nibabel data files
##################

``phantom_truncated.REC`` is a copy of ``phantom_EPI_asc_CLEAR_2_1.REC``.

``phantom_truncated.PAR`` is a slightly edited copy of
``phantom_EPI_asc_CLEAR_2_1.PAR``.
129 changes: 129 additions & 0 deletions nibabel/tests/data/phantom_truncated.PAR
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
# === DATA DESCRIPTION FILE ======================================================
#
# CAUTION - Investigational device.
# Limited by Federal Law to investigational use.
#
# Dataset name: E:\\Export\phantom_EPI_asc_CLEAR_2_1
#
# CLINICAL TRYOUT Research image export tool V4.2
#
# === GENERAL INFORMATION ========================================================
#
. Patient name : phantom
. Examination name : Konvertertest
. Protocol name : EPI_asc CLEAR
. Examination date/time : 2014.02.14 / 09:00:57
. Series Type : Image MRSERIES
. Acquisition nr : 2
. Reconstruction nr : 1
. Scan Duration [sec] : 14
. Max. number of cardiac phases : 1
. Max. number of echoes : 1
. Max. number of slices/locations : 9
. Max. number of dynamics : 4
. Max. number of mixes : 1
. Patient position : Head First Supine
. Preparation direction : Anterior-Posterior
. Technique : FEEPI
. Scan resolution (x, y) : 64 39
. Scan mode : MS
. Repetition time [ms] : 2000.000
. FOV (ap,fh,rl) [mm] : 240.000 70.000 240.000
. Water Fat shift [pixels] : 11.050
. Angulation midslice(ap,fh,rl)[degr]: -13.265 0.000 0.000
. Off Centre midslice(ap,fh,rl) [mm] : 2.508 30.339 -16.032
. Flow compensation <0=no 1=yes> ? : 0
. Presaturation <0=no 1=yes> ? : 0
. Phase encoding velocity [cm/sec] : 0.000000 0.000000 0.000000
. MTC <0=no 1=yes> ? : 0
. SPIR <0=no 1=yes> ? : 1
. EPI factor <0,1=no EPI> : 39
. Dynamic scan <0=no 1=yes> ? : 1
. Diffusion <0=no 1=yes> ? : 0
. Diffusion echo time [ms] : 0.0000
. Max. number of diffusion values : 1
. Max. number of gradient orients : 1
. Number of label types <0=no ASL> : 0
#
# === PIXEL VALUES =============================================================
# PV = pixel value in REC file, FP = floating point value, DV = displayed value on console
# RS = rescale slope, RI = rescale intercept, SS = scale slope
# DV = PV * RS + RI FP = DV / (RS * SS)
#
# === IMAGE INFORMATION DEFINITION =============================================
# The rest of this file contains ONE line per image, this line contains the following information:
#
# slice number (integer)
# echo number (integer)
# dynamic scan number (integer)
# cardiac phase number (integer)
# image_type_mr (integer)
# scanning sequence (integer)
# index in REC file (in images) (integer)
# image pixel size (in bits) (integer)
# scan percentage (integer)
# recon resolution (x y) (2*integer)
# rescale intercept (float)
# rescale slope (float)
# scale slope (float)
# window center (integer)
# window width (integer)
# image angulation (ap,fh,rl in degrees ) (3*float)
# image offcentre (ap,fh,rl in mm ) (3*float)
# slice thickness (in mm ) (float)
# slice gap (in mm ) (float)
# image_display_orientation (integer)
# slice orientation ( TRA/SAG/COR ) (integer)
# fmri_status_indication (integer)
# image_type_ed_es (end diast/end syst) (integer)
# pixel spacing (x,y) (in mm) (2*float)
# echo_time (float)
# dyn_scan_begin_time (float)
# trigger_time (float)
# diffusion_b_factor (float)
# number of averages (integer)
# image_flip_angle (in degrees) (float)
# cardiac frequency (bpm) (integer)
# minimum RR-interval (in ms) (integer)
# maximum RR-interval (in ms) (integer)
# TURBO factor <0=no turbo> (integer)
# Inversion delay (in ms) (float)
# diffusion b value number (imagekey!) (integer)
# gradient orientation number (imagekey!) (integer)
# contrast type (string)
# diffusion anisotropy type (string)
# diffusion (ap, fh, rl) (3*float)
# label type (ASL) (imagekey!) (integer)
#
# === IMAGE INFORMATION ==========================================================
# sl ec dyn ph ty idx pix scan% rec size (re)scale window angulation offcentre thick gap info spacing echo dtime ttime diff avg flip freq RR-int turbo delay b grad cont anis diffusion L.ty

1 1 1 1 0 2 0 16 62 64 64 0.00000 1.29035 4.28404e-003 1070 1860 -13.26 -0.00 -0.00 2.51 -0.81 -8.69 6.000 2.000 0 1 0 2 3.750 3.750 30.00 0.00 0.00 0.00 0 90.00 0 0 0 39 0.0 1 1 8 0 0.000 0.000 0.000 1
2 1 1 1 0 2 1 16 62 64 64 0.00000 1.29035 4.28404e-003 1122 1951 -13.26 -0.00 -0.00 2.51 6.98 -10.53 6.000 2.000 0 1 0 2 3.750 3.750 30.00 0.00 0.00 0.00 0 90.00 0 0 0 39 0.0 1 1 8 0 0.000 0.000 0.000 1
3 1 1 1 0 2 2 16 62 64 64 0.00000 1.29035 4.28404e-003 1137 1977 -13.26 -0.00 -0.00 2.51 14.77 -12.36 6.000 2.000 0 1 0 2 3.750 3.750 30.00 0.00 0.00 0.00 0 90.00 0 0 0 39 0.0 1 1 8 0 0.000 0.000 0.000 1
4 1 1 1 0 2 3 16 62 64 64 0.00000 1.29035 4.28404e-003 1217 2116 -13.26 -0.00 -0.00 2.51 22.55 -14.20 6.000 2.000 0 1 0 2 3.750 3.750 30.00 0.00 0.00 0.00 0 90.00 0 0 0 39 0.0 1 1 8 0 0.000 0.000 0.000 1
5 1 1 1 0 2 4 16 62 64 64 0.00000 1.29035 4.28404e-003 1216 2113 -13.26 -0.00 -0.00 2.51 30.34 -16.03 6.000 2.000 0 1 0 2 3.750 3.750 30.00 0.00 0.00 0.00 0 90.00 0 0 0 39 0.0 1 1 8 0 0.000 0.000 0.000 1
6 1 1 1 0 2 5 16 62 64 64 0.00000 1.29035 4.28404e-003 1141 1983 -13.26 -0.00 -0.00 2.51 38.13 -17.87 6.000 2.000 0 1 0 2 3.750 3.750 30.00 0.00 0.00 0.00 0 90.00 0 0 0 39 0.0 1 1 8 0 0.000 0.000 0.000 1
7 1 1 1 0 2 6 16 62 64 64 0.00000 1.29035 4.28404e-003 1119 1945 -13.26 -0.00 -0.00 2.51 45.91 -19.70 6.000 2.000 0 1 0 2 3.750 3.750 30.00 0.00 0.00 0.00 0 90.00 0 0 0 39 0.0 1 1 8 0 0.000 0.000 0.000 1
8 1 1 1 0 2 7 16 62 64 64 0.00000 1.29035 4.28404e-003 1097 1907 -13.26 -0.00 -0.00 2.51 53.70 -21.54 6.000 2.000 0 1 0 2 3.750 3.750 30.00 0.00 0.00 0.00 0 90.00 0 0 0 39 0.0 1 1 8 0 0.000 0.000 0.000 1
9 1 1 1 0 2 8 16 62 64 64 0.00000 1.29035 4.28404e-003 1146 1991 -13.26 -0.00 -0.00 2.51 61.49 -23.37 6.000 2.000 0 1 0 2 3.750 3.750 30.00 0.00 0.00 0.00 0 90.00 0 0 0 39 0.0 1 1 8 0 0.000 0.000 0.000 1
1 1 2 1 0 2 9 16 62 64 64 0.00000 1.29035 4.28404e-003 1071 1863 -13.26 -0.00 -0.00 2.51 -0.81 -8.69 6.000 2.000 0 1 0 2 3.750 3.750 30.00 2.00 0.00 0.00 0 90.00 0 0 0 39 0.0 1 1 8 0 0.000 0.000 0.000 1
2 1 2 1 0 2 10 16 62 64 64 0.00000 1.29035 4.28404e-003 1123 1953 -13.26 -0.00 -0.00 2.51 6.98 -10.53 6.000 2.000 0 1 0 2 3.750 3.750 30.00 2.00 0.00 0.00 0 90.00 0 0 0 39 0.0 1 1 8 0 0.000 0.000 0.000 1
3 1 2 1 0 2 11 16 62 64 64 0.00000 1.29035 4.28404e-003 1135 1973 -13.26 -0.00 -0.00 2.51 14.77 -12.36 6.000 2.000 0 1 0 2 3.750 3.750 30.00 2.00 0.00 0.00 0 90.00 0 0 0 39 0.0 1 1 8 0 0.000 0.000 0.000 1
4 1 2 1 0 2 12 16 62 64 64 0.00000 1.29035 4.28404e-003 1209 2101 -13.26 -0.00 -0.00 2.51 22.55 -14.20 6.000 2.000 0 1 0 2 3.750 3.750 30.00 2.00 0.00 0.00 0 90.00 0 0 0 39 0.0 1 1 8 0 0.000 0.000 0.000 1
5 1 2 1 0 2 13 16 62 64 64 0.00000 1.29035 4.28404e-003 1215 2113 -13.26 -0.00 -0.00 2.51 30.34 -16.03 6.000 2.000 0 1 0 2 3.750 3.750 30.00 2.00 0.00 0.00 0 90.00 0 0 0 39 0.0 1 1 8 0 0.000 0.000 0.000 1
6 1 2 1 0 2 14 16 62 64 64 0.00000 1.29035 4.28404e-003 1145 1990 -13.26 -0.00 -0.00 2.51 38.13 -17.87 6.000 2.000 0 1 0 2 3.750 3.750 30.00 2.00 0.00 0.00 0 90.00 0 0 0 39 0.0 1 1 8 0 0.000 0.000 0.000 1
7 1 2 1 0 2 15 16 62 64 64 0.00000 1.29035 4.28404e-003 1119 1945 -13.26 -0.00 -0.00 2.51 45.91 -19.70 6.000 2.000 0 1 0 2 3.750 3.750 30.00 2.00 0.00 0.00 0 90.00 0 0 0 39 0.0 1 1 8 0 0.000 0.000 0.000 1
8 1 2 1 0 2 16 16 62 64 64 0.00000 1.29035 4.28404e-003 1093 1899 -13.26 -0.00 -0.00 2.51 53.70 -21.54 6.000 2.000 0 1 0 2 3.750 3.750 30.00 2.00 0.00 0.00 0 90.00 0 0 0 39 0.0 1 1 8 0 0.000 0.000 0.000 1
9 1 2 1 0 2 17 16 62 64 64 0.00000 1.29035 4.28404e-003 1150 1999 -13.26 -0.00 -0.00 2.51 61.49 -23.37 6.000 2.000 0 1 0 2 3.750 3.750 30.00 2.00 0.00 0.00 0 90.00 0 0 0 39 0.0 1 1 8 0 0.000 0.000 0.000 1
1 1 3 1 0 2 18 16 62 64 64 0.00000 1.29035 4.28404e-003 1070 1860 -13.26 -0.00 -0.00 2.51 -0.81 -8.69 6.000 2.000 0 1 0 2 3.750 3.750 30.00 4.00 0.00 0.00 0 90.00 0 0 0 39 0.0 1 1 8 0 0.000 0.000 0.000 1
2 1 3 1 0 2 19 16 62 64 64 0.00000 1.29035 4.28404e-003 1125 1955 -13.26 -0.00 -0.00 2.51 6.98 -10.53 6.000 2.000 0 1 0 2 3.750 3.750 30.00 4.00 0.00 0.00 0 90.00 0 0 0 39 0.0 1 1 8 0 0.000 0.000 0.000 1
3 1 3 1 0 2 20 16 62 64 64 0.00000 1.29035 4.28404e-003 1135 1973 -13.26 -0.00 -0.00 2.51 14.77 -12.36 6.000 2.000 0 1 0 2 3.750 3.750 30.00 4.00 0.00 0.00 0 90.00 0 0 0 39 0.0 1 1 8 0 0.000 0.000 0.000 1
4 1 3 1 0 2 21 16 62 64 64 0.00000 1.29035 4.28404e-003 1211 2105 -13.26 -0.00 -0.00 2.51 22.55 -14.20 6.000 2.000 0 1 0 2 3.750 3.750 30.00 4.00 0.00 0.00 0 90.00 0 0 0 39 0.0 1 1 8 0 0.000 0.000 0.000 1
5 1 3 1 0 2 22 16 62 64 64 0.00000 1.29035 4.28404e-003 1218 2118 -13.26 -0.00 -0.00 2.51 30.34 -16.03 6.000 2.000 0 1 0 2 3.750 3.750 30.00 4.00 0.00 0.00 0 90.00 0 0 0 39 0.0 1 1 8 0 0.000 0.000 0.000 1
6 1 3 1 0 2 23 16 62 64 64 0.00000 1.29035 4.28404e-003 1143 1987 -13.26 -0.00 -0.00 2.51 38.13 -17.87 6.000 2.000 0 1 0 2 3.750 3.750 30.00 4.00 0.00 0.00 0 90.00 0 0 0 39 0.0 1 1 8 0 0.000 0.000 0.000 1
7 1 3 1 0 2 24 16 62 64 64 0.00000 1.29035 4.28404e-003 1120 1947 -13.26 -0.00 -0.00 2.51 45.91 -19.70 6.000 2.000 0 1 0 2 3.750 3.750 30.00 4.00 0.00 0.00 0 90.00 0 0 0 39 0.0 1 1 8 0 0.000 0.000 0.000 1
8 1 3 1 0 2 25 16 62 64 64 0.00000 1.29035 4.28404e-003 1093 1901 -13.26 -0.00 -0.00 2.51 53.70 -21.54 6.000 2.000 0 1 0 2 3.750 3.750 30.00 4.00 0.00 0.00 0 90.00 0 0 0 39 0.0 1 1 8 0 0.000 0.000 0.000 1
9 1 3 1 0 2 26 16 62 64 64 0.00000 1.29035 4.28404e-003 1151 2001 -13.26 -0.00 -0.00 2.51 61.49 -23.37 6.000 2.000 0 1 0 2 3.750 3.750 30.00 4.00 0.00 0.00 0 90.00 0 0 0 39 0.0 1 1 8 0 0.000 0.000 0.000 1

# === END OF DATA DESCRIPTION FILE ===============================================
Binary file added nibabel/tests/data/phantom_truncated.REC
Binary file not shown.
39 changes: 39 additions & 0 deletions nibabel/tests/test_keywordonly.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
""" Test kw_only decorators """

from ..keywordonly import kw_only_func, kw_only_meth

from nose.tools import assert_true, assert_false, assert_equal, assert_raises


def test_kw_only_func():
# Test decorator
def func(an_arg):
"My docstring"
return an_arg
assert_equal(func(1), 1)
assert_raises(TypeError, func, 1, 2)
dec_func = kw_only_func(1)(func)
assert_equal(dec_func(1), 1)
assert_raises(TypeError, dec_func, 1, 2)
assert_raises(TypeError, dec_func, 1, akeyarg=3)
assert_equal(dec_func.__doc__, 'My docstring')
@kw_only_func(1)
def kw_func(an_arg, a_kwarg='thing'):
"Another docstring"
return an_arg, a_kwarg
assert_equal(kw_func(1), (1, 'thing'))
assert_raises(TypeError, kw_func, 1, 2)
assert_equal(kw_func(1, a_kwarg=2), (1, 2))
assert_raises(TypeError, kw_func, 1, akeyarg=3)
assert_equal(kw_func.__doc__, 'Another docstring')
class C(object):
@kw_only_meth(1)
def kw_meth(self, an_arg, a_kwarg='thing'):
"Method docstring"
return an_arg, a_kwarg
c = C()
assert_equal(c.kw_meth(1), (1, 'thing'))
assert_raises(TypeError, c.kw_meth, 1, 2)
assert_equal(c.kw_meth(1, a_kwarg=2), (1, 2))
assert_raises(TypeError, c.kw_meth, 1, akeyarg=3)
assert_equal(c.kw_meth.__doc__, 'Method docstring')
Loading