def write_hdr(self, filename): "Write ANALYZE format header (.hdr) file." image = self.image # NOT IMPLEMENTING YET # ReconImage r0 is the offset in xyz-space, but ANALYZE # calls for an offset in vox-space xform = image.orientation_xform.tomatrix() orientation_name = canonical_orient(xform) inv_xform = np.linalg.inv(xform) r0 = np.array([image.x0, image.y0, image.z0]) dimscl = np.array([image.isize, image.jsize, image.ksize]) r0_ana = (-np.dot(inv_xform, r0) / dimscl).astype(np.int16) dtype = datatype2dtype[self.datatype] if dtype in np.sctypes['int']: glmax = 2**(datatype2bitpix[self.datatype] - 1) - 1 elif dtype in np.sctypes['uint']: glmax = 2**(datatype2bitpix[self.datatype]) - 1 else: glmax = 1 imagevalues = { 'datatype': self.datatype, 'bitpix': datatype2bitpix[self.datatype], # should be 4 for (z,y,x) and (t,z,y,x) ... # should be 3 for (y,x) ?? 'ndim': (image.ndim >= 3 and 4 or image.ndim), 'idim': image.idim, 'jdim': image.jdim, 'kdim': image.kdim, 'tdim': (image.tdim or 1), 'isize': image.isize, 'jsize': image.jsize, 'ksize': image.ksize, 'tsize': image.tsize, 'x0': r0_ana[0], 'y0': r0_ana[1], 'z0': r0_ana[2], 'scale_factor': self.scaling, # SPM says that these are (0,2^bitpix-1) for integers, # or (0,1) for floating points 'glmin': 0, 'glmax': glmax, 'cal_min': 0.0, # don't suggest display color mapping 'cal_max': 0.0, 'orient': orientname2orientcode.get(orientation_name, 255), } def fieldvalue(fieldname, fieldformat): if imagevalues.has_key(fieldname): return imagevalues[fieldname] return self._default_field_value(fieldname, fieldformat) fieldvalues = [fieldvalue(*field) for field in struct_fields.items()] header = struct_pack(NATIVE, field_formats, fieldvalues) file(filename, 'wb').write(header)
def make_hdr(self): "Pack a NIFTI format header." # The NIFTI Q-form field is helpful for keeping track of anatomical # dimensions in the presence of rotations. # # (un)rotation is handled like this: take the image as transformed with # 2 rotations, Qs, Rb (Qs represents a transform between scanner space # and a right-handed interpretation of the data, Rb is the xform # applied by slicing coronal-wise, sagital-wise, etc with the slice # gradient). # Then I_m = Qs*Rb(psi,theta,phi)*I_real # The goal is to encode a quaternion which corrects this rotation: # Rc = inv(Rb)*inv(Qs) # so I_real = Rc*I_m # # The euler angles for slicing rotations are from procpar # To avoid the chance of interpolation, these angles are "normalized" # to the closest multiple of pi/2 (ie 22 degrees->0, 49 degrees->90) # image = self.image Qform = image.orientation_xform.Q Qform_mat = image.orientation_xform.tomatrix() # hack alert! ## (pe_dim, fe_dim) = Qform_mat[0,0] == 0. and (2, 1) or (1, 2) (pe_dim, fe_dim) = (2,1) imagevalues = { 'dim_info': (3<<4 | pe_dim<<2 | fe_dim), 'datatype': self.datatype, 'bitpix': datatype2bitpix[self.datatype], 'ndim': image.ndim, 'idim': image.idim, 'jdim': image.jdim, # including t,z info should probably depend on targetdims 'kdim': image.kdim, 'tdim': image.tdim, 'isize': image.isize, 'jsize': image.jsize, 'ksize': image.ksize, 'tsize': image.tsize, 'scl_slope': self.scaling, 'xyzt_units': (NIFTI_UNITS_MM | NIFTI_UNITS_SEC), 'qfac': image.orientation_xform.qfac, 'qform_code': NIFTI_XFORM_SCANNER_ANAT, 'quatern_b': Qform[0], 'quatern_c': Qform[1], 'quatern_d': Qform[2], 'qoffset_x': image.x0, 'qoffset_y': image.y0, 'qoffset_z': image.z0, 'slice_start': 0, # THIS KDIM ASSUMPTION IS ONLY SAFE BECAUSE NOTHING ROTATES # IMAGES OFF THE ORIGINAL SLICE AXIS (YET!!) 'slice_end': image.kdim-1, # VARIAN ACQUIRES FROM +Z TO -Z (IN NEURO FRAME) 'slice_code': NIFTI_SLICE_SEQ_DEC, 'slice_duration': float(image.tsize) / image.kdim } if self.filetype=='single': imagevalues['vox_offset'] = HEADER_SIZE + self.sizeof_extension imagevalues['magic'] = 'n+1' else: imagevalues['magic'] = 'ni1' def fieldvalue(fieldname, fieldformat): return imagevalues.get(fieldname) or \ self._default_field_value(fieldname, fieldformat) fieldvalues = [fieldvalue(*field) for field in struct_fields.items()] return struct_pack(NATIVE, field_formats, fieldvalues)