Skip to content

Commit dabf52d

Browse files
committed
NF: add support for V4 of PARREC format
V4 appears to differ only in having fewer general and image information fields.
1 parent 20ff9e5 commit dabf52d

File tree

3 files changed

+160
-19
lines changed

3 files changed

+160
-19
lines changed

nibabel/parrec.py

Lines changed: 25 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,11 @@
1111
This is yet another MRI image format generated by Philips scanners. It is an
1212
ASCII header (PAR) plus a binary blob (REC).
1313
14-
This implementation aims to read version 4.2 of this format. Other versions
15-
could probably be supported, but the author is lacking samples of them.
14+
This implementation aims to read version 4 and 4.2 of this format. Other
15+
versions could probably be supported, but we need example images to test
16+
against. If you want us to support another version, and have an image we can
17+
add to the test suite, let us know. You would make us very happy by submitting
18+
a pull request.
1619
1720
###############
1821
PAR file format
@@ -109,8 +112,6 @@
109112
[1, 0, 0, 0], # L
110113
[0, 0, 0, 1]])
111114
)
112-
# PAR header versions we claim to understand
113-
supported_versions = ['V4.2']
114115

115116
# General information dict definitions
116117
# assign props to PAR header entries
@@ -155,7 +156,8 @@
155156

156157
# Image information as coded into a numpy structured array
157158
# header items order per image definition line
158-
image_def_dtd = [
159+
image_def_dtds = {}
160+
image_def_dtds['V4'] = [
159161
('slice number', int),
160162
('echo number', int,),
161163
('dynamic scan number', int,),
@@ -190,17 +192,25 @@
190192
('minimum RR-interval', int,),
191193
('maximum RR-interval', int,),
192194
('TURBO factor', int,),
193-
('Inversion delay', float),
195+
('Inversion delay', float)]
196+
197+
# Extra image def fields for 4.2 compared to 4
198+
image_def_dtds['V4.2'] = image_def_dtds['V4'] + [
194199
('diffusion b value number', int,), # (imagekey!)
195200
('gradient orientation number', int,), # (imagekey!)
196201
('contrast type', 'S30'), # XXX might be too short?
197202
('diffusion anisotropy type', 'S30'), # XXX might be too short?
198203
('diffusion', float, (3,)),
199204
('label type', int,), # (imagekey!)
200205
]
201-
image_def_dtype = np.dtype(image_def_dtd)
202206

203-
# slice orientation codes
207+
#: PAR header versions we claim to understand
208+
supported_versions = list(image_def_dtds.keys())
209+
210+
#: Deprecated; please don't use
211+
image_def_dtype = np.dtype(image_def_dtds['V4.2'])
212+
213+
#: slice orientation codes
204214
slice_orientation_codes = Recoder(( # code, label
205215
(1, 'transverse'),
206216
(2, 'sagittal'),
@@ -270,12 +280,13 @@ def _process_gen_dict(gen_dict):
270280
return general_info
271281

272282

273-
def _process_image_lines(image_lines):
274-
""" Process image information definition lines
283+
def _process_image_lines(image_lines, version):
284+
""" Process image information definition lines according to `version`
275285
"""
276286
# postproc image def props
287+
image_def_dtd = image_def_dtds[version]
277288
# create an array for all image defs
278-
image_defs = np.zeros(len(image_lines), dtype=image_def_dtype)
289+
image_defs = np.zeros(len(image_lines), dtype=image_def_dtd)
279290
# for every image definition
280291
for i, line in enumerate(image_lines):
281292
items = line.split()
@@ -374,6 +385,8 @@ def _err_or_warn(msg):
374385
warnings.warn(msg)
375386

376387
def _chk_trunc(idef_name, gdef_max_name):
388+
if not gdef_max_name in general_info:
389+
return
377390
id_values = image_defs[idef_name + ' number']
378391
n_have = len(set(id_values))
379392
n_expected = general_info[gdef_max_name]
@@ -424,7 +437,7 @@ def parse_PAR_header(fobj):
424437
list, if you are interested in adding support for this version.
425438
""".format(version)))
426439
general_info = _process_gen_dict(gen_dict)
427-
image_defs = _process_image_lines(image_lines)
440+
image_defs = _process_image_lines(image_lines, version)
428441
return general_info, image_defs
429442

430443

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
# === DATA DESCRIPTION FILE ======================================================
2+
#
3+
# CAUTION - Investigational device.
4+
# Limited by Federal Law to investigational use.
5+
#
6+
# Dataset name: E:\\Export\phantom_EPI_asc_CLEAR_2_1
7+
#
8+
# CLINICAL TRYOUT Research image export tool V4
9+
#
10+
# === GENERAL INFORMATION ========================================================
11+
#
12+
. Patient name : phantom
13+
. Examination name : Konvertertest
14+
. Protocol name : EPI_asc CLEAR
15+
. Examination date/time : 2014.02.14 / 09:00:57
16+
. Series Type : Image MRSERIES
17+
. Acquisition nr : 2
18+
. Reconstruction nr : 1
19+
. Scan Duration [sec] : 14
20+
. Max. number of cardiac phases : 1
21+
. Max. number of echoes : 1
22+
. Max. number of slices/locations : 9
23+
. Max. number of dynamics : 3
24+
. Max. number of mixes : 1
25+
. Patient position : Head First Supine
26+
. Preparation direction : Anterior-Posterior
27+
. Technique : FEEPI
28+
. Scan resolution (x, y) : 64 39
29+
. Scan mode : MS
30+
. Repetition time [ms] : 2000.000
31+
. FOV (ap,fh,rl) [mm] : 240.000 70.000 240.000
32+
. Water Fat shift [pixels] : 11.050
33+
. Angulation midslice(ap,fh,rl)[degr]: -13.265 0.000 0.000
34+
. Off Centre midslice(ap,fh,rl) [mm] : 2.508 30.339 -16.032
35+
. Flow compensation <0=no 1=yes> ? : 0
36+
. Presaturation <0=no 1=yes> ? : 0
37+
. Phase encoding velocity [cm/sec] : 0.000000 0.000000 0.000000
38+
. MTC <0=no 1=yes> ? : 0
39+
. SPIR <0=no 1=yes> ? : 1
40+
. EPI factor <0,1=no EPI> : 39
41+
. Dynamic scan <0=no 1=yes> ? : 1
42+
. Diffusion <0=no 1=yes> ? : 0
43+
. Diffusion echo time [ms] : 0.0000
44+
#
45+
# === PIXEL VALUES =============================================================
46+
# PV = pixel value in REC file, FP = floating point value, DV = displayed value on console
47+
# RS = rescale slope, RI = rescale intercept, SS = scale slope
48+
# DV = PV * RS + RI FP = DV / (RS * SS)
49+
#
50+
# === IMAGE INFORMATION DEFINITION =============================================
51+
# The rest of this file contains ONE line per image, this line contains the following information:
52+
#
53+
# slice number (integer)
54+
# echo number (integer)
55+
# dynamic scan number (integer)
56+
# cardiac phase number (integer)
57+
# image_type_mr (integer)
58+
# scanning sequence (integer)
59+
# index in REC file (in images) (integer)
60+
# image pixel size (in bits) (integer)
61+
# scan percentage (integer)
62+
# recon resolution (x y) (2*integer)
63+
# rescale intercept (float)
64+
# rescale slope (float)
65+
# scale slope (float)
66+
# window center (integer)
67+
# window width (integer)
68+
# image angulation (ap,fh,rl in degrees ) (3*float)
69+
# image offcentre (ap,fh,rl in mm ) (3*float)
70+
# slice thickness (in mm ) (float)
71+
# slice gap (in mm ) (float)
72+
# image_display_orientation (integer)
73+
# slice orientation ( TRA/SAG/COR ) (integer)
74+
# fmri_status_indication (integer)
75+
# image_type_ed_es (end diast/end syst) (integer)
76+
# pixel spacing (x,y) (in mm) (2*float)
77+
# echo_time (float)
78+
# dyn_scan_begin_time (float)
79+
# trigger_time (float)
80+
# diffusion_b_factor (float)
81+
# number of averages (integer)
82+
# image_flip_angle (in degrees) (float)
83+
# cardiac frequency (bpm) (integer)
84+
# minimum RR-interval (in ms) (integer)
85+
# maximum RR-interval (in ms) (integer)
86+
# TURBO factor <0=no turbo> (integer)
87+
# Inversion delay (in ms) (float)
88+
#
89+
# === IMAGE INFORMATION ==========================================================
90+
# 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
91+
92+
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
93+
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
94+
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
95+
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
96+
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
97+
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
98+
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
99+
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
100+
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
101+
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
102+
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
103+
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
104+
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
105+
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
106+
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
107+
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
108+
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
109+
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
110+
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
111+
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
112+
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
113+
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
114+
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
115+
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
116+
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
117+
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
118+
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
119+
120+
# === END OF DATA DESCRIPTION FILE ===============================================

nibabel/tests/test_parrec.py

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@
3636
# Fake truncated
3737
TRUNC_PAR = pjoin(DATA_PATH, 'phantom_truncated.PAR')
3838
TRUNC_REC = pjoin(DATA_PATH, 'phantom_truncated.REC')
39+
# Fake V4
40+
V4_PAR = pjoin(DATA_PATH, 'phantom_fake_v4.PAR')
3941
# Affine as we determined it mid-2014
4042
AN_OLD_AFFINE = np.array(
4143
[[-3.64994708, 0., 1.83564171, 123.66276611],
@@ -144,13 +146,19 @@ def test_top_level_load():
144146

145147

146148
def test_header():
147-
hdr = PARRECHeader(HDR_INFO, HDR_DEFS)
148-
assert_equal(hdr.get_data_shape(), (64, 64, 9, 3))
149-
assert_equal(hdr.get_data_dtype(), np.dtype('<i2'))
150-
assert_equal(hdr.get_zooms(), (3.75, 3.75, 8.0, 2.0))
151-
assert_equal(hdr.get_data_offset(), 0)
152-
si = np.array([np.unique(x) for x in hdr.get_data_scaling()]).ravel()
153-
assert_almost_equal(si, (1.2903541326522827, 0.0), 5)
149+
v42_hdr = PARRECHeader(HDR_INFO, HDR_DEFS)
150+
with open(V4_PAR, 'rt') as fobj:
151+
v4_hdr = PARRECHeader.from_fileobj(fobj)
152+
for hdr in (v42_hdr, v4_hdr):
153+
hdr = PARRECHeader(HDR_INFO, HDR_DEFS)
154+
assert_equal(hdr.get_data_shape(), (64, 64, 9, 3))
155+
assert_equal(hdr.get_data_dtype(), np.dtype('<i2'))
156+
assert_equal(hdr.get_zooms(), (3.75, 3.75, 8.0, 2.0))
157+
assert_equal(hdr.get_data_offset(), 0)
158+
si = np.array([np.unique(x) for x in hdr.get_data_scaling()]).ravel()
159+
assert_almost_equal(si, (1.2903541326522827, 0.0), 5)
160+
assert_equal(hdr.get_q_vectors(), None)
161+
assert_equal(hdr.get_bvals_bvecs(), (None, None))
154162

155163

156164
def test_header_scaling():

0 commit comments

Comments
 (0)