Skip to content

Commit c462a6d

Browse files
committed
RF - nib-ls is now functioning without PyMVPA with improved robustness
1 parent 88fed18 commit c462a6d

File tree

2 files changed

+199
-91
lines changed

2 files changed

+199
-91
lines changed

COPYING

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ documentation is covered by the MIT license.
2424
Copyright (c) 2006-2010 Michael Hanke <[email protected]>
2525
Copyright (c) 2011 Christian Haselgrove <[email protected]>
2626
Copyright (c) 2010-2011 Jarrod Millman <[email protected]>
27+
Copyright (c) 2011-2012 Yaroslav Halchenko <[email protected]>
2728

2829
Permission is hereby granted, free of charge, to any person obtaining a copy
2930
of this software and associated documentation files (the "Software"), to deal

bin/nib-ls

Lines changed: 198 additions & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -1,120 +1,227 @@
11
#!/usr/bin/python
2-
#emacs: -*- mode: python-mode; py-indent-offset: 4; tab-width: 4; indent-tabs-mode: nil -*-
3-
#ex: set sts=4 ts=4 sw=4 noet:
2+
# emacs: -*- mode: python-mode; py-indent-offset: 4; indent-tabs-mode: nil -*-
3+
# vi: set ft=python sts=4 ts=4 sw=4 et:
4+
### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ##
5+
#
6+
# See COPYING file distributed along with the NiBabel package for the
7+
# copyright and license terms.
8+
#
9+
### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ##
410
"""
5-
Little script to list summary over given nifti files
6-
7-
COPYRIGHT: Yaroslav Halchenko 2011
8-
9-
LICENSE: MIT
10-
11-
Permission is hereby granted, free of charge, to any person obtaining a copy
12-
of this software and associated documentation files (the "Software"), to deal
13-
in the Software without restriction, including without limitation the rights
14-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
15-
copies of the Software, and to permit persons to whom the Software is
16-
furnished to do so, subject to the following conditions:
17-
18-
The above copyright notice and this permission notice shall be included in
19-
all copies or substantial portions of the Software.
20-
21-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
24-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
26-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
27-
THE SOFTWARE.
11+
Output a summary table for neuroimaging files (resolution, dimensionality, etc.)
2812
"""
29-
#-----------------\____________________________________/------------------
3013

3114
__author__ = 'Yaroslav Halchenko'
32-
__copyright__ = 'Copyright (c) 2011 Yaroslav Halchenko'
15+
__copyright__ = 'Copyright (c) 2011-2012 Yaroslav Halchenko ' \
16+
'and NiBabel contributors'
3317
__license__ = 'MIT'
3418

19+
import re
3520
import sys
36-
from mvpa2.base.dochelpers import table2string
37-
from mvpa2.base import verbose, debug
38-
from mvpa2.misc.cmdline import parser, opts, Option
21+
3922
import nibabel as nib
4023
import numpy as np
4124

25+
from math import ceil
26+
from optparse import OptionParser, Option
27+
from StringIO import StringIO
28+
29+
# global verbosity switch
30+
verbose_level = 0
31+
32+
def verbose(l, msg):
33+
"""Print `s` if `l` is less than the `verbose_level`
34+
"""
35+
# TODO: consider using nibabel's logger
36+
if l <= int(verbose_level):
37+
print "%s%s" % (' ' * l, msg)
38+
39+
def error(msg, exit_code):
40+
print >> sys.stderr, msg
41+
sys.exit(exit_code)
42+
43+
44+
45+
def table2string(table, out=None):
46+
"""Given list of lists figure out their common widths and print to out
47+
48+
Parameters
49+
----------
50+
table : list of lists of strings
51+
What is aimed to be printed
52+
out : None or stream
53+
Where to print. If None -- will print and return string
54+
55+
Returns
56+
-------
57+
string if out was None
58+
"""
59+
60+
print2string = out is None
61+
if print2string:
62+
out = StringIO()
63+
64+
# equalize number of elements in each row
65+
Nelements_max = len(table) \
66+
and max(len(x) for x in table)
67+
68+
for i, table_ in enumerate(table):
69+
table[i] += [''] * (Nelements_max - len(table_))
70+
71+
# figure out lengths within each column
72+
atable = np.asarray(table)
73+
# eat whole entry while computing width for @w (for wide)
74+
markup_strip = re.compile('^@([lrc]|w.*)')
75+
col_width = [ max( [len(markup_strip.sub('', x))
76+
for x in column] ) for column in atable.T ]
77+
string = ""
78+
for i, table_ in enumerate(table):
79+
string_ = ""
80+
for j, item in enumerate(table_):
81+
item = str(item)
82+
if item.startswith('@'):
83+
align = item[1]
84+
item = item[2:]
85+
if not align in ['l', 'r', 'c', 'w']:
86+
raise ValueError, 'Unknown alignment %s. Known are l,r,c' % align
87+
else:
88+
align = 'c'
4289

43-
def ap(l, format, sep=', '):
44-
"""Little helper to enforce consistency"""
45-
ls = [format % x for x in l]
46-
return sep.join(ls)
47-
48-
49-
parser.usage = """Usage: %s [options] [infile1] [infile2] ...""" % sys.argv[0]
50-
parser.option_groups = [opts.common]
90+
NspacesL = max(ceil((col_width[j] - len(item))/2.0), 0)
91+
NspacesR = max(col_width[j] - NspacesL - len(item), 0)
5192

52-
parser.add_options([
53-
Option("-s", "--stats",
54-
action="store_true", dest='stats', default=False,
55-
help="Output basic data statistics"),
56-
Option("-z", "--zeros",
57-
action="store_true", dest='stats_zeros', default=False,
58-
help="Include zeros into output basic data statistics (--stats)"),
59-
])
93+
if align in ['w', 'c']:
94+
pass
95+
elif align == 'l':
96+
NspacesL, NspacesR = 0, NspacesL + NspacesR
97+
elif align == 'r':
98+
NspacesL, NspacesR = NspacesL + NspacesR, 0
99+
else:
100+
raise RuntimeError, 'Should not get here with align=%s' % align
60101

61-
(options, files) = parser.parse_args()
102+
string_ += "%%%ds%%s%%%ds " \
103+
% (NspacesL, NspacesR) % ('', item, '')
104+
string += string_.rstrip() + '\n'
105+
out.write(string)
62106

63-
if verbose.level < 3:
64-
# suppress nibabel format-compliance warnings
65-
nib.imageglobals.logger.level = 50
107+
if print2string:
108+
value = out.getvalue()
109+
out.close()
110+
return value
66111

67-
if len(files):
68-
maxfnlen = max([len(f) for f in files])
112+
def ap(l, format, sep=', '):
113+
"""Little helper to enforce consistency"""
114+
if l == '-':
115+
return l
116+
ls = [format % x for x in l]
117+
return sep.join(ls)
69118

70-
t = []
71-
for f in files:
72-
row = ["%%-%ds" % maxfnlen % f]
73-
verbose(2, "Loading %s" % f)
74-
try:
75-
vol = nib.load(f)
76-
h = vol.get_header()
77-
except Exception, e:
78-
row += ['failed']
79-
t.append(row)
80-
verbose(2, "Failed to gather information due to %s" % str(e))
81-
continue
82-
row += [ str(h.get_data_dtype()),
83-
'@l[%s]' %ap(h.get_data_shape(), '%3g'),
84-
'@l%s' % ap(h.get_zooms(), '%.2f', 'x') ]
85-
# Slope
86-
if (h.has_data_slope or h.has_data_intercept) \
87-
and not h.get_slope_inter() in [(1.0, 0.0), (None, None)]:
88-
row += ['@l*%.3g+%.3g' % h.get_slope_inter()]
119+
def safe_get(obj, name):
120+
"""
121+
"""
122+
try:
123+
f = getattr(obj, 'get_' + name)
124+
return f()
125+
except Exception, e:
126+
verbose(2, "get_%s() failed -- %s" % (name, e))
127+
return '-'
128+
129+
def get_opt_parser():
130+
# use module docstring for help output
131+
p = OptionParser(
132+
usage="%s [OPTIONS] [FILE ...]\n\n" % sys.argv[0] + __doc__,
133+
version="%prog " + nib.__version__)
134+
135+
p.add_options([
136+
Option("-v", "--verbose", action="count",
137+
dest="verbose", default=0,
138+
help="Make more noise. Could be specified multiple times"),
139+
140+
Option("-s", "--stats",
141+
action="store_true", dest='stats', default=False,
142+
help="Output basic data statistics"),
143+
144+
Option("-z", "--zeros",
145+
action="store_true", dest='stats_zeros', default=False,
146+
help="Include zeros into output basic data statistics (--stats)"),
147+
])
148+
149+
return p
150+
151+
def proc_file(f, opts):
152+
verbose(1, "Loading %s" % f)
153+
154+
row = ["@l%s" % f]
155+
try:
156+
vol = nib.load(f)
157+
h = vol.get_header()
158+
except Exception, e:
159+
row += ['failed']
160+
verbose(2, "Failed to gather information -- %s" % str(e))
161+
return row
162+
163+
row += [ str(safe_get(h, 'data_dtype')),
164+
'@l[%s]' %ap(safe_get(h, 'data_shape'), '%3g'),
165+
'@l%s' % ap(safe_get(h, 'zooms'), '%.2f', 'x') ]
166+
# Slope
167+
if (hasattr(h, 'has_data_slope')
168+
and (h.has_data_slope or h.has_data_intercept)) \
169+
and not h.get_slope_inter() in [(1.0, 0.0), (None, None)]:
170+
row += ['@l*%.3g+%.3g' % h.get_slope_inter()]
171+
else:
172+
row += [ '' ]
173+
174+
if (hasattr(h, 'extensions') and len(h.extensions)):
175+
row += ['@l#exts: %d' % len(h.extensions)]
176+
else:
177+
row += [ '' ]
178+
179+
try:
180+
if (hasattr(h, 'get_qform') and hasattr(h, 'get_sform')
181+
and (h.get_qform() != h.get_sform()).any()):
182+
row += ['sform']
89183
else:
90-
row += [ '' ]
91-
92-
if (hasattr(h, 'extensions') and len(h.extensions)):
93-
row += ['@l#exts: %d' % len(h.extensions)]
184+
row += ['']
185+
except Exception, e:
186+
verbose(2, "Failed to obtain qform or sform -- %s" % str(e))
187+
if isinstance(h, nib.AnalyzeHeader):
188+
row += ['']
94189
else:
95-
row += [ '' ]
190+
row += ['error']
96191

192+
if opts.stats:
193+
# We are doomed to load data
97194
try:
98-
if (h.get_qform() != h.get_sform()).any():
99-
row += ['sform']
100-
else:
101-
row += ['']
102-
except Exception, e:
103-
verbose(2, "Failed to obtain qform or sform for %s due to %s" % (h, e))
104-
if isinstance(h, nib.AnalyzeHeader):
105-
row += ['']
106-
else:
107-
row += ['error']
108-
if options.stats:
109-
# We are doomed to load data
110195
d = vol.get_data()
111-
if not options.stats_zeros:
196+
if not opts.stats_zeros:
112197
d = d[np.nonzero(d)]
113198
# just # of elements
114199
row += ["[%d] " % np.prod(d.shape)]
115200
# stats
116201
row += [len(d) and '%.2g:%.2g' % (np.min(d), np.max(d)) or '-']
202+
except Exception, e:
203+
verbose(2, "Failed to obtain stats -- %s" % str(e))
204+
row += ['error']
205+
return row
206+
207+
208+
def main():
209+
"""Show must go on"""
210+
211+
parser = get_opt_parser()
212+
(opts, files) = parser.parse_args()
213+
214+
global verbose_level
215+
verbose_level = opts.verbose
216+
217+
if verbose_level < 3:
218+
# suppress nibabel format-compliance warnings
219+
nib.imageglobals.logger.level = 50
220+
221+
rows = [proc_file(f, opts) for f in files]
222+
223+
print(table2string(rows))
117224

118-
t.append(row)
119225

120-
print(table2string(t))
226+
if __name__ == '__main__':
227+
main()

0 commit comments

Comments
 (0)