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

WIP: adding Philips XML/REC support #683

Open
wants to merge 16 commits into
base: master
Choose a base branch
from
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
10 changes: 10 additions & 0 deletions bin/xmlrec2nii
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#!python
"""XML/REC to NIfTI converter
"""

# parrec2nii reads XML/REC as well
from nibabel.cmdline.parrec2nii import main


if __name__ == '__main__':
main()
19 changes: 14 additions & 5 deletions nibabel/cmdline/parrec2nii.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import csv
import nibabel
import nibabel.parrec as pr
import nibabel.xmlrec as xr
from nibabel.parrec import one_line
from nibabel.mriutils import calculate_dwell_time, MRIError
import nibabel.nifti1 as nifti1
Expand Down Expand Up @@ -147,7 +148,15 @@ def error(msg, exit_code):

def proc_file(infile, opts):
# figure out the output filename, and see if it exists
basefilename = splitext_addext(os.path.basename(infile))[0]
basefilename, ext = splitext_addext(os.path.basename(infile))[:2]

ext = ext.lower()
if (ext == '.xml' or
(ext == '.rec' and os.path.exists(basefilename + '.xml'))):
pr_module = xr
else:
pr_module = pr

if opts.outdir is not None:
# set output path
basefilename = os.path.join(opts.outdir, basefilename)
Expand All @@ -165,10 +174,10 @@ def proc_file(infile, opts):
# load the PAR header and data
scaling = 'dv' if opts.scaling == 'off' else opts.scaling
infile = fname_ext_ul_case(infile)
pr_img = pr.load(infile,
permit_truncated=opts.permit_truncated,
scaling=scaling,
strict_sort=opts.strict_sort)
pr_img = pr_module.load(infile,
permit_truncated=opts.permit_truncated,
scaling=scaling,
strict_sort=opts.strict_sort)
pr_hdr = pr_img.header
affine = pr_hdr.get_affine(origin=opts.origin)
slope, intercept = pr_hdr.get_data_scaling(scaling)
Expand Down
11 changes: 9 additions & 2 deletions nibabel/imageclasses.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
from .nifti1 import Nifti1Pair, Nifti1Image
from .nifti2 import Nifti2Pair, Nifti2Image
from .parrec import PARRECImage
from .xmlrec import XMLRECImage
from .spm99analyze import Spm99AnalyzeImage
from .spm2analyze import Spm2AnalyzeImage
from .volumeutils import Recoder
Expand All @@ -32,7 +33,7 @@
Cifti2Image, Nifti2Image, # Cifti2 before Nifti2
Spm2AnalyzeImage, Spm99AnalyzeImage, AnalyzeImage,
Minc1Image, Minc2Image, MGHImage,
PARRECImage, GiftiImage, AFNIImage]
PARRECImage, XMLRECImage, GiftiImage, AFNIImage]


# DEPRECATED: mapping of names to classes and class functionality
Expand Down Expand Up @@ -90,6 +91,11 @@ def __getitem__(self, *args, **kwargs):
'has_affine': True,
'makeable': False,
'rw': False},
xml={'class': XMLRECImage,
'ext': '.xml',
'has_affine': True,
'makeable': False,
'rw': False},
afni={'class': AFNIImage,
'ext': '.brik',
'has_affine': True,
Expand All @@ -113,6 +119,7 @@ def __getitem__(self, *args, **kwargs):
('mgh', '.mgh'),
('mgz', '.mgz'),
('par', '.par'),
('xml', '.xml'),
('brik', '.brik')
))

Expand All @@ -121,7 +128,7 @@ def __getitem__(self, *args, **kwargs):
# here.
KNOWN_SPATIAL_FIRST = (Nifti1Pair, Nifti1Image, Nifti2Pair, Nifti2Image,
Spm2AnalyzeImage, Spm99AnalyzeImage, AnalyzeImage,
MGHImage, PARRECImage, AFNIImage)
MGHImage, PARRECImage, XMLRECImage, AFNIImage)


def spatial_axes_first(img):
Expand Down
10 changes: 10 additions & 0 deletions nibabel/loadsave.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
from .arrayproxy import is_proxy
from .py3k import FileNotFoundError
from .deprecated import deprecate_with_version
from .parrec import PARRECImage
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you import this inside load? See the comment involved in importing Nifti* in save():

nibabel/nibabel/loadsave.py

Lines 109 to 112 in d5494f3

# Special-case Nifti singles and Pairs
# Inline imports, as this module really shouldn't reference any image type
from .nifti1 import Nifti1Image, Nifti1Pair
from .nifti2 import Nifti2Image, Nifti2Pair



def load(filename, **kwargs):
Expand Down Expand Up @@ -46,6 +47,15 @@ def load(filename, **kwargs):
for image_klass in all_image_classes:
is_valid, sniff = image_klass.path_maybe_image(filename, sniff)
if is_valid:
if image_klass is PARRECImage and '.REC' in filename:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if image_klass is PARRECImage and '.REC' in filename:
if image_klass is PARRECImage and filename.endswith('.REC'):

# a .REC file can have either a .PAR of .xml header.
# This skip case assumes PARRECImage is beforeXMLRECImage in
# all_image_classes.
par_exists = os.path.exists(filename.replace('.REC', '.PAR'))
xml_exists = os.path.exists(filename.replace('.REC', '.xml'))
if not par_exists and xml_exists:
continue # skip trying .PAR and proceed to .xml
print(image_klass)
img = image_klass.from_filename(filename, **kwargs)
return img

Expand Down
4 changes: 2 additions & 2 deletions nibabel/parrec.py
Original file line number Diff line number Diff line change
Expand Up @@ -865,7 +865,7 @@ def get_data_offset(self):
def set_data_offset(self, offset):
""" PAR header always has 0 data offset (into REC file) """
if offset != 0:
raise PARRECError("PAR header assumes offset 0")
raise PARRECError("header assumes offset 0")

def _calc_zooms(self):
"""Compute image zooms from header data.
Expand Down Expand Up @@ -894,7 +894,7 @@ def _calc_zooms(self):
# If 4D dynamic scan, convert time from milliseconds to seconds
if len(zooms) > 3 and self.general_info['dyn_scan']:
if len(self.general_info['repetition_time']) > 1:
warnings.warn("multiple TRs found in .PAR file")
warnings.warn("multiple TRs found in header file")
zooms[3] = self.general_info['repetition_time'][0] / 1000.
return zooms

Expand Down
Loading