/usr/share/pyshared/nifti/format.py is in python-nifti 0.20100607.1-4.
This file is owned by root:root, with mode 0o644.
The actual contents of the file can be viewed below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 | #emacs: -*- mode: python-mode; py-indent-offset: 4; indent-tabs-mode: nil -*-
#ex: set sts=4 ts=4 sw=4 et:
### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ##
#
# See COPYING file distributed along with the PyNIfTI package for the
# copyright and license terms.
#
### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ##
"""This modules provides a class representation of a NIfTI image header. The
interface provides pythonic access to NIfTI properties using Python datatypes.
"""
__docformat__ = 'restructuredtext'
import cPickle
from warnings import warn
import numpy as N
# the NIfTI pieces
import nifti.clib as ncl
from nifti.extensions import NiftiExtensions
from nifti.utils import nhdr2dict, updateNiftiHeaderFromDict, \
Ndtype2niftidtype, nifti_xform_map, nifti_xform_inv_map, nifti_units_map, \
_checkUnit, valid_xyz_unit_codes, valid_time_unit_codes, \
nifti2numpy_dtype_map
class NiftiFormat(object):
"""NIfTI header representation.
NIfTI header can be created by loading information from an existing NIfTI
file or by creating a matching NIfTI header for a ndarray.
In addition, a number of methods to manipulate the header information are
provided. However, this class is not able to write a NIfTI header back to
disk. Please refer to the NIfTIImage class for this functionality.
.. note::
Handling of NIfTI header extensions is provided by the
:class:`~nifti.extensions.NiftiExtensions` class (see its documentation
for more information). Access to an instance of this class is available
through the `NiftiFormat.extensions` attribute.
"""
#
# object constructors, destructors and generic Python interface
#
def __init__(self, source, header=None, loadmeta=False):
"""
The constructor decides whether to load a nifti image header from file
or create one from ndarray data, depending on the datatype of `source`.
:Parameters:
source: str | ndarray
If source is a string, it is assumed to be a filename and an
attempt will be made to open the corresponding NIfTI file.
Filenames might be provided as unicode strings. However, as the
underlying library does not support unicode, they must be
ascii-encodable, i.e. must not contain pure unicode characters.
In case of an ndarray the array data will be used for the to be
created nifti image and a matching nifti header is generated.
If an object of a different type is supplied as 'source' a
ValueError exception will be thrown.
header: dict
Additional header data might be supplied if image data is not loaded
from a file. However, dimensionality and datatype are determined
from the ndarray and not taken from a header dictionary.
"""
self.__nimg = None
# prepare empty meta dict
self.meta = {}
# placeholder for extensions interface
self.extensions = None
if type(source) == N.ndarray:
self.__newFromArray(source, header)
elif type(source) in (str, unicode):
self.__newFromFile(source, loadmeta)
else:
raise ValueError, \
"Unsupported source type. Only NumPy arrays and filename " \
+ "string are supported."
def __newFromArray(self, data, hdr=None):
"""Create a `nifti_image` struct from a ndarray.
:Parameters:
data: ndarray
Source ndarray.
hdr: dict
Optional dictionary with NIfTI header data.
.. warning::
This is an internal method. Neither its availability nor its API is
guarenteed.
"""
if hdr == None:
hdr = {}
# check array
if len(data.shape) > 7:
raise ValueError, \
"NIfTI does not support data with more than 7 dimensions."
# create template nifti header struct
niptr = ncl.nifti_simple_init_nim()
nhdr = ncl.nifti_convert_nim2nhdr(niptr)
# intermediate cleanup
ncl.nifti_image_free(niptr)
# convert virgin nifti header to dict to merge properties
# with supplied information and array properties
hdic = nhdr2dict(nhdr)
# copy data from supplied header dict
for k, v in hdr.iteritems():
hdic[k] = v
# finally set header data that is determined by the data array
# convert NumPy to nifti datatype
hdic['datatype'] = Ndtype2niftidtype(data)
# make sure there are no zeros in the dim vector
# especially not in #4 as FSLView doesn't like that
hdic['dim'] = [ 1 for i in hdic['dim'] ]
# set number of dims
hdic['dim'][0] = len(data.shape)
# set size of each dim (and reverse the order to match nifti format
# requirements)
for i, s in enumerate(data.shape):
hdic['dim'][len(data.shape)-i] = s
# set magic field to mark as nifti file
hdic['magic'] = 'n+1'
self._rebuildNimgFromHdrAndDict(nhdr, hdic)
def __newFromFile(self, filename, loadmeta):
"""Open a NIfTI file.
:Parameters:
filename: str
Filename of the to be opened image file.
.. warning::
This is an internal method. Neither its availability nor its API is
guarenteed.
"""
# make sure filename is not unicode
try:
filename = str(filename)
except UnicodeEncodeError:
raise UnicodeError, \
"The filename must not contain unicode characters, since " \
"the NIfTI library cannot handle them."
# do not load image data!
self.__nimg = ncl.nifti_image_read(filename, 0)
if not self.__nimg:
raise RuntimeError, "Error while opening NIfTI file '%s'." % \
filename
# simply create extension interface since nifticlib took care of
# loading all extensions already
self.extensions = NiftiExtensions(self.raw_nimg)
# unpickle meta data if present
if loadmeta and 'pypickle' in self.extensions:
if self.extensions.count('pypickle') > 1:
warn("Handling more than one 'pypickle' extension is not "
"supported. Will continue using the first detected "
"extension.")
# unpickle meta data
self.meta = cPickle.loads(self.extensions['pypickle'])
# and remove the pickle extension to not confuse data integrity when
# users would add something to it manually, i.e. via
# self.extensions['pypickle']
self._removePickleExtension()
def __del__(self):
# enforce del on extensions wrapper so Python GC doesn't try
# to free it up later on causing writes to freed memory.
del self.extensions
if self.__nimg:
ncl.nifti_image_free(self.__nimg)
def __str__(self):
lines = []
lines.append('extent' + str(self.extent))
lines.append('dtype(' \
+ nifti2numpy_dtype_map[self.raw_nimg.datatype] \
+ ')')
s = 'voxels('
s += 'x'.join(["%f" % d for d in self.voxdim])
if self.xyz_unit:
s += ' ' + self.getXYZUnit(as_string=True)
lines.append(s + ')')
if self.timepoints > 1:
s = "timepoints(%i, dt=%f" % (self.timepoints, self.rtime)
if self.time_unit:
s += ' ' + self.getTimeUnit(as_string=True)
s += ')'
lines.append(s)
if self.slope:
lines.append("scaling(slope=%f, intercept=%f)" \
% (self.slope, self.intercept))
if self.qform_code:
lines.append("qform(%s)" % self.getQFormCode(as_string=True))
lines.append("qform_orientation(%s)" \
% ', '.join(self.getQOrientation(as_string=True)))
if self.sform_code:
lines.append("sform(%s)" % self.getSFormCode(as_string=True))
lines.append("sform_orientation(%s)" \
% ', '.join(self.getSOrientation(as_string=True)))
if self.description:
lines.append("descr('%s')" % self.description)
if len(self.meta.keys()):
lines.append("meta(%s)" % str(self.meta.keys()))
return '<NIfTI:\n ' + ';\n '.join(lines) + ';\n>'
#
# converters
#
def asDict(self):
"""Returns the header data of the `NiftiImage` in a dictionary.
:Returns:
dict
The dictionary contains all NIfTI header information. Additionally,
it might also contain a special 'meta' item that contains the
meta data currently assigned to this instance.
.. note::
Modifications done to the returned dictionary do not cause any
modifications in the NIfTI image itself. Please use
:meth:`~nifti.format.NiftiFormat.updateFromDict` to apply
changes to the image.
.. seealso::
:meth:`~nifti.format.NiftiFormat.updateFromDict`,
:attr:`~nifti.format.NiftiFormat.header`
"""
# Convert nifti_image struct into nifti1 header struct.
# This get us all data that will actually make it into a
# NIfTI file.
nhdr = ncl.nifti_convert_nim2nhdr(self.raw_nimg)
# pass extensions as well
ret = nhdr2dict(nhdr, extensions=self.extensions)
if len(self.meta.keys()):
ret['meta'] = self.meta
return ret
def updateFromDict(self, hdrdict):
"""Update NIfTI header information.
Updated header data is read from the supplied dictionary. One cannot
modify dimensionality and datatype of the image data. If such
information is present in the header dictionary it is removed before
the update. If resizing or datatype casting are required one has to
convert the image data into a separate array and perform resize and
data manipulations on this array. When finished, the array can be
converted into a nifti file by calling the NiftiImage constructor with
the modified array as 'source' and the nifti header of the original
NiftiImage object as 'header'.
.. note::
If the provided dictionary contains a 'meta' item its content is
used to overwrite any potentially existing meta data.
dictionary.
The same behavior will be used for 'extensions'. If extensions
are defined in the provided dictionary all currently existing
extensions will be overwritten.
.. seealso::
:meth:`~nifti.format.NiftiFormat.asDict`,
:attr:`~nifti.format.NiftiFormat.header`
"""
# rebuild nifti header from current image struct
nhdr = ncl.nifti_convert_nim2nhdr(self.__nimg)
# remove settings from the hdrdict that are determined by
# the data set and must not be modified to preserve data integrity
if hdrdict.has_key('datatype'):
del hdrdict['datatype']
if hdrdict.has_key('dim'):
del hdrdict['dim']
self._rebuildNimgFromHdrAndDict(nhdr, hdrdict)
def vx2q(self, coord):
"""Transform a voxel's index into coordinates (qform-defined).
:Parameter:
coord: 3-tuple
A voxel's index in the volume fiven as three positive integers
(i, j, k).
:Returns:
vector
.. seealso::
:meth:`~nifti.format.NiftiFormat.setQForm`,
:meth:`~nifti.format.NiftiFormat.getQForm`
:attr:`~nifti.format.NiftiFormat.qform`
"""
# add dummy one to row vector
coord_ = N.r_[coord, [1.0]]
# apply affine transformation
result = N.dot(self.qform, coord_)
# return 3D coordinates
return result[0:-1]
def vx2s(self, coord):
"""Transform a voxel's index into coordinates (sform-defined).
:Parameter:
coord: 3-tuple
A voxel's index in the volume fiven as three positive integers
(i, j, k).
:Returns:
vector
.. seealso::
:meth:`~nifti.format.NiftiFormat.setSForm`,
:meth:`~nifti.format.NiftiFormat.getSForm`
:attr:`~nifti.format.NiftiFormat.sform`
"""
# add dummy one to row vector
coord_ = N.r_[coord, [1.0]]
# apply affine transformation
result = N.dot(self.sform, coord_)
# return 3D coordinates
return result[0:-1]
#
# private helpers
#
def _updateNimgFromArray(self, val):
"""Update all relevant items in the nimg struct to match a given
array's properties.
We can only savely modify the respective nimg items since the data
array is disconnected from the struct all the time.
.. warning::
This is an internal method. Neither its availability nor its API is
guarenteed.
"""
# convert dtype and store in struct
self.raw_nimg.datatype = Ndtype2niftidtype(val)
# wrap dims
dim = ncl.intArray_frompointer(self.raw_nimg.dim)
# make sure there are no zeros in the dim vector
# especially not in #4 as FSLView doesn't like that
target_dim = N.ones(7, dtype='int')
# reverse the array shape
target_dim[:len(val.shape)] = val.shape[::-1]
# set number of dims
dim[0] = len(val.shape)
# assign remaining dim vector
for i in range(7):
# cast to prevent type issues with the IntArray swig container
dim[i+1] = int(target_dim[i])
# expand dim vector
self.raw_nimg.ndim = dim[0]
self.raw_nimg.nx = dim[1]
self.raw_nimg.ny = dim[2]
self.raw_nimg.nz = dim[3]
self.raw_nimg.nt = dim[4]
self.raw_nimg.nu = dim[5]
self.raw_nimg.nv = dim[6]
self.raw_nimg.nw = dim[7]
def _rebuildNimgFromHdrAndDict(self, nhdr, hdic):
"""
.. warning::
This is an internal method. Neither its availability nor its API is
guarenteed.
"""
# first updated the header struct from the provided dictionary
# data
updateNiftiHeaderFromDict(nhdr, hdic)
# if no filename was set already (e.g. image from array) set a temp
# name now, as otherwise nifti_convert_nhdr2nim will fail
have_temp_filename = False
if not self.__nimg:
# we are creating from scratch
new_nimg = ncl.nifti_convert_nhdr2nim(nhdr, 'pynifti_none')
have_temp_filename = True
elif not self.filename:
# rebuild but no filename yet
self.__nimg.fname = 'pynifti_updateheader_temp_name'
self.__nimg.iname = 'pynifti_updateheader_temp_name'
new_nimg = ncl.nifti_convert_nhdr2nim(nhdr, 'pynifti_none')
have_temp_filename = True
else:
# recreate nifti image struct using current filename
new_nimg = ncl.nifti_convert_nhdr2nim(nhdr, self.filename)
if not new_nimg:
raise RuntimeError, \
"Could not create NIfTI image struct from header."
# rescue all extensions
if self.extensions:
# need to create a new extensions wrapper around the new
# nifti image struct
new_ext = NiftiExtensions(
new_nimg,
[ext for ext in self.extensions.iteritems()])
else:
# just create an empty wrapper
new_ext = NiftiExtensions(new_nimg)
# replace old image struct by new one
# be careful with memory leak (still not checked whether successful)
# assign the new image struct
self.__nimg = new_nimg
# and the extensions
self.extensions = new_ext
# reset filename if temp name was set
if have_temp_filename:
self.__nimg.fname = ''
self.__nimg.iname = ''
#
# merge new extensions
#
if hdic.has_key('extensions'):
# wipe current set of extensions
self.extensions.clear()
for e in hdic['extensions']:
self.extensions.append(e)
#
# assign new meta data
if hdic.has_key('meta'):
self.meta = hdic['meta']
def _removePickleExtension(self):
"""Remove the 'pypickle' extension from the raw NIfTI image struct.
Its content is expanded into the `meta` attribute in a NiftiImage
instance.
.. warning::
This is an internal method. Neither its availability nor its API is
guarenteed.
"""
if 'pypickle' in self.extensions:
del self.extensions['pypickle']
def updateQFormFromQuaternion(self):
"""Only here for backward compatibility."""
from warnings import warn
warn("The method has been renamed to " \
"NiftiFormat.__updateQFormFromQuaternion and should not be used " \
"in user code. This redirect will be removed with PyNIfTI 1.0.", \
DeprecationWarning)
self.__updateQFormFromQuaternion()
def __updateQFormFromQuaternion(self):
"""Recalculates the qform matrix (and the inverse) from the quaternion
representation.
.. warning::
This is an internal method. Neither its availability nor its API is
guarenteed.
"""
# recalculate qform
self.__nimg.qto_xyz = ncl.nifti_quatern_to_mat44 (
self.__nimg.quatern_b, self.__nimg.quatern_c, self.__nimg.quatern_d,
self.__nimg.qoffset_x, self.__nimg.qoffset_y, self.__nimg.qoffset_z,
self.__nimg.dx, self.__nimg.dy, self.__nimg.dz,
self.__nimg.qfac )
# recalculate inverse
self.__nimg.qto_ijk = \
ncl.nifti_mat44_inverse( self.__nimg.qto_xyz )
#
# getters and setters
#
def getVoxDims(self):
"""Returns a 3-tuple a voxel dimensions/size in (x,y,z).
.. seealso::
:meth:`~nifti.format.NiftiFormat.setVoxDims`,
:attr:`~nifti.format.NiftiFormat.voxdim`
"""
return (self.__nimg.dx, self.__nimg.dy, self.__nimg.dz)
def setVoxDims(self, value):
"""Set voxel dimensions/size.
The qform matrix and its inverse will be recalculated automatically.
:Parameter:
value: 3-tuple of floats
Have to be given in (x,y,z) order.
.. seealso::
:meth:`~nifti.format.NiftiFormat.getVoxDims`,
:attr:`~nifti.format.NiftiFormat.voxdim`
"""
if len(value) != 3:
raise ValueError, 'Requires 3-tuple.'
self.__nimg.dx = float(value[0])
self.__nimg.dy = float(value[1])
self.__nimg.dz = float(value[2])
self.__updateQFormFromQuaternion()
def setPixDims(self, value):
"""Set the pixel dimensions.
:Parameter:
value: sequence
Up to 7 values (max. number of dimensions supported by the
NIfTI format) are allowed in the sequence.
The supplied sequence can be shorter than seven elements. In
this case only present values are assigned starting with the
first dimension (spatial: x).
.. note::
Calling `setPixDims()` with a length-3 sequence equals calling
`setVoxDims()`.
.. seealso::
:meth:`~nifti.format.NiftiFormat.setVoxDims`,
:meth:`~nifti.format.NiftiFormat.getPixDims`,
:attr:`~nifti.format.NiftiFormat.pixdim`
"""
if len(value) > 7:
raise ValueError, \
'The Nifti format does not support more than 7 dimensions.'
pixdim = ncl.floatArray_frompointer( self.__nimg.pixdim )
for i, val in enumerate(value):
pixdim[i+1] = float(val)
# The nifticlib uses dimension deltas (dx, dy, dz, dt...) to store
# the pixdim values (in addition to the pixdim array). When
# saving the image to a file, the deltas are used, not the pixdims.
# The nifti_update_dims_from_array sync's the deltas with the pixdims.
# (It also syncs the dim array with it's duplicate scalar variables.)
ncl.nifti_update_dims_from_array(self.__nimg)
def getPixDims(self):
"""Returns the pixel dimensions on all 7 dimensions.
The function is similar to `getVoxDims()`, but instead of the 3d
spatial dimensions of a voxel it returns the dimensions of an image
pixel on all 7 dimensions supported by the NIfTI dataformat.
.. seealso::
:meth:`~nifti.format.NiftiFormat.getVoxDims`,
:meth:`~nifti.format.NiftiFormat.setPixDims`,
:attr:`~nifti.format.NiftiFormat.pixdim`
"""
return \
tuple([ ncl.floatArray_frompointer(self.__nimg.pixdim)[i]
for i in range(1,8) ] )
def getExtent(self):
"""Returns the shape of the dataimage.
:Returns:
tuple: Tuple with the size in voxel/timepoints.
The order of dimensions is (x,y,z,t,u,v,w). If the image has less
dimensions than 7 the return tuple will be shortened accordingly.
Please note that the order of dimensions is different from the tuple
returned by calling `NiftiImage.data.shape`!
.. seealso::
:meth:`~nifti.format.NiftiFormat.getVolumeExtent`,
:meth:`~nifti.format.NiftiFormat.getTimepoints`,
:attr:`~nifti.format.NiftiFormat.extent`
"""
# wrap dim array in nifti image struct
dims_array = ncl.intArray_frompointer(self.__nimg.dim)
dims = [ dims_array[i] for i in range(8) ]
return tuple( dims[1:dims[0]+1] )
def getVolumeExtent(self):
"""Returns the size/shape of the volume(s) in the image as a tuple.
:Returns:
tuple:
Either a 3-tuple or 2-tuple or 1-tuple depending on the available
dimensions in the image.
The order of dimensions in the tuple is (x [, y [, z ] ] ).
.. seealso::
:meth:`~nifti.format.NiftiFormat.getExtent`,
:attr:`~nifti.format.NiftiFormat.volextent`
"""
# it is save to do this even if self.extent is shorter than 4 items
return self.extent[:3]
def getTimepoints(self):
"""Returns the number of timepoints in the image.
In case of a 3d (or less dimension) image this method returns 1.
.. seealso::
:attr:`~nifti.format.NiftiFormat.timepoints`
"""
if len(self.extent) < 4:
return 1
else:
return self.extent[3]
def getRepetitionTime(self):
"""Returns the temporal distance between the volumes in a timeseries.
.. seealso::
:meth:`~nifti.format.NiftiFormat.setRepetitionTime`,
:attr:`~nifti.format.NiftiFormat.rtime`
"""
return self.__nimg.dt
def setRepetitionTime(self, value):
"""Set the repetition time of a NIfTI image (dt).
.. seealso::
:meth:`~nifti.format.NiftiFormat.getRepetitionTime`,
:attr:`~nifti.format.NiftiFormat.rtime`
"""
self.__nimg.dt = float(value)
def setSlope(self, value):
"""Set the slope attribute in the NIfTI header.
Setting the slope to zero, will disable scaling.
.. seealso::
:attr:`~nifti.format.NiftiFormat.slope`,
:attr:`~nifti.format.NiftiFormat.intercept`
"""
self.__nimg.scl_slope = float(value)
def setIntercept(self, value):
"""Set the intercept attribute in the NIfTI header.
The intercept is only considered for scaling in case of a non-zero
slope value.
.. seealso::
:attr:`~nifti.format.NiftiFormat.slope`,
:attr:`~nifti.format.NiftiFormat.intercept`
"""
self.__nimg.scl_inter = float(value)
def setDescription(self, value):
"""Set the description element in the NIfTI header.
:Parameter:
value: str
Description -- must not be longer than 79 characters.
.. seealso::
:attr:`~nifti.format.NiftiFormat.description`
"""
if len(value) > 79:
raise ValueError, \
"The NIfTI format only supports descriptions shorter than " \
"80 chars. (got length %i)" % len(value)
self.__nimg.descrip = value
def setXFormCode(self, xform, code):
"""Set the type of space described by the NIfTI transformations.
The NIfTI format defines five coordinate system types which are used
to describe the target space of a transformation (qform or sform).
Please note, that the last four transformation types are only available
in the NIfTI format and not when saving into ANALYZE.
'unkown', `NIFTI_XFORM_UNKNOWN`, 0:
Transformation is arbitrary. This is the ANALYZE compatibility
mode. In this case no *sform* matrix will be written, even when
stored in NIfTI and not in ANALYZE format. Additionally, only the
pixdim parts of the *qform* matrix will be saved (upper-left 3x3).
'scanner', `NIFTI_XFORM_SCANNER_ANAT`, 1:
Scanner-based anatomical coordinates.
'aligned', `NIFTI_XFORM_ALIGNED_ANAT`, 2:
Coordinates are aligned to another file's coordinate system.
'talairach', `NIFTI_XFORM_TALAIRACH`, 3:
Coordinate system is shifted to have its origin (0,0,0) at the
anterior commissure, as in the Talairach-Tournoux Atlas.
'mni152', `NIFTI_XFORM_MNI_152`, 4:
Coordinates are in MNI152 space.
:Parameters:
xform: 'qform' | 'q' | 'sform' | 's'
Which of the two NIfTI transformations to set.
code: str | `NIFTI_XFORM_CODE` | int (0..4)
The Transformation code can be specified either by a string, the
`NIFTI_XFORM_CODE` defined in the nifti1.h header file (accessible
via the `nifti.clib` module, or the corresponding integer value.
.. seealso::
:meth:`~nifti.format.NiftiFormat.setQFormCode`,
:meth:`~nifti.format.NiftiFormat.getQFormCode`,
:meth:`~nifti.format.NiftiFormat.setSFormCode`,
:meth:`~nifti.format.NiftiFormat.getSFormCode`,
:attr:`~nifti.format.NiftiFormat.qform_code`,
:attr:`~nifti.format.NiftiFormat.sform_code`
"""
if isinstance(code, str):
if not code in nifti_xform_map.keys():
raise ValueError, \
"Unknown xform code '%s'. Must be one of '%s'" \
% (code, str(nifti_xform_map.keys()))
code = nifti_xform_map[code]
else:
if not code in nifti_xform_map.values():
raise ValueError, \
"Unknown xform code '%s'. Must be one of '%s'" \
% (str(code), str(nifti_xform_map.values()))
if xform == 'qform' or xform == 'q':
self.raw_nimg.qform_code = code
elif xform == 'sform' or xform == 's':
self.raw_nimg.sform_code = code
else:
raise ValueError, "Unkown transformation '%s'" % xform
def setQFormCode(self, code):
"""Set the qform code.
.. note::
This is a convenience frontend for
:meth:`~nifti.format.NiftiFormat.setXFormCode`. Please see its
documentation for more information.
"""
self.setXFormCode('qform', code)
def getQFormCode(self, as_string = False):
"""Return the qform code.
By default NIfTI xform codes are returned, but if `as_string` is set to
true a string representation ala 'talairach' is returned instead.
.. seealso::
:meth:`~nifti.format.NiftiFormat.getQFormCode`,
:attr:`~nifti.format.NiftiFormat.qform_code`
"""
code = self.raw_nimg.qform_code
if as_string:
code = nifti_xform_inv_map[code]
return code
def getSFormCode(self, as_string = False):
"""Return the sform code.
By default NIfTI xform codes are returned, but if `as_string` is set to
true a string representation ala 'talairach' is returned instead.
.. seealso::
:meth:`~nifti.format.NiftiFormat.getSFormCode`,
:attr:`~nifti.format.NiftiFormat.sform_code`
"""
code = self.raw_nimg.sform_code
if as_string:
code = nifti_xform_inv_map[code]
return code
def setSFormCode(self, code):
"""Set the sform code.
.. note::
This is a convenience frontend for
:meth:`~nifti.format.NiftiFormat.setXFormCode`. Please see its
documentation for more information.
"""
self.setXFormCode('sform', code)
def getSForm(self):
"""Returns the sform matrix.
.. note::
The returned sform matrix is not bound to the object. Therefore it
cannot be successfully modified in-place. Modifications to the sform
matrix can only be done by setting a new sform matrix
.. seealso::
:meth:`~nifti.format.NiftiFormat.setSForm`,
:meth:`~nifti.format.NiftiFormat.setSFormCode`,
:meth:`~nifti.format.NiftiFormat.getSFormCode`,
:attr:`~nifti.format.NiftiFormat.sform`,
:attr:`~nifti.format.NiftiFormat.sform_inv`,
:attr:`~nifti.format.NiftiFormat.sform_code`
"""
return ncl.mat442array(self.__nimg.sto_xyz)
def setSForm(self, m, code='mni152'):
"""Sets the sform matrix.
The supplied value has to be a 4x4 matrix. The matrix elements will be
converted to floats. By definition the last row of the sform matrix has
to be (0,0,0,1). However, different values can be assigned, but will
not be stored when the NIfTI image is saved to a file.
The inverse sform matrix will be automatically recalculated.
:Parameters:
m: 4x4 ndarray
The sform matrix.
code: str | `NIFTI_XFORM_CODE` | int (0..4)
The type of the coordinate system the sform matrix is describing.
By default this coordinate system is assumed to be the MNI152
space. Please refer to the
:meth:`~nifti.format.NiftiFormat.setXFormCode` method for a
full list of possible codes and their meaning.
.. seealso::
:meth:`~nifti.format.NiftiFormat.getSForm`,
:meth:`~nifti.format.NiftiFormat.setSFormCode`,
:meth:`~nifti.format.NiftiFormat.getSFormCode`,
:attr:`~nifti.format.NiftiFormat.sform`,
:attr:`~nifti.format.NiftiFormat.sform_code`
"""
if m.shape != (4, 4):
raise ValueError, "SForm matrix has to be of size 4x4."
# make sure it is float
m = m.astype('float')
ncl.set_mat44( self.__nimg.sto_xyz,
m[0,0], m[0,1], m[0,2], m[0,3],
m[1,0], m[1,1], m[1,2], m[1,3],
m[2,0], m[2,1], m[2,2], m[2,3],
m[3,0], m[3,1], m[3,2], m[3,3] )
# recalculate inverse
self.__nimg.sto_ijk = \
ncl.nifti_mat44_inverse( self.__nimg.sto_xyz )
# set sform code, which decides how the sform matrix is interpreted
self.setXFormCode('sform', code)
def getInverseSForm(self):
"""Returns the inverse sform matrix.
.. note::
The inverse sform matrix cannot be modified in-place. One needs to
set a new sform matrix instead. The corresponding inverse matrix is
then re-calculated automatically.
.. seealso::
:meth:`~nifti.format.NiftiFormat.getSForm`,
:attr:`~nifti.format.NiftiFormat.sform`,
:attr:`~nifti.format.NiftiFormat.sform_inv`,
"""
return ncl.mat442array(self.__nimg.sto_ijk)
def getQForm(self):
"""Returns the qform matrix.
.. note::
The returned qform matrix is not bound to the object. Therefore it
cannot be successfully modified in-place. Modifications to the qform
matrix can only be done by setting a new qform matrix
.. seealso::
:meth:`~nifti.format.NiftiFormat.setQForm`,
:meth:`~nifti.format.NiftiFormat.setQFormCode`,
:meth:`~nifti.format.NiftiFormat.getQFormCode`,
:meth:`~nifti.format.NiftiFormat.getQuaternion`,
:meth:`~nifti.format.NiftiFormat.getQOffset`,
:meth:`~nifti.format.NiftiFormat.setQuaternion`,
:meth:`~nifti.format.NiftiFormat.setQOffset`,
:meth:`~nifti.format.NiftiFormat.setQFac`,
:attr:`~nifti.format.NiftiFormat.qform`,
:attr:`~nifti.format.NiftiFormat.qform_inv`,
:attr:`~nifti.format.NiftiFormat.qform_code`,
:attr:`~nifti.format.NiftiFormat.quatern`,
:attr:`~nifti.format.NiftiFormat.qoffset`,
:attr:`~nifti.format.NiftiFormat.qfac`
"""
return ncl.mat442array(self.__nimg.qto_xyz)
def getInverseQForm(self):
"""Returns the inverse qform matrix.
.. note::
The inverse qform matrix cannot be modified in-place. One needs to
set a new qform matrix instead. The corresponding inverse matrix is
then re-calculated automatically.
.. seealso::
:meth:`~nifti.format.NiftiFormat.getQForm`,
:attr:`~nifti.format.NiftiFormat.qform`,
:attr:`~nifti.format.NiftiFormat.qform_inv`,
"""
return ncl.mat442array(self.__nimg.qto_ijk)
def setQForm(self, m, code='scanner'):
"""Sets the qform matrix.
The supplied value has to be a 4x4 matrix. The matrix will be converted
to float.
The inverse qform matrix and the quaternion representation will be
automatically recalculated.
:Parameters:
m: 4x4 ndarray
The qform matrix.
code: str | `NIFTI_XFORM_CODE` | int (0..4)
The type of the coordinate system the qform matrix is describing.
By default this coordinate system is assumed to be the scanner
space. Please refer to the
:meth:`~nifti.format.NiftiFormat.setXFormCode` method for a
full list of possible codes and their meaning.
.. seealso::
:meth:`~nifti.format.NiftiFormat.getQForm`,
:meth:`~nifti.format.NiftiFormat.setQFormCode`,
:meth:`~nifti.format.NiftiFormat.getQFormCode`,
:meth:`~nifti.format.NiftiFormat.getQuaternion`,
:meth:`~nifti.format.NiftiFormat.getQOffset`,
:meth:`~nifti.format.NiftiFormat.setQuaternion`,
:meth:`~nifti.format.NiftiFormat.setQOffset`,
:meth:`~nifti.format.NiftiFormat.setQFac`,
:attr:`~nifti.format.NiftiFormat.qform`,
:attr:`~nifti.format.NiftiFormat.qform_inv`,
:attr:`~nifti.format.NiftiFormat.qform_code`,
:attr:`~nifti.format.NiftiFormat.quatern`,
:attr:`~nifti.format.NiftiFormat.qoffset`,
:attr:`~nifti.format.NiftiFormat.qfac`
"""
if m.shape != (4, 4):
raise ValueError, "QForm matrix has to be of size 4x4."
# make sure it is float
m = m.astype('float')
ncl.set_mat44( self.__nimg.qto_xyz,
m[0,0], m[0,1], m[0,2], m[0,3],
m[1,0], m[1,1], m[1,2], m[1,3],
m[2,0], m[2,1], m[2,2], m[2,3],
m[3,0], m[3,1], m[3,2], m[3,3] )
# recalculate inverse
self.__nimg.qto_ijk = \
ncl.nifti_mat44_inverse( self.__nimg.qto_xyz )
# update quaternions
( self.__nimg.quatern_b, self.__nimg.quatern_c, self.__nimg.quatern_d,
self.__nimg.qoffset_x, self.__nimg.qoffset_y, self.__nimg.qoffset_z,
self.__nimg.dx, self.__nimg.dy, self.__nimg.dz,
self.__nimg.qfac ) = \
ncl.nifti_mat44_to_quatern( self.__nimg.qto_xyz )
# set qform code, which decides how the qform matrix is interpreted
self.setXFormCode('qform', code)
def setQuaternion(self, value, code='scanner'):
"""Set Quaternion from 3-tuple (qb, qc, qd).
The qform matrix and it's inverse are re-computed automatically.
:Parameters:
value: length-3 sequence
qb, qc and qd quaternions.
code: str | `NIFTI_XFORM_CODE` | int (0..4)
The type of the coordinate system the corresponding qform matrix is
describing. By default this coordinate system is assumed to be the
scanner space. Please refer to the
:meth:`~nifti.format.NiftiFormat.setXFormCode` method for a
full list of possible codes and their meaning.
.. seealso::
:meth:`~nifti.format.NiftiFormat.getQForm`,
:meth:`~nifti.format.NiftiFormat.setQForm`,
:meth:`~nifti.format.NiftiFormat.setQFormCode`,
:meth:`~nifti.format.NiftiFormat.getQFormCode`,
:meth:`~nifti.format.NiftiFormat.getQuaternion`,
:meth:`~nifti.format.NiftiFormat.getQOffset`,
:meth:`~nifti.format.NiftiFormat.setQOffset`,
:meth:`~nifti.format.NiftiFormat.setQFac`,
:attr:`~nifti.format.NiftiFormat.qform`,
:attr:`~nifti.format.NiftiFormat.qform_inv`,
:attr:`~nifti.format.NiftiFormat.qform_code`,
:attr:`~nifti.format.NiftiFormat.quatern`,
:attr:`~nifti.format.NiftiFormat.qoffset`,
:attr:`~nifti.format.NiftiFormat.qfac`
"""
if len(value) != 3:
raise ValueError, 'Requires 3-tuple.'
self.__nimg.quatern_b = float(value[0])
self.__nimg.quatern_c = float(value[1])
self.__nimg.quatern_d = float(value[2])
self.__updateQFormFromQuaternion()
self.setXFormCode('qform', code)
def getQuaternion(self):
"""Returns a 3-tuple containing (qb, qc, qd).
.. seealso::
:meth:`~nifti.format.NiftiFormat.setQuaternion`,
:attr:`~nifti.format.NiftiFormat.qform`,
:attr:`~nifti.format.NiftiFormat.quatern`
"""
return( ( self.__nimg.quatern_b,
self.__nimg.quatern_c,
self.__nimg.quatern_d ) )
def setQOffset(self, value, code='scanner'):
"""Set QOffset from 3-tuple (qx, qy, qz).
The qform matrix and its inverse are re-computed automatically.
Besides reading it is also possible to set the qoffset by assigning
to the `qoffset` property.
:Parameters:
value: length-3 sequence
qx, qy and qz offsets.
code: str | `NIFTI_XFORM_CODE` | int (0..4)
The type of the coordinate system the corresponding qform matrix is
describing. By default this coordinate system is assumed to be the
scanner space. Please refer to the
:meth:`~nifti.format.NiftiFormat.setXFormCode` method for a
full list of possible codes and their meaning.
.. seealso::
:meth:`~nifti.format.NiftiFormat.getQOffset`,
:attr:`~nifti.format.NiftiFormat.qform`,
:attr:`~nifti.format.NiftiFormat.qoffset`
"""
if len(value) != 3:
raise ValueError, 'Requires 3-tuple.'
self.__nimg.qoffset_x = float(value[0])
self.__nimg.qoffset_y = float(value[1])
self.__nimg.qoffset_z = float(value[2])
self.__updateQFormFromQuaternion()
self.setXFormCode('qform', code)
def getQOffset(self):
"""Returns a 3-tuple containing (qx, qy, qz).
.. seealso::
:meth:`~nifti.format.NiftiFormat.setQOffset`,
:attr:`~nifti.format.NiftiFormat.qform`,
:attr:`~nifti.format.NiftiFormat.qoffset`
"""
return( ( self.__nimg.qoffset_x,
self.__nimg.qoffset_y,
self.__nimg.qoffset_z ) )
def setQFac(self, value, code='scanner'):
"""Set qfac (scaling factor of qform matrix).
The qform matrix and its inverse are re-computed automatically.
Besides reading it is also possible to set the qfac by assigning
to the `qfac` property.
:Parameters:
value: float
Scaling factor.
code: str | `NIFTI_XFORM_CODE` | int (0..4)
The type of the coordinate system the corresponding qform matrix is
describing. By default this coordinate system is assumed to be the
scanner space. Please refer to the
:meth:`~nifti.format.NiftiFormat.setXFormCode` method for a
full list of possible codes and their meaning.
.. seealso::
:attr:`~nifti.format.NiftiFormat.qform`,
:attr:`~nifti.format.NiftiFormat.qfac`
"""
self.__nimg.qfac = float(value)
self.__updateQFormFromQuaternion()
self.setXFormCode('qform', code)
def getQOrientation(self, as_string = False):
"""Returns to orientation of the i, j and k axis as stored in the
qform matrix.
By default NIfTI orientation codes are returned, but if `as_string` is
set to true a string representation ala 'Left-to-right' is returned
instead.
:Returns:
list
orientations fo the x, y and z axis respectively.
.. seealso::
:attr:`~nifti.format.NiftiFormat.qform`
"""
codes = ncl.nifti_mat44_to_orientation(self.__nimg.qto_xyz)
if as_string:
return [ ncl.nifti_orientation_string(i) for i in codes ]
else:
return codes
def getSOrientation(self, as_string = False):
"""Returns to orientation of the i, j and k axis as stored in the
sform matrix.
By default NIfTI orientation codes are returned, but if `as_string` is
set to true a string representation ala 'Left-to-right' is returned
instead.
:Returns:
list
orientations fo the x, y and z axis respectively.
.. seealso::
:attr:`~nifti.format.NiftiFormat.sform`
"""
codes = ncl.nifti_mat44_to_orientation(self.__nimg.sto_xyz)
if as_string:
return [ ncl.nifti_orientation_string(i) for i in codes ]
else:
return codes
def getXYZUnit(self, as_string = False):
"""Return 3D-space unit.
By default NIfTI unit codes are returned, but if `as_string` is set to
true a string representation ala 'mm' is returned instead.
.. seealso::
:meth:`~nifti.format.NiftiFormat.setXYZUnit`,
:attr:`~nifti.format.NiftiFormat.xyz_unit`
"""
code = self.__nimg.xyz_units
if as_string:
code = ncl.nifti_units_string(code)
return code
def setXYZUnit(self, value):
"""Set the unit of the spatial axes.
:Parameter:
value: int | str
The unit can either be given as a NIfTI unit code or as any of the
plain text abbrevations returned by
:meth:'~nifti.format.NiftiFormat.getXYZUnit`
.. seealso::
:meth:`~nifti.format.NiftiFormat.getXYZUnit`,
:attr:`~nifti.format.NiftiFormat.xyz_unit`
"""
# check for valid codes according to NIfTI1 standard
code = _checkUnit(value, valid_xyz_unit_codes)
self.raw_nimg.xyz_units = code
def getTimeUnit(self, as_string = False):
"""Return unit of temporal (4th) axis.
By default NIfTI unit codes are returned, but if `as_string` is set to
true a string representation ala 's' is returned instead.
.. seealso::
:meth:`~nifti.format.NiftiFormat.setTimeUnit`,
:attr:`~nifti.format.NiftiFormat.time_unit`
"""
code = self.__nimg.time_units
if as_string:
code = ncl.nifti_units_string(code)
return code
def setTimeUnit(self, value):
"""Set the unit of the temporal axis (4th).
:Parameter:
value: int | str
The unit can either be given as a NIfTI unit code or as any of the
plain text abbrevations returned by
:meth:'~nifti.format.NiftiFormat.getTimeUnit`
.. seealso::
:meth:`~nifti.format.NiftiFormat.getTimeUnit`,
:attr:`~nifti.format.NiftiFormat.time_unit`
"""
# check for valid codes according to NIfTI1 standard
code = _checkUnit(value, valid_time_unit_codes)
self.raw_nimg.time_units = code
def getFilename(self):
"""Returns the filename.
To distinguish ANALYZE from 2-file NIfTI images the image filename is
returned for ANALYZE images while the header filename is returned for
NIfTI files.
.. seealso::
:attr:`~nifti.format.NiftiFormat.filename`
"""
if self.__nimg.nifti_type == ncl.NIFTI_FTYPE_ANALYZE:
return self.__nimg.iname
else:
return self.__nimg.fname
#
# class properties
#
# read only
nvox = property(fget=lambda self: self.__nimg.nvox)
max = property(fget=lambda self: self.__nimg.cal_max)
min = property(fget=lambda self: self.__nimg.cal_min)
sform_inv = property(fget=getInverseSForm)
qform_inv = property(fget=getInverseQForm)
extent = property(fget=getExtent)
volextent = property(fget=getVolumeExtent)
timepoints = property(fget=getTimepoints)
raw_nimg = property(fget=lambda self: self.__nimg)
filename = property(fget=getFilename)
# read and write
slope = property(fget=lambda self: self.__nimg.scl_slope,
fset=setSlope)
intercept = property(fget=lambda self: self.__nimg.scl_inter,
fset=setIntercept)
voxdim = property(fget=getVoxDims, fset=setVoxDims)
pixdim = property(fget=getPixDims, fset=setPixDims)
description = property(fget=lambda self: self.__nimg.descrip,
fset=setDescription)
header = property(
fget=asDict, fset=updateFromDict,
doc="""Access to a dictionary version of the NIfTI header data.
.. note::
This property cannot be used like this::
nimg.header['something'] = 'new value'
Instead one has to get the header dictionary, modify
and later reassign it::
h = nimg.header
h['something'] = 'new value'
nimg.header = h
.. seealso::
:meth:`~nifti.format.NiftiFormat.asDict`,
:meth:`~nifti.format.NiftiFormat.updateFromDict`
""")
sform = property(fget=getSForm, fset=setSForm)
sform_code = property(fget=getSFormCode, fset=setSFormCode)
qform = property(fget=getQForm, fset=setQForm)
qform_code = property(fget=getQFormCode, fset=setQFormCode)
quatern = property(fget=getQuaternion, fset=setQuaternion)
qoffset = property(fget=getQOffset, fset=setQOffset)
qfac = property(fget=lambda self: self.__nimg.qfac, fset=setQFac)
rtime = property(fget=getRepetitionTime, fset=setRepetitionTime)
xyz_unit = property(fget=getXYZUnit, fset=setXYZUnit)
time_unit = property(fget=getTimeUnit, fset=setTimeUnit)
|